Index: mojo/public/dart/third_party/analyzer/lib/src/generated/error_verifier.dart |
diff --git a/mojo/public/dart/third_party/analyzer/lib/src/generated/error_verifier.dart b/mojo/public/dart/third_party/analyzer/lib/src/generated/error_verifier.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9624115a04dda4d37714897862f39e0f2d4fd406 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/analyzer/lib/src/generated/error_verifier.dart |
@@ -0,0 +1,6001 @@ |
+// 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. |
+ |
+library engine.resolver.error_verifier; |
+ |
+import 'dart:collection'; |
+import "dart:math" as math; |
+ |
+import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
+ |
+import 'ast.dart'; |
+import 'constant.dart'; |
+import 'element.dart'; |
+import 'element_resolver.dart'; |
+import 'error.dart'; |
+import 'java_engine.dart'; |
+import 'parser.dart' show Parser, ParserErrorCode; |
+import 'resolver.dart'; |
+import 'scanner.dart' as sc; |
+import 'sdk.dart' show DartSdk, SdkLibrary; |
+import 'utilities_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 object providing access to the types defined by the language. |
+ */ |
+ final TypeProvider _typeProvider; |
+ |
+ /** |
+ * 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. |
+ */ |
+ ClassElement _enclosingClass; |
+ |
+ /** |
+ * 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]. |
+ */ |
+ HashMap<FieldElement, INIT_STATE> _initialFieldElementsMap; |
+ |
+ /** |
+ * A table mapping name of the library to the export directive which export |
+ * this library. |
+ */ |
+ HashMap<String, LibraryElement> _nameToExportElement = |
+ new HashMap<String, LibraryElement>(); |
+ |
+ /** |
+ * A table mapping name of the library to the import directive which import |
+ * this library. |
+ */ |
+ HashMap<String, LibraryElement> _nameToImportElement = |
+ new HashMap<String, LibraryElement>(); |
+ |
+ /** |
+ * A table mapping names to the exported elements. |
+ */ |
+ HashMap<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>(); |
+ |
+ /** |
+ * 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; |
+ |
+ /** |
+ * Initialize a newly created error verifier. |
+ */ |
+ ErrorVerifier(this._errorReporter, this._currentLibrary, this._typeProvider, |
+ this._inheritanceManager, this.enableSuperMixins) { |
+ 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; |
+ } |
+ |
+ @override |
+ Object visitAnnotation(Annotation node) { |
+ _checkForInvalidAnnotationFromDeferredLibrary(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 visitAssertStatement(AssertStatement node) { |
+ _checkForNonBoolExpression(node); |
+ return super.visitAssertStatement(node); |
+ } |
+ |
+ @override |
+ Object visitAssignmentExpression(AssignmentExpression node) { |
+ sc.TokenType operatorType = node.operator.type; |
+ Expression lhs = node.leftHandSide; |
+ Expression rhs = node.rightHandSide; |
+ if (operatorType == sc.TokenType.EQ || |
+ operatorType == sc.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) { |
+ sc.Token operator = node.operator; |
+ sc.TokenType type = operator.type; |
+ if (type == sc.TokenType.AMPERSAND_AMPERSAND || |
+ type == sc.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); |
+ } |
+ return super.visitBinaryExpression(node); |
+ } |
+ |
+ @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) { |
+ bool previousIsInCatchClause = _isInCatchClause; |
+ try { |
+ _isInCatchClause = true; |
+ _checkForTypeAnnotationDeferredClass(node.exceptionType); |
+ return super.visitCatchClause(node); |
+ } finally { |
+ _isInCatchClause = previousIsInCatchClause; |
+ } |
+ } |
+ |
+ @override |
+ Object visitClassDeclaration(ClassDeclaration node) { |
+ ClassElement outerClass = _enclosingClass; |
+ try { |
+ _isInNativeClass = node.nativeClause != null; |
+ _enclosingClass = node.element; |
+ ExtendsClause extendsClause = node.extendsClause; |
+ ImplementsClause implementsClause = node.implementsClause; |
+ WithClause withClause = node.withClause; |
+ _checkForBuiltInIdentifierAsName( |
+ node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
+ _checkForMemberWithClassName(); |
+ _checkForNoDefaultSuperConstructorImplicit(node); |
+ _checkForConflictingTypeVariableErrorCodes(node); |
+ // Only do error checks on the clause nodes if there is a non-null clause |
+ if (implementsClause != null || |
+ extendsClause != null || |
+ withClause != null) { |
+ // 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 (!_checkForImplementsDisallowedClass(implementsClause) && |
+ !_checkForExtendsDisallowedClass(extendsClause) && |
+ !_checkForAllMixinErrorCodes(withClause)) { |
+ _checkForExtendsDeferredClass(extendsClause); |
+ _checkForImplementsDeferredClass(implementsClause); |
+ _checkForNonAbstractClassInheritsAbstractMember(node.name); |
+ _checkForInconsistentMethodInheritance(); |
+ _checkForRecursiveInterfaceInheritance(_enclosingClass); |
+ _checkForConflictingGetterAndMethod(); |
+ _checkForConflictingInstanceGetterAndSuperclassMember(); |
+ _checkImplementsSuperClass(node); |
+ _checkImplementsFunctionWithoutCall(node); |
+ _checkForMixinHasNoConstructors(node); |
+ } |
+ } |
+ visitClassDeclarationIncrementally(node); |
+ _checkForFinalNotInitializedInClass(node); |
+ _checkForDuplicateDefinitionInheritance(); |
+ _checkForConflictingInstanceMethodSetter(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 = 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); |
+ ClassElement outerClassElement = _enclosingClass; |
+ try { |
+ _enclosingClass = node.element; |
+ ImplementsClause implementsClause = node.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 (!_checkForExtendsDisallowedClassInTypeAlias(node) && |
+ !_checkForImplementsDisallowedClass(implementsClause) && |
+ !_checkForAllMixinErrorCodes(node.withClause)) { |
+ _checkForExtendsDeferredClassInTypeAlias(node); |
+ _checkForImplementsDeferredClass(implementsClause); |
+ _checkForRecursiveInterfaceInheritance(_enclosingClass); |
+ _checkForNonAbstractClassInheritsAbstractMember(node.name); |
+ _checkForMixinHasNoConstructors(node); |
+ } |
+ } 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) { |
+ _checkForDeferredPrefixCollisions(node); |
+ return super.visitCompilationUnit(node); |
+ } |
+ |
+ @override |
+ Object visitConditionalExpression(ConditionalExpression node) { |
+ _checkForNonBoolCondition(node.condition); |
+ 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); |
+ _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 outerClass = _enclosingClass; |
+ try { |
+ _isInNativeClass = false; |
+ _enclosingClass = node.element; |
+ return super.visitEnumDeclaration(node); |
+ } finally { |
+ _enclosingClass = outerClass; |
+ } |
+ } |
+ |
+ @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 == null ? null : _enclosingFunction.type; |
+ DartType expectedReturnType = functionType == null |
+ ? DynamicTypeImpl.instance |
+ : functionType.returnType; |
+ _checkForReturnOfInvalidType(node.expression, expectedReturnType); |
+ 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 visitFunctionDeclaration(FunctionDeclaration node) { |
+ ExecutableElement outerFunction = _enclosingFunction; |
+ try { |
+ SimpleIdentifier identifier = node.name; |
+ String methodName = ""; |
+ if (identifier != null) { |
+ methodName = identifier.name; |
+ } |
+ _enclosingFunction = node.element; |
+ TypeName 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); |
+ 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 (!_isFunctionType(expressionType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION, |
+ functionExpression); |
+ } |
+ 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); |
+ return super.visitFunctionTypedFormalParameter(node); |
+ } finally { |
+ _isInFunctionTypedFormalParameter = old; |
+ } |
+ } |
+ |
+ @override |
+ Object visitIfStatement(IfStatement node) { |
+ _checkForNonBoolCondition(node.condition); |
+ return super.visitIfStatement(node); |
+ } |
+ |
+ @override |
+ Object visitImportDirective(ImportDirective node) { |
+ ImportElement importElement = node.element; |
+ 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) { |
+ InterfaceType interfaceType = type; |
+ _checkForConstOrNewWithAbstractClass(node, typeName, interfaceType); |
+ _checkForConstOrNewWithEnum(node, typeName, interfaceType); |
+ if (_isInConstInstanceCreation) { |
+ _checkForConstWithNonConst(node); |
+ _checkForConstWithUndefinedConstructor( |
+ node, constructorName, typeName); |
+ _checkForConstWithTypeParameters(typeName); |
+ _checkForConstDeferredClass(node, constructorName, typeName); |
+ } else { |
+ _checkForNewWithUndefinedConstructor(node, constructorName, typeName); |
+ } |
+ } |
+ return super.visitInstanceCreationExpression(node); |
+ } finally { |
+ _isInConstInstanceCreation = wasInConstInstanceCreation; |
+ } |
+ } |
+ |
+ @override |
+ Object visitIsExpression(IsExpression node) { |
+ _checkForTypeAnnotationDeferredClass(node.type); |
+ return super.visitIsExpression(node); |
+ } |
+ |
+ @override |
+ Object visitListLiteral(ListLiteral node) { |
+ TypeArgumentList typeArguments = node.typeArguments; |
+ if (typeArguments != null) { |
+ if (node.constKeyword != null) { |
+ NodeList<TypeName> arguments = typeArguments.arguments; |
+ if (arguments.length != 0) { |
+ _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, |
+ CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST); |
+ } |
+ } |
+ _checkForExpectedOneListTypeArgument(node, typeArguments); |
+ _checkForListElementTypeNotAssignable(node, typeArguments); |
+ } |
+ return super.visitListLiteral(node); |
+ } |
+ |
+ @override |
+ Object visitMapLiteral(MapLiteral node) { |
+ TypeArgumentList typeArguments = node.typeArguments; |
+ if (typeArguments != null) { |
+ NodeList<TypeName> arguments = typeArguments.arguments; |
+ if (arguments.length != 0) { |
+ if (node.constKeyword != null) { |
+ _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, |
+ CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP); |
+ } |
+ } |
+ _checkExpectedTwoMapTypeArguments(typeArguments); |
+ _checkForMapTypeNotAssignable(node, typeArguments); |
+ } |
+ _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; |
+ } |
+ TypeName returnTypeName = node.returnType; |
+ if (node.isSetter || node.isGetter) { |
+ _checkForMismatchedAccessorTypes(node, methodName); |
+ } |
+ if (node.isGetter) { |
+ _checkForVoidReturnType(node); |
+ _checkForConflictingStaticGetterAndInstanceSetter(node); |
+ } else if (node.isSetter) { |
+ _checkForInvalidModifierOnBody( |
+ node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER); |
+ _checkForWrongNumberOfParametersForSetter(node.name, node.parameters); |
+ _checkForNonVoidReturnTypeForSetter(returnTypeName); |
+ _checkForConflictingStaticSetterAndInstanceMember(node); |
+ } else if (node.isOperator) { |
+ _checkForOptionalParameterInOperator(node); |
+ _checkForWrongNumberOfParametersForOperator(node); |
+ _checkForNonVoidReturnTypeForOperator(node); |
+ } |
+ _checkForConcreteClassWithAbstractMember(node); |
+ _checkForAllInvalidOverrideErrorCodesForMethod(node); |
+ _checkForTypeAnnotationDeferredClass(returnTypeName); |
+ _checkForIllegalReturnType(returnTypeName); |
+ 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); |
+ } |
+ 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) { |
+ sc.TokenType operatorType = node.operator.type; |
+ Expression operand = node.operand; |
+ if (operatorType == sc.TokenType.BANG) { |
+ _checkForNonBoolNegationExpression(operand); |
+ } else if (operatorType.isIncrementOperator) { |
+ _checkForAssignmentToFinal(operand); |
+ } |
+ _checkForIntNotAssignable(operand); |
+ return super.visitPrefixExpression(node); |
+ } |
+ |
+ @override |
+ Object visitPropertyAccess(PropertyAccess node) { |
+ bool isConditional = node.operator.type == sc.TokenType.QUESTION_PERIOD; |
+ 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); |
+ return super.visitSimpleFormalParameter(node); |
+ } |
+ |
+ @override |
+ Object visitSimpleIdentifier(SimpleIdentifier 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 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); |
+ return super.visitThrowExpression(node); |
+ } |
+ |
+ @override |
+ Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
+ _checkForFinalNotInitialized(node.variables); |
+ return super.visitTopLevelVariableDeclaration(node); |
+ } |
+ |
+ @override |
+ Object visitTypeArgumentList(TypeArgumentList node) { |
+ NodeList<TypeName> list = node.arguments; |
+ for (TypeName typeName in list) { |
+ _checkForTypeAnnotationDeferredClass(typeName); |
+ } |
+ 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); |
+ return super.visitTypeParameter(node); |
+ } |
+ |
+ @override |
+ Object visitVariableDeclaration(VariableDeclaration node) { |
+ SimpleIdentifier nameNode = node.name; |
+ Expression initializerNode = node.initializer; |
+ // do checks |
+ _checkForInvalidAssignment(nameNode, initializerNode); |
+ // 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); |
+ } |
+ // 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 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); |
+ } |
+ |
+ /** |
+ * Verify that the given list of [typeArguments] contains exactly two |
+ * elements. |
+ * |
+ * See [StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS]. |
+ */ |
+ bool _checkExpectedTwoMapTypeArguments(TypeArgumentList typeArguments) { |
+ // check number of type arguments |
+ int num = typeArguments.arguments.length; |
+ if (num == 2) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS, typeArguments, |
+ [num]); |
+ return true; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForAllFinalInitializedErrorCodes( |
+ ConstructorDeclaration constructor) { |
+ if (constructor.factoryKeyword != null || |
+ constructor.redirectedConstructor != null || |
+ constructor.externalKeyword != null) { |
+ return false; |
+ } |
+ // Ignore if native class. |
+ if (_isInNativeClass) { |
+ return false; |
+ } |
+ bool foundError = false; |
+ HashMap<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 parameter = formalParameter; |
+ if (parameter is DefaultFormalParameter) { |
+ parameter = (parameter as DefaultFormalParameter).parameter; |
+ } |
+ 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]); |
+ foundError = true; |
+ } |
+ } 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]); |
+ foundError = true; |
+ } |
+ } |
+ } |
+ } |
+ // Visit all of the initializers |
+ NodeList<ConstructorInitializer> initializers = constructor.initializers; |
+ for (ConstructorInitializer constructorInitializer in initializers) { |
+ if (constructorInitializer is RedirectingConstructorInvocation) { |
+ return false; |
+ } |
+ if (constructorInitializer is ConstructorFieldInitializer) { |
+ ConstructorFieldInitializer constructorFieldInitializer = |
+ constructorInitializer; |
+ SimpleIdentifier fieldName = constructorFieldInitializer.fieldName; |
+ Element element = fieldName.staticElement; |
+ if (element is FieldElement) { |
+ FieldElement fieldElement = element; |
+ INIT_STATE state = fieldElementsMap[fieldElement]; |
+ if (state == INIT_STATE.NOT_INIT) { |
+ fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_INITIALIZERS; |
+ } else if (state == INIT_STATE.INIT_IN_DECLARATION) { |
+ if (fieldElement.isFinal || fieldElement.isConst) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION, |
+ fieldName); |
+ foundError = true; |
+ } |
+ } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER, |
+ fieldName); |
+ foundError = true; |
+ } else if (state == INIT_STATE.INIT_IN_INITIALIZERS) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS, |
+ fieldName, [fieldElement.displayName]); |
+ foundError = true; |
+ } |
+ } |
+ } |
+ } |
+ // 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]); |
+ foundError = true; |
+ } |
+ } |
+ }); |
+ if (notInitFinalFields.isNotEmpty) { |
+ foundError = true; |
+ AnalysisErrorWithProperties analysisError; |
+ if (notInitFinalFields.length == 1) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1, |
+ constructor.returnType, [notInitFinalFields[0].name]); |
+ } else if (notInitFinalFields.length == 2) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2, |
+ constructor.returnType, [ |
+ notInitFinalFields[0].name, |
+ notInitFinalFields[1].name |
+ ]); |
+ } else { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS, |
+ constructor.returnType, [ |
+ notInitFinalFields[0].name, |
+ notInitFinalFields[1].name, |
+ notInitFinalFields.length - 2 |
+ ]); |
+ } |
+ analysisError.setProperty( |
+ ErrorProperty.NOT_INITIALIZED_FIELDS, notInitFinalFields); |
+ _errorReporter.reportError(analysisError); |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Check the given [executableElement] against override-error codes. The |
+ * [overriddenExecutable] 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 [StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC], |
+ * [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 executableElement, |
+ ExecutableElement overriddenExecutable, List<ParameterElement> parameters, |
+ List<AstNode> parameterLocations, SimpleIdentifier errorNameTarget) { |
+ bool isGetter = false; |
+ bool isSetter = false; |
+ if (executableElement is PropertyAccessorElement) { |
+ PropertyAccessorElement accessorElement = executableElement; |
+ isGetter = accessorElement.isGetter; |
+ isSetter = accessorElement.isSetter; |
+ } |
+ String executableElementName = executableElement.name; |
+ FunctionType overridingFT = executableElement.type; |
+ FunctionType overriddenFT = overriddenExecutable.type; |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ overriddenFT = _inheritanceManager |
+ .substituteTypeArgumentsInMemberFromInheritance( |
+ overriddenFT, executableElementName, enclosingType); |
+ if (overridingFT == null || overriddenFT == null) { |
+ return false; |
+ } |
+ DartType overridingFTReturnType = overridingFT.returnType; |
+ DartType overriddenFTReturnType = overriddenFT.returnType; |
+ List<DartType> overridingNormalPT = overridingFT.normalParameterTypes; |
+ List<DartType> overriddenNormalPT = overriddenFT.normalParameterTypes; |
+ List<DartType> overridingPositionalPT = overridingFT.optionalParameterTypes; |
+ List<DartType> overriddenPositionalPT = overriddenFT.optionalParameterTypes; |
+ Map<String, DartType> overridingNamedPT = overridingFT.namedParameterTypes; |
+ Map<String, DartType> overriddenNamedPT = overriddenFT.namedParameterTypes; |
+ // CTEC.INVALID_OVERRIDE_REQUIRED, CTEC.INVALID_OVERRIDE_POSITIONAL and |
+ // CTEC.INVALID_OVERRIDE_NAMED |
+ if (overridingNormalPT.length > overriddenNormalPT.length) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.INVALID_OVERRIDE_REQUIRED, errorNameTarget, [ |
+ overriddenNormalPT.length, |
+ overriddenExecutable.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ if (overridingNormalPT.length + overridingPositionalPT.length < |
+ overriddenPositionalPT.length + overriddenNormalPT.length) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.INVALID_OVERRIDE_POSITIONAL, errorNameTarget, [ |
+ overriddenPositionalPT.length + overriddenNormalPT.length, |
+ overriddenExecutable.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 overriddenNamedPT.keys) { |
+ if (!overridingNamedPT.containsKey(overriddenParamName)) { |
+ // The overridden method expected the overriding method to have |
+ // overridingParamName, but it does not. |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.INVALID_OVERRIDE_NAMED, errorNameTarget, [ |
+ overriddenParamName, |
+ overriddenExecutable.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ } |
+ // SWC.INVALID_METHOD_OVERRIDE_RETURN_TYPE |
+ if (overriddenFTReturnType != VoidTypeImpl.instance && |
+ !overridingFTReturnType.isAssignableTo(overriddenFTReturnType)) { |
+ _errorReporter.reportTypeErrorForNode(!isGetter |
+ ? StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE |
+ : StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE, |
+ errorNameTarget, [ |
+ overridingFTReturnType, |
+ overriddenFTReturnType, |
+ overriddenExecutable.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ // SWC.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE |
+ if (parameterLocations == null) { |
+ return false; |
+ } |
+ int parameterIndex = 0; |
+ for (int i = 0; i < overridingNormalPT.length; i++) { |
+ if (!overridingNormalPT[i].isAssignableTo(overriddenNormalPT[i])) { |
+ _errorReporter.reportTypeErrorForNode(!isSetter |
+ ? StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE |
+ : StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE, |
+ parameterLocations[parameterIndex], [ |
+ overridingNormalPT[i], |
+ overriddenNormalPT[i], |
+ overriddenExecutable.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ parameterIndex++; |
+ } |
+ // SWC.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE |
+ for (int i = 0; i < overriddenPositionalPT.length; i++) { |
+ if (!overridingPositionalPT[i] |
+ .isAssignableTo(overriddenPositionalPT[i])) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE, |
+ parameterLocations[parameterIndex], [ |
+ overridingPositionalPT[i], |
+ overriddenPositionalPT[i], |
+ overriddenExecutable.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ parameterIndex++; |
+ } |
+ // SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE & |
+ // SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES |
+ for (String overriddenName in overriddenNamedPT.keys) { |
+ DartType overridingType = overridingNamedPT[overriddenName]; |
+ if (overridingType == null) { |
+ // Error, this is never reached- INVALID_OVERRIDE_NAMED would have been |
+ // created above if this could be reached. |
+ continue; |
+ } |
+ DartType overriddenType = overriddenNamedPT[overriddenName]; |
+ if (!overriddenType.isAssignableTo(overridingType)) { |
+ // 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.parameterKind == ParameterKind.NAMED && |
+ overriddenName == parameter.name) { |
+ parameterToSelect = parameter; |
+ parameterLocationToSelect = parameterLocations[i]; |
+ break; |
+ } |
+ } |
+ if (parameterToSelect != null) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE, |
+ parameterLocationToSelect, [ |
+ overridingType, |
+ overriddenType, |
+ overriddenExecutable.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 = overriddenExecutable.parameters; |
+ for (int i = 0; i < parameters.length; i++) { |
+ ParameterElement parameter = parameters[i]; |
+ if (parameter.parameterKind.isOptional) { |
+ formalParameters.add(parameterLocations[i]); |
+ parameterElts.add(parameter as ParameterElementImpl); |
+ } |
+ } |
+ for (ParameterElement parameterElt in overriddenPEs) { |
+ if (parameterElt.parameterKind.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].parameterKind == ParameterKind.NAMED) { |
+ // 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], [ |
+ overriddenExecutable.enclosingElement.displayName, |
+ overriddenExecutable.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], [ |
+ overriddenExecutable.enclosingElement.displayName, |
+ overriddenExecutable.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. |
+ */ |
+ bool _checkForAllInvalidOverrideErrorCodesForExecutable( |
+ ExecutableElement executableElement, List<ParameterElement> parameters, |
+ List<AstNode> parameterLocations, SimpleIdentifier errorNameTarget) { |
+ // |
+ // Compute the overridden executable from the InheritanceManager |
+ // |
+ List<ExecutableElement> overriddenExecutables = _inheritanceManager |
+ .lookupOverrides(_enclosingClass, executableElement.name); |
+ if (_checkForInstanceMethodNameCollidesWithSuperclassStatic( |
+ executableElement, errorNameTarget)) { |
+ return true; |
+ } |
+ for (ExecutableElement overriddenElement in overriddenExecutables) { |
+ if (_checkForAllInvalidOverrideErrorCodes(executableElement, |
+ overriddenElement, parameters, parameterLocations, errorNameTarget)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check the given field [declaration] against override-error codes. |
+ * |
+ * See [_checkForAllInvalidOverrideErrorCodes]. |
+ */ |
+ bool _checkForAllInvalidOverrideErrorCodesForField( |
+ FieldDeclaration declaration) { |
+ if (_enclosingClass == null || declaration.isStatic) { |
+ return false; |
+ } |
+ bool hasProblems = false; |
+ 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) { |
+ if (_checkForAllInvalidOverrideErrorCodesForExecutable(getter, |
+ ParameterElement.EMPTY_LIST, AstNode.EMPTY_LIST, fieldName)) { |
+ hasProblems = true; |
+ } |
+ } |
+ if (setter != null) { |
+ if (_checkForAllInvalidOverrideErrorCodesForExecutable( |
+ setter, setter.parameters, <AstNode>[fieldName], fieldName)) { |
+ hasProblems = true; |
+ } |
+ } |
+ } |
+ return hasProblems; |
+ } |
+ |
+ /** |
+ * Check the given [method] declaration against override-error codes. |
+ * |
+ * See [_checkForAllInvalidOverrideErrorCodes]. |
+ */ |
+ bool _checkForAllInvalidOverrideErrorCodesForMethod( |
+ MethodDeclaration method) { |
+ if (_enclosingClass == null || |
+ method.isStatic || |
+ method.body is NativeFunctionBody) { |
+ return false; |
+ } |
+ ExecutableElement executableElement = method.element; |
+ if (executableElement == null) { |
+ return false; |
+ } |
+ SimpleIdentifier methodName = method.name; |
+ if (methodName.isSynthetic) { |
+ return false; |
+ } |
+ FormalParameterList formalParameterList = method.parameters; |
+ NodeList<FormalParameter> parameterList = |
+ formalParameterList != null ? formalParameterList.parameters : null; |
+ List<AstNode> parameters = |
+ parameterList != null ? new List.from(parameterList) : null; |
+ return _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) { |
+ continue; |
+ } |
+ if (_checkForExtendsOrImplementsDisallowedClass( |
+ mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) { |
+ problemReported = true; |
+ } else { |
+ ClassElement mixinElement = (mixinType as InterfaceType).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]. |
+ */ |
+ bool _checkForAllRedirectConstructorErrorCodes( |
+ ConstructorDeclaration declaration) { |
+ // |
+ // Prepare redirected constructor node |
+ // |
+ ConstructorName redirectedConstructor = declaration.redirectedConstructor; |
+ if (redirectedConstructor == null) { |
+ return false; |
+ } |
+ // |
+ // 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 true; |
+ } |
+ return false; |
+ } |
+ FunctionType redirectedType = redirectedElement.type; |
+ DartType redirectedReturnType = redirectedType.returnType; |
+ // |
+ // Report specific problem when return type is incompatible |
+ // |
+ FunctionType constructorType = declaration.element.type; |
+ DartType constructorReturnType = constructorType.returnType; |
+ if (!redirectedReturnType.isAssignableTo(constructorReturnType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE, |
+ redirectedConstructor, [redirectedReturnType, constructorReturnType]); |
+ return true; |
+ } |
+ // |
+ // Check parameters |
+ // |
+ if (!redirectedType.isSubtypeOf(constructorType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, |
+ redirectedConstructor, [redirectedType, constructorType]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * 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 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]. |
+ */ |
+ bool _checkForAllReturnStatementErrorCodes(ReturnStatement statement) { |
+ FunctionType functionType = |
+ _enclosingFunction == null ? null : _enclosingFunction.type; |
+ DartType expectedReturnType = functionType == null |
+ ? DynamicTypeImpl.instance |
+ : functionType.returnType; |
+ Expression returnExpression = statement.expression; |
+ // RETURN_IN_GENERATIVE_CONSTRUCTOR |
+ bool isGenerativeConstructor = _enclosingFunction is ConstructorElement && |
+ !(_enclosingFunction as ConstructorElement).isFactory; |
+ if (isGenerativeConstructor) { |
+ if (returnExpression == null) { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, |
+ returnExpression); |
+ return true; |
+ } |
+ // RETURN_WITHOUT_VALUE |
+ if (returnExpression == null) { |
+ if (_inGenerator || |
+ _computeReturnTypeForMethod(null) |
+ .isAssignableTo(expectedReturnType)) { |
+ return false; |
+ } |
+ _hasReturnWithoutValue = true; |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.RETURN_WITHOUT_VALUE, statement); |
+ return true; |
+ } else if (_inGenerator) { |
+ // RETURN_IN_GENERATOR |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RETURN_IN_GENERATOR, statement); |
+ } |
+ // RETURN_OF_INVALID_TYPE |
+ 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]. |
+ */ |
+ bool _checkForAmbiguousExport(ExportDirective directive, |
+ ExportElement exportElement, LibraryElement exportedLibrary) { |
+ if (exportedLibrary == null) { |
+ return false; |
+ } |
+ // 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 true; |
+ } else { |
+ _exportedElements[name] = element; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForArgumentTypeNotAssignable(Expression expression, |
+ DartType expectedStaticType, DartType actualStaticType, |
+ ErrorCode errorCode) { |
+ // |
+ // Warning case: test static type information |
+ // |
+ if (actualStaticType != null && expectedStaticType != null) { |
+ if (!actualStaticType.isAssignableTo(expectedStaticType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ errorCode, expression, [actualStaticType, expectedStaticType]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given [argument] can be assigned to its corresponding |
+ * parameter. |
+ * |
+ * This method corresponds to |
+ * [BestPracticesVerifier.checkForArgumentTypeNotAssignableForArgument]. |
+ * |
+ * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
+ */ |
+ bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) { |
+ if (argument == null) { |
+ return false; |
+ } |
+ ParameterElement staticParameterElement = argument.staticParameterElement; |
+ DartType staticParameterType = |
+ staticParameterElement == null ? null : staticParameterElement.type; |
+ return _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]. |
+ */ |
+ bool _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]. |
+ */ |
+ bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { |
+ if (argumentList == null) { |
+ return false; |
+ } |
+ bool problemReported = false; |
+ for (Expression argument in argumentList.arguments) { |
+ if (_checkForArgumentTypeNotAssignableForArgument(argument)) { |
+ problemReported = true; |
+ } |
+ } |
+ return problemReported; |
+ } |
+ |
+ /** |
+ * 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. |
+ */ |
+ bool _checkForAssignability(Expression expression, InterfaceType type, |
+ ErrorCode errorCode, List<Object> arguments) { |
+ if (expression == null) { |
+ return false; |
+ } |
+ DartType expressionType = expression.staticType; |
+ if (expressionType == null) { |
+ return false; |
+ } |
+ if (expressionType.isAssignableTo(type)) { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode(errorCode, expression, arguments); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given [expression] is not final. |
+ * |
+ * See [StaticWarningCode.ASSIGNMENT_TO_CONST], |
+ * [StaticWarningCode.ASSIGNMENT_TO_FINAL], and |
+ * [StaticWarningCode.ASSIGNMENT_TO_METHOD]. |
+ */ |
+ bool _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) { |
+ PropertyAccess propertyAccess = expression; |
+ element = propertyAccess.propertyName.staticElement; |
+ highlightedNode = propertyAccess.propertyName; |
+ } |
+ // check if element is assignable |
+ if (element is PropertyAccessorElement) { |
+ PropertyAccessorElement accessor = element as PropertyAccessorElement; |
+ element = accessor.variable; |
+ } |
+ if (element is VariableElement) { |
+ if (element.isConst) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.ASSIGNMENT_TO_CONST, expression); |
+ return true; |
+ } |
+ if (element.isFinal) { |
+ if (element is FieldElementImpl && |
+ element.setter == null && |
+ element.isSynthetic) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.ASSIGNMENT_TO_FINAL_NO_SETTER, highlightedNode, |
+ [element.name, element.enclosingElement.displayName]); |
+ return true; |
+ } |
+ _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_FINAL, |
+ highlightedNode, [element.name]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ if (element is FunctionElement) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.ASSIGNMENT_TO_FUNCTION, expression); |
+ return true; |
+ } |
+ if (element is MethodElement) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.ASSIGNMENT_TO_METHOD, expression); |
+ return true; |
+ } |
+ if (element is ClassElement || |
+ element is FunctionTypeAliasElement || |
+ element is TypeParameterElement) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.ASSIGNMENT_TO_TYPE, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForBuiltInIdentifierAsName( |
+ SimpleIdentifier identifier, ErrorCode errorCode) { |
+ sc.Token token = identifier.token; |
+ if (token.type == sc.TokenType.KEYWORD) { |
+ _errorReporter.reportErrorForNode( |
+ errorCode, identifier, [identifier.name]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given [switchCase] is terminated with 'break', 'continue', |
+ * 'return' or 'throw'. |
+ * |
+ * see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
+ */ |
+ bool _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) { |
+ SwitchStatement switchStatement = parent; |
+ NodeList<SwitchMember> members = switchStatement.members; |
+ int index = members.indexOf(switchCase); |
+ if (index != -1 && index < members.length - 1) { |
+ return false; |
+ } |
+ } |
+ // no other switch member after this one |
+ } else { |
+ Statement statement = statements[statements.length - 1]; |
+ // terminated with statement |
+ if (statement is BreakStatement || |
+ statement is ContinueStatement || |
+ statement is ReturnStatement) { |
+ return false; |
+ } |
+ // terminated with 'throw' expression |
+ if (statement is ExpressionStatement) { |
+ Expression expression = statement.expression; |
+ if (expression is ThrowExpression) { |
+ return false; |
+ } |
+ } |
+ } |
+ // report error |
+ _errorReporter.reportErrorForToken( |
+ StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the switch cases in the given switch [statement] are terminated |
+ * with 'break', 'continue', 'return' or 'throw'. |
+ * |
+ * See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
+ */ |
+ bool _checkForCaseBlocksNotTerminated(SwitchStatement statement) { |
+ bool foundError = false; |
+ 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)) { |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that the given [method] declaration is abstract only if the |
+ * enclosing class is also abstract. |
+ * |
+ * See [StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER]. |
+ */ |
+ bool _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) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER, nameNode, [ |
+ memberName, |
+ _enclosingClass.displayName |
+ ]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _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 true; |
+ } |
+ } |
+ // conflict with class member |
+ if (constructorName != null && |
+ constructorElement != null && |
+ !constructorName.isSynthetic) { |
+ // fields |
+ FieldElement field = classElement.getField(name); |
+ if (field != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD, |
+ constructor, [name]); |
+ return true; |
+ } |
+ // methods |
+ MethodElement method = classElement.getMethod(name); |
+ if (method != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD, |
+ constructor, [name]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForConflictingGetterAndMethod() { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ bool hasProblem = false; |
+ // 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; |
+ } |
+ // report problem |
+ hasProblem = true; |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD, method.nameOffset, |
+ name.length, [ |
+ _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; |
+ } |
+ // report problem |
+ hasProblem = true; |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER, |
+ accessor.nameOffset, name.length, [ |
+ _enclosingClass.displayName, |
+ inherited.enclosingElement.displayName, |
+ name |
+ ]); |
+ } |
+ // done |
+ return hasProblem; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForConflictingInstanceGetterAndSuperclassMember() { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ // check every accessor |
+ bool hasProblem = false; |
+ 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 alreay 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; |
+ // report problem |
+ hasProblem = true; |
+ 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]); |
+ } |
+ } |
+ // done |
+ return hasProblem; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _checkForConflictingInstanceMethodSetter(ClassDeclaration declaration) { |
+ // Reference all of the class members in this class. |
+ NodeList<ClassMember> classMembers = declaration.members; |
+ if (classMembers.isEmpty) { |
+ return false; |
+ } |
+ // 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. |
+ bool foundError = false; |
+ HashMap<String, ClassMember> memberHashMap = |
+ new HashMap<String, ClassMember>(); |
+ for (ClassMember classMember in classMembers) { |
+ if (classMember is MethodDeclaration) { |
+ MethodDeclaration method = classMember; |
+ if (method.isStatic) { |
+ continue; |
+ } |
+ // prepare name |
+ SimpleIdentifier name = method.name; |
+ if (name == null) { |
+ continue; |
+ } |
+ bool addThisMemberToTheMap = true; |
+ bool isGetter = method.isGetter; |
+ bool isSetter = method.isSetter; |
+ bool isOperator = method.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 = |
+ conflictingSetter.element.enclosingElement; |
+ } else { |
+ ExecutableElement elementFromInheritance = _inheritanceManager |
+ .lookupInheritance(_enclosingClass, setterName); |
+ if (elementFromInheritance != null) { |
+ enclosingElementOfSetter = |
+ elementFromInheritance.enclosingElement; |
+ } |
+ } |
+ if (enclosingElementOfSetter != null) { |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER, name, [ |
+ _enclosingClass.displayName, |
+ name.name, |
+ enclosingElementOfSetter.displayName |
+ ]); |
+ foundError = true; |
+ addThisMemberToTheMap = false; |
+ } |
+ } else if (isSetter) { |
+ String methodName = name.name; |
+ ClassMember conflictingMethod = memberHashMap[methodName]; |
+ if (conflictingMethod != null && |
+ conflictingMethod is MethodDeclaration && |
+ !conflictingMethod.isGetter) { |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER2, name, [ |
+ _enclosingClass.displayName, |
+ name.name |
+ ]); |
+ foundError = true; |
+ addThisMemberToTheMap = false; |
+ } |
+ } |
+ // Finally, add this member into the HashMap. |
+ if (addThisMemberToTheMap) { |
+ if (method.isSetter) { |
+ memberHashMap["${name.name}="] = method; |
+ } else { |
+ memberHashMap[name.name] = method; |
+ } |
+ } |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that the enclosing class does not have an instance member with the |
+ * same name as the given static [method] declaration. |
+ * |
+ * See [StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER]. |
+ */ |
+ bool _checkForConflictingStaticGetterAndInstanceSetter( |
+ MethodDeclaration method) { |
+ if (!method.isStatic) { |
+ return false; |
+ } |
+ // prepare name |
+ SimpleIdentifier nameNode = method.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name = nameNode.name; |
+ // prepare enclosing type |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ // try to find setter |
+ ExecutableElement setter = |
+ enclosingType.lookUpSetter(name, _currentLibrary); |
+ if (setter == null) { |
+ return false; |
+ } |
+ // OK, also static |
+ if (setter.isStatic) { |
+ return false; |
+ } |
+ // prepare "setter" type to report its name |
+ ClassElement setterClass = setter.enclosingElement as ClassElement; |
+ InterfaceType setterType = setterClass.type; |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER, |
+ nameNode, [setterType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the enclosing class does not have an instance member with the |
+ * same name as the given static [method] declaration. |
+ * |
+ * See [StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER]. |
+ */ |
+ bool _checkForConflictingStaticSetterAndInstanceMember( |
+ MethodDeclaration method) { |
+ if (!method.isStatic) { |
+ return false; |
+ } |
+ // prepare name |
+ SimpleIdentifier nameNode = method.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name = nameNode.name; |
+ // prepare enclosing type |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ // try to find member |
+ ExecutableElement member; |
+ member = enclosingType.lookUpMethod(name, _currentLibrary); |
+ if (member == null) { |
+ member = enclosingType.lookUpGetter(name, _currentLibrary); |
+ } |
+ if (member == null) { |
+ member = enclosingType.lookUpSetter(name, _currentLibrary); |
+ } |
+ if (member == null) { |
+ return false; |
+ } |
+ // OK, also static |
+ if (member.isStatic) { |
+ return false; |
+ } |
+ // prepare "member" type to report its name |
+ ClassElement memberClass = member.enclosingElement as ClassElement; |
+ InterfaceType memberType = memberClass.type; |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER, |
+ nameNode, [memberType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify all conflicts between type variable and enclosing class. |
+ * TODO(scheglov) |
+ * |
+ * See [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS], and |
+ * [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER]. |
+ */ |
+ bool _checkForConflictingTypeVariableErrorCodes( |
+ ClassDeclaration declaration) { |
+ bool problemReported = false; |
+ for (TypeParameterElement typeParameter in _enclosingClass.typeParameters) { |
+ String name = typeParameter.name; |
+ // name is same as the name of the enclosing class |
+ if (_enclosingClass.name == name) { |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS, |
+ typeParameter.nameOffset, name.length, [name]); |
+ problemReported = true; |
+ } |
+ // check members |
+ if (_enclosingClass.getMethod(name) != null || |
+ _enclosingClass.getGetter(name) != null || |
+ _enclosingClass.getSetter(name) != null) { |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER, |
+ typeParameter.nameOffset, name.length, [name]); |
+ problemReported = true; |
+ } |
+ } |
+ return problemReported; |
+ } |
+ |
+ /** |
+ * Verify that if the given [constructor] declaration is 'const' then there |
+ * are no invocations of non-'const' super constructors. |
+ * |
+ * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER]. |
+ */ |
+ bool _checkForConstConstructorWithNonConstSuper( |
+ ConstructorDeclaration constructor) { |
+ if (!_isEnclosingConstructorConst) { |
+ return false; |
+ } |
+ // OK, const factory, checked elsewhere |
+ if (constructor.factoryKeyword != null) { |
+ return false; |
+ } |
+ // check for mixins |
+ if (_enclosingClass.mixins.length != 0) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN, |
+ constructor.returnType); |
+ return true; |
+ } |
+ // try to find and check super constructor invocation |
+ for (ConstructorInitializer initializer in constructor.initializers) { |
+ if (initializer is SuperConstructorInvocation) { |
+ SuperConstructorInvocation superInvocation = initializer; |
+ ConstructorElement element = superInvocation.staticElement; |
+ if (element == null || element.isConst) { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
+ superInvocation, [element.enclosingElement.displayName]); |
+ return true; |
+ } |
+ } |
+ // no explicit super constructor invocation, check default constructor |
+ InterfaceType supertype = _enclosingClass.supertype; |
+ if (supertype == null) { |
+ return false; |
+ } |
+ if (supertype.isObject) { |
+ return false; |
+ } |
+ ConstructorElement unnamedConstructor = |
+ supertype.element.unnamedConstructor; |
+ if (unnamedConstructor == null) { |
+ return false; |
+ } |
+ if (unnamedConstructor.isConst) { |
+ return false; |
+ } |
+ // default constructor is not 'const', report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
+ constructor.returnType, [supertype.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that if the given [constructor] declaration is 'const' then there |
+ * are no non-final instance variable. The [constructorElement] is the |
+ * constructor element. |
+ * |
+ * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD]. |
+ */ |
+ bool _checkForConstConstructorWithNonFinalField( |
+ ConstructorDeclaration constructor, |
+ ConstructorElement constructorElement) { |
+ if (!_isEnclosingConstructorConst) { |
+ return false; |
+ } |
+ // check if there is non-final field |
+ ClassElement classElement = constructorElement.enclosingElement; |
+ if (!classElement.hasNonFinalField) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, |
+ constructor); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given 'const' instance creation [expression] is not |
+ * creating a deferred type. The [constructorName] is the constructor name, |
+ * always non-`null`. The [typeName] is the name of the type defining the |
+ * constructor, always non-`null`. |
+ * |
+ * See [CompileTimeErrorCode.CONST_DEFERRED_CLASS]. |
+ */ |
+ bool _checkForConstDeferredClass(InstanceCreationExpression expression, |
+ ConstructorName constructorName, TypeName typeName) { |
+ if (typeName.isDeferred) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_DEFERRED_CLASS, constructorName, |
+ [typeName.name.name]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given throw [expression] is not enclosed in a 'const' |
+ * constructor declaration. |
+ * |
+ * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION]. |
+ */ |
+ bool _checkForConstEvalThrowsException(ThrowExpression expression) { |
+ if (_isEnclosingConstructorConst) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given normal formal [parameter] is not 'const'. |
+ * |
+ * See [CompileTimeErrorCode.CONST_FORMAL_PARAMETER]. |
+ */ |
+ bool _checkForConstFormalParameter(NormalFormalParameter parameter) { |
+ if (parameter.isConst) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_FORMAL_PARAMETER, parameter); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given instance creation [expression] is not being invoked |
+ * on an abstract class. The [typeName] is the [TypeName] of the |
+ * [ConstructorName] from the [InstanceCreationExpression], this is the AST |
+ * node that the error is attached to. The [type] is the type being |
+ * constructed with this [InstanceCreationExpression]. |
+ * |
+ * See [StaticWarningCode.CONST_WITH_ABSTRACT_CLASS], and |
+ * [StaticWarningCode.NEW_WITH_ABSTRACT_CLASS]. |
+ */ |
+ bool _checkForConstOrNewWithAbstractClass( |
+ InstanceCreationExpression expression, TypeName typeName, |
+ InterfaceType type) { |
+ if (type.element.isAbstract) { |
+ ConstructorElement element = expression.staticElement; |
+ if (element != null && !element.isFactory) { |
+ if ((expression.keyword as sc.KeywordToken).keyword == |
+ sc.Keyword.CONST) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.CONST_WITH_ABSTRACT_CLASS, typeName); |
+ } else { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, typeName); |
+ } |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given instance creation [expression] is not being invoked |
+ * on an enum. The [typeName] is the [TypeName] of the [ConstructorName] from |
+ * the [InstanceCreationExpression], this is the AST node that the error is |
+ * attached to. The [type] is the type being constructed with this |
+ * [InstanceCreationExpression]. |
+ * |
+ * See [CompileTimeErrorCode.INSTANTIATE_ENUM]. |
+ */ |
+ bool _checkForConstOrNewWithEnum(InstanceCreationExpression expression, |
+ TypeName typeName, InterfaceType type) { |
+ if (type.element.isEnum) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INSTANTIATE_ENUM, typeName); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given 'const' instance creation [expression] is not being |
+ * invoked on a constructor that is not 'const'. |
+ * |
+ * This method assumes that the instance creation was tested to be 'const' |
+ * before being called. |
+ * |
+ * See [CompileTimeErrorCode.CONST_WITH_NON_CONST]. |
+ */ |
+ bool _checkForConstWithNonConst(InstanceCreationExpression expression) { |
+ ConstructorElement constructorElement = expression.staticElement; |
+ if (constructorElement != null && !constructorElement.isConst) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_WITH_NON_CONST, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given [typeName] does not reference any type parameters. |
+ * |
+ * See [CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS]. |
+ */ |
+ bool _checkForConstWithTypeParameters(TypeName typeName) { |
+ // something wrong with AST |
+ if (typeName == null) { |
+ return false; |
+ } |
+ Identifier name = typeName.name; |
+ if (name == null) { |
+ return false; |
+ } |
+ // should not be a type parameter |
+ if (name.staticElement is TypeParameterElement) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS, name); |
+ } |
+ // check type arguments |
+ TypeArgumentList typeArguments = typeName.typeArguments; |
+ if (typeArguments != null) { |
+ bool hasError = false; |
+ for (TypeName argument in typeArguments.arguments) { |
+ if (_checkForConstWithTypeParameters(argument)) { |
+ hasError = true; |
+ } |
+ } |
+ return hasError; |
+ } |
+ // OK |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that if the given 'const' instance creation [expression] is being |
+ * invoked on the resolved constructor. The [constructorName] is the |
+ * constructor name, always non-`null`. The [typeName] is the name of the type |
+ * defining the constructor, always non-`null`. |
+ * |
+ * This method assumes that the instance creation was tested to be 'const' |
+ * before being called. |
+ * |
+ * See [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR], and |
+ * [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT]. |
+ */ |
+ bool _checkForConstWithUndefinedConstructor( |
+ InstanceCreationExpression expression, ConstructorName constructorName, |
+ TypeName typeName) { |
+ // OK if resolved |
+ if (expression.staticElement != null) { |
+ return false; |
+ } |
+ DartType type = typeName.type; |
+ if (type is InterfaceType) { |
+ ClassElement element = type.element; |
+ if (element != null && element.isEnum) { |
+ // We have already reported the error. |
+ return false; |
+ } |
+ } |
+ Identifier className = typeName.name; |
+ // report as named or default constructor absence |
+ SimpleIdentifier name = constructorName.name; |
+ if (name != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR, name, [ |
+ className, |
+ name |
+ ]); |
+ } else { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, |
+ constructorName, [className]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that there are no default parameters in the given function type |
+ * [alias]. |
+ * |
+ * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS]. |
+ */ |
+ bool _checkForDefaultValueInFunctionTypeAlias(FunctionTypeAlias alias) { |
+ bool result = false; |
+ FormalParameterList formalParameterList = alias.parameters; |
+ NodeList<FormalParameter> parameters = formalParameterList.parameters; |
+ for (FormalParameter formalParameter in parameters) { |
+ if (formalParameter is DefaultFormalParameter) { |
+ DefaultFormalParameter defaultFormalParameter = formalParameter; |
+ if (defaultFormalParameter.defaultValue != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS, alias); |
+ result = true; |
+ } |
+ } |
+ } |
+ return result; |
+ } |
+ |
+ /** |
+ * Verify that the given default formal [parameter] is not part of a function |
+ * typed parameter. |
+ * |
+ * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER]. |
+ */ |
+ bool _checkForDefaultValueInFunctionTypedParameter( |
+ DefaultFormalParameter parameter) { |
+ // OK, not in a function typed parameter. |
+ if (!_isInFunctionTypedFormalParameter) { |
+ return false; |
+ } |
+ // OK, no default value. |
+ if (parameter.defaultValue == null) { |
+ return false; |
+ } |
+ // Report problem. |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER, |
+ parameter); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that any deferred imports in the given compilation [unit] have a |
+ * unique prefix. |
+ * |
+ * See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX]. |
+ */ |
+ bool _checkForDeferredPrefixCollisions(CompilationUnit unit) { |
+ bool foundError = false; |
+ NodeList<Directive> directives = unit.directives; |
+ int count = directives.length; |
+ if (count > 0) { |
+ HashMap<PrefixElement, List<ImportDirective>> prefixToDirectivesMap = |
+ new HashMap<PrefixElement, List<ImportDirective>>(); |
+ for (int i = 0; i < count; i++) { |
+ Directive directive = directives[i]; |
+ if (directive is ImportDirective) { |
+ ImportDirective importDirective = directive; |
+ SimpleIdentifier prefix = importDirective.prefix; |
+ if (prefix != null) { |
+ Element element = prefix.staticElement; |
+ if (element is PrefixElement) { |
+ PrefixElement prefixElement = element; |
+ List<ImportDirective> elements = |
+ prefixToDirectivesMap[prefixElement]; |
+ if (elements == null) { |
+ elements = new List<ImportDirective>(); |
+ prefixToDirectivesMap[prefixElement] = elements; |
+ } |
+ elements.add(importDirective); |
+ } |
+ } |
+ } |
+ } |
+ for (List<ImportDirective> imports in prefixToDirectivesMap.values) { |
+ if (_hasDeferredPrefixCollision(imports)) { |
+ foundError = true; |
+ } |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that the enclosing class does not have an instance member with the |
+ * given name of the static member. |
+ * |
+ * See [CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE]. |
+ */ |
+ bool _checkForDuplicateDefinitionInheritance() { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ bool hasProblem = false; |
+ for (ExecutableElement member in _enclosingClass.methods) { |
+ if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) { |
+ hasProblem = true; |
+ } |
+ } |
+ for (ExecutableElement member in _enclosingClass.accessors) { |
+ if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) { |
+ hasProblem = true; |
+ } |
+ } |
+ return hasProblem; |
+ } |
+ |
+ /** |
+ * Verify that the enclosing class does not have an instance member with the |
+ * given name of the [staticMember]. |
+ * |
+ * See [CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE]. |
+ */ |
+ bool _checkForDuplicateDefinitionOfMember(ExecutableElement staticMember) { |
+ // prepare name |
+ String name = staticMember.name; |
+ if (name == null) { |
+ return false; |
+ } |
+ // try to find member |
+ ExecutableElement inheritedMember = |
+ _inheritanceManager.lookupInheritance(_enclosingClass, name); |
+ if (inheritedMember == null) { |
+ return false; |
+ } |
+ // OK, also static |
+ if (inheritedMember.isStatic) { |
+ return false; |
+ } |
+ // determine the display name, use the extended display name if the |
+ // enclosing class of the inherited member is in a different source |
+ String displayName; |
+ Element enclosingElement = inheritedMember.enclosingElement; |
+ if (enclosingElement.source == _enclosingClass.source) { |
+ displayName = enclosingElement.displayName; |
+ } else { |
+ displayName = enclosingElement.getExtendedDisplayName(null); |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE, |
+ staticMember.nameOffset, name.length, [name, displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that if the given list [literal] has type arguments then there is |
+ * exactly one. The [typeArguments] are the type arguments. |
+ * |
+ * See [StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS]. |
+ */ |
+ bool _checkForExpectedOneListTypeArgument( |
+ ListLiteral literal, TypeArgumentList typeArguments) { |
+ // check number of type arguments |
+ int num = typeArguments.arguments.length; |
+ if (num == 1) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS, typeArguments, |
+ [num]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given export [directive] has a unique name among other |
+ * exported libraries. 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.EXPORT_DUPLICATED_LIBRARY_NAME]. |
+ */ |
+ bool _checkForExportDuplicateLibraryName(ExportDirective directive, |
+ ExportElement exportElement, LibraryElement exportedLibrary) { |
+ if (exportedLibrary == null) { |
+ return false; |
+ } |
+ String name = exportedLibrary.name; |
+ // check if there is other exported library with the same name |
+ LibraryElement prevLibrary = _nameToExportElement[name]; |
+ if (prevLibrary != null) { |
+ if (prevLibrary != exportedLibrary) { |
+ if (!name.isEmpty) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.EXPORT_DUPLICATED_LIBRARY_NAMED, directive, [ |
+ prevLibrary.definingCompilationUnit.displayName, |
+ exportedLibrary.definingCompilationUnit.displayName, |
+ name |
+ ]); |
+ } |
+ return true; |
+ } |
+ } else { |
+ _nameToExportElement[name] = exportedLibrary; |
+ } |
+ // OK |
+ return false; |
+ } |
+ |
+ /** |
+ * Check that if the visiting library is not system, then any given library |
+ * should not be SDK internal library. The [exportElement] is the |
+ * [ExportElement] retrieved from the node, if the element in the node was |
+ * `null`, then this method is not called. |
+ * |
+ * See [CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY]. |
+ */ |
+ bool _checkForExportInternalLibrary( |
+ ExportDirective directive, ExportElement exportElement) { |
+ if (_isInSystemLibrary) { |
+ return false; |
+ } |
+ // should be private |
+ DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
+ String uri = exportElement.uri; |
+ SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri); |
+ if (sdkLibrary == null) { |
+ return false; |
+ } |
+ if (!sdkLibrary.isInternal) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, directive, |
+ [directive.uri]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given extends [clause] does not extend a deferred class. |
+ * |
+ * See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS]. |
+ */ |
+ bool _checkForExtendsDeferredClass(ExtendsClause clause) { |
+ if (clause == null) { |
+ return false; |
+ } |
+ return _checkForExtendsOrImplementsDeferredClass( |
+ clause.superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS); |
+ } |
+ |
+ /** |
+ * Verify that the given type [alias] does not extend a deferred class. |
+ * |
+ * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]. |
+ */ |
+ bool _checkForExtendsDeferredClassInTypeAlias(ClassTypeAlias alias) { |
+ if (alias == null) { |
+ return false; |
+ } |
+ return _checkForExtendsOrImplementsDeferredClass( |
+ alias.superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS); |
+ } |
+ |
+ /** |
+ * Verify that the given extends [clause] does not extend classes such as |
+ * 'num' or 'String'. |
+ * |
+ * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]. |
+ */ |
+ bool _checkForExtendsDisallowedClass(ExtendsClause clause) { |
+ if (clause == null) { |
+ return false; |
+ } |
+ return _checkForExtendsOrImplementsDisallowedClass( |
+ clause.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS); |
+ } |
+ |
+ /** |
+ * Verify that the given type [alias] does not extend classes such as 'num' or |
+ * 'String'. |
+ * |
+ * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]. |
+ */ |
+ bool _checkForExtendsDisallowedClassInTypeAlias(ClassTypeAlias alias) { |
+ if (alias == null) { |
+ return false; |
+ } |
+ return _checkForExtendsOrImplementsDisallowedClass( |
+ alias.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS); |
+ } |
+ |
+ /** |
+ * Verify that the given [typeName] does not extend, implement or mixin |
+ * classes that are deferred. |
+ * |
+ * See [_checkForExtendsDeferredClass], |
+ * [_checkForExtendsDeferredClassInTypeAlias], |
+ * [_checkForImplementsDeferredClass], |
+ * [_checkForAllMixinErrorCodes], |
+ * [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS], |
+ * [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS], and |
+ * [CompileTimeErrorCode.MIXIN_DEFERRED_CLASS]. |
+ */ |
+ bool _checkForExtendsOrImplementsDeferredClass( |
+ TypeName typeName, ErrorCode errorCode) { |
+ if (typeName.isSynthetic) { |
+ return false; |
+ } |
+ if (typeName.isDeferred) { |
+ _errorReporter.reportErrorForNode( |
+ errorCode, typeName, [typeName.name.name]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given [typeName] does not extend, implement or mixin |
+ * classes such as 'num' or 'String'. |
+ * |
+ * See [_checkForExtendsDisallowedClass], |
+ * [_checkForExtendsDisallowedClassInTypeAlias], |
+ * [_checkForImplementsDisallowedClass], |
+ * [_checkForAllMixinErrorCodes], |
+ * [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS], |
+ * [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS], and |
+ * [CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS]. |
+ */ |
+ bool _checkForExtendsOrImplementsDisallowedClass( |
+ TypeName typeName, ErrorCode errorCode) { |
+ if (typeName.isSynthetic) { |
+ return false; |
+ } |
+ DartType superType = typeName.type; |
+ for (InterfaceType disallowedType |
+ in _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT) { |
+ if (superType != null && superType == disallowedType) { |
+ // if the violating type happens to be 'num', we need to rule out the |
+ // case where the enclosing class is 'int' or 'double' |
+ if (superType == _typeProvider.numType) { |
+ AstNode grandParent = typeName.parent.parent; |
+ // Note: this is a corner case that won't happen often, so adding a |
+ // field currentClass (see currentFunction) to ErrorVerifier isn't |
+ // worth if for this case, but if the field currentClass is added, |
+ // then this message should become a todo to not lookup the |
+ // grandparent node. |
+ if (grandParent is ClassDeclaration) { |
+ ClassElement classElement = grandParent.element; |
+ DartType classType = classElement.type; |
+ if (classType != null && |
+ (classType == _intType || |
+ classType == _typeProvider.doubleType)) { |
+ return false; |
+ } |
+ } |
+ } |
+ // otherwise, report the error |
+ _errorReporter.reportErrorForNode( |
+ errorCode, typeName, [disallowedType.displayName]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given constructor field [initializer] has compatible field |
+ * and initializer expression types. The [staticElement] is the static element |
+ * from the name in the [ConstructorFieldInitializer]. |
+ * |
+ * See [CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE], and |
+ * [StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE]. |
+ */ |
+ bool _checkForFieldInitializerNotAssignable( |
+ ConstructorFieldInitializer initializer, Element staticElement) { |
+ // prepare field element |
+ if (staticElement is! FieldElement) { |
+ return false; |
+ } |
+ FieldElement fieldElement = staticElement as FieldElement; |
+ // prepare field type |
+ DartType fieldType = fieldElement.type; |
+ // prepare expression type |
+ Expression expression = initializer.expression; |
+ if (expression == null) { |
+ return false; |
+ } |
+ // test the static type of the expression |
+ DartType staticType = getStaticType(expression); |
+ if (staticType == null) { |
+ return false; |
+ } |
+ if (staticType.isAssignableTo(fieldType)) { |
+ return false; |
+ } |
+ // report problem |
+ if (_isEnclosingConstructorConst) { |
+ // TODO(paulberry): this error should be based on the actual type of the |
+ // constant, not the static type. See dartbug.com/21119. |
+ _errorReporter.reportTypeErrorForNode( |
+ CheckedModeCompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE, |
+ expression, [staticType, fieldType]); |
+ } |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE, expression, [ |
+ staticType, |
+ fieldType |
+ ]); |
+ return true; |
+ // TODO(brianwilkerson) Define a hint corresponding to these errors and |
+ // report it if appropriate. |
+// // test the propagated type of the expression |
+// Type propagatedType = expression.getPropagatedType(); |
+// if (propagatedType != null && propagatedType.isAssignableTo(fieldType)) { |
+// return false; |
+// } |
+// // report problem |
+// if (isEnclosingConstructorConst) { |
+// errorReporter.reportTypeErrorForNode( |
+// CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE, |
+// expression, |
+// propagatedType == null ? staticType : propagatedType, |
+// fieldType); |
+// } else { |
+// errorReporter.reportTypeErrorForNode( |
+// StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE, |
+// expression, |
+// propagatedType == null ? staticType : propagatedType, |
+// fieldType); |
+// } |
+// return true; |
+ } |
+ |
+ /** |
+ * Verify that the given field formal [parameter] is in a constructor |
+ * declaration. |
+ * |
+ * See [CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR]. |
+ */ |
+ bool _checkForFieldInitializingFormalRedirectingConstructor( |
+ FieldFormalParameter parameter) { |
+ ConstructorDeclaration constructor = |
+ parameter.getAncestor((node) => node is ConstructorDeclaration); |
+ if (constructor == null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, |
+ parameter); |
+ return true; |
+ } |
+ // constructor cannot be a factory |
+ if (constructor.factoryKeyword != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR, |
+ parameter); |
+ return true; |
+ } |
+ // constructor cannot have a redirection |
+ for (ConstructorInitializer initializer in constructor.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, |
+ parameter); |
+ return true; |
+ } |
+ } |
+ // OK |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given variable declaration [list] has only initialized |
+ * variables if the list is final or const. |
+ * |
+ * See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and |
+ * [StaticWarningCode.FINAL_NOT_INITIALIZED]. |
+ */ |
+ bool _checkForFinalNotInitialized(VariableDeclarationList list) { |
+ if (_isInNativeClass) { |
+ return false; |
+ } |
+ bool foundError = false; |
+ if (!list.isSynthetic) { |
+ NodeList<VariableDeclaration> variables = list.variables; |
+ for (VariableDeclaration variable in variables) { |
+ if (variable.initializer == null) { |
+ if (list.isConst) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.CONST_NOT_INITIALIZED, variable.name, |
+ [variable.name.name]); |
+ } else if (list.isFinal) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.FINAL_NOT_INITIALIZED, variable.name, |
+ [variable.name.name]); |
+ } |
+ foundError = true; |
+ } |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that final fields in the given clas [declaration] that are declared, |
+ * without any constructors in the enclosing class, are initialized. Cases in |
+ * which there is at least one constructor are handled at the end of |
+ * [_checkForAllFinalInitializedErrorCodes]. |
+ * |
+ * See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and |
+ * [StaticWarningCode.FINAL_NOT_INITIALIZED]. |
+ */ |
+ bool _checkForFinalNotInitializedInClass(ClassDeclaration declaration) { |
+ NodeList<ClassMember> classMembers = declaration.members; |
+ for (ClassMember classMember in classMembers) { |
+ if (classMember is ConstructorDeclaration) { |
+ return false; |
+ } |
+ } |
+ bool foundError = false; |
+ for (ClassMember classMember in classMembers) { |
+ if (classMember is FieldDeclaration && |
+ _checkForFinalNotInitialized(classMember.fields)) { |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * If the current function is async, async*, or sync*, verify that its |
+ * declared return type is assignable to Future, Stream, or Iterable, |
+ * respectively. If not, report the error using [returnType]. |
+ */ |
+ void _checkForIllegalReturnType(TypeName returnType) { |
+ if (returnType == null) { |
+ // No declared return type, so the return type must be dynamic, which is |
+ // assignable to everything. |
+ return; |
+ } |
+ if (_enclosingFunction.isAsynchronous) { |
+ if (_enclosingFunction.isGenerator) { |
+ if (!_enclosingFunction.returnType |
+ .isAssignableTo(_typeProvider.streamDynamicType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE, |
+ returnType); |
+ } |
+ } else { |
+ if (!_enclosingFunction.returnType |
+ .isAssignableTo(_typeProvider.futureDynamicType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.ILLEGAL_ASYNC_RETURN_TYPE, returnType); |
+ } |
+ } |
+ } else if (_enclosingFunction.isGenerator) { |
+ if (!_enclosingFunction.returnType |
+ .isAssignableTo(_typeProvider.iterableDynamicType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.ILLEGAL_SYNC_GENERATOR_RETURN_TYPE, |
+ returnType); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Verify that the given implements [clause] does not implement classes that |
+ * are deferred. |
+ * |
+ * See [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS]. |
+ */ |
+ bool _checkForImplementsDeferredClass(ImplementsClause clause) { |
+ if (clause == null) { |
+ return false; |
+ } |
+ bool foundError = false; |
+ for (TypeName type in clause.interfaces) { |
+ if (_checkForExtendsOrImplementsDeferredClass( |
+ type, CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS)) { |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that the given implements [clause] does not implement classes such |
+ * as 'num' or 'String'. |
+ * |
+ * See [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]. |
+ */ |
+ bool _checkForImplementsDisallowedClass(ImplementsClause clause) { |
+ if (clause == null) { |
+ return false; |
+ } |
+ bool foundError = false; |
+ for (TypeName type in clause.interfaces) { |
+ if (_checkForExtendsOrImplementsDisallowedClass( |
+ type, CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS)) { |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that if the given [identifier] is part of a constructor initializer, |
+ * then it does not implicitly reference 'this' expression. |
+ * |
+ * See [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER], and |
+ * [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC]. |
+ * TODO(scheglov) rename thid method |
+ */ |
+ bool _checkForImplicitThisReferenceInInitializer( |
+ SimpleIdentifier identifier) { |
+ if (!_isInConstructorInitializer && |
+ !_isInStaticMethod && |
+ !_isInFactory && |
+ !_isInInstanceVariableInitializer && |
+ !_isInStaticVariableDeclaration) { |
+ return false; |
+ } |
+ // prepare element |
+ Element element = identifier.staticElement; |
+ if (!(element is MethodElement || element is PropertyAccessorElement)) { |
+ return false; |
+ } |
+ // static element |
+ ExecutableElement executableElement = element as ExecutableElement; |
+ if (executableElement.isStatic) { |
+ return false; |
+ } |
+ // not a class member |
+ Element enclosingElement = element.enclosingElement; |
+ if (enclosingElement is! ClassElement) { |
+ return false; |
+ } |
+ // comment |
+ AstNode parent = identifier.parent; |
+ if (parent is CommentReference) { |
+ return false; |
+ } |
+ // qualified method invocation |
+ if (parent is MethodInvocation) { |
+ MethodInvocation invocation = parent; |
+ if (identical(invocation.methodName, identifier) && |
+ invocation.realTarget != null) { |
+ return false; |
+ } |
+ } |
+ // qualified property access |
+ if (parent is PropertyAccess) { |
+ PropertyAccess access = parent; |
+ if (identical(access.propertyName, identifier) && |
+ access.realTarget != null) { |
+ return false; |
+ } |
+ } |
+ if (parent is PrefixedIdentifier) { |
+ PrefixedIdentifier prefixed = parent; |
+ if (identical(prefixed.identifier, identifier)) { |
+ return false; |
+ } |
+ } |
+ // report problem |
+ if (_isInStaticMethod) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier); |
+ } else if (_isInFactory) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier); |
+ } else { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER, |
+ identifier); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given import [directive] has a unique name among other |
+ * imported libraries. The [importElement] is the [ImportElement] retrieved |
+ * from the node, if the element in the node was `null`, then this method is |
+ * not called. |
+ * |
+ * See [CompileTimeErrorCode.IMPORT_DUPLICATED_LIBRARY_NAME]. |
+ */ |
+ bool _checkForImportDuplicateLibraryName( |
+ ImportDirective directive, ImportElement importElement) { |
+ // prepare imported library |
+ LibraryElement nodeLibrary = importElement.importedLibrary; |
+ if (nodeLibrary == null) { |
+ return false; |
+ } |
+ String name = nodeLibrary.name; |
+ // check if there is another imported library with the same name |
+ LibraryElement prevLibrary = _nameToImportElement[name]; |
+ if (prevLibrary != null) { |
+ if (prevLibrary != nodeLibrary) { |
+ if (!name.isEmpty) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.IMPORT_DUPLICATED_LIBRARY_NAMED, directive, [ |
+ prevLibrary.definingCompilationUnit.displayName, |
+ nodeLibrary.definingCompilationUnit.displayName, |
+ name |
+ ]); |
+ } |
+ return true; |
+ } |
+ } else { |
+ _nameToImportElement[name] = nodeLibrary; |
+ } |
+ // OK |
+ return false; |
+ } |
+ |
+ /** |
+ * Check that if the visiting library is not system, then any given library |
+ * should not be SDK internal library. The [importElement] is the |
+ * [ImportElement] retrieved from the node, if the element in the node was |
+ * `null`, then this method is not called |
+ * |
+ * See [CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY]. |
+ */ |
+ bool _checkForImportInternalLibrary( |
+ ImportDirective directive, ImportElement importElement) { |
+ if (_isInSystemLibrary) { |
+ return false; |
+ } |
+ // should be private |
+ DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
+ String uri = importElement.uri; |
+ SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri); |
+ if (sdkLibrary == null) { |
+ return false; |
+ } |
+ if (!sdkLibrary.isInternal) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, directive, |
+ [directive.uri]); |
+ return true; |
+ } |
+ |
+ /** |
+ * For each class declaration, this method is called which verifies that all |
+ * inherited members are inherited consistently. |
+ * |
+ * See [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE]. |
+ */ |
+ bool _checkForInconsistentMethodInheritance() { |
+ // Ensure that the inheritance manager has a chance to generate all errors |
+ // we may care about, note that we ensure that the interfaces data since |
+ // there are no errors. |
+ _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass); |
+ HashSet<AnalysisError> errors = |
+ _inheritanceManager.getErrors(_enclosingClass); |
+ if (errors == null || errors.isEmpty) { |
+ return false; |
+ } |
+ for (AnalysisError error in errors) { |
+ _errorReporter.reportError(error); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that the given [typeReference] is not a type reference and that then |
+ * the [name] is reference to an instance member. |
+ * |
+ * See [StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER]. |
+ */ |
+ bool _checkForInstanceAccessToStaticMember( |
+ ClassElement typeReference, SimpleIdentifier name) { |
+ // OK, in comment |
+ if (_isInComment) { |
+ return false; |
+ } |
+ // OK, target is a type |
+ if (typeReference != null) { |
+ return false; |
+ } |
+ // prepare member Element |
+ Element element = name.staticElement; |
+ if (element is! ExecutableElement) { |
+ return false; |
+ } |
+ ExecutableElement executableElement = element as ExecutableElement; |
+ // OK, top-level element |
+ if (executableElement.enclosingElement is! ClassElement) { |
+ return false; |
+ } |
+ // OK, instance member |
+ if (!executableElement.isStatic) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, name, |
+ [name.name]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check whether the given [executableElement] collides with the name of a |
+ * static method in one of its superclasses, and reports the appropriate |
+ * warning if it does. The [errorNameTarget] is the node to report problems |
+ * on. |
+ * |
+ * See [StaticTypeWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC]. |
+ */ |
+ bool _checkForInstanceMethodNameCollidesWithSuperclassStatic( |
+ ExecutableElement executableElement, SimpleIdentifier errorNameTarget) { |
+ String executableElementName = executableElement.name; |
+ if (executableElement is! PropertyAccessorElement && |
+ !executableElement.isOperator) { |
+ HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
+ InterfaceType superclassType = _enclosingClass.supertype; |
+ ClassElement superclassElement = |
+ superclassType == null ? null : superclassType.element; |
+ bool executableElementPrivate = |
+ Identifier.isPrivateName(executableElementName); |
+ while (superclassElement != null && |
+ !visitedClasses.contains(superclassElement)) { |
+ visitedClasses.add(superclassElement); |
+ LibraryElement superclassLibrary = superclassElement.library; |
+ // Check fields. |
+ FieldElement fieldElt = |
+ superclassElement.getField(executableElementName); |
+ if (fieldElt != null) { |
+ // Ignore if private in a different library - cannot collide. |
+ if (executableElementPrivate && |
+ _currentLibrary != superclassLibrary) { |
+ continue; |
+ } |
+ // instance vs. static |
+ if (fieldElt.isStatic) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, |
+ errorNameTarget, [ |
+ executableElementName, |
+ fieldElt.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ } |
+ // Check methods. |
+ List<MethodElement> methodElements = superclassElement.methods; |
+ for (MethodElement methodElement in methodElements) { |
+ // We need the same name. |
+ if (methodElement.name != executableElementName) { |
+ continue; |
+ } |
+ // Ignore if private in a different library - cannot collide. |
+ if (executableElementPrivate && |
+ _currentLibrary != superclassLibrary) { |
+ continue; |
+ } |
+ // instance vs. static |
+ if (methodElement.isStatic) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC, |
+ errorNameTarget, [ |
+ executableElementName, |
+ methodElement.enclosingElement.displayName |
+ ]); |
+ return true; |
+ } |
+ } |
+ superclassType = superclassElement.supertype; |
+ superclassElement = |
+ superclassType == null ? null : superclassType.element; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that an 'int' can be assigned to the parameter corresponding to the |
+ * given [argument]. This is used for prefix and postfix expressions where |
+ * the argument value is implicit. |
+ * |
+ * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
+ */ |
+ bool _checkForIntNotAssignable(Expression argument) { |
+ if (argument == null) { |
+ return false; |
+ } |
+ ParameterElement staticParameterElement = argument.staticParameterElement; |
+ DartType staticParameterType = |
+ staticParameterElement == null ? null : staticParameterElement.type; |
+ return _checkForArgumentTypeNotAssignable(argument, staticParameterType, |
+ _intType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
+ } |
+ |
+ /** |
+ * Verify that the given [annotation] isn't defined in a deferred library. |
+ * |
+ * See [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY]. |
+ */ |
+ bool _checkForInvalidAnnotationFromDeferredLibrary(Annotation annotation) { |
+ Identifier nameIdentifier = annotation.name; |
+ if (nameIdentifier is PrefixedIdentifier) { |
+ if (nameIdentifier.isDeferred) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY, |
+ annotation.name); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given left hand side ([lhs]) and right hand side ([rhs]) |
+ * represent a valid assignment. |
+ * |
+ * See [StaticTypeWarningCode.INVALID_ASSIGNMENT]. |
+ */ |
+ bool _checkForInvalidAssignment(Expression lhs, Expression rhs) { |
+ if (lhs == null || rhs == null) { |
+ return false; |
+ } |
+ VariableElement leftVariableElement = getVariableElement(lhs); |
+ DartType leftType = (leftVariableElement == null) |
+ ? getStaticType(lhs) |
+ : leftVariableElement.type; |
+ DartType staticRightType = getStaticType(rhs); |
+ if (!staticRightType.isAssignableTo(leftType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [ |
+ staticRightType, |
+ leftType |
+ ]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Given an [assignment] using a compound assignment operator, this verifies |
+ * that the given assignment is valid. The [lhs] is the left hand side |
+ * expression. The [rhs] is the right hand side expression. |
+ * |
+ * See [StaticTypeWarningCode.INVALID_ASSIGNMENT]. |
+ */ |
+ bool _checkForInvalidCompoundAssignment( |
+ AssignmentExpression assignment, Expression lhs, Expression rhs) { |
+ if (lhs == null) { |
+ return false; |
+ } |
+ VariableElement leftVariableElement = getVariableElement(lhs); |
+ DartType leftType = (leftVariableElement == null) |
+ ? getStaticType(lhs) |
+ : leftVariableElement.type; |
+ MethodElement invokedMethod = assignment.staticElement; |
+ if (invokedMethod == null) { |
+ return false; |
+ } |
+ DartType rightType = invokedMethod.type.returnType; |
+ if (leftType == null || rightType == null) { |
+ return false; |
+ } |
+ if (!rightType.isAssignableTo(leftType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [rightType, leftType]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check the given [initializer] to ensure that the field being initialized is |
+ * a valid field. The [fieldName] is the field name from the |
+ * [ConstructorFieldInitializer]. The [staticElement] is the static element |
+ * from the name in the [ConstructorFieldInitializer]. |
+ */ |
+ void _checkForInvalidField(ConstructorFieldInitializer initializer, |
+ SimpleIdentifier fieldName, Element staticElement) { |
+ if (staticElement is FieldElement) { |
+ FieldElement fieldElement = staticElement; |
+ if (fieldElement.isSynthetic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, |
+ initializer, [fieldName]); |
+ } else if (fieldElement.isStatic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, initializer, |
+ [fieldName]); |
+ } |
+ } else { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, initializer, |
+ [fieldName]); |
+ return; |
+ } |
+ } |
+ |
+ /** |
+ * Check to see whether the given function [body] has a modifier associated |
+ * with it, and report it as an error if it does. |
+ */ |
+ bool _checkForInvalidModifierOnBody( |
+ FunctionBody body, CompileTimeErrorCode errorCode) { |
+ sc.Token keyword = body.keyword; |
+ if (keyword != null) { |
+ _errorReporter.reportErrorForToken(errorCode, keyword, [keyword.lexeme]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the usage of the given 'this' is valid. |
+ * |
+ * See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS]. |
+ */ |
+ bool _checkForInvalidReferenceToThis(ThisExpression expression) { |
+ if (!_isThisInValidContext(expression)) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Checks to ensure that the given list of type [arguments] does not have a |
+ * type parameter as a type argument. The [errorCode] is either |
+ * [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or |
+ * [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP]. |
+ */ |
+ bool _checkForInvalidTypeArgumentInConstTypedLiteral( |
+ NodeList<TypeName> arguments, ErrorCode errorCode) { |
+ bool foundError = false; |
+ for (TypeName typeName in arguments) { |
+ if (typeName.type is TypeParameterType) { |
+ _errorReporter.reportErrorForNode(errorCode, typeName, [typeName.name]); |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Verify that the elements given list [literal] are subtypes of the specified |
+ * element type. The [typeArguments] are the type arguments. |
+ * |
+ * See [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], and |
+ * [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE]. |
+ */ |
+ bool _checkForListElementTypeNotAssignable( |
+ ListLiteral literal, TypeArgumentList typeArguments) { |
+ NodeList<TypeName> typeNames = typeArguments.arguments; |
+ if (typeNames.length < 1) { |
+ return false; |
+ } |
+ DartType listElementType = typeNames[0].type; |
+ // Check every list element. |
+ bool hasProblems = false; |
+ for (Expression element in literal.elements) { |
+ if (literal.constKeyword != null) { |
+ // TODO(paulberry): this error should be based on the actual type of the |
+ // list element, not the static type. See dartbug.com/21119. |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes(element, |
+ listElementType, |
+ CheckedModeCompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ } |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes(element, |
+ listElementType, |
+ StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ } |
+ return hasProblems; |
+ } |
+ |
+ /** |
+ * Verify that the key/value of entries of the given map [literal] are |
+ * subtypes of the key/value types specified in the type arguments. The |
+ * [typeArguments] are the type arguments. |
+ * |
+ * See [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]. |
+ */ |
+ bool _checkForMapTypeNotAssignable( |
+ MapLiteral literal, TypeArgumentList typeArguments) { |
+ // Prepare maps key/value types. |
+ NodeList<TypeName> typeNames = typeArguments.arguments; |
+ if (typeNames.length < 2) { |
+ return false; |
+ } |
+ DartType keyType = typeNames[0].type; |
+ DartType valueType = typeNames[1].type; |
+ // Check every map entry. |
+ bool hasProblems = false; |
+ NodeList<MapLiteralEntry> entries = literal.entries; |
+ for (MapLiteralEntry entry in entries) { |
+ Expression key = entry.key; |
+ Expression value = entry.value; |
+ if (literal.constKeyword != null) { |
+ // TODO(paulberry): this error should be based on the actual type of the |
+ // list element, not the static type. See dartbug.com/21119. |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes(key, keyType, |
+ CheckedModeCompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes(value, |
+ valueType, |
+ CheckedModeCompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ } |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
+ key, keyType, StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
+ value, valueType, StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) { |
+ hasProblems = true; |
+ } |
+ } |
+ return hasProblems; |
+ } |
+ |
+ /** |
+ * Verify that the [_enclosingClass] does not define members with the same name |
+ * as the enclosing class. |
+ * |
+ * See [CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME]. |
+ */ |
+ bool _checkForMemberWithClassName() { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ String className = _enclosingClass.name; |
+ if (className == null) { |
+ return false; |
+ } |
+ bool problemReported = false; |
+ // check accessors |
+ for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
+ if (className == accessor.name) { |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME, accessor.nameOffset, |
+ className.length); |
+ problemReported = true; |
+ } |
+ } |
+ // don't check methods, they would be constructors |
+ // done |
+ return problemReported; |
+ } |
+ |
+ /** |
+ * Check to make sure that all similarly typed accessors are of the same type |
+ * (including inherited accessors). |
+ * |
+ * See [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES], and |
+ * [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE]. |
+ */ |
+ bool _checkForMismatchedAccessorTypes( |
+ Declaration accessorDeclaration, String accessorTextName) { |
+ ExecutableElement accessorElement = |
+ accessorDeclaration.element as ExecutableElement; |
+ if (accessorElement is! PropertyAccessorElement) { |
+ return false; |
+ } |
+ PropertyAccessorElement propertyAccessorElement = |
+ accessorElement as PropertyAccessorElement; |
+ PropertyAccessorElement counterpartAccessor = null; |
+ ClassElement enclosingClassForCounterpart = null; |
+ if (propertyAccessorElement.isGetter) { |
+ counterpartAccessor = propertyAccessorElement.correspondingSetter; |
+ } else { |
+ counterpartAccessor = propertyAccessorElement.correspondingGetter; |
+ // If the setter and getter are in the same enclosing element, return, |
+ // this prevents having MISMATCHED_GETTER_AND_SETTER_TYPES reported twice. |
+ if (counterpartAccessor != null && |
+ identical(counterpartAccessor.enclosingElement, |
+ propertyAccessorElement.enclosingElement)) { |
+ return false; |
+ } |
+ } |
+ if (counterpartAccessor == null) { |
+ // If the accessor is declared in a class, check the superclasses. |
+ if (_enclosingClass != null) { |
+ // Figure out the correct identifier to lookup in the inheritance graph, |
+ // if 'x', then 'x=', or if 'x=', then 'x'. |
+ String lookupIdentifier = propertyAccessorElement.name; |
+ if (StringUtilities.endsWithChar(lookupIdentifier, 0x3D)) { |
+ lookupIdentifier = |
+ lookupIdentifier.substring(0, lookupIdentifier.length - 1); |
+ } else { |
+ lookupIdentifier += "="; |
+ } |
+ // lookup with the identifier. |
+ ExecutableElement elementFromInheritance = _inheritanceManager |
+ .lookupInheritance(_enclosingClass, lookupIdentifier); |
+ // Verify that we found something, and that it is an accessor |
+ if (elementFromInheritance != null && |
+ elementFromInheritance is PropertyAccessorElement) { |
+ enclosingClassForCounterpart = |
+ elementFromInheritance.enclosingElement as ClassElement; |
+ counterpartAccessor = elementFromInheritance; |
+ } |
+ } |
+ if (counterpartAccessor == null) { |
+ return false; |
+ } |
+ } |
+ // Default of null == no accessor or no type (dynamic) |
+ DartType getterType = null; |
+ DartType setterType = null; |
+ // Get an existing counterpart accessor if any. |
+ if (propertyAccessorElement.isGetter) { |
+ getterType = _getGetterType(propertyAccessorElement); |
+ setterType = _getSetterType(counterpartAccessor); |
+ } else if (propertyAccessorElement.isSetter) { |
+ setterType = _getSetterType(propertyAccessorElement); |
+ getterType = _getGetterType(counterpartAccessor); |
+ } |
+ // If either types are not assignable to each other, report an error |
+ // (if the getter is null, it is dynamic which is assignable to everything). |
+ if (setterType != null && |
+ getterType != null && |
+ !getterType.isAssignableTo(setterType)) { |
+ if (enclosingClassForCounterpart == null) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES, |
+ accessorDeclaration, [accessorTextName, setterType, getterType]); |
+ return true; |
+ } else { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE, |
+ accessorDeclaration, [ |
+ accessorTextName, |
+ setterType, |
+ getterType, |
+ enclosingClassForCounterpart.displayName |
+ ]); |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check to make sure that the given switch [statement] whose static type is |
+ * an enum type either have a default case or include all of the enum |
+ * constants. |
+ */ |
+ bool _checkForMissingEnumConstantInSwitch(SwitchStatement statement) { |
+ // TODO(brianwilkerson) This needs to be checked after constant values have |
+ // been computed. |
+ Expression expression = statement.expression; |
+ DartType expressionType = getStaticType(expression); |
+ if (expressionType == null) { |
+ return false; |
+ } |
+ Element expressionElement = expressionType.element; |
+ if (expressionElement is! ClassElement) { |
+ return false; |
+ } |
+ ClassElement classElement = expressionElement as ClassElement; |
+ if (!classElement.isEnum) { |
+ return false; |
+ } |
+ List<String> constantNames = new List<String>(); |
+ List<FieldElement> fields = classElement.fields; |
+ int fieldCount = fields.length; |
+ for (int i = 0; i < fieldCount; i++) { |
+ FieldElement field = fields[i]; |
+ if (field.isStatic && !field.isSynthetic) { |
+ constantNames.add(field.name); |
+ } |
+ } |
+ NodeList<SwitchMember> members = statement.members; |
+ int memberCount = members.length; |
+ for (int i = 0; i < memberCount; i++) { |
+ SwitchMember member = members[i]; |
+ if (member is SwitchDefault) { |
+ return false; |
+ } |
+ String constantName = _getConstantName((member as SwitchCase).expression); |
+ if (constantName != null) { |
+ constantNames.remove(constantName); |
+ } |
+ } |
+ int nameCount = constantNames.length; |
+ if (nameCount == 0) { |
+ return false; |
+ } |
+ for (int i = 0; i < nameCount; i++) { |
+ int offset = statement.offset; |
+ int end = statement.rightParenthesis.end; |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.MISSING_ENUM_CONSTANT_IN_SWITCH, offset, |
+ end - offset, [constantNames[i]]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given function [body] does not contain return statements |
+ * that both have and do not have return values. |
+ * |
+ * See [StaticWarningCode.MIXED_RETURN_TYPES]. |
+ */ |
+ bool _checkForMixedReturns(BlockFunctionBody body) { |
+ if (_hasReturnWithoutValue) { |
+ return false; |
+ } |
+ int withCount = _returnsWith.length; |
+ int withoutCount = _returnsWithout.length; |
+ if (withCount > 0 && withoutCount > 0) { |
+ for (int i = 0; i < withCount; i++) { |
+ _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES, |
+ _returnsWith[i].returnKeyword); |
+ } |
+ for (int i = 0; i < withoutCount; i++) { |
+ _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES, |
+ _returnsWithout[i].returnKeyword); |
+ } |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given mixin does not have an explicitly declared |
+ * constructor. The [mixinName] is the node to report problem on. The |
+ * [mixinElement] is the mixing to evaluate. |
+ * |
+ * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR]. |
+ */ |
+ bool _checkForMixinDeclaresConstructor( |
+ TypeName mixinName, ClassElement mixinElement) { |
+ for (ConstructorElement constructor in mixinElement.constructors) { |
+ if (!constructor.isSynthetic && !constructor.isFactory) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR, mixinName, |
+ [mixinElement.name]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Report the error [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] if |
+ * appropriate. |
+ */ |
+ void _checkForMixinHasNoConstructors(AstNode node) { |
+ if ((_enclosingClass as ClassElementImpl).doesMixinLackConstructors) { |
+ ErrorCode errorCode = CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS; |
+ _errorReporter.reportErrorForNode( |
+ errorCode, node, [_enclosingClass.supertype]); |
+ } |
+ } |
+ |
+ /** |
+ * Verify that the given mixin has the 'Object' superclass. The [mixinName] is |
+ * the node to report problem on. The [mixinElement] is the mixing to |
+ * evaluate. |
+ * |
+ * See [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT]. |
+ */ |
+ bool _checkForMixinInheritsNotFromObject( |
+ TypeName mixinName, ClassElement mixinElement) { |
+ InterfaceType mixinSupertype = mixinElement.supertype; |
+ if (mixinSupertype != null) { |
+ if (!mixinSupertype.isObject || |
+ !mixinElement.isMixinApplication && mixinElement.mixins.length != 0) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, mixinName, |
+ [mixinElement.name]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given mixin does not reference 'super'. The [mixinName] is |
+ * the node to report problem on. The [mixinElement] is the mixing to |
+ * evaluate. |
+ * |
+ * See [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER]. |
+ */ |
+ bool _checkForMixinReferencesSuper( |
+ TypeName mixinName, ClassElement mixinElement) { |
+ if (!enableSuperMixins && mixinElement.hasReferenceToSuper) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.MIXIN_REFERENCES_SUPER, mixinName, |
+ [mixinElement.name]); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given [constructor] has at most one 'super' initializer. |
+ * |
+ * See [CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS]. |
+ */ |
+ bool _checkForMultipleSuperInitializers(ConstructorDeclaration constructor) { |
+ int numSuperInitializers = 0; |
+ for (ConstructorInitializer initializer in constructor.initializers) { |
+ if (initializer is SuperConstructorInvocation) { |
+ numSuperInitializers++; |
+ if (numSuperInitializers > 1) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS, initializer); |
+ } |
+ } |
+ } |
+ return numSuperInitializers > 0; |
+ } |
+ |
+ /** |
+ * Checks to ensure that the given native function [body] is in SDK code. |
+ * |
+ * See [ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE]. |
+ */ |
+ bool _checkForNativeFunctionBodyInNonSDKCode(NativeFunctionBody body) { |
+ if (!_isInSystemLibrary && !_hasExtUri) { |
+ _errorReporter.reportErrorForNode( |
+ ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, body); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given instance creation [expression] invokes an existing |
+ * constructor. The [constructorName] is the constructor name. The [typeName] |
+ * is the name of the type defining the constructor. |
+ * |
+ * This method assumes that the instance creation was tested to be 'new' |
+ * before being called. |
+ * |
+ * See [StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR]. |
+ */ |
+ bool _checkForNewWithUndefinedConstructor( |
+ InstanceCreationExpression expression, ConstructorName constructorName, |
+ TypeName typeName) { |
+ // OK if resolved |
+ if (expression.staticElement != null) { |
+ return false; |
+ } |
+ DartType type = typeName.type; |
+ if (type is InterfaceType) { |
+ ClassElement element = type.element; |
+ if (element != null && element.isEnum) { |
+ // We have already reported the error. |
+ return false; |
+ } |
+ } |
+ // prepare class name |
+ Identifier className = typeName.name; |
+ // report as named or default constructor absence |
+ SimpleIdentifier name = constructorName.name; |
+ if (name != null) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR, name, [ |
+ className, |
+ name |
+ ]); |
+ } else { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, |
+ constructorName, [className]); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that if the given class [declaration] implicitly calls default |
+ * constructor of its superclass, there should be such default constructor - |
+ * implicit or explicit. |
+ * |
+ * See [CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT]. |
+ */ |
+ bool _checkForNoDefaultSuperConstructorImplicit( |
+ ClassDeclaration declaration) { |
+ // do nothing if mixin errors have already been reported for this class. |
+ ClassElementImpl enclosingClass = _enclosingClass; |
+ if (enclosingClass.doesMixinLackConstructors) { |
+ return false; |
+ } |
+ // do nothing if there is explicit constructor |
+ List<ConstructorElement> constructors = _enclosingClass.constructors; |
+ if (!constructors[0].isSynthetic) { |
+ return false; |
+ } |
+ // prepare super |
+ InterfaceType superType = _enclosingClass.supertype; |
+ if (superType == null) { |
+ return false; |
+ } |
+ ClassElement superElement = superType.element; |
+ // try to find default generative super constructor |
+ ConstructorElement superUnnamedConstructor = |
+ superElement.unnamedConstructor; |
+ if (superUnnamedConstructor != null) { |
+ if (superUnnamedConstructor.isFactory) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, declaration.name, |
+ [superUnnamedConstructor]); |
+ return true; |
+ } |
+ if (superUnnamedConstructor.isDefaultConstructor && |
+ _enclosingClass |
+ .isSuperConstructorAccessible(superUnnamedConstructor)) { |
+ return true; |
+ } |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT, |
+ declaration.name, [superType.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that the given class declaration overrides all members required by |
+ * its superclasses and interfaces. The [classNameNode] is the |
+ * [SimpleIdentifier] to be used if there is a violation, this is either the |
+ * named from the [ClassDeclaration] or from the [ClassTypeAlias]. |
+ * |
+ * See [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], |
+ * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO], |
+ * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE], |
+ * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR], and |
+ * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS]. |
+ */ |
+ bool _checkForNonAbstractClassInheritsAbstractMember( |
+ SimpleIdentifier classNameNode) { |
+ if (_enclosingClass.isAbstract) { |
+ return false; |
+ } |
+ // |
+ // Store in local sets the set of all method and accessor names |
+ // |
+ MethodElement method = |
+ _enclosingClass.getMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME); |
+ if (method != null) { |
+ // If the enclosing class declares the method noSuchMethod(), then return. |
+ // From Spec: It is a static warning if a concrete class does not have an |
+ // implementation for a method in any of its superinterfaces unless it |
+ // declares its own noSuchMethod method (7.10). |
+ return false; |
+ } |
+ HashSet<ExecutableElement> missingOverrides = |
+ new HashSet<ExecutableElement>(); |
+ // |
+ // Loop through the set of all executable elements declared in the implicit |
+ // interface. |
+ // |
+ MemberMap membersInheritedFromInterfaces = _inheritanceManager |
+ .getMapOfMembersInheritedFromInterfaces(_enclosingClass); |
+ MemberMap membersInheritedFromSuperclasses = _inheritanceManager |
+ .getMapOfMembersInheritedFromClasses(_enclosingClass); |
+ for (int i = 0; i < membersInheritedFromInterfaces.size; i++) { |
+ String memberName = membersInheritedFromInterfaces.getKey(i); |
+ ExecutableElement executableElt = |
+ membersInheritedFromInterfaces.getValue(i); |
+ if (memberName == null) { |
+ break; |
+ } |
+ // If the element is not synthetic and can be determined to be defined in |
+ // Object, skip it. |
+ if (executableElt.enclosingElement != null && |
+ (executableElt.enclosingElement as ClassElement).type.isObject) { |
+ continue; |
+ } |
+ // Check to see if some element is in local enclosing class that matches |
+ // the name of the required member. |
+ if (_isMemberInClassOrMixin(executableElt, _enclosingClass)) { |
+ // We do not have to verify that this implementation of the found method |
+ // matches the required function type: the set of |
+ // StaticWarningCode.INVALID_METHOD_OVERRIDE_* warnings break out the |
+ // different specific situations. |
+ continue; |
+ } |
+ // First check to see if this element was declared in the superclass |
+ // chain, in which case there is already a concrete implementation. |
+ ExecutableElement elt = membersInheritedFromSuperclasses.get(memberName); |
+ // Check to see if an element was found in the superclass chain with the |
+ // correct name. |
+ if (elt != null) { |
+ // Reference the types, if any are null then continue. |
+ InterfaceType enclosingType = _enclosingClass.type; |
+ FunctionType concreteType = elt.type; |
+ FunctionType requiredMemberType = executableElt.type; |
+ if (enclosingType == null || |
+ concreteType == null || |
+ requiredMemberType == null) { |
+ continue; |
+ } |
+ // Some element was found in the superclass chain that matches the name |
+ // of the required member. |
+ // If it is not abstract and it is the correct one (types match- the |
+ // version of this method that we have has the correct number of |
+ // parameters, etc), then this class has a valid implementation of this |
+ // method, so skip it. |
+ if ((elt is MethodElement && !elt.isAbstract) || |
+ (elt is PropertyAccessorElement && !elt.isAbstract)) { |
+ // Since we are comparing two function types, we need to do the |
+ // appropriate type substitutions first (). |
+ FunctionType foundConcreteFT = _inheritanceManager |
+ .substituteTypeArgumentsInMemberFromInheritance( |
+ concreteType, memberName, enclosingType); |
+ FunctionType requiredMemberFT = _inheritanceManager |
+ .substituteTypeArgumentsInMemberFromInheritance( |
+ requiredMemberType, memberName, enclosingType); |
+ if (foundConcreteFT.isSubtypeOf(requiredMemberFT)) { |
+ continue; |
+ } |
+ } |
+ } |
+ // The not qualifying concrete executable element was found, add it to the |
+ // list. |
+ missingOverrides.add(executableElt); |
+ } |
+ // Now that we have the set of missing overrides, generate a warning on this |
+ // class. |
+ int missingOverridesSize = missingOverrides.length; |
+ if (missingOverridesSize == 0) { |
+ return false; |
+ } |
+ List<ExecutableElement> missingOverridesArray = |
+ new List.from(missingOverrides); |
+ List<String> stringMembersArrayListSet = new List<String>(); |
+ for (int i = 0; i < missingOverridesArray.length; i++) { |
+ String newStrMember; |
+ Element enclosingElement = missingOverridesArray[i].enclosingElement; |
+ String prefix = StringUtilities.EMPTY; |
+ if (missingOverridesArray[i] is PropertyAccessorElement) { |
+ PropertyAccessorElement propertyAccessorElement = |
+ missingOverridesArray[i] as PropertyAccessorElement; |
+ if (propertyAccessorElement.isGetter) { |
+ prefix = _GETTER_SPACE; |
+ // "getter " |
+ } else { |
+ prefix = _SETTER_SPACE; |
+ // "setter " |
+ } |
+ } |
+ if (enclosingElement != null) { |
+ newStrMember = |
+ "$prefix'${enclosingElement.displayName}.${missingOverridesArray[i].displayName}'"; |
+ } else { |
+ newStrMember = "$prefix'${missingOverridesArray[i].displayName}'"; |
+ } |
+ stringMembersArrayListSet.add(newStrMember); |
+ } |
+ List<String> stringMembersArray = new List.from(stringMembersArrayListSet); |
+ AnalysisErrorWithProperties analysisError; |
+ if (stringMembersArray.length == 1) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, |
+ classNameNode, [stringMembersArray[0]]); |
+ } else if (stringMembersArray.length == 2) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO, |
+ classNameNode, [stringMembersArray[0], stringMembersArray[1]]); |
+ } else if (stringMembersArray.length == 3) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE, |
+ classNameNode, [ |
+ stringMembersArray[0], |
+ stringMembersArray[1], |
+ stringMembersArray[2] |
+ ]); |
+ } else if (stringMembersArray.length == 4) { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR, |
+ classNameNode, [ |
+ stringMembersArray[0], |
+ stringMembersArray[1], |
+ stringMembersArray[2], |
+ stringMembersArray[3] |
+ ]); |
+ } else { |
+ analysisError = _errorReporter.newErrorWithProperties( |
+ StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS, |
+ classNameNode, [ |
+ stringMembersArray[0], |
+ stringMembersArray[1], |
+ stringMembersArray[2], |
+ stringMembersArray[3], |
+ stringMembersArray.length - 4 |
+ ]); |
+ } |
+ analysisError.setProperty( |
+ ErrorProperty.UNIMPLEMENTED_METHODS, missingOverridesArray); |
+ _errorReporter.reportError(analysisError); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check to ensure that the [condition] is of type bool, are. Otherwise an |
+ * error is reported on the expression. |
+ * |
+ * See [StaticTypeWarningCode.NON_BOOL_CONDITION]. |
+ */ |
+ bool _checkForNonBoolCondition(Expression condition) { |
+ DartType conditionType = getStaticType(condition); |
+ if (conditionType != null && !conditionType.isAssignableTo(_boolType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.NON_BOOL_CONDITION, condition); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given assert [statement] has either a 'bool' or |
+ * '() -> bool' input. |
+ * |
+ * See [StaticTypeWarningCode.NON_BOOL_EXPRESSION]. |
+ */ |
+ bool _checkForNonBoolExpression(AssertStatement statement) { |
+ Expression expression = statement.condition; |
+ DartType type = getStaticType(expression); |
+ if (type is InterfaceType) { |
+ if (!type.isAssignableTo(_boolType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression); |
+ return true; |
+ } |
+ } else if (type is FunctionType) { |
+ FunctionType functionType = type; |
+ if (functionType.typeArguments.length == 0 && |
+ !functionType.returnType.isAssignableTo(_boolType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Checks to ensure that the given [expression] is assignable to bool. |
+ * |
+ * See [StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION]. |
+ */ |
+ bool _checkForNonBoolNegationExpression(Expression expression) { |
+ DartType conditionType = getStaticType(expression); |
+ if (conditionType != null && !conditionType.isAssignableTo(_boolType)) { |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify the given map [literal] either: |
+ * * has `const modifier` |
+ * * has explicit type arguments |
+ * * is not start of the statement |
+ * |
+ * See [CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT]. |
+ */ |
+ bool _checkForNonConstMapAsExpressionStatement(MapLiteral literal) { |
+ // "const" |
+ if (literal.constKeyword != null) { |
+ return false; |
+ } |
+ // has type arguments |
+ if (literal.typeArguments != null) { |
+ return false; |
+ } |
+ // prepare statement |
+ Statement statement = |
+ literal.getAncestor((node) => node is ExpressionStatement); |
+ if (statement == null) { |
+ return false; |
+ } |
+ // OK, statement does not start with map |
+ if (!identical(statement.beginToken, literal.beginToken)) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, literal); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given method [declaration] of operator `[]=`, has `void` |
+ * return type. |
+ * |
+ * See [StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR]. |
+ */ |
+ bool _checkForNonVoidReturnTypeForOperator(MethodDeclaration declaration) { |
+ // check that []= operator |
+ SimpleIdentifier name = declaration.name; |
+ if (name.name != "[]=") { |
+ return false; |
+ } |
+ // check return type |
+ TypeName typeName = declaration.returnType; |
+ if (typeName != null) { |
+ DartType type = typeName.type; |
+ if (type != null && !type.isVoid) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR, typeName); |
+ } |
+ } |
+ // no warning |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify the [typeName], used as the return type of a setter, is valid |
+ * (either `null` or the type 'void'). |
+ * |
+ * See [StaticWarningCode.NON_VOID_RETURN_FOR_SETTER]. |
+ */ |
+ bool _checkForNonVoidReturnTypeForSetter(TypeName typeName) { |
+ if (typeName != null) { |
+ DartType type = typeName.type; |
+ if (type != null && !type.isVoid) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.NON_VOID_RETURN_FOR_SETTER, typeName); |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify the given operator-method [declaration], does not have an optional |
+ * parameter. This method assumes that the method declaration was tested to be |
+ * an operator declaration before being called. |
+ * |
+ * See [CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR]. |
+ */ |
+ bool _checkForOptionalParameterInOperator(MethodDeclaration declaration) { |
+ FormalParameterList parameterList = declaration.parameters; |
+ if (parameterList == null) { |
+ return false; |
+ } |
+ bool foundError = false; |
+ NodeList<FormalParameter> formalParameters = parameterList.parameters; |
+ for (FormalParameter formalParameter in formalParameters) { |
+ if (formalParameter.kind.isOptional) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR, |
+ formalParameter); |
+ foundError = true; |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Check that the given named optional [parameter] does not begin with '_'. |
+ * |
+ * See [CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER]. |
+ */ |
+ bool _checkForPrivateOptionalParameter(FormalParameter parameter) { |
+ // should be named parameter |
+ if (parameter.kind != ParameterKind.NAMED) { |
+ return false; |
+ } |
+ // name should start with '_' |
+ SimpleIdentifier name = parameter.identifier; |
+ if (name.isSynthetic || !StringUtilities.startsWithChar(name.name, 0x5F)) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, parameter); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check whether the given constructor [declaration] is the redirecting |
+ * generative constructor and references itself directly or indirectly. The |
+ * [constructorElement] is the constructor element. |
+ * |
+ * See [CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT]. |
+ */ |
+ bool _checkForRecursiveConstructorRedirect(ConstructorDeclaration declaration, |
+ ConstructorElement constructorElement) { |
+ // we check generative constructor here |
+ if (declaration.factoryKeyword != null) { |
+ return false; |
+ } |
+ // try to find redirecting constructor invocation and analyzer it for |
+ // recursion |
+ for (ConstructorInitializer initializer in declaration.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ // OK if no cycle |
+ if (!_hasRedirectingFactoryConstructorCycle(constructorElement)) { |
+ return false; |
+ } |
+ // report error |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT, initializer); |
+ return true; |
+ } |
+ } |
+ // OK, no redirecting constructor invocation |
+ return false; |
+ } |
+ |
+ /** |
+ * Check whether the given constructor [declaration] has redirected |
+ * constructor and references itself directly or indirectly. The |
+ * constructor [element] is the element introduced by the declaration. |
+ * |
+ * See [CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT]. |
+ */ |
+ bool _checkForRecursiveFactoryRedirect( |
+ ConstructorDeclaration declaration, ConstructorElement element) { |
+ // prepare redirected constructor |
+ ConstructorName redirectedConstructorNode = |
+ declaration.redirectedConstructor; |
+ if (redirectedConstructorNode == null) { |
+ return false; |
+ } |
+ // OK if no cycle |
+ if (!_hasRedirectingFactoryConstructorCycle(element)) { |
+ return false; |
+ } |
+ // report error |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT, |
+ redirectedConstructorNode); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that the class [element] is not a superinterface to itself. |
+ * |
+ * See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE], |
+ * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS], and |
+ * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS]. |
+ */ |
+ bool _checkForRecursiveInterfaceInheritance(ClassElement element) { |
+ if (element == null) { |
+ return false; |
+ } |
+ return _safeCheckForRecursiveInterfaceInheritance( |
+ element, new List<ClassElement>()); |
+ } |
+ |
+ /** |
+ * Check that the given constructor [declaration] has a valid combination of |
+ * redirected constructor invocation(s), super constructor invocations and |
+ * field initializers. |
+ * |
+ * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR], |
+ * [CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR], |
+ * [CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS], |
+ * [CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR], and |
+ * [CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR]. |
+ */ |
+ bool _checkForRedirectingConstructorErrorCodes( |
+ ConstructorDeclaration declaration) { |
+ bool errorReported = false; |
+ // |
+ // Check for default values in the parameters |
+ // |
+ ConstructorName redirectedConstructor = declaration.redirectedConstructor; |
+ if (redirectedConstructor != null) { |
+ for (FormalParameter parameter in declaration.parameters.parameters) { |
+ if (parameter is DefaultFormalParameter && |
+ parameter.defaultValue != null) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR, |
+ parameter.identifier); |
+ errorReported = true; |
+ } |
+ } |
+ } |
+ // check if there are redirected invocations |
+ int numRedirections = 0; |
+ for (ConstructorInitializer initializer in declaration.initializers) { |
+ if (initializer is RedirectingConstructorInvocation) { |
+ if (numRedirections > 0) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS, |
+ initializer); |
+ errorReported = true; |
+ } |
+ if (declaration.factoryKeyword == null) { |
+ RedirectingConstructorInvocation invocation = initializer; |
+ ConstructorElement redirectingElement = invocation.staticElement; |
+ if (redirectingElement == null) { |
+ String enclosingTypeName = _enclosingClass.displayName; |
+ String constructorStrName = enclosingTypeName; |
+ if (invocation.constructorName != null) { |
+ constructorStrName += ".${invocation.constructorName.name}"; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR, |
+ invocation, [constructorStrName, enclosingTypeName]); |
+ } else { |
+ if (redirectingElement.isFactory) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR, |
+ initializer); |
+ } |
+ } |
+ } |
+ numRedirections++; |
+ } |
+ } |
+ // check for other initializers |
+ if (numRedirections > 0) { |
+ for (ConstructorInitializer initializer in declaration.initializers) { |
+ if (initializer is SuperConstructorInvocation) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR, |
+ initializer); |
+ errorReported = true; |
+ } |
+ if (initializer is ConstructorFieldInitializer) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, |
+ initializer); |
+ errorReported = true; |
+ } |
+ } |
+ } |
+ // done |
+ return errorReported; |
+ } |
+ |
+ /** |
+ * Check whether the given constructor [declaration] has redirected |
+ * constructor and references itself directly or indirectly. The |
+ * constructor [element] is the element introduced by the declaration. |
+ * |
+ * See [CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR]. |
+ */ |
+ bool _checkForRedirectToNonConstConstructor( |
+ ConstructorDeclaration declaration, ConstructorElement element) { |
+ // prepare redirected constructor |
+ ConstructorName redirectedConstructorNode = |
+ declaration.redirectedConstructor; |
+ if (redirectedConstructorNode == null) { |
+ return false; |
+ } |
+ // prepare element |
+ if (element == null) { |
+ return false; |
+ } |
+ // OK, it is not 'const' |
+ if (!element.isConst) { |
+ return false; |
+ } |
+ // prepare redirected constructor |
+ ConstructorElement redirectedConstructor = element.redirectedConstructor; |
+ if (redirectedConstructor == null) { |
+ return false; |
+ } |
+ // OK, it is also 'const' |
+ if (redirectedConstructor.isConst) { |
+ return false; |
+ } |
+ // report error |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR, |
+ redirectedConstructorNode); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that the given rethrow [expression] is inside of a catch clause. |
+ * |
+ * See [CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH]. |
+ */ |
+ bool _checkForRethrowOutsideCatch(RethrowExpression expression) { |
+ if (!_isInCatchClause) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH, expression); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check that if the the given constructor [declaration] is generative, then |
+ * it does not have an expression function body. |
+ * |
+ * See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR]. |
+ */ |
+ bool _checkForReturnInGenerativeConstructor( |
+ ConstructorDeclaration declaration) { |
+ // ignore factory |
+ if (declaration.factoryKeyword != null) { |
+ return false; |
+ } |
+ // block body (with possible return statement) is checked elsewhere |
+ FunctionBody body = declaration.body; |
+ if (body is! ExpressionFunctionBody) { |
+ return false; |
+ } |
+ // report error |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, body); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that a type mis-match between the type of the [returnExpression] and |
+ * the [expectedReturnType] by the enclosing method or function. |
+ * |
+ * This method is called both by [_checkForAllReturnStatementErrorCodes] |
+ * and [visitExpressionFunctionBody]. |
+ * |
+ * See [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE]. |
+ */ |
+ bool _checkForReturnOfInvalidType( |
+ Expression returnExpression, DartType expectedReturnType) { |
+ if (_enclosingFunction == null) { |
+ return false; |
+ } |
+ if (_inGenerator) { |
+ // "return expression;" is disallowed in generators, but this is checked |
+ // elsewhere. Bare "return" is always allowed in generators regardless |
+ // of the return type. So no need to do any further checking. |
+ return false; |
+ } |
+ DartType staticReturnType = _computeReturnTypeForMethod(returnExpression); |
+ if (expectedReturnType.isVoid) { |
+ if (staticReturnType.isVoid || |
+ staticReturnType.isDynamic || |
+ staticReturnType.isBottom) { |
+ return false; |
+ } |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [ |
+ staticReturnType, |
+ expectedReturnType, |
+ _enclosingFunction.displayName |
+ ]); |
+ return true; |
+ } |
+ if (staticReturnType.isAssignableTo(expectedReturnType)) { |
+ return false; |
+ } |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [ |
+ staticReturnType, |
+ expectedReturnType, |
+ _enclosingFunction.displayName |
+ ]); |
+ return true; |
+ // TODO(brianwilkerson) Define a hint corresponding to the warning and |
+ // report it if appropriate. |
+// Type propagatedReturnType = returnExpression.getPropagatedType(); |
+// boolean isPropagatedAssignable = propagatedReturnType.isAssignableTo(expectedReturnType); |
+// if (isStaticAssignable || isPropagatedAssignable) { |
+// return false; |
+// } |
+// errorReporter.reportTypeErrorForNode( |
+// StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, |
+// returnExpression, |
+// staticReturnType, |
+// expectedReturnType, |
+// enclosingFunction.getDisplayName()); |
+// return true; |
+ } |
+ |
+ /** |
+ * Check the given [typeReference] and that the [name] is not the reference to |
+ * an instance member. |
+ * |
+ * See [StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER]. |
+ */ |
+ bool _checkForStaticAccessToInstanceMember( |
+ ClassElement typeReference, SimpleIdentifier name) { |
+ // OK, target is not a type |
+ if (typeReference == null) { |
+ return false; |
+ } |
+ // prepare member Element |
+ Element element = name.staticElement; |
+ if (element is! ExecutableElement) { |
+ return false; |
+ } |
+ ExecutableElement memberElement = element as ExecutableElement; |
+ // OK, static |
+ if (memberElement.isStatic) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, name, [name.name]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that the type of the expression in the given 'switch' [statement] is |
+ * assignable to the type of the 'case' members. |
+ * |
+ * See [StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE]. |
+ */ |
+ bool _checkForSwitchExpressionNotAssignable(SwitchStatement statement) { |
+ // prepare 'switch' expression type |
+ Expression expression = statement.expression; |
+ DartType expressionType = getStaticType(expression); |
+ if (expressionType == null) { |
+ return false; |
+ } |
+ // compare with type of the first 'case' |
+ NodeList<SwitchMember> members = statement.members; |
+ for (SwitchMember switchMember in members) { |
+ if (switchMember is! SwitchCase) { |
+ continue; |
+ } |
+ SwitchCase switchCase = switchMember as SwitchCase; |
+ // prepare 'case' type |
+ Expression caseExpression = switchCase.expression; |
+ DartType caseType = getStaticType(caseExpression); |
+ // check types |
+ if (expressionType.isAssignableTo(caseType)) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE, expression, [ |
+ expressionType, |
+ caseType |
+ ]); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given function type [alias] does not reference itself |
+ * directly. |
+ * |
+ * See [CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF]. |
+ */ |
+ bool _checkForTypeAliasCannotReferenceItself_function( |
+ FunctionTypeAlias alias) { |
+ FunctionTypeAliasElement element = alias.element; |
+ if (!_hasTypedefSelfReference(element)) { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, alias); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify that the given type [name] is not a deferred type. |
+ * |
+ * See [StaticWarningCode.TYPE_ANNOTATION_DEFERRED_CLASS]. |
+ */ |
+ bool _checkForTypeAnnotationDeferredClass(TypeName name) { |
+ if (name != null && name.isDeferred) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.TYPE_ANNOTATION_DEFERRED_CLASS, name, [name.name]); |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the type arguments in the given [typeName] are all within |
+ * their bounds. |
+ * |
+ * See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS]. |
+ */ |
+ bool _checkForTypeArgumentNotMatchingBounds(TypeName typeName) { |
+ if (typeName.typeArguments == null) { |
+ return false; |
+ } |
+ // prepare Type |
+ DartType type = typeName.type; |
+ if (type == null) { |
+ return false; |
+ } |
+ // prepare ClassElement |
+ Element element = type.element; |
+ if (element is! ClassElement) { |
+ return false; |
+ } |
+ ClassElement classElement = element as ClassElement; |
+ // prepare type parameters |
+ List<DartType> typeParameters = classElement.type.typeArguments; |
+ List<TypeParameterElement> boundingElts = classElement.typeParameters; |
+ // iterate over each bounded type parameter and corresponding argument |
+ NodeList<TypeName> typeNameArgList = typeName.typeArguments.arguments; |
+ List<DartType> typeArguments = (type as InterfaceType).typeArguments; |
+ int loopThroughIndex = |
+ math.min(typeNameArgList.length, boundingElts.length); |
+ bool foundError = false; |
+ for (int i = 0; i < loopThroughIndex; i++) { |
+ TypeName argTypeName = typeNameArgList[i]; |
+ DartType argType = argTypeName.type; |
+ DartType boundType = boundingElts[i].bound; |
+ if (argType != null && boundType != null) { |
+ if (typeArguments.length != 0 && |
+ typeArguments.length == typeParameters.length) { |
+ boundType = boundType.substitute2(typeArguments, typeParameters); |
+ } |
+ if (!argType.isSubtypeOf(boundType)) { |
+ ErrorCode errorCode; |
+ if (_isInConstInstanceCreation) { |
+ errorCode = CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS; |
+ } else { |
+ errorCode = StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS; |
+ } |
+ _errorReporter.reportTypeErrorForNode( |
+ errorCode, argTypeName, [argType, boundType]); |
+ foundError = true; |
+ } |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Check whether the given type [name] is a type parameter being used to |
+ * define a static member. |
+ * |
+ * See [StaticWarningCode.TYPE_PARAMETER_REFERENCED_BY_STATIC]. |
+ */ |
+ bool _checkForTypeParameterReferencedByStatic(TypeName name) { |
+ if (_isInStaticMethod || _isInStaticVariableDeclaration) { |
+ DartType type = name.type; |
+ if (type is TypeParameterType) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.TYPE_PARAMETER_REFERENCED_BY_STATIC, name); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check whether the given type [parameter] is a supertype of its bound. |
+ * |
+ * See [StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND]. |
+ */ |
+ bool _checkForTypeParameterSupertypeOfItsBound(TypeParameter parameter) { |
+ TypeParameterElement element = parameter.element; |
+ // prepare bound |
+ DartType bound = element.bound; |
+ if (bound == null) { |
+ return false; |
+ } |
+ // OK, type parameter is not supertype of its bound |
+ if (!bound.isMoreSpecificThan(element.type)) { |
+ return false; |
+ } |
+ // report problem |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND, parameter, |
+ [element.displayName]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that if the given generative [constructor] has neither an explicit |
+ * super constructor invocation nor a redirecting constructor invocation, that |
+ * the superclass has a default generative constructor. |
+ * |
+ * See [CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT], |
+ * [CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR], and |
+ * [StaticWarningCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT]. |
+ */ |
+ bool _checkForUndefinedConstructorInInitializerImplicit( |
+ ConstructorDeclaration constructor) { |
+ if (_enclosingClass == null) { |
+ return false; |
+ } |
+ // do nothing if mixin errors have already been reported for this class. |
+ ClassElementImpl enclosingClass = _enclosingClass; |
+ if (enclosingClass.doesMixinLackConstructors) { |
+ return false; |
+ } |
+ // |
+ // Ignore if the constructor is not generative. |
+ // |
+ if (constructor.factoryKeyword != null) { |
+ return false; |
+ } |
+ // |
+ // Ignore if the constructor has either an implicit super constructor |
+ // invocation or a redirecting constructor invocation. |
+ // |
+ for (ConstructorInitializer constructorInitializer |
+ in constructor.initializers) { |
+ if (constructorInitializer is SuperConstructorInvocation || |
+ constructorInitializer is RedirectingConstructorInvocation) { |
+ return false; |
+ } |
+ } |
+ // |
+ // Check to see whether the superclass has a non-factory unnamed |
+ // constructor. |
+ // |
+ InterfaceType superType = _enclosingClass.supertype; |
+ if (superType == null) { |
+ return false; |
+ } |
+ ClassElement superElement = superType.element; |
+ ConstructorElement superUnnamedConstructor = |
+ superElement.unnamedConstructor; |
+ if (superUnnamedConstructor != null) { |
+ if (superUnnamedConstructor.isFactory) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, |
+ constructor.returnType, [superUnnamedConstructor]); |
+ return true; |
+ } |
+ if (!superUnnamedConstructor.isDefaultConstructor || |
+ !_enclosingClass |
+ .isSuperConstructorAccessible(superUnnamedConstructor)) { |
+ int offset; |
+ int length; |
+ { |
+ Identifier returnType = constructor.returnType; |
+ SimpleIdentifier name = constructor.name; |
+ offset = returnType.offset; |
+ length = (name != null ? name.end : returnType.end) - offset; |
+ } |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT, offset, |
+ length, [superType.displayName]); |
+ } |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, |
+ constructor.returnType, [superElement.name]); |
+ return true; |
+ } |
+ |
+ /** |
+ * Check that if the given [name] is a reference to a static member it is |
+ * defined in the enclosing class rather than in a superclass. |
+ * |
+ * See [StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER]. |
+ */ |
+ bool _checkForUnqualifiedReferenceToNonLocalStaticMember( |
+ SimpleIdentifier name) { |
+ Element element = name.staticElement; |
+ if (element == null || element is TypeParameterElement) { |
+ return false; |
+ } |
+ Element enclosingElement = element.enclosingElement; |
+ if (enclosingElement is! ClassElement) { |
+ return false; |
+ } |
+ if ((element is MethodElement && !element.isStatic) || |
+ (element is PropertyAccessorElement && !element.isStatic)) { |
+ return false; |
+ } |
+ if (identical(enclosingElement, _enclosingClass)) { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER, |
+ name, [name.name]); |
+ return true; |
+ } |
+ |
+ void _checkForValidField(FieldFormalParameter parameter) { |
+ ParameterElement element = parameter.element; |
+ if (element is FieldFormalParameterElement) { |
+ FieldElement fieldElement = element.field; |
+ if (fieldElement == null || fieldElement.isSynthetic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
+ parameter, [parameter.identifier.name]); |
+ } else { |
+ ParameterElement parameterElement = parameter.element; |
+ if (parameterElement is FieldFormalParameterElementImpl) { |
+ FieldFormalParameterElementImpl fieldFormal = parameterElement; |
+ DartType declaredType = fieldFormal.type; |
+ DartType fieldType = fieldElement.type; |
+ if (fieldElement.isSynthetic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
+ parameter, [parameter.identifier.name]); |
+ } else if (fieldElement.isStatic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, |
+ parameter, [parameter.identifier.name]); |
+ } else if (declaredType != null && |
+ fieldType != null && |
+ !declaredType.isAssignableTo(fieldType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, |
+ parameter, [declaredType, fieldType]); |
+ } |
+ } else { |
+ if (fieldElement.isSynthetic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
+ parameter, [parameter.identifier.name]); |
+ } else if (fieldElement.isStatic) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD, |
+ parameter, [parameter.identifier.name]); |
+ } |
+ } |
+ } |
+ } |
+// else { |
+// // TODO(jwren) Report error, constructor initializer variable is a top level element |
+// // (Either here or in ErrorVerifier.checkForAllFinalInitializedErrorCodes) |
+// } |
+ } |
+ |
+ /** |
+ * Verify that the given [getter] does not have a return type of 'void'. |
+ * |
+ * See [StaticWarningCode.VOID_RETURN_FOR_GETTER]. |
+ */ |
+ bool _checkForVoidReturnType(MethodDeclaration getter) { |
+ TypeName returnType = getter.returnType; |
+ if (returnType == null || returnType.name.name != "void") { |
+ return false; |
+ } |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.VOID_RETURN_FOR_GETTER, returnType); |
+ return true; |
+ } |
+ |
+ /** |
+ * Verify the given operator-method [declaration], has correct number of |
+ * parameters. |
+ * |
+ * This method assumes that the method declaration was tested to be an |
+ * operator declaration before being called. |
+ * |
+ * See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR]. |
+ */ |
+ bool _checkForWrongNumberOfParametersForOperator( |
+ MethodDeclaration declaration) { |
+ // prepare number of parameters |
+ FormalParameterList parameterList = declaration.parameters; |
+ if (parameterList == null) { |
+ return false; |
+ } |
+ int numParameters = parameterList.parameters.length; |
+ // prepare operator name |
+ SimpleIdentifier nameNode = declaration.name; |
+ if (nameNode == null) { |
+ return false; |
+ } |
+ String name = nameNode.name; |
+ // check for exact number of parameters |
+ int expected = -1; |
+ if ("[]=" == name) { |
+ expected = 2; |
+ } else if ("<" == name || |
+ ">" == name || |
+ "<=" == name || |
+ ">=" == name || |
+ "==" == name || |
+ "+" == name || |
+ "/" == name || |
+ "~/" == name || |
+ "*" == name || |
+ "%" == name || |
+ "|" == name || |
+ "^" == name || |
+ "&" == name || |
+ "<<" == name || |
+ ">>" == name || |
+ "[]" == name) { |
+ expected = 1; |
+ } else if ("~" == name) { |
+ expected = 0; |
+ } |
+ if (expected != -1 && numParameters != expected) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR, |
+ nameNode, [name, expected, numParameters]); |
+ return true; |
+ } |
+ // check for operator "-" |
+ if ("-" == name && numParameters > 1) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS, |
+ nameNode, [numParameters]); |
+ return true; |
+ } |
+ // OK |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given setter [parameterList] has only one required |
+ * parameter. The [setterName] is the name of the setter to report problems |
+ * on. |
+ * |
+ * This method assumes that the method declaration was tested to be a setter |
+ * before being called. |
+ * |
+ * See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER]. |
+ */ |
+ bool _checkForWrongNumberOfParametersForSetter( |
+ SimpleIdentifier setterName, FormalParameterList parameterList) { |
+ if (setterName == null) { |
+ return false; |
+ } |
+ if (parameterList == null) { |
+ return false; |
+ } |
+ NodeList<FormalParameter> parameters = parameterList.parameters; |
+ if (parameters.length != 1 || |
+ parameters[0].kind != ParameterKind.REQUIRED) { |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER, |
+ setterName); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Check for a type mis-match between the yielded type and the declared |
+ * return type of a generator function. |
+ * |
+ * This method should only be called in generator functions. |
+ */ |
+ bool _checkForYieldOfInvalidType( |
+ Expression yieldExpression, bool isYieldEach) { |
+ assert(_inGenerator); |
+ if (_enclosingFunction == null) { |
+ return false; |
+ } |
+ DartType declaredReturnType = _enclosingFunction.returnType; |
+ DartType staticYieldedType = getStaticType(yieldExpression); |
+ DartType impliedReturnType; |
+ if (isYieldEach) { |
+ impliedReturnType = staticYieldedType; |
+ } else if (_enclosingFunction.isAsynchronous) { |
+ impliedReturnType = |
+ _typeProvider.streamType.substitute4(<DartType>[staticYieldedType]); |
+ } else { |
+ impliedReturnType = |
+ _typeProvider.iterableType.substitute4(<DartType>[staticYieldedType]); |
+ } |
+ if (!impliedReturnType.isAssignableTo(declaredReturnType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.YIELD_OF_INVALID_TYPE, yieldExpression, [ |
+ impliedReturnType, |
+ declaredReturnType |
+ ]); |
+ return true; |
+ } |
+ if (isYieldEach) { |
+ // Since the declared return type might have been "dynamic", we need to |
+ // also check that the implied return type is assignable to generic |
+ // Stream/Iterable. |
+ DartType requiredReturnType; |
+ if (_enclosingFunction.isAsynchronous) { |
+ requiredReturnType = _typeProvider.streamDynamicType; |
+ } else { |
+ requiredReturnType = _typeProvider.iterableDynamicType; |
+ } |
+ if (!impliedReturnType.isAssignableTo(requiredReturnType)) { |
+ _errorReporter.reportTypeErrorForNode( |
+ StaticTypeWarningCode.YIELD_OF_INVALID_TYPE, yieldExpression, [ |
+ impliedReturnType, |
+ requiredReturnType |
+ ]); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that if the given class [declaration] implements the class Function |
+ * that it has a concrete implementation of the call method. |
+ * |
+ * See [StaticWarningCode.FUNCTION_WITHOUT_CALL]. |
+ */ |
+ bool _checkImplementsFunctionWithoutCall(ClassDeclaration declaration) { |
+ if (declaration.isAbstract) { |
+ return false; |
+ } |
+ ClassElement classElement = declaration.element; |
+ if (classElement == null) { |
+ return false; |
+ } |
+ if (!classElement.type.isSubtypeOf(_typeProvider.functionType)) { |
+ return false; |
+ } |
+ // If there is a noSuchMethod method, then don't report the warning, |
+ // see dartbug.com/16078 |
+ if (classElement.getMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME) != |
+ null) { |
+ return false; |
+ } |
+ ExecutableElement callMethod = _inheritanceManager.lookupMember( |
+ classElement, FunctionElement.CALL_METHOD_NAME); |
+ if (callMethod == null || |
+ callMethod is! MethodElement || |
+ (callMethod as MethodElement).isAbstract) { |
+ _errorReporter.reportErrorForNode( |
+ StaticWarningCode.FUNCTION_WITHOUT_CALL, declaration.name); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Verify that the given class [declaration] does not have the same class in |
+ * the 'extends' and 'implements' clauses. |
+ * |
+ * See [CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS]. |
+ */ |
+ bool _checkImplementsSuperClass(ClassDeclaration declaration) { |
+ // prepare super type |
+ InterfaceType superType = _enclosingClass.supertype; |
+ if (superType == null) { |
+ return false; |
+ } |
+ // prepare interfaces |
+ ImplementsClause implementsClause = declaration.implementsClause; |
+ if (implementsClause == null) { |
+ return false; |
+ } |
+ // check interfaces |
+ bool hasProblem = false; |
+ for (TypeName interfaceNode in implementsClause.interfaces) { |
+ if (interfaceNode.type == superType) { |
+ hasProblem = true; |
+ _errorReporter.reportErrorForNode( |
+ CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS, interfaceNode, |
+ [superType.displayName]); |
+ } |
+ } |
+ // done |
+ return hasProblem; |
+ } |
+ |
+ DartType _computeReturnTypeForMethod(Expression returnExpression) { |
+ // This method should never be called for generators, since generators are |
+ // never allowed to contain return statements with expressions. |
+ assert(!_inGenerator); |
+ if (returnExpression == null) { |
+ if (_enclosingFunction.isAsynchronous) { |
+ return _typeProvider.futureNullType; |
+ } else { |
+ return VoidTypeImpl.instance; |
+ } |
+ } |
+ DartType staticReturnType = getStaticType(returnExpression); |
+ if (staticReturnType != null && _enclosingFunction.isAsynchronous) { |
+ return _typeProvider.futureType.substitute4(<DartType>[ |
+ StaticTypeAnalyzer.flattenFutures(_typeProvider, staticReturnType) |
+ ]); |
+ } |
+ return staticReturnType; |
+ } |
+ |
+ /** |
+ * Return the error code that should be used when the given class [element] |
+ * references itself directly. |
+ */ |
+ ErrorCode _getBaseCaseErrorCode(ClassElement element) { |
+ InterfaceType supertype = element.supertype; |
+ if (supertype != null && _enclosingClass == supertype.element) { |
+ return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS; |
+ } |
+ List<InterfaceType> mixins = element.mixins; |
+ for (int i = 0; i < mixins.length; i++) { |
+ if (_enclosingClass == mixins[i].element) { |
+ return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH; |
+ } |
+ } |
+ return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS; |
+ } |
+ |
+ /** |
+ * Given an [expression] in a switch case whose value is expected to be an |
+ * enum constant, return the name of the constant. |
+ */ |
+ String _getConstantName(Expression expression) { |
+ // TODO(brianwilkerson) Convert this to return the element representing the |
+ // constant. |
+ if (expression is SimpleIdentifier) { |
+ return expression.name; |
+ } else if (expression is PrefixedIdentifier) { |
+ return expression.identifier.name; |
+ } else if (expression is PropertyAccess) { |
+ return expression.propertyName.name; |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Return the return type of the given [getter]. |
+ */ |
+ DartType _getGetterType(PropertyAccessorElement getter) { |
+ FunctionType functionType = getter.type; |
+ if (functionType != null) { |
+ return functionType.returnType; |
+ } else { |
+ return null; |
+ } |
+ } |
+ |
+ /** |
+ * Return the type of the first and only parameter of the given [setter]. |
+ */ |
+ DartType _getSetterType(PropertyAccessorElement setter) { |
+ // Get the parameters for MethodDeclaration or FunctionDeclaration |
+ List<ParameterElement> setterParameters = setter.parameters; |
+ // If there are no setter parameters, return no type. |
+ if (setterParameters.length == 0) { |
+ return null; |
+ } |
+ return setterParameters[0].type; |
+ } |
+ |
+ /** |
+ * 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]. |
+ */ |
+ bool _hasDeferredPrefixCollision(List<ImportDirective> directives) { |
+ bool foundError = false; |
+ int count = directives.length; |
+ if (count > 1) { |
+ for (int i = 0; i < count; i++) { |
+ sc.Token deferredToken = directives[i].deferredKeyword; |
+ if (deferredToken != null) { |
+ _errorReporter.reportErrorForToken( |
+ CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken); |
+ foundError = true; |
+ } |
+ } |
+ } |
+ return foundError; |
+ } |
+ |
+ /** |
+ * Return `true` if the given [constructor] redirects to itself, directly or |
+ * indirectly. |
+ */ |
+ bool _hasRedirectingFactoryConstructorCycle(ConstructorElement constructor) { |
+ Set<ConstructorElement> constructors = new HashSet<ConstructorElement>(); |
+ ConstructorElement current = constructor; |
+ while (current != null) { |
+ if (constructors.contains(current)) { |
+ return identical(current, constructor); |
+ } |
+ constructors.add(current); |
+ current = current.redirectedConstructor; |
+ if (current is ConstructorMember) { |
+ current = (current as ConstructorMember).baseElement; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Return `true` if the given [element] has direct or indirect reference to |
+ * itself from anywhere except a class element or type parameter bounds. |
+ */ |
+ bool _hasTypedefSelfReference(Element element) { |
+ Set<Element> checked = new HashSet<Element>(); |
+ List<Element> toCheck = new List<Element>(); |
+ GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference elementVisitor = |
+ new GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference( |
+ toCheck); |
+ toCheck.add(element); |
+ bool firstIteration = true; |
+ while (true) { |
+ Element current; |
+ // get next element |
+ while (true) { |
+ // may be no more elements to check |
+ if (toCheck.isEmpty) { |
+ return false; |
+ } |
+ // try to get next element |
+ current = toCheck.removeAt(toCheck.length - 1); |
+ if (element == current) { |
+ if (firstIteration) { |
+ firstIteration = false; |
+ break; |
+ } else { |
+ return true; |
+ } |
+ } |
+ if (current != null && !checked.contains(current)) { |
+ break; |
+ } |
+ } |
+ // check current element |
+ current.accept(elementVisitor); |
+ checked.add(current); |
+ } |
+ } |
+ |
+ bool _isFunctionType(DartType type) { |
+ if (type.isDynamic || type.isBottom) { |
+ return true; |
+ } else if (type is FunctionType || type.isDartCoreFunction) { |
+ return true; |
+ } else if (type is InterfaceType) { |
+ MethodElement callMethod = |
+ type.lookUpMethod(FunctionElement.CALL_METHOD_NAME, _currentLibrary); |
+ return callMethod != null; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Return `true` iff the given [classElement] has a concrete method, getter or |
+ * setter that matches the name of the given [executableElement] in either the |
+ * class itself, or one of its' mixins. |
+ * |
+ * By "match", only the name of the member is tested to match, it does not |
+ * have to equal or be a subtype of the given executable element, this is due |
+ * to the specific use where this method is used in |
+ * [_checkForNonAbstractClassInheritsAbstractMember]. |
+ */ |
+ bool _isMemberInClassOrMixin( |
+ ExecutableElement executableElement, ClassElement classElement) { |
+ ExecutableElement foundElt = null; |
+ String executableName = executableElement.name; |
+ if (executableElement is MethodElement) { |
+ foundElt = classElement.getMethod(executableName); |
+ if (foundElt != null && !(foundElt as MethodElement).isAbstract) { |
+ return true; |
+ } |
+ List<InterfaceType> mixins = classElement.mixins; |
+ for (int i = 0; i < mixins.length && foundElt == null; i++) { |
+ foundElt = mixins[i].getMethod(executableName); |
+ } |
+ if (foundElt != null && !(foundElt as MethodElement).isAbstract) { |
+ return true; |
+ } |
+ } else if (executableElement is PropertyAccessorElement) { |
+ PropertyAccessorElement propertyAccessorElement = executableElement; |
+ if (propertyAccessorElement.isGetter) { |
+ foundElt = classElement.getGetter(executableName); |
+ } |
+ if (foundElt == null && propertyAccessorElement.isSetter) { |
+ foundElt = classElement.getSetter(executableName); |
+ } |
+ if (foundElt != null && |
+ !(foundElt as PropertyAccessorElement).isAbstract) { |
+ return true; |
+ } |
+ List<InterfaceType> mixins = classElement.mixins; |
+ for (int i = 0; i < mixins.length && foundElt == null; i++) { |
+ foundElt = mixins[i].getGetter(executableName); |
+ if (foundElt == null) { |
+ foundElt = mixins[i].getSetter(executableName); |
+ } |
+ } |
+ if (foundElt != null && |
+ !(foundElt as PropertyAccessorElement).isAbstract) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Return `true` if the given 'this' [expression] is in a valid context. |
+ */ |
+ bool _isThisInValidContext(ThisExpression expression) { |
+ for (AstNode node = expression.parent; node != null; node = node.parent) { |
+ if (node is CompilationUnit) { |
+ return false; |
+ } |
+ if (node is ConstructorDeclaration) { |
+ return node.factoryKeyword == null; |
+ } |
+ if (node is ConstructorInitializer) { |
+ return false; |
+ } |
+ if (node is MethodDeclaration) { |
+ return !node.isStatic; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Return `true` if the given [identifier] is in a location where it is |
+ * allowed to resolve to a static member of a supertype. |
+ */ |
+ bool _isUnqualifiedReferenceToNonLocalStaticMemberAllowed( |
+ SimpleIdentifier identifier) { |
+ if (identifier.inDeclarationContext()) { |
+ return true; |
+ } |
+ AstNode parent = identifier.parent; |
+ if (parent is ConstructorName || |
+ parent is MethodInvocation || |
+ parent is PropertyAccess || |
+ parent is SuperConstructorInvocation) { |
+ return true; |
+ } |
+ if (parent is PrefixedIdentifier && |
+ identical(parent.identifier, identifier)) { |
+ return true; |
+ } |
+ if (parent is Annotation && identical(parent.constructorName, identifier)) { |
+ return true; |
+ } |
+ if (parent is CommentReference) { |
+ CommentReference commentReference = parent; |
+ if (commentReference.newKeyword != null) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ bool _isUserDefinedObject(EvaluationResultImpl result) => result == null || |
+ (result.value != null && result.value.isUserDefinedObject); |
+ |
+ /** |
+ * Check that the given class [element] is not a superinterface to itself. The |
+ * [path] is a list containing the potentially cyclic implements path. |
+ * |
+ * See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE], |
+ * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS], |
+ * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS], |
+ * and [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH]. |
+ */ |
+ bool _safeCheckForRecursiveInterfaceInheritance( |
+ ClassElement element, List<ClassElement> path) { |
+ // Detect error condition. |
+ int size = path.length; |
+ // If this is not the base case (size > 0), and the enclosing class is the |
+ // given class element then an error an error. |
+ if (size > 0 && _enclosingClass == element) { |
+ String enclosingClassName = _enclosingClass.displayName; |
+ if (size > 1) { |
+ // Construct a string showing the cyclic implements path: |
+ // "A, B, C, D, A" |
+ String separator = ", "; |
+ StringBuffer buffer = new StringBuffer(); |
+ for (int i = 0; i < size; i++) { |
+ buffer.write(path[i].displayName); |
+ buffer.write(separator); |
+ } |
+ buffer.write(element.displayName); |
+ _errorReporter.reportErrorForOffset( |
+ CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE, |
+ _enclosingClass.nameOffset, enclosingClassName.length, [ |
+ enclosingClassName, |
+ buffer.toString() |
+ ]); |
+ return true; |
+ } else { |
+ // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS or |
+ // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or |
+ // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH |
+ _errorReporter.reportErrorForOffset(_getBaseCaseErrorCode(element), |
+ _enclosingClass.nameOffset, enclosingClassName.length, |
+ [enclosingClassName]); |
+ return true; |
+ } |
+ } |
+ if (path.indexOf(element) > 0) { |
+ return false; |
+ } |
+ path.add(element); |
+ // n-case |
+ InterfaceType supertype = element.supertype; |
+ if (supertype != null && |
+ _safeCheckForRecursiveInterfaceInheritance(supertype.element, path)) { |
+ return true; |
+ } |
+ List<InterfaceType> interfaceTypes = element.interfaces; |
+ for (InterfaceType interfaceType in interfaceTypes) { |
+ if (_safeCheckForRecursiveInterfaceInheritance( |
+ interfaceType.element, path)) { |
+ return true; |
+ } |
+ } |
+ List<InterfaceType> mixinTypes = element.mixins; |
+ for (InterfaceType mixinType in mixinTypes) { |
+ if (_safeCheckForRecursiveInterfaceInheritance(mixinType.element, path)) { |
+ return true; |
+ } |
+ } |
+ path.removeAt(path.length - 1); |
+ return false; |
+ } |
+ |
+ /** |
+ * Return the static type of the given [expression] that is to be used for |
+ * type analysis. |
+ */ |
+ static DartType getStaticType(Expression expression) { |
+ DartType type = expression.staticType; |
+ if (type == null) { |
+ // TODO(brianwilkerson) This should never happen. |
+ return DynamicTypeImpl.instance; |
+ } |
+ return type; |
+ } |
+ |
+ /** |
+ * Return the variable element represented by the given [expression], or |
+ * `null` if there is no such element. |
+ */ |
+ static VariableElement getVariableElement(Expression expression) { |
+ if (expression is Identifier) { |
+ Element element = expression.staticElement; |
+ if (element is VariableElement) { |
+ return element; |
+ } |
+ } |
+ return null; |
+ } |
+} |
+ |
+class GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference |
+ extends GeneralizingElementVisitor<Object> { |
+ List<Element> toCheck; |
+ |
+ GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference(this.toCheck) |
+ : super(); |
+ |
+ @override |
+ Object visitClassElement(ClassElement element) { |
+ // Typedefs are allowed to reference themselves via classes. |
+ return null; |
+ } |
+ |
+ @override |
+ Object visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
+ _addTypeToCheck(element.returnType); |
+ return super.visitFunctionTypeAliasElement(element); |
+ } |
+ |
+ @override |
+ Object visitParameterElement(ParameterElement element) { |
+ _addTypeToCheck(element.type); |
+ return super.visitParameterElement(element); |
+ } |
+ |
+ @override |
+ Object visitTypeParameterElement(TypeParameterElement element) { |
+ _addTypeToCheck(element.bound); |
+ return super.visitTypeParameterElement(element); |
+ } |
+ |
+ void _addTypeToCheck(DartType type) { |
+ if (type == null) { |
+ return; |
+ } |
+ // schedule for checking |
+ toCheck.add(type.element); |
+ // type arguments |
+ if (type is InterfaceType) { |
+ InterfaceType interfaceType = type; |
+ for (DartType typeArgument in interfaceType.typeArguments) { |
+ _addTypeToCheck(typeArgument); |
+ } |
+ } |
+ } |
+} |