| // 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), |
| ); |
| } |
| } |
| } |
| } |