blob: 16bbd48d48f0b9d3ff4c5dc31e5c605511e25ed9 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart';
import 'package:analyzer/src/error/codes.dart';
/// A factory used to create diagnostics.
class DiagnosticFactory {
/// Initialize a newly created diagnostic factory.
DiagnosticFactory();
/// Return a diagnostic indicating that [duplicate] uses the same [variable]
/// as a previous [original] node in a pattern assignment.
Diagnostic duplicateAssignmentPatternVariable({
required Source source,
required PromotableElement variable,
required AssignedVariablePatternImpl original,
required AssignedVariablePatternImpl duplicate,
}) {
return Diagnostic.tmp(
source: source,
offset: duplicate.offset,
length: duplicate.length,
errorCode: CompileTimeErrorCode.DUPLICATE_PATTERN_ASSIGNMENT_VARIABLE,
arguments: [variable.name3!],
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
length: original.length,
message: 'The first assigned variable pattern.',
offset: original.offset,
url: source.uri.toString(),
),
],
);
}
/// Return a diagnostic indicating that [duplicateElement] reuses a name
/// already used by [originalElement].
Diagnostic duplicateDefinition(
DiagnosticCode code,
Element duplicateElement,
Element originalElement,
List<Object> arguments,
) {
var duplicate = duplicateElement.nonSynthetic2;
var duplicateFragment = duplicate.firstFragment;
var original = originalElement.nonSynthetic2;
var originalFragment = original.firstFragment;
return Diagnostic.tmp(
source: duplicateFragment.libraryFragment!.source,
offset: duplicateFragment.nameOffset2 ?? -1,
length: duplicate.name3!.length,
errorCode: code,
arguments: arguments,
contextMessages: [
DiagnosticMessageImpl(
filePath: originalFragment.libraryFragment!.source.fullName,
message: "The first definition of this name.",
offset: originalFragment.nameOffset2 ?? -1,
length: original.name3!.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that [duplicateNode] reuses a name
/// already used by [originalNode].
Diagnostic duplicateDefinitionForNodes(
Source source,
DiagnosticCode code,
SyntacticEntity duplicateNode,
SyntacticEntity originalNode,
List<Object> arguments,
) {
return Diagnostic.tmp(
source: source,
offset: duplicateNode.offset,
length: duplicateNode.length,
errorCode: code,
arguments: arguments,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The first definition of this name.",
offset: originalNode.offset,
length: originalNode.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that [duplicateField] reuses a name
/// already used by [originalField].
Diagnostic duplicateFieldDefinitionInLiteral(
Source source,
NamedExpression duplicateField,
NamedExpression originalField,
) {
var duplicateNode = duplicateField.name.label;
var duplicateName = duplicateNode.name;
return Diagnostic.tmp(
source: source,
offset: duplicateNode.offset,
length: duplicateNode.length,
errorCode: CompileTimeErrorCode.DUPLICATE_FIELD_NAME,
arguments: [duplicateName],
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
length: duplicateName.length,
message: 'The first ',
offset: originalField.name.label.offset,
url: source.uri.toString(),
),
],
);
}
/// Return a diagnostic indicating that [duplicateField] reuses a name
/// already used by [originalField].
///
/// This method requires that both the [duplicateField] and [originalField]
/// have a non-null `name`.
Diagnostic duplicateFieldDefinitionInType(
Source source,
RecordTypeAnnotationField duplicateField,
RecordTypeAnnotationField originalField,
) {
var duplicateNode = duplicateField.name!;
var duplicateName = duplicateNode.lexeme;
return Diagnostic.tmp(
source: source,
offset: duplicateNode.offset,
length: duplicateNode.length,
errorCode: CompileTimeErrorCode.DUPLICATE_FIELD_NAME,
arguments: [duplicateName],
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
length: duplicateName.length,
message: 'The first ',
offset: originalField.name!.offset,
url: source.uri.toString(),
),
],
);
}
/// Return a diagnostic indicating that [duplicateField] reuses a name
/// already used by [originalField].
Diagnostic duplicatePatternField({
required Source source,
required String name,
required PatternField duplicateField,
required PatternField originalField,
}) {
var originalNode = originalField.name!;
var originalTarget = originalNode.name ?? originalNode.colon;
var duplicateNode = duplicateField.name!;
var duplicateTarget = duplicateNode.name ?? duplicateNode.colon;
return Diagnostic.tmp(
source: source,
offset: duplicateTarget.offset,
length: duplicateTarget.length,
errorCode: CompileTimeErrorCode.DUPLICATE_PATTERN_FIELD,
arguments: [name],
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
length: originalTarget.length,
message: 'The first field.',
offset: originalTarget.offset,
url: source.uri.toString(),
),
],
);
}
/// Return a diagnostic indicating that [duplicateElement] reuses a name
/// already used by [originalElement].
Diagnostic duplicateRestElementInPattern({
required Source source,
required RestPatternElement originalElement,
required RestPatternElement duplicateElement,
}) {
return Diagnostic.tmp(
source: source,
offset: duplicateElement.offset,
length: duplicateElement.length,
errorCode: CompileTimeErrorCode.DUPLICATE_REST_ELEMENT_IN_PATTERN,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
length: originalElement.length,
message: 'The first rest element.',
offset: originalElement.offset,
url: source.uri.toString(),
),
],
);
}
/// Return a diagnostic indicating that the [duplicateElement] (in a constant
/// set) is a duplicate of the [originalElement].
Diagnostic equalElementsInConstSet(
Source source,
Expression duplicateElement,
Expression originalElement,
) {
return Diagnostic.tmp(
source: source,
offset: duplicateElement.offset,
length: duplicateElement.length,
errorCode: CompileTimeErrorCode.EQUAL_ELEMENTS_IN_CONST_SET,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The first element with this value.",
offset: originalElement.offset,
length: originalElement.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that the [duplicateKey] (in a constant map)
/// is a duplicate of the [originalKey].
Diagnostic equalKeysInConstMap(
Source source,
Expression duplicateKey,
Expression originalKey,
) {
return Diagnostic.tmp(
source: source,
offset: duplicateKey.offset,
length: duplicateKey.length,
errorCode: CompileTimeErrorCode.EQUAL_KEYS_IN_CONST_MAP,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The first key with this value.",
offset: originalKey.offset,
length: originalKey.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that the [duplicateKey] (in a map pattern)
/// is a duplicate of the [originalKey].
Diagnostic equalKeysInMapPattern(
Source source,
Expression duplicateKey,
Expression originalKey,
) {
return Diagnostic.tmp(
source: source,
offset: duplicateKey.offset,
length: duplicateKey.length,
errorCode: CompileTimeErrorCode.EQUAL_KEYS_IN_MAP_PATTERN,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The first key with this value.",
offset: originalKey.offset,
length: originalKey.length,
url: null,
),
],
);
}
Diagnostic invalidNullAwareAfterShortCircuit(
Source source,
int offset,
int length,
List<Object> arguments,
Token previousToken,
) {
var lexeme = previousToken.lexeme;
return Diagnostic.tmp(
source: source,
offset: offset,
length: length,
errorCode:
StaticWarningCode.INVALID_NULL_AWARE_OPERATOR_AFTER_SHORT_CIRCUIT,
arguments: arguments,
contextMessages: [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The operator '$lexeme' is causing the short circuiting.",
offset: previousToken.offset,
length: previousToken.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that [member] is not a correct override of
/// [superMember].
Diagnostic invalidOverride(
Source source,
DiagnosticCode code,
SyntacticEntity errorNode,
ExecutableElement member,
ExecutableElement superMember,
String memberName,
) {
// Elements enclosing members that can participate in overrides are always
// named, so we can safely assume `_thisMember.enclosingElement3.name` and
// `superMember.enclosingElement3.name` are non-`null`.
var superFragment = superMember.nonSynthetic2.firstFragment;
return Diagnostic.tmp(
source: source,
offset: errorNode.offset,
length: errorNode.length,
errorCode: code,
arguments: [
memberName,
member.enclosingElement2!.name3,
member.type,
superMember.enclosingElement2!.name3,
superMember.type,
],
contextMessages: [
// Only include the context location for INVALID_OVERRIDE because for
// some other types this location is not ideal (for example
// INVALID_IMPLEMENTATION_OVERRIDE may provide the subclass as superMember
// if the subclass has an abstract member and the superclass has the
// concrete).
if (code == CompileTimeErrorCode.INVALID_OVERRIDE)
DiagnosticMessageImpl(
filePath: superFragment.libraryFragment!.source.fullName,
message: "The member being overridden.",
offset: superFragment.nameOffset2 ?? -1,
length: superFragment.name2!.length,
url: null,
),
if (code == CompileTimeErrorCode.INVALID_OVERRIDE_SETTER)
DiagnosticMessageImpl(
filePath: superFragment.libraryFragment!.source.fullName,
message: "The setter being overridden.",
offset: superFragment.nameOffset2 ?? -1,
length: superFragment.name2!.length,
url: null,
),
],
);
}
/// Return a diagnostic indicating that the given [nameToken] was referenced
/// before it was declared.
Diagnostic referencedBeforeDeclaration(
Source source, {
required Token nameToken,
required Element element2,
}) {
String name = nameToken.lexeme;
List<DiagnosticMessage>? contextMessages;
int declarationOffset = element2.firstFragment.nameOffset2 ?? -1;
if (declarationOffset >= 0) {
contextMessages = [
DiagnosticMessageImpl(
filePath: source.fullName,
message: "The declaration of '$name' is here.",
offset: declarationOffset,
length: name.length,
url: null,
),
];
}
return Diagnostic.tmp(
source: source,
offset: nameToken.offset,
length: nameToken.length,
errorCode: CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
arguments: [name],
contextMessages: contextMessages ?? const [],
);
}
}