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