| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:collection'; |
| |
| import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/syntactic_entity.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/type_provider.dart'; |
| import 'package:analyzer/diagnostic/diagnostic.dart'; |
| import 'package:analyzer/error/error.dart'; |
| import 'package:analyzer/error/listener.dart'; |
| import 'package:analyzer/src/dart/analysis/session.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/ast/extensions.dart'; |
| import 'package:analyzer/src/dart/element/class_hierarchy.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/dart/resolver/variance.dart'; |
| import 'package:analyzer/src/diagnostic/diagnostic_factory.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/error/constructor_fields_verifier.dart'; |
| import 'package:analyzer/src/error/correct_override.dart'; |
| import 'package:analyzer/src/error/duplicate_definition_verifier.dart'; |
| import 'package:analyzer/src/error/getter_setter_types_verifier.dart'; |
| import 'package:analyzer/src/error/literal_element_verifier.dart'; |
| import 'package:analyzer/src/error/required_parameters_verifier.dart'; |
| import 'package:analyzer/src/error/return_type_verifier.dart'; |
| import 'package:analyzer/src/error/type_arguments_verifier.dart'; |
| import 'package:analyzer/src/error/use_result_verifier.dart'; |
| import 'package:analyzer/src/generated/element_resolver.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/error_detection_helpers.dart'; |
| import 'package:analyzer/src/generated/java_engine.dart'; |
| import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode; |
| import 'package:analyzer/src/generated/this_access_tracker.dart'; |
| import 'package:collection/collection.dart'; |
| |
| class EnclosingExecutableContext { |
| final ExecutableElement? element; |
| final bool isAsynchronous; |
| final bool isConstConstructor; |
| final bool isGenerativeConstructor; |
| final bool isGenerator; |
| final bool inFactoryConstructor; |
| final bool inStaticMethod; |
| |
| /// If this [EnclosingExecutableContext] is the first argument in a method |
| /// invocation of [Future.catchError], returns the return type expected for |
| /// `Future<T>.catchError`'s `onError` parameter, which is `FutureOr<T>`, |
| /// otherwise `null`. |
| final InterfaceType? catchErrorOnErrorReturnType; |
| |
| /// The return statements that have a value. |
| final List<ReturnStatement> _returnsWith = []; |
| |
| /// The return statements that do not have a value. |
| final List<ReturnStatement> _returnsWithout = []; |
| |
| /// This flag is set to `false` when the declared return type is not legal |
| /// for the kind of the function body, e.g. not `Future` for `async`. |
| bool hasLegalReturnType = true; |
| |
| EnclosingExecutableContext(this.element, |
| {bool? isAsynchronous, this.catchErrorOnErrorReturnType}) |
| : isAsynchronous = |
| isAsynchronous ?? (element != null && element.isAsynchronous), |
| isConstConstructor = element is ConstructorElement && element.isConst, |
| isGenerativeConstructor = |
| element is ConstructorElement && !element.isFactory, |
| isGenerator = element != null && element.isGenerator, |
| inFactoryConstructor = _inFactoryConstructor(element), |
| inStaticMethod = _inStaticMethod(element); |
| |
| EnclosingExecutableContext.empty() : this(null); |
| |
| String? get displayName { |
| final element = this.element; |
| if (element is ConstructorElement) { |
| var className = element.enclosingElement.displayName; |
| var constructorName = element.displayName; |
| return constructorName.isEmpty |
| ? className |
| : '$className.$constructorName'; |
| } else { |
| return element?.displayName; |
| } |
| } |
| |
| bool get isClosure { |
| return element is FunctionElement && element!.displayName.isEmpty; |
| } |
| |
| bool get isConstructor => element is ConstructorElement; |
| |
| bool get isFunction { |
| if (element is FunctionElement) { |
| return element!.displayName.isNotEmpty; |
| } |
| return element is PropertyAccessorElement; |
| } |
| |
| bool get isMethod => element is MethodElement; |
| |
| bool get isSynchronous => !isAsynchronous; |
| |
| DartType get returnType { |
| return catchErrorOnErrorReturnType ?? element!.returnType; |
| } |
| |
| static bool _inFactoryConstructor(Element? element) { |
| var enclosing = element?.enclosingElement; |
| if (enclosing == null) { |
| return false; |
| } |
| if (element is ConstructorElement) { |
| return element.isFactory; |
| } |
| return _inFactoryConstructor(enclosing); |
| } |
| |
| static bool _inStaticMethod(Element? element) { |
| var enclosing = element?.enclosingElement; |
| if (enclosing == null) { |
| return false; |
| } |
| if (enclosing is ClassElement || enclosing is ExtensionElement) { |
| if (element is ExecutableElement) { |
| return element.isStatic; |
| } |
| } |
| return _inStaticMethod(enclosing); |
| } |
| } |
| |
| /// A visitor used to traverse an AST structure looking for additional errors |
| /// and warnings not covered by the parser and resolver. |
| class ErrorVerifier extends RecursiveAstVisitor<void> |
| with ErrorDetectionHelpers { |
| /// The error reporter by which errors will be reported. |
| @override |
| final ErrorReporter errorReporter; |
| |
| /// The current library that is being analyzed. |
| final LibraryElementImpl _currentLibrary; |
| |
| /// The type representing the type 'int'. |
| late final InterfaceType _intType; |
| |
| /// The options for verification. |
| late final AnalysisOptionsImpl _options; |
| |
| /// The object providing access to the types defined by the language. |
| final TypeProvider _typeProvider; |
| |
| /// The type system primitives |
| @override |
| late final TypeSystemImpl typeSystem; |
| |
| /// The manager for the inheritance mappings. |
| final InheritanceManager3 _inheritanceManager; |
| |
| /// 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; |
| |
| /// The stack of flags, where `true` at the top (last) of the stack indicates |
| /// that the visitor is in the initializer of a lazy local variable. When the |
| /// top is `false`, we might be not in a local variable, or it is not `lazy`, |
| /// etc. |
| final List<bool> _isInLateLocalVariable = [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, which is not `late`. |
| bool _isInInstanceNotLateVariableDeclaration = 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 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; |
| |
| /// The class containing the AST nodes being visited, or `null` if we are not |
| /// in the scope of a class. |
| ClassElementImpl? _enclosingClass; |
| |
| /// The enum containing the AST nodes being visited, or `null` if we are not |
| /// in the scope of an enum. |
| ClassElement? _enclosingEnum; |
| |
| /// The element of the extension being visited, or `null` if we are not |
| /// in the scope of an extension. |
| ExtensionElement? _enclosingExtension; |
| |
| /// The helper for tracking if the current location has access to `this`. |
| final ThisAccessTracker _thisAccessTracker = ThisAccessTracker.unit(); |
| |
| /// The context of the method or function that we are currently visiting, or |
| /// `null` if we are not inside a method or function. |
| EnclosingExecutableContext _enclosingExecutable = |
| EnclosingExecutableContext.empty(); |
| |
| /// A table mapping names to the exported elements. |
| final Map<String, Element> _exportedElements = HashMap<String, Element>(); |
| |
| /// A set of the names of the variable initializers we are visiting now. |
| final HashSet<String> _namesForReferenceToDeclaredVariableInInitializer = |
| HashSet<String>(); |
| |
| /// The elements that will be defined later in the current scope, but right |
| /// now are not declared. |
| HiddenElements? _hiddenElements; |
| |
| final _UninstantiatedBoundChecker _uninstantiatedBoundChecker; |
| |
| /// The features enabled in the unit currently being checked for errors. |
| FeatureSet? _featureSet; |
| |
| final RequiredParametersVerifier _requiredParametersVerifier; |
| final DuplicateDefinitionVerifier _duplicateDefinitionVerifier; |
| final UseResultVerifier _checkUseVerifier; |
| late final TypeArgumentsVerifier _typeArgumentsVerifier; |
| late final ConstructorFieldsVerifier _constructorFieldsVerifier; |
| late final ReturnTypeVerifier _returnTypeVerifier; |
| |
| /// Initialize a newly created error verifier. |
| ErrorVerifier(this.errorReporter, this._currentLibrary, this._typeProvider, |
| this._inheritanceManager) |
| : _uninstantiatedBoundChecker = |
| _UninstantiatedBoundChecker(errorReporter), |
| _checkUseVerifier = UseResultVerifier(errorReporter), |
| _requiredParametersVerifier = RequiredParametersVerifier(errorReporter), |
| _duplicateDefinitionVerifier = |
| DuplicateDefinitionVerifier(_currentLibrary, errorReporter) { |
| _isInSystemLibrary = _currentLibrary.source.isInSystemLibrary; |
| _hasExtUri = _currentLibrary.hasExtUri; |
| _isInCatchClause = false; |
| _isInStaticVariableDeclaration = false; |
| _isInConstructorInitializer = false; |
| _intType = _typeProvider.intType; |
| typeSystem = _currentLibrary.typeSystem; |
| _options = _currentLibrary.context.analysisOptions as AnalysisOptionsImpl; |
| _typeArgumentsVerifier = |
| TypeArgumentsVerifier(_options, _currentLibrary, errorReporter); |
| _constructorFieldsVerifier = ConstructorFieldsVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ); |
| _returnTypeVerifier = ReturnTypeVerifier( |
| typeProvider: _typeProvider as TypeProviderImpl, |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ); |
| } |
| |
| ClassElement? get enclosingClass => _enclosingClass; |
| |
| /// For consumers of error verification as a library, (currently just the |
| /// angular plugin), expose a setter that can make the errors reported more |
| /// accurate when dangling code snippets are being resolved from a class |
| /// context. Note that this setter is very defensive for potential misuse; it |
| /// should not be modified in the middle of visiting a tree and requires an |
| /// analyzer-provided Impl instance to work. |
| set enclosingClass(ClassElement? classElement) { |
| assert(classElement is ClassElementImpl); |
| assert(_enclosingClass == null); |
| assert(_enclosingEnum == null); |
| assert(_enclosingExecutable.element == null); |
| _enclosingClass = classElement as ClassElementImpl; |
| } |
| |
| /// The language team is thinking about adding abstract fields, or external |
| /// fields. But for now we will ignore such fields in `Struct` subtypes. |
| bool get _isEnclosingClassFfiStruct { |
| var superClass = _enclosingClass?.supertype?.element; |
| return superClass != null && |
| superClass.library.name == 'dart.ffi' && |
| superClass.name == 'Struct'; |
| } |
| |
| bool get _isNonNullableByDefault => |
| _featureSet?.isEnabled(Feature.non_nullable) ?? false; |
| |
| @override |
| List<DiagnosticMessage> computeWhyNotPromotedMessages( |
| SyntacticEntity errorEntity, |
| Map<DartType, NonPromotionReason>? whyNotPromoted) { |
| return []; |
| } |
| |
| @override |
| void visitAnnotation(Annotation node) { |
| _checkForInvalidAnnotationFromDeferredLibrary(node); |
| _checkForMissingJSLibAnnotation(node); |
| super.visitAnnotation(node); |
| } |
| |
| @override |
| void visitAsExpression(AsExpression node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| super.visitAsExpression(node); |
| } |
| |
| @override |
| void visitAssertInitializer(AssertInitializer node) { |
| _isInConstructorInitializer = true; |
| try { |
| super.visitAssertInitializer(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitAssignmentExpression(AssignmentExpression node) { |
| TokenType operatorType = node.operator.type; |
| Expression lhs = node.leftHandSide; |
| if (operatorType == TokenType.QUESTION_QUESTION_EQ) { |
| _checkForDeadNullCoalesce(node.readType as TypeImpl, node.rightHandSide); |
| } |
| _checkForAssignmentToFinal(lhs); |
| super.visitAssignmentExpression(node); |
| } |
| |
| @override |
| void visitAwaitExpression(AwaitExpression node) { |
| if (!_enclosingExecutable.isAsynchronous) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, node.awaitKeyword); |
| } |
| if (_isNonNullableByDefault) { |
| checkForUseOfVoidResult(node.expression); |
| } |
| _checkForAwaitInLateLocalVariableInitializer(node); |
| super.visitAwaitExpression(node); |
| } |
| |
| @override |
| void visitBinaryExpression(BinaryExpression node) { |
| Token operator = node.operator; |
| TokenType type = operator.type; |
| if (type == TokenType.AMPERSAND_AMPERSAND || type == TokenType.BAR_BAR) { |
| checkForUseOfVoidResult(node.rightOperand); |
| } else { |
| // Assignability checking is done by the resolver. |
| } |
| |
| if (type == TokenType.QUESTION_QUESTION) { |
| _checkForDeadNullCoalesce( |
| node.leftOperand.staticType as TypeImpl, node.rightOperand); |
| } |
| |
| checkForUseOfVoidResult(node.leftOperand); |
| |
| super.visitBinaryExpression(node); |
| } |
| |
| @override |
| void visitBlock(Block node) { |
| _withHiddenElements(node.statements, () { |
| _duplicateDefinitionVerifier.checkStatements(node.statements); |
| super.visitBlock(node); |
| }); |
| } |
| |
| @override |
| void visitBlockFunctionBody(BlockFunctionBody node) { |
| _thisAccessTracker.enterFunctionBody(node); |
| try { |
| super.visitBlockFunctionBody(node); |
| } finally { |
| _thisAccessTracker.exitFunctionBody(node); |
| } |
| } |
| |
| @override |
| void visitBreakStatement(BreakStatement node) { |
| var labelNode = node.label; |
| if (labelNode != null) { |
| var labelElement = labelNode.staticElement; |
| if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode); |
| } |
| } |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| _duplicateDefinitionVerifier.checkCatchClause(node); |
| bool previousIsInCatchClause = _isInCatchClause; |
| try { |
| _isInCatchClause = true; |
| _checkForTypeAnnotationDeferredClass(node.exceptionType); |
| super.visitCatchClause(node); |
| } finally { |
| _isInCatchClause = previousIsInCatchClause; |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| var outerClass = _enclosingClass; |
| try { |
| _isInNativeClass = node.nativeClause != null; |
| _enclosingClass = node.declaredElement as ClassElementImpl; |
| |
| List<ClassMember> members = node.members; |
| _duplicateDefinitionVerifier.checkClass(node); |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
| _checkForConflictingClassTypeVariableErrorCodes(); |
| var superclass = node.extendsClause?.superclass; |
| var implementsClause = node.implementsClause; |
| var withClause = node.withClause; |
| |
| // Only do error checks on the clause nodes if there is a non-null clause |
| if (implementsClause != null || |
| superclass != null || |
| withClause != null) { |
| _checkClassInheritance(node, superclass, withClause, implementsClause); |
| } |
| |
| _checkForConflictingClassMembers(); |
| _constructorFieldsVerifier.enterClass(node); |
| _checkForFinalNotInitializedInClass(members); |
| _checkForBadFunctionUse(node); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| _checkForMainFunction(node.name); |
| super.visitClassDeclaration(node); |
| } finally { |
| _isInNativeClass = false; |
| _constructorFieldsVerifier.leaveClass(); |
| _enclosingClass = outerClass; |
| } |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| var outerClassElement = _enclosingClass; |
| try { |
| _enclosingClass = node.declaredElement as ClassElementImpl; |
| _checkClassInheritance( |
| node, node.superclass, node.withClause, node.implementsClause); |
| _checkForMainFunction(node.name); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| } finally { |
| _enclosingClass = outerClassElement; |
| } |
| super.visitClassTypeAlias(node); |
| } |
| |
| @override |
| void visitComment(Comment node) { |
| _isInComment = true; |
| try { |
| super.visitComment(node); |
| } finally { |
| _isInComment = false; |
| } |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| _featureSet = node.featureSet; |
| _duplicateDefinitionVerifier.checkUnit(node); |
| _checkForDeferredPrefixCollisions(node); |
| super.visitCompilationUnit(node); |
| _featureSet = null; |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| var element = node.declaredElement!; |
| _withEnclosingExecutable(element, () { |
| _checkForInvalidModifierOnBody( |
| node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR); |
| _checkForConstConstructorWithNonFinalField(node, element); |
| _checkForConstConstructorWithNonConstSuper(node); |
| _constructorFieldsVerifier.verify(node); |
| _checkForRedirectingConstructorErrorCodes(node); |
| _checkForMultipleSuperInitializers(node); |
| _checkForRecursiveConstructorRedirect(node, element); |
| if (!_checkForRecursiveFactoryRedirect(node, element)) { |
| _checkForAllRedirectConstructorErrorCodes(node); |
| } |
| _checkForUndefinedConstructorInInitializerImplicit(node); |
| _checkForReturnInGenerativeConstructor(node); |
| super.visitConstructorDeclaration(node); |
| }); |
| } |
| |
| @override |
| void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| _isInConstructorInitializer = true; |
| try { |
| SimpleIdentifier fieldName = node.fieldName; |
| var staticElement = fieldName.staticElement; |
| _checkForInvalidField(node, fieldName, staticElement); |
| if (staticElement is FieldElement) { |
| _checkForAbstractOrExternalFieldConstructorInitializer( |
| node.fieldName, staticElement); |
| } |
| super.visitConstructorFieldInitializer(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitContinueStatement(ContinueStatement node) { |
| var labelNode = node.label; |
| if (labelNode != null) { |
| var labelElement = labelNode.staticElement; |
| if (labelElement is LabelElementImpl && |
| labelElement.isOnSwitchStatement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode); |
| } |
| } |
| } |
| |
| @override |
| void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| checkForInvalidAssignment(node.identifier, node.defaultValue); |
| super.visitDefaultFormalParameter(node); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| var outerEnum = _enclosingEnum; |
| try { |
| _enclosingEnum = node.declaredElement; |
| _duplicateDefinitionVerifier.checkEnum(node); |
| super.visitEnumDeclaration(node); |
| } finally { |
| _enclosingEnum = outerEnum; |
| } |
| } |
| |
| @override |
| void visitExportDirective(ExportDirective node) { |
| var exportElement = node.element; |
| if (exportElement != null) { |
| var exportedLibrary = exportElement.exportedLibrary; |
| _checkForAmbiguousExport(node, exportElement, exportedLibrary); |
| _checkForExportInternalLibrary(node, exportElement); |
| _checkForExportLegacySymbol(node); |
| } |
| super.visitExportDirective(node); |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| _thisAccessTracker.enterFunctionBody(node); |
| try { |
| _returnTypeVerifier.verifyExpressionFunctionBody(node); |
| super.visitExpressionFunctionBody(node); |
| } finally { |
| _thisAccessTracker.exitFunctionBody(node); |
| } |
| } |
| |
| @override |
| void visitExtensionDeclaration(ExtensionDeclaration node) { |
| _enclosingExtension = node.declaredElement; |
| _duplicateDefinitionVerifier.checkExtension(node); |
| _checkForConflictingExtensionTypeVariableErrorCodes(); |
| _checkForFinalNotInitializedInClass(node.members); |
| |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkExtension(node); |
| |
| final name = node.name; |
| if (name != null) { |
| _checkForBuiltInIdentifierAsName( |
| name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_NAME); |
| } |
| super.visitExtensionDeclaration(node); |
| _enclosingExtension = null; |
| } |
| |
| @override |
| void visitFieldDeclaration(FieldDeclaration node) { |
| var fields = node.fields; |
| _thisAccessTracker.enterFieldDeclaration(node); |
| _isInStaticVariableDeclaration = node.isStatic; |
| _isInInstanceNotLateVariableDeclaration = |
| !node.isStatic && !node.fields.isLate; |
| if (!_isInStaticVariableDeclaration) { |
| if (fields.isConst) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.CONST_INSTANCE_FIELD, fields.keyword!); |
| } |
| } |
| try { |
| _checkForNotInitializedNonNullableStaticField(node); |
| _checkForWrongTypeParameterVarianceInField(node); |
| _checkForLateFinalFieldWithConstConstructor(node); |
| super.visitFieldDeclaration(node); |
| } finally { |
| _isInStaticVariableDeclaration = false; |
| _isInInstanceNotLateVariableDeclaration = false; |
| _thisAccessTracker.exitFieldDeclaration(node); |
| } |
| } |
| |
| @override |
| void visitFieldFormalParameter(FieldFormalParameter node) { |
| _checkForValidField(node); |
| _checkForPrivateOptionalParameter(node); |
| _checkForFieldInitializingFormalRedirectingConstructor(node); |
| _checkForTypeAnnotationDeferredClass(node.type); |
| ParameterElement element = node.declaredElement!; |
| if (element is FieldFormalParameterElement) { |
| var fieldElement = element.field; |
| if (fieldElement != null) { |
| _checkForAbstractOrExternalFieldConstructorInitializer( |
| node.identifier, fieldElement); |
| } |
| } |
| super.visitFieldFormalParameter(node); |
| } |
| |
| @override |
| void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { |
| DeclaredIdentifier loopVariable = node.loopVariable; |
| if (_checkForEachParts(node, loopVariable.identifier)) { |
| if (loopVariable.isConst) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE, |
| loopVariable.keyword!); |
| } |
| } |
| super.visitForEachPartsWithDeclaration(node); |
| } |
| |
| @override |
| void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) { |
| SimpleIdentifier identifier = node.identifier; |
| if (_checkForEachParts(node, identifier)) { |
| _checkForAssignmentToFinal(identifier); |
| } |
| super.visitForEachPartsWithIdentifier(node); |
| } |
| |
| @override |
| void visitFormalParameterList(FormalParameterList node) { |
| _duplicateDefinitionVerifier.checkParameters(node); |
| _checkUseOfCovariantInParameters(node); |
| _checkUseOfDefaultValuesInParameters(node); |
| super.visitFormalParameterList(node); |
| } |
| |
| @override |
| void visitForPartsWithDeclarations(ForPartsWithDeclarations node) { |
| _duplicateDefinitionVerifier.checkForVariables(node.variables); |
| super.visitForPartsWithDeclarations(node); |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| ExecutableElement functionElement = node.declaredElement!; |
| if (functionElement.enclosingElement is! CompilationUnitElement) { |
| _hiddenElements!.declare(functionElement); |
| } |
| |
| _withEnclosingExecutable(functionElement, () { |
| SimpleIdentifier identifier = node.name; |
| TypeAnnotation? returnType = node.returnType; |
| if (node.isGetter) { |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkGetter( |
| node.name, node.declaredElement as PropertyAccessorElement); |
| } |
| if (node.isSetter) { |
| FunctionExpression functionExpression = node.functionExpression; |
| _checkForWrongNumberOfParametersForSetter( |
| identifier, functionExpression.parameters); |
| _checkForNonVoidReturnTypeForSetter(returnType); |
| } |
| _checkForTypeAnnotationDeferredClass(returnType); |
| _returnTypeVerifier.verifyReturnType(returnType); |
| _checkForImplicitDynamicReturn(node.name, node.declaredElement!); |
| _checkForMainFunction(node.name); |
| super.visitFunctionDeclaration(node); |
| }); |
| } |
| |
| @override |
| void visitFunctionExpression(FunctionExpression node) { |
| _isInLateLocalVariable.add(false); |
| |
| if (node.parent is FunctionDeclaration) { |
| super.visitFunctionExpression(node); |
| } else { |
| _withEnclosingExecutable(node.declaredElement!, () { |
| super.visitFunctionExpression(node); |
| }); |
| } |
| |
| _isInLateLocalVariable.removeLast(); |
| } |
| |
| @override |
| void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| Expression functionExpression = node.function; |
| |
| if (functionExpression is ExtensionOverride) { |
| return super.visitFunctionExpressionInvocation(node); |
| } |
| |
| DartType expressionType = functionExpression.typeOrThrow; |
| if (expressionType is FunctionType) { |
| _typeArgumentsVerifier.checkFunctionExpressionInvocation(node); |
| } |
| _requiredParametersVerifier.visitFunctionExpressionInvocation(node); |
| super.visitFunctionExpressionInvocation(node); |
| } |
| |
| @override |
| void visitFunctionReference(FunctionReference node) { |
| _typeArgumentsVerifier.checkFunctionReference(node); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| _checkForMainFunction(node.name); |
| _checkForTypeAliasCannotReferenceItself( |
| node.name, node.declaredElement as TypeAliasElementImpl); |
| super.visitFunctionTypeAlias(node); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| bool old = _isInFunctionTypedFormalParameter; |
| _isInFunctionTypedFormalParameter = true; |
| try { |
| _checkForTypeAnnotationDeferredClass(node.returnType); |
| |
| // TODO(jmesserly): ideally we'd use _checkForImplicitDynamicReturn, and |
| // we can get the function element via `node?.element?.type?.element` but |
| // it doesn't have hasImplicitReturnType set correctly. |
| if (!_options.implicitDynamic && node.returnType == null) { |
| DartType parameterType = node.declaredElement!.type; |
| if (parameterType is FunctionType && |
| parameterType.returnType.isDynamic) { |
| errorReporter.reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_RETURN, |
| node.identifier, [node.identifier]); |
| } |
| } |
| |
| super.visitFunctionTypedFormalParameter(node); |
| } finally { |
| _isInFunctionTypedFormalParameter = old; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| _checkForMainFunction(node.name); |
| _checkForTypeAliasCannotReferenceItself( |
| node.name, node.declaredElement as TypeAliasElementImpl); |
| super.visitGenericTypeAlias(node); |
| } |
| |
| @override |
| void visitImplementsClause(ImplementsClause node) { |
| node.interfaces.forEach(_checkForImplicitDynamicType); |
| super.visitImplementsClause(node); |
| } |
| |
| @override |
| void visitImportDirective(ImportDirective node) { |
| var importElement = node.element; |
| if (node.prefix != null) { |
| _checkForBuiltInIdentifierAsName(node.prefix!, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME); |
| } |
| if (importElement != null) { |
| _checkForImportInternalLibrary(node, importElement); |
| if (importElement.isDeferred) { |
| _checkForDeferredImportOfExtensions(node, importElement); |
| } |
| } |
| super.visitImportDirective(node); |
| } |
| |
| @override |
| void visitIndexExpression(IndexExpression node) { |
| if (node.isNullAware) { |
| _checkForUnnecessaryNullAware( |
| node.realTarget, |
| node.question ?? node.period ?? node.leftBracket, |
| ); |
| } |
| |
| super.visitIndexExpression(node); |
| } |
| |
| @override |
| void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| ConstructorName constructorName = node.constructorName; |
| TypeName typeName = constructorName.type; |
| DartType type = typeName.typeOrThrow; |
| if (type is InterfaceType) { |
| _checkForConstOrNewWithAbstractClass(node, typeName, type); |
| _checkForConstOrNewWithEnum(node, typeName, type); |
| _checkForConstOrNewWithMixin(node, typeName, type); |
| _requiredParametersVerifier.visitInstanceCreationExpression(node); |
| if (node.isConst) { |
| _checkForConstWithNonConst(node); |
| _checkForConstWithUndefinedConstructor(node, constructorName, typeName); |
| _checkForConstDeferredClass(node, constructorName, typeName); |
| } else { |
| _checkForNewWithUndefinedConstructor(node, constructorName, typeName); |
| } |
| _checkForListConstructor(node, type); |
| } |
| _checkForImplicitDynamicType(typeName); |
| super.visitInstanceCreationExpression(node); |
| } |
| |
| @override |
| void visitIntegerLiteral(IntegerLiteral node) { |
| _checkForOutOfRange(node); |
| super.visitIntegerLiteral(node); |
| } |
| |
| @override |
| void visitInterpolationExpression(InterpolationExpression node) { |
| checkForUseOfVoidResult(node.expression); |
| super.visitInterpolationExpression(node); |
| } |
| |
| @override |
| void visitIsExpression(IsExpression node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| checkForUseOfVoidResult(node.expression); |
| super.visitIsExpression(node); |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral node) { |
| _typeArgumentsVerifier.checkListLiteral(node); |
| _checkForListElementTypeNotAssignable(node); |
| |
| super.visitListLiteral(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| _withEnclosingExecutable(node.declaredElement!, () { |
| var returnType = node.returnType; |
| if (node.isStatic && node.isGetter) { |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkGetter( |
| node.name, node.declaredElement as PropertyAccessorElement); |
| } |
| if (node.isSetter) { |
| _checkForWrongNumberOfParametersForSetter(node.name, node.parameters); |
| _checkForNonVoidReturnTypeForSetter(returnType); |
| } else if (node.isOperator) { |
| var hasWrongNumberOfParameters = |
| _checkForWrongNumberOfParametersForOperator(node); |
| if (!hasWrongNumberOfParameters) { |
| // If the operator has too many parameters including one or more |
| // optional parameters, only report one error. |
| _checkForOptionalParameterInOperator(node); |
| } |
| _checkForNonVoidReturnTypeForOperator(node); |
| } |
| _checkForExtensionDeclaresMemberOfObject(node); |
| _checkForTypeAnnotationDeferredClass(returnType); |
| _returnTypeVerifier.verifyReturnType(returnType); |
| _checkForImplicitDynamicReturn(node, node.declaredElement!); |
| _checkForWrongTypeParameterVarianceInMethod(node); |
| super.visitMethodDeclaration(node); |
| }); |
| } |
| |
| @override |
| void visitMethodInvocation(MethodInvocation node) { |
| var target = node.realTarget; |
| SimpleIdentifier methodName = node.methodName; |
| if (target != null) { |
| var typeReference = ElementResolver.getTypeReference(target); |
| _checkForStaticAccessToInstanceMember(typeReference, methodName); |
| _checkForInstanceAccessToStaticMember( |
| typeReference, node.target, methodName); |
| _checkForUnnecessaryNullAware(target, node.operator!); |
| } else { |
| _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName); |
| } |
| _typeArgumentsVerifier.checkMethodInvocation(node); |
| _requiredParametersVerifier.visitMethodInvocation(node); |
| _checkUseVerifier.checkMethodInvocation(node); |
| super.visitMethodInvocation(node); |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| // TODO(scheglov) Verify for all mixin errors. |
| var outerClass = _enclosingClass; |
| try { |
| _enclosingClass = node.declaredElement as ClassElementImpl; |
| |
| List<ClassMember> members = node.members; |
| _duplicateDefinitionVerifier.checkMixin(node); |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
| _checkForConflictingClassTypeVariableErrorCodes(); |
| |
| var onClause = node.onClause; |
| var implementsClause = node.implementsClause; |
| |
| // Only do error checks only if there is a non-null clause. |
| if (onClause != null || implementsClause != null) { |
| _checkMixinInheritance(node, onClause, implementsClause); |
| } |
| |
| _checkForConflictingClassMembers(); |
| _checkForFinalNotInitializedInClass(members); |
| _checkForMainFunction(node.name); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| // _checkForBadFunctionUse(node); |
| super.visitMixinDeclaration(node); |
| } finally { |
| _enclosingClass = outerClass; |
| } |
| } |
| |
| @override |
| void visitNativeClause(NativeClause node) { |
| // TODO(brianwilkerson) Figure out the right rule for when 'native' is |
| // allowed. |
| if (!_isInSystemLibrary) { |
| errorReporter.reportErrorForNode( |
| ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, node); |
| } |
| super.visitNativeClause(node); |
| } |
| |
| @override |
| void visitNativeFunctionBody(NativeFunctionBody node) { |
| _checkForNativeFunctionBodyInNonSdkCode(node); |
| super.visitNativeFunctionBody(node); |
| } |
| |
| @override |
| void visitPostfixExpression(PostfixExpression node) { |
| var operand = node.operand; |
| if (node.operator.type == TokenType.BANG) { |
| checkForUseOfVoidResult(node); |
| _checkForUnnecessaryNullAware(operand, node.operator); |
| } else { |
| _checkForAssignmentToFinal(operand); |
| _checkForIntNotAssignable(operand); |
| } |
| super.visitPostfixExpression(node); |
| } |
| |
| @override |
| void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| if (node.parent is! Annotation) { |
| var typeReference = ElementResolver.getTypeReference(node.prefix); |
| SimpleIdentifier name = node.identifier; |
| _checkForStaticAccessToInstanceMember(typeReference, name); |
| _checkForInstanceAccessToStaticMember(typeReference, node.prefix, name); |
| } |
| super.visitPrefixedIdentifier(node); |
| } |
| |
| @override |
| void visitPrefixExpression(PrefixExpression node) { |
| TokenType operatorType = node.operator.type; |
| Expression operand = node.operand; |
| if (operatorType != TokenType.BANG) { |
| if (operatorType.isIncrementOperator) { |
| _checkForAssignmentToFinal(operand); |
| } |
| checkForUseOfVoidResult(operand); |
| _checkForIntNotAssignable(operand); |
| } |
| super.visitPrefixExpression(node); |
| } |
| |
| @override |
| void visitPropertyAccess(PropertyAccess node) { |
| var target = node.realTarget; |
| var typeReference = ElementResolver.getTypeReference(target); |
| SimpleIdentifier propertyName = node.propertyName; |
| _checkForStaticAccessToInstanceMember(typeReference, propertyName); |
| _checkForInstanceAccessToStaticMember( |
| typeReference, node.target, propertyName); |
| _checkForUnnecessaryNullAware(target, node.operator); |
| _checkUseVerifier.checkPropertyAccess(node); |
| super.visitPropertyAccess(node); |
| } |
| |
| @override |
| void visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node) { |
| _requiredParametersVerifier.visitRedirectingConstructorInvocation(node); |
| _isInConstructorInitializer = true; |
| try { |
| super.visitRedirectingConstructorInvocation(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitRethrowExpression(RethrowExpression node) { |
| _checkForRethrowOutsideCatch(node); |
| super.visitRethrowExpression(node); |
| } |
| |
| @override |
| void visitReturnStatement(ReturnStatement node) { |
| if (node.expression == null) { |
| _enclosingExecutable._returnsWithout.add(node); |
| } else { |
| _enclosingExecutable._returnsWith.add(node); |
| } |
| _returnTypeVerifier.verifyReturnStatement(node); |
| super.visitReturnStatement(node); |
| } |
| |
| @override |
| void visitSetOrMapLiteral(SetOrMapLiteral node) { |
| if (node.isMap) { |
| _typeArgumentsVerifier.checkMapLiteral(node); |
| _checkForMapTypeNotAssignable(node); |
| _checkForNonConstMapAsExpressionStatement3(node); |
| } else if (node.isSet) { |
| _typeArgumentsVerifier.checkSetLiteral(node); |
| _checkForSetElementTypeNotAssignable3(node); |
| } |
| super.visitSetOrMapLiteral(node); |
| } |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter node) { |
| _checkForPrivateOptionalParameter(node); |
| _checkForTypeAnnotationDeferredClass(node.type); |
| |
| // Checks for an implicit dynamic parameter type. |
| // |
| // We can skip other parameter kinds besides simple formal, because: |
| // - DefaultFormalParameter contains a simple one, so it gets here, |
| // - FieldFormalParameter error should be reported on the field, |
| // - FunctionTypedFormalParameter is a function type, not dynamic. |
| _checkForImplicitDynamicIdentifier(node, node.identifier); |
| |
| super.visitSimpleFormalParameter(node); |
| } |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| _checkForAmbiguousImport(node); |
| _checkForReferenceBeforeDeclaration(node); |
| _checkForInvalidInstanceMemberAccess(node); |
| _checkForTypeParameterReferencedByStatic(node); |
| if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) { |
| _checkForUnqualifiedReferenceToNonLocalStaticMember(node); |
| } |
| _checkUseVerifier.checkSimpleIdentifier(node); |
| super.visitSimpleIdentifier(node); |
| } |
| |
| @override |
| void visitSpreadElement(SpreadElement node) { |
| if (node.isNullAware) { |
| _checkForUnnecessaryNullAware(node.expression, node.spreadOperator); |
| } |
| super.visitSpreadElement(node); |
| } |
| |
| @override |
| void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| _requiredParametersVerifier.visitSuperConstructorInvocation(node); |
| _isInConstructorInitializer = true; |
| try { |
| super.visitSuperConstructorInvocation(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| _withHiddenElements(node.statements, () { |
| _duplicateDefinitionVerifier.checkStatements(node.statements); |
| super.visitSwitchCase(node); |
| }); |
| } |
| |
| @override |
| void visitSwitchDefault(SwitchDefault node) { |
| _withHiddenElements(node.statements, () { |
| _duplicateDefinitionVerifier.checkStatements(node.statements); |
| super.visitSwitchDefault(node); |
| }); |
| } |
| |
| @override |
| void visitSwitchStatement(SwitchStatement node) { |
| _checkForSwitchExpressionNotAssignable(node); |
| _checkForCaseBlocksNotTerminated(node); |
| _checkForMissingEnumConstantInSwitch(node); |
| super.visitSwitchStatement(node); |
| } |
| |
| @override |
| void visitThisExpression(ThisExpression node) { |
| _checkForInvalidReferenceToThis(node); |
| super.visitThisExpression(node); |
| } |
| |
| @override |
| void visitThrowExpression(ThrowExpression node) { |
| _checkForConstEvalThrowsException(node); |
| checkForUseOfVoidResult(node.expression); |
| _checkForThrowOfInvalidType(node); |
| super.visitThrowExpression(node); |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| _checkForFinalNotInitialized(node.variables); |
| _checkForNotInitializedNonNullableVariable(node.variables); |
| |
| for (var declaration in node.variables.variables) { |
| _checkForMainFunction(declaration.name); |
| } |
| |
| super.visitTopLevelVariableDeclaration(node); |
| } |
| |
| @override |
| void visitTypeArgumentList(TypeArgumentList node) { |
| NodeList<TypeAnnotation> list = node.arguments; |
| for (TypeAnnotation type in list) { |
| _checkForTypeAnnotationDeferredClass(type); |
| } |
| super.visitTypeArgumentList(node); |
| } |
| |
| @override |
| void visitTypeName(TypeName node) { |
| _typeArgumentsVerifier.checkTypeName(node); |
| super.visitTypeName(node); |
| } |
| |
| @override |
| void visitTypeParameter(TypeParameter node) { |
| _checkForBuiltInIdentifierAsName(node.name, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME); |
| _checkForTypeAnnotationDeferredClass(node.bound); |
| _checkForImplicitDynamicType(node.bound); |
| _checkForGenericFunctionType(node.bound); |
| node.bound?.accept(_uninstantiatedBoundChecker); |
| super.visitTypeParameter(node); |
| } |
| |
| @override |
| void visitTypeParameterList(TypeParameterList node) { |
| _duplicateDefinitionVerifier.checkTypeParameters(node); |
| _checkForTypeParameterBoundRecursion(node.typeParameters); |
| super.visitTypeParameterList(node); |
| } |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| SimpleIdentifier nameNode = node.name; |
| var initializerNode = node.initializer; |
| // do checks |
| _checkForImplicitDynamicIdentifier(node, nameNode); |
| _checkForAbstractOrExternalVariableInitializer(node); |
| // visit name |
| nameNode.accept(this); |
| // visit initializer |
| String name = nameNode.name; |
| _namesForReferenceToDeclaredVariableInInitializer.add(name); |
| try { |
| if (initializerNode != null) { |
| initializerNode.accept(this); |
| } |
| } finally { |
| _namesForReferenceToDeclaredVariableInInitializer.remove(name); |
| } |
| // declare the variable |
| AstNode grandparent = node.parent!.parent!; |
| if (grandparent is! TopLevelVariableDeclaration && |
| grandparent is! FieldDeclaration) { |
| VariableElement element = node.declaredElement!; |
| // There is no hidden elements if we are outside of a function body, |
| // which will happen for variables declared in control flow elements. |
| _hiddenElements?.declare(element); |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList node) { |
| _checkForTypeAnnotationDeferredClass(node.type); |
| super.visitVariableDeclarationList(node); |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| _isInLateLocalVariable.add(node.variables.isLate); |
| |
| _checkForFinalNotInitialized(node.variables); |
| super.visitVariableDeclarationStatement(node); |
| |
| _isInLateLocalVariable.removeLast(); |
| } |
| |
| @override |
| void visitWithClause(WithClause node) { |
| node.mixinTypes.forEach(_checkForImplicitDynamicType); |
| super.visitWithClause(node); |
| } |
| |
| /// Checks the class for problems with the superclass, mixins, or implemented |
| /// interfaces. |
| void _checkClassInheritance( |
| NamedCompilationUnitMember node, |
| TypeName? superclass, |
| WithClause? withClause, |
| ImplementsClause? implementsClause) { |
| // Only check for all of the inheritance logic around clauses if there |
| // isn't an error code such as "Cannot extend double" already on the |
| // class. |
| if (!_checkForExtendsDisallowedClass(superclass) && |
| !_checkForImplementsClauseErrorCodes(implementsClause) && |
| !_checkForAllMixinErrorCodes(withClause) && |
| !_checkForNoGenerativeConstructorsInSuperclass(superclass)) { |
| _checkForImplicitDynamicType(superclass); |
| _checkForExtendsDeferredClass(superclass); |
| _checkForRepeatedType(implementsClause?.interfaces, |
| CompileTimeErrorCode.IMPLEMENTS_REPEATED); |
| _checkImplementsSuperClass(implementsClause); |
| _checkMixinsSuperClass(withClause); |
| _checkMixinInference(node, withClause); |
| _checkForMixinWithConflictingPrivateMember(withClause, superclass); |
| _checkForConflictingGenerics(node); |
| if (node is ClassDeclaration) { |
| _checkForNoDefaultSuperConstructorImplicit(node); |
| } |
| } |
| } |
| |
| /// Given a list of [directives] that have the same prefix, generate an error |
| /// if there is more than one import and any of those imports is deferred. |
| /// |
| /// See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX]. |
| void _checkDeferredPrefixCollision(List<ImportDirective> directives) { |
| int count = directives.length; |
| if (count > 1) { |
| for (int i = 0; i < count; i++) { |
| var deferredToken = directives[i].deferredKeyword; |
| if (deferredToken != null) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken); |
| } |
| } |
| } |
| } |
| |
| void _checkForAbstractOrExternalFieldConstructorInitializer( |
| AstNode node, FieldElement fieldElement) { |
| if (fieldElement.isAbstract) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ABSTRACT_FIELD_CONSTRUCTOR_INITIALIZER, node); |
| } |
| if (fieldElement.isExternal) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXTERNAL_FIELD_CONSTRUCTOR_INITIALIZER, node); |
| } |
| } |
| |
| void _checkForAbstractOrExternalVariableInitializer( |
| VariableDeclaration node) { |
| var declaredElement = node.declaredElement; |
| if (node.initializer != null) { |
| if (declaredElement is FieldElement) { |
| if (declaredElement.isAbstract) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER, node.name); |
| } |
| if (declaredElement.isExternal) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXTERNAL_FIELD_INITIALIZER, node.name); |
| } |
| } else if (declaredElement is TopLevelVariableElement) { |
| if (declaredElement.isExternal) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXTERNAL_VARIABLE_INITIALIZER, node.name); |
| } |
| } |
| } |
| } |
| |
| /// Verify that all classes of the given [withClause] are valid. |
| /// |
| /// See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR], |
| /// [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT]. |
| bool _checkForAllMixinErrorCodes(WithClause? withClause) { |
| if (withClause == null) { |
| return false; |
| } |
| bool problemReported = false; |
| int mixinTypeIndex = -1; |
| for (int mixinNameIndex = 0; |
| mixinNameIndex < withClause.mixinTypes.length; |
| mixinNameIndex++) { |
| TypeName mixinName = withClause.mixinTypes[mixinNameIndex]; |
| DartType mixinType = mixinName.typeOrThrow; |
| if (mixinType is InterfaceType) { |
| mixinTypeIndex++; |
| if (_checkForExtendsOrImplementsDisallowedClass( |
| mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) { |
| problemReported = true; |
| } else { |
| ClassElement mixinElement = mixinType.element; |
| if (_checkForExtendsOrImplementsDeferredClass( |
| mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) { |
| problemReported = true; |
| } |
| if (mixinElement.isMixin) { |
| if (_checkForMixinSuperclassConstraints( |
| mixinNameIndex, mixinName)) { |
| problemReported = true; |
| } else if (_checkForMixinSuperInvokedMembers( |
| mixinTypeIndex, mixinName, mixinElement, mixinType)) { |
| problemReported = true; |
| } |
| } else { |
| if (_checkForMixinClassDeclaresConstructor( |
| mixinName, mixinElement)) { |
| problemReported = true; |
| } |
| if (_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) { |
| problemReported = true; |
| } |
| } |
| } |
| } |
| } |
| return problemReported; |
| } |
| |
| /// Check for errors related to the redirected constructors. |
| void _checkForAllRedirectConstructorErrorCodes( |
| ConstructorDeclaration declaration) { |
| // Prepare redirected constructor node |
| var redirectedConstructor = declaration.redirectedConstructor; |
| if (redirectedConstructor == null) { |
| return; |
| } |
| |
| // Prepare redirected constructor type |
| var 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.typeOrThrow; |
| if (redirectedType.element != null && !redirectedType.isDynamic) { |
| // Prepare the constructor name |
| String constructorStrName = constructorTypeName.name.name; |
| if (redirectedConstructor.name != null) { |
| constructorStrName += ".${redirectedConstructor.name!.name}"; |
| } |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR, |
| redirectedConstructor, |
| [constructorStrName, redirectedType]); |
| } |
| return; |
| } |
| FunctionType redirectedType = redirectedElement.type; |
| DartType redirectedReturnType = redirectedType.returnType; |
| |
| // Report specific problem when return type is incompatible |
| FunctionType constructorType = declaration.declaredElement!.type; |
| DartType constructorReturnType = constructorType.returnType; |
| if (!typeSystem.isAssignableTo( |
| redirectedReturnType, constructorReturnType)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.REDIRECT_TO_INVALID_RETURN_TYPE, |
| redirectedConstructor, |
| [redirectedReturnType, constructorReturnType]); |
| return; |
| } else if (!typeSystem.isSubtypeOf(redirectedType, constructorType)) { |
| // Check parameters. |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, |
| redirectedConstructor, |
| [redirectedType, constructorType]); |
| } |
| } |
| |
| /// Verify that the export namespace of the given export [directive] does not |
| /// export any name already exported by another export directive. The |
| /// [exportElement] is the [ExportElement] retrieved from the node. If the |
| /// element in the node was `null`, then this method is not called. The |
| /// [exportedLibrary] is the library element containing the exported element. |
| /// |
| /// See [CompileTimeErrorCode.AMBIGUOUS_EXPORT]. |
| void _checkForAmbiguousExport(ExportDirective directive, |
| ExportElement exportElement, LibraryElement? exportedLibrary) { |
| if (exportedLibrary == null) { |
| return; |
| } |
| // check exported names |
| Namespace namespace = |
| NamespaceBuilder().createExportNamespaceForDirective(exportElement); |
| Map<String, Element> definedNames = namespace.definedNames; |
| for (String name in definedNames.keys) { |
| var element = definedNames[name]!; |
| var prevElement = _exportedElements[name]; |
| if (prevElement != null && prevElement != element) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.AMBIGUOUS_EXPORT, directive.uri, [ |
| name, |
| prevElement.library!.definingCompilationUnit.source.uri, |
| element.library!.definingCompilationUnit.source.uri |
| ]); |
| return; |
| } else { |
| _exportedElements[name] = element; |
| } |
| } |
| } |
| |
| /// Check the given node to see whether it was ambiguous because the name was |
| /// imported from two or more imports. |
| void _checkForAmbiguousImport(SimpleIdentifier node) { |
| var element = node.writeOrReadElement; |
| if (element is MultiplyDefinedElementImpl) { |
| String name = element.displayName; |
| List<Element> conflictingMembers = element.conflictingElements; |
| var libraryNames = |
| conflictingMembers.map((e) => _getLibraryName(e)).toList(); |
| libraryNames.sort(); |
| errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_IMPORT, |
| node, [name, StringUtilities.printListOfQuotedNames(libraryNames)]); |
| } |
| } |
| |
| /// Verify that the given [expression] is not final. |
| /// |
| /// See [StaticWarningCode.ASSIGNMENT_TO_CONST], |
| /// [StaticWarningCode.ASSIGNMENT_TO_FINAL], and |
| /// [StaticWarningCode.ASSIGNMENT_TO_METHOD]. |
| void _checkForAssignmentToFinal(Expression expression) { |
| // TODO(scheglov) Check SimpleIdentifier(s) as all other nodes. |
| if (expression is! SimpleIdentifier) return; |
| |
| // Already handled in the assignment resolver. |
| if (expression.parent is AssignmentExpression) { |
| return; |
| } |
| |
| // prepare element |
| var highlightedNode = expression; |
| var element = expression.staticElement; |
| if (expression is PrefixedIdentifier) { |
| var prefixedIdentifier = expression as PrefixedIdentifier; |
| highlightedNode = prefixedIdentifier.identifier; |
| } |
| // check if element is assignable |
| if (element is VariableElement) { |
| if (element.isConst) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_CONST, |
| expression, |
| ); |
| } else if (element.isFinal) { |
| if (_isNonNullableByDefault) { |
| // Handled during resolution, with flow analysis. |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, |
| expression, |
| [element.name], |
| ); |
| } |
| } |
| } else if (element is PropertyAccessorElement && element.isGetter) { |
| var variable = element.variable; |
| if (variable.isConst) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_CONST, |
| expression, |
| ); |
| } else if (variable is FieldElement && variable.isSynthetic) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, |
| highlightedNode, |
| [variable.name, variable.enclosingElement.displayName], |
| ); |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, |
| highlightedNode, |
| [variable.name], |
| ); |
| } |
| } else if (element is FunctionElement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_FUNCTION, expression); |
| } else if (element is MethodElement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_METHOD, expression); |
| } else if (element is ClassElement || |
| element is DynamicElementImpl || |
| element is TypeParameterElement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.ASSIGNMENT_TO_TYPE, expression); |
| } |
| } |
| |
| void _checkForAwaitInLateLocalVariableInitializer(AwaitExpression node) { |
| if (_isInLateLocalVariable.last) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.AWAIT_IN_LATE_LOCAL_VARIABLE_INITIALIZER, |
| node.awaitKeyword, |
| ); |
| } |
| } |
| |
| /// Verifies that the class is not named `Function` and that it doesn't |
| /// extends/implements/mixes in `Function`. |
| void _checkForBadFunctionUse(ClassDeclaration node) { |
| var extendsClause = node.extendsClause; |
| var implementsClause = node.implementsClause; |
| var withClause = node.withClause; |
| |
| if (node.name.name == "Function") { |
| errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION, node.name); |
| } |
| |
| if (extendsClause != null) { |
| var superElement = extendsClause.superclass.name.staticElement; |
| if (superElement != null && superElement.name == "Function") { |
| errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_EXTENDS_FUNCTION, extendsClause.superclass); |
| } |
| } |
| |
| if (implementsClause != null) { |
| for (var interface in implementsClause.interfaces) { |
| var type = interface.type; |
| if (type != null && type.isDartCoreFunction) { |
| errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_IMPLEMENTS_FUNCTION, |
| interface, |
| ); |
| break; |
| } |
| } |
| } |
| |
| if (withClause != null) { |
| for (TypeName type in withClause.mixinTypes) { |
| var mixinElement = type.name.staticElement; |
| if (mixinElement != null && mixinElement.name == "Function") { |
| errorReporter.reportErrorForNode( |
| HintCode.DEPRECATED_MIXIN_FUNCTION, type); |
| } |
| } |
| } |
| } |
| |
| /// Verify that the given [identifier] is not a keyword, and generates the |
| /// given [errorCode] on the identifier if it is a keyword. |
| /// |
| /// See [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_NAME], |
| /// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME], |
| /// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME], and |
| /// [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME]. |
| void _checkForBuiltInIdentifierAsName( |
| SimpleIdentifier identifier, ErrorCode errorCode) { |
| Token token = identifier.token; |
| if (token.type.isKeyword && token.keyword?.isPseudo != true) { |
| errorReporter |
| .reportErrorForNode(errorCode, identifier, [identifier.name]); |
| } |
| } |
| |
| /// Verify that the given [switchCase] is terminated with 'break', 'continue', |
| /// 'return' or 'throw'. |
| /// |
| /// see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
| void _checkForCaseBlockNotTerminated(SwitchCase switchCase) { |
| NodeList<Statement> statements = switchCase.statements; |
| if (statements.isEmpty) { |
| // fall-through without statements at all |
| var parent = switchCase.parent; |
| if (parent is SwitchStatement) { |
| NodeList<SwitchMember> members = parent.members; |
| int index = members.indexOf(switchCase); |
| if (index != -1 && index < members.length - 1) { |
| return; |
| } |
| } |
| // no other switch member after this one |
| } else { |
| Statement statement = statements.last; |
| if (statement is Block && statement.statements.isNotEmpty) { |
| Block block = statement; |
| statement = block.statements.last; |
| } |
| // terminated with statement |
| if (statement is BreakStatement || |
| statement is ContinueStatement || |
| statement is ReturnStatement) { |
| return; |
| } |
| // terminated with 'throw' expression |
| if (statement is ExpressionStatement) { |
| Expression expression = statement.expression; |
| if (expression is ThrowExpression || expression is RethrowExpression) { |
| return; |
| } |
| } |
| } |
| |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword); |
| } |
| |
| /// Verify that the switch cases in the given switch [statement] are |
| /// terminated with 'break', 'continue', 'rethrow', 'return' or 'throw'. |
| /// |
| /// See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED]. |
| void _checkForCaseBlocksNotTerminated(SwitchStatement statement) { |
| if (_isNonNullableByDefault) return; |
| |
| NodeList<SwitchMember> members = statement.members; |
| int lastMember = members.length - 1; |
| for (int i = 0; i < lastMember; i++) { |
| SwitchMember member = members[i]; |
| if (member is SwitchCase) { |
| _checkForCaseBlockNotTerminated(member); |
| } |
| } |
| } |
| |
| /// Verify that the [_enclosingClass] does not have a method and getter pair |
| /// with the same name, via inheritance. |
| /// |
| /// See [CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE], |
| /// [CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD], and |
| /// [CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD]. |
| void _checkForConflictingClassMembers() { |
| if (_enclosingClass == null) { |
| return; |
| } |
| Uri libraryUri = _currentLibrary.source.uri; |
| |
| // method declared in the enclosing class vs. inherited getter/setter |
| for (MethodElement method in _enclosingClass!.methods) { |
| String name = method.name; |
| |
| // find inherited property accessor |
| var inherited = _inheritanceManager.getInherited2( |
| _enclosingClass!, Name(libraryUri, name)); |
| inherited ??= _inheritanceManager.getInherited2( |
| _enclosingClass!, Name(libraryUri, '$name=')); |
| |
| if (method.isStatic && inherited != null) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, method, [ |
| _enclosingClass!.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ]); |
| } else if (inherited is PropertyAccessorElement) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD, method, [ |
| _enclosingClass!.displayName, |
| name, |
| inherited.enclosingElement.displayName |
| ]); |
| } |
| } |
| |
| // getter declared in the enclosing class vs. inherited method |
| for (PropertyAccessorElement accessor in _enclosingClass!.accessors) { |
| String name = accessor.displayName; |
| |
| // find inherited method or property accessor |
| var inherited = _inheritanceManager.getInherited2( |
| _enclosingClass!, Name(libraryUri, name)); |
| inherited ??= _inheritanceManager.getInherited2( |
| _enclosingClass!, Name(libraryUri, '$name=')); |
| |
| if (accessor.isStatic && inherited != null) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, accessor, [ |
| _enclosingClass!.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ]); |
| } else if (inherited is MethodElement) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, accessor, [ |
| _enclosingClass!.displayName, |
| name, |
| inherited.enclosingElement.displayName |
| ]); |
| } |
| } |
| } |
| |
| /// Verify all conflicts between type variable and enclosing class. |
| /// TODO(scheglov) |
| void _checkForConflictingClassTypeVariableErrorCodes() { |
| for (TypeParameterElement typeParameter |
| in _enclosingClass!.typeParameters) { |
| String name = typeParameter.name; |
| // name is same as the name of the enclosing class |
| if (_enclosingClass!.name == name) { |
| var code = _enclosingClass!.isMixin |
| ? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MIXIN |
| : CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS; |
| errorReporter.reportErrorForElement(code, typeParameter, [name]); |
| } |
| // check members |
| if (_enclosingClass!.getMethod(name) != null || |
| _enclosingClass!.getGetter(name) != null || |
| _enclosingClass!.getSetter(name) != null) { |
| var code = _enclosingClass!.isMixin |
| ? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_MIXIN |
| : CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_CLASS; |
| errorReporter.reportErrorForElement(code, typeParameter, [name]); |
| } |
| } |
| } |
| |
| /// Verify all conflicts between type variable and enclosing extension. |
| /// |
| /// See [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION], and |
| /// [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION_MEMBER]. |
| void _checkForConflictingExtensionTypeVariableErrorCodes() { |
| for (TypeParameterElement typeParameter |
| in _enclosingExtension!.typeParameters) { |
| String name = typeParameter.name; |
| // name is same as the name of the enclosing class |
| if (_enclosingExtension!.name == name) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION, |
| typeParameter, |
| [name]); |
| } |
| // check members |
| if (_enclosingExtension!.getMethod(name) != null || |
| _enclosingExtension!.getGetter(name) != null || |
| _enclosingExtension!.getSetter(name) != null) { |
| errorReporter.reportErrorForElement( |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_EXTENSION, |
| typeParameter, |
| [name]); |
| } |
| } |
| } |
| |
| void _checkForConflictingGenerics(NamedCompilationUnitMember node) { |
| var element = node.declaredElement as ClassElement; |
| |
| var analysisSession = _currentLibrary.session as AnalysisSessionImpl; |
| var errors = analysisSession.classHierarchy.errors(element); |
| |
| for (var error in errors) { |
| if (error is IncompatibleInterfacesClassHierarchyError) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES, |
| node.name, |
| [ |
| _enclosingClass!.name, |
| error.first.getDisplayString(withNullability: true), |
| error.second.getDisplayString(withNullability: true), |
| ], |
| ); |
| } else { |
| throw UnimplementedError('${error.runtimeType}'); |
| } |
| } |
| } |
| |
| /// Verify that if the given [constructor] declaration is 'const' then there |
| /// are no invocations of non-'const' super constructors, and that there are |
| /// no instance variables mixed in. |
| /// |
| /// See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER], and |
| /// [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD]. |
| void _checkForConstConstructorWithNonConstSuper( |
| ConstructorDeclaration constructor) { |
| if (!_enclosingExecutable.isConstConstructor) { |
| return; |
| } |
| // OK, const factory, checked elsewhere |
| if (constructor.factoryKeyword != null) { |
| return; |
| } |
| |
| // check for mixins |
| var instanceFields = <FieldElement>[]; |
| for (var mixin in _enclosingClass!.mixins) { |
| instanceFields.addAll(mixin.element.fields |
| .where((field) => !field.isStatic && !field.isSynthetic)); |
| } |
| if (instanceFields.length == 1) { |
| var field = instanceFields.single; |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD, |
| constructor.returnType, |
| ["'${field.enclosingElement.name}.${field.name}'"]); |
| return; |
| } else if (instanceFields.length > 1) { |
| var fieldNames = instanceFields |
| .map((field) => "'${field.enclosingElement.name}.${field.name}'") |
| .join(', '); |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELDS, |
| constructor.returnType, |
| [fieldNames]); |
| return; |
| } |
| |
| // try to find and check super constructor invocation |
| for (ConstructorInitializer initializer in constructor.initializers) { |
| if (initializer is SuperConstructorInvocation) { |
| var element = initializer.staticElement; |
| if (element == null || element.isConst) { |
| return; |
| } |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
| initializer, |
| [element.enclosingElement.displayName]); |
| return; |
| } |
| } |
| // no explicit super constructor invocation, check default constructor |
| var supertype = _enclosingClass!.supertype; |
| if (supertype == null) { |
| return; |
| } |
| if (supertype.isDartCoreObject) { |
| return; |
| } |
| var unnamedConstructor = supertype.element.unnamedConstructor; |
| if (unnamedConstructor == null || unnamedConstructor.isConst) { |
| return; |
| } |
| |
| // default constructor is not 'const', report problem |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
| constructor.returnType, |
| [supertype]); |
| } |
| |
| /// Verify that if the given [constructor] declaration is 'const' then there |
| /// are no non-final instance variable. The [constructorElement] is the |
| /// constructor element. |
| void _checkForConstConstructorWithNonFinalField( |
| ConstructorDeclaration constructor, |
| ConstructorElement constructorElement) { |
| if (!_enclosingExecutable.isConstConstructor) { |
| return; |
| } |
| // check if there is non-final field |
| ClassElement classElement = constructorElement.enclosingElement; |
| if (!classElement.hasNonFinalField) { |
| return; |
| } |
| // TODO(brianwilkerson) Stop generating |
| // CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD when either |
| // CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER or |
| // CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD is also generated. |
| errorReporter.reportErrorForName( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD, |
| constructor); |
| } |
| |
| /// Verify that the given 'const' instance creation [expression] is not |
| /// creating a deferred type. The [constructorName] is the constructor name, |
| /// always non-`null`. The [typeName] is the name of the type defining the |
| /// constructor, always non-`null`. |
| /// |
| /// See [CompileTimeErrorCode.CONST_DEFERRED_CLASS]. |
| void _checkForConstDeferredClass(InstanceCreationExpression expression, |
| ConstructorName constructorName, TypeName typeName) { |
| if (typeName.isDeferred) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_DEFERRED_CLASS, |
| constructorName, |
| [typeName.name.name]); |
| } |
| } |
| |
| /// Verify that the given throw [expression] is not enclosed in a 'const' |
| /// constructor declaration. |
| /// |
| /// See [CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION]. |
| void _checkForConstEvalThrowsException(ThrowExpression expression) { |
| if (_enclosingExecutable.isConstConstructor) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, expression); |
| } |
| } |
| |
| /// 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]. |
| void _checkForConstOrNewWithAbstractClass( |
| InstanceCreationExpression expression, |
| TypeName typeName, |
| InterfaceType type) { |
| if (type.element.isAbstract && !type.element.isMixin) { |
| var element = expression.constructorName.staticElement; |
| if (element != null && !element.isFactory) { |
| bool isImplicit = |
| (expression as InstanceCreationExpressionImpl).isImplicit; |
| if (!isImplicit) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, typeName); |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, typeName); |
| } |
| } |
| } |
| } |
| |
| /// Verify that the given instance creation [expression] is not being invoked |
| /// on an enum. The [typeName] is the [TypeName] of the [ConstructorName] from |
| /// the [InstanceCreationExpression], this is the AST node that the error is |
| /// attached to. The [type] is the type being constructed with this |
| /// [InstanceCreationExpression]. |
| /// |
| /// See [CompileTimeErrorCode.INSTANTIATE_ENUM]. |
| void _checkForConstOrNewWithEnum(InstanceCreationExpression expression, |
| TypeName typeName, InterfaceType type) { |
| if (type.element.isEnum) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANTIATE_ENUM, typeName); |
| } |
| } |
| |
| /// Verify that the given [expression] is not a mixin instantiation. |
| void _checkForConstOrNewWithMixin(InstanceCreationExpression expression, |
| TypeName typeName, InterfaceType type) { |
| if (type.element.isMixin) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MIXIN_INSTANTIATE, typeName); |
| } |
| } |
| |
| /// Verify that the given 'const' instance creation [expression] is not being |
| /// invoked on a constructor that is not 'const'. |
| /// |
| /// This method assumes that the instance creation was tested to be 'const' |
| /// before being called. |
| /// |
| /// See [CompileTimeErrorCode.CONST_WITH_NON_CONST]. |
| void _checkForConstWithNonConst(InstanceCreationExpression expression) { |
| var constructorElement = expression.constructorName.staticElement; |
| if (constructorElement != null && !constructorElement.isConst) { |
| if (expression.keyword != null) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.CONST_WITH_NON_CONST, expression.keyword!); |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_WITH_NON_CONST, expression); |
| } |
| } |
| } |
| |
| /// Verify that if the given 'const' instance creation [expression] is being |
| /// invoked on the resolved constructor. The [constructorName] is the |
| /// constructor name, always non-`null`. The [typeName] is the name of the |
| /// type defining the constructor, always non-`null`. |
| /// |
| /// This method assumes that the instance creation was tested to be 'const' |
| /// before being called. |
| /// |
| /// See [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR], and |
| /// [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT]. |
| void _checkForConstWithUndefinedConstructor( |
| InstanceCreationExpression expression, |
| ConstructorName constructorName, |
| TypeName typeName) { |
| // OK if resolved |
| if (constructorName.staticElement != null) { |
| return; |
| } |
| DartType type = typeName.typeOrThrow; |
| if (type is InterfaceType) { |
| ClassElement element = type.element; |
| if (element.isEnum) { |
| // We have already reported the error. |
| return; |
| } |
| } |
| Identifier className = typeName.name; |
| // report as named or default constructor absence |
| var 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]); |
| } |
| } |
| |
| void _checkForDeadNullCoalesce(TypeImpl lhsType, Expression rhs) { |
| if (!_isNonNullableByDefault) return; |
| |
| if (typeSystem.isStrictlyNonNullable(lhsType)) { |
| errorReporter.reportErrorForNode( |
| StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION, |
| rhs, |
| ); |
| } |
| } |
| |
| /// Report a diagnostic if there are any extensions in the imported library |
| /// that are not hidden. |
| void _checkForDeferredImportOfExtensions( |
| ImportDirective directive, ImportElement importElement) { |
| for (var element in importElement.namespace.definedNames.values) { |
| if (element is ExtensionElement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DEFERRED_IMPORT_OF_EXTENSION, |
| directive.uri, |
| ); |
| return; |
| } |
| } |
| } |
| |
| /// Verify that any deferred imports in the given compilation [unit] have a |
| /// unique prefix. |
| /// |
| /// See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX]. |
| void _checkForDeferredPrefixCollisions(CompilationUnit unit) { |
| NodeList<Directive> directives = unit.directives; |
| int count = directives.length; |
| if (count > 0) { |
| Map<PrefixElement, List<ImportDirective>> prefixToDirectivesMap = |
| HashMap<PrefixElement, List<ImportDirective>>(); |
| for (int i = 0; i < count; i++) { |
| Directive directive = directives[i]; |
| if (directive is ImportDirective) { |
| var prefix = directive.prefix; |
| if (prefix != null) { |
| var element = prefix.staticElement; |
| if (element is PrefixElement) { |
| var elements = prefixToDirectivesMap[element]; |
| if (elements == null) { |
| elements = <ImportDirective>[]; |
| prefixToDirectivesMap[element] = elements; |
| } |
| elements.add(directive); |
| } |
| } |
| } |
| } |
| for (List<ImportDirective> imports in prefixToDirectivesMap.values) { |
| _checkDeferredPrefixCollision(imports); |
| } |
| } |
| } |
| |
| /// Return `true` if the caller should continue checking the rest of the |
| /// information in the for-each part. |
| bool _checkForEachParts(ForEachParts node, SimpleIdentifier variable) { |
| if (checkForUseOfVoidResult(node.iterable)) { |
| return false; |
| } |
| |
| DartType iterableType = node.iterable.typeOrThrow; |
| |
| // TODO(scheglov) use NullableDereferenceVerifier |
| if (_isNonNullableByDefault) { |
| if (typeSystem.isNullable(iterableType)) { |
| return false; |
| } |
| } |
| |
| // The type of the loop variable. |
| DartType variableType; |
| var variableElement = variable.staticElement; |
| if (variableElement is VariableElement) { |
| variableType = variableElement.type; |
| } else { |
| return false; |
| } |
| |
| Token? awaitKeyword; |
| var parent = node.parent; |
| if (parent is ForStatement) { |
| awaitKeyword = parent.awaitKeyword; |
| } else if (parent is ForElement) { |
| awaitKeyword = parent.awaitKeyword; |
| } |
| |
| // Use an explicit string instead of [loopType] to remove the "<E>". |
| String loopTypeName = awaitKeyword != null ? "Stream" : "Iterable"; |
| |
| // The object being iterated has to implement Iterable<T> for some T that |
| // is assignable to the variable's type. |
| // TODO(rnystrom): Move this into mostSpecificTypeArgument()? |
| iterableType = iterableType.resolveToBound(_typeProvider.objectType); |
| |
| var requiredSequenceType = awaitKeyword != null |
| ? _typeProvider.streamDynamicType |
| : _typeProvider.iterableDynamicType; |
| |
| if (typeSystem.isTop(iterableType)) { |
| iterableType = requiredSequenceType; |
| } |
| |
| if (!typeSystem.isAssignableTo(iterableType, requiredSequenceType)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_TYPE, |
| node.iterable, |
| [iterableType, loopTypeName], |
| ); |
| return false; |
| } |
| |
| DartType? sequenceElementType; |
| { |
| var sequenceElement = awaitKeyword != null |
| ? _typeProvider.streamElement |
| : _typeProvider.iterableElement; |
| var sequenceType = iterableType.asInstanceOf(sequenceElement); |
| if (sequenceType != null) { |
| sequenceElementType = sequenceType.typeArguments[0]; |
| } |
| } |
| |
| if (sequenceElementType == null) { |
| return true; |
| } |
| |
| if (!typeSystem.isAssignableTo(sequenceElementType, variableType)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, |
| node.iterable, |
| [iterableType, loopTypeName, variableType], |
| ); |
| } |
| |
| return true; |
| } |
| |
| /// Check that if the visiting library is not system, then any given library |
| /// should not be SDK internal library. The [exportElement] is the |
| /// [ExportElement] retrieved from the node, if the element in the node was |
| /// `null`, then this method is not called. |
| /// |
| /// See [CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY]. |
| void _checkForExportInternalLibrary( |
| ExportDirective directive, ExportElement exportElement) { |
| if (_isInSystemLibrary) { |
| return; |
| } |
| |
| var exportedLibrary = exportElement.exportedLibrary; |
| if (exportedLibrary == null) { |
| return; |
| } |
| |
| // should be private |
| var sdk = _currentLibrary.context.sourceFactory.dartSdk!; |
| var uri = exportedLibrary.source.uri.toString(); |
| var sdkLibrary = sdk.getSdkLibrary(uri); |
| if (sdkLibrary == null) { |
| return; |
| } |
| if (!sdkLibrary.isInternal) { |
| return; |
| } |
| |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, |
| directive, |
| [directive.uri]); |
| } |
| |
| /// See [CompileTimeErrorCode.EXPORT_LEGACY_SYMBOL]. |
| void _checkForExportLegacySymbol(ExportDirective node) { |
| if (!_isNonNullableByDefault) { |
| return; |
| } |
| |
| var element = node.element!; |
| // TODO(scheglov) Expose from ExportElement. |
| var namespace = |
| NamespaceBuilder().createExportNamespaceForDirective(element); |
| |
| for (var element in namespace.definedNames.values) { |
| if (element == DynamicElementImpl.instance || |
| element == NeverElementImpl.instance) { |
| continue; |
| } |
| if (!element.library!.isNonNullableByDefault) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXPORT_LEGACY_SYMBOL, |
| node.uri, |
| [element.displayName], |
| ); |
| // Stop after the first symbol. |
| // We don't want to list them all. |
| break; |
| } |
| } |
| } |
| |
| /// Verify that the given extends [clause] does not extend a deferred class. |
| /// |
| /// See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS]. |
| void _checkForExtendsDeferredClass(TypeName? superclass) { |
| if (superclass == null) { |
| return; |
| } |
| _checkForExtendsOrImplementsDeferredClass( |
| superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS); |
| } |
| |
| /// Verify that the given extends [clause] does not extend classes such as |
| /// 'num' or 'String'. |
| /// |
| /// See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]. |
| bool _checkForExtendsDisallowedClass(TypeName? superclass) { |
| if (superclass == null) { |
| return false; |
| } |
| return _checkForExtendsOrImplementsDisallowedClass( |
| 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); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Verify that the given [typeName] does not extend, implement or mixin |
| /// classes such as 'num' or 'String'. |
| /// |
| /// TODO(scheglov) Remove this method, when all inheritance / override |
| /// is concentrated. We keep it for now only because we need to know when |
| /// inheritance is completely wrong, so that we don't need to check anything |
| /// else. |
| bool _checkForExtendsOrImplementsDisallowedClass( |
| TypeName typeName, ErrorCode errorCode) { |
| if (typeName.isSynthetic) { |
| return false; |
| } |
| // The SDK implementation may implement disallowed types. For example, |
| // JSNumber in dart2js and _Smi in Dart VM both implement int. |
| if (_currentLibrary.source.isInSystemLibrary) { |
| return false; |
| } |
| var type = typeName.type; |
| return type is InterfaceType && |
| _typeProvider.isNonSubtypableClass(type.element); |
| } |
| |
| void _checkForExtensionDeclaresMemberOfObject(MethodDeclaration node) { |
| if (_enclosingExtension == null) return; |
| |
| var name = node.name.name; |
| if (name == '==' || |
| name == 'hashCode' || |
| name == 'toString' || |
| name == 'runtimeType' || |
| name == 'noSuchMethod') { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, |
| node.name, |
| ); |
| } |
| } |
| |
| /// Verify that the given field formal [parameter] is in a constructor |
| /// declaration. |
| /// |
| /// See [CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR]. |
| void _checkForFieldInitializingFormalRedirectingConstructor( |
| FieldFormalParameter parameter) { |
| // prepare the node that should be a ConstructorDeclaration |
| var formalParameterList = parameter.parent; |
| if (formalParameterList is! FormalParameterList) { |
| formalParameterList = formalParameterList?.parent; |
| } |
| var constructor = formalParameterList?.parent; |
| // now check whether the node is actually a ConstructorDeclaration |
| if (constructor is ConstructorDeclaration) { |
| // constructor cannot be a factory |
| if (constructor.factoryKeyword != null) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR, |
| parameter); |
| return; |
| } |
| // constructor cannot have a redirection |
| for (ConstructorInitializer initializer in constructor.initializers) { |
| if (initializer is RedirectingConstructorInvocation) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, |
| parameter); |
| return; |
| } |
| } |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, |
| parameter); |
| } |
| } |
| |
| /// 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]. |
| void _checkForFinalNotInitialized(VariableDeclarationList list) { |
| if (_isInNativeClass || list.isSynthetic) { |
| return; |
| } |
| |
| // Handled during resolution, with flow analysis. |
| if (_isNonNullableByDefault && |
| list.isFinal && |
| list.parent is VariableDeclarationStatement) { |
| return; |
| } |
| |
| bool isConst = list.isConst; |
| if (!(isConst || list.isFinal)) { |
| return; |
| } |
| NodeList<VariableDeclaration> variables = list.variables; |
| for (VariableDeclaration variable in variables) { |
| if (variable.initializer == null) { |
| if (isConst) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.CONST_NOT_INITIALIZED, |
| variable.name, |
| [variable.name.name]); |
| } else { |
| var variableElement = variable.declaredElement; |
| if (variableElement is FieldElement && |
| (variableElement.isAbstract || variableElement.isExternal)) { |
| // Abstract and external fields can't be initialized, so no error. |
| } else if (variableElement is TopLevelVariableElement && |
| variableElement.isExternal) { |
| // External top level variables can't be initialized, so no error. |
| } else if (!_isNonNullableByDefault || !variable.isLate) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.FINAL_NOT_INITIALIZED, |
| variable.name, |
| [variable.name.name]); |
| } |
| } |
| } |
| } |
| } |
| |
| /// If there are no constructors in the given [members], verify that all |
| /// final fields are initialized. Cases in which there is at least one |
| /// constructor are handled in [_checkForAllFinalInitializedErrorCodes]. |
| /// |
| /// See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and |
| /// [StaticWarningCode.FINAL_NOT_INITIALIZED]. |
| void _checkForFinalNotInitializedInClass(List<ClassMember> members) { |
| for (ClassMember classMember in members) { |
| if (classMember is ConstructorDeclaration) { |
| if (_isNonNullableByDefault) { |
| if (classMember.factoryKeyword == null) { |
| return; |
| } |
| } else { |
| return; |
| } |
| } |
| } |
| for (ClassMember classMember in members) { |
| if (classMember is FieldDeclaration) { |
| var fields = classMember.fields; |
| _checkForFinalNotInitialized(fields); |
| _checkForNotInitializedNonNullableInstanceFields(classMember); |
| } |
| } |
| } |
| |
| void _checkForGenericFunctionType(TypeAnnotation? node) { |
| if (node == null) { |
| return; |
| } |
| if (_featureSet?.isEnabled(Feature.generic_metadata) ?? false) { |
| return; |
| } |
| DartType type = node.typeOrThrow; |
| if (type is FunctionType && type.typeFormals.isNotEmpty) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND, |
| node, |
| [type]); |
| } |
| } |
| |
| /// Verify that the given implements [clause] does not implement classes such |
| /// as 'num' or 'String'. |
| /// |
| /// See [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS], |
| /// [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS]. |
| bool _checkForImplementsClauseErrorCodes(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; |
| } else if (_checkForExtendsOrImplementsDeferredClass( |
| type, CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS)) { |
| foundError = true; |
| } |
| } |
| return foundError; |
| } |
| |
| void _checkForImplicitDynamicIdentifier(AstNode node, Identifier? id) { |
| if (_options.implicitDynamic) { |
| return; |
| } |
| var variable = getVariableElement(id); |
| if (variable != null && |
| variable.hasImplicitType && |
| variable.type.isDynamic) { |
| ErrorCode errorCode; |
| if (variable is FieldElement) { |
| errorCode = LanguageCode.IMPLICIT_DYNAMIC_FIELD; |
| } else if (variable is ParameterElement) { |
| errorCode = LanguageCode.IMPLICIT_DYNAMIC_PARAMETER; |
| } else { |
| errorCode = LanguageCode.IMPLICIT_DYNAMIC_VARIABLE; |
| } |
| errorReporter.reportErrorForNode(errorCode, node, [id]); |
| } |
| } |
| |
| void _checkForImplicitDynamicReturn( |
| AstNode functionName, ExecutableElement element) { |
| if (_options.implicitDynamic) { |
| return; |
| } |
| if (element is PropertyAccessorElement && element.isSetter) { |
| return; |
| } |
| if (element.hasImplicitReturnType && element.returnType.isDynamic) { |
| errorReporter.reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_RETURN, |
| functionName, [element.displayName]); |
| } |
| } |
| |
| void _checkForImplicitDynamicType(TypeAnnotation? node) { |
| if (_options.implicitDynamic || |
| node == null || |
| (node is TypeName && node.typeArguments != null)) { |
| return; |
| } |
| DartType type = node.typeOrThrow; |
| if (type is ParameterizedType && |
| type.typeArguments.isNotEmpty && |
| type.typeArguments.any((t) => t.isDynamic)) { |
| errorReporter |
| .reportErrorForNode(LanguageCode.IMPLICIT_DYNAMIC_TYPE, node, [type]); |
| } |
| } |
| |
| /// 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]. |
| void _checkForImportInternalLibrary( |
| ImportDirective directive, ImportElement importElement) { |
| if (_isInSystemLibrary) { |
| return; |
| } |
| |
| var importedLibrary = importElement.importedLibrary; |
| if (importedLibrary == null) { |
| return; |
| } |
| |
| // should be private |
| var sdk = _currentLibrary.context.sourceFactory.dartSdk!; |
| var uri = importedLibrary.source.uri.toString(); |
| var sdkLibrary = sdk.getSdkLibrary(uri); |
| if (sdkLibrary == null || !sdkLibrary.isInternal) { |
| return; |
| } |
| |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, |
| directive.uri, |
| [directive.uri.stringValue]); |
| } |
| |
| /// Check that the given [typeReference] is not a type reference and that then |
| /// the [name] is reference to an instance member. |
| /// |
| /// See [CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER]. |
| void _checkForInstanceAccessToStaticMember( |
| ClassElement? typeReference, Expression? target, SimpleIdentifier name) { |
| if (_isInComment) { |
| // OK, in comment |
| return; |
| } |
| // prepare member Element |
| var element = name.writeOrReadElement; |
| if (element is ExecutableElement) { |
| if (!element.isStatic) { |
| // OK, instance member |
| return; |
| } |
| Element enclosingElement = element.enclosingElement; |
| if (enclosingElement is ExtensionElement) { |
| if (target is ExtensionOverride) { |
| // OK, target is an extension override |
| return; |
| } else if (target is SimpleIdentifier && |
| target.staticElement is ExtensionElement) { |
| return; |
| } else if (target is PrefixedIdentifier && |
| target.staticElement is ExtensionElement) { |
| return; |
| } |
| } else { |
| if (typeReference != null) { |
| // OK, target is a type |
| return; |
| } |
| if (enclosingElement is! ClassElement) { |
| // OK, top-level element |
| return; |
| } |
| } |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, |
| name, |
| [name.name, _getKind(element), element.enclosingElement.name]); |
| } |
| } |
| |
| /// 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]. |
| void _checkForIntNotAssignable(Expression argument) { |
| var staticParameterElement = argument.staticParameterElement; |
| var staticParameterType = staticParameterElement?.type; |
| checkForArgumentTypeNotAssignable(argument, staticParameterType, _intType, |
| CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
| } |
| |
| /// Verify that the given [annotation] isn't defined in a deferred library. |
| /// |
| /// See [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY]. |
| void _checkForInvalidAnnotationFromDeferredLibrary(Annotation annotation) { |
| Identifier nameIdentifier = annotation.name; |
| if (nameIdentifier is PrefixedIdentifier && nameIdentifier.isDeferred) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY, |
| annotation.name); |
| } |
| } |
| |
| /// 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) { |
| if (staticElement.isSynthetic) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, |
| initializer, |
| [fieldName]); |
| } else if (staticElement.isStatic) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, |
| initializer, |
| [fieldName]); |
| } |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, |
| initializer, |
| [fieldName]); |
| return; |
| } |
| } |
| |
| /// 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], |
| /// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY], and |
| /// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC]. |
| void _checkForInvalidInstanceMemberAccess(SimpleIdentifier identifier) { |
| if (_isInComment) { |
| return; |
| } |
| if (!_isInConstructorInitializer && |
| !_enclosingExecutable.inStaticMethod && |
| !_enclosingExecutable.inFactoryConstructor && |
| !_isInInstanceNotLateVariableDeclaration && |
| !_isInStaticVariableDeclaration) { |
| return; |
| } |
| // prepare element |
| var element = identifier.writeOrReadElement; |
| if (!(element is MethodElement || element is PropertyAccessorElement)) { |
| return; |
| } |
| // static element |
| ExecutableElement executableElement = element as ExecutableElement; |
| if (executableElement.isStatic) { |
| return; |
| } |
| // not a class member |
| Element enclosingElement = element.enclosingElement; |
| if (enclosingElement is! ClassElement && |
| enclosingElement is! ExtensionElement) { |
| return; |
| } |
| // qualified method invocation |
| var parent = identifier.parent; |
| if (parent is MethodInvocation) { |
| if (identical(parent.methodName, identifier) && |
| parent.realTarget != null) { |
| return; |
| } |
| } |
| // qualified property access |
| if (parent is PropertyAccess) { |
| if (identical(parent.propertyName, identifier)) { |
| return; |
| } |
| } |
| if (parent is PrefixedIdentifier) { |
| if (identical(parent.identifier, identifier)) { |
| return; |
| } |
| } |
| |
| if (_enclosingExecutable.inStaticMethod) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier); |
| } else if (_enclosingExecutable.inFactoryConstructor) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier); |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER, |
| identifier, |
| [identifier.name]); |
| } |
| } |
| |
| /// Check to see whether the given function [body] has a modifier associated |
| /// with it, and report it as an error if it does. |
| void _checkForInvalidModifierOnBody( |
| FunctionBody body, CompileTimeErrorCode errorCode) { |
| var keyword = body.keyword; |
| if (keyword != null) { |
| errorReporter.reportErrorForToken(errorCode, keyword, [keyword.lexeme]); |
| } |
| } |
| |
| /// Verify that the usage of the given 'this' is valid. |
| /// |
| /// See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS]. |
| void _checkForInvalidReferenceToThis(ThisExpression expression) { |
| if (!_thisAccessTracker.hasAccess) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, expression); |
| } |
| } |
| |
| void _checkForLateFinalFieldWithConstConstructor(FieldDeclaration node) { |
| if (node.isStatic) return; |
| |
| var variableList = node.fields; |
| if (!variableList.isFinal) return; |
| |
| var lateKeyword = variableList.lateKeyword; |
| if (lateKeyword == null) return; |
| |
| var hasConstConstructor = |
| _enclosingClass!.constructors.any((c) => c.isConst); |
| if (!hasConstConstructor) return; |
| |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR, |
| lateKeyword, |
| ); |
| } |
| |
| void _checkForListConstructor( |
| InstanceCreationExpression node, InterfaceType type) { |
| if (!_isNonNullableByDefault) return; |
| |
| if (node.constructorName.name == null && type.isDartCoreList) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DEFAULT_LIST_CONSTRUCTOR, |
| node.constructorName, |
| ); |
| } |
| } |
| |
| /// Verify that the elements of the given list [literal] are subtypes of the |
| /// list's static type. |
| /// |
| /// See [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], and |
| /// [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE]. |
| void _checkForListElementTypeNotAssignable(ListLiteral literal) { |
| // Determine the list's element type. We base this on the static type and |
| // not the literal's type arguments because in strong mode, the type |
| // arguments may be inferred. |
| DartType listType = literal.typeOrThrow; |
| assert(listType is InterfaceTypeImpl); |
| |
| List<DartType> typeArguments = |
| (listType as InterfaceTypeImpl).typeArguments; |
| assert(typeArguments.length == 1); |
| |
| DartType listElementType = typeArguments[0]; |
| |
| // Check every list element. |
| var verifier = LiteralElementVerifier( |
| _typeProvider, |
| typeSystem, |
| errorReporter, |
| checkForUseOfVoidResult, |
| forList: true, |
| elementType: listElementType, |
| featureSet: _featureSet!, |
| ); |
| for (CollectionElement element in literal.elements) { |
| verifier.verify(element); |
| } |
| } |
| |
| void _checkForMainFunction(SimpleIdentifier nameNode) { |
| if (!_currentLibrary.isNonNullableByDefault) { |
| return; |
| } |
| |
| var element = nameNode.staticElement!; |
| |
| // We should only check exported declarations, i.e. top-level. |
| if (element.enclosingElement is! CompilationUnitElement) { |
| return; |
| } |
| |
| if (element.displayName != 'main') { |
| return; |
| } |
| |
| if (element is! FunctionElement) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MAIN_IS_NOT_FUNCTION, |
| nameNode, |
| ); |
| return; |
| } |
| |
| var functionDeclaration = nameNode.parent as FunctionDeclaration; |
| var functionExpression = functionDeclaration.functionExpression; |
| var parameters = functionExpression.parameters!.parameters; |
| var positional = parameters.where((e) => e.isPositional).toList(); |
| var requiredPositional = |
| parameters.where((e) => e.isRequiredPositional).toList(); |
| |
| if (requiredPositional.length > 2) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS, |
| nameNode, |
| ); |
| } |
| |
| if (parameters.any((e) => e.isRequiredNamed)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS, |
| nameNode, |
| ); |
| } |
| |
| if (positional.isNotEmpty) { |
| var first = positional.first; |
| var type = first.declaredElement!.type; |
| var listOfString = _typeProvider.listType(_typeProvider.stringType); |
| if (!typeSystem.isSubtypeOf(listOfString, type)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE, |
| first.notDefault.typeOrSelf, |
| ); |
| } |
| } |
| } |
| |
| void _checkForMapTypeNotAssignable(SetOrMapLiteral literal) { |
| // Determine the map's key and value types. We base this on the static type |
| // and not the literal's type arguments because in strong mode, the type |
| // arguments may be inferred. |
| DartType mapType = literal.typeOrThrow; |
| assert(mapType is InterfaceTypeImpl); |
| |
| List<DartType> typeArguments = (mapType as InterfaceTypeImpl).typeArguments; |
| // It is possible for the number of type arguments to be inconsistent when |
| // the literal is ambiguous and a non-map type was selected. |
| // TODO(brianwilkerson) Unify this and _checkForSetElementTypeNotAssignable3 |
| // to better handle recovery situations. |
| if (typeArguments.length == 2) { |
| DartType keyType = typeArguments[0]; |
| DartType valueType = typeArguments[1]; |
| |
| var verifier = LiteralElementVerifier( |
| _typeProvider, |
| typeSystem, |
| errorReporter, |
| checkForUseOfVoidResult, |
| forMap: true, |
| mapKeyType: keyType, |
| mapValueType: valueType, |
| featureSet: _featureSet!, |
| ); |
| for (CollectionElement element in literal.elements) { |
| verifier.verify(element); |
| } |
| } |
| } |
| |
| /// 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. |
| void _checkForMissingEnumConstantInSwitch(SwitchStatement statement) { |
| // TODO(brianwilkerson) This needs to be checked after constant values have |
| // been computed. |
| var expressionType = statement.expression.staticType; |
| |
| var hasCaseNull = false; |
| if (expressionType is InterfaceType) { |
| var enumElement = expressionType.element; |
| if (enumElement.isEnum) { |
| var constantNames = enumElement.fields |
| .where((field) => field.isStatic && !field.isSynthetic) |
| .map((field) => field.name) |
| .toSet(); |
| |
| for (var member in statement.members) { |
| if (member is SwitchCase) { |
| var expression = member.expression.unParenthesized; |
| if (expression is NullLiteral) { |
| hasCaseNull = true; |
| } else { |
| var constantName = _getConstantName(expression); |
| constantNames.remove(constantName); |
| } |
| } |
| if (member is SwitchDefault) { |
| return; |
| } |
| } |
| |
| for (var constantName in constantNames) { |
| int offset = statement.offset; |
| int end = statement.rightParenthesis.end; |
| errorReporter.reportErrorForOffset( |
| StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, |
| offset, |
| end - offset, |
| [constantName], |
| ); |
| } |
| |
| if (typeSystem.isNullable(expressionType) && !hasCaseNull) { |
| int offset = statement.offset; |
| int end = statement.rightParenthesis.end; |
| errorReporter.reportErrorForOffset( |
| StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, |
| offset, |
| end - offset, |
| ['null'], |
| ); |
| } |
| } |
| } |
| } |
| |
| void _checkForMissingJSLibAnnotation(Annotation node) { |
| if (node.elementAnnotation?.isJS ?? false) { |
| if (_currentLibrary.hasJS != true) { |
| errorReporter.reportErrorForNode( |
| HintCode.MISSING_JS_LIB_ANNOTATION, node); |
| } |
| } |
| } |
| |
| /// 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_CLASS_DECLARES_CONSTRUCTOR]. |
| bool _checkForMixinClassDeclaresConstructor( |
| TypeName mixinName, ClassElement mixinElement) { |
| for (ConstructorElement constructor in mixinElement.constructors) { |
| if (!constructor.isSynthetic && !constructor.isFactory) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MIXIN_CLASS_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) { |
| var mixinSupertype = mixinElement.supertype; |
| if (mixinSupertype == null || mixinSupertype.isDartCoreObject) { |
| var mixins = mixinElement.mixins; |
| if (mixins.isEmpty || |
| mixinElement.isMixinApplication && mixins.length < 2) { |
| return false; |
| } |
| } |
| |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, |
| mixinName, |
| [mixinElement.name], |
| ); |
| return true; |
| } |
| |
| /// Check that superclass constrains for the mixin type of [mixinName] at |
| /// the [mixinIndex] position in the mixins list are satisfied by the |
| /// [_enclosingClass], or a previous mixin. |
| bool _checkForMixinSuperclassConstraints(int mixinIndex, TypeName mixinName) { |
| InterfaceType mixinType = mixinName.type as InterfaceType; |
| for (var constraint in mixinType.superclassConstraints) { |
| var superType = _enclosingClass!.supertype as InterfaceTypeImpl; |
| if (_currentLibrary.isNonNullableByDefault) { |
| superType = superType.withNullability(NullabilitySuffix.none); |
| } |
| |
| bool isSatisfied = typeSystem.isSubtypeOf(superType, constraint); |
| if (!isSatisfied) { |
| for (int i = 0; i < mixinIndex && !isSatisfied; i++) { |
| isSatisfied = |
| typeSystem.isSubtypeOf(_enclosingClass!.mixins[i], constraint); |
| } |
| } |
| if (! |