blob: 97cbefd2ca7b79790b5f9200e4cb39e82ab6704f [file] [log] [blame]
// 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/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
/// Helper for verifying resolution of assignments, in form of explicit
/// an [AssignmentExpression], or a [PrefixExpression] or [PostfixExpression]
/// when the operator is an increment operator.
class AssignmentVerifier {
final LibraryElement _definingLibrary;
final ErrorReporter _errorReporter;
AssignmentVerifier(this._definingLibrary, this._errorReporter);
/// 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
/// [CompileTimeErrorCode.UNDEFINED_SETTER] instead of a more generic
/// [CompileTimeErrorCode.UNDEFINED_IDENTIFIER].
void verify({
required SimpleIdentifier node,
required Element? requested,
required Element? recovery,
required DartType? receiverType,
}) {
if (requested != null) {
if (requested is VariableElement) {
if (requested.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
node,
);
} else if (requested.isFinal) {
if (_definingLibrary.isNonNullableByDefault) {
// Handled during resolution, with flow analysis.
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL,
node,
[requested.name],
);
}
}
}
return;
}
if (recovery is ClassElement ||
recovery is DynamicElementImpl ||
recovery is TypeAliasElement ||
recovery is TypeParameterElement) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_TYPE,
node,
);
} else if (recovery is FunctionElement) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FUNCTION,
node,
);
} else if (recovery is MethodElement) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_METHOD,
node,
);
} else if (recovery is PrefixElement) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
node,
[recovery.name],
);
} else if (recovery is PropertyAccessorElement && recovery.isGetter) {
var variable = recovery.variable;
if (variable.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_CONST,
node,
);
} else if (variable is FieldElement && variable.isSynthetic) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
node,
[variable.name, variable.enclosingElement.displayName],
);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.ASSIGNMENT_TO_FINAL,
node,
[variable.name],
);
}
} else if (recovery is MultiplyDefinedElementImpl) {
// Will be reported in ErrorVerifier.
} else {
if (node.isSynthetic) {
return;
}
if (receiverType != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_SETTER,
node,
[node.name, receiverType],
);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
node,
[node.name],
);
}
}
}
}