blob: 3c780bd0a5a6cb951e8548cf3de4a679f83b19a5 [file] [edit]
// Copyright (c) 2020, 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/ast.dart';
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/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
import 'package:analyzer/src/error/listener.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
/// Helper for verifying resolution of explicit [AssignmentExpression]s or
/// implicit [IncrementOrDecrementExpression]s.
class AssignmentVerifier {
final DiagnosticReporter _diagnosticReporter;
AssignmentVerifier(this._diagnosticReporter);
/// We resolved [node] and found that it references the [requested] element.
/// Verify that this element is actually writable.
///
/// If the [requested] element is `null`, we might have the [recovery]
/// element, which is definitely not a valid write target. We want to report
/// a good error about this.
///
/// When the [receiverType] is not `null`, we report
/// [diag.undefinedSetter] instead of a more generic
/// [diag.undefinedIdentifier].
void verify({
required SimpleIdentifier node,
required Element? requested,
required Element? recovery,
required DartType? receiverType,
}) => _verify(
node: node,
name: node.name,
isSynthetic: node.isSynthetic,
requested: requested,
recovery: recovery,
receiverType: receiverType,
);
void verifyPropertyAssignmentTarget({
required PropertyAssignmentTarget node,
required Element? requested,
required Element? recovery,
required DartType receiverType,
}) {
_verify(
node: node.name,
name: node.name.lexeme,
isSynthetic: node.name.isSynthetic,
requested: requested,
recovery: recovery,
receiverType: receiverType,
);
}
void verifyUnqualifiedName({
required SyntacticEntity node,
required Token name,
required Element? requested,
required Element? recovery,
}) {
var ambiguousElement =
requested.tryCast<MultiplyDefinedElementImpl>() ??
recovery.tryCast<MultiplyDefinedElementImpl>();
if (ambiguousElement != null) {
_diagnosticReporter.report(
DiagnosticFactory().ambiguousImport(
name: name,
element: ambiguousElement,
),
);
return;
}
_verify(
node: node,
name: name.lexeme,
isSynthetic: name.isSynthetic,
requested: requested,
recovery: recovery,
receiverType: null,
);
}
void _verify({
required SyntacticEntity node,
required String name,
required bool isSynthetic,
required Element? requested,
required Element? recovery,
required DartType? receiverType,
}) {
if (requested != null) {
if (requested is VariableElement) {
if (requested.isConst) {
_diagnosticReporter.report(diag.assignmentToConst.at(node));
}
}
return;
}
if (recovery is DynamicElementImpl ||
recovery is InterfaceElement ||
recovery is TypeAliasElement ||
recovery is TypeParameterElement) {
_diagnosticReporter.report(diag.assignmentToType.at(node));
} else if (recovery is LocalFunctionElement ||
recovery is TopLevelFunctionElement) {
_diagnosticReporter.report(diag.assignmentToFunction.at(node));
} else if (recovery is MethodElement) {
_diagnosticReporter.report(diag.assignmentToMethod.at(node));
} else if (recovery is PrefixElement) {
if (recovery.name case var prefixName?) {
_diagnosticReporter.report(
diag.prefixIdentifierNotFollowedByDot
.withArguments(name: prefixName)
.at(node),
);
}
} else if (recovery is GetterElement) {
var variable = recovery.variable;
var variableName = variable.name;
if (variableName == null) {
return;
}
if (variable.isConst) {
_diagnosticReporter.report(diag.assignmentToConst.at(node));
} else if (variable is FieldElement && variable.isOriginGetterSetter) {
_diagnosticReporter.report(
diag.assignmentToFinalNoSetter
.withArguments(
variableName: variableName,
className: variable.enclosingElement.displayName,
)
.at(node),
);
} else {
_diagnosticReporter.report(
diag.assignmentToFinal
.withArguments(variableName: variableName)
.at(node),
);
}
} else if (recovery is MultiplyDefinedElementImpl) {
// Will be reported in ErrorVerifier.
} else {
if (isSynthetic) {
return;
}
if (receiverType != null) {
_diagnosticReporter.report(
diag.undefinedSetter
.withArguments(setterName: name, type: receiverType)
.at(node),
);
} else {
_diagnosticReporter.report(
diag.undefinedIdentifier.withArguments(name: name).at(node),
);
}
}
}
}