| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:collection'; |
| import "dart:math" as math; |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/visitor.dart'; |
| import 'package:analyzer/error/error.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/constant/evaluation.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager2.dart'; |
| import 'package:analyzer/src/dart/element/member.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/error/literal_element_verifier.dart'; |
| import 'package:analyzer/src/error/pending_error.dart'; |
| import 'package:analyzer/src/generated/element_resolver.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/java_engine.dart'; |
| import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary; |
| import 'package:analyzer/src/generated/source.dart'; |
| |
| /** |
| * 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<void> { |
| /** |
| * The error reporter by which errors will be reported. |
| */ |
| final ErrorReporter _errorReporter; |
| |
| /** |
| * The current library that is being analyzed. |
| */ |
| final LibraryElement _currentLibrary; |
| |
| /** |
| * The type representing the type 'bool'. |
| */ |
| InterfaceType _boolType; |
| |
| /** |
| * The type representing the type 'int'. |
| */ |
| InterfaceType _intType; |
| |
| /** |
| * The options for verification. |
| */ |
| AnalysisOptionsImpl _options; |
| |
| /** |
| * The object providing access to the types defined by the language. |
| */ |
| final TypeProvider _typeProvider; |
| |
| /** |
| * The type system primitives |
| */ |
| TypeSystem _typeSystem; |
| |
| /** |
| * The manager for the inheritance mappings. |
| */ |
| final InheritanceManager2 _inheritanceManager; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a constructor |
| * declaration that is 'const'. |
| * |
| * See [visitConstructorDeclaration]. |
| */ |
| bool _isEnclosingConstructorConst = false; |
| |
| /** |
| * A flag indicating whether we are currently within a function body marked as |
| * being asynchronous. |
| */ |
| bool _inAsync = false; |
| |
| /** |
| * A flag indicating whether we are currently within a function body marked a |
| * being a generator. |
| */ |
| bool _inGenerator = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a catch clause. |
| * |
| * See [visitCatchClause]. |
| */ |
| bool _isInCatchClause = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a comment. |
| */ |
| bool _isInComment = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within an instance |
| * creation expression. |
| */ |
| bool _isInConstInstanceCreation = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a native class |
| * declaration. |
| */ |
| bool _isInNativeClass = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a static variable |
| * declaration. |
| */ |
| bool _isInStaticVariableDeclaration = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within an instance |
| * variable declaration. |
| */ |
| bool _isInInstanceVariableDeclaration = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within an instance |
| * variable initializer. |
| */ |
| bool _isInInstanceVariableInitializer = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a constructor |
| * initializer. |
| */ |
| bool _isInConstructorInitializer = false; |
| |
| /** |
| * This is set to `true` iff the visitor is currently within a function typed |
| * formal parameter. |
| */ |
| bool _isInFunctionTypedFormalParameter = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a static method. |
| * By "method" here getter, setter and operator declarations are also implied |
| * since they are all represented with a [MethodDeclaration] in the AST |
| * structure. |
| */ |
| bool _isInStaticMethod = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within a factory |
| * constructor. |
| */ |
| bool _isInFactory = false; |
| |
| /** |
| * A flag indicating whether the visitor is currently within code in the SDK. |
| */ |
| bool _isInSystemLibrary = false; |
| |
| /** |
| * A flag indicating whether the current library contains at least one import |
| * directive with a URI that uses the "dart-ext" scheme. |
| */ |
| bool _hasExtUri = false; |
| |
| /** |
| * This is set to `false` on the entry of every [BlockFunctionBody], and is |
| * restored to the enclosing value on exit. The value is used in |
| * [_checkForMixedReturns] to prevent both |
| * [StaticWarningCode.MIXED_RETURN_TYPES] and |
| * [StaticWarningCode.RETURN_WITHOUT_VALUE] from being generated in the same |
| * function body. |
| */ |
| bool _hasReturnWithoutValue = false; |
| |
| /** |
| * The class containing the AST nodes being visited, or `null` if we are not |
| * in the scope of a class. |
| */ |
| ClassElementImpl _enclosingClass; |
| |
| /** |
| * The enum containing the AST nodes being visited, or `null` if we are not |
| * in the scope of an enum. |
| */ |
| ClassElement _enclosingEnum; |
| |
| /** |
| * The method or function that we are currently visiting, or `null` if we are |
| * not inside a method or function. |
| */ |
| ExecutableElement _enclosingFunction; |
| |
| /** |
| * The return statements found in the method or function that we are currently |
| * visiting that have a return value. |
| */ |
| List<ReturnStatement> _returnsWith = new List<ReturnStatement>(); |
| |
| /** |
| * The return statements found in the method or function that we are currently |
| * visiting that do not have a return value. |
| */ |
| List<ReturnStatement> _returnsWithout = new List<ReturnStatement>(); |
| |
| /** |
| * This map is initialized when visiting the contents of a class declaration. |
| * If the visitor is not in an enclosing class declaration, then the map is |
| * set to `null`. |
| * |
| * When set the map maps the set of [FieldElement]s in the class to an |
| * [INIT_STATE.NOT_INIT] or [INIT_STATE.INIT_IN_DECLARATION]. The `checkFor*` |
| * methods, specifically [_checkForAllFinalInitializedErrorCodes], can make a |
| * copy of the map to compute error code states. The `checkFor*` methods |
| * should only ever make a copy, or read from this map after it has been set |
| * in [visitClassDeclaration]. |
| * |
| * See [visitClassDeclaration], and [_checkForAllFinalInitializedErrorCodes]. |
| */ |
| Map<FieldElement, INIT_STATE> _initialFieldElementsMap; |
| |
| /** |
| * A table mapping name of the library to the export directive which export |
| * this library. |
| */ |
| Map<String, LibraryElement> _nameToExportElement = |
| new HashMap<String, LibraryElement>(); |
| |
| /** |
| * A table mapping name of the library to the import directive which import |
| * this library. |
| */ |
| Map<String, LibraryElement> _nameToImportElement = |
| new HashMap<String, LibraryElement>(); |
| |
| /** |
| * A table mapping names to the exported elements. |
| */ |
| Map<String, Element> _exportedElements = new HashMap<String, Element>(); |
| |
| /** |
| * A set of the names of the variable initializers we are visiting now. |
| */ |
| HashSet<String> _namesForReferenceToDeclaredVariableInInitializer = |
| new HashSet<String>(); |
| |
| /** |
| * The elements that will be defined later in the current scope, but right |
| * now are not declared. |
| */ |
| HiddenElements _hiddenElements = null; |
| |
| /** |
| * A list of types used by the [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS] |
| * and [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS] error codes. |
| */ |
| List<InterfaceType> _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT; |
| |
| final _UninstantiatedBoundChecker _uninstantiatedBoundChecker; |
| |
| /// Setting this flag to `true` disables the check for conflicting generics. |
| /// This is used when running with the old task model to work around |
| /// dartbug.com/32421. |
| /// |
| /// TODO(paulberry): remove this flag once dartbug.com/32421 is properly |
| /// fixed. |
| final bool disableConflictingGenericsCheck; |
| |
| bool _isNonNullable = false; |
| |
| /** |
| * Initialize a newly created error verifier. |
| */ |
| ErrorVerifier(ErrorReporter errorReporter, this._currentLibrary, |
| this._typeProvider, this._inheritanceManager, bool enableSuperMixins, |
| {this.disableConflictingGenericsCheck: false}) |
| : _errorReporter = errorReporter, |
| _uninstantiatedBoundChecker = |
| new _UninstantiatedBoundChecker(errorReporter) { |
| this._isInSystemLibrary = _currentLibrary.source.isInSystemLibrary; |
| this._hasExtUri = _currentLibrary.hasExtUri; |
| _isEnclosingConstructorConst = false; |
| _isInCatchClause = false; |
| _isInStaticVariableDeclaration = false; |
| _isInInstanceVariableDeclaration = false; |
| _isInInstanceVariableInitializer = false; |
| _isInConstructorInitializer = false; |
| _isInStaticMethod = false; |
| _boolType = _typeProvider.boolType; |
| _intType = _typeProvider.intType; |
| _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT = _typeProvider.nonSubtypableTypes; |
| _typeSystem = _currentLibrary.context.typeSystem; |
| _options = _currentLibrary.context.analysisOptions; |
| } |
| |
| /** |
| * If `true`, mixins are allowed to inherit from types other than Object, and |
| * are allowed to reference `super`. |
| */ |
| @deprecated |
| bool get enableSuperMixins => false; |
| |
| ClassElement get enclosingClass => _enclosingClass; |
| |
| /** |
| * For consumers of error verification as a library, (currently just the |
| * angular plugin), expose a setter that can make the errors reported more |
| * accurate when dangling code snippets are being resolved from a class |
| * context. Note that this setter is very defensive for potential misuse; it |
| * should not be modified in the middle of visiting a tree and requires an |
| * analyzer-provided Impl instance to work. |
| */ |
| set enclosingClass(ClassElement classElement) { |
| assert(classElement is ClassElementImpl); |
| assert(_enclosingClass == null); |
| assert(_enclosingEnum == null); |
| assert(_enclosingFunction == null); |
| _enclosingClass = classElement; |
| } |
| |
| @override |
| void visitAnnotation(Annotation node) { |
| _checkForInvalidAnnotationFromDeferredLibrary(node); |
| _checkForMissingJSLibAnnotation(node); |
| super.visitAnnotation(node); |
| } |
| |
| @override |
| void visitArgumentList(ArgumentList node) { |
| _checkForArgumentTypesNotAssignableInList(node); |
| super.visitArgumentList(node); |
| } |
| |
| @override |
| void visitAsExpression(AsExpression node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| super.visitAsExpression(node); |
| } |
| |
| @override |
| void visitAssertInitializer(AssertInitializer node) { |
| _checkForNonBoolExpression(node); |
| super.visitAssertInitializer(node); |
| } |
| |
| @override |
| void visitAssertStatement(AssertStatement node) { |
| _checkForNonBoolExpression(node); |
| super.visitAssertStatement(node); |
| } |
| |
| @override |
| void visitAssignmentExpression(AssignmentExpression node) { |
| TokenType operatorType = node.operator.type; |
| Expression lhs = node.leftHandSide; |
| Expression rhs = node.rightHandSide; |
| if (operatorType == TokenType.EQ || |
| operatorType == TokenType.QUESTION_QUESTION_EQ) { |
| _checkForInvalidAssignment(lhs, rhs); |
| } else { |
| _checkForInvalidCompoundAssignment(node, lhs, rhs); |
| _checkForArgumentTypeNotAssignableForArgument(rhs); |
| _checkForNullableDereference(lhs); |
| } |
| _checkForAssignmentToFinal(lhs); |
| super.visitAssignmentExpression(node); |
| } |
| |
| @override |
| void visitAwaitExpression(AwaitExpression node) { |
| if (!_inAsync) { |
| _errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, node.awaitKeyword); |
| } |
| super.visitAwaitExpression(node); |
| } |
| |
| @override |
| void visitBinaryExpression(BinaryExpression node) { |
| Token operator = node.operator; |
| TokenType type = operator.type; |
| if (type == TokenType.AMPERSAND_AMPERSAND || type == TokenType.BAR_BAR) { |
| String lexeme = operator.lexeme; |
| _checkForAssignability(node.leftOperand, _boolType, |
| StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]); |
| _checkForAssignability(node.rightOperand, _boolType, |
| StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]); |
| _checkForUseOfVoidResult(node.rightOperand); |
| _checkForNullableDereference(node.leftOperand); |
| _checkForNullableDereference(node.rightOperand); |
| } else if (type != TokenType.EQ_EQ && |
| type != TokenType.BANG_EQ && |
| type != TokenType.QUESTION_QUESTION) { |
| _checkForArgumentTypeNotAssignableForArgument(node.rightOperand); |
| _checkForNullableDereference(node.leftOperand); |
| } else { |
| _checkForArgumentTypeNotAssignableForArgument(node.rightOperand); |
| } |
| |
| _checkForUseOfVoidResult(node.leftOperand); |
| |
| super.visitBinaryExpression(node); |
| } |
| |
| @override |
| void visitBlock(Block node) { |
| _hiddenElements = new HiddenElements(_hiddenElements, node); |
| try { |
| _checkDuplicateDeclarationInStatements(node.statements); |
| super.visitBlock(node); |
| } finally { |
| _hiddenElements = _hiddenElements.outerElements; |
| } |
| } |
| |
| @override |
| void 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; |
| } |
| } |
| |
| @override |
| void 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); |
| } |
| } |
| } |
| |
| void visitCascadeExpression(CascadeExpression node) { |
| _checkForNullableDereference(node.target); |
| super.visitCascadeExpression(node); |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| _checkDuplicateDefinitionInCatchClause(node); |
| bool previousIsInCatchClause = _isInCatchClause; |
| try { |
| _isInCatchClause = true; |
| _checkForTypeAnnotationDeferredClass(node.exceptionType); |
| _checkForPotentiallyNullableType(node.exceptionType); |
| super.visitCatchClause(node); |
| } finally { |
| _isInCatchClause = previousIsInCatchClause; |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| ClassElementImpl outerClass = _enclosingClass; |
| try { |
| _isInNativeClass = node.nativeClause != null; |
| _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement); |
| |
| List<ClassMember> members = node.members; |
| _checkDuplicateClassMembers(members); |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
| _checkForMemberWithClassName(); |
| _checkForNoDefaultSuperConstructorImplicit(node); |
| _checkForConflictingTypeVariableErrorCodes(); |
| TypeName superclass = node.extendsClause?.superclass; |
| ImplementsClause implementsClause = node.implementsClause; |
| WithClause withClause = node.withClause; |
| |
| // Only do error checks on the clause nodes if there is a non-null clause |
| if (implementsClause != null || |
| superclass != null || |
| withClause != null) { |
| _checkClassInheritance(node, superclass, withClause, implementsClause); |
| } |
| |
| _initializeInitialFieldElementsMap(_enclosingClass.fields); |
| _checkForFinalNotInitializedInClass(members); |
| _checkForBadFunctionUse(node); |
| super.visitClassDeclaration(node); |
| } finally { |
| _isInNativeClass = false; |
| _initialFieldElementsMap = null; |
| _enclosingClass = outerClass; |
| } |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| ClassElementImpl outerClassElement = _enclosingClass; |
| try { |
| _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement); |
| _checkClassInheritance( |
| node, node.superclass, node.withClause, node.implementsClause); |
| } finally { |
| _enclosingClass = outerClassElement; |
| } |
| super.visitClassTypeAlias(node); |
| } |
| |
| @override |
| void visitComment(Comment node) { |
| _isInComment = true; |
| try { |
| super.visitComment(node); |
| } finally { |
| _isInComment = false; |
| } |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| _isNonNullable = (node as CompilationUnitImpl).isNonNullable; |
| _checkDuplicateUnitMembers(node); |
| _checkForDeferredPrefixCollisions(node); |
| super.visitCompilationUnit(node); |
| _isNonNullable = false; |
| } |
| |
| @override |
| void visitConditionalExpression(ConditionalExpression node) { |
| _checkForNonBoolCondition(node.condition); |
| super.visitConditionalExpression(node); |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| ExecutableElement outerFunction = _enclosingFunction; |
| try { |
| ConstructorElement constructorElement = node.declaredElement; |
| _enclosingFunction = constructorElement; |
| _isEnclosingConstructorConst = node.constKeyword != null; |
| _isInFactory = node.factoryKeyword != null; |
| _checkForInvalidModifierOnBody( |
| node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR); |
| _checkForConstConstructorWithNonFinalField(node, constructorElement); |
| _checkForConstConstructorWithNonConstSuper(node); |
| _checkForAllFinalInitializedErrorCodes(node); |
| _checkForRedirectingConstructorErrorCodes(node); |
| _checkForMixinDeclaresConstructor(node); |
| _checkForMultipleSuperInitializers(node); |
| _checkForRecursiveConstructorRedirect(node, constructorElement); |
| if (!_checkForRecursiveFactoryRedirect(node, constructorElement)) { |
| _checkForAllRedirectConstructorErrorCodes(node); |
| } |
| _checkForUndefinedConstructorInInitializerImplicit(node); |
| _checkForRedirectToNonConstConstructor(node, constructorElement); |
| _checkForReturnInGenerativeConstructor(node); |
| super.visitConstructorDeclaration(node); |
| } finally { |
| _isEnclosingConstructorConst = false; |
| _isInFactory = false; |
| _enclosingFunction = outerFunction; |
| } |
| } |
| |
| @override |
| void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| _isInConstructorInitializer = true; |
| try { |
| SimpleIdentifier fieldName = node.fieldName; |
| Element staticElement = fieldName.staticElement; |
| _checkForInvalidField(node, fieldName, staticElement); |
| if (staticElement is FieldElement) { |
| _checkForFieldInitializerNotAssignable(node, staticElement); |
| } |
| super.visitConstructorFieldInitializer(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void 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); |
| } |
| } |
| } |
| |
| @override |
| void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| _checkForInvalidAssignment(node.identifier, node.defaultValue); |
| _checkForDefaultValueInFunctionTypedParameter(node); |
| super.visitDefaultFormalParameter(node); |
| } |
| |
| @override |
| void visitDoStatement(DoStatement node) { |
| _checkForNonBoolCondition(node.condition); |
| super.visitDoStatement(node); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| ClassElement outerEnum = _enclosingEnum; |
| try { |
| _enclosingEnum = node.declaredElement; |
| _checkDuplicateEnumMembers(node); |
| super.visitEnumDeclaration(node); |
| } finally { |
| _enclosingEnum = outerEnum; |
| } |
| } |
| |
| @override |
| void 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); |
| } |
| super.visitExportDirective(node); |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| bool wasInAsync = _inAsync; |
| bool wasInGenerator = _inGenerator; |
| try { |
| _inAsync = node.isAsynchronous; |
| _inGenerator = node.isGenerator; |
| FunctionType functionType = _enclosingFunction?.type; |
| DartType expectedReturnType = functionType == null |
| ? DynamicTypeImpl.instance |
| : functionType.returnType; |
| ExecutableElement function = _enclosingFunction; |
| bool isSetterWithImplicitReturn = function.hasImplicitReturnType && |
| function is PropertyAccessorElement && |
| function.isSetter; |
| if (!isSetterWithImplicitReturn) { |
| _checkForReturnOfInvalidType(node.expression, expectedReturnType, |
| isArrowFunction: true); |
| } |
| super.visitExpressionFunctionBody(node); |
| } finally { |
| _inAsync = wasInAsync; |
| _inGenerator = wasInGenerator; |
| } |
| } |
| |
| @override |
| void 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 { |
| super.visitFieldDeclaration(node); |
| } finally { |
| _isInStaticVariableDeclaration = false; |
| _isInInstanceVariableDeclaration = false; |
| } |
| } |
| |
| @override |
| void visitFieldFormalParameter(FieldFormalParameter node) { |
| _checkForValidField(node); |
| _checkForConstFormalParameter(node); |
| _checkForPrivateOptionalParameter(node); |
| _checkForFieldInitializingFormalRedirectingConstructor(node); |
| _checkForTypeAnnotationDeferredClass(node.type); |
| super.visitFieldFormalParameter(node); |
| } |
| |
| @override |
| void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { |
| DeclaredIdentifier loopVariable = node.loopVariable; |
| if (loopVariable == null) { |
| // Ignore malformed for statements. |
| return; |
| } |
| if (_checkForEachParts(node, loopVariable.identifier)) { |
| if (loopVariable.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE, loopVariable); |
| } |
| } |
| super.visitForEachPartsWithDeclaration(node); |
| } |
| |
| @override |
| void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) { |
| SimpleIdentifier identifier = node.identifier; |
| if (identifier == null) { |
| // Ignore malformed for statements. |
| return; |
| } |
| if (_checkForEachParts(node, identifier)) { |
| Element variableElement = identifier.staticElement; |
| if (variableElement is VariableElement && variableElement.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE, identifier); |
| } |
| } |
| super.visitForEachPartsWithIdentifier(node); |
| } |
| |
| @override |
| void visitFormalParameterList(FormalParameterList node) { |
| _checkDuplicateDefinitionInParameterList(node); |
| _checkUseOfCovariantInParameters(node); |
| super.visitFormalParameterList(node); |
| } |
| |
| @override |
| void visitForPartsWithDeclarations(ForPartsWithDeclarations node) { |
| if (node.condition != null) { |
| _checkForNonBoolCondition(node.condition); |
| } |
| if (node.variables != null) { |
| _checkDuplicateVariables(node.variables); |
| } |
| super.visitForPartsWithDeclarations(node); |
| } |
| |
| @override |
| void visitForPartsWithExpression(ForPartsWithExpression node) { |
| if (node.condition != null) { |
| _checkForNonBoolCondition(node.condition); |
| } |
| super.visitForPartsWithExpression(node); |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| ExecutableElement functionElement = node.declaredElement; |
| if (functionElement != null && |
| functionElement.enclosingElement is! CompilationUnitElement) { |
| _hiddenElements.declare(functionElement); |
| } |
| ExecutableElement outerFunction = _enclosingFunction; |
| try { |
| SimpleIdentifier identifier = node.name; |
| String methodName = ""; |
| if (identifier != null) { |
| methodName = identifier.name; |
| } |
| _enclosingFunction = functionElement; |
| TypeAnnotation returnType = node.returnType; |
| if (node.isSetter || node.isGetter) { |
| _checkForMismatchedAccessorTypes(node, methodName); |
| if (node.isSetter) { |
| FunctionExpression functionExpression = node.functionExpression; |
| if (functionExpression != null) { |
| _checkForWrongNumberOfParametersForSetter( |
| identifier, functionExpression.parameters); |
| } |
| _checkForNonVoidReturnTypeForSetter(returnType); |
| } |
| } |
| if (node.isSetter) { |
| _checkForInvalidModifierOnBody(node.functionExpression.body, |
| CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER); |
| } |
| _checkForTypeAnnotationDeferredClass(returnType); |
| _checkForIllegalReturnType(returnType); |
| _checkForImplicitDynamicReturn(node.name, node.declaredElement); |
| super.visitFunctionDeclaration(node); |
| } finally { |
| _enclosingFunction = outerFunction; |
| } |
| } |
| |
| @override |
| void 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.declaredElement; |
| super.visitFunctionExpression(node); |
| } finally { |
| _enclosingFunction = outerFunction; |
| } |
| } else { |
| super.visitFunctionExpression(node); |
| } |
| } |
| |
| @override |
| void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| Expression functionExpression = node.function; |
| DartType expressionType = functionExpression.staticType; |
| if (!_checkForNullableDereference(functionExpression) && |
| !_checkForUseOfVoidResult(functionExpression) && |
| !_isFunctionType(expressionType)) { |
| _errorReporter.reportErrorForNode( |
| StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION, |
| functionExpression); |
| } else if (expressionType is FunctionType) { |
| _checkTypeArguments(node); |
| } |
| _checkForImplicitDynamicInvoke(node); |
| _checkForNullableDereference(node.function); |
| super.visitFunctionExpressionInvocation(node); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| _checkForDefaultValueInFunctionTypeAlias(node); |
| _checkForTypeAliasCannotReferenceItself_function(node); |
| super.visitFunctionTypeAlias(node); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| bool old = _isInFunctionTypedFormalParameter; |
| _isInFunctionTypedFormalParameter = true; |
| try { |
| _checkForTypeAnnotationDeferredClass(node.returnType); |
| |
| // TODO(jmesserly): ideally we'd use _checkForImplicitDynamicReturn, and |
| // we can get the function element via `node?.element?.type?.element` but |
| // it doesn't have hasImplicitReturnType set correctly. |
| if (!_options.implicitDynamic && node.returnType == null) { |
| DartType parameterType = |
| resolutionMap.elementDeclaredByFormalParameter(node).type; |
| if (parameterType is FunctionType && |
| parameterType.returnType.isDynamic) { |
| _errorReporter.reportErrorForNode( |
| StrongModeCode.IMPLICIT_DYNAMIC_RETURN, |
| node.identifier, |
| [node.identifier]); |
| } |
| } |
| |
| // TODO(paulberry): remove this once dartbug.com/28515 is fixed. |
| if (node.typeParameters != null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.GENERIC_FUNCTION_TYPED_PARAM_UNSUPPORTED, |
| node); |
| } |
| |
| super.visitFunctionTypedFormalParameter(node); |
| } finally { |
| _isInFunctionTypedFormalParameter = old; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| if (_hasTypedefSelfReference(node.declaredElement)) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, node); |
| } |
| super.visitGenericTypeAlias(node); |
| } |
| |
| @override |
| void visitIfElement(IfElement node) { |
| _checkForNonBoolCondition(node.condition); |
| super.visitIfElement(node); |
| } |
| |
| @override |
| void visitIfStatement(IfStatement node) { |
| _checkForNonBoolCondition(node.condition); |
| super.visitIfStatement(node); |
| } |
| |
| @override |
| void visitImplementsClause(ImplementsClause node) { |
| node.interfaces.forEach(_checkForImplicitDynamicType); |
| super.visitImplementsClause(node); |
| } |
| |
| @override |
| void visitImportDirective(ImportDirective node) { |
| ImportElement importElement = node.element; |
| if (node.prefix != null) { |
| _checkForBuiltInIdentifierAsName( |
| node.prefix, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME); |
| } |
| if (importElement != null) { |
| _checkForImportDuplicateLibraryName(node, importElement); |
| _checkForImportInternalLibrary(node, importElement); |
| } |
| super.visitImportDirective(node); |
| } |
| |
| @override |
| void visitIndexExpression(IndexExpression node) { |
| _checkForArgumentTypeNotAssignableForArgument(node.index); |
| _checkForNullableDereference(node.target); |
| super.visitIndexExpression(node); |
| } |
| |
| @override |
| void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| bool wasInConstInstanceCreation = _isInConstInstanceCreation; |
| _isInConstInstanceCreation = node.isConst; |
| try { |
| ConstructorName constructorName = node.constructorName; |
| TypeName typeName = constructorName.type; |
| DartType type = typeName.type; |
| if (type is InterfaceType) { |
| _checkForConstOrNewWithAbstractClass(node, typeName, type); |
| _checkForConstOrNewWithEnum(node, typeName, type); |
| _checkForConstOrNewWithMixin(node, typeName, type); |
| if (_isInConstInstanceCreation) { |
| _checkForConstWithNonConst(node); |
| _checkForConstWithUndefinedConstructor( |
| node, constructorName, typeName); |
| _checkForConstDeferredClass(node, constructorName, typeName); |
| } else { |
| _checkForNewWithUndefinedConstructor(node, constructorName, typeName); |
| } |
| } |
| _checkForImplicitDynamicType(typeName); |
| super.visitInstanceCreationExpression(node); |
| } finally { |
| _isInConstInstanceCreation = wasInConstInstanceCreation; |
| } |
| } |
| |
| @override |
| void visitIntegerLiteral(IntegerLiteral node) { |
| _checkForOutOfRange(node); |
| super.visitIntegerLiteral(node); |
| } |
| |
| @override |
| void visitInterpolationExpression(InterpolationExpression node) { |
| _checkForUseOfVoidResult(node.expression); |
| super.visitInterpolationExpression(node); |
| } |
| |
| @override |
| void visitIsExpression(IsExpression node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| _checkForUseOfVoidResult(node.expression); |
| super.visitIsExpression(node); |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral node) { |
| TypeArgumentList typeArguments = node.typeArguments; |
| if (typeArguments != null) { |
| if (node.isConst) { |
| NodeList<TypeAnnotation> arguments = typeArguments.arguments; |
| if (arguments.isNotEmpty) { |
| _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, |
| CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST); |
| } |
| } |
| _checkTypeArgumentCount(typeArguments, 1, |
| StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS); |
| } |
| _checkForInferenceFailureOnCollectionLiteral(node); |
| _checkForImplicitDynamicTypedLiteral(node); |
| _checkForListElementTypeNotAssignable(node); |
| |
| super.visitListLiteral(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| ExecutableElement previousFunction = _enclosingFunction; |
| try { |
| _isInStaticMethod = node.isStatic; |
| _enclosingFunction = node.declaredElement; |
| TypeAnnotation returnType = node.returnType; |
| if (node.isSetter) { |
| _checkForInvalidModifierOnBody( |
| node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER); |
| _checkForWrongNumberOfParametersForSetter(node.name, node.parameters); |
| _checkForNonVoidReturnTypeForSetter(returnType); |
| } else if (node.isOperator) { |
| _checkForOptionalParameterInOperator(node); |
| _checkForWrongNumberOfParametersForOperator(node); |
| _checkForNonVoidReturnTypeForOperator(node); |
| } |
| _checkForTypeAnnotationDeferredClass(returnType); |
| _checkForIllegalReturnType(returnType); |
| _checkForImplicitDynamicReturn(node, node.declaredElement); |
| _checkForMustCallSuper(node); |
| super.visitMethodDeclaration(node); |
| } finally { |
| _enclosingFunction = previousFunction; |
| _isInStaticMethod = false; |
| } |
| } |
| |
| @override |
| void 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); |
| _checkForUnnecessaryNullAware(target, node.operator); |
| } else { |
| _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName); |
| _checkForNullableDereference(node.function); |
| } |
| _checkTypeArguments(node); |
| _checkForImplicitDynamicInvoke(node); |
| if (node.operator?.type != TokenType.QUESTION_PERIOD && |
| methodName.name != 'toString' && |
| methodName.name != 'noSuchMethod') { |
| _checkForNullableDereference(target); |
| } |
| super.visitMethodInvocation(node); |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| // TODO(scheglov) Verify for all mixin errors. |
| ClassElementImpl outerClass = _enclosingClass; |
| try { |
| _enclosingClass = AbstractClassElementImpl.getImpl(node.declaredElement); |
| |
| List<ClassMember> members = node.members; |
| _checkDuplicateClassMembers(members); |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
| _checkForMemberWithClassName(); |
| _checkForConflictingTypeVariableErrorCodes(); |
| |
| OnClause onClause = node.onClause; |
| ImplementsClause implementsClause = node.implementsClause; |
| |
| // Only do error checks only if there is a non-null clause. |
| if (onClause != null || implementsClause != null) { |
| _checkMixinInheritance(node, onClause, implementsClause); |
| } |
| |
| _initializeInitialFieldElementsMap(_enclosingClass.fields); |
| _checkForFinalNotInitializedInClass(members); |
| // _checkForBadFunctionUse(node); |
| super.visitMixinDeclaration(node); |
| } finally { |
| _initialFieldElementsMap = null; |
| _enclosingClass = outerClass; |
| } |
| } |
| |
| @override |
| void 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); |
| } |
| super.visitNativeClause(node); |
| } |
| |
| @override |
| void visitNativeFunctionBody(NativeFunctionBody node) { |
| _checkForNativeFunctionBodyInNonSdkCode(node); |
| super.visitNativeFunctionBody(node); |
| } |
| |
| @override |
| void visitPostfixExpression(PostfixExpression node) { |
| if (node.operator.type != TokenType.BANG) { |
| _checkForAssignmentToFinal(node.operand); |
| _checkForIntNotAssignable(node.operand); |
| _checkForNullableDereference(node.operand); |
| } |
| super.visitPostfixExpression(node); |
| } |
| |
| @override |
| void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| if (node.parent is! Annotation) { |
| ClassElement typeReference = |
| ElementResolver.getTypeReference(node.prefix); |
| SimpleIdentifier name = node.identifier; |
| _checkForStaticAccessToInstanceMember(typeReference, name); |
| _checkForInstanceAccessToStaticMember(typeReference, name); |
| } |
| String property = node.identifier.name; |
| if (node.staticElement is ExecutableElement && |
| property != 'hashCode' && |
| property != 'runtimeType') { |
| _checkForNullableDereference(node.prefix); |
| } |
| super.visitPrefixedIdentifier(node); |
| } |
| |
| @override |
| void visitPrefixExpression(PrefixExpression node) { |
| TokenType operatorType = node.operator.type; |
| Expression operand = node.operand; |
| if (operatorType == TokenType.BANG) { |
| _checkForNonBoolNegationExpression(operand); |
| } else if (operatorType.isIncrementOperator) { |
| _checkForAssignmentToFinal(operand); |
| } |
| _checkForIntNotAssignable(operand); |
| _checkForNullableDereference(operand); |
| _checkForUseOfVoidResult(operand); |
| super.visitPrefixExpression(node); |
| } |
| |
| @override |
| void visitPropertyAccess(PropertyAccess node) { |
| ClassElement typeReference = |
| ElementResolver.getTypeReference(node.realTarget); |
| SimpleIdentifier propertyName = node.propertyName; |
| _checkForStaticAccessToInstanceMember(typeReference, propertyName); |
| _checkForInstanceAccessToStaticMember(typeReference, propertyName); |
| if (node.operator?.type != TokenType.QUESTION_PERIOD && |
| propertyName.name != 'hashCode' && |
| propertyName.name != 'runtimeType') { |
| _checkForNullableDereference(node.target); |
| } |
| _checkForUnnecessaryNullAware(node.target, node.operator); |
| super.visitPropertyAccess(node); |
| } |
| |
| @override |
| void visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node) { |
| _isInConstructorInitializer = true; |
| try { |
| super.visitRedirectingConstructorInvocation(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitRethrowExpression(RethrowExpression node) { |
| _checkForRethrowOutsideCatch(node); |
| super.visitRethrowExpression(node); |
| } |
| |
| @override |
| void visitReturnStatement(ReturnStatement node) { |
| if (node.expression == null) { |
| _returnsWithout.add(node); |
| } else { |
| _returnsWith.add(node); |
| } |
| _checkForAllReturnStatementErrorCodes(node); |
| super.visitReturnStatement(node); |
| } |
| |
| @override |
| void visitSetOrMapLiteral(SetOrMapLiteral node) { |
| TypeArgumentList typeArguments = node.typeArguments; |
| if (node.isMap) { |
| if (typeArguments != null) { |
| NodeList<TypeAnnotation> arguments = typeArguments.arguments; |
| if (node.isConst) { |
| if (arguments.isNotEmpty) { |
| _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, |
| CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP); |
| } |
| } |
| _checkTypeArgumentCount(typeArguments, 2, |
| StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS); |
| } |
| _checkForInferenceFailureOnCollectionLiteral(node); |
| _checkForImplicitDynamicTypedLiteral(node); |
| _checkForMapTypeNotAssignable(node); |
| _checkForNonConstMapAsExpressionStatement3(node); |
| } else if (node.isSet) { |
| if (typeArguments != null) { |
| if (node.isConst) { |
| NodeList<TypeAnnotation> arguments = typeArguments.arguments; |
| if (arguments.isNotEmpty) { |
| _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, |
| CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_SET); |
| } |
| } |
| _checkTypeArgumentCount(typeArguments, 1, |
| StaticTypeWarningCode.EXPECTED_ONE_SET_TYPE_ARGUMENTS); |
| } |
| _checkForInferenceFailureOnCollectionLiteral(node); |
| _checkForImplicitDynamicTypedLiteral(node); |
| _checkForSetElementTypeNotAssignable3(node); |
| } |
| super.visitSetOrMapLiteral(node); |
| } |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter node) { |
| _checkForConstFormalParameter(node); |
| _checkForPrivateOptionalParameter(node); |
| _checkForTypeAnnotationDeferredClass(node.type); |
| |
| // Checks for an implicit dynamic parameter type. |
| // |
| // We can skip other parameter kinds besides simple formal, because: |
| // - DefaultFormalParameter contains a simple one, so it gets here, |
| // - FieldFormalParameter error should be reported on the field, |
| // - FunctionTypedFormalParameter is a function type, not dynamic. |
| _checkForImplicitDynamicIdentifier(node, node.identifier); |
| |
| super.visitSimpleFormalParameter(node); |
| } |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| _checkForAmbiguousImport(node); |
| _checkForReferenceBeforeDeclaration(node); |
| _checkForImplicitThisReferenceInInitializer(node); |
| _checkForTypeParameterReferencedByStatic(node); |
| if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) { |
| _checkForUnqualifiedReferenceToNonLocalStaticMember(node); |
| } |
| super.visitSimpleIdentifier(node); |
| } |
| |
| @override |
| void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| _isInConstructorInitializer = true; |
| try { |
| super.visitSuperConstructorInvocation(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| _checkDuplicateDeclarationInStatements(node.statements); |
| super.visitSwitchCase(node); |
| } |
| |
| @override |
| void visitSwitchDefault(SwitchDefault node) { |
| _checkDuplicateDeclarationInStatements(node.statements); |
| super.visitSwitchDefault(node); |
| } |
| |
| @override |
| void visitSwitchStatement(SwitchStatement node) { |
| _checkForSwitchExpressionNotAssignable(node); |
| _checkForCaseBlocksNotTerminated(node); |
| _checkForMissingEnumConstantInSwitch(node); |
| super.visitSwitchStatement(node); |
| } |
| |
| @override |
| void visitThisExpression(ThisExpression node) { |
| _checkForInvalidReferenceToThis(node); |
| super.visitThisExpression(node); |
| } |
| |
| @override |
| void visitThrowExpression(ThrowExpression node) { |
| _checkForConstEvalThrowsException(node); |
| _checkForNullableDereference(node.expression); |
| _checkForUseOfVoidResult(node.expression); |
| super.visitThrowExpression(node); |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| _checkForFinalNotInitialized(node.variables); |
| super.visitTopLevelVariableDeclaration(node); |
| } |
| |
| @override |
| void visitTypeArgumentList(TypeArgumentList node) { |
| NodeList<TypeAnnotation> list = node.arguments; |
| for (TypeAnnotation type in list) { |
| _checkForTypeAnnotationDeferredClass(type); |
| } |
| super.visitTypeArgumentList(node); |
| } |
| |
| @override |
| void visitTypeName(TypeName node) { |
| _checkForTypeArgumentNotMatchingBounds(node); |
| if (node.parent is ConstructorName && |
| node.parent.parent is InstanceCreationExpression) { |
| _checkForInferenceFailureOnInstanceCreation(node, node.parent.parent); |
| } else { |
| _checkForRawTypeName(node); |
| } |
| super.visitTypeName(node); |
| } |
| |
| @override |
| void visitTypeParameter(TypeParameter node) { |
| _checkForBuiltInIdentifierAsName(node.name, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME); |
| _checkForTypeParameterSupertypeOfItsBound(node); |
| _checkForTypeAnnotationDeferredClass(node.bound); |
| _checkForImplicitDynamicType(node.bound); |
| _checkForGenericFunctionType(node.bound); |
| node.bound?.accept(_uninstantiatedBoundChecker); |
| super.visitTypeParameter(node); |
| } |
| |
| @override |
| void visitTypeParameterList(TypeParameterList node) { |
| _checkDuplicateDefinitionInTypeParameterList(node); |
| super.visitTypeParameterList(node); |
| } |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| SimpleIdentifier nameNode = node.name; |
| Expression initializerNode = node.initializer; |
| // do checks |
| _checkForInvalidAssignment(nameNode, initializerNode); |
| _checkForImplicitDynamicIdentifier(node, nameNode); |
| // visit name |
| nameNode.accept(this); |
| // visit initializer |
| String name = nameNode.name; |
| _namesForReferenceToDeclaredVariableInInitializer.add(name); |
| bool wasInInstanceVariableInitializer = _isInInstanceVariableInitializer; |
| _isInInstanceVariableInitializer = _isInInstanceVariableDeclaration; |
| try { |
| if (initializerNode != null) { |
| initializerNode.accept(this); |
| } |
| } finally { |
| _isInInstanceVariableInitializer = wasInInstanceVariableInitializer; |
| _namesForReferenceToDeclaredVariableInInitializer.remove(name); |
| } |
| // declare the variable |
| AstNode grandparent = node.parent.parent; |
| if (grandparent is! TopLevelVariableDeclaration && |
| grandparent is! FieldDeclaration) { |
| VariableElement element = node.declaredElement; |
| if (element != null) { |
| // There is no hidden elements if we are outside of a function body, |
| // which will happen for variables declared in control flow elements. |
| _hiddenElements?.declare(element); |
| } |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| super.visitVariableDeclarationList(node); |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| _checkForFinalNotInitialized(node.variables); |
| super.visitVariableDeclarationStatement(node); |
| } |
| |
| @override |
| void visitWhileStatement(WhileStatement node) { |
| _checkForNonBoolCondition(node.condition); |
| super.visitWhileStatement(node); |
| } |
| |
| @override |
| void visitWithClause(WithClause node) { |
| node.mixinTypes.forEach(_checkForImplicitDynamicType); |
| super.visitWithClause(node); |
| } |
| |
| @override |
| void visitYieldStatement(YieldStatement node) { |
| if (_inGenerator) { |
| _checkForYieldOfInvalidType(node.expression, node.star != null); |
| if (node.star != null) { |
| _checkForNullableDereference(node.expression); |
| } |
| } 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); |
| } |
| _checkForUseOfVoidResult(node.expression); |
| super.visitYieldStatement(node); |
| } |
| |
| /** |
| * Checks the class for problems with the superclass, mixins, or implemented |
| * interfaces. |
| */ |
| void _checkClassInheritance( |
| NamedCompilationUnitMember node, |
| TypeName superclass, |
| WithClause withClause, |
| ImplementsClause implementsClause) { |
| // Only check for all of the inheritance logic around clauses if there |
| // isn't an error code such as "Cannot extend double" already on the |
| // class. |
| if (!_checkForExtendsDisallowedClass(superclass) && |
| !_checkForImplementsClauseErrorCodes(implementsClause) && |
| !_checkForAllMixinErrorCodes(withClause)) { |
| _checkForImplicitDynamicType(superclass); |
| _checkForExtendsDeferredClass(superclass); |
| _checkForConflictingClassMembers(); |
| _checkForRepeatedType(implementsClause?.interfaces, |
| CompileTimeErrorCode.IMPLEMENTS_REPEATED); |
| _checkImplementsSuperClass(implementsClause); |
| _checkMixinInference(node, withClause); |
| _checkForMixinWithConflictingPrivateMember(withClause, superclass); |
| if (!disableConflictingGenericsCheck) { |
| _checkForConflictingGenerics(node); |
| } |
| } |
| } |
| |
| /** |
| * Given a list of [directives] that have the same prefix, generate an error |
| * if there is more than one import and any of those imports is deferred. |
| * |
| * See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX]. |
| */ |
| void _checkDeferredPrefixCollision(List<ImportDirective> directives) { |
| int count = directives.length; |
| if (count > 1) { |
| for (int i = 0; i < count; i++) { |
| Token deferredToken = directives[i].deferredKeyword; |
| if (deferredToken != null) { |
| _errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check that there are no members with the same name. |
| */ |
| void _checkDuplicateClassMembers(List<ClassMember> members) { |
| Set<String> constructorNames = new HashSet<String>(); |
| Map<String, Element> instanceGetters = new HashMap<String, Element>(); |
| Map<String, Element> instanceSetters = new HashMap<String, Element>(); |
| Map<String, Element> staticGetters = new HashMap<String, Element>(); |
| Map<String, Element> staticSetters = new HashMap<String, Element>(); |
| |
| for (ClassMember member in members) { |
| if (member is ConstructorDeclaration) { |
| var name = member.name?.name ?? ''; |
| if (!constructorNames.add(name)) { |
| if (name.isEmpty) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT, member); |
| } else { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME, |
| member, |
| [name]); |
| } |
| } |
| } else if (member is FieldDeclaration) { |
| for (VariableDeclaration field in member.fields.variables) { |
| SimpleIdentifier identifier = field.name; |
| _checkDuplicateIdentifier( |
| member.isStatic ? staticGetters : instanceGetters, |
| identifier, |
| setterScope: member.isStatic ? staticSetters : instanceSetters, |
| ); |
| } |
| } else if (member is MethodDeclaration) { |
| _checkDuplicateIdentifier( |
| member.isStatic ? staticGetters : instanceGetters, |
| member.name, |
| setterScope: member.isStatic ? staticSetters : instanceSetters, |
| ); |
| } |
| } |
| |
| // Check for local static members conflicting with local instance members. |
| for (ClassMember member in members) { |
| if (member is ConstructorDeclaration) { |
| if (member.name != null) { |
| String name = member.name.name; |
| var staticMember = staticGetters[name] ?? staticSetters[name]; |
| if (staticMember != null) { |
| if (staticMember is PropertyAccessorElement) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_AND_STATIC_FIELD, |
| member.name, |
| [name], |
| ); |
| } else { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_AND_STATIC_METHOD, |
| member.name, |
| [name], |
| ); |
| } |
| } |
| } |
| } else if (member is FieldDeclaration) { |
| if (member.isStatic) { |
| for (VariableDeclaration field in member.fields.variables) { |
| SimpleIdentifier identifier = field.name; |
| String name = identifier.name; |
| if (instanceGetters.containsKey(name) || |
| instanceSetters.containsKey(name)) { |
| String className = _enclosingClass.displayName; |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, |
| identifier, |
| [className, name, className]); |
| } |
| } |
| } |
| } else if (member is MethodDeclaration) { |
| if (member.isStatic) { |
| SimpleIdentifier identifier = member.name; |
| String name = identifier.name; |
| if (instanceGetters.containsKey(name) || |
| instanceSetters.containsKey(name)) { |
| String className = identifier.staticElement.enclosingElement.name; |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, |
| identifier, |
| [className, name, className]); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check that all of the parameters have unique names. |
| */ |
| void _checkDuplicateDeclarationInStatements(List<Statement> statements) { |
| Map<String, Element> definedNames = new HashMap<String, Element>(); |
| for (Statement statement in statements) { |
| if (statement is VariableDeclarationStatement) { |
| for (VariableDeclaration variable in statement.variables.variables) { |
| _checkDuplicateIdentifier(definedNames, variable.name); |
| } |
| } else if (statement is FunctionDeclarationStatement) { |
| _checkDuplicateIdentifier( |
| definedNames, statement.functionDeclaration.name); |
| } |
| } |
| } |
| |
| /** |
| * Check that the exception and stack trace parameters have different names. |
| */ |
| void _checkDuplicateDefinitionInCatchClause(CatchClause node) { |
| SimpleIdentifier exceptionParameter = node.exceptionParameter; |
| SimpleIdentifier stackTraceParameter = node.stackTraceParameter; |
| if (exceptionParameter != null && stackTraceParameter != null) { |
| String exceptionName = exceptionParameter.name; |
| if (exceptionName == stackTraceParameter.name) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DUPLICATE_DEFINITION, |
| stackTraceParameter, |
| [exceptionName]); |
| } |
| } |
| } |
| |
| /** |
| * Check that all of the parameters have unique names. |
| */ |
| void _checkDuplicateDefinitionInParameterList(FormalParameterList node) { |
| Map<String, Element> definedNames = new HashMap<String, Element>(); |
| for (FormalParameter parameter in node.parameters) { |
| SimpleIdentifier identifier = parameter.identifier; |
| if (identifier != null) { |
| // The identifier can be null if this is a parameter list for a generic |
| // function type. |
| _checkDuplicateIdentifier(definedNames, identifier); |
| } |
| } |
| } |
| |
| /** |
| * Check that all of the parameters have unique names. |
| */ |
| void _checkDuplicateDefinitionInTypeParameterList(TypeParameterList node) { |
| Map<String, Element> definedNames = new HashMap<String, Element>(); |
| for (TypeParameter parameter in node.typeParameters) { |
| _checkDuplicateIdentifier(definedNames, parameter.name); |
| } |
| } |
| |
| /** |
| * Check that there are no members with the same name. |
| */ |
| void _checkDuplicateEnumMembers(EnumDeclaration node) { |
| ClassElement element = node.declaredElement; |
| |
| Map<String, Element> instanceGetters = new HashMap<String, Element>(); |
| Map<String, Element> staticGetters = new HashMap<String, Element>(); |
| |
| String indexName = 'index'; |
| String valuesName = 'values'; |
| instanceGetters[indexName] = element.getGetter(indexName); |
| staticGetters[valuesName] = element.getGetter(valuesName); |
| |
| for (EnumConstantDeclaration constant in node.constants) { |
| _checkDuplicateIdentifier(staticGetters, constant.name); |
| } |
| |
| for (EnumConstantDeclaration constant in node.constants) { |
| SimpleIdentifier identifier = constant.name; |
| String name = identifier.name; |
| if (instanceGetters.containsKey(name)) { |
| String enumName = element.displayName; |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, |
| identifier, |
| [enumName, name, enumName]); |
| } |
| } |
| } |
| |
| /** |
| * Check whether the given [element] defined by the [identifier] is already |
| * in one of the scopes - [getterScope] or [setterScope], and produce an |
| * error if it is. |
| */ |
| void _checkDuplicateIdentifier( |
| Map<String, Element> getterScope, SimpleIdentifier identifier, |
| {Element element, Map<String, Element> setterScope}) { |
| element ??= identifier.staticElement; |
| |
| // Fields define getters and setters, so check them separately. |
| if (element is PropertyInducingElement) { |
| _checkDuplicateIdentifier(getterScope, identifier, |
| element: element.getter, setterScope: setterScope); |
| if (!element.isConst && !element.isFinal) { |
| _checkDuplicateIdentifier(getterScope, identifier, |
| element: element.setter, setterScope: setterScope); |
| } |
| return; |
| } |
| |
| ErrorCode getError(Element previous, Element current) { |
| if (previous is PrefixElement) { |
| return CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER; |
| } |
| return CompileTimeErrorCode.DUPLICATE_DEFINITION; |
| } |
| |
| bool isGetterSetterPair(Element a, Element b) { |
| if (a is PropertyAccessorElement && b is PropertyAccessorElement) { |
| return a.isGetter && b.isSetter || a.isSetter && b.isGetter; |
| } |
| return false; |
| } |
| |
| String name = identifier.name; |
| if (element is MethodElement && element.isOperator && name == '-') { |
| if (element.parameters.length == 0) { |
| name = 'unary-'; |
| } |
| } |
| |
| Element previous = getterScope[name]; |
| if (previous != null) { |
| if (isGetterSetterPair(element, previous)) { |
| // OK |
| } else { |
| _errorReporter.reportErrorForNode( |
| getError(previous, element), |
| identifier, |
| [name], |
| ); |
| } |
| } else { |
| getterScope[name] = element; |
| } |
| |
| if (element is PropertyAccessorElement && element.isSetter) { |
| previous = setterScope[name]; |
| if (previous != null) { |
| _errorReporter.reportErrorForNode( |
| getError(previous, element), |
| identifier, |
| [name], |
| ); |
| } else { |
| setterScope[name] = element; |
| } |
| } |
| } |
| |
| /** |
| * Check that there are no members with the same name. |
| */ |
| void _checkDuplicateUnitMembers(CompilationUnit node) { |
| Map<String, Element> definedGetters = new HashMap<String, Element>(); |
| Map<String, Element> definedSetters = new HashMap<String, Element>(); |
| |
| void addWithoutChecking(CompilationUnitElement element) { |
| for (PropertyAccessorElement accessor in element.accessors) { |
| String name = accessor.name; |
| if (accessor.isSetter) { |
| name += '='; |
| } |
| definedGetters[name] = accessor; |
| } |
| for (ClassElement type in element.enums) { |
| definedGetters[type.name] = type; |
| } |
| for (FunctionElement function in element.functions) { |
| definedGetters[function.name] = function; |
| } |
| for (FunctionTypeAliasElement alias in element.functionTypeAliases) { |
| definedGetters[alias.name] = alias; |
| } |
| for (TopLevelVariableElement variable in element.topLevelVariables) { |
| definedGetters[variable.name] = variable; |
| if (!variable.isFinal && !variable.isConst) { |
| definedGetters[variable.name + '='] = variable; |
| } |
| } |
| for (ClassElement type in element.types) { |
| definedGetters[type.name] = type; |
| } |
| } |
| |
| for (ImportElement importElement in _currentLibrary.imports) { |
| PrefixElement prefix = importElement.prefix; |
| if (prefix != null) { |
| definedGetters[prefix.name] = prefix; |
| } |
| } |
| CompilationUnitElement element = node.declaredElement; |
| if (element != _currentLibrary.definingCompilationUnit) { |
| addWithoutChecking(_currentLibrary.definingCompilationUnit); |
| for (CompilationUnitElement part in _currentLibrary.parts) { |
| if (element == part) { |
| break; |
| } |
| addWithoutChecking(part); |
| } |
| } |
| for (CompilationUnitMember member in node.declarations) { |
| if (member is NamedCompilationUnitMember) { |
| _checkDuplicateIdentifier(definedGetters, member.name, |
| setterScope: definedSetters); |
| } else if (member is TopLevelVariableDeclaration) { |
| for (VariableDeclaration variable in member.variables.variables) { |
| _checkDuplicateIdentifier(definedGetters, variable.name, |
| setterScope: definedSetters); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check that the given list of variable declarations does not define multiple |
| * variables of the same name. |
| */ |
| void _checkDuplicateVariables(VariableDeclarationList node) { |
| Map<String, Element> definedNames = new HashMap<String, Element>(); |
| for (VariableDeclaration variable in node.variables) { |
| _checkDuplicateIdentifier(definedNames, variable.name); |
| } |
| } |
| |
| /** |
| * Check that return statements without expressions are not in a generative |
| * constructor and the return type is not assignable to `null`; that is, we |
| * don't have `return;` if the enclosing method has a non-void containing |
| * return type. |
| * |
| * See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR], |
| * [StaticWarningCode.RETURN_WITHOUT_VALUE], and |
| * [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE]. |
| */ |
| void _checkForAllEmptyReturnStatementErrorCodes( |
| ReturnStatement statement, DartType expectedReturnType) { |
| if (_inGenerator) { |
| return; |
| } |
| var returnType = |
| _inAsync ? _typeSystem.flatten(expectedReturnType) : expectedReturnType; |
| if (returnType.isDynamic || |
| returnType.isDartCoreNull || |
| returnType.isVoid) { |
| return; |
| } |
| // If we reach here, this is an invalid return |
| _hasReturnWithoutValue = true; |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.RETURN_WITHOUT_VALUE, statement); |
| return; |
| } |
| |
| /** |
| * Verify that the given [constructor] declaration does not violate any of the |
| * error codes relating to the initialization of fields in the enclosing |
| * class. |
| * |
| * See [_initialFieldElementsMap], |
| * [StaticWarningCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR], and |
| * [CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES]. |
| */ |
| void _checkForAllFinalInitializedErrorCodes( |
| ConstructorDeclaration constructor) { |
| if (constructor.factoryKeyword != null || |
| constructor.redirectedConstructor != null || |
| constructor.externalKeyword != null) { |
| return; |
| } |
| // Ignore if native class. |
| if (_isInNativeClass) { |
| return; |
| } |
| |
| Map<FieldElement, INIT_STATE> fieldElementsMap = |
| new HashMap<FieldElement, INIT_STATE>.from(_initialFieldElementsMap); |
| // Visit all of the field formal parameters |
| NodeList<FormalParameter> formalParameters = |
| constructor.parameters.parameters; |
| for (FormalParameter formalParameter in formalParameters) { |
| FormalParameter baseParameter(FormalParameter parameter) { |
| if (parameter is DefaultFormalParameter) { |
| return parameter.parameter; |
| } |
| return parameter; |
| } |
| |
| FormalParameter parameter = baseParameter(formalParameter); |
| if (parameter is FieldFormalParameter) { |
| FieldElement fieldElement = |
| (parameter.declaredElement as FieldFormalParameterElementImpl) |
| .field; |
| INIT_STATE state = fieldElementsMap[fieldElement]; |
| if (state == INIT_STATE.NOT_INIT) { |
| fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_FIELD_FORMAL; |
| } else if (state == INIT_STATE.INIT_IN_DECLARATION) { |
| if (fieldElement.isFinal || fieldElement.isConst) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode |
| .FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR, |
| formalParameter.identifier, |
| [fieldElement.displayName]); |
| } |
| } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) { |
| if (fieldElement.isFinal || fieldElement.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES, |
| formalParameter.identifier, |
| [fieldElement.displayName]); |
| } |
| } |
| } |
| } |
| // Visit all of the initializers |
| NodeList<ConstructorInitializer> initializers = constructor.initializers; |
| for (ConstructorInitializer constructorInitializer in initializers) { |
| if (constructorInitializer is RedirectingConstructorInvocation) { |
| return; |
| } |
| if (constructorInitializer is ConstructorFieldInitializer) { |
| SimpleIdentifier fieldName = constructorInitializer.fieldName; |
| Element element = fieldName.staticElement; |
| if (element is FieldElement) { |
| INIT_STATE state = fieldElementsMap[element]; |
| if (state == INIT_STATE.NOT_INIT) { |
| fieldElementsMap[element] = INIT_STATE.INIT_IN_INITIALIZERS; |
| } else if (state == INIT_STATE.INIT_IN_DECLARATION) { |
| if (element.isFinal || element.isConst) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode |
| .FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION, |
| fieldName); |
| } |
| } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode |
| .FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER, |
| fieldName); |
| } else if (state == INIT_STATE.INIT_IN_INITIALIZERS) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS, |
| fieldName, |
| [element.displayName]); |
| } |
| } |
| } |
| } |
| // Prepare a list of not initialized fields. |
| List<FieldElement> notInitFinalFields = <FieldElement>[]; |
| fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) { |
| if (state == INIT_STATE.NOT_INIT) { |
| if (fieldElement.isFinal) { |
| notInitFinalFields.add(fieldElement); |
| } |
| } |
| }); |
| // Visit all of the states in the map to ensure that none were never |
| // initialized. |
| fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) { |
| if (state == INIT_STATE.NOT_INIT) { |
| if (fieldElement.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_NOT_INITIALIZED, |
| constructor.returnType, |
| [fieldElement.name]); |
| } |
| } |
| }); |
| |
| if (notInitFinalFields.isNotEmpty) { |
| List<String> names = notInitFinalFields.map((item) => item.name).toList(); |
| names.sort(); |
| if (names.length == 1) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1, |
| constructor.returnType, |
| names); |
| } else if (names.length == 2) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2, |
| constructor.returnType, |
| names); |
| } else { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS, |
| constructor.returnType, |
| [names[0], names[1], names.length - 2]); |
| } |
| } |
| } |
| |
| /** |
| * Verify that all classes of the given [withClause] are valid. |
| * |
| * See [CompileTimeErrorCode.MIXIN_CLASS_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; |
| int mixinTypeIndex = -1; |
| for (int mixinNameIndex = 0; |
| mixinNameIndex < withClause.mixinTypes.length; |
| mixinNameIndex++) { |
| TypeName mixinName = withClause.mixinTypes[mixinNameIndex]; |
| DartType mixinType = mixinName.type; |
| if (mixinType is InterfaceType) { |
| mixinTypeIndex++; |
| if (_checkForExtendsOrImplementsDisallowedClass( |
| mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) { |
| problemReported = true; |
| } else { |
| ClassElement mixinElement = mixinType.element; |
| if (_checkForExtendsOrImplementsDeferredClass( |
| mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) { |
| problemReported = true; |
| } |
| if (mixinElement.isMixin) { |
| if (_checkForMixinSuperclassConstraints( |
| mixinNameIndex, mixinName)) { |
| problemReported = true; |
| } else if (_checkForMixinSuperInvokedMembers( |
| mixinTypeIndex, mixinName, mixinElement, mixinType)) { |
| problemReported = true; |
| } |
| } else { |
| if (_checkForMixinClassDeclaresConstructor( |
| mixinName, mixinElement)) { |
| problemReported = true; |
| } |
| if (_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) { |
| problemReported = true; |
| } |
| if (_checkForMixinReferencesSuper(mixinName, mixinElement)) { |
| problemReported = true; |
| } |
| } |
| } |
| } |
| } |
| return problemReported; |
| } |
| |
| /** |
| * Check for errors related to the redirected constructors. |
| * |
| * See [StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE], |
| * [StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE], and |
| * [StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR]. |
| */ |
| void _checkForAllRedirectConstructorErrorCodes( |
| ConstructorDeclaration declaration) { |
| // Prepare redirected constructor node |
| ConstructorName redirectedConstructor = declaration.redirectedConstructor; |
| if (redirectedConstructor == null) { |
| return; |
| } |
| |
| // Prepare redirected constructor type |
| ConstructorElement redirectedElement = redirectedConstructor.staticElement; |
| if (redirectedElement == null) { |
| // If the element is null, we check for the |
| // REDIRECT_TO_MISSING_CONSTRUCTOR case |
| TypeName constructorTypeName = redirectedConstructor.type; |
| DartType redirectedType = constructorTypeName.type; |
| if (redirectedType != null && |
| redirectedType.element != null && |
| !redirectedType.isDynamic) { |
| // Prepare the constructor name |
| String constructorStrName = constructorTypeName.name.name; |
| if (redirectedConstructor.name != null) { |
| constructorStrName += ".${redirectedConstructor.name.name}"; |
| } |
| ErrorCode errorCode = (declaration.constKeyword != null |
| ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR |
| : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR); |
| _errorReporter.reportErrorForNode(errorCode, redirectedConstructor, |
| [constructorStrName, redirectedType.displayName]); |
| } |
| return; |
| } |
| FunctionType redirectedType = redirectedElement.type; |
| DartType redirectedReturnType = redirectedType.returnType; |
| |
| // Report specific problem when return type is incompatible |
| FunctionType constructorType = |
| resolutionMap.elementDeclaredByConstructorDeclaration(declaration).type; |
| DartType constructorReturnType = constructorType.returnType; |
| if (!_typeSystem.isAssignableTo( |
| redirectedReturnType, constructorReturnType)) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE, |
| redirectedConstructor, |
| [redirectedReturnType, constructorReturnType]); |
| return; |
| } else if (!_typeSystem.isSubtypeOf(redirectedType, constructorType)) { |
| // Check parameters. |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, |
| redirectedConstructor, |
| [redirectedType, constructorType]); |
| } |
| } |
| |
| /** |
| * Check that the return [statement] of the form <i>return e;</i> is not in a |
| * generative constructor. |
| * |
| * Check that return statements without expressions are not in a generative |
| * constructor and the return type is not assignable to `null`; that is, we |
| * don't have `return;` if the enclosing method has a non-void containing |
| * return type. |
| * |
| * Check that the return type matches the type of the declared return type in |
| * the enclosing method or function. |
| * |
| * See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR], |
| * [StaticWarningCode.RETURN_WITHOUT_VALUE], and |
| * [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE]. |
| */ |
| void _checkForAllReturnStatementErrorCodes(ReturnStatement statement) { |
| FunctionType functionType = _enclosingFunction?.type; |
| DartType expectedReturnType = functionType == null |
| ? DynamicTypeImpl.instance |
| : functionType.returnType; |
| Expression returnExpression = statement.expression; |
| |
| // RETURN_IN_GENERATIVE_CONSTRUCTOR |
| bool isGenerativeConstructor(ExecutableElement element) => |
| element is ConstructorElement && !element.isFactory; |
| if (isGenerativeConstructor(_enclosingFunction)) { |
| if (returnExpression == null) { |
| return; |
| } |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, |
| returnExpression); |
| return; |
| } |
| // RETURN_WITHOUT_VALUE |
| if (returnExpression == null) { |
| _checkForAllEmptyReturnStatementErrorCodes(statement, expectedReturnType); |
| return; |
| } else if (_inGenerator) { |
| // RETURN_IN_GENERATOR |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.RETURN_IN_GENERATOR, |
| statement, |
| [_inAsync ? "async*" : "sync*"]); |
| return; |
| } |
| |
| _checkForReturnOfInvalidType(returnExpression, expectedReturnType); |
| } |
| |
| /** |
| * Verify that the export namespace of the given export [directive] does not |
| * export any name already exported by another export directive. The |
| * [exportElement] is the [ExportElement] retrieved from the node. If the |
| * element in the node was `null`, then this method is not called. The |
| * [exportedLibrary] is the library element containing the exported element. |
| * |
| * See [CompileTimeErrorCode.AMBIGUOUS_EXPORT]. |
| */ |
| void _checkForAmbiguousExport(ExportDirective directive, |
| ExportElement exportElement, LibraryElement exportedLibrary) { |
| if (exportedLibrary == null) { |
| return; |
| } |
| // check exported names |
| Namespace namespace = |
| new NamespaceBuilder().createExportNamespaceForDirective(exportElement); |
| Map<String, Element> definedNames = namespace.definedNames; |
| for (String name in definedNames.keys) { |
| Element element = definedNames[name]; |
| Element prevElement = _exportedElements[name]; |
| if (element != null && prevElement != null && prevElement != element) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.AMBIGUOUS_EXPORT, directive, [ |
| name, |
| prevElement.library.definingCompilationUnit.source.uri, |
| element.library.definingCompilationUnit.source.uri |
| ]); |
| return; |
| } else { |
| _exportedElements[name] = element; |
| } |
| } |
| } |
| |
| /** |
| * Check the given node to see whether it was ambiguous because the name was |
| * imported from two or more imports. |
| */ |
| void _checkForAmbiguousImport(SimpleIdentifier node) { |
| Element element = node.staticElement; |
| if (element is MultiplyDefinedElementImpl) { |
| String name = element.displayName; |
| List<Element> conflictingMembers = element.conflictingElements; |
| int count = conflictingMembers.length; |
| List<String> libraryNames = new List<String>(count); |
| for (int i = 0; i < count; i++) { |
| libraryNames[i] = _getLibraryName(conflictingMembers[i]); |
| } |
| libraryNames.sort(); |
| _errorReporter.reportErrorForNode(StaticWarningCode.AMBIGUOUS_IMPORT, |
| node, [name, StringUtilities.printListOfQuotedNames(libraryNames)]); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| void _checkForArgumentTypeNotAssignable( |
| Expression expression, |
| DartType expectedStaticType, |
| DartType actualStaticType, |
| ErrorCode errorCode) { |
| // Warning case: test static type information |
| if (actualStaticType != null && expectedStaticType != null) { |
| if (!expectedStaticType.isVoid && _checkForUseOfVoidResult(expression)) { |
| return; |
| } |
| |
| _checkForAssignableExpressionAtType( |
| expression, actualStaticType, expectedStaticType, errorCode); |
| } |
| } |
| |
| /** |
| * Verify that the given [argument] can be assigned to its corresponding |
| * parameter. |
| * |
| * This method corresponds to |
| * [BestPracticesVerifier.checkForArgumentTypeNotAssignableForArgument]. |
| * |
| * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| */ |
| void _checkForArgumentTypeNotAssignableForArgument(Expression argument) { |
| if (argument == null) { |
| return; |
| } |
| |
| ParameterElement staticParameterElement = argument.staticParameterElement; |
| DartType staticParameterType = staticParameterElement?.type; |
| _checkForArgumentTypeNotAssignableWithExpectedTypes(argument, |
| staticParameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
| } |
| |
| /** |
| * Verify that the given [expression] can be assigned to its corresponding |
| * parameters. The [expectedStaticType] is the expected static type. |
| * |
| * This method corresponds to |
| * [BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes]. |
| * |
| * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE], |
| * [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], |
| * [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], |
| * [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], |
| * [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE], |
| * [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and |
| * [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]. |
| */ |
| void _checkForArgumentTypeNotAssignableWithExpectedTypes( |
| Expression expression, DartType expectedStaticType, ErrorCode errorCode) { |
| _checkForArgumentTypeNotAssignable( |
| expression, expectedStaticType, getStaticType(expression), errorCode); |
| } |
| |
| /** |
| * Verify that the arguments in the given [argumentList] can be assigned to |
| * their corresponding parameters. |
| * |
| * This method corresponds to |
| * [BestPracticesVerifier.checkForArgumentTypesNotAssignableInList]. |
| * |
| * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| */ |
| void _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { |
| if (argumentList == null) { |
| return; |
| } |
| |
| for (Expression argument in argumentList.arguments) { |
| _checkForArgumentTypeNotAssignableForArgument(argument); |
| } |
| } |
| |
| /** |
| * Check that the static type of the given expression is assignable to the |
| * given type. If it isn't, report an error with the given error code. The |
| * [type] is the type that the expression must be assignable to. The |
| * [errorCode] is the error code to be reported. The [arguments] are the |
| * arguments to pass in when creating the error. |
| */ |
| void _checkForAssignability(Expression expression, InterfaceType type, |
| ErrorCode errorCode, List<Object> arguments) { |
| if (expression == null) { |
| return; |
| } |
| DartType expressionType = expression.staticType; |
| if (expressionType == null) { |
| return; |
| } |
| if (_typeSystem.isAssignableTo(expressionType, type)) { |
| return; |
| } |
| _errorReporter.reportErrorForNode(errorCode, expression, arguments); |
| } |
| |
| bool _checkForAssignableExpression( |
| Expression expression, DartType expectedStaticType, ErrorCode errorCode) { |
| DartType actualStaticType = getStaticType(expression); |
| return actualStaticType != null && |
| _checkForAssignableExpressionAtType( |
| expression, actualStaticType, expectedStaticType, errorCode); |
| } |
| |
| bool _checkForAssignableExpressionAtType( |
| Expression expression, |
| DartType actualStaticType, |
| DartType expectedStaticType, |
| ErrorCode errorCode) { |
| if (!_typeSystem.isAssignableTo(actualStaticType, expectedStaticType)) { |
| _errorReporter.reportTypeErrorForNode( |
| errorCode, expression, [actualStaticType, expectedStaticType]); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Verify that the given [expression] is not final. |
| * |
| * See [StaticWarningCode.ASSIGNMENT_TO_CONST], |
| * [StaticWarningCode.ASSIGNMENT_TO_FINAL], and |
| * [StaticWarningCode.ASSIGNMENT_TO_METHOD]. |
| */ |
| void _checkForAssignmentToFinal(Expression expression) { |
| // prepare element |
| Element element = null; |
| AstNode highlightedNode = expression; |
| if (expression is Identifier) { |
| element = expression.staticElement; |
| if (expression is PrefixedIdentifier) { |
| highlightedNode = expression.identifier; |
| } |
| } else if (expression is PropertyAccess) { |
| element = expression.propertyName.staticElement; |
| highlightedNode = expression.propertyName; |
| } |
| // check if element is assignable |
| Element toVariable(Element element) { |
| return element is PropertyAccessorElement ? element.variable : element; |
| } |
| |
| element = toVariable(element); |
| if (element is VariableElement) { |
| if (element.isConst) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_CONST, expression); |
| } else if (element.isFinal) { |
| if (element is FieldElementImpl) { |
| if (element.setter == null && element.isSynthetic) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_FINAL_NO_SETTER, |
| highlightedNode, |
| [element.name, element.enclosingElement.displayName]); |
| } else { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_FINAL, |
| highlightedNode, |
| [element.name]); |
| } |
| return; |
| } |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_FINAL_LOCAL, |
| highlightedNode, |
| [element.name]); |
| } |
| } else if (element is FunctionElement) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_FUNCTION, expression); |
| } else if (element is MethodElement) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_METHOD, expression); |
| } else if (element is ClassElement || |
| element is FunctionTypeAliasElement || |
| element is TypeParameterElement) { |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.ASSIGNMENT_TO_TYPE, expression); |
| } |
| } |
| |
| /** |
| * Verifies that the class is not named `Function` and that it doesn't |
| * extends/implements/mixes in `Function`. |
| */ |
| void _checkForBadFunctionUse(ClassDeclaration node) { |
| ExtendsClause extendsClause = node.extendsClause; |
| WithClause withClause = node.withClause; |
| |
| if (node.name.name == "Function") { |
| _errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION, node.name); |
| } |
| |
| if (extendsClause != null) { |
| InterfaceType superclassType = _enclosingClass.supertype; |
| ClassElement superclassElement = superclassType?.element; |
| if (superclassElement != null && superclassElement.name == "Function") { |
| _errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_EXTENDS_FUNCTION, extendsClause.superclass); |
| } |
| } |
| |
| if (withClause != null) { |
| for (TypeName type in withClause.mixinTypes) { |
| Element mixinElement = type.name.staticElement; |
| if (mixinElement != null && mixinElement.name == "Function") { |
| _errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_MIXIN_FUNCTION, type); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Verify that the given [identifier] is not a keyword, and generates the |
| * given [errorCode] on the identifier if it is a keyword. |
| * |
| * See [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME], |
| * [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME], and |
| * [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME]. |
| */ |
| void _checkForBuiltInIdentifierAsName( |
| SimpleIdentifier identifier, ErrorCode errorCode) { |
| Token token = identifier.token; |
| if (token.type.isKeyword && token.keyword?.isPseudo != true) { |
| _errorReporter |
| .reportErrorForNode(errorCode, identifier, [identifier.name]); |
| } |
| } |
| |
| /** |
| * Verify that the given [switchCase] is terminated with 'break', 'continue', |
| * 'return' or 'throw'. |
| * |
| * see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
| */ |
| void _checkForCaseBlockNotTerminated(SwitchCase switchCase) { |
| NodeList<Statement> statements = switchCase.statements; |
| if (statements.isEmpty) { |
| // fall-through without statements at all |
| AstNode parent = switchCase.parent; |
| if (parent is SwitchStatement) { |
| NodeList<SwitchMember> members = parent.members; |
| int index = members.indexOf(switchCase); |
| if (index != -1 && index < members.length - 1) { |
| return; |
| } |
| } |
| // no other switch member after this one |
| } else { |
| Statement statement = statements.last; |
| if (statement is Block && statement.statements.isNotEmpty) { |
| Block block = statement; |
| statement = block.statements.last; |
| } |
| // terminated with statement |
| if (statement is BreakStatement || |
| statement is ContinueStatement || |
| statement is ReturnStatement) { |
| return; |
| } |
| // terminated with 'throw' expression |
| if (statement is ExpressionStatement) { |
| Expression expression = statement.expression; |
| if (expression is ThrowExpression || expression is RethrowExpression) { |
| return; |
| } |
| } |
| } |
| |
| _errorReporter.reportErrorForToken( |
| StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword); |
| } |
| |
| /** |
| * Verify that the switch cases in the given switch [statement] are terminated |
| * with 'break', 'continue', 'rethrow', 'return' or 'throw'. |
| * |
| * See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
| */ |
| void _checkForCaseBlocksNotTerminated(SwitchStatement statement) { |
| NodeList<SwitchMember> members = statement.members; |
| int lastMember = members.length - 1; |
| for (int i = 0; i < lastMember; i++) { |
| SwitchMember member = members[i]; |
| if (member is SwitchCase) { |
| _checkForCaseBlockNotTerminated(member); |
| } |
| } |
| } |
| |
| /** |
| * Verify that the [_enclosingClass] does not have a method and getter pair |
| * with the same name on, via inheritance. |
| * |
| * See [CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE], |
| * [CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD], and |
| * [CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD]. |
| */ |
| void _checkForConflictingClassMembers() { |
| if (_enclosingClass == null) { |
| return; |
| } |
| InterfaceType enclosingType = _enclosingClass.type; |
| Uri libraryUri = _currentLibrary.source.uri; |
| |
| // method declared in the enclosing class vs. inherited getter/setter |
| for (MethodElement method in _enclosingClass.methods) { |
| String name = method.name; |
| |
| // find inherited property accessor |
| ExecutableElement inherited = _inheritanceManager |
| .getInherited(enclosingType, new Name(libraryUri, name)) |
| ?.element; |
| inherited ??= _inheritanceManager |
| .getInherited(enclosingType, new Name(libraryUri, '$name=')) |
| ?.element; |
| |
| if (method.isStatic && inherited != null) { |
| _errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, method, [ |
| _enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ]); |
| } else if (inherited is PropertyAccessorElement) { |
| _errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD, method, [ |
| _enclosingClass.displayName, |
| inherited.enclosingElement.displayName, |
| name |
| ]); |
| } |
| } |
| |
| // getter declared in the enclosing class vs. inherited method |
| for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
| String name = accessor.displayName; |
| |
| // find inherited method or property accessor |
| ExecutableElement inherited = _inheritanceManager |
| .getInherited(enclosingType, new Name(libraryUri, name)) |
| ?.element; |
| inherited ??= _inheritanceManager |
| .getInherited(enclosingType, new Name(libraryUri, '$name=')) |
| ?.element; |
| |
| if (accessor.isStatic && inherited != null) { |
| _errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, accessor, [ |
| _enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ]); |
| } else if (inherited is MethodElement) { |
| _errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, accessor, [ |
| _enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName |
| ]); |
| } |
| } |
| } |
| |
| void _checkForConflictingGenerics(NamedCompilationUnitMember node) { |
| var visitedClasses = <ClassElement>[]; |
| var interfaces = <ClassElement, InterfaceType>{}; |
| void visit(InterfaceType type) { |
| if (type == null) return; |
| var element = type.element; |
| if (visitedClasses.contains(element)) return; |
| visitedClasses.add(element); |
| if (element.typeParameters.isNotEmpty) { |
| var oldType = interfaces[element]; |
| if (oldType == null) { |
| interfaces[element] = type; |
| } else if (!oldType.isEquivalentTo(type)) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES, |
| node, |
| [_enclosingClass.name, oldType, type]); |
| } |
| } |
| visit(type.superclass); |
| type.mixins.forEach(visit); |
| type.superclassConstraints.forEach(visit); |
| type.interfaces.forEach(visit); |
| visitedClasses.removeLast(); |
| } |
| |
| visit(_enclosingClass.type); |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConflictingTypeVariableErrorCodes() { |
| 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.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS, |
| typeParameter, |
| [name]); |
| } |
| // check members |
| if (_enclosingClass.getMethod(name) != null || |
| _enclosingClass.getGetter(name) != null || |
| _enclosingClass.getSetter(name) != null) { |
| _errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER, |
| typeParameter, |
| [name]); |
| } |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstConstructorWithNonConstSuper( |
| ConstructorDeclaration constructor) { |
| if (!_isEnclosingConstructorConst) { |
| return; |
| } |
| // OK, const factory, checked elsewhere |
| if (constructor.factoryKeyword != null) { |
| return; |
| } |
| |
| // check for mixins |
| var hasInstanceField = false; |
| for (var mixin in _enclosingClass.mixins) { |
| var fields = mixin.element.fields; |
| for (var i = 0; i < fields.length; ++i) { |
| if (!fields[i].isStatic) { |
| hasInstanceField = true; |
| break; |
| } |
| } |
| } |
| if (hasInstanceField) { |
| // TODO(scheglov) Provide the list of fields. |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD, |
| constructor.returnType); |
| return; |
| } |
| |
| // try to find and check super constructor invocation |
| for (ConstructorInitializer initializer in constructor.initializers) { |
| if (initializer is SuperConstructorInvocation) { |
| ConstructorElement element = initializer.staticElement; |
| if (element == null || element.isConst) { |
| return; |
| } |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
| initializer, |
| [element.enclosingElement.displayName]); |
| return; |
| } |
| } |
| // no explicit super constructor invocation, check default constructor |
| InterfaceType supertype = _enclosingClass.supertype; |
| if (supertype == null) { |
| return; |
| } |
| if (supertype.isObject) { |
| return; |
| } |
| ConstructorElement unnamedConstructor = |
| supertype.element.unnamedConstructor; |
| if (unnamedConstructor == null || unnamedConstructor.isConst) { |
| return; |
| } |
| |
| // default constructor is not 'const', report problem |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
| constructor.returnType, |
| [supertype.displayName]); |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstConstructorWithNonFinalField( |
| ConstructorDeclaration constructor, |
| ConstructorElement constructorElement) { |
| if (!_isEnclosingConstructorConst) { |
| return; |
| } |
| // check if there is non-final field |
| ClassElement classElement = constructorElement.enclosingElement; |
| if (!classElement.hasNonFinalField) { |
| return; |
| } |
| |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, |
| constructor); |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstDeferredClass(InstanceCreationExpression expression, |
| ConstructorName constructorName, TypeName typeName) { |
| if (typeName.isDeferred) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_DEFERRED_CLASS, |
| constructorName, |
| [typeName.name.name]); |
| } |
| } |
| |
| /** |
| * Verify that the given throw [expression] is not enclosed in a 'const' |
| * constructor declaration. |
| * |
| * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION]. |
| */ |
| void _checkForConstEvalThrowsException(ThrowExpression expression) { |
| if (_isEnclosingConstructorConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, expression); |
| } |
| } |
| |
| /** |
| * Verify that the given normal formal [parameter] is not 'const'. |
| * |
| * See [CompileTimeErrorCode.CONST_FORMAL_PARAMETER]. |
| */ |
| void _checkForConstFormalParameter(NormalFormalParameter parameter) { |
| if (parameter.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_FORMAL_PARAMETER, parameter); |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstOrNewWithAbstractClass( |
| InstanceCreationExpression expression, |
| TypeName typeName, |
| InterfaceType type) { |
| if (type.element.isAbstract && !type.element.isMixin) { |
| ConstructorElement element = expression.staticElement; |
| if (element != null && !element.isFactory) { |
| bool isImplicit = |
| (expression as InstanceCreationExpressionImpl).isImplicit; |
| if (!isImplicit) { |
| _errorReporter.reportErrorForNode( |
| expression.isConst |
| ? StaticWarningCode.CONST_WITH_ABSTRACT_CLASS |
| : StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, |
| typeName); |
| } else { |
| // TODO(brianwilkerson/jwren) Create a new different StaticWarningCode |
| // which does not call out the new keyword so explicitly. |
| _errorReporter.reportErrorForNode( |
| StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, typeName); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstOrNewWithEnum(InstanceCreationExpression expression, |
| TypeName typeName, InterfaceType type) { |
| if (type.element.isEnum) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANTIATE_ENUM, typeName); |
| } |
| } |
| |
| /** |
| * Verify that the given [expression] is not a mixin instantiation. |
| */ |
| void _checkForConstOrNewWithMixin(InstanceCreationExpression expression, |
| TypeName typeName, InterfaceType type) { |
| if (type.element.isMixin) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MIXIN_INSTANTIATE, typeName); |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstWithNonConst(InstanceCreationExpression expression) { |
| ConstructorElement constructorElement = expression.staticElement; |
| if (constructorElement != null && !constructorElement.isConst) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_WITH_NON_CONST, expression); |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForConstWithUndefinedConstructor( |
| InstanceCreationExpression expression, |
| ConstructorName constructorName, |
| TypeName typeName) { |
| // OK if resolved |
| if (expression.staticElement != null) { |
| return; |
| } |
| DartType type = typeName.type; |
| if (type is InterfaceType) { |
| ClassElement element = type.element; |
| if (element != null && element.isEnum) { |
| // We have already reported the error. |
| return; |
| } |
| } |
| 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]); |
| } |
| } |
| |
| /** |
| * Verify that there are no default parameters in the given function type |
| * [alias]. |
| * |
| * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS]. |
| */ |
| void _checkForDefaultValueInFunctionTypeAlias(FunctionTypeAlias alias) { |
| FormalParameterList formalParameterList = alias.parameters; |
| NodeList<FormalParameter> parameters = formalParameterList.parameters; |
| for (FormalParameter parameter in parameters) { |
| if (parameter is DefaultFormalParameter) { |
| if (parameter.defaultValue != null) { |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS, alias); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Verify that the given default formal [parameter] is not part of a function |
| * typed parameter. |
| * |
| * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER]. |
| */ |
| void _checkForDefaultValueInFunctionTypedParameter( |
| DefaultFormalParameter parameter) { |
| // OK, not in a function typed parameter. |
| if (!_isInFunctionTypedFormalParameter) { |
| return; |
| } |
| // OK, no default value. |
| if (parameter.defaultValue == null) { |
| return; |
| } |
| |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER, |
| parameter); |
| } |
| |
| /** |
| * Verify that any deferred imports in the given compilation [unit] have a |
| * unique prefix. |
| * |
| * See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX]. |
| */ |
| void _checkForDeferredPrefixCollisions(CompilationUnit unit) { |
| NodeList<Directive> directives = unit.directives; |
| int count = directives.length; |
| if (count > 0) { |
| Map<PrefixElement, List<ImportDirective>> prefixToDirectivesMap = |
| new HashMap<PrefixElement, List<ImportDirective>>(); |
| for (int i = 0; i < count; i++) { |
| Directive directive = directives[i]; |
| if (directive is ImportDirective) { |
| SimpleIdentifier prefix = directive.prefix; |
| if (prefix != null) { |
| Element element = prefix.staticElement; |
| if (element is PrefixElement) { |
| List<ImportDirective> elements = prefixToDirectivesMap[element]; |
| if (elements == null) { |
| elements = new List<ImportDirective>(); |
| prefixToDirectivesMap[element] = elements; |
| } |
| elements.add(directive); |
| } |
| } |
| } |
| } |
| for (List<ImportDirective> imports in prefixToDirectivesMap.values) { |
| _checkDeferredPrefixCollision(imports); |
| } |
| } |
| } |
| |
| /** |
| * Return `true` if the caller should continue checking the rest of the |
| * information in the for-each part. |
| */ |
| bool _checkForEachParts(ForEachParts node, SimpleIdentifier variable) { |
| if (_checkForNullableDereference(node.iterable)) { |
| return false; |
| } |
| |
| if (_checkForUseOfVoidResult(node.iterable)) { |
| return false; |
| } |
| |
| DartType iterableType = getStaticType(node.iterable); |
| if (iterableType.isDynamic) { |
| return false; |
| } |
| |
| // The type of the loop variable. |
| DartType variableType = getStaticType(variable); |
| |
| AstNode parent = node.parent; |
| Token awaitKeyword; |
| if (parent is ForStatement) { |
| awaitKeyword = parent.awaitKeyword; |
| } else if (parent is ForElement) { |
| awaitKeyword = parent.awaitKeyword; |
| } |
| DartType loopType = awaitKeyword != null |
| ? _typeProvider.streamType |
| : _typeProvider.iterableType; |
| |
| // Use an explicit string instead of [loopType] to remove the "<E>". |
| String loopTypeName = awaitKeyword != null ? "Stream" : "Iterable"; |
| |
| // The object being iterated has to implement Iterable<T> for some T that |
| // is assignable to the variable's type. |
| // TODO(rnystrom): Move this into mostSpecificTypeArgument()? |
| iterableType = iterableType.resolveToBound(_typeProvider.objectType); |
| DartType bestIterableType = |
| _typeSystem.mostSpecificTypeArgument(iterableType, loopType); |
| |
| // Allow it to be a supertype of Iterable<T> (basically just Object) and do |
| // an implicit downcast to Iterable<dynamic>. |
| if (bestIterableType == null) { |
| if (_typeSystem.isSubtypeOf(loopType, iterableType)) { |
| bestIterableType = DynamicTypeImpl.instance; |
| } |
| } |
| |
| if (bestIterableType == null) { |
| _errorReporter.reportTypeErrorForNode( |
| StaticTypeWarningCode.FOR_IN_OF_INVALID_TYPE, |
| node.iterable, |
| [iterableType, loopTypeName]); |
| } else if (!_typeSystem.isAssignableTo(bestIterableType, variableType)) { |
| _errorReporter.reportTypeErrorForNode( |
| StaticTypeWarningCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, |
| node.iterable, |
| [iterableType, loopTypeName, variableType]); |
| } |
| 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]. |
| */ |
| void _checkForExportDuplicateLibraryName(ExportDirective directive, |
| ExportElement exportElement, LibraryElement exportedLibrary) { |
| if (exportedLibrary == null) { |
| return; |
| } |
| 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.source.uri.toString(), |
| exportedLibrary.definingCompilationUnit.source.uri.toString(), |
| name |
| ]); |
| } |
| return; |
| } |
| } else { |
| _nameToExportElement[name] = exportedLibrary; |
| } |
| } |
| |
| /** |
| * 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]. |
| */ |
| void _checkForExportInternalLibrary( |
| ExportDirective directive, ExportElement exportElement) { |
| if (_isInSystemLibrary) { |
| return; |
| } |
| |
| LibraryElement exportedLibrary = exportElement.exportedLibrary; |
| if (exportedLibrary == null) { |
| return; |
| } |
| |
| // should be private |
| DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
| String uri = exportedLibrary.source.uri.toString(); |
| SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri); |
| if (sdkLibrary == null) { |
| return; |
| } |
| if (!sdkLibrary.isInternal) { |
| return; |
| } |
| |
| _errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, |
| directive, |
| [directive.uri]); |
| } |
| |
| /** |
| * Verify that the given extends [clause] does not extend a deferred class. |
| * |
| * See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS]. |
| */ |
| void _checkForExtendsDeferredClass(TypeName superclass) { |
| if (superclass == null) { |
| return; |
| } |
| _checkForExtendsOrImplementsDeferredClass( |
| superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS); |
| } |
| |
| /** |
| * Verify that the given extends [clause] does not extend classes such as |
| |