blob: b470ac2bbd3c872b43ae2e8b9184ab6854efa7f8 [file] [log] [blame]
// Copyright (c) 2014, 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 'dart:collection';
import "dart:math" as math;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/error/pending_error.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/src/generated/element_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/dart.dart';
/**
* A visitor used to traverse an AST structure looking for additional errors and
* warnings not covered by the parser and resolver.
*/
class ErrorVerifier extends RecursiveAstVisitor<Object> {
/**
* Static final string with value `"getter "` used in the construction of the
* [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and
* similar, error code messages.
*
* See [_checkForNonAbstractClassInheritsAbstractMember].
*/
static String _GETTER_SPACE = "getter ";
/**
* Static final string with value `"setter "` used in the construction of the
* [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and
* similar, error code messages.
*
* See [_checkForNonAbstractClassInheritsAbstractMember].
*/
static String _SETTER_SPACE = "setter ";
/**
* The error reporter by which errors will be reported.
*/
final ErrorReporter _errorReporter;
/**
* The current library that is being analyzed.
*/
final LibraryElement _currentLibrary;
/**
* The type representing the type 'bool'.
*/
InterfaceType _boolType;
/**
* The type representing the type 'int'.
*/
InterfaceType _intType;
/**
* The options for verification.
*/
AnalysisOptionsImpl _options;
/**
* The object providing access to the types defined by the language.
*/
final TypeProvider _typeProvider;
/**
* The type system primitives
*/
TypeSystem _typeSystem;
/**
* The manager for the inheritance mappings.
*/
final InheritanceManager _inheritanceManager;
/**
* A flag indicating whether the visitor is currently within a constructor
* declaration that is 'const'.
*
* See [visitConstructorDeclaration].
*/
bool _isEnclosingConstructorConst = false;
/**
* A flag indicating whether we are currently within a function body marked as
* being asynchronous.
*/
bool _inAsync = false;
/**
* A flag indicating whether we are currently within a function body marked a
* being a generator.
*/
bool _inGenerator = false;
/**
* A flag indicating whether the visitor is currently within a catch clause.
*
* See [visitCatchClause].
*/
bool _isInCatchClause = false;
/**
* A flag indicating whether the visitor is currently within a comment.
*/
bool _isInComment = false;
/**
* A flag indicating whether the visitor is currently within an instance
* creation expression.
*/
bool _isInConstInstanceCreation = false;
/**
* A flag indicating whether the visitor is currently within a native class
* declaration.
*/
bool _isInNativeClass = false;
/**
* A flag indicating whether the visitor is currently within a static variable
* declaration.
*/
bool _isInStaticVariableDeclaration = false;
/**
* A flag indicating whether the visitor is currently within an instance
* variable declaration.
*/
bool _isInInstanceVariableDeclaration = false;
/**
* A flag indicating whether the visitor is currently within an instance
* variable initializer.
*/
bool _isInInstanceVariableInitializer = false;
/**
* A flag indicating whether the visitor is currently within a constructor
* initializer.
*/
bool _isInConstructorInitializer = false;
/**
* This is set to `true` iff the visitor is currently within a function typed
* formal parameter.
*/
bool _isInFunctionTypedFormalParameter = false;
/**
* A flag indicating whether the visitor is currently within a static method.
* By "method" here getter, setter and operator declarations are also implied
* since they are all represented with a [MethodDeclaration] in the AST
* structure.
*/
bool _isInStaticMethod = false;
/**
* A flag indicating whether the visitor is currently within a factory
* constructor.
*/
bool _isInFactory = false;
/**
* A flag indicating whether the visitor is currently within code in the SDK.
*/
bool _isInSystemLibrary = false;
/**
* A flag indicating whether the current library contains at least one import
* directive with a URI that uses the "dart-ext" scheme.
*/
bool _hasExtUri = false;
/**
* This is set to `false` on the entry of every [BlockFunctionBody], and is
* restored to the enclosing value on exit. The value is used in
* [_checkForMixedReturns] to prevent both
* [StaticWarningCode.MIXED_RETURN_TYPES] and
* [StaticWarningCode.RETURN_WITHOUT_VALUE] from being generated in the same
* function body.
*/
bool _hasReturnWithoutValue = false;
/**
* The class containing the AST nodes being visited, or `null` if we are not
* in the scope of a class.
*/
ClassElementImpl _enclosingClass;
/**
* The enum containing the AST nodes being visited, or `null` if we are not
* in the scope of an enum.
*/
ClassElement _enclosingEnum;
/**
* The method or function that we are currently visiting, or `null` if we are
* not inside a method or function.
*/
ExecutableElement _enclosingFunction;
/**
* The return statements found in the method or function that we are currently
* visiting that have a return value.
*/
List<ReturnStatement> _returnsWith = new List<ReturnStatement>();
/**
* The return statements found in the method or function that we are currently
* visiting that do not have a return value.
*/
List<ReturnStatement> _returnsWithout = new List<ReturnStatement>();
/**
* This map is initialized when visiting the contents of a class declaration.
* If the visitor is not in an enclosing class declaration, then the map is
* set to `null`.
*
* When set the map maps the set of [FieldElement]s in the class to an
* [INIT_STATE.NOT_INIT] or [INIT_STATE.INIT_IN_DECLARATION]. The `checkFor*`
* methods, specifically [_checkForAllFinalInitializedErrorCodes], can make a
* copy of the map to compute error code states. The `checkFor*` methods
* should only ever make a copy, or read from this map after it has been set
* in [visitClassDeclaration].
*
* See [visitClassDeclaration], and [_checkForAllFinalInitializedErrorCodes].
*/
Map<FieldElement, INIT_STATE> _initialFieldElementsMap;
/**
* A table mapping name of the library to the export directive which export
* this library.
*/
Map<String, LibraryElement> _nameToExportElement =
new HashMap<String, LibraryElement>();
/**
* A table mapping name of the library to the import directive which import
* this library.
*/
Map<String, LibraryElement> _nameToImportElement =
new HashMap<String, LibraryElement>();
/**
* A table mapping names to the exported elements.
*/
Map<String, Element> _exportedElements = new HashMap<String, Element>();
/**
* A set of the names of the variable initializers we are visiting now.
*/
HashSet<String> _namesForReferenceToDeclaredVariableInInitializer =
new HashSet<String>();
/**
* The elements that will be defined later in the current scope, but right
* now are not declared.
*/
HiddenElements _hiddenElements = null;
/**
* A list of types used by the [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]
* and [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS] error codes.
*/
List<InterfaceType> _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT;
/**
* If `true`, mixins are allowed to inherit from types other than Object, and
* are allowed to reference `super`.
*/
final bool enableSuperMixins;
final _UninstantiatedBoundChecker _uninstantiatedBoundChecker;
/// Setting this flag to `true` disables the check for conflicting generics.
/// This is used when running with the old task model to work around
/// dartbug.com/32421.
///
/// TODO(paulberry): remove this flag once dartbug.com/32421 is properly
/// fixed.
final bool disableConflictingGenericsCheck;
/**
* Initialize a newly created error verifier.
*/
ErrorVerifier(ErrorReporter errorReporter, this._currentLibrary,
this._typeProvider, this._inheritanceManager, this.enableSuperMixins,
{this.disableConflictingGenericsCheck: false})
: _errorReporter = errorReporter,
_uninstantiatedBoundChecker =
new _UninstantiatedBoundChecker(errorReporter) {
this._isInSystemLibrary = _currentLibrary.source.isInSystemLibrary;
this._hasExtUri = _currentLibrary.hasExtUri;
_isEnclosingConstructorConst = false;
_isInCatchClause = false;
_isInStaticVariableDeclaration = false;
_isInInstanceVariableDeclaration = false;
_isInInstanceVariableInitializer = false;
_isInConstructorInitializer = false;
_isInStaticMethod = false;
_boolType = _typeProvider.boolType;
_intType = _typeProvider.intType;
_DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT = _typeProvider.nonSubtypableTypes;
_typeSystem = _currentLibrary.context.typeSystem;
_options = _currentLibrary.context.analysisOptions;
}
ClassElement get enclosingClass => _enclosingClass;
/**
* For consumers of error verification as a library, (currently just the
* angular plugin), expose a setter that can make the errors reported more
* accurate when dangling code snippets are being resolved from a class
* context. Note that this setter is very defensive for potential misuse; it
* should not be modified in the middle of visiting a tree and requires an
* analyzer-provided Impl instance to work.
*/
set enclosingClass(ClassElement classElement) {
assert(classElement is ClassElementImpl);
assert(_enclosingClass == null);
assert(_enclosingEnum == null);
assert(_enclosingFunction == null);
_enclosingClass = classElement;
}
@override
Object visitAnnotation(Annotation node) {
_checkForInvalidAnnotationFromDeferredLibrary(node);
_checkForMissingJSLibAnnotation(node);
return super.visitAnnotation(node);
}
@override
Object visitArgumentList(ArgumentList node) {
_checkForArgumentTypesNotAssignableInList(node);
return super.visitArgumentList(node);
}
@override
Object visitAsExpression(AsExpression node) {
_checkForTypeAnnotationDeferredClass(node.type);
return super.visitAsExpression(node);
}
@override
Object visitAssertInitializer(AssertInitializer node) {
_checkForNonBoolExpression(node);
return super.visitAssertInitializer(node);
}
@override
Object visitAssertStatement(AssertStatement node) {
_checkForNonBoolExpression(node);
return super.visitAssertStatement(node);
}
@override
Object visitAssignmentExpression(AssignmentExpression node) {
TokenType operatorType = node.operator.type;
Expression lhs = node.leftHandSide;
Expression rhs = node.rightHandSide;
if (operatorType == TokenType.EQ ||
operatorType == TokenType.QUESTION_QUESTION_EQ) {
_checkForInvalidAssignment(lhs, rhs);
} else {
_checkForInvalidCompoundAssignment(node, lhs, rhs);
_checkForArgumentTypeNotAssignableForArgument(rhs);
}
_checkForAssignmentToFinal(lhs);
return super.visitAssignmentExpression(node);
}
@override
Object visitAwaitExpression(AwaitExpression node) {
if (!_inAsync) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, node.awaitKeyword);
}
return super.visitAwaitExpression(node);
}
@override
Object visitBinaryExpression(BinaryExpression node) {
Token operator = node.operator;
TokenType type = operator.type;
if (type == TokenType.AMPERSAND_AMPERSAND || type == TokenType.BAR_BAR) {
String lexeme = operator.lexeme;
_checkForAssignability(node.leftOperand, _boolType,
StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]);
_checkForAssignability(node.rightOperand, _boolType,
StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]);
} else {
_checkForArgumentTypeNotAssignableForArgument(node.rightOperand);
}
_checkForUseOfVoidResult(node.leftOperand);
return super.visitBinaryExpression(node);
}
@override
Object visitBlock(Block node) {
_hiddenElements = new HiddenElements(_hiddenElements, node);
try {
_checkDuplicateDeclarationInStatements(node.statements);
return super.visitBlock(node);
} finally {
_hiddenElements = _hiddenElements.outerElements;
}
}
@override
Object visitBlockFunctionBody(BlockFunctionBody node) {
bool wasInAsync = _inAsync;
bool wasInGenerator = _inGenerator;
bool previousHasReturnWithoutValue = _hasReturnWithoutValue;
_hasReturnWithoutValue = false;
List<ReturnStatement> previousReturnsWith = _returnsWith;
List<ReturnStatement> previousReturnsWithout = _returnsWithout;
try {
_inAsync = node.isAsynchronous;
_inGenerator = node.isGenerator;
_returnsWith = new List<ReturnStatement>();
_returnsWithout = new List<ReturnStatement>();
super.visitBlockFunctionBody(node);
_checkForMixedReturns(node);
} finally {
_inAsync = wasInAsync;
_inGenerator = wasInGenerator;
_returnsWith = previousReturnsWith;
_returnsWithout = previousReturnsWithout;
_hasReturnWithoutValue = previousHasReturnWithoutValue;
}
return null;
}
@override
Object visitBreakStatement(BreakStatement node) {
SimpleIdentifier labelNode = node.label;
if (labelNode != null) {
Element labelElement = labelNode.staticElement;
if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) {
_errorReporter.reportErrorForNode(
ResolverErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode);
}
}
return null;
}
@override
Object visitCatchClause(CatchClause node) {
_checkDuplicateDefinitionInCatchClause(node);
bool previousIsInCatchClause = _isInCatchClause;
try {
_isInCatchClause = true;
_checkForTypeAnnotationDeferredClass(node.exceptionType);
return super.visitCatchClause(node);
} finally {
_isInCatchClause = previousIsInCatchClause;
}
}
@override
Object visitClassDeclaration(ClassDeclaration node) {
ClassElementImpl outerClass = _enclosingClass;
try {
_isInNativeClass = node.nativeClause != null;
_enclosingClass = AbstractClassElementImpl.getImpl(node.element);
_checkDuplicateClassMembers(node);
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
_checkForMemberWithClassName();
_checkForNoDefaultSuperConstructorImplicit(node);
_checkForConflictingTypeVariableErrorCodes(node);
TypeName superclass = node.extendsClause?.superclass;
ImplementsClause implementsClause = node.implementsClause;
WithClause withClause = node.withClause;
// Only do error checks on the clause nodes if there is a non-null clause
if (implementsClause != null ||
superclass != null ||
withClause != null) {
_checkClassInheritance(node, superclass, withClause, implementsClause);
}
visitClassDeclarationIncrementally(node);
_checkForFinalNotInitializedInClass(node);
_checkForDuplicateDefinitionInheritance();
_checkForConflictingInstanceMethodSetter(node);
_checkForBadFunctionUse(node);
return super.visitClassDeclaration(node);
} finally {
_isInNativeClass = false;
_initialFieldElementsMap = null;
_enclosingClass = outerClass;
}
}
/**
* Implementation of this method should be synchronized with
* [visitClassDeclaration].
*/
void visitClassDeclarationIncrementally(ClassDeclaration node) {
_isInNativeClass = node.nativeClause != null;
_enclosingClass = AbstractClassElementImpl.getImpl(node.element);
// initialize initialFieldElementsMap
if (_enclosingClass != null) {
List<FieldElement> fieldElements = _enclosingClass.fields;
_initialFieldElementsMap = new HashMap<FieldElement, INIT_STATE>();
for (FieldElement fieldElement in fieldElements) {
if (!fieldElement.isSynthetic) {
_initialFieldElementsMap[fieldElement] =
fieldElement.initializer == null
? INIT_STATE.NOT_INIT
: INIT_STATE.INIT_IN_DECLARATION;
}
}
}
}
@override
Object visitClassTypeAlias(ClassTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
ClassElementImpl outerClassElement = _enclosingClass;
try {
_enclosingClass = AbstractClassElementImpl.getImpl(node.element);
_checkClassInheritance(
node, node.superclass, node.withClause, node.implementsClause);
} finally {
_enclosingClass = outerClassElement;
}
return super.visitClassTypeAlias(node);
}
@override
Object visitComment(Comment node) {
_isInComment = true;
try {
return super.visitComment(node);
} finally {
_isInComment = false;
}
}
@override
Object visitCompilationUnit(CompilationUnit node) {
_checkDuplicateUnitMembers(node);
_checkForDeferredPrefixCollisions(node);
return super.visitCompilationUnit(node);
}
@override
Object visitConditionalExpression(ConditionalExpression node) {
_checkForNonBoolCondition(node.condition);
// TODO(mfairhurst) Enable this and get code compliant.
//_checkForUseOfVoidResult(node.thenExpression);
//_checkForUseOfVoidResult(node.elseExpression);
return super.visitConditionalExpression(node);
}
@override
Object visitConstructorDeclaration(ConstructorDeclaration node) {
ExecutableElement outerFunction = _enclosingFunction;
try {
ConstructorElement constructorElement = node.element;
_enclosingFunction = constructorElement;
_isEnclosingConstructorConst = node.constKeyword != null;
_isInFactory = node.factoryKeyword != null;
_checkForInvalidModifierOnBody(
node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR);
_checkForConstConstructorWithNonFinalField(node, constructorElement);
_checkForConstConstructorWithNonConstSuper(node);
_checkForConflictingConstructorNameAndMember(node, constructorElement);
_checkForAllFinalInitializedErrorCodes(node);
_checkForRedirectingConstructorErrorCodes(node);
_checkForMultipleSuperInitializers(node);
_checkForRecursiveConstructorRedirect(node, constructorElement);
if (!_checkForRecursiveFactoryRedirect(node, constructorElement)) {
_checkForAllRedirectConstructorErrorCodes(node);
}
_checkForUndefinedConstructorInInitializerImplicit(node);
_checkForRedirectToNonConstConstructor(node, constructorElement);
_checkForReturnInGenerativeConstructor(node);
return super.visitConstructorDeclaration(node);
} finally {
_isEnclosingConstructorConst = false;
_isInFactory = false;
_enclosingFunction = outerFunction;
}
}
@override
Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
_isInConstructorInitializer = true;
try {
SimpleIdentifier fieldName = node.fieldName;
Element staticElement = fieldName.staticElement;
_checkForInvalidField(node, fieldName, staticElement);
if (staticElement is FieldElement) {
_checkForFieldInitializerNotAssignable(node, staticElement);
}
return super.visitConstructorFieldInitializer(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
Object visitContinueStatement(ContinueStatement node) {
SimpleIdentifier labelNode = node.label;
if (labelNode != null) {
Element labelElement = labelNode.staticElement;
if (labelElement is LabelElementImpl &&
labelElement.isOnSwitchStatement) {
_errorReporter.reportErrorForNode(
ResolverErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode);
}
}
return null;
}
@override
Object visitDefaultFormalParameter(DefaultFormalParameter node) {
_checkForInvalidAssignment(node.identifier, node.defaultValue);
_checkForDefaultValueInFunctionTypedParameter(node);
return super.visitDefaultFormalParameter(node);
}
@override
Object visitDoStatement(DoStatement node) {
_checkForNonBoolCondition(node.condition);
return super.visitDoStatement(node);
}
@override
Object visitEnumDeclaration(EnumDeclaration node) {
ClassElement outerEnum = _enclosingEnum;
try {
_enclosingEnum = node.element;
_checkDuplicateEnumMembers(node);
return super.visitEnumDeclaration(node);
} finally {
_enclosingEnum = outerEnum;
}
}
@override
Object visitExportDirective(ExportDirective node) {
ExportElement exportElement = node.element;
if (exportElement != null) {
LibraryElement exportedLibrary = exportElement.exportedLibrary;
_checkForAmbiguousExport(node, exportElement, exportedLibrary);
_checkForExportDuplicateLibraryName(node, exportElement, exportedLibrary);
_checkForExportInternalLibrary(node, exportElement);
}
return super.visitExportDirective(node);
}
@override
Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
bool wasInAsync = _inAsync;
bool wasInGenerator = _inGenerator;
try {
_inAsync = node.isAsynchronous;
_inGenerator = node.isGenerator;
FunctionType functionType = _enclosingFunction?.type;
DartType expectedReturnType = functionType == null
? DynamicTypeImpl.instance
: functionType.returnType;
ExecutableElement function = _enclosingFunction;
bool isSetterWithImplicitReturn = function.hasImplicitReturnType &&
function is PropertyAccessorElement &&
function.isSetter;
if (!isSetterWithImplicitReturn) {
_checkForReturnOfInvalidType(node.expression, expectedReturnType,
isArrowFunction: true);
}
return super.visitExpressionFunctionBody(node);
} finally {
_inAsync = wasInAsync;
_inGenerator = wasInGenerator;
}
}
@override
Object visitFieldDeclaration(FieldDeclaration node) {
_isInStaticVariableDeclaration = node.isStatic;
_isInInstanceVariableDeclaration = !_isInStaticVariableDeclaration;
if (_isInInstanceVariableDeclaration) {
VariableDeclarationList variables = node.fields;
if (variables.isConst) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.CONST_INSTANCE_FIELD, variables.keyword);
}
}
try {
_checkForAllInvalidOverrideErrorCodesForField(node);
return super.visitFieldDeclaration(node);
} finally {
_isInStaticVariableDeclaration = false;
_isInInstanceVariableDeclaration = false;
}
}
@override
Object visitFieldFormalParameter(FieldFormalParameter node) {
_checkForValidField(node);
_checkForConstFormalParameter(node);
_checkForPrivateOptionalParameter(node);
_checkForFieldInitializingFormalRedirectingConstructor(node);
_checkForTypeAnnotationDeferredClass(node.type);
return super.visitFieldFormalParameter(node);
}
@override
Object visitForEachStatement(ForEachStatement node) {
_checkForInIterable(node);
return super.visitForEachStatement(node);
}
@override
Object visitFormalParameterList(FormalParameterList node) {
_checkDuplicateDefinitionInParameterList(node);
_checkUseOfCovariantInParameters(node);
return super.visitFormalParameterList(node);
}
@override
Object visitForStatement(ForStatement node) {
if (node.condition != null) {
_checkForNonBoolCondition(node.condition);
}
if (node.variables != null) {
_checkDuplicateVariables(node.variables);
}
return super.visitForStatement(node);
}
@override
Object visitFunctionDeclaration(FunctionDeclaration node) {
ExecutableElement functionElement = node.element;
if (functionElement != null &&
functionElement.enclosingElement is! CompilationUnitElement) {
_hiddenElements.declare(functionElement);
}
ExecutableElement outerFunction = _enclosingFunction;
try {
SimpleIdentifier identifier = node.name;
String methodName = "";
if (identifier != null) {
methodName = identifier.name;
}
_enclosingFunction = functionElement;
TypeAnnotation returnType = node.returnType;
if (node.isSetter || node.isGetter) {
_checkForMismatchedAccessorTypes(node, methodName);
if (node.isSetter) {
FunctionExpression functionExpression = node.functionExpression;
if (functionExpression != null) {
_checkForWrongNumberOfParametersForSetter(
identifier, functionExpression.parameters);
}
_checkForNonVoidReturnTypeForSetter(returnType);
}
}
if (node.isSetter) {
_checkForInvalidModifierOnBody(node.functionExpression.body,
CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
}
_checkForTypeAnnotationDeferredClass(returnType);
_checkForIllegalReturnType(returnType);
_checkForImplicitDynamicReturn(node.name, node.element);
return super.visitFunctionDeclaration(node);
} finally {
_enclosingFunction = outerFunction;
}
}
@override
Object visitFunctionExpression(FunctionExpression node) {
// If this function expression is wrapped in a function declaration, don't
// change the enclosingFunction field.
if (node.parent is! FunctionDeclaration) {
ExecutableElement outerFunction = _enclosingFunction;
try {
_enclosingFunction = node.element;
return super.visitFunctionExpression(node);
} finally {
_enclosingFunction = outerFunction;
}
} else {
return super.visitFunctionExpression(node);
}
}
@override
Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
Expression functionExpression = node.function;
DartType expressionType = functionExpression.staticType;
if (!_checkForUseOfVoidResult(functionExpression) &&
!_isFunctionType(expressionType)) {
_errorReporter.reportErrorForNode(
StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
functionExpression);
} else if (expressionType is FunctionType) {
_checkTypeArguments(node);
}
_checkForImplicitDynamicInvoke(node);
return super.visitFunctionExpressionInvocation(node);
}
@override
Object visitFunctionTypeAlias(FunctionTypeAlias node) {
_checkForBuiltInIdentifierAsName(
node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
_checkForDefaultValueInFunctionTypeAlias(node);
_checkForTypeAliasCannotReferenceItself_function(node);
return super.visitFunctionTypeAlias(node);
}
@override
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
bool old = _isInFunctionTypedFormalParameter;
_isInFunctionTypedFormalParameter = true;
try {
_checkForTypeAnnotationDeferredClass(node.returnType);
// TODO(jmesserly): ideally we'd use _checkForImplicitDynamicReturn, and
// we can get the function element via `node?.element?.type?.element` but
// it doesn't have hasImplicitReturnType set correctly.
if (!_options.implicitDynamic && node.returnType == null) {
DartType parameterType =
resolutionMap.elementDeclaredByFormalParameter(node).type;
if (parameterType is FunctionType &&
parameterType.returnType.isDynamic) {
_errorReporter.reportErrorForNode(
StrongModeCode.IMPLICIT_DYNAMIC_RETURN,
node.identifier,
[node.identifier]);
}
}
// TODO(paulberry): remove this once dartbug.com/28515 is fixed.
if (node.typeParameters != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.GENERIC_FUNCTION_TYPED_PARAM_UNSUPPORTED,
node);
}
return super.visitFunctionTypedFormalParameter(node);
} finally {
_isInFunctionTypedFormalParameter = old;
}
}
@override
Object visitGenericTypeAlias(GenericTypeAlias node) {
if (_hasTypedefSelfReference(node.element)) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, node);
}
return super.visitGenericTypeAlias(node);
}
@override
Object visitIfStatement(IfStatement node) {
_checkForNonBoolCondition(node.condition);
return super.visitIfStatement(node);
}
@override
Object visitImplementsClause(ImplementsClause node) {
node.interfaces.forEach(_checkForImplicitDynamicType);
return super.visitImplementsClause(node);
}
@override
Object visitImportDirective(ImportDirective node) {
ImportElement importElement = node.element;
if (node.prefix != null) {
_checkForBuiltInIdentifierAsName(
node.prefix, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME);
}
if (importElement != null) {
_checkForImportDuplicateLibraryName(node, importElement);
_checkForImportInternalLibrary(node, importElement);
}
return super.visitImportDirective(node);
}
@override
Object visitIndexExpression(IndexExpression node) {
_checkForArgumentTypeNotAssignableForArgument(node.index);
return super.visitIndexExpression(node);
}
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
bool wasInConstInstanceCreation = _isInConstInstanceCreation;
_isInConstInstanceCreation = node.isConst;
try {
ConstructorName constructorName = node.constructorName;
TypeName typeName = constructorName.type;
DartType type = typeName.type;
if (type is InterfaceType) {
_checkForConstOrNewWithAbstractClass(node, typeName, type);
_checkForConstOrNewWithEnum(node, typeName, type);
if (_isInConstInstanceCreation) {
_checkForConstWithNonConst(node);
_checkForConstWithUndefinedConstructor(
node, constructorName, typeName);
if (!_options.strongMode) {
_checkForConstWithTypeParameters(typeName);
}
_checkForConstDeferredClass(node, constructorName, typeName);
} else {
_checkForNewWithUndefinedConstructor(node, constructorName, typeName);
}
}
_checkForImplicitDynamicType(typeName);
return super.visitInstanceCreationExpression(node);
} finally {
_isInConstInstanceCreation = wasInConstInstanceCreation;
}
}
@override
Object visitIntegerLiteral(IntegerLiteral node) {
_checkForOutOfRange(node);
return super.visitIntegerLiteral(node);
}
@override
Object visitIsExpression(IsExpression node) {
_checkForTypeAnnotationDeferredClass(node.type);
_checkForUseOfVoidResult(node.expression);
return super.visitIsExpression(node);
}
@override
Object visitListLiteral(ListLiteral node) {
TypeArgumentList typeArguments = node.typeArguments;
if (typeArguments != null) {
if (!_options.strongMode && node.isConst) {
NodeList<TypeAnnotation> arguments = typeArguments.arguments;
if (arguments.isNotEmpty) {
_checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST);
}
}
_checkForExpectedOneListTypeArgument(node, typeArguments);
}
_checkForImplicitDynamicTypedLiteral(node);
_checkForListElementTypeNotAssignable(node);
return super.visitListLiteral(node);
}
@override
Object visitMapLiteral(MapLiteral node) {
TypeArgumentList typeArguments = node.typeArguments;
if (typeArguments != null) {
NodeList<TypeAnnotation> arguments = typeArguments.arguments;
if (!_options.strongMode && arguments.isNotEmpty) {
if (node.isConst) {
_checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP);
}
}
_checkExpectedTwoMapTypeArguments(typeArguments);
}
_checkForImplicitDynamicTypedLiteral(node);
_checkForMapTypeNotAssignable(node);
_checkForNonConstMapAsExpressionStatement(node);
return super.visitMapLiteral(node);
}
@override
Object visitMethodDeclaration(MethodDeclaration node) {
ExecutableElement previousFunction = _enclosingFunction;
try {
_isInStaticMethod = node.isStatic;
_enclosingFunction = node.element;
SimpleIdentifier identifier = node.name;
String methodName = "";
if (identifier != null) {
methodName = identifier.name;
}
TypeAnnotation returnType = node.returnType;
if (node.isSetter || node.isGetter) {
_checkForMismatchedAccessorTypes(node, methodName);
}
if (node.isGetter) {
_checkForConflictingStaticGetterAndInstanceSetter(node);
} else if (node.isSetter) {
_checkForInvalidModifierOnBody(
node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
_checkForWrongNumberOfParametersForSetter(node.name, node.parameters);
_checkForNonVoidReturnTypeForSetter(returnType);
_checkForConflictingStaticSetterAndInstanceMember(node);
} else if (node.isOperator) {
_checkForOptionalParameterInOperator(node);
_checkForWrongNumberOfParametersForOperator(node);
_checkForNonVoidReturnTypeForOperator(node);
}
_checkForConcreteClassWithAbstractMember(node);
_checkForAllInvalidOverrideErrorCodesForMethod(node);
_checkForTypeAnnotationDeferredClass(returnType);
_checkForIllegalReturnType(returnType);
_checkForImplicitDynamicReturn(node, node.element);
_checkForMustCallSuper(node);
return super.visitMethodDeclaration(node);
} finally {
_enclosingFunction = previousFunction;
_isInStaticMethod = false;
}
}
@override
Object visitMethodInvocation(MethodInvocation node) {
Expression target = node.realTarget;
SimpleIdentifier methodName = node.methodName;
if (target != null) {
ClassElement typeReference = ElementResolver.getTypeReference(target);
_checkForStaticAccessToInstanceMember(typeReference, methodName);
_checkForInstanceAccessToStaticMember(typeReference, methodName);
} else {
_checkForUnqualifiedReferenceToNonLocalStaticMember(methodName);
}
_checkTypeArguments(node);
_checkForImplicitDynamicInvoke(node);
return super.visitMethodInvocation(node);
}
@override
Object visitNativeClause(NativeClause node) {
// TODO(brianwilkerson) Figure out the right rule for when 'native' is
// allowed.
if (!_isInSystemLibrary) {
_errorReporter.reportErrorForNode(
ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, node);
}
return super.visitNativeClause(node);
}
@override
Object visitNativeFunctionBody(NativeFunctionBody node) {
_checkForNativeFunctionBodyInNonSdkCode(node);
return super.visitNativeFunctionBody(node);
}
@override
Object visitPostfixExpression(PostfixExpression node) {
_checkForAssignmentToFinal(node.operand);
_checkForIntNotAssignable(node.operand);
return super.visitPostfixExpression(node);
}
@override
Object visitPrefixedIdentifier(PrefixedIdentifier node) {
if (node.parent is! Annotation) {
ClassElement typeReference =
ElementResolver.getTypeReference(node.prefix);
SimpleIdentifier name = node.identifier;
_checkForStaticAccessToInstanceMember(typeReference, name);
_checkForInstanceAccessToStaticMember(typeReference, name);
}
return super.visitPrefixedIdentifier(node);
}
@override
Object visitPrefixExpression(PrefixExpression node) {
TokenType operatorType = node.operator.type;
Expression operand = node.operand;
if (operatorType == TokenType.BANG) {
_checkForNonBoolNegationExpression(operand);
} else if (operatorType.isIncrementOperator) {
_checkForAssignmentToFinal(operand);
}
_checkForIntNotAssignable(operand);
return super.visitPrefixExpression(node);
}
@override
Object visitPropertyAccess(PropertyAccess node) {
ClassElement typeReference =
ElementResolver.getTypeReference(node.realTarget);
SimpleIdentifier propertyName = node.propertyName;
_checkForStaticAccessToInstanceMember(typeReference, propertyName);
_checkForInstanceAccessToStaticMember(typeReference, propertyName);
return super.visitPropertyAccess(node);
}
@override
Object visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
_isInConstructorInitializer = true;
try {
return super.visitRedirectingConstructorInvocation(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
Object visitRethrowExpression(RethrowExpression node) {
_checkForRethrowOutsideCatch(node);
return super.visitRethrowExpression(node);
}
@override
Object visitReturnStatement(ReturnStatement node) {
if (node.expression == null) {
_returnsWithout.add(node);
} else {
_returnsWith.add(node);
}
_checkForAllReturnStatementErrorCodes(node);
return super.visitReturnStatement(node);
}
@override
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
_checkForConstFormalParameter(node);
_checkForPrivateOptionalParameter(node);
_checkForTypeAnnotationDeferredClass(node.type);
// Checks for an implicit dynamic parameter type.
//
// We can skip other parameter kinds besides simple formal, because:
// - DefaultFormalParameter contains a simple one, so it gets here,
// - FieldFormalParameter error should be reported on the field,
// - FunctionTypedFormalParameter is a function type, not dynamic.
_checkForImplicitDynamicIdentifier(node, node.identifier);
return super.visitSimpleFormalParameter(node);
}
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
_checkForAmbiguousImport(node);
_checkForReferenceBeforeDeclaration(node);
_checkForImplicitThisReferenceInInitializer(node);
if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) {
_checkForUnqualifiedReferenceToNonLocalStaticMember(node);
}
return super.visitSimpleIdentifier(node);
}
@override
Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
_isInConstructorInitializer = true;
try {
return super.visitSuperConstructorInvocation(node);
} finally {
_isInConstructorInitializer = false;
}
}
@override
Object visitSwitchCase(SwitchCase node) {
_checkDuplicateDeclarationInStatements(node.statements);
return super.visitSwitchCase(node);
}
@override
Object visitSwitchDefault(SwitchDefault node) {
_checkDuplicateDeclarationInStatements(node.statements);
return super.visitSwitchDefault(node);
}
@override
Object visitSwitchStatement(SwitchStatement node) {
_checkForSwitchExpressionNotAssignable(node);
_checkForCaseBlocksNotTerminated(node);
_checkForMissingEnumConstantInSwitch(node);
return super.visitSwitchStatement(node);
}
@override
Object visitThisExpression(ThisExpression node) {
_checkForInvalidReferenceToThis(node);
return super.visitThisExpression(node);
}
@override
Object visitThrowExpression(ThrowExpression node) {
_checkForConstEvalThrowsException(node);
_checkForUseOfVoidResult(node.expression);
return super.visitThrowExpression(node);
}
@override
Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_checkForFinalNotInitialized(node.variables);
return super.visitTopLevelVariableDeclaration(node);
}
@override
Object visitTypeArgumentList(TypeArgumentList node) {
NodeList<TypeAnnotation> list = node.arguments;
for (TypeAnnotation type in list) {
_checkForTypeAnnotationDeferredClass(type);
}
return super.visitTypeArgumentList(node);
}
@override
Object visitTypeName(TypeName node) {
_checkForTypeArgumentNotMatchingBounds(node);
_checkForTypeParameterReferencedByStatic(node);
return super.visitTypeName(node);
}
@override
Object visitTypeParameter(TypeParameter node) {
_checkForBuiltInIdentifierAsName(node.name,
CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME);
_checkForTypeParameterSupertypeOfItsBound(node);
_checkForTypeAnnotationDeferredClass(node.bound);
_checkForImplicitDynamicType(node.bound);
_checkForGenericFunctionType(node.bound);
if (_options.strongMode) node.bound?.accept(_uninstantiatedBoundChecker);
return super.visitTypeParameter(node);
}
@override
Object visitTypeParameterList(TypeParameterList node) {
_checkDuplicateDefinitionInTypeParameterList(node);
return super.visitTypeParameterList(node);
}
@override
Object visitVariableDeclaration(VariableDeclaration node) {
SimpleIdentifier nameNode = node.name;
Expression initializerNode = node.initializer;
// do checks
_checkForInvalidAssignment(nameNode, initializerNode,
isDeclarationCast: true);
_checkForImplicitDynamicIdentifier(node, nameNode);
// visit name
nameNode.accept(this);
// visit initializer
String name = nameNode.name;
_namesForReferenceToDeclaredVariableInInitializer.add(name);
bool wasInInstanceVariableInitializer = _isInInstanceVariableInitializer;
_isInInstanceVariableInitializer = _isInInstanceVariableDeclaration;
try {
if (initializerNode != null) {
initializerNode.accept(this);
}
} finally {
_isInInstanceVariableInitializer = wasInInstanceVariableInitializer;
_namesForReferenceToDeclaredVariableInInitializer.remove(name);
}
// declare the variable
AstNode grandparent = node.parent.parent;
if (grandparent is! TopLevelVariableDeclaration &&
grandparent is! FieldDeclaration) {
VariableElement element = node.element;
if (element != null) {
_hiddenElements.declare(element);
}
}
// done
return null;
}
@override
Object visitVariableDeclarationList(VariableDeclarationList node) {
_checkForTypeAnnotationDeferredClass(node.type);
return super.visitVariableDeclarationList(node);
}
@override
Object visitVariableDeclarationStatement(VariableDeclarationStatement node) {
_checkForFinalNotInitialized(node.variables);
return super.visitVariableDeclarationStatement(node);
}
@override
Object visitWhileStatement(WhileStatement node) {
_checkForNonBoolCondition(node.condition);
return super.visitWhileStatement(node);
}
@override
Object visitWithClause(WithClause node) {
node.mixinTypes.forEach(_checkForImplicitDynamicType);
return super.visitWithClause(node);
}
@override
Object visitYieldStatement(YieldStatement node) {
if (_inGenerator) {
_checkForYieldOfInvalidType(node.expression, node.star != null);
} else {
CompileTimeErrorCode errorCode;
if (node.star != null) {
errorCode = CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR;
} else {
errorCode = CompileTimeErrorCode.YIELD_IN_NON_GENERATOR;
}
_errorReporter.reportErrorForNode(errorCode, node);
}
return super.visitYieldStatement(node);
}
/**
* Checks the class for problems with the superclass, mixins, or implemented
* interfaces.
*/
void _checkClassInheritance(
NamedCompilationUnitMember node,
TypeName superclass,
WithClause withClause,
ImplementsClause implementsClause) {
// Only check for all of the inheritance logic around clauses if there
// isn't an error code such as "Cannot extend double" already on the
// class.
if (!_checkForExtendsDisallowedClass(superclass) &&
!_checkForImplementsDisallowedClass(implementsClause) &&
!_checkForAllMixinErrorCodes(withClause)) {
_checkForImplicitDynamicType(superclass);
_checkForExtendsDeferredClass(superclass);
_checkForImplementsDeferredClass(implementsClause);
_checkForNonAbstractClassInheritsAbstractMember(node.name);
_checkForInconsistentMethodInheritance();
_checkForRecursiveInterfaceInheritance(_enclosingClass);
_checkForConflictingGetterAndMethod();
_checkForConflictingInstanceGetterAndSuperclassMember();
_checkImplementsSuperClass(implementsClause);
_checkImplementsFunctionWithoutCall(node.name);
_checkForMixinHasNoConstructors(node);
_checkMixinInference(node, withClause);
if (_options.strongMode) {
_checkForMixinWithConflictingPrivateMember(withClause, superclass);
if (!disableConflictingGenericsCheck) {
_checkForConflictingGenerics(node);
}
}
}
}
/**
* Given a list of [directives] that have the same prefix, generate an error
* if there is more than one import and any of those imports is deferred.
*
* See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX].
*/
void _checkDeferredPrefixCollision(List<ImportDirective> directives) {
int count = directives.length;
if (count > 1) {
for (int i = 0; i < count; i++) {
Token deferredToken = directives[i].deferredKeyword;
if (deferredToken != null) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken);
}
}
}
}
/**
* Check that there are no members with the same name.
*/
void _checkDuplicateClassMembers(ClassDeclaration node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
Set<String> visitedFields = new HashSet<String>();
for (ClassMember member in node.members) {
// We ignore constructors because they are checked in the method
// _checkForConflictingConstructorNameAndMember.
if (member is FieldDeclaration) {
for (VariableDeclaration field in member.fields.variables) {
SimpleIdentifier identifier = field.name;
_checkDuplicateIdentifier(definedNames, identifier);
String name = identifier.name;
if (!field.isFinal &&
!field.isConst &&
!visitedFields.contains(name)) {
_checkDuplicateIdentifier(definedNames, identifier,
implicitSetter: true);
}
visitedFields.add(name);
}
} else if (member is MethodDeclaration) {
_checkDuplicateIdentifier(definedNames, member.name);
}
}
}
/**
* Check that all of the parameters have unique names.
*/
void _checkDuplicateDeclarationInStatements(List<Statement> statements) {
Map<String, Element> definedNames = new HashMap<String, Element>();
for (Statement statement in statements) {
if (statement is VariableDeclarationStatement) {
for (VariableDeclaration variable in statement.variables.variables) {
_checkDuplicateIdentifier(definedNames, variable.name);
}
} else if (statement is FunctionDeclarationStatement) {
_checkDuplicateIdentifier(
definedNames, statement.functionDeclaration.name);
}
}
}
/**
* Check that the exception and stack trace parameters have different names.
*/
void _checkDuplicateDefinitionInCatchClause(CatchClause node) {
SimpleIdentifier exceptionParameter = node.exceptionParameter;
SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
if (exceptionParameter != null && stackTraceParameter != null) {
String exceptionName = exceptionParameter.name;
if (exceptionName == stackTraceParameter.name) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_DEFINITION,
stackTraceParameter,
[exceptionName]);
}
}
}
/**
* Check that all of the parameters have unique names.
*/
void _checkDuplicateDefinitionInParameterList(FormalParameterList node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
for (FormalParameter parameter in node.parameters) {
SimpleIdentifier identifier = parameter.identifier;
if (identifier != null) {
// The identifier can be null if this is a parameter list for a generic
// function type.
_checkDuplicateIdentifier(definedNames, identifier);
}
}
}
/**
* Check that all of the parameters have unique names.
*/
void _checkDuplicateDefinitionInTypeParameterList(TypeParameterList node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
for (TypeParameter parameter in node.typeParameters) {
_checkDuplicateIdentifier(definedNames, parameter.name);
}
}
/**
* Check that there are no members with the same name.
*/
void _checkDuplicateEnumMembers(EnumDeclaration node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
ClassElement element = node.element;
String indexName = 'index';
String valuesName = 'values';
definedNames[indexName] = element.getField(indexName);
definedNames[valuesName] = element.getField(valuesName);
for (EnumConstantDeclaration constant in node.constants) {
_checkDuplicateIdentifier(definedNames, constant.name);
}
}
/**
* Check whether the given [identifier] is already in the set of
* [definedNames], and produce an error if it is. If [implicitSetter] is
* `true`, then the identifier represents the definition of a setter.
*/
void _checkDuplicateIdentifier(
Map<String, Element> definedNames, SimpleIdentifier identifier,
{bool implicitSetter: false}) {
ErrorCode getError(Element previous, Element current) {
if (previous is MethodElement && current is PropertyAccessorElement) {
if (current.isGetter) {
return CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME;
}
} else if (previous is PropertyAccessorElement &&
current is MethodElement) {
if (previous.isGetter) {
return CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME;
}
} else if (previous is PrefixElement) {
return CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER;
}
return CompileTimeErrorCode.DUPLICATE_DEFINITION;
}
Element current = identifier.staticElement;
String name = identifier.name;
if (current is PropertyAccessorElement && current.isSetter) {
name += '=';
} else if (current is MethodElement && current.isOperator && name == '-') {
if (current.parameters.length == 0) {
name = 'unary-';
}
} else if (implicitSetter) {
name += '=';
}
Element previous = definedNames[name];
if (previous != null) {
_errorReporter
.reportErrorForNode(getError(previous, current), identifier, [name]);
} else {
definedNames[name] = identifier.staticElement;
}
}
/**
* Check that there are no members with the same name.
*/
void _checkDuplicateUnitMembers(CompilationUnit node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
void addWithoutChecking(CompilationUnitElement element) {
for (PropertyAccessorElement accessor in element.accessors) {
String name = accessor.name;
if (accessor.isSetter) {
name += '=';
}
definedNames[name] = accessor;
}
for (ClassElement type in element.enums) {
definedNames[type.name] = type;
}
for (FunctionElement function in element.functions) {
definedNames[function.name] = function;
}
for (FunctionTypeAliasElement alias in element.functionTypeAliases) {
definedNames[alias.name] = alias;
}
for (TopLevelVariableElement variable in element.topLevelVariables) {
definedNames[variable.name] = variable;
if (!variable.isFinal && !variable.isConst) {
definedNames[variable.name + '='] = variable;
}
}
for (ClassElement type in element.types) {
definedNames[type.name] = type;
}
}
for (ImportElement importElement in _currentLibrary.imports) {
PrefixElement prefix = importElement.prefix;
if (prefix != null) {
definedNames[prefix.name] = prefix;
}
}
CompilationUnitElement element = node.element;
if (element != _currentLibrary.definingCompilationUnit) {
addWithoutChecking(_currentLibrary.definingCompilationUnit);
for (CompilationUnitElement part in _currentLibrary.parts) {
if (element == part) {
break;
}
addWithoutChecking(part);
}
}
for (CompilationUnitMember member in node.declarations) {
if (member is NamedCompilationUnitMember) {
_checkDuplicateIdentifier(definedNames, member.name);
} else if (member is TopLevelVariableDeclaration) {
for (VariableDeclaration variable in member.variables.variables) {
_checkDuplicateIdentifier(definedNames, variable.name);
if (!variable.isFinal && !variable.isConst) {
_checkDuplicateIdentifier(definedNames, variable.name,
implicitSetter: true);
}
}
}
}
}
/**
* Check that the given list of variable declarations does not define multiple
* variables of the same name.
*/
void _checkDuplicateVariables(VariableDeclarationList node) {
Map<String, Element> definedNames = new HashMap<String, Element>();
for (VariableDeclaration variable in node.variables) {
_checkDuplicateIdentifier(definedNames, variable.name);
}
}
/**
* Verify that the given list of [typeArguments] contains exactly two
* elements.
*
* See [StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS].
*/
void _checkExpectedTwoMapTypeArguments(TypeArgumentList typeArguments) {
int num = typeArguments.arguments.length;
if (num != 2) {
_errorReporter.reportErrorForNode(
StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS,
typeArguments,
[num]);
}
}
/**
* Check that return statements without expressions are not in a generative
* constructor and the return type is not assignable to `null`; that is, we
* don't have `return;` if the enclosing method has a non-void containing
* return type.
*
* See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR],
* [StaticWarningCode.RETURN_WITHOUT_VALUE], and
* [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE].
*/
void _checkForAllEmptyReturnStatementErrorCodes(
ReturnStatement statement, DartType expectedReturnType) {
if (_inGenerator) {
return;
}
if (_options.strongMode) {
var returnType = _inAsync
? expectedReturnType.flattenFutures(_typeSystem)
: expectedReturnType;
if (returnType.isDynamic ||
returnType.isDartCoreNull ||
returnType.isVoid) {
return;
}
} else {
// TODO(leafp): Delete this non-strong path
if (_inAsync) {
if (expectedReturnType.isDynamic || expectedReturnType.isVoid) {
return;
}
if (expectedReturnType is InterfaceType &&
expectedReturnType.isDartAsyncFuture) {
DartType futureArgument = expectedReturnType.typeArguments[0];
if (futureArgument.isDynamic ||
futureArgument.isDartCoreNull ||
futureArgument.isVoid ||
futureArgument.isObject) {
return;
}
}
} else if (expectedReturnType.isDynamic || expectedReturnType.isVoid) {
return;
}
}
// If we reach here, this is an invalid return
_hasReturnWithoutValue = true;
_errorReporter.reportErrorForNode(
StaticWarningCode.RETURN_WITHOUT_VALUE, statement);
return;
}
/**
* Verify that the given [constructor] declaration does not violate any of the
* error codes relating to the initialization of fields in the enclosing
* class.
*
* See [_initialFieldElementsMap],
* [StaticWarningCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR], and
* [CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES].
*/
void _checkForAllFinalInitializedErrorCodes(
ConstructorDeclaration constructor) {
if (constructor.factoryKeyword != null ||
constructor.redirectedConstructor != null ||
constructor.externalKeyword != null) {
return;
}
// Ignore if native class.
if (_isInNativeClass) {
return;
}
Map<FieldElement, INIT_STATE> fieldElementsMap =
new HashMap<FieldElement, INIT_STATE>.from(_initialFieldElementsMap);
// Visit all of the field formal parameters
NodeList<FormalParameter> formalParameters =
constructor.parameters.parameters;
for (FormalParameter formalParameter in formalParameters) {
FormalParameter baseParameter(FormalParameter parameter) {
if (parameter is DefaultFormalParameter) {
return parameter.parameter;
}
return parameter;
}
FormalParameter parameter = baseParameter(formalParameter);
if (parameter is FieldFormalParameter) {
FieldElement fieldElement =
(parameter.element as FieldFormalParameterElementImpl).field;
INIT_STATE state = fieldElementsMap[fieldElement];
if (state == INIT_STATE.NOT_INIT) {
fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_FIELD_FORMAL;
} else if (state == INIT_STATE.INIT_IN_DECLARATION) {
if (fieldElement.isFinal || fieldElement.isConst) {
_errorReporter.reportErrorForNode(
StaticWarningCode
.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR,
formalParameter.identifier,
[fieldElement.displayName]);
}
} else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) {
if (fieldElement.isFinal || fieldElement.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES,
formalParameter.identifier,
[fieldElement.displayName]);
}
}
}
}
// Visit all of the initializers
NodeList<ConstructorInitializer> initializers = constructor.initializers;
for (ConstructorInitializer constructorInitializer in initializers) {
if (constructorInitializer is RedirectingConstructorInvocation) {
return;
}
if (constructorInitializer is ConstructorFieldInitializer) {
SimpleIdentifier fieldName = constructorInitializer.fieldName;
Element element = fieldName.staticElement;
if (element is FieldElement) {
INIT_STATE state = fieldElementsMap[element];
if (state == INIT_STATE.NOT_INIT) {
fieldElementsMap[element] = INIT_STATE.INIT_IN_INITIALIZERS;
} else if (state == INIT_STATE.INIT_IN_DECLARATION) {
if (element.isFinal || element.isConst) {
_errorReporter.reportErrorForNode(
StaticWarningCode
.FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION,
fieldName);
}
} else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode
.FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER,
fieldName);
} else if (state == INIT_STATE.INIT_IN_INITIALIZERS) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS,
fieldName,
[element.displayName]);
}
}
}
}
// Prepare a list of not initialized fields.
List<FieldElement> notInitFinalFields = <FieldElement>[];
fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
if (state == INIT_STATE.NOT_INIT) {
if (fieldElement.isFinal) {
notInitFinalFields.add(fieldElement);
}
}
});
// Visit all of the states in the map to ensure that none were never
// initialized.
fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
if (state == INIT_STATE.NOT_INIT) {
if (fieldElement.isConst) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONST_NOT_INITIALIZED,
constructor.returnType,
[fieldElement.name]);
}
}
});
if (notInitFinalFields.isNotEmpty) {
List<String> names = notInitFinalFields.map((item) => item.name).toList();
names.sort();
if (names.length == 1) {
_errorReporter.reportErrorForNode(
StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1,
constructor.returnType,
names);
} else if (names.length == 2) {
_errorReporter.reportErrorForNode(
StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2,
constructor.returnType,
names);
} else {
_errorReporter.reportErrorForNode(
StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS,
constructor.returnType,
[names[0], names[1], names.length - 2]);
}
}
}
/**
* Check the given [derivedElement] against override-error codes. The
* [baseElement] is the element that the executable element is
* overriding. The [parameters] is the parameters of the executable element.
* The [errorNameTarget] is the node to report problems on.
*
* See [CompileTimeErrorCode.INVALID_OVERRIDE_REQUIRED],
* [CompileTimeErrorCode.INVALID_OVERRIDE_POSITIONAL],
* [CompileTimeErrorCode.INVALID_OVERRIDE_NAMED],
* [StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE],
* [StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE],
* [StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE],
* [StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE],
* [StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE],
* [StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE], and
* [StaticWarningCode.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES].
*/
bool _checkForAllInvalidOverrideErrorCodes(
ExecutableElement derivedElement,
ExecutableElement baseElement,
List<ParameterElement> parameters,
List<AstNode> parameterLocations,
SimpleIdentifier errorNameTarget) {
if (_options.strongMode) {
return false; // strong mode already checked for this
}
bool isGetter = false;
bool isSetter = false;
if (derivedElement is PropertyAccessorElement) {
isGetter = derivedElement.isGetter;
isSetter = derivedElement.isSetter;
}
String executableElementName = derivedElement.name;
FunctionType derivedFT = derivedElement.type;
FunctionType baseFT = baseElement.type;
InterfaceType enclosingType = _enclosingClass.type;
baseFT = _inheritanceManager.substituteTypeArgumentsInMemberFromInheritance(
baseFT, executableElementName, enclosingType);
if (derivedFT == null || baseFT == null) {
return false;
}
// Handle generic function type parameters.
// TODO(jmesserly): this duplicates some code in isSubtypeOf and most of
// _isGenericFunctionSubtypeOf. Ideally, we'd let TypeSystem produce
// an error message once it's ready to "return false".
if (!derivedFT.typeFormals.isEmpty) {
if (baseFT.typeFormals.isEmpty) {
derivedFT = _typeSystem.instantiateToBounds(derivedFT);
} else {
List<TypeParameterElement> params1 = derivedFT.typeFormals;
List<TypeParameterElement> params2 = baseFT.typeFormals;
int count = params1.length;
if (params2.length != count) {
_errorReporter.reportErrorForNode(
HintCode.INVALID_METHOD_OVERRIDE_TYPE_PARAMETERS,
errorNameTarget, [
count,
params2.length,
baseElement.enclosingElement.displayName
]);
return true;
}
// We build up a substitution matching up the type parameters
// from the two types, {variablesFresh/variables1} and
// {variablesFresh/variables2}
List<DartType> variables1 = new List<DartType>();
List<DartType> variables2 = new List<DartType>();
List<DartType> variablesFresh = new List<DartType>();
for (int i = 0; i < count; i++) {
TypeParameterElement p1 = params1[i];
TypeParameterElement p2 = params2[i];
TypeParameterElementImpl pFresh =
new TypeParameterElementImpl(p1.name, -1);
DartType variable1 = p1.type;
DartType variable2 = p2.type;
DartType variableFresh = new TypeParameterTypeImpl(pFresh);
variables1.add(variable1);
variables2.add(variable2);
variablesFresh.add(variableFresh);
DartType bound1 = p1.bound ?? DynamicTypeImpl.instance;
DartType bound2 = p2.bound ?? DynamicTypeImpl.instance;
bound1 = bound1.substitute2(variablesFresh, variables1);
bound2 = bound2.substitute2(variablesFresh, variables2);
pFresh.bound = bound2;
if (!_typeSystem.isSubtypeOf(bound2, bound1)) {
_errorReporter.reportErrorForNode(
HintCode.INVALID_METHOD_OVERRIDE_TYPE_PARAMETER_BOUND,
errorNameTarget, [
p1.displayName,
p1.bound,
p2.displayName,
p2.bound,
baseElement.enclosingElement.displayName
]);
return true;
}
}
// Proceed with the rest of the checks, using instantiated types.
derivedFT = derivedFT.instantiate(variablesFresh);
baseFT = baseFT.instantiate(variablesFresh);
}
}
DartType derivedFTReturnType = derivedFT.returnType;
DartType baseFTReturnType = baseFT.returnType;
List<DartType> derivedNormalPT = derivedFT.normalParameterTypes;
List<DartType> baseNormalPT = baseFT.normalParameterTypes;
List<DartType> derivedPositionalPT = derivedFT.optionalParameterTypes;
List<DartType> basePositionalPT = baseFT.optionalParameterTypes;
Map<String, DartType> derivedNamedPT = derivedFT.namedParameterTypes;
Map<String, DartType> baseNamedPT = baseFT.namedParameterTypes;
// CTEC.INVALID_OVERRIDE_REQUIRED, CTEC.INVALID_OVERRIDE_POSITIONAL and
// CTEC.INVALID_OVERRIDE_NAMED
if (derivedNormalPT.length > baseNormalPT.length) {
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_REQUIRED, errorNameTarget, [
baseNormalPT.length,
baseElement,
baseElement.enclosingElement.displayName
]);
return true;
}
if (derivedNormalPT.length + derivedPositionalPT.length <
basePositionalPT.length + baseNormalPT.length) {
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_POSITIONAL, errorNameTarget, [
basePositionalPT.length + baseNormalPT.length,
baseElement,
baseElement.enclosingElement.displayName
]);
return true;
}
// For each named parameter in the overridden method, verify that there is
// the same name in the overriding method.
for (String overriddenParamName in baseNamedPT.keys) {
if (!derivedNamedPT.containsKey(overriddenParamName)) {
// The overridden method expected the overriding method to have
// overridingParamName, but it does not.
_errorReporter.reportErrorForNode(
StaticWarningCode.INVALID_OVERRIDE_NAMED, errorNameTarget, [
overriddenParamName,
baseElement,
baseElement.enclosingElement.displayName
]);
return true;
}
}
// SWC.INVALID_METHOD_OVERRIDE_RETURN_TYPE
if (baseFTReturnType != VoidTypeImpl.instance &&
!_typeSystem.isAssignableTo(derivedFTReturnType, baseFTReturnType)) {
_errorReporter.reportTypeErrorForNode(
!isGetter
? StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE
: StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE,
errorNameTarget,
[
derivedFTReturnType,
baseFTReturnType,
baseElement.enclosingElement.displayName
]);
return true;
}
// SWC.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE
if (parameterLocations == null) {
return false;
}
int parameterIndex = 0;
for (int i = 0; i < derivedNormalPT.length; i++) {
if (!_typeSystem.isAssignableTo(baseNormalPT[i], derivedNormalPT[i])) {
_errorReporter.reportTypeErrorForNode(
!isSetter
? StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE
: StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE,
parameterLocations[parameterIndex],
[
derivedNormalPT[i],
baseNormalPT[i],
baseElement.enclosingElement.displayName
]);
return true;
}
parameterIndex++;
}
// SWC.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE
for (int i = 0; i < basePositionalPT.length; i++) {
if (!_typeSystem.isAssignableTo(
basePositionalPT[i], derivedPositionalPT[i])) {
_errorReporter.reportTypeErrorForNode(
StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE,
parameterLocations[parameterIndex], [
derivedPositionalPT[i],
basePositionalPT[i],
baseElement.enclosingElement.displayName
]);
return true;
}
parameterIndex++;
}
// SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE &
// SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
for (String overriddenName in baseNamedPT.keys) {
DartType derivedType = derivedNamedPT[overriddenName];
if (derivedType == null) {
// Error, this is never reached- INVALID_OVERRIDE_NAMED would have been
// created above if this could be reached.
continue;
}
DartType baseType = baseNamedPT[overriddenName];
if (!_typeSystem.isAssignableTo(baseType, derivedType)) {
// lookup the parameter for the error to select
ParameterElement parameterToSelect = null;
AstNode parameterLocationToSelect = null;
for (int i = 0; i < parameters.length; i++) {
ParameterElement parameter = parameters[i];
if (parameter.isNamed && overriddenName == parameter.name) {
parameterToSelect = parameter;
parameterLocationToSelect = parameterLocations[i];
break;
}
}
if (parameterToSelect != null) {
_errorReporter.reportTypeErrorForNode(
StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE,
parameterLocationToSelect, [
derivedType,
baseType,
baseElement.enclosingElement.displayName
]);
return true;
}
}
}
// SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
//
// Create three lists: a list of the optional parameter ASTs
// (FormalParameters), a list of the optional parameters elements from our
// method, and finally a list of the optional parameter elements from the
// method we are overriding.
//
bool foundError = false;
List<AstNode> formalParameters = new List<AstNode>();
List<ParameterElementImpl> parameterElts = new List<ParameterElementImpl>();
List<ParameterElementImpl> overriddenParameterElts =
new List<ParameterElementImpl>();
List<ParameterElement> overriddenPEs = baseElement.parameters;
for (int i = 0; i < parameters.length; i++) {
ParameterElement parameter = parameters[i];
if (parameter.isOptional) {
formalParameters.add(parameterLocations[i]);
parameterElts.add(parameter as ParameterElementImpl);
}
}
for (ParameterElement parameterElt in overriddenPEs) {
if (parameterElt.isOptional) {
if (parameterElt is ParameterElementImpl) {
overriddenParameterElts.add(parameterElt);
}
}
}
//
// Next compare the list of optional parameter elements to the list of
// overridden optional parameter elements.
//
if (parameterElts.length > 0) {
if (parameterElts[0].isNamed) {
// Named parameters, consider the names when matching the parameterElts
// to the overriddenParameterElts
for (int i = 0; i < parameterElts.length; i++) {
ParameterElementImpl parameterElt = parameterElts[i];
EvaluationResultImpl result = parameterElt.evaluationResult;
// TODO (jwren) Ignore Object types, see Dart bug 11287
if (_isUserDefinedObject(result)) {
continue;
}
String parameterName = parameterElt.name;
for (int j = 0; j < overriddenParameterElts.length; j++) {
ParameterElementImpl overriddenParameterElt =
overriddenParameterElts[j];
if (overriddenParameterElt.initializer == null) {
// There is no warning if the overridden parameter has an
// implicit default.
continue;
}
String overriddenParameterName = overriddenParameterElt.name;
if (parameterName != null &&
parameterName == overriddenParameterName) {
EvaluationResultImpl overriddenResult =
overriddenParameterElt.evaluationResult;
if (_isUserDefinedObject(overriddenResult)) {
break;
}
if (!result.equalValues(_typeProvider, overriddenResult)) {
_errorReporter.reportErrorForNode(
StaticWarningCode
.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_NAMED,
formalParameters[i],
[
baseElement.enclosingElement.displayName,
baseElement.displayName,
parameterName
]);
foundError = true;
}
}
}
}
} else {
// Positional parameters, consider the positions when matching the
// parameterElts to the overriddenParameterElts
for (int i = 0;
i < parameterElts.length && i < overriddenParameterElts.length;
i++) {
ParameterElementImpl parameterElt = parameterElts[i];
EvaluationResultImpl result = parameterElt.evaluationResult;
// TODO (jwren) Ignore Object types, see Dart bug 11287
if (_isUserDefinedObject(result)) {
continue;
}
ParameterElementImpl overriddenParameterElt =
overriddenParameterElts[i];
if (overriddenParameterElt.initializer == null) {
// There is no warning if the overridden parameter has an implicit
// default.
continue;
}
EvaluationResultImpl overriddenResult =
overriddenParameterElt.evaluationResult;
if (_isUserDefinedObject(overriddenResult)) {
continue;
}
if (!result.equalValues(_typeProvider, overriddenResult)) {
_errorReporter.reportErrorForNode(
StaticWarningCode
.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_POSITIONAL,
formalParameters[i],
[
baseElement.enclosingElement.displayName,
baseElement.displayName
]);
foundError = true;
}
}
}
}
return foundError;
}
/**
* Check the given [executableElement] against override-error codes. This
* method computes the given executableElement is overriding and calls
* [_checkForAllInvalidOverrideErrorCodes] when the [InheritanceManager]
* returns a [MultiplyInheritedExecutableElement], this method loops through
* the list in the [MultiplyInheritedExecutableElement]. The [parameters] are
* the parameters of the executable element. The [errorNameTarget] is the node
* to report problems on.
*/
void _checkForAllInvalidOverrideErrorCodesForExecutable(
ExecutableElement executableElement,
List<ParameterElement> parameters,
List<AstNode> parameterLocations,
SimpleIdentifier errorNameTarget) {
assert(!_options.strongMode); // strong mode already checked for these
//
// Compute the overridden executable from the InheritanceManager
//
List<ExecutableElement> overriddenExecutables = _inheritanceManager
.lookupOverrides(_enclosingClass, executableElement.name);
for (ExecutableElement overriddenElement in overriddenExecutables) {
if (_checkForAllInvalidOverrideErrorCodes(executableElement,
overriddenElement, parameters, parameterLocations, errorNameTarget)) {
return;
}
}
}
/**
* Check the given field [declaration] against override-error codes.
*
* See [_checkForAllInvalidOverrideErrorCodes].
*/
void _checkForAllInvalidOverrideErrorCodesForField(
FieldDeclaration declaration) {
if (_options.strongMode) {
return; // strong mode already checked for this
}
if (_enclosingClass == null || declaration.isStatic) {
return;
}
VariableDeclarationList fields = declaration.fields;
for (VariableDeclaration field in fields.variables) {
FieldElement element = field.element as FieldElement;
if (element == null) {
continue;
}
PropertyAccessorElement getter = element.getter;
PropertyAccessorElement setter = element.setter;
SimpleIdentifier fieldName = field.name;
if (getter != null) {
_checkForAllInvalidOverrideErrorCodesForExecutable(
getter, ParameterElement.EMPTY_LIST, AstNode.EMPTY_LIST, fieldName);
}
if (setter != null) {
_checkForAllInvalidOverrideErrorCodesForExecutable(
setter, setter.parameters, <AstNode>[fieldName], fieldName);
}
}
}
/**
* Check the given [method] declaration against override-error codes.
*
* See [_checkForAllInvalidOverrideErrorCodes].
*/
void _checkForAllInvalidOverrideErrorCodesForMethod(
MethodDeclaration method) {
if (_options.strongMode) {
return; // strong mode already checked for this
}
if (_enclosingClass == null ||
method.isStatic ||
method.body is NativeFunctionBody) {
return;
}
ExecutableElement executableElement = method.element;
if (executableElement == null) {
return;
}
SimpleIdentifier methodName = method.name;
if (methodName.isSynthetic) {
return;
}
FormalParameterList formalParameterList = method.parameters;
NodeList<FormalParameter> parameterList = formalParameterList?.parameters;
List<AstNode> parameters =
parameterList != null ? new List.from(parameterList) : null;
_checkForAllInvalidOverrideErrorCodesForExecutable(executableElement,
executableElement.parameters, parameters, methodName);
}
/**
* Verify that all classes of the given [withClause] are valid.
*
* See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR],
* [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT], and
* [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER].
*/
bool _checkForAllMixinErrorCodes(WithClause withClause) {
if (withClause == null) {
return false;
}
bool problemReported = false;
for (TypeName mixinName in withClause.mixinTypes) {
DartType mixinType = mixinName.type;
if (mixinType is InterfaceType) {
if (_checkForExtendsOrImplementsDisallowedClass(
mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) {
problemReported = true;
} else {
ClassElement mixinElement = mixinType.element;
if (_checkForExtendsOrImplementsDeferredClass(
mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) {
problemReported = true;
}
if (_checkForMixinDeclaresConstructor(mixinName, mixinElement)) {
problemReported = true;
}
if (!enableSuperMixins &&
_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) {
problemReported = true;
}
if (_checkForMixinReferencesSuper(mixinName, mixinElement)) {
problemReported = true;
}
}
}
}
return problemReported;
}
/**
* Check for errors related to the redirected constructors.
*
* See [StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE],
* [StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE], and
* [StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR].
*/
void _checkForAllRedirectConstructorErrorCodes(
ConstructorDeclaration declaration) {
// Prepare redirected constructor node
ConstructorName redirectedConstructor = declaration.redirectedConstructor;
if (redirectedConstructor == null) {
return;
}
// Prepare redirected constructor type
ConstructorElement redirectedElement = redirectedConstructor.staticElement;
if (redirectedElement == null) {
// If the element is null, we check for the
// REDIRECT_TO_MISSING_CONSTRUCTOR case
TypeName constructorTypeName = redirectedConstructor.type;
DartType redirectedType = constructorTypeName.type;
if (redirectedType != null &&
redirectedType.element != null &&
!redirectedType.isDynamic) {
// Prepare the constructor name
String constructorStrName = constructorTypeName.name.name;
if (redirectedConstructor.name != null) {
constructorStrName += ".${redirectedConstructor.name.name}";
}
ErrorCode errorCode = (declaration.constKeyword != null
? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR
: StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR);
_errorReporter.reportErrorForNode(errorCode, redirectedConstructor,
[constructorStrName, redirectedType.displayName]);
}
return;
}
FunctionType redirectedType = redirectedElement.type;
DartType redirectedReturnType = redirectedType.returnType;
// Report specific problem when return type is incompatible
FunctionType constructorType =
resolutionMap.elementDeclaredByConstructorDeclaration(declaration).type;
DartType constructorReturnType = constructorType.returnType;
if (!_typeSystem.isAssignableTo(
redirectedReturnType, constructorReturnType)) {
_errorReporter.reportErrorForNode(
StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE,
redirectedConstructor,
[redirectedReturnType, constructorReturnType]);
return;
} else if (!_typeSystem.isSubtypeOf(redirectedType, constructorType)) {
// Check parameters.
_errorReporter.reportErrorForNode(
StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE,
redirectedConstructor,
[redirectedType, constructorType]);
}
}
/**
* Check that the return [statement] of the form <i>return e;</i> is not in a
* generative constructor.
*
* Check that return statements without expressions are not in a generative
* constructor and the return type is not assignable to `null`; that is, we
* don't have `return;` if the enclosing method has a non-void containing
* return type.
*
* Check that the return type matches the type of the declared return type in
* the enclosing method or function.
*
* See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR],
* [StaticWarningCode.RETURN_WITHOUT_VALUE], and
* [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE].
*/
void _checkForAllReturnStatementErrorCodes(ReturnStatement statement) {
FunctionType functionType = _enclosingFunction?.type;
DartType expectedReturnType = functionType == null
? DynamicTypeImpl.instance
: functionType.returnType;
Expression returnExpression = statement.expression;
// RETURN_IN_GENERATIVE_CONSTRUCTOR
bool isGenerativeConstructor(ExecutableElement element) =>
element is ConstructorElement && !element.isFactory;
if (isGenerativeConstructor(_enclosingFunction)) {
if (returnExpression == null) {
return;
}
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR,
returnExpression);
return;
}
// RETURN_WITHOUT_VALUE
if (returnExpression == null) {
_checkForAllEmptyReturnStatementErrorCodes(statement, expectedReturnType);
return;
} else if (_inGenerator) {
// RETURN_IN_GENERATOR
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.RETURN_IN_GENERATOR,
statement,
[_inAsync ? "async*" : "sync*"]);
return;
}
_checkForReturnOfInvalidType(returnExpression, expectedReturnType);
}
/**
* Verify that the export namespace of the given export [directive] does not
* export any name already exported by another export directive. The
* [exportElement] is the [ExportElement] retrieved from the node. If the
* element in the node was `null`, then this method is not called. The
* [exportedLibrary] is the library element containing the exported element.
*
* See [CompileTimeErrorCode.AMBIGUOUS_EXPORT].
*/
void _checkForAmbiguousExport(ExportDirective directive,
ExportElement exportElement, LibraryElement exportedLibrary) {
if (exportedLibrary == null) {
return;
}
// check exported names
Namespace namespace =
new NamespaceBuilder().createExportNamespaceForDirective(exportElement);
Map<String, Element> definedNames = namespace.definedNames;
for (String name in definedNames.keys) {
Element element = definedNames[name];
Element prevElement = _exportedElements[name];
if (element != null && prevElement != null && prevElement != element) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.AMBIGUOUS_EXPORT, directive, [
name,
prevElement.library.definingCompilationUnit.displayName,
element.library.definingCompilationUnit.displayName
]);
return;
} else {
_exportedElements[name] = element;
}
}
}
/**
* Check the given node to see whether it was ambiguous because the name was
* imported from two or more imports.
*/
void _checkForAmbiguousImport(SimpleIdentifier node) {
Element element = node.staticElement;
if (element is MultiplyDefinedElementImpl) {
String name = element.displayName;
List<Element> conflictingMembers = element.conflictingElements;
int count = conflictingMembers.length;
List<String> libraryNames = new List<String>(count);
for (int i = 0; i < count; i++) {
libraryNames[i] = _getLibraryName(conflictingMembers[i]);
}
libraryNames.sort();
_errorReporter.reportErrorForNode(StaticWarningCode.AMBIGUOUS_IMPORT,
node, [name, StringUtilities.printListOfQuotedNames(libraryNames)]);
} else if (element != null) {
List<Element> sdkElements =
node.getProperty(LibraryImportScope.conflictingSdkElements);
if (sdkElements != null) {
_errorReporter.reportErrorForNode(
StaticWarningCode.CONFLICTING_DART_IMPORT, node, [
element.displayName,
_getLibraryName(sdkElements[0]),
_getLibraryName(element)
]);
}
}
}
/**
* Verify that the given [expression] can be assigned to its corresponding
* parameters. The [expectedStaticType] is the expected static type of the
* parameter. The [actualStaticType] is the actual static type of the
* argument.
*
* This method corresponds to
* [BestPracticesVerifier.checkForArgumentTypeNotAssignable].
*
* See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
* [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE],
* [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
* [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE], and
* [StaticWarningCode.USE_OF_VOID_RESULT].
*/
void _checkForArgumentTypeNotAssignable(
Expression expression,
DartType expectedStaticType,
DartType actualStaticType,
ErrorCode errorCode) {
// Warning case: test static type information
if (actualStaticType != null && expectedStaticType != null) {
if (!expectedStaticType.isVoid && _checkForUseOfVoidResult(expression)) {
return;
}
_checkForAssignableExpressionAtType(
expression, actualStaticType, expectedStaticType, errorCode);
}
}
/**
* Verify that the given [argument] can be assigned to its corresponding
* parameter.
*
* This method corresponds to
* [BestPracticesVerifier.checkForArgumentTypeNotAssignableForArgument].
*
* See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
*/
void _checkForArgumentTypeNotAssignableForArgument(Expression argument) {
if (argument == null) {
return;
}
ParameterElement staticParameterElement = argument.staticParameterElement;
DartType staticParameterType = staticParameterElement?.type;
_checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
staticParameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
}
/**
* Verify that the given [expression] can be assigned to its corresponding
* parameters. The [expectedStaticType] is the expected static type.
*
* This method corresponds to
* [BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes].
*
* See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
* [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE],
* [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE],
* [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
* [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE].
*/
void _checkForArgumentTypeNotAssignableWithExpectedTypes(
Expression expression, DartType expectedStaticType, ErrorCode errorCode) {
_checkForArgumentTypeNotAssignable(
expression, expectedStaticType, getStaticType(expression), errorCode);
}
/**
* Verify that the arguments in the given [argumentList] can be assigned to
* their corresponding parameters.
*
* This method corresponds to
* [BestPracticesVerifier.checkForArgumentTypesNotAssignableInList].
*
* See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
*/
void _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) {
if (argumentList == null) {
return;
}
for (Expression argument in argumentList.arguments) {
_checkForArgumentTypeNotAssignableForArgument(argument);
}
}
/**
* Check that the static type of the given expression is assignable to the
* given type. If it isn't, report an error with the given error code. The
* [type] is the type that the expression must be assignable to. The
* [errorCode] is the error code to be reported. The [arguments] are the
* arguments to pass in when creating the error.
*/
void _checkForAssignability(Expression expression, InterfaceType type,
ErrorCode errorCode, List<Object> arguments) {
if (expression == null) {
return;
}
DartType expressionType = expression.staticType;
if (expressionType == null) {
return;
}
if (_expressionIsAssignableAtType(expression, expressionType, type)) {
return;
}
_errorReporter.reportErrorForNode(errorCode, expression, arguments);
}
bool _checkForAssignableExpression(
Expression expression, DartType expectedStaticType, ErrorCode errorCode,
{bool isDeclarationCast = false}) {
DartType actualStaticType = getStaticType(expression);
return actualStaticType != null &&
_checkForAssignableExpressionAtType(
expression, actualStaticType, expectedStaticType, errorCode,
isDeclarationCast: isDeclarationCast);
}
bool _checkForAssignableExpressionAtType(
Expression expression,
DartType actualStaticType,
DartType expectedStaticType,
ErrorCode errorCode,
{bool isDeclarationCast = false}) {
if (!_expressionIsAssignableAtType(
expression, actualStaticType, expectedStaticType,
isDeclarationCast: isDeclarationCast)) {
_errorReporter.reportTypeErrorForNode(
errorCode, expression, [actualStaticType, expectedStaticType]);
return false;
}
return true;
}
/**
* Verify that the given [expression] is not final.
*
* See [StaticWarningCode.ASSIGNMENT_TO_CONST],
* [StaticWarningCode.ASSIGNMENT_TO_FINAL], and
* [StaticWarningCode.ASSIGNMENT_TO_METHOD].
*/
void _checkForAssignmentToFinal(Expression expression) {
// prepare element
Element element = null;
AstNode highlightedNode = expression;
if (expression is Identifier) {
element = expression.staticElement;
if (expression is PrefixedIdentifier) {
highlightedNode = expression.identifier;
}
} else if (expression is PropertyAccess) {
element = expression.propertyName.staticElement;
highlightedNode = expression.propertyName;
}
// check if element is assignable
Element toVariable(Element element) {
return element is PropertyAccessorElement ? element.variable : element;
}
element = toVariable(element);
if (element is VariableElement) {
if (element.isConst) {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_CONST, expression);
} else if (element.isFinal) {
if (element is FieldElementImpl) {
if (element.setter == null && element.isSynthetic) {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_FINAL_NO_SETTER,
highlightedNode,
[element.name, element.enclosingElement.displayName]);
} else {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_FINAL,
highlightedNode,
[element.name]);
}
return;
}
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_FINAL_LOCAL,
highlightedNode,
[element.name]);
}
} else if (element is FunctionElement) {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_FUNCTION, expression);
} else if (element is MethodElement) {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_METHOD, expression);
} else if (element is ClassElement ||
element is FunctionTypeAliasElement ||
element is TypeParameterElement) {
_errorReporter.reportErrorForNode(
StaticWarningCode.ASSIGNMENT_TO_TYPE, expression);
}
}
/**
* Verifies that the class is not named `Function` and that it doesn't
* extends/implements/mixes in `Function`.
*/
void _checkForBadFunctionUse(ClassDeclaration node) {
ExtendsClause extendsClause = node.extendsClause;
WithClause withClause = node.withClause;
if (node.name.name == "Function") {
_errorReporter.reportErrorForNode(
HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION, node.name);
}
if (extendsClause != null) {
InterfaceType superclassType = _enclosingClass.supertype;
ClassElement superclassElement = superclassType?.element;
if (superclassElement != null && superclassElement.name == "Function") {
_errorReporter.reportErrorForNode(
HintCode.DEPRECATED_EXTENDS_FUNCTION, extendsClause.superclass);
}
}
if (withClause != null) {
for (TypeName type in withClause.mixinTypes) {
Element mixinElement = type.name.staticElement;
if (mixinElement != null && mixinElement.name == "Function") {
_errorReporter.reportErrorForNode(
HintCode.DEPRECATED_MIXIN_FUNCTION, type);
}
}
}
}
/**
* Verify that the given [identifier] is not a keyword, and generates the
* given [errorCode] on the identifier if it is a keyword.
*
* See [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME],
* [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME], and
* [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME].
*/
void _checkForBuiltInIdentifierAsName(
SimpleIdentifier identifier, ErrorCode errorCode) {
Token token = identifier.token;
if (token.type.isKeyword && token.keyword?.isPseudo != true) {
_errorReporter
.reportErrorForNode(errorCode, identifier, [identifier.name]);
}
}
/**
* Verify that the given [switchCase] is terminated with 'break', 'continue',
* 'return' or 'throw'.
*
* see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
*/
void _checkForCaseBlockNotTerminated(SwitchCase switchCase) {
NodeList<Statement> statements = switchCase.statements;
if (statements.isEmpty) {
// fall-through without statements at all
AstNode parent = switchCase.parent;
if (parent is SwitchStatement) {
NodeList<SwitchMember> members = parent.members;
int index = members.indexOf(switchCase);
if (index != -1 && index < members.length - 1) {
return;
}
}
// no other switch member after this one
} else {
Statement statement = statements.last;
if (statement is Block && statement.statements.isNotEmpty) {
Block block = statement;
statement = block.statements.last;
}
// terminated with statement
if (statement is BreakStatement ||
statement is ContinueStatement ||
statement is ReturnStatement) {
return;
}
// terminated with 'throw' expression
if (statement is ExpressionStatement) {
Expression expression = statement.expression;
if (expression is ThrowExpression || expression is RethrowExpression) {
return;
}
}
}
_errorReporter.reportErrorForToken(
StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword);
}
/**
* Verify that the switch cases in the given switch [statement] are terminated
* with 'break', 'continue', 'rethrow', 'return' or 'throw'.
*
* See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
*/
void _checkForCaseBlocksNotTerminated(SwitchStatement statement) {
NodeList<SwitchMember> members = statement.members;
int lastMember = members.length - 1;
for (int i = 0; i < lastMember; i++) {
SwitchMember member = members[i];
if (member is SwitchCase) {
_checkForCaseBlockNotTerminated(member);
}
}
}
/**
* Verify that the given [method] declaration is abstract only if the
* enclosing class is also abstract.
*
* See [StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER].
*/
void _checkForConcreteClassWithAbstractMember(MethodDeclaration method) {
if (method.isAbstract &&
_enclosingClass != null &&
!_enclosingClass.isAbstract) {
SimpleIdentifier nameNode = method.name;
String memberName = nameNode.name;
ExecutableElement overriddenMember;
if (method.isGetter) {
overriddenMember = _enclosingClass.lookUpInheritedConcreteGetter(
memberName, _currentLibrary);
} else if (method.isSetter) {
overriddenMember = _enclosingClass.lookUpInheritedConcreteSetter(
memberName, _currentLibrary);
} else {
overriddenMember = _enclosingClass.lookUpInheritedConcreteMethod(
memberName, _currentLibrary);
}
if (overriddenMember == null && !_enclosingClass.hasNoSuchMethod) {
_errorReporter.reportErrorForNode(
StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER,
nameNode,
[memberName, _enclosingClass.displayName]);
}
}
}
/**
* Verify all possible conflicts of the given [constructor]'s name with other
* constructors and members of the same class. The [constructorElement] is the
* constructor's element.
*
* See [CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT],
* [CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME],
* [CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD], and
* [CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD].
*/
void _checkForConflictingConstructorNameAndMember(
ConstructorDeclaration constructor,
ConstructorElement constructorElement) {
SimpleIdentifier constructorName = constructor.name;
String name = constructorElement.name;
ClassElement classElement = constructorElement.enclosingElement;
// constructors
List<ConstructorElement> constructors = classElement.constructors;
for (ConstructorElement otherConstructor in constructors) {
if (identical(otherConstructor, constructorElement)) {
continue;
}
if (name == otherConstructor.name) {
if (name == null || name.length == 0) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT, constructor);
} else {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME,
constructor,
[name]);
}
return;
}
}
// conflict with class member
if (constructorName != null &&
constructorElement != null &&
!constructorName.isSynthetic) {
FieldElement field = classElement.getField(name);
if (field != null && field.getter != null) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD,
constructor,
[name]);
} else if (classElement.getMethod(name) != null) {
// methods
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD,
constructor,
[name]);
}
}
}
void _checkForConflictingGenerics(NamedCompilationUnitMember node) {
var visitedClasses = <ClassElement>[];
var interfaces = <ClassElement, InterfaceType>{};
void visit(InterfaceType type) {
if (type == null) return;
var element = type.element;
if (visitedClasses.contains(element)) return;
visitedClasses.add(element);
if (element.typeParameters.isNotEmpty) {
var oldType = interfaces[element];
if (oldType == null) {
interfaces[element] = type;
} else if (oldType != type) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES,
node,
[_enclosingClass.name, oldType, type]);
}
}
visit(type.superclass);
type.mixins.forEach(visit);
type.interfaces.forEach(visit);
visitedClasses.removeLast();
}
visit(_enclosingClass.type);
}
/**
* Verify that the [_enclosingClass] does not have a method and getter pair
* with the same name on, via inheritance.
*
* See [CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD], and
* [CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER].
*/
void _checkForConflictingGetterAndMethod() {
if (_enclosingClass == null) {
return;
}
// method declared in the enclosing class vs. inherited getter
for (MethodElement method in _enclosingClass.methods) {
String name = method.name;
// find inherited property accessor (and can be only getter)
ExecutableElement inherited =
_inheritanceManager.lookupInheritance(_enclosingClass, name);
if (inherited is! PropertyAccessorElement) {
continue;
}
_errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD, method, [
_enclosingClass.displayName,
inherited.enclosingElement.displayName,
name
]);
}
// getter declared in the enclosing class vs. inherited method
for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
if (!accessor.isGetter) {
continue;
}
String name = accessor.name;
// find inherited method
ExecutableElement inherited =
_inheritanceManager.lookupInheritance(_enclosingClass, name);
if (inherited is! MethodElement) {
continue;
}
_errorReporter.reportErrorForElement(
CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER, accessor, [
_enclosingClass.displayName,
inherited.enclosingElement.displayName,
name
]);
}
}
/**
* Verify that the superclass of the [_enclosingClass] does not declare
* accessible static members with the same name as the instance
* getters/setters declared in [_enclosingClass].
*
* See [StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER], and
* [StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER].
*/
void _checkForConflictingInstanceGetterAndSuperclassMember() {
if (_enclosingClass == null) {
return;
}
InterfaceType enclosingType = _enclosingClass.type;
// check every accessor
for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
// we analyze instance accessors here
if (accessor.isStatic) {
continue;
}
// prepare accessor properties
String name = accessor.displayName;
bool getter = accessor.isGetter;
// if non-final variable, ignore setter - we already reported problem for
// getter
if (accessor.isSetter && accessor.isSynthetic) {
continue;
}
// try to find super element
ExecutableElement superElement;
superElement =
enclosingType.lookUpGetterInSuperclass(name, _currentLibrary);
if (superElement == null) {
superElement =
enclosingType.lookUpSetterInSuperclass(name, _currentLibrary);
}
if (superElement == null) {
superElement =
enclosingType.lookUpMethodInSuperclass(name, _currentLibrary);
}
if (superElement == null) {
continue;
}
// OK, not static
if (!superElement.isStatic) {
continue;
}
// prepare "super" type to report its name
ClassElement superElementClass =
superElement.enclosingElement as ClassElement;
InterfaceType superElementType = superElementClass.type;
if (getter) {
_errorReporter.reportErrorForElement(
StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER,
accessor,
[superElementType.displayName]);
} else {
_errorReporter.reportErrorForElement(
StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER,
accessor,
[superElementType.displayName]);
}
}
}
/**
* Verify that the enclosing class does not have a setter with the same name
* as the given instance method declaration.
*
* TODO(jwren) add other "conflicting" error codes into algorithm/ data
* structure.
*
* See [StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER].
*/
void _checkForConflictingInstanceMethodSetter(ClassDeclaration declaration) {
// Reference all of the class members in this class.
NodeList<ClassMember> classMembers = declaration.members;
if (classMembers.isEmpty) {
return;
}
// Create a HashMap to track conflicting members, and then loop through
// members in the class to construct the HashMap, at the same time,
// look for violations. Don't add members if they are part of a conflict,
// this prevents multiple warnings for one issue.
Map<String, ClassMember> memberHashMap = new HashMap<String, ClassMember>();
for (ClassMember member in classMembers) {
if (member is MethodDeclaration) {
if (member.isStatic) {
continue;
}
// prepare name
SimpleIdentifier name = member.name;
if (name == null) {
continue;
}
bool addThisMemberToTheMap = true;
bool isGetter = member.isGetter;
bool isSetter = member.isSetter;
bool isOperator = member.isOperator;
bool isMethod = !isGetter && !isSetter && !isOperator;
// Do lookups in the enclosing class (and the inherited member) if the
// member is a method or a setter for
// StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER warning.
if (isMethod) {
String setterName = "${name.name}=";
Element enclosingElementOfSetter = null;
ClassMember conflictingSetter = memberHashMap[setterName];
if (conflictingSetter != null) {
enclosingElementOfSetter = resolutionMap
.elementDeclaredByDeclaration(conflictingSetter)
.enclosingElement;
} else {
ExecutableElement elementFromInheritance = _inheritanceManager
.lookupInheritance(_enclosingClass, setterName);
if (elementFromInheritance != null) {
enclosingElementOfSetter =
elementFromInheritance.enclosingElement;
}
}
if (enclosingElementOfSetter != null) {
_errorReporter.reportErrorForNode(
StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER, name, [
_enclosingClass.displayName,
name.name,
enclosingElementOfSetter