| // 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:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart' |
| show Variance; |
| import 'package:analyzer/dart/analysis/features.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/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/file_state.dart'; |
| import 'package:analyzer/src/dart/analysis/results.dart'; |
| import 'package:analyzer/src/dart/analysis/unit_analysis.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/extensions.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/non_covariant_type_parameter_position.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/element/well_bounded.dart'; |
| import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/diagnostic/diagnostic.dart'; |
| import 'package:analyzer/src/diagnostic/diagnostic_factory.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/error/const_argument_verifier.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/super_formal_parameters_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_core.dart'; |
| import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode; |
| import 'package:analyzer/src/summary2/macro_application_error.dart'; |
| import 'package:analyzer/src/summary2/macro_type_location.dart'; |
| import 'package:analyzer/src/utilities/extensions/element.dart'; |
| import 'package:analyzer/src/utilities/extensions/object.dart'; |
| import 'package:analyzer/src/utilities/extensions/string.dart'; |
| import 'package:collection/collection.dart'; |
| import 'package:macros/macros.dart' as macro; |
| |
| 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; |
| |
| /// The number of enclosing [CatchClause] in this executable. |
| int catchClauseLevel = 0; |
| |
| 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 { |
| 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 InterfaceElement || 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. |
| 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 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; |
| |
| /// The class containing the AST nodes being visited, or `null` if we are not |
| /// in the scope of a class. |
| InterfaceElement? _enclosingClass; |
| |
| /// The element of the extension being visited, or `null` if we are not |
| /// in the scope of an extension. |
| ExtensionElement? _enclosingExtension; |
| |
| /// Whether the current location has access to `this`. |
| bool _hasAccessToThis = false; |
| |
| /// 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 LibraryVerificationContext libraryVerificationContext; |
| final RequiredParametersVerifier _requiredParametersVerifier; |
| final ConstArgumentsVerifier _constArgumentsVerifier; |
| final DuplicateDefinitionVerifier _duplicateDefinitionVerifier; |
| final UseResultVerifier _checkUseVerifier; |
| late final TypeArgumentsVerifier _typeArgumentsVerifier; |
| late final ReturnTypeVerifier _returnTypeVerifier; |
| final TypeSystemOperations typeSystemOperations; |
| |
| /// Initialize a newly created error verifier. |
| ErrorVerifier( |
| this.errorReporter, |
| this._currentLibrary, |
| this._typeProvider, |
| this._inheritanceManager, |
| this.libraryVerificationContext, |
| this.options, { |
| required this.typeSystemOperations, |
| }) : _uninstantiatedBoundChecker = |
| _UninstantiatedBoundChecker(errorReporter), |
| _checkUseVerifier = UseResultVerifier(errorReporter), |
| _requiredParametersVerifier = RequiredParametersVerifier(errorReporter), |
| _constArgumentsVerifier = ConstArgumentsVerifier(errorReporter), |
| _duplicateDefinitionVerifier = DuplicateDefinitionVerifier( |
| _inheritanceManager, |
| _currentLibrary, |
| errorReporter, |
| libraryVerificationContext.duplicationDefinitionContext, |
| ) { |
| _isInSystemLibrary = _currentLibrary.source.uri.isScheme('dart'); |
| _isInStaticVariableDeclaration = false; |
| _isInConstructorInitializer = false; |
| _intType = _typeProvider.intType; |
| typeSystem = _currentLibrary.typeSystem; |
| _typeArgumentsVerifier = |
| TypeArgumentsVerifier(options, _currentLibrary, errorReporter); |
| _returnTypeVerifier = ReturnTypeVerifier( |
| typeProvider: _typeProvider as TypeProviderImpl, |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| strictCasts: strictCasts, |
| ); |
| } |
| |
| InterfaceElement? 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(InterfaceElement? interfaceElement) { |
| assert(_enclosingClass == null); |
| assert(_enclosingExecutable.element == null); |
| } |
| |
| @override |
| bool get strictCasts => options.strictCasts; |
| |
| /// 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 && |
| _isDartFfiLibrary(superClass.library) && |
| superClass.name == 'Struct'; |
| } |
| |
| /// 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 _isEnclosingClassFfiUnion { |
| var superClass = _enclosingClass?.supertype?.element; |
| return superClass != null && |
| _isDartFfiLibrary(superClass.library) && |
| superClass.name == 'Union'; |
| } |
| |
| @override |
| List<DiagnosticMessage> computeWhyNotPromotedMessages( |
| SyntacticEntity errorEntity, |
| Map<DartType, NonPromotionReason>? whyNotPromoted) { |
| return []; |
| } |
| |
| @override |
| void visitAnnotation(Annotation node) { |
| _checkForInvalidAnnotationFromDeferredLibrary(node); |
| _requiredParametersVerifier.visitAnnotation(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); |
| |
| _constArgumentsVerifier.visitAssignmentExpression(node); |
| super.visitAssignmentExpression(node); |
| } |
| |
| @override |
| void visitAwaitExpression(AwaitExpression node) { |
| if (!_enclosingExecutable.isAsynchronous) { |
| errorReporter.atToken( |
| node.awaitKeyword, |
| CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, |
| ); |
| } |
| checkForUseOfVoidResult(node.expression); |
| _checkForAwaitInLateLocalVariableInitializer(node); |
| _checkForAwaitOfIncompatibleType(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); |
| _constArgumentsVerifier.visitBinaryExpression(node); |
| |
| super.visitBinaryExpression(node); |
| } |
| |
| @override |
| void visitBlock(Block node) { |
| _withHiddenElements(node.statements, () { |
| _duplicateDefinitionVerifier.checkStatements(node.statements); |
| super.visitBlock(node); |
| }); |
| } |
| |
| @override |
| void visitBlockFunctionBody(BlockFunctionBody node) { |
| var oldHasAccessToThis = _hasAccessToThis; |
| try { |
| _hasAccessToThis = _computeThisAccessForFunctionBody(node); |
| super.visitBlockFunctionBody(node); |
| } finally { |
| _hasAccessToThis = oldHasAccessToThis; |
| } |
| } |
| |
| @override |
| void visitBreakStatement(BreakStatement node) { |
| var labelNode = node.label; |
| if (labelNode != null) { |
| var labelElement = labelNode.staticElement; |
| if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) { |
| errorReporter.atNode( |
| labelNode, |
| CompileTimeErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, |
| ); |
| } |
| } |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| _duplicateDefinitionVerifier.checkCatchClause(node); |
| try { |
| _enclosingExecutable.catchClauseLevel++; |
| _checkForTypeAnnotationDeferredClass(node.exceptionType); |
| super.visitCatchClause(node); |
| } finally { |
| _enclosingExecutable.catchClauseLevel--; |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(covariant ClassDeclarationImpl node) { |
| try { |
| var element = node.declaredElement!; |
| |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| |
| _isInNativeClass = node.nativeClause != null; |
| |
| var augmented = element.augmented; |
| var declarationElement = augmented.declaration; |
| _enclosingClass = declarationElement; |
| |
| List<ClassMember> members = node.members; |
| _duplicateDefinitionVerifier.checkClass(node); |
| if (!declarationElement.isDartCoreFunctionImpl) { |
| _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) { |
| var moreChecks = _checkClassInheritance( |
| node, superclass, withClause, implementsClause); |
| if (moreChecks) { |
| _checkForNoDefaultSuperConstructorImplicit(element, augmented); |
| } |
| } |
| |
| if (node.nativeClause == null) { |
| libraryVerificationContext.constructorFieldsVerifier |
| .addConstructors(errorReporter, augmented, members); |
| } |
| |
| _checkForConflictingClassMembers(); |
| _checkForFinalNotInitializedInClass(element, members); |
| _checkForBadFunctionUse( |
| superclass: node.extendsClause?.superclass, |
| withClause: node.withClause, |
| implementsClause: node.implementsClause, |
| ); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForMixinClassErrorCodes(node, members, superclass, withClause); |
| _reportMacroDiagnostics(element); |
| |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkStaticAccessors(declarationElement.accessors); |
| |
| super.visitClassDeclaration(node); |
| } finally { |
| _isInNativeClass = false; |
| _enclosingClass = null; |
| } |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| try { |
| _enclosingClass = node.declaredElement as ClassElementImpl; |
| _checkClassInheritance( |
| node, node.superclass, node.withClause, node.implementsClause); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForMixinClassErrorCodes( |
| node, List.empty(), node.superclass, node.withClause); |
| _checkForBadFunctionUse( |
| superclass: node.superclass, |
| withClause: node.withClause, |
| implementsClause: node.implementsClause, |
| ); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| } finally { |
| _enclosingClass = null; |
| } |
| super.visitClassTypeAlias(node); |
| } |
| |
| @override |
| void visitComment(Comment node) { |
| _isInComment = true; |
| try { |
| super.visitComment(node); |
| } finally { |
| _isInComment = false; |
| } |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| var element = node.declaredElement as CompilationUnitElement; |
| _featureSet = node.featureSet; |
| _duplicateDefinitionVerifier.checkUnit(node); |
| _checkForDeferredPrefixCollisions(node); |
| _checkForIllegalLanguageOverride(node); |
| |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkStaticAccessors(element.accessors); |
| |
| super.visitCompilationUnit(node); |
| _featureSet = null; |
| } |
| |
| @override |
| void visitConstructorDeclaration( |
| covariant ConstructorDeclarationImpl node, |
| ) { |
| var element = node.declaredElement!; |
| _withEnclosingExecutable(element, () { |
| _checkForNonConstGenerativeEnumConstructor(node); |
| _checkForInvalidModifierOnBody( |
| node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR); |
| if (!_checkForConstConstructorWithNonConstSuper(node)) { |
| _checkForConstConstructorWithNonFinalField(node, element); |
| } |
| _checkForRedirectingConstructorErrorCodes(node); |
| _checkForConflictingInitializerErrorCodes(node); |
| _checkForRecursiveConstructorRedirect(node, element); |
| if (!_checkForRecursiveFactoryRedirect(node, element)) { |
| _checkForAllRedirectConstructorErrorCodes(node); |
| } |
| _checkForUndefinedConstructorInInitializerImplicit(node); |
| _checkForReturnInGenerativeConstructor(node); |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| _reportMacroDiagnostics(element); |
| 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.token, staticElement); |
| } |
| super.visitConstructorFieldInitializer(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitConstructorReference(ConstructorReference node) { |
| _typeArgumentsVerifier.checkConstructorReference(node); |
| _checkForInvalidGenerativeConstructorReference(node.constructorName); |
| } |
| |
| @override |
| void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| var defaultValue = node.defaultValue; |
| if (defaultValue != null) { |
| checkForAssignableExpressionAtType( |
| defaultValue, |
| defaultValue.typeOrThrow, |
| node.declaredElement!.type, |
| CompileTimeErrorCode.INVALID_ASSIGNMENT, |
| ); |
| } |
| |
| super.visitDefaultFormalParameter(node); |
| } |
| |
| @override |
| void visitEnumConstantDeclaration(EnumConstantDeclaration node) { |
| _requiredParametersVerifier.visitEnumConstantDeclaration(node); |
| _typeArgumentsVerifier.checkEnumConstantDeclaration(node); |
| super.visitEnumConstantDeclaration(node); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| try { |
| var element = node.declaredElement as EnumElementImpl; |
| |
| var augmented = element.augmented; |
| _enclosingClass = element; |
| _duplicateDefinitionVerifier.checkEnum(node); |
| |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME); |
| _checkForConflictingEnumTypeVariableErrorCodes(element); |
| var implementsClause = node.implementsClause; |
| var withClause = node.withClause; |
| |
| if (implementsClause != null || withClause != null) { |
| _checkClassInheritance(node, null, withClause, implementsClause); |
| } |
| |
| if (!element.isAugmentation) { |
| if (element.augmented.constants.isEmpty) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.ENUM_WITHOUT_CONSTANTS, |
| ); |
| } |
| } |
| |
| var members = node.members; |
| libraryVerificationContext.constructorFieldsVerifier |
| .addConstructors(errorReporter, augmented, members); |
| _checkForFinalNotInitializedInClass(element, members); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForEnumInstantiatedToBoundsIsNotWellBounded(node, element); |
| |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkStaticAccessors(element.accessors); |
| |
| super.visitEnumDeclaration(node); |
| } finally { |
| _enclosingClass = null; |
| } |
| } |
| |
| @override |
| void visitExportDirective(ExportDirective node) { |
| var exportElement = node.element; |
| if (exportElement != null) { |
| var exportedLibrary = exportElement.exportedLibrary; |
| _checkForAmbiguousExport(node, exportElement, exportedLibrary); |
| _checkForExportInternalLibrary(node, exportElement); |
| } |
| super.visitExportDirective(node); |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| var oldHasAccessToThis = _hasAccessToThis; |
| try { |
| _hasAccessToThis = _computeThisAccessForFunctionBody(node); |
| _returnTypeVerifier.verifyExpressionFunctionBody(node); |
| super.visitExpressionFunctionBody(node); |
| } finally { |
| _hasAccessToThis = oldHasAccessToThis; |
| } |
| } |
| |
| @override |
| void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) { |
| var element = node.declaredElement!; |
| _enclosingExtension = element; |
| _duplicateDefinitionVerifier.checkExtension(node); |
| _checkForConflictingExtensionTypeVariableErrorCodes(); |
| _checkForFinalNotInitializedInClass(element, node.members); |
| |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkExtension(element); |
| |
| var name = node.name; |
| if (name != null) { |
| _checkForBuiltInIdentifierAsName( |
| name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_NAME); |
| } |
| super.visitExtensionDeclaration(node); |
| _enclosingExtension = null; |
| } |
| |
| @override |
| void visitExtensionTypeDeclaration( |
| covariant ExtensionTypeDeclarationImpl node, |
| ) { |
| try { |
| var element = node.declaredElement!; |
| var augmented = element.augmented; |
| var declarationElement = augmented.declaration; |
| _enclosingClass = declarationElement; |
| |
| _checkForBuiltInIdentifierAsName(node.name, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_EXTENSION_TYPE_NAME); |
| _checkForConflictingExtensionTypeTypeVariableErrorCodes(element); |
| |
| var members = node.members; |
| _duplicateDefinitionVerifier.checkExtensionType(node, declarationElement); |
| _checkForRepeatedType(node.implementsClause?.interfaces, |
| CompileTimeErrorCode.IMPLEMENTS_REPEATED); |
| _checkForConflictingClassMembers(); |
| _checkForConflictingGenerics(node); |
| libraryVerificationContext.constructorFieldsVerifier |
| .addConstructors(errorReporter, augmented, members); |
| _checkForNonCovariantTypeParameterPositionInRepresentationType( |
| node, element); |
| _checkForExtensionTypeRepresentationDependsOnItself(node, element); |
| _checkForExtensionTypeRepresentationTypeBottom(node, element); |
| _checkForExtensionTypeImplementsDeferred(node); |
| _checkForExtensionTypeImplementsItself(node, element); |
| _checkForExtensionTypeMemberConflicts( |
| node: node, |
| element: element, |
| ); |
| _checkForExtensionTypeWithAbstractMember(node); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| |
| var interface = _inheritanceManager.getInterface(element); |
| GetterSetterTypesVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| ).checkExtensionType(element, interface); |
| |
| super.visitExtensionTypeDeclaration(node); |
| } finally { |
| _enclosingClass = null; |
| } |
| } |
| |
| @override |
| void visitFieldDeclaration(covariant FieldDeclarationImpl node) { |
| var fields = node.fields; |
| _isInStaticVariableDeclaration = node.isStatic; |
| _isInInstanceNotLateVariableDeclaration = |
| !node.isStatic && !node.fields.isLate; |
| if (!_isInStaticVariableDeclaration) { |
| if (fields.isConst) { |
| errorReporter.atToken( |
| fields.keyword!, |
| CompileTimeErrorCode.CONST_INSTANCE_FIELD, |
| ); |
| } |
| } |
| var oldHasAccessToThis = _hasAccessToThis; |
| try { |
| _hasAccessToThis = !node.isStatic && node.fields.isLate; |
| _checkForExtensionTypeDeclaresInstanceField(node); |
| _checkForNotInitializedNonNullableStaticField(node); |
| _checkForWrongTypeParameterVarianceInField(node); |
| _checkForLateFinalFieldWithConstConstructor(node); |
| _checkForNonFinalFieldInEnum(node); |
| |
| for (var field in fields.variables) { |
| var element = field.declaredElement; |
| element as FieldElementImpl; |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| _reportMacroDiagnostics(element); |
| } |
| |
| super.visitFieldDeclaration(node); |
| } finally { |
| _isInStaticVariableDeclaration = false; |
| _isInInstanceNotLateVariableDeclaration = false; |
| _hasAccessToThis = oldHasAccessToThis; |
| } |
| } |
| |
| @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.name, fieldElement); |
| } |
| } |
| super.visitFieldFormalParameter(node); |
| } |
| |
| @override |
| void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { |
| DeclaredIdentifier loopVariable = node.loopVariable; |
| if (_checkForEachParts(node, loopVariable.declaredElement)) { |
| if (loopVariable.isConst) { |
| errorReporter.atToken( |
| loopVariable.keyword!, |
| CompileTimeErrorCode.FOR_IN_WITH_CONST_VARIABLE, |
| ); |
| } |
| } |
| super.visitForEachPartsWithDeclaration(node); |
| } |
| |
| @override |
| void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) { |
| SimpleIdentifier identifier = node.identifier; |
| if (_checkForEachParts(node, identifier.staticElement)) { |
| _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(covariant FunctionDeclarationImpl node) { |
| var element = node.declaredElement!; |
| if (element.enclosingElement is! CompilationUnitElement) { |
| _hiddenElements!.declare(element); |
| } |
| |
| _withEnclosingExecutable(element, () { |
| TypeAnnotation? returnType = node.returnType; |
| if (node.isSetter) { |
| FunctionExpression functionExpression = node.functionExpression; |
| _checkForWrongNumberOfParametersForSetter( |
| node.name, functionExpression.parameters); |
| _checkForNonVoidReturnTypeForSetter(returnType); |
| } |
| _checkForTypeAnnotationDeferredClass(returnType); |
| _returnTypeVerifier.verifyReturnType(returnType); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForMainFunction2(node); |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| _reportMacroDiagnostics(element); |
| 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); |
| _constArgumentsVerifier.visitFunctionExpressionInvocation(node); |
| _checkUseVerifier.checkFunctionExpressionInvocation(node); |
| super.visitFunctionExpressionInvocation(node); |
| } |
| |
| @override |
| void visitFunctionReference(FunctionReference node) { |
| _typeArgumentsVerifier.checkFunctionReference(node); |
| super.visitFunctionReference(node); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForTypeAliasCannotReferenceItself( |
| node.name, node.declaredElement as TypeAliasElementImpl); |
| super.visitFunctionTypeAlias(node); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| bool old = _isInFunctionTypedFormalParameter; |
| _isInFunctionTypedFormalParameter = true; |
| try { |
| _checkForTypeAnnotationDeferredClass(node.returnType); |
| |
| super.visitFunctionTypedFormalParameter(node); |
| } finally { |
| _isInFunctionTypedFormalParameter = old; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(covariant GenericTypeAliasImpl node) { |
| var element = node.declaredElement as TypeAliasElementImpl; |
| _checkForBuiltInIdentifierAsName( |
| node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME); |
| _checkForMainFunction1(node.name, node.declaredElement!); |
| _checkForTypeAliasCannotReferenceItself( |
| node.name, node.declaredElement as TypeAliasElementImpl); |
| _reportMacroDiagnostics(element); |
| super.visitGenericTypeAlias(node); |
| } |
| |
| @override |
| void visitGuardedPattern(covariant GuardedPatternImpl node) { |
| _withHiddenElementsGuardedPattern(node, () { |
| node.pattern.accept(this); |
| }); |
| node.whenClause?.accept(this); |
| } |
| |
| @override |
| void visitImportDirective(ImportDirective node) { |
| var importElement = node.element; |
| if (node.prefix != null) { |
| _checkForBuiltInIdentifierAsName(node.prefix!.token, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_PREFIX_NAME); |
| } |
| if (importElement != null) { |
| _checkForImportInternalLibrary(node, importElement); |
| if (importElement.prefix is DeferredImportElementPrefix) { |
| _checkForDeferredImportOfExtensions(node, importElement); |
| } |
| } |
| super.visitImportDirective(node); |
| } |
| |
| @override |
| void visitImportPrefixReference(ImportPrefixReference node) { |
| _checkForReferenceBeforeDeclaration( |
| nameToken: node.name, |
| element: node.element, |
| ); |
| } |
| |
| @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; |
| NamedType namedType = constructorName.type; |
| DartType type = namedType.typeOrThrow; |
| if (type is InterfaceType) { |
| _checkForConstOrNewWithAbstractClass(node, namedType, type); |
| _checkForInvalidGenerativeConstructorReference(constructorName); |
| _checkForConstOrNewWithMixin(node, namedType, type); |
| _requiredParametersVerifier.visitInstanceCreationExpression(node); |
| _constArgumentsVerifier.visitInstanceCreationExpression(node); |
| _checkUseVerifier.checkInstanceCreationExpression(node); |
| if (node.isConst) { |
| _checkForConstWithNonConst(node); |
| _checkForConstWithUndefinedConstructor( |
| node, constructorName, namedType); |
| _checkForConstDeferredClass(node, constructorName, namedType); |
| } else { |
| _checkForNewWithUndefinedConstructor(node, constructorName, namedType); |
| } |
| } |
| 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 visitLibraryDirective(covariant LibraryDirectiveImpl node) { |
| if (node.element case var element?) { |
| _reportMacroDiagnostics(element); |
| } |
| |
| super.visitLibraryDirective(node); |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral node) { |
| _typeArgumentsVerifier.checkListLiteral(node); |
| _checkForListElementTypeNotAssignable(node); |
| |
| super.visitListLiteral(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(covariant MethodDeclarationImpl node) { |
| var element = node.declaredElement!; |
| _withEnclosingExecutable(element, () { |
| var returnType = node.returnType; |
| 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); |
| _checkForWrongTypeParameterVarianceInMethod(node); |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| _reportMacroDiagnostics(element); |
| 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); |
| _constArgumentsVerifier.visitMethodInvocation(node); |
| _checkUseVerifier.checkMethodInvocation(node); |
| super.visitMethodInvocation(node); |
| } |
| |
| @override |
| void visitMixinDeclaration(covariant MixinDeclarationImpl node) { |
| // TODO(scheglov): Verify for all mixin errors. |
| try { |
| var element = node.declaredElement!; |
| |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| |
| var augmented = element.augmented; |
| var declarationElement = augmented.declaration; |
| _enclosingClass = declarationElement; |
| |
| 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(element, members); |
| _checkForMainFunction1(node.name, declarationElement); |
| _checkForWrongTypeParameterVarianceInSuperinterfaces(); |
| _reportMacroDiagnostics(element); |
| // _checkForBadFunctionUse(node); |
| super.visitMixinDeclaration(node); |
| } finally { |
| _enclosingClass = null; |
| } |
| } |
| |
| @override |
| void visitNamedType(NamedType node) { |
| _checkForAmbiguousImport( |
| name: node.name2, |
| element: node.element, |
| ); |
| _checkForTypeParameterReferencedByStatic( |
| name: node.name2, |
| element: node.element, |
| ); |
| _typeArgumentsVerifier.checkNamedType(node); |
| super.visitNamedType(node); |
| } |
| |
| @override |
| void visitNativeClause(NativeClause node) { |
| // TODO(brianwilkerson): Figure out the right rule for when 'native' is |
| // allowed. |
| if (!_isInSystemLibrary) { |
| errorReporter.atNode( |
| node, |
| ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, |
| ); |
| } |
| super.visitNativeClause(node); |
| } |
| |
| @override |
| void visitNativeFunctionBody(NativeFunctionBody node) { |
| _checkForNativeFunctionBodyInNonSdkCode(node); |
| super.visitNativeFunctionBody(node); |
| } |
| |
| @override |
| void visitPatternVariableDeclarationStatement( |
| covariant PatternVariableDeclarationStatementImpl node, |
| ) { |
| super.visitPatternVariableDeclarationStatement(node); |
| for (var variable in node.declaration.elements) { |
| _hiddenElements?.declare(variable); |
| } |
| } |
| |
| @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); |
| _constArgumentsVerifier.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); |
| super.visitSimpleFormalParameter(node); |
| } |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| _checkForAmbiguousImport( |
| name: node.token, |
| element: node.writeOrReadElement, |
| ); |
| _checkForReferenceBeforeDeclaration( |
| nameToken: node.token, |
| element: node.staticElement, |
| ); |
| _checkForInvalidInstanceMemberAccess(node); |
| _checkForTypeParameterReferencedByStatic( |
| name: node.token, |
| element: node.staticElement, |
| ); |
| 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, |
| enclosingConstructor: _enclosingExecutable.element.ifTypeOrNull(), |
| ); |
| _constArgumentsVerifier.visitSuperConstructorInvocation(node); |
| _isInConstructorInitializer = true; |
| try { |
| _checkForExtensionTypeConstructorWithSuperInvocation(node); |
| super.visitSuperConstructorInvocation(node); |
| } finally { |
| _isInConstructorInitializer = false; |
| } |
| } |
| |
| @override |
| void visitSuperFormalParameter(SuperFormalParameter node) { |
| super.visitSuperFormalParameter(node); |
| |
| if (_enclosingClass is ExtensionTypeElement) { |
| errorReporter.atToken( |
| node.superKeyword, |
| CompileTimeErrorCode |
| .EXTENSION_TYPE_CONSTRUCTOR_WITH_SUPER_FORMAL_PARAMETER, |
| ); |
| return; |
| } |
| |
| var constructor = node.parentFormalParameterList.parent; |
| if (!(constructor is ConstructorDeclaration && |
| constructor.isNonRedirectingGenerative)) { |
| errorReporter.atToken( |
| node.superKeyword, |
| CompileTimeErrorCode.INVALID_SUPER_FORMAL_PARAMETER_LOCATION, |
| ); |
| return; |
| } |
| |
| var element = node.declaredElement as SuperFormalParameterElementImpl; |
| var superParameter = element.superConstructorParameter; |
| |
| if (superParameter == null) { |
| errorReporter.atToken( |
| node.name, |
| node.isNamed |
| ? CompileTimeErrorCode |
| .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_NAMED |
| : CompileTimeErrorCode |
| .SUPER_FORMAL_PARAMETER_WITHOUT_ASSOCIATED_POSITIONAL, |
| ); |
| return; |
| } |
| |
| if (!_currentLibrary.typeSystem |
| .isSubtypeOf(element.type, superParameter.type)) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode |
| .SUPER_FORMAL_PARAMETER_TYPE_IS_NOT_SUBTYPE_OF_ASSOCIATED, |
| arguments: [element.type, superParameter.type], |
| ); |
| } |
| } |
| |
| @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 visitSwitchExpression(SwitchExpression node) { |
| checkForUseOfVoidResult(node.expression); |
| super.visitSwitchExpression(node); |
| } |
| |
| @override |
| void visitSwitchPatternCase(SwitchPatternCase node) { |
| _withHiddenElements(node.statements, () { |
| _duplicateDefinitionVerifier.checkStatements(node.statements); |
| super.visitSwitchPatternCase(node); |
| }); |
| } |
| |
| @override |
| void visitSwitchStatement(SwitchStatement node) { |
| checkForUseOfVoidResult(node.expression); |
| _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, true); |
| |
| for (var variable in node.variables.variables) { |
| var element = variable.declaredElement; |
| element as TopLevelVariableElementImpl; |
| _checkForMainFunction1(variable.name, element); |
| _checkAugmentations( |
| augmentKeyword: node.augmentKeyword, |
| element: element, |
| ); |
| _reportMacroDiagnostics(element); |
| } |
| |
| 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 visitTypeParameter(TypeParameter node) { |
| _checkForBuiltInIdentifierAsName(node.name, |
| CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME); |
| _checkForTypeAnnotationDeferredClass(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) { |
| var nameToken = node.name; |
| var initializerNode = node.initializer; |
| // do checks |
| _checkForAbstractOrExternalVariableInitializer(node); |
| // visit initializer |
| String name = nameToken.lexeme; |
| _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(); |
| } |
| |
| void _checkAugmentations<T extends ElementImpl>({ |
| required Token? augmentKeyword, |
| required T element, |
| }) { |
| if (augmentKeyword == null) { |
| return; |
| } |
| |
| if (element is AugmentableElement<T>) { |
| var augmentationTarget = element.augmentationTarget; |
| if (augmentationTarget == null) { |
| errorReporter.atToken( |
| augmentKeyword, |
| CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, |
| ); |
| return; |
| } |
| } |
| } |
| |
| /// Checks the class for problems with the superclass, mixins, or implemented |
| /// interfaces. |
| /// |
| /// Returns `false` if a severe hierarchy error was found, so that further |
| /// checking is not useful. |
| bool _checkClassInheritance( |
| NamedCompilationUnitMember node, |
| NamedType? 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)) { |
| _checkForExtendsDeferredClass(superclass); |
| _checkForRepeatedType(implementsClause?.interfaces, |
| CompileTimeErrorCode.IMPLEMENTS_REPEATED); |
| _checkImplementsSuperClass(implementsClause); |
| _checkMixinsSuperClass(withClause); |
| _checkForMixinWithConflictingPrivateMember(withClause, superclass); |
| _checkForConflictingGenerics(node); |
| _checkForBaseClassOrMixinImplementedOutsideOfLibrary(implementsClause); |
| _checkForInterfaceClassOrMixinSuperclassOutsideOfLibrary( |
| superclass, withClause); |
| _checkForFinalSupertypeOutsideOfLibrary( |
| superclass, withClause, implementsClause, null); |
| _checkForClassUsedAsMixin(withClause); |
| _checkForSealedSupertypeOutsideOfLibrary( |
| superclass, withClause, implementsClause, null); |
| return true; |
| } |
| return false; |
| } |
| |
| /// 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.atToken( |
| deferredToken, |
| CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, |
| ); |
| } |
| } |
| } |
| } |
| |
| void _checkForAbstractOrExternalFieldConstructorInitializer( |
| Token identifier, FieldElement fieldElement) { |
| if (fieldElement.isAbstract) { |
| errorReporter.atToken( |
| identifier, |
| CompileTimeErrorCode.ABSTRACT_FIELD_CONSTRUCTOR_INITIALIZER, |
| ); |
| } |
| if (fieldElement.isExternal) { |
| errorReporter.atToken( |
| identifier, |
| CompileTimeErrorCode.EXTERNAL_FIELD_CONSTRUCTOR_INITIALIZER, |
| ); |
| } |
| } |
| |
| void _checkForAbstractOrExternalVariableInitializer( |
| VariableDeclaration node) { |
| var declaredElement = node.declaredElement; |
| if (node.initializer != null) { |
| if (declaredElement is FieldElement) { |
| if (declaredElement.isAbstract) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER, |
| ); |
| } |
| if (declaredElement.isExternal) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTERNAL_FIELD_INITIALIZER, |
| ); |
| } |
| } else if (declaredElement is TopLevelVariableElement) { |
| if (declaredElement.isExternal) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTERNAL_VARIABLE_INITIALIZER, |
| ); |
| } |
| } |
| } |
| } |
| |
| /// 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++) { |
| NamedType mixinName = withClause.mixinTypes[mixinNameIndex]; |
| DartType mixinType = mixinName.typeOrThrow; |
| if (mixinType is InterfaceType) { |
| mixinTypeIndex++; |
| if (_checkForExtendsOrImplementsDisallowedClass( |
| mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) { |
| problemReported = true; |
| } else { |
| var mixinElement = mixinType.element; |
| if (_checkForExtendsOrImplementsDeferredClass( |
| mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) { |
| problemReported = true; |
| } |
| if (mixinType.element is ExtensionTypeElement) { |
| // Already reported. |
| } else if (mixinElement is MixinElement) { |
| if (_checkForMixinSuperclassConstraints( |
| mixinNameIndex, mixinName)) { |
| problemReported = true; |
| } else if (_checkForMixinSuperInvokedMembers( |
| mixinTypeIndex, mixinName, mixinElement, mixinType)) { |
| problemReported = true; |
| } |
| } else { |
| bool isMixinClass = |
| mixinElement is ClassElementImpl && mixinElement.isMixinClass; |
| if (!isMixinClass && |
| _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 |
| NamedType constructorNamedType = redirectedConstructor.type; |
| DartType redirectedType = constructorNamedType.typeOrThrow; |
| if (!(redirectedType is DynamicType || redirectedType is InvalidType)) { |
| // Prepare the constructor name |
| String constructorStrName = constructorNamedType.qualifiedName; |
| if (redirectedConstructor.name != null) { |
| constructorStrName += ".${redirectedConstructor.name!.name}"; |
| } |
| errorReporter.atNode( |
| redirectedConstructor, |
| CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR, |
| arguments: [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, |
| strictCasts: strictCasts)) { |
| errorReporter.atNode( |
| redirectedConstructor, |
| CompileTimeErrorCode.REDIRECT_TO_INVALID_RETURN_TYPE, |
| arguments: [redirectedReturnType, constructorReturnType], |
| ); |
| return; |
| } else if (!typeSystem.isSubtypeOf(redirectedType, constructorType)) { |
| // Check parameters. |
| errorReporter.atNode( |
| redirectedConstructor, |
| CompileTimeErrorCode.REDIRECT_TO_INVALID_FUNCTION_TYPE, |
| arguments: [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 [LibraryExportElement] 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, |
| LibraryExportElement 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.atNode( |
| directive.uri, |
| CompileTimeErrorCode.AMBIGUOUS_EXPORT, |
| arguments: [ |
| 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({ |
| required Token name, |
| required Element? element, |
| }) { |
| if (element is MultiplyDefinedElementImpl) { |
| var conflictingMembers = element.conflictingElements; |
| var libraryNames = |
| conflictingMembers.map((e) => _getLibraryName(e)).toList(); |
| libraryNames.sort(); |
| errorReporter.atToken( |
| name, |
| CompileTimeErrorCode.AMBIGUOUS_IMPORT, |
| arguments: [name.lexeme, libraryNames.quotedAndCommaSeparatedWithAnd], |
| ); |
| } |
| } |
| |
| /// Verify that the given [expression] is not final. |
| /// |
| /// See [CompileTimeErrorCode.ASSIGNMENT_TO_CONST], |
| /// [CompileTimeErrorCode.ASSIGNMENT_TO_FINAL], and |
| /// [CompileTimeErrorCode.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.atNode( |
| expression, |
| CompileTimeErrorCode.ASSIGNMENT_TO_CONST, |
| ); |
| } |
| } else if (element is PropertyAccessorElement && element.isGetter) { |
| var variable = element.variable2; |
| if (variable == null) { |
| return; |
| } |
| if (variable.isConst) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.ASSIGNMENT_TO_CONST, |
| ); |
| } else if (variable is FieldElement && variable.isSynthetic) { |
| errorReporter.atNode( |
| highlightedNode, |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_NO_SETTER, |
| arguments: [variable.name, variable.enclosingElement.displayName], |
| ); |
| } else { |
| errorReporter.atNode( |
| highlightedNode, |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL, |
| arguments: [variable.name], |
| ); |
| } |
| } else if (element is FunctionElement) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.ASSIGNMENT_TO_FUNCTION, |
| ); |
| } else if (element is MethodElement) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.ASSIGNMENT_TO_METHOD, |
| ); |
| } else if (element is InterfaceElement || |
| element is DynamicElementImpl || |
| element is TypeParameterElement) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.ASSIGNMENT_TO_TYPE, |
| ); |
| } |
| } |
| |
| void _checkForAwaitInLateLocalVariableInitializer(AwaitExpression node) { |
| if (_isInLateLocalVariable.last) { |
| errorReporter.atToken( |
| node.awaitKeyword, |
| CompileTimeErrorCode.AWAIT_IN_LATE_LOCAL_VARIABLE_INITIALIZER, |
| ); |
| } |
| } |
| |
| void _checkForAwaitOfIncompatibleType(AwaitExpression node) { |
| var expression = node.expression; |
| var expressionType = expression.typeOrThrow; |
| if (typeSystem.isIncompatibleWithAwait(expressionType)) { |
| errorReporter.atToken( |
| node.awaitKeyword, |
| CompileTimeErrorCode.AWAIT_OF_INCOMPATIBLE_TYPE, |
| ); |
| } |
| } |
| |
| /// Verifies that the nodes don't reference `Function` from `dart:core`. |
| void _checkForBadFunctionUse({ |
| required NamedType? superclass, |
| required ImplementsClause? implementsClause, |
| required WithClause? withClause, |
| }) { |
| // With the `class_modifiers` feature `Function` is final. |
| if (_featureSet!.isEnabled(Feature.class_modifiers)) { |
| return; |
| } |
| |
| if (superclass != null) { |
| var type = superclass.type; |
| if (type != null && type.isDartCoreFunction) { |
| errorReporter.atNode( |
| superclass, |
| WarningCode.DEPRECATED_EXTENDS_FUNCTION, |
| ); |
| } |
| } |
| |
| if (implementsClause != null) { |
| for (var interface in implementsClause.interfaces) { |
| var type = interface.type; |
| if (type != null && type.isDartCoreFunction) { |
| errorReporter.atNode( |
| interface, |
| WarningCode.DEPRECATED_IMPLEMENTS_FUNCTION, |
| ); |
| break; |
| } |
| } |
| } |
| |
| if (withClause != null) { |
| for (NamedType mixin in withClause.mixinTypes) { |
| var type = mixin.type; |
| if (type != null && type.isDartCoreFunction) { |
| errorReporter.atNode( |
| mixin, |
| WarningCode.DEPRECATED_MIXIN_FUNCTION, |
| ); |
| } |
| } |
| } |
| } |
| |
| /// Verify that if a class is implementing a base class or mixin, it must be |
| /// within the same library as that class or mixin. |
| /// |
| /// See [CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY], |
| /// [CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY]. |
| void _checkForBaseClassOrMixinImplementedOutsideOfLibrary( |
| ImplementsClause? implementsClause) { |
| if (implementsClause == null) return; |
| for (NamedType interface in implementsClause.interfaces) { |
| var interfaceType = interface.type; |
| if (interfaceType is InterfaceType) { |
| var implementedInterfaces = [ |
| interfaceType, |
| ...interfaceType.element.allSupertypes, |
| ].map((e) => e.element).toList(); |
| for (var interfaceElement in implementedInterfaces) { |
| if (interfaceElement is ClassOrMixinElementImpl && |
| interfaceElement.isBase && |
| interfaceElement.library != _currentLibrary && |
| !_mayIgnoreClassModifiers(interfaceElement.library)) { |
| // Should this be combined with _checkForImplementsClauseErrorCodes |
| // to avoid double errors if implementing `int`. |
| if (interfaceElement is ClassElementImpl && |
| !interfaceElement.isSealed) { |
| errorReporter.atNode( |
| interface, |
| CompileTimeErrorCode.BASE_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, |
| arguments: [interfaceElement.name], |
| ); |
| } else if (interfaceElement is MixinElement) { |
| errorReporter.atNode( |
| interface, |
| CompileTimeErrorCode.BASE_MIXIN_IMPLEMENTED_OUTSIDE_OF_LIBRARY, |
| arguments: [interfaceElement.name], |
| ); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /// Verify that the given [token] 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(Token token, ErrorCode errorCode) { |
| if (token.type.isKeyword && token.keyword?.isPseudo != true) { |
| errorReporter.atToken( |
| token, |
| errorCode, |
| arguments: [token.lexeme], |
| ); |
| } |
| } |
| |
| /// Verify that if a class is being mixed in and class modifiers are enabled |
| /// in that class' library, then it must be a mixin class. |
| /// |
| /// See [CompileTimeErrorCode.CLASS_USED_AS_MIXIN]. |
| void _checkForClassUsedAsMixin(WithClause? withClause) { |
| if (withClause != null) { |
| for (NamedType withMixin in withClause.mixinTypes) { |
| var withType = withMixin.type; |
| if (withType is InterfaceType) { |
| var withElement = withType.element; |
| if (withElement is ClassElementImpl && |
| !withElement.isMixinClass && |
| withElement.library.featureSet |
| .isEnabled(Feature.class_modifiers) && |
| !_mayIgnoreClassModifiers(withElement.library)) { |
| errorReporter.atNode( |
| withMixin, |
| CompileTimeErrorCode.CLASS_USED_AS_MIXIN, |
| arguments: [withElement.name], |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| /// 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() { |
| var enclosingClass = _enclosingClass; |
| if (enclosingClass == null) { |
| return; |
| } |
| |
| Uri libraryUri = _currentLibrary.source.uri; |
| var conflictingDeclaredNames = <String>{}; |
| |
| // method declared in the enclosing class vs. inherited getter/setter |
| for (MethodElement method in enclosingClass.methods) { |
| String name = method.name; |
| |
| // find inherited property accessors |
| var getter = _inheritanceManager.getInherited2( |
| enclosingClass, Name(libraryUri, name)); |
| var setter = _inheritanceManager.getInherited2( |
| enclosingClass, Name(libraryUri, '$name=')); |
| |
| if (method.isStatic) { |
| void reportStaticConflict(ExecutableElement inherited) { |
| errorReporter.atElement( |
| method, |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, |
| arguments: [ |
| enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ], |
| ); |
| } |
| |
| if (getter != null) { |
| reportStaticConflict(getter); |
| continue; |
| } |
| |
| if (setter != null) { |
| reportStaticConflict(setter); |
| continue; |
| } |
| } |
| |
| // Extension type methods preclude accessors. |
| if (enclosingClass is ExtensionTypeElement) { |
| continue; |
| } |
| |
| void reportFieldConflict(PropertyAccessorElement inherited) { |
| errorReporter.atElement( |
| method, |
| CompileTimeErrorCode.CONFLICTING_METHOD_AND_FIELD, |
| arguments: [ |
| enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName |
| ], |
| ); |
| } |
| |
| if (getter is PropertyAccessorElement) { |
| reportFieldConflict(getter); |
| continue; |
| } |
| |
| if (setter is PropertyAccessorElement) { |
| reportFieldConflict(setter); |
| continue; |
| } |
| } |
| |
| // 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.atElement( |
| accessor, |
| CompileTimeErrorCode.CONFLICTING_STATIC_AND_INSTANCE, |
| arguments: [ |
| enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName, |
| ], |
| ); |
| conflictingDeclaredNames.add(name); |
| } else if (inherited is MethodElement) { |
| // Extension type accessors preclude inherited accessors/methods. |
| if (enclosingClass is ExtensionTypeElement) { |
| continue; |
| } |
| errorReporter.atElement( |
| accessor, |
| CompileTimeErrorCode.CONFLICTING_FIELD_AND_METHOD, |
| arguments: [ |
| enclosingClass.displayName, |
| name, |
| inherited.enclosingElement.displayName |
| ], |
| ); |
| conflictingDeclaredNames.add(name); |
| } |
| } |
| |
| // Inherited method and setter with the same name. |
| var inherited = _inheritanceManager.getInheritedMap2(enclosingClass); |
| for (var entry in inherited.entries) { |
| var method = entry.value; |
| if (method is MethodElement) { |
| var methodName = entry.key; |
| if (conflictingDeclaredNames.contains(methodName.name)) { |
| continue; |
| } |
| var setterName = methodName.forSetter; |
| var setter = inherited[setterName]; |
| if (setter is PropertyAccessorElement) { |
| errorReporter.atElement( |
| enclosingClass, |
| CompileTimeErrorCode.CONFLICTING_INHERITED_METHOD_AND_SETTER, |
| arguments: [ |
| enclosingClass.kind.displayName, |
| enclosingClass.displayName, |
| methodName.name, |
| ], |
| contextMessages: [ |
| DiagnosticMessageImpl( |
| filePath: method.source.fullName, |
| message: formatList( |
| "The method is inherited from the {0} '{1}'.", |
| [ |
| method.enclosingElement.kind.displayName, |
| method.enclosingElement.name, |
| ], |
| ), |
| offset: method.nameOffset, |
| length: method.nameLength, |
| url: null, |
| ), |
| DiagnosticMessageImpl( |
| filePath: setter.source.fullName, |
| message: formatList( |
| "The setter is inherited from the {0} '{1}'.", |
| [ |
| setter.enclosingElement.kind.displayName, |
| setter.enclosingElement.name, |
| ], |
| ), |
| offset: setter.nameOffset, |
| length: setter.nameLength, |
| url: null, |
| ), |
| ], |
| ); |
| } |
| } |
| } |
| } |
| |
| /// Verify all conflicts between type variable and enclosing class. |
| void _checkForConflictingClassTypeVariableErrorCodes() { |
| var enclosingClass = _enclosingClass!; |
| 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 is MixinElement |
| ? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MIXIN |
| : CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS; |
| errorReporter.atElement( |
| typeParameter, |
| code, |
| arguments: [name], |
| ); |
| } |
| // check members |
| if (enclosingClass.getNamedConstructor(name) != null || |
| enclosingClass.getMethod(name) != null || |
| enclosingClass.getGetter(name) != null || |
| enclosingClass.getSetter(name) != null) { |
| var code = enclosingClass is MixinElement |
| ? CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_MIXIN |
| : CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_CLASS; |
| errorReporter.atElement( |
| typeParameter, |
| code, |
| arguments: [name], |
| ); |
| } |
| } |
| } |
| |
| void _checkForConflictingEnumTypeVariableErrorCodes( |
| EnumElementImpl element, |
| ) { |
| for (var typeParameter in element.typeParameters) { |
| var name = typeParameter.name; |
| // name is same as the name of the enclosing enum |
| if (element.name == name) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_ENUM, |
| arguments: [name], |
| ); |
| } |
| // check members |
| if (element.getMethod(name) != null || |
| element.getGetter(name) != null || |
| element.getSetter(name) != null) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_ENUM, |
| arguments: [name], |
| ); |
| } |
| } |
| } |
| |
| void _checkForConflictingExtensionTypeTypeVariableErrorCodes( |
| ExtensionTypeElementImpl element, |
| ) { |
| for (var typeParameter in element.typeParameters) { |
| var name = typeParameter.name; |
| // name is same as the name of the enclosing class |
| if (element.name == name) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION_TYPE, |
| arguments: [name], |
| ); |
| } |
| // check members |
| if (element.getNamedConstructor(name) != null || |
| element.getMethod(name) != null || |
| element.getGetter(name) != null || |
| element.getSetter(name) != null) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode |
| .CONFLICTING_TYPE_VARIABLE_AND_MEMBER_EXTENSION_TYPE, |
| arguments: [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.atElement( |
| typeParameter, |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_EXTENSION, |
| arguments: [name], |
| ); |
| } |
| // check members |
| if (_enclosingExtension!.getMethod(name) != null || |
| _enclosingExtension!.getGetter(name) != null || |
| _enclosingExtension!.getSetter(name) != null) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER_EXTENSION, |
| arguments: [name], |
| ); |
| } |
| } |
| } |
| |
| void _checkForConflictingGenerics(NamedCompilationUnitMember node) { |
| var element = node.declaredElement as InterfaceElementImpl; |
| |
| // Report only on the declaration. |
| if (element.isAugmentation) { |
| return; |
| } |
| |
| var analysisSession = _currentLibrary.session; |
| var errors = analysisSession.classHierarchy.errors(element); |
| |
| for (var error in errors) { |
| if (error is IncompatibleInterfacesClassHierarchyError) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.CONFLICTING_GENERIC_INTERFACES, |
| arguments: [ |
| _enclosingClass!.kind.displayName, |
| _enclosingClass!.name, |
| error.first.getDisplayString(), |
| error.second.getDisplayString(), |
| ], |
| ); |
| } else { |
| throw UnimplementedError('${error.runtimeType}'); |
| } |
| } |
| } |
| |
| /// Check that the given constructor [declaration] has a valid combination of |
| /// redirecting constructor invocation(s), super constructor invocation(s), |
| /// field initializers, and assert initializers. |
| void _checkForConflictingInitializerErrorCodes( |
| ConstructorDeclaration declaration) { |
| var enclosingClass = _enclosingClass; |
| if (enclosingClass == null) { |
| return; |
| } |
| // Count and check each redirecting initializer. |
| var redirectingInitializerCount = 0; |
| var superInitializerCount = 0; |
| late SuperConstructorInvocation superInitializer; |
| for (ConstructorInitializer initializer in declaration.initializers) { |
| if (initializer is RedirectingConstructorInvocation) { |
| if (redirectingInitializerCount > 0) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS, |
| ); |
| } |
| if (declaration.factoryKeyword == null) { |
| RedirectingConstructorInvocation invocation = initializer; |
| var redirectingElement = invocation.staticElement; |
| if (redirectingElement == null) { |
| String enclosingNamedType = enclosingClass.displayName; |
| String constructorStrName = enclosingNamedType; |
| if (invocation.constructorName != null) { |
| constructorStrName += ".${invocation.constructorName!.name}"; |
| } |
| errorReporter.atNode( |
| invocation, |
| CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR, |
| arguments: [constructorStrName, enclosingNamedType], |
| ); |
| } else { |
| if (redirectingElement.isFactory) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode |
| .REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR, |
| ); |
| } |
| } |
| } |
| // [declaration] is a redirecting constructor via a redirecting |
| // initializer. |
| _checkForRedirectToNonConstConstructor( |
| declaration.declaredElement!, |
| initializer.staticElement, |
| initializer.constructorName ?? initializer.thisKeyword, |
| ); |
| redirectingInitializerCount++; |
| } else if (initializer is SuperConstructorInvocation) { |
| if (enclosingClass is EnumElement) { |
| errorReporter.atToken( |
| initializer.superKeyword, |
| CompileTimeErrorCode.SUPER_IN_ENUM_CONSTRUCTOR, |
| ); |
| } else if (superInitializerCount == 1) { |
| // Only report the second (first illegal) superinitializer. |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS, |
| ); |
| } |
| superInitializer = initializer; |
| superInitializerCount++; |
| } |
| } |
| // Check for initializers which are illegal when alongside a redirecting |
| // initializer. |
| if (redirectingInitializerCount > 0) { |
| for (ConstructorInitializer initializer in declaration.initializers) { |
| if (initializer is SuperConstructorInvocation) { |
| if (enclosingClass is! EnumElement) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR, |
| ); |
| } |
| } |
| if (initializer is ConstructorFieldInitializer) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, |
| ); |
| } |
| if (initializer is AssertInitializer) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.ASSERT_IN_REDIRECTING_CONSTRUCTOR, |
| ); |
| } |
| } |
| } |
| if (enclosingClass is! EnumElement && |
| redirectingInitializerCount == 0 && |
| superInitializerCount == 1 && |
| superInitializer != declaration.initializers.last) { |
| var superType = enclosingClass.supertype; |
| if (superType != null) { |
| var superNamedType = superType.element.displayName; |
| var constructorStrName = superNamedType; |
| var constructorName = superInitializer.constructorName; |
| if (constructorName != null) { |
| constructorStrName += '.${constructorName.name}'; |
| } |
| errorReporter.atToken( |
| superInitializer.superKeyword, |
| CompileTimeErrorCode.SUPER_INVOCATION_NOT_LAST, |
| arguments: [constructorStrName], |
| ); |
| } |
| } |
| } |
| |
| /// 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. |
| /// |
| /// Return `true` if an error is reported here, and the caller should stop |
| /// checking the constructor for constant-related errors. |
| /// |
| /// See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER], and |
| /// [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD]. |
| bool _checkForConstConstructorWithNonConstSuper( |
| ConstructorDeclaration constructor) { |
| var enclosingClass = _enclosingClass; |
| if (enclosingClass == null || !_enclosingExecutable.isConstConstructor) { |
| return false; |
| } |
| |
| // OK, const factory, checked elsewhere |
| if (constructor.factoryKeyword != null) { |
| return false; |
| } |
| |
| // check for mixins |
| var instanceFields = <FieldElement>[]; |
| for (var mixin in enclosingClass.mixins) { |
| instanceFields.addAll(mixin.element.fields.where((field) { |
| if (field.isStatic) { |
| return false; |
| } |
| if (field.isSynthetic) { |
| return false; |
| } |
| // From the abstract and external fields specification: |
| // > An abstract instance variable declaration D is treated as an |
| // > abstract getter declaration and possibly an abstract setter |
| // > declaration. The setter is included if and only if D is non-final. |
| if (field.isAbstract && field.isFinal) { |
| return false; |
| } |
| return true; |
| })); |
| } |
| if (instanceFields.length == 1) { |
| var field = instanceFields.single; |
| errorReporter.atNode( |
| constructor.returnType, |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELD, |
| arguments: ["'${field.enclosingElement.name}.${field.name}'"], |
| ); |
| return true; |
| } else if (instanceFields.length > 1) { |
| var fieldNames = instanceFields |
| .map((field) => "'${field.enclosingElement.name}.${field.name}'") |
| .join(', '); |
| errorReporter.atNode( |
| constructor.returnType, |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN_WITH_FIELDS, |
| arguments: [fieldNames], |
| ); |
| return true; |
| } |
| |
| // Enum(s) always call a const super-constructor. |
| if (enclosingClass is EnumElement) { |
| return false; |
| } |
| |
| var element = constructor.declaredElement; |
| if (element == null) { |
| return false; |
| } |
| |
| // Redirecting constructors are checked to be const elsewhere. |
| if (element.redirectedConstructor != null) { |
| return false; |
| } |
| |
| var invokedSuper = element.superConstructor; |
| if (invokedSuper == null || invokedSuper.isConst) { |
| return false; |
| } |
| |
| // Often there is an explicit `super()` invocation, report on it. |
| var superInvocation = constructor.initializers |
| .whereType<SuperConstructorInvocation>() |
| .firstOrNull; |
| var errorNode = superInvocation ?? constructor.returnType; |
| |
| errorReporter.atNode( |
| errorNode, |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER, |
| arguments: [element.enclosingElement.displayName], |
| ); |
| return true; |
| } |
| |
| /// Verify that if the given [constructor] declaration is 'const' then there |
| /// are no non-final instance variable. The [constructorElement] is the |
| /// constructor element. |
| void _checkForConstConstructorWithNonFinalField( |
| ConstructorDeclaration constructor, |
| ConstructorElement constructorElement) { |
| if (!_enclosingExecutable.isConstConstructor) { |
| return; |
| } |
| if (!_enclosingExecutable.isGenerativeConstructor) { |
| return; |
| } |
| // check if there is non-final field |
| var classElement = constructorElement.enclosingElement; |
| if (classElement is! ClassElement || !classElement.hasNonFinalField) { |
| return; |
| } |
| 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 [namedType] is the name of the type defining the |
| /// constructor, always non-`null`. |
| /// |
| /// See [CompileTimeErrorCode.CONST_DEFERRED_CLASS]. |
| void _checkForConstDeferredClass(InstanceCreationExpression expression, |
| ConstructorName constructorName, NamedType namedType) { |
| if (namedType.isDeferred) { |
| errorReporter.atNode( |
| constructorName, |
| CompileTimeErrorCode.CONST_DEFERRED_CLASS, |
| ); |
| } |
| } |
| |
| /// 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.atNode( |
| expression, |
| CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, |
| ); |
| } |
| } |
| |
| /// Verify that the given instance creation [expression] is not being invoked |
| /// on an abstract class. The [namedType] is the [NamedType] 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, |
| NamedType namedType, |
| InterfaceType type) { |
| var element = type.element; |
| if (element is ClassElement && element.isAbstract) { |
| var element = expression.constructorName.staticElement; |
| if (element != null && !element.isFactory) { |
| bool isImplicit = |
| (expression as InstanceCreationExpressionImpl).isImplicit; |
| if (!isImplicit) { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, |
| ); |
| } else { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS, |
| ); |
| } |
| } |
| } |
| } |
| |
| /// Verify that the given [expression] is not a mixin instantiation. |
| void _checkForConstOrNewWithMixin(InstanceCreationExpression expression, |
| NamedType namedType, InterfaceType type) { |
| if (type.element is MixinElement) { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.MIXIN_INSTANTIATE, |
| ); |
| } |
| } |
| |
| /// 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.atToken( |
| expression.keyword!, |
| CompileTimeErrorCode.CONST_WITH_NON_CONST, |
| ); |
| } else { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.CONST_WITH_NON_CONST, |
| ); |
| } |
| } |
| } |
| |
| /// 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 [namedType] 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, |
| NamedType namedType) { |
| // OK if resolved |
| if (constructorName.staticElement != null) { |
| return; |
| } |
| // report as named or default constructor absence |
| var name = constructorName.name; |
| if (name != null) { |
| errorReporter.atNode( |
| name, |
| CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR, |
| arguments: [namedType.qualifiedName, name.name], |
| ); |
| } else { |
| errorReporter.atNode( |
| constructorName, |
| CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, |
| arguments: [namedType.qualifiedName], |
| ); |
| } |
| } |
| |
| void _checkForDeadNullCoalesce(TypeImpl lhsType, Expression rhs) { |
| if (typeSystem.isStrictlyNonNullable(lhsType)) { |
| errorReporter.atNode( |
| rhs, |
| StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION, |
| ); |
| } |
| } |
| |
| /// Report a diagnostic if there are any extensions in the imported library |
| /// that are not hidden. |
| void _checkForDeferredImportOfExtensions( |
| ImportDirective directive, LibraryImportElement importElement) { |
| for (var element in importElement.namespace.definedNames.values) { |
| if (element is ExtensionElement) { |
| errorReporter.atNode( |
| directive.uri, |
| CompileTimeErrorCode.DEFERRED_IMPORT_OF_EXTENSION, |
| ); |
| 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, Element? variableElement) { |
| if (checkForUseOfVoidResult(node.iterable)) { |
| return false; |
| } |
| |
| DartType iterableType = node.iterable.typeOrThrow; |
| |
| 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 loopNamedType = awaitKeyword != null ? 'Stream' : 'Iterable'; |
| |
| if (iterableType is DynamicType && strictCasts) { |
| errorReporter.atNode( |
| node.iterable, |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_TYPE, |
| arguments: [iterableType, loopNamedType], |
| ); |
| return false; |
| } |
| |
| // TODO(scheglov): use NullableDereferenceVerifier |
| if (typeSystem.isNullable(iterableType)) { |
| return false; |
| } |
| |
| // The type of the loop variable. |
| DartType variableType; |
| if (variableElement is VariableElement) { |
| variableType = variableElement.type; |
| } else { |
| return false; |
| } |
| |
| // 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 = typeSystem.resolveToBound(iterableType); |
| |
| var requiredSequenceType = awaitKeyword != null |
| ? _typeProvider.streamDynamicType |
| : _typeProvider.iterableDynamicType; |
| |
| if (typeSystem.isTop(iterableType)) { |
| iterableType = requiredSequenceType; |
| } |
| |
| if (!typeSystem.isAssignableTo(iterableType, requiredSequenceType, |
| strictCasts: strictCasts)) { |
| errorReporter.atNode( |
| node.iterable, |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_TYPE, |
| arguments: [iterableType, loopNamedType], |
| ); |
| 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, |
| strictCasts: strictCasts)) { |
| // Use an explicit string instead of [loopType] to remove the "<E>". |
| String loopNamedType = awaitKeyword != null ? 'Stream' : 'Iterable'; |
| |
| // A for-in loop is specified to desugar to a different set of statements |
| // which include an assignment of the sequence element's `iterator`'s |
| // `current` value, at which point "implicit tear-off conversion" may be |
| // performed. We do not perform this desugaring; instead we allow a |
| // special assignability here. |
| var implicitCallMethod = getImplicitCallMethod( |
| sequenceElementType, variableType, node.iterable); |
| if (implicitCallMethod == null) { |
| errorReporter.atNode( |
| node.iterable, |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, |
| arguments: [iterableType, loopNamedType, variableType], |
| ); |
| } else { |
| var tearoffType = implicitCallMethod.type; |
| // An implicit tear-off conversion does occur on the values of the |
| // iterator, but this does not guarantee their assignability. |
| |
| if (_featureSet?.isEnabled(Feature.constructor_tearoffs) ?? true) { |
| var typeArguments = typeSystem.inferFunctionTypeInstantiation( |
| variableType as FunctionType, |
| tearoffType, |
| errorReporter: errorReporter, |
| errorNode: node.iterable, |
| genericMetadataIsEnabled: true, |
| strictInference: options.strictInference, |
| strictCasts: options.strictCasts, |
| typeSystemOperations: typeSystemOperations, |
| dataForTesting: null, |
| nodeForTesting: null, |
| ); |
| if (typeArguments.isNotEmpty) { |
| tearoffType = tearoffType.instantiate(typeArguments); |
| } |
| } |
| |
| if (!typeSystem.isAssignableTo(tearoffType, variableType, |
| strictCasts: strictCasts)) { |
| errorReporter.atNode( |
| node.iterable, |
| CompileTimeErrorCode.FOR_IN_OF_INVALID_ELEMENT_TYPE, |
| arguments: [iterableType, loopNamedType, variableType], |
| ); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void _checkForEnumInstantiatedToBoundsIsNotWellBounded( |
| EnumDeclaration node, |
| EnumElementImpl element, |
| ) { |
| var valuesFieldType = element.valuesField?.type; |
| if (valuesFieldType is InterfaceType) { |
| var isWellBounded = typeSystem.isWellBounded( |
| valuesFieldType.typeArguments.single, |
| allowSuperBounded: true, |
| ); |
| if (isWellBounded is NotWellBoundedTypeResult) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.ENUM_INSTANTIATED_TO_BOUNDS_IS_NOT_WELL_BOUNDED, |
| ); |
| } |
| } |
| } |
| |
| /// Check that if the visiting library is not system, then any given library |
| /// should not be SDK internal library. The [exportElement] is the |
| /// [LibraryExportElement] 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, LibraryExportElement 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(); |
| |
| // We allow exporting `dart:_macros` from `package:macros`. |
| if (uri == 'dart:_macros' && |
| _currentLibrary.source.uri.scheme == 'package' && |
| _currentLibrary.source.uri.pathSegments.first == 'macros') { |
| return; |
| } |
| var sdkLibrary = sdk.getSdkLibrary(uri); |
| if (sdkLibrary == null) { |
| return; |
| } |
| if (!sdkLibrary.isInternal) { |
| return; |
| } |
| |
| // It is safe to assume that `directive.uri.stringValue` is non-`null`, |
| // because the only time it is `null` is if the URI contains a string |
| // interpolation, in which case the export would never have resolved in the |
| // first place. |
| errorReporter.atNode( |
| directive, |
| CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, |
| arguments: [directive.uri.stringValue!], |
| ); |
| } |
| |
| /// Verify that the given extends [clause] does not extend a deferred class. |
| /// |
| /// See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS]. |
| void _checkForExtendsDeferredClass(NamedType? 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(NamedType? superclass) { |
| if (superclass == null) { |
| return false; |
| } |
| return _checkForExtendsOrImplementsDisallowedClass( |
| superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS); |
| } |
| |
| /// Verify that the given [namedType] 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( |
| NamedType namedType, ErrorCode errorCode) { |
| if (namedType.isSynthetic) { |
| return false; |
| } |
| if (namedType.isDeferred) { |
| errorReporter.atNode( |
| namedType, |
| errorCode, |
| ); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Verify that the given [namedType] 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( |
| NamedType namedType, ErrorCode errorCode) { |
| if (namedType.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.uri.isScheme('dart')) { |
| return false; |
| } |
| var type = namedType.type; |
| return type is InterfaceType && |
| _typeProvider.isNonSubtypableClass(type.element); |
| } |
| |
| void _checkForExtensionDeclaresMemberOfObject(MethodDeclaration node) { |
| if (_enclosingExtension != null) { |
| if (node.hasObjectMemberName) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTENSION_DECLARES_MEMBER_OF_OBJECT, |
| ); |
| } |
| } |
| |
| if (_enclosingClass is ExtensionTypeElement) { |
| if (node.hasObjectMemberName) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_MEMBER_OF_OBJECT, |
| ); |
| } |
| } |
| } |
| |
| void _checkForExtensionTypeConstructorWithSuperInvocation( |
| SuperConstructorInvocation node, |
| ) { |
| if (_enclosingClass is ExtensionTypeElement) { |
| errorReporter.atToken( |
| node.superKeyword, |
| CompileTimeErrorCode.EXTENSION_TYPE_CONSTRUCTOR_WITH_SUPER_INVOCATION, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeDeclaresInstanceField(FieldDeclaration node) { |
| if (_enclosingClass is! ExtensionTypeElement) { |
| return; |
| } |
| |
| if (node.isStatic || node.externalKeyword != null) { |
| return; |
| } |
| |
| for (var field in node.fields.variables) { |
| errorReporter.atToken( |
| field.name, |
| CompileTimeErrorCode.EXTENSION_TYPE_DECLARES_INSTANCE_FIELD, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeImplementsDeferred( |
| ExtensionTypeDeclarationImpl node, |
| ) { |
| var clause = node.implementsClause; |
| if (clause == null) { |
| return; |
| } |
| |
| for (var type in clause.interfaces) { |
| _checkForExtendsOrImplementsDeferredClass( |
| type, |
| CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeImplementsItself( |
| ExtensionTypeDeclarationImpl node, |
| ExtensionTypeElementImpl element, |
| ) { |
| if (element.hasImplementsSelfReference) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTENSION_TYPE_IMPLEMENTS_ITSELF, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeMemberConflicts({ |
| required ExtensionTypeDeclaration node, |
| required ExtensionTypeElement element, |
| }) { |
| void report(String memberName, List<ExecutableElement> candidates) { |
| var contextMessages = candidates.map<DiagnosticMessage>((executable) { |
| var container = executable.enclosingElement as InterfaceElement; |
| return DiagnosticMessageImpl( |
| filePath: executable.source.fullName, |
| offset: executable.nameOffset, |
| length: executable.nameLength, |
| message: "Inherited from '${container.name}'", |
| url: null, |
| ); |
| }).toList(); |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTENSION_TYPE_INHERITED_MEMBER_CONFLICT, |
| arguments: [node.name.lexeme, memberName], |
| contextMessages: contextMessages, |
| ); |
| } |
| |
| var interface = _inheritanceManager.getInterface(element); |
| for (var conflict in interface.conflicts) { |
| switch (conflict) { |
| case CandidatesConflict _: |
| report(conflict.name.name, conflict.candidates); |
| case HasNonExtensionAndExtensionMemberConflict _: |
| report(conflict.name.name, [ |
| ...conflict.nonExtension, |
| ...conflict.extension, |
| ]); |
| case NotUniqueExtensionMemberConflict _: |
| report(conflict.name.name, conflict.candidates); |
| } |
| } |
| } |
| |
| void _checkForExtensionTypeRepresentationDependsOnItself( |
| ExtensionTypeDeclarationImpl node, |
| ExtensionTypeElementImpl element, |
| ) { |
| if (element.hasRepresentationSelfReference) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.EXTENSION_TYPE_REPRESENTATION_DEPENDS_ON_ITSELF, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeRepresentationTypeBottom( |
| ExtensionTypeDeclarationImpl node, |
| ExtensionTypeElementImpl element, |
| ) { |
| var representationType = element.representation.type; |
| if (typeSystem.isBottom(representationType)) { |
| errorReporter.atNode( |
| node.representation.fieldType, |
| CompileTimeErrorCode.EXTENSION_TYPE_REPRESENTATION_TYPE_BOTTOM, |
| ); |
| } |
| } |
| |
| void _checkForExtensionTypeWithAbstractMember( |
| ExtensionTypeDeclarationImpl node, |
| ) { |
| for (var member in node.members) { |
| if (member is MethodDeclarationImpl && !member.isStatic) { |
| if (member.isAbstract) { |
| errorReporter.atNode( |
| member, |
| CompileTimeErrorCode.EXTENSION_TYPE_WITH_ABSTRACT_MEMBER, |
| arguments: [member.name.lexeme, node.name.lexeme], |
| ); |
| } |
| } |
| } |
| } |
| |
| /// 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.atNode( |
| parameter, |
| CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR, |
| ); |
| return; |
| } |
| // constructor cannot have a redirection |
| for (ConstructorInitializer initializer in constructor.initializers) { |
| if (initializer is RedirectingConstructorInvocation) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR, |
| ); |
| return; |
| } |
| } |
| } else { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, |
| ); |
| } |
| } |
| |
| /// Verify that the given variable declaration [list] has only initialized |
| /// variables if the list is final or const. |
| /// |
| /// See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and |
| /// [CompileTimeErrorCode.FINAL_NOT_INITIALIZED]. |
| void _checkForFinalNotInitialized(VariableDeclarationList list) { |
| if (_isInNativeClass || list.isSynthetic) { |
| return; |
| } |
| |
| // Handled during resolution, with flow analysis. |
| if (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.atToken( |
| variable.name, |
| CompileTimeErrorCode.CONST_NOT_INITIALIZED, |
| arguments: [variable.name.lexeme], |
| ); |
| } 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 (!variable.isLate) { |
| errorReporter.atToken( |
| variable.name, |
| CompileTimeErrorCode.FINAL_NOT_INITIALIZED, |
| arguments: [variable.name.lexeme], |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| /// 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 |
| /// [CompileTimeErrorCode.FINAL_NOT_INITIALIZED]. |
| void _checkForFinalNotInitializedInClass( |
| InstanceElementImpl container, |
| List<ClassMember> members, |
| ) { |
| if (container is InterfaceElementImpl) { |
| var augmented = container.augmented; |
| for (var constructor in augmented.constructors) { |
| if (constructor.isGenerative && !constructor.isSynthetic) { |
| return; |
| } |
| } |
| } |
| |
| for (ClassMember classMember in members) { |
| if (classMember is FieldDeclaration) { |
| var fields = classMember.fields; |
| _checkForFinalNotInitialized(fields); |
| _checkForNotInitializedNonNullableInstanceFields(classMember); |
| } |
| } |
| } |
| |
| /// Check that if a direct supertype of a node is final, then it must be in |
| /// the same library. |
| /// |
| /// See [CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY], |
| /// [CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY], |
| /// [CompileTimeErrorCode. |
| /// FINAL_CLASS_USED_AS_MIXIN_CONSTRAINT_OUTSIDE_OF_LIBRARY]. |
| void _checkForFinalSupertypeOutsideOfLibrary( |
| NamedType? superclass, |
| WithClause? withClause, |
| ImplementsClause? implementsClause, |
| MixinOnClause? onClause, |
| ) { |
| if (superclass != null) { |
| var type = superclass.type; |
| if (type is InterfaceType) { |
| var element = type.element; |
| if (element is ClassElementImpl && |
| element.isFinal && |
| !element.isSealed && |
| element.library != _currentLibrary && |
| !_mayIgnoreClassModifiers(element.library)) { |
| errorReporter.atNode( |
| superclass, |
| CompileTimeErrorCode.FINAL_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| if (implementsClause != null) { |
| for (NamedType namedType in implementsClause.interfaces) { |
| var type = namedType.type; |
| if (type is InterfaceType) { |
| var implementedInterfaces = [ |
| type, |
| ...type.element.allSupertypes, |
| ].map((e) => e.element).toList(); |
| for (var element in implementedInterfaces) { |
| if (element is ClassElement && |
| element.isFinal && |
| !element.isSealed && |
| element.library != _currentLibrary && |
| !_mayIgnoreClassModifiers(element.library)) { |
| // If the final interface is an indirect interface and is in a |
| // different library that has class modifiers enabled, there is a |
| // nearer declaration that would emit an error, if any. |
| if (element != type.element && |
| type.element.library.featureSet |
| .isEnabled(Feature.class_modifiers)) { |
| continue; |
| } |
| |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.FINAL_CLASS_IMPLEMENTED_OUTSIDE_OF_LIBRARY, |
| arguments: [element.name], |
| ); |
| break; |
| } |
| } |
| } |
| } |
| } |
| if (onClause != null) { |
| for (NamedType namedType in onClause.superclassConstraints) { |
| var type = namedType.type; |
| if (type is InterfaceType) { |
| var element = type.element; |
| if (element is ClassElement && |
| element.isFinal && |
| !element.isSealed && |
| element.library != _currentLibrary && |
| !_mayIgnoreClassModifiers(element.library)) { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode |
| .FINAL_CLASS_USED_AS_MIXIN_CONSTRAINT_OUTSIDE_OF_LIBRARY, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| 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.atNode( |
| node, |
| CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND, |
| ); |
| } |
| } |
| |
| void _checkForIllegalLanguageOverride(CompilationUnit node) { |
| var sourceLanguageConstraint = options.sourceLanguageConstraint; |
| if (sourceLanguageConstraint == null) { |
| return; |
| } |
| |
| var languageVersion = _currentLibrary.languageVersion.effective; |
| if (sourceLanguageConstraint.allows(languageVersion)) { |
| return; |
| } |
| |
| var languageVersionToken = node.languageVersionToken; |
| if (languageVersionToken != null) { |
| errorReporter.atToken( |
| languageVersionToken, |
| CompileTimeErrorCode.ILLEGAL_LANGUAGE_VERSION_OVERRIDE, |
| arguments: ['$sourceLanguageConstraint'], |
| ); |
| } |
| } |
| |
| /// 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 (NamedType 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; |
| } |
| |
| /// Check that if the visiting library is not system, then any given library |
| /// should not be SDK internal library. The [importElement] is the |
| /// [LibraryImportElement] retrieved from the node, if the element in the node |
| /// was `null`, then this method is not called. |
| void _checkForImportInternalLibrary( |
| ImportDirective directive, LibraryImportElement importElement) { |
| if (_isInSystemLibrary || _isWasm(importElement)) { |
| 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; |
| } |
| // The only way an import URI's `stringValue` can be `null` is if the string |
| // contained interpolations, in which case the import would have failed to |
| // resolve, and we would never reach here. So it is safe to assume that |
| // `directive.uri.stringValue` is non-`null`. |
| errorReporter.atNode( |
| directive.uri, |
| CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, |
| arguments: [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(InterfaceElement? 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! InterfaceElement) { |
| // OK, top-level element |
| return; |
| } |
| } |
| } |
| } |
| |
| /// Verify that if a class is extending an interface class or mixing in an |
| /// interface mixin, it must be within the same library as that class or |
| /// mixin. |
| /// |
| /// See |
| /// [CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY]. |
| void _checkForInterfaceClassOrMixinSuperclassOutsideOfLibrary( |
| NamedType? superclass, WithClause? withClause) { |
| if (superclass != null) { |
| var superclassType = superclass.type; |
| if (superclassType is InterfaceType) { |
| var superclassElement = superclassType.element; |
| if (superclassElement is ClassElementImpl && |
| superclassElement.isInterface && |
| !superclassElement.isSealed && |
| superclassElement.library != _currentLibrary && |
| !_mayIgnoreClassModifiers(superclassElement.library)) { |
| errorReporter.atNode( |
| superclass, |
| CompileTimeErrorCode.INTERFACE_CLASS_EXTENDED_OUTSIDE_OF_LIBRARY, |
| arguments: [superclassElement.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 [CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| void _checkForIntNotAssignable(Expression argument) { |
| var staticParameterElement = argument.staticParameterElement; |
| var staticParameterType = staticParameterElement?.type; |
| if (staticParameterType != null) { |
| 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.atNode( |
| annotation.name, |
| CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY, |
| ); |
| } |
| } |
| |
| /// 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.atNode( |
| initializer, |
| CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, |
| arguments: [fieldName.name], |
| ); |
| } else if (staticElement.isStatic) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, |
| arguments: [fieldName.name], |
| ); |
| } |
| } else { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, |
| arguments: [fieldName.name], |
| ); |
| return; |
| } |
| } |
| |
| void _checkForInvalidGenerativeConstructorReference(ConstructorName node) { |
| var constructorElement = node.staticElement; |
| if (constructorElement != null && |
| constructorElement.isGenerative && |
| constructorElement.enclosingElement is EnumElement) { |
| if (_currentLibrary.featureSet.isEnabled(Feature.enhanced_enums)) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.INVALID_REFERENCE_TO_GENERATIVE_ENUM_CONSTRUCTOR, |
| ); |
| } else { |
| errorReporter.atNode( |
| node.type, |
| CompileTimeErrorCode.INSTANTIATE_ENUM, |
| ); |
| } |
| } |
| } |
| |
| /// 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! InterfaceElement && |
| 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.atNode( |
| identifier, |
| CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, |
| ); |
| } else if (_enclosingExecutable.inFactoryConstructor) { |
| errorReporter.atNode( |
| identifier, |
| CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, |
| ); |
| } else { |
| errorReporter.atNode( |
| identifier, |
| CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER, |
| arguments: [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.atToken( |
| keyword, |
| errorCode, |
| arguments: [keyword.lexeme], |
| ); |
| } |
| } |
| |
| /// Verify that the usage of the given 'this' is valid. |
| /// |
| /// See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS]. |
| void _checkForInvalidReferenceToThis(ThisExpression expression) { |
| if (!_hasAccessToThis) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, |
| ); |
| } |
| } |
| |
| 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 enclosingClass = _enclosingClass; |
| if (enclosingClass == null) { |
| // The field is in an extension and should be handled elsewhere. |
| return; |
| } |
| |
| var hasGenerativeConstConstructor = |
| _enclosingClass!.constructors.any((c) => c.isConst && !c.isFactory); |
| if (!hasGenerativeConstConstructor) return; |
| |
| errorReporter.atToken( |
| lateKeyword, |
| CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR, |
| ); |
| } |
| |
| /// Verify that the elements of the given list [literal] are subtypes of the |
| /// list's static type. |
| /// |
| /// See [CompileTimeErrorCode.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, |
| this, |
| forList: true, |
| elementType: listElementType, |
| featureSet: _featureSet!, |
| ); |
| for (CollectionElement element in literal.elements) { |
| verifier.verify(element); |
| } |
| } |
| |
| void _checkForMainFunction1(Token nameToken, Element declaredElement) { |
| // We should only check exported declarations, i.e. top-level. |
| if (declaredElement.enclosingElement is! CompilationUnitElement) { |
| return; |
| } |
| |
| if (declaredElement.displayName != 'main') { |
| return; |
| } |
| |
| if (declaredElement is! FunctionElement) { |
| errorReporter.atToken( |
| nameToken, |
| CompileTimeErrorCode.MAIN_IS_NOT_FUNCTION, |
| ); |
| } |
| } |
| |
| void _checkForMainFunction2(FunctionDeclaration functionDeclaration) { |
| if (functionDeclaration.name.lexeme != 'main') { |
| return; |
| } |
| |
| if (functionDeclaration.parent is! CompilationUnit) { |
| return; |
| } |
| |
| var parameterList = functionDeclaration.functionExpression.parameters; |
| if (parameterList == null) { |
| return; |
| } |
| |
| var parameters = parameterList.parameters; |
| var positional = parameters.where((e) => e.isPositional).toList(); |
| var requiredPositional = |
| parameters.where((e) => e.isRequiredPositional).toList(); |
| |
| if (requiredPositional.length > 2) { |
| errorReporter.atToken( |
| functionDeclaration.name, |
| CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS, |
| ); |
| } |
| |
| if (parameters.any((e) => e.isRequiredNamed)) { |
| errorReporter.atToken( |
| functionDeclaration.name, |
| CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS, |
| ); |
| } |
| |
| if (positional.isNotEmpty) { |
| var first = positional.first; |
| var type = first.declaredElement!.type; |
| var listOfString = _typeProvider.listType(_typeProvider.stringType); |
| if (!typeSystem.isSubtypeOf(listOfString, type)) { |
| errorReporter.atNode( |
| first.notDefault.typeOrSelf, |
| CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE, |
| ); |
| } |
| } |
| } |
| |
| 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, |
| this, |
| 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) { |
| if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) { |
| // Exhaustiveness checking cover this warning. |
| return; |
| } |
| |
| // 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 is EnumElement) { |
| var constantNames = enumElement.fields |
| .where((field) => field.isEnumConstant) |
| .map((field) => field.name) |
| .toSet(); |
| |
| for (var member in statement.members) { |
| Expression? caseConstant; |
| if (member is SwitchCase) { |
| caseConstant = member.expression; |
| } else if (member is SwitchPatternCase) { |
| var guardedPattern = member.guardedPattern; |
| if (guardedPattern.whenClause == null) { |
| var pattern = guardedPattern.pattern.unParenthesized; |
| if (pattern is ConstantPattern) { |
| caseConstant = pattern.expression; |
| } |
| } |
| } |
| if (caseConstant != null) { |
| var expression = caseConstant.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.atOffset( |
| offset: offset, |
| length: end - offset, |
| errorCode: StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, |
| arguments: [constantName], |
| ); |
| } |
| |
| if (typeSystem.isNullable(expressionType) && !hasCaseNull) { |
| int offset = statement.offset; |
| int end = statement.rightParenthesis.end; |
| errorReporter.atOffset( |
| offset: offset, |
| length: end - offset, |
| errorCode: StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, |
| arguments: ['null'], |
| ); |
| } |
| } |
| } |
| } |
| |
| /// 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( |
| NamedType mixinName, InterfaceElement mixinElement) { |
| for (ConstructorElement constructor in mixinElement.constructors) { |
| if (!constructor.isSynthetic && !constructor.isFactory) { |
| errorReporter.atNode( |
| mixinName, |
| CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR, |
| arguments: [mixinElement.name], |
| ); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Verify that mixin classes must have 'Object' as their superclass and that |
| /// they do not have a constructor. |
| /// |
| /// See [CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR], |
| /// [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT]. |
| void _checkForMixinClassErrorCodes( |
| NamedCompilationUnitMember node, |
| List<ClassMember> members, |
| NamedType? superclass, |
| WithClause? withClause) { |
| var element = node.declaredElement; |
| if (element is ClassElementImpl && element.isMixinClass) { |
| // Check that the class does not have a constructor. |
| for (ClassMember member in members) { |
| if (member is ConstructorDeclarationImpl) { |
| if (!member.isSynthetic && member.factoryKeyword == null) { |
| // Report errors on non-trivial generative constructors on mixin |
| // classes. |
| if (!member.isTrivial) { |
| errorReporter.atNode( |
| member.returnType, |
| CompileTimeErrorCode.MIXIN_CLASS_DECLARES_CONSTRUCTOR, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| } |
| // Check that the class has 'Object' as their superclass. |
| if (superclass != null && !superclass.typeOrThrow.isDartCoreObject) { |
| errorReporter.atNode( |
| superclass, |
| CompileTimeErrorCode.MIXIN_CLASS_DECLARATION_EXTENDS_NOT_OBJECT, |
| arguments: [element.name], |
| ); |
| } else if (withClause != null && |
| !(element.isMixinApplication && withClause.mixinTypes.length < 2)) { |
| errorReporter.atNode( |
| withClause, |
| CompileTimeErrorCode.MIXIN_CLASS_DECLARATION_EXTENDS_NOT_OBJECT, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| |
| /// 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( |
| NamedType mixinName, InterfaceElement mixinElement) { |
| if (mixinElement is! ClassElement) { |
| return false; |
| } |
| |
| var mixinSupertype = mixinElement.supertype; |
| if (mixinSupertype == null || mixinSupertype.isDartCoreObject) { |
| var mixins = mixinElement.mixins; |
| if (mixins.isEmpty || |
| mixinElement.isMixinApplication && mixins.length < 2) { |
| return false; |
| } |
| } |
| |
| errorReporter.atNode( |
| mixinName, |
| CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, |
| arguments: [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, NamedType mixinName) { |
| InterfaceType mixinType = mixinName.type as InterfaceType; |
| for (var constraint in mixinType.superclassConstraints) { |
| var superType = _enclosingClass!.supertype as InterfaceTypeImpl; |
| 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 (!isSatisfied) { |
| // This error can only occur if [mixinName] resolved to an actual mixin, |
| // so we can safely rely on `mixinName.type` being non-`null`. |
| errorReporter.atToken( |
| mixinName.name2, |
| CompileTimeErrorCode.MIXIN_APPLICATION_NOT_IMPLEMENTED_INTERFACE, |
| arguments: [ |
| mixinName.type!, |
| superType, |
| constraint, |
| ], |
| ); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Check that the superclass of the given [mixinElement] at the given |
| /// [mixinIndex] in the list of mixins of [_enclosingClass] has concrete |
| /// implementations of all the super-invoked members of the [mixinElement]. |
| bool _checkForMixinSuperInvokedMembers(int mixinIndex, NamedType mixinName, |
| InterfaceElement mixinElement, InterfaceType mixinType) { |
| var mixinElementImpl = mixinElement as MixinElementImpl; |
| if (mixinElementImpl.superInvokedNames.isEmpty) { |
| return false; |
| } |
| |
| Uri mixinLibraryUri = mixinElement.librarySource.uri; |
| for (var name in mixinElementImpl.superInvokedNames) { |
| var nameObject = Name(mixinLibraryUri, name); |
| |
| var superMember = _inheritanceManager.getMember2( |
| _enclosingClass!, nameObject, |
| forMixinIndex: mixinIndex, concrete: true, forSuper: true); |
| |
| if (superMember == null) { |
| var isSetter = name.endsWith('='); |
| |
| var errorCode = isSetter |
| ? CompileTimeErrorCode |
| .MIXIN_APPLICATION_NO_CONCRETE_SUPER_INVOKED_SETTER |
| : CompileTimeErrorCode |
| .MIXIN_APPLICATION_NO_CONCRETE_SUPER_INVOKED_MEMBER; |
| |
| if (isSetter) { |
| name = name.substring(0, name.length - 1); |
| } |
| |
| errorReporter.atNode( |
| mixinName, |
| errorCode, |
| arguments: [name], |
| ); |
| return true; |
| } |
| |
| var mixinMember = |
| _inheritanceManager.getMember(mixinType, nameObject, forSuper: true); |
| |
| if (mixinMember != null) { |
| var isCorrect = CorrectOverrideHelper( |
| typeSystem: typeSystem, |
| thisMember: superMember, |
| ).isCorrectOverrideOf( |
| superMember: mixinMember, |
| ); |
| if (!isCorrect) { |
| errorReporter.atNode( |
| mixinName, |
| CompileTimeErrorCode |
| .MIXIN_APPLICATION_CONCRETE_SUPER_INVOKED_MEMBER_TYPE, |
| arguments: [name, mixinMember.type, superMember.type], |
| ); |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /// Check for the declaration of a mixin from a library other than the current |
| /// library that defines a private member that conflicts with a private name |
| /// from the same library but from a superclass or a different mixin. |
| void _checkForMixinWithConflictingPrivateMember( |
| WithClause? withClause, NamedType? superclassName) { |
| if (withClause == null) { |
| return; |
| } |
| var declaredSupertype = superclassName?.type ?? _typeProvider.objectType; |
| if (declaredSupertype is! InterfaceType) { |
| return; |
| } |
| Map<LibraryElement, Map<String, String>> mixedInNames = |
| <LibraryElement, Map<String, String>>{}; |
| |
| /// Report an error and return `true` if the given [name] is a private name |
| /// (which is defined in the given [library]) and it conflicts with another |
| /// definition of that name inherited from the superclass. |
| bool isConflictingName( |
| String name, LibraryElement library, NamedType namedType) { |
| if (Identifier.isPrivateName(name)) { |
| Map<String, String> names = mixedInNames.putIfAbsent(library, () => {}); |
| var conflictingName = names[name]; |
| if (conflictingName != null) { |
| if (name.endsWith('=')) { |
| name = name.substring(0, name.length - 1); |
| } |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.PRIVATE_COLLISION_IN_MIXIN_APPLICATION, |
| arguments: [name, namedType.name2.lexeme, conflictingName], |
| ); |
| return true; |
| } |
| names[name] = namedType.name2.lexeme; |
| var inheritedMember = _inheritanceManager.getMember2( |
| declaredSupertype.element, |
| Name(library.source.uri, name), |
| concrete: true, |
| ); |
| if (inheritedMember != null) { |
| if (name.endsWith('=')) { |
| name = name.substring(0, name.length - 1); |
| } |
| // Inherited members are always contained inside named elements, so we |
| // can safely assume `inheritedMember.enclosingElement3.name` is |
| // non-`null`. |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.PRIVATE_COLLISION_IN_MIXIN_APPLICATION, |
| arguments: [ |
| name, |
| namedType.name2.lexeme, |
| inheritedMember.enclosingElement.name! |
| ], |
| ); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| for (NamedType mixinType in withClause.mixinTypes) { |
| DartType type = mixinType.typeOrThrow; |
| if (type is InterfaceType) { |
| LibraryElement library = type.element.library; |
| if (library != _currentLibrary) { |
| for (PropertyAccessorElement accessor in type.accessors) { |
| if (accessor.isStatic) { |
| continue; |
| } |
| if (isConflictingName(accessor.name, library, mixinType)) { |
| return; |
| } |
| } |
| for (MethodElement method in type.methods) { |
| if (method.isStatic) { |
| continue; |
| } |
| if (isConflictingName(method.name, library, mixinType)) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /// Checks to ensure that the given native function [body] is in SDK code. |
| /// |
| /// See [ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE]. |
| void _checkForNativeFunctionBodyInNonSdkCode(NativeFunctionBody body) { |
| if (!_isInSystemLibrary) { |
| errorReporter.atNode( |
| body, |
| ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, |
| ); |
| } |
| } |
| |
| /// Verify that the given instance creation [expression] invokes an existing |
| /// constructor. The [constructorName] is the constructor name. |
| /// The [namedType] is the name of the type defining the constructor. |
| /// |
| /// This method assumes that the instance creation was tested to be 'new' |
| /// before being called. |
| /// |
| /// See [CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR]. |
| void _checkForNewWithUndefinedConstructor( |
| InstanceCreationExpression expression, |
| ConstructorName constructorName, |
| NamedType namedType) { |
| // OK if resolved |
| if (constructorName.staticElement != null) { |
| return; |
| } |
| DartType type = namedType.typeOrThrow; |
| if (type is InterfaceType) { |
| var element = type.element; |
| if (element is EnumElement || element is MixinElement) { |
| // We have already reported the error. |
| return; |
| } |
| } |
| // report as named or default constructor absence |
| var name = constructorName.name; |
| if (name != null) { |
| errorReporter.atNode( |
| name, |
| CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR, |
| arguments: [namedType.qualifiedName, name.name], |
| ); |
| } else { |
| errorReporter.atNode( |
| constructorName, |
| CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, |
| arguments: [namedType.qualifiedName], |
| ); |
| } |
| } |
| |
| /// Check that if the given class [element] implicitly calls default |
| /// constructor of its superclass, there should be such default constructor - |
| /// implicit or explicit. |
| /// |
| /// See [CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT]. |
| void _checkForNoDefaultSuperConstructorImplicit( |
| ClassElementImpl element, |
| AugmentedClassElement augmented, |
| ) { |
| // do nothing if there is explicit constructor |
| var constructors = augmented.constructors; |
| if (!constructors[0].isSynthetic) { |
| return; |
| } |
| // prepare super |
| var superType = element.supertype; |
| if (superType == null) { |
| return; |
| } |
| var superElement = superType.element; |
| // try to find default generative super constructor |
| var superUnnamedConstructor = superElement.unnamedConstructor; |
| if (superUnnamedConstructor != null) { |
| if (superUnnamedConstructor.isFactory) { |
| errorReporter.atElement( |
| element, |
| CompileTimeErrorCode.NON_GENERATIVE_IMPLICIT_CONSTRUCTOR, |
| arguments: [ |
| superElement.name, |
| element.name, |
| superUnnamedConstructor, |
| ], |
| ); |
| return; |
| } |
| if (superUnnamedConstructor.isDefaultConstructor) { |
| return; |
| } |
| } |
| |
| if (!_typeProvider.isNonSubtypableClass(superType.element)) { |
| // Don't report this diagnostic for non-subtypable classes because the |
| // real problem was already reported. |
| errorReporter.atElement( |
| element, |
| CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT, |
| arguments: [superType, element.displayName], |
| ); |
| } |
| } |
| |
| bool _checkForNoGenerativeConstructorsInSuperclass(NamedType? superclass) { |
| var superType = _enclosingClass!.supertype; |
| if (superType == null) { |
| return false; |
| } |
| if (_enclosingClass!.constructors |
| .every((constructor) => constructor.isFactory)) { |
| // A class with no generative constructors *can* be extended if the |
| // subclass has only factory constructors. |
| return false; |
| } |
| var superElement = superType.element; |
| if (superElement.constructors.isEmpty) { |
| // Exclude empty constructor set, which indicates other errors occurred. |
| return false; |
| } |
| if (superElement.constructors |
| .every((constructor) => constructor.isFactory)) { |
| // For `E extends Exception`, etc., this will never work, because it has |
| // no generative constructors. State this clearly to users. |
| errorReporter.atNode( |
| superclass!, |
| CompileTimeErrorCode.NO_GENERATIVE_CONSTRUCTORS_IN_SUPERCLASS, |
| arguments: [_enclosingClass!.name, superElement.name], |
| ); |
| return true; |
| } |
| return false; |
| } |
| |
| void _checkForNonConstGenerativeEnumConstructor(ConstructorDeclaration node) { |
| if (_enclosingClass is EnumElement && |
| node.constKeyword == null && |
| node.factoryKeyword == null) { |
| errorReporter.reportErrorForName( |
| CompileTimeErrorCode.NON_CONST_GENERATIVE_ENUM_CONSTRUCTOR, |
| node, |
| ); |
| } |
| } |
| |
| /// Verify the given map [literal] either: |
| /// * has `const modifier` |
| /// * has explicit type arguments |
| /// * is not start of the statement |
| /// |
| /// See [CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT]. |
| void _checkForNonConstMapAsExpressionStatement3(SetOrMapLiteral literal) { |
| // "const" |
| if (literal.constKeyword != null) { |
| return; |
| } |
| // has type arguments |
| if (literal.typeArguments != null) { |
| return; |
| } |
| // prepare statement |
| var statement = literal.thisOrAncestorOfType<ExpressionStatement>(); |
| if (statement == null) { |
| return; |
| } |
| // OK, statement does not start with map |
| if (!identical(statement.beginToken, literal.beginToken)) { |
| return; |
| } |
| |
| // TODO(srawlins): Add any tests showing this is reported. |
| errorReporter.atNode( |
| literal, |
| CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, |
| ); |
| } |
| |
| void _checkForNonCovariantTypeParameterPositionInRepresentationType( |
| ExtensionTypeDeclaration node, |
| ExtensionTypeElement element, |
| ) { |
| var typeParameters = node.typeParameters?.typeParameters; |
| if (typeParameters == null) { |
| return; |
| } |
| |
| var representationType = element.representation.type; |
| |
| for (var typeParameterNode in typeParameters) { |
| var typeParameterElement = typeParameterNode.declaredElement!; |
| var nonCovariant = representationType.accept( |
| NonCovariantTypeParameterPositionVisitor( |
| [typeParameterElement], |
| initialVariance: Variance.covariant, |
| ), |
| ); |
| if (nonCovariant) { |
| errorReporter.atNode( |
| typeParameterNode, |
| CompileTimeErrorCode |
| .NON_COVARIANT_TYPE_PARAMETER_POSITION_IN_REPRESENTATION_TYPE, |
| ); |
| } |
| } |
| } |
| |
| void _checkForNonFinalFieldInEnum(FieldDeclaration node) { |
| if (node.isStatic) return; |
| |
| var variableList = node.fields; |
| if (variableList.isFinal) return; |
| |
| var enclosingClass = _enclosingClass; |
| if (enclosingClass == null || enclosingClass is! EnumElement) { |
| return; |
| } |
| |
| errorReporter.atToken( |
| variableList.variables.first.name, |
| CompileTimeErrorCode.NON_FINAL_FIELD_IN_ENUM, |
| ); |
| } |
| |
| /// Verify that the given method [declaration] of operator `[]=`, has `void` |
| /// return type. |
| /// |
| /// See [CompileTimeErrorCode.NON_VOID_RETURN_FOR_OPERATOR]. |
| void _checkForNonVoidReturnTypeForOperator(MethodDeclaration declaration) { |
| // check that []= operator |
| if (declaration.name.lexeme != "[]=") { |
| return; |
| } |
| // check return type |
| var annotation = declaration.returnType; |
| if (annotation != null) { |
| DartType type = annotation.typeOrThrow; |
| if (type is! VoidType) { |
| errorReporter.atNode( |
| annotation, |
| CompileTimeErrorCode.NON_VOID_RETURN_FOR_OPERATOR, |
| ); |
| } |
| } |
| } |
| |
| /// Verify the [namedType], used as the return type of a setter, is valid |
| /// (either `null` or the type 'void'). |
| /// |
| /// See [CompileTimeErrorCode.NON_VOID_RETURN_FOR_SETTER]. |
| void _checkForNonVoidReturnTypeForSetter(TypeAnnotation? namedType) { |
| if (namedType != null) { |
| DartType type = namedType.typeOrThrow; |
| if (type is! VoidType) { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.NON_VOID_RETURN_FOR_SETTER, |
| ); |
| } |
| } |
| } |
| |
| void _checkForNotInitializedNonNullableInstanceFields( |
| FieldDeclaration fieldDeclaration, |
| ) { |
| if (fieldDeclaration.isStatic) return; |
| var fields = fieldDeclaration.fields; |
| |
| if (fields.isLate) return; |
| if (fields.isFinal) return; |
| |
| if (_isEnclosingClassFfiStruct) return; |
| if (_isEnclosingClassFfiUnion) return; |
| |
| for (var field in fields.variables) { |
| var fieldElement = field.declaredElement as FieldElement; |
| if (fieldElement.isAbstract || fieldElement.isExternal) continue; |
| if (field.initializer != null) continue; |
| |
| var type = fieldElement.type; |
| if (!typeSystem.isPotentiallyNonNullable(type)) continue; |
| |
| errorReporter.atNode( |
| field, |
| CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD, |
| arguments: [field.name.lexeme], |
| ); |
| } |
| } |
| |
| void _checkForNotInitializedNonNullableStaticField(FieldDeclaration node) { |
| if (!node.isStatic) { |
| return; |
| } |
| _checkForNotInitializedNonNullableVariable(node.fields, false); |
| } |
| |
| void _checkForNotInitializedNonNullableVariable( |
| VariableDeclarationList node, |
| bool topLevel, |
| ) { |
| // Checked separately. |
| if (node.isConst || (topLevel && node.isFinal)) { |
| return; |
| } |
| |
| if (node.isLate) { |
| return; |
| } |
| |
| var parent = node.parent; |
| if (parent is FieldDeclaration) { |
| if (parent.externalKeyword != null) { |
| return; |
| } |
| } else if (parent is TopLevelVariableDeclaration) { |
| if (parent.externalKeyword != null) { |
| return; |
| } |
| } |
| |
| if (node.type == null) { |
| return; |
| } |
| var type = node.type!.typeOrThrow; |
| |
| if (!typeSystem.isPotentiallyNonNullable(type)) { |
| return; |
| } |
| |
| for (var variable in node.variables) { |
| if (variable.initializer == null) { |
| errorReporter.atToken( |
| variable.name, |
| CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_VARIABLE, |
| arguments: [variable.name.lexeme], |
| ); |
| } |
| } |
| } |
| |
| /// Verify that all classes of the given [onClause] are valid. |
| /// |
| /// See [CompileTimeErrorCode.MIXIN_SUPER_CLASS_CONSTRAINT_DISALLOWED_CLASS], |
| /// [CompileTimeErrorCode.MIXIN_SUPER_CLASS_CONSTRAINT_DEFERRED_CLASS]. |
| bool _checkForOnClauseErrorCodes(MixinOnClause? onClause) { |
| if (onClause == null) { |
| return false; |
| } |
| bool problemReported = false; |
| for (NamedType namedType in onClause.superclassConstraints) { |
| DartType type = namedType.typeOrThrow; |
| if (type is InterfaceType) { |
| if (_checkForExtendsOrImplementsDisallowedClass( |
| namedType, |
| CompileTimeErrorCode |
| .MIXIN_SUPER_CLASS_CONSTRAINT_DISALLOWED_CLASS)) { |
| problemReported = true; |
| } else { |
| if (_checkForExtendsOrImplementsDeferredClass( |
| namedType, |
| CompileTimeErrorCode |
| .MIXIN_SUPER_CLASS_CONSTRAINT_DEFERRED_CLASS)) { |
| problemReported = true; |
| } |
| } |
| } |
| } |
| return problemReported; |
| } |
| |
| /// Verify the given operator-method [declaration], does not have an optional |
| /// parameter. |
| /// |
| /// This method assumes that the method declaration was tested to be an |
| /// operator declaration before being called. |
| /// |
| /// See [CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR]. |
| void _checkForOptionalParameterInOperator(MethodDeclaration declaration) { |
| var parameterList = declaration.parameters; |
| if (parameterList == null) { |
| return; |
| } |
| |
| NodeList<FormalParameter> formalParameters = parameterList.parameters; |
| for (FormalParameter formalParameter in formalParameters) { |
| if (formalParameter.isOptional) { |
| errorReporter.atNode( |
| formalParameter, |
| CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR, |
| ); |
| } |
| } |
| } |
| |
| /// Via informal specification: dart-lang/language/issues/4 |
| /// |
| /// If e is an integer literal which is not the operand of a unary minus |
| /// operator, then: |
| /// - If the context type is double, it is a compile-time error if the |
| /// numerical value of e is not precisely representable by a double. |
| /// Otherwise the static type of e is double and the result of evaluating e |
| /// is a double instance representing that value. |
| /// - Otherwise (the current behavior of e, with a static type of int). |
| /// |
| /// and |
| /// |
| /// If e is -n and n is an integer literal, then |
| /// - If the context type is double, it is a compile-time error if the |
| /// numerical value of n is not precisely representable by a double. |
| /// Otherwise the static type of e is double and the result of evaluating e |
| /// is the result of calling the unary minus operator on a double instance |
| /// representing the numerical value of n. |
| /// - Otherwise (the current behavior of -n) |
| void _checkForOutOfRange(IntegerLiteral node) { |
| String lexeme = node.literal.lexeme; |
| bool isNegated = (node as IntegerLiteralImpl).immediatelyNegated; |
| List<Object> extraErrorArgs = []; |
| |
| bool treatedAsDouble = node.staticType == _typeProvider.doubleType; |
| bool valid = treatedAsDouble |
| ? IntegerLiteralImpl.isValidAsDouble(lexeme) |
| : IntegerLiteralImpl.isValidAsInteger(lexeme, isNegated); |
| |
| if (!valid) { |
| extraErrorArgs.add(isNegated ? '-$lexeme' : lexeme); |
| |
| if (treatedAsDouble) { |
| // Suggest the nearest valid double (as a BigInt for printing reasons). |
| extraErrorArgs.add( |
| BigInt.from(IntegerLiteralImpl.nearestValidDouble(lexeme)) |
| .toString()); |
| } |
| |
| errorReporter.atNode( |
| node, |
| treatedAsDouble |
| ? CompileTimeErrorCode.INTEGER_LITERAL_IMPRECISE_AS_DOUBLE |
| : CompileTimeErrorCode.INTEGER_LITERAL_OUT_OF_RANGE, |
| arguments: extraErrorArgs, |
| ); |
| } |
| } |
| |
| /// Check that the given named optional [parameter] does not begin with '_'. |
| void _checkForPrivateOptionalParameter(FormalParameter parameter) { |
| // should be named parameter |
| if (!parameter.isNamed) { |
| return; |
| } |
| // name should start with '_' |
| var name = parameter.name; |
| if (name == null || name.isSynthetic || !name.lexeme.startsWith('_')) { |
| return; |
| } |
| |
| errorReporter.atToken( |
| name, |
| CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, |
| ); |
| } |
| |
| /// Check whether the given constructor [declaration] is the redirecting |
| /// generative constructor and references itself directly or indirectly. The |
| /// [constructorElement] is the constructor element. |
| /// |
| /// See [CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT]. |
| void _checkForRecursiveConstructorRedirect(ConstructorDeclaration declaration, |
| ConstructorElement constructorElement) { |
| // we check generative constructor here |
| if (declaration.factoryKeyword != null) { |
| return; |
| } |
| // try to find redirecting constructor invocation and analyze it for |
| // recursion |
| for (ConstructorInitializer initializer in declaration.initializers) { |
| if (initializer is RedirectingConstructorInvocation) { |
| if (_hasRedirectingFactoryConstructorCycle(constructorElement)) { |
| errorReporter.atNode( |
| initializer, |
| CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT, |
| ); |
| } |
| return; |
| } |
| } |
| } |
| |
| /// Check whether the given constructor [declaration] has redirected |
| /// constructor and references itself directly or indirectly. The |
| /// constructor [element] is the element introduced by the declaration. |
| /// |
| /// See [CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT]. |
| bool _checkForRecursiveFactoryRedirect( |
| ConstructorDeclaration declaration, ConstructorElement element) { |
| // prepare redirected constructor |
| var redirectedConstructorNode = declaration.redirectedConstructor; |
| if (redirectedConstructorNode == null) { |
| return false; |
| } |
| // OK if no cycle |
| if (!_hasRedirectingFactoryConstructorCycle(element)) { |
| return false; |
| } |
| // report error |
| errorReporter.atNode( |
| redirectedConstructorNode, |
| CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT, |
| ); |
| return true; |
| } |
| |
| /// Check that the given constructor [declaration] has a valid redirected |
| /// constructor. |
| void _checkForRedirectingConstructorErrorCodes( |
| ConstructorDeclaration declaration) { |
| // Check for default values in the parameters. |
| var redirectedConstructor = declaration.redirectedConstructor; |
| if (redirectedConstructor == null) { |
| return; |
| } |
| for (FormalParameter parameter in declaration.parameters.parameters) { |
| if (parameter is DefaultFormalParameter && |
| parameter.defaultValue != null) { |
| errorReporter.atToken( |
| parameter.name!, |
| CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR, |
| ); |
| } |
| } |
| var redirectedElement = redirectedConstructor.staticElement; |
| _checkForRedirectToNonConstConstructor( |
| declaration.declaredElement!, |
| redirectedElement, |
| redirectedConstructor, |
| ); |
| var redirectedClass = redirectedElement?.enclosingElement; |
| if (redirectedClass is ClassElement && |
| redirectedClass.isAbstract && |
| redirectedElement != null && |
| !redirectedElement.isFactory) { |
| String enclosingNamedType = _enclosingClass!.displayName; |
| String constructorStrName = enclosingNamedType; |
| if (declaration.name != null) { |
| constructorStrName += ".${declaration.name!.lexeme}"; |
| } |
| errorReporter.atNode( |
| redirectedConstructor, |
| CompileTimeErrorCode.REDIRECT_TO_ABSTRACT_CLASS_CONSTRUCTOR, |
| arguments: [constructorStrName, redirectedClass.name], |
| ); |
| } |
| _checkForInvalidGenerativeConstructorReference(redirectedConstructor); |
| } |
| |
| /// Check whether the redirecting constructor, [element], is const, and |
| /// [redirectedElement], its redirectee, is not const. |
| /// |
| /// See [CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR]. |
| void _checkForRedirectToNonConstConstructor( |
| ConstructorElement element, |
| ConstructorElement? redirectedElement, |
| SyntacticEntity errorEntity, |
| ) { |
| // This constructor is const, but it redirects to a non-const constructor. |
| if (redirectedElement != null && |
| element.isConst && |
| !redirectedElement.isConst) { |
| errorReporter.atOffset( |
| offset: errorEntity.offset, |
| length: errorEntity.end - errorEntity.offset, |
| errorCode: CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR, |
| ); |
| } |
| } |
| |
| void _checkForReferenceBeforeDeclaration({ |
| required Token nameToken, |
| required Element? element, |
| }) { |
| if (element != null && |
| _hiddenElements != null && |
| _hiddenElements!.contains(element)) { |
| errorReporter.reportError( |
| DiagnosticFactory().referencedBeforeDeclaration( |
| errorReporter.source, |
| nameToken: nameToken, |
| element: element, |
| ), |
| ); |
| } |
| } |
| |
| void _checkForRepeatedType(List<NamedType>? namedTypes, ErrorCode errorCode) { |
| if (namedTypes == null) { |
| return; |
| } |
| |
| int count = namedTypes.length; |
| List<bool> detectedRepeatOnIndex = List<bool>.filled(count, false); |
| for (int i = 0; i < count; i++) { |
| if (!detectedRepeatOnIndex[i]) { |
| var type = namedTypes[i].type; |
| if (type is InterfaceType) { |
| var element = type.element; |
| for (int j = i + 1; j < count; j++) { |
| var otherNode = namedTypes[j]; |
| var otherType = otherNode.type; |
| if (otherType is InterfaceType && otherType.element == element) { |
| detectedRepeatOnIndex[j] = true; |
| errorReporter.atNode( |
| otherNode, |
| errorCode, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /// Check that the given rethrow [expression] is inside of a catch clause. |
| /// |
| /// See [CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH]. |
| void _checkForRethrowOutsideCatch(RethrowExpression expression) { |
| if (_enclosingExecutable.catchClauseLevel == 0) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH, |
| ); |
| } |
| } |
| |
| /// Check that if the given constructor [declaration] is generative, then |
| /// it does not have an expression function body. |
| /// |
| /// See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR]. |
| void _checkForReturnInGenerativeConstructor( |
| ConstructorDeclaration declaration) { |
| // ignore factory |
| if (declaration.factoryKeyword != null) { |
| return; |
| } |
| // block body (with possible return statement) is checked elsewhere |
| FunctionBody body = declaration.body; |
| if (body is! ExpressionFunctionBody) { |
| return; |
| } |
| |
| errorReporter.atNode( |
| body, |
| CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, |
| ); |
| } |
| |
| /// Check that if a direct supertype of a node is sealed, then it must be in |
| /// the same library. |
| /// |
| /// See [CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY]. |
| void _checkForSealedSupertypeOutsideOfLibrary( |
| NamedType? superclass, |
| WithClause? withClause, |
| ImplementsClause? implementsClause, |
| MixinOnClause? onClause) { |
| void reportErrorsForSealedClassesAndMixins(List<NamedType> namedTypes) { |
| for (NamedType namedType in namedTypes) { |
| var type = namedType.type; |
| if (type is InterfaceType) { |
| var element = type.element; |
| if (element is ClassElement && |
| element.isSealed && |
| element.library != _currentLibrary) { |
| errorReporter.atNode( |
| namedType, |
| CompileTimeErrorCode.SEALED_CLASS_SUBTYPE_OUTSIDE_OF_LIBRARY, |
| arguments: [element.name], |
| ); |
| } |
| } |
| } |
| } |
| |
| if (superclass != null) { |
| reportErrorsForSealedClassesAndMixins([superclass]); |
| } |
| if (withClause != null) { |
| reportErrorsForSealedClassesAndMixins(withClause.mixinTypes); |
| } |
| if (implementsClause != null) { |
| reportErrorsForSealedClassesAndMixins(implementsClause.interfaces); |
| } |
| if (onClause != null) { |
| reportErrorsForSealedClassesAndMixins(onClause.superclassConstraints); |
| } |
| } |
| |
| /// Verify that the elements in the given set [literal] are subtypes of the |
| /// set's static type. |
| /// |
| /// See [CompileTimeErrorCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE]. |
| void _checkForSetElementTypeNotAssignable3(SetOrMapLiteral literal) { |
| // Determine the set'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 setType = literal.typeOrThrow; |
| assert(setType is InterfaceTypeImpl); |
| |
| List<DartType> typeArguments = (setType as InterfaceTypeImpl).typeArguments; |
| // It is possible for the number of type arguments to be inconsistent when |
| // the literal is ambiguous and a non-set type was selected. |
| // TODO(brianwilkerson): Unify this and _checkForMapTypeNotAssignable3 to |
| // better handle recovery situations. |
| if (typeArguments.length == 1) { |
| DartType setElementType = typeArguments[0]; |
| |
| // Check every set element. |
| var verifier = LiteralElementVerifier( |
| _typeProvider, |
| typeSystem, |
| errorReporter, |
| this, |
| forSet: true, |
| elementType: setElementType, |
| featureSet: _featureSet!, |
| ); |
| for (CollectionElement element in literal.elements) { |
| verifier.verify(element); |
| } |
| } |
| } |
| |
| /// Check the given [typeReference] and that the [name] is not a reference to |
| /// an instance member. |
| /// |
| /// See [CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER]. |
| void _checkForStaticAccessToInstanceMember( |
| InterfaceElement? typeReference, SimpleIdentifier name) { |
| // OK, in comment |
| if (_isInComment) { |
| return; |
| } |
| // OK, target is not a type |
| if (typeReference == null) { |
| return; |
| } |
| // prepare member Element |
| var element = name.staticElement; |
| if (element is ExecutableElement) { |
| // OK, static |
| if (element.isStatic || element is ConstructorElement) { |
| return; |
| } |
| errorReporter.atNode( |
| name, |
| CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, |
| arguments: [name.name], |
| ); |
| } |
| } |
| |
| void _checkForThrowOfInvalidType(ThrowExpression node) { |
| var expression = node.expression; |
| var type = node.expression.typeOrThrow; |
| |
| if (!typeSystem.isAssignableTo(type, typeSystem.objectNone, |
| strictCasts: strictCasts)) { |
| errorReporter.atNode( |
| expression, |
| CompileTimeErrorCode.THROW_OF_INVALID_TYPE, |
| arguments: [type], |
| ); |
| } |
| } |
| |
| /// Verify that the given [element] does not reference itself directly. |
| /// If it does, report the error on the [node]. |
| /// |
| /// See [CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF]. |
| void _checkForTypeAliasCannotReferenceItself( |
| Token nameToken, |
| TypeAliasElementImpl element, |
| ) { |
| if (element.hasSelfReference) { |
| errorReporter.atToken( |
| nameToken, |
| CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, |
| ); |
| } |
| } |
| |
| /// Verify that the [type] is not a deferred type. |
| /// |
| /// See [CompileTimeErrorCode.TYPE_ANNOTATION_DEFERRED_CLASS]. |
| void _checkForTypeAnnotationDeferredClass(TypeAnnotation? type) { |
| if (type is NamedType && type.isDeferred) { |
| errorReporter.atNode( |
| type, |
| CompileTimeErrorCode.TYPE_ANNOTATION_DEFERRED_CLASS, |
| arguments: [type.qualifiedName], |
| ); |
| } |
| } |
| |
| /// Check that none of the type [parameters] references itself in its bound. |
| /// |
| /// See [CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND]. |
| void _checkForTypeParameterBoundRecursion(List<TypeParameter> parameters) { |
| Map<TypeParameterElement, TypeParameter>? elementToNode; |
| for (var parameter in parameters) { |
| if (parameter.bound != null) { |
| if (elementToNode == null) { |
| elementToNode = {}; |
| for (var parameter in parameters) { |
| elementToNode[parameter.declaredElement!] = parameter; |
| } |
| } |
| |
| TypeParameter? current = parameter; |
| for (var step = 0; current != null; step++) { |
| var boundNode = current.bound; |
| if (boundNode is NamedType) { |
| var boundType = boundNode.typeOrThrow; |
| boundType = boundType.extensionTypeErasure; |
| current = elementToNode[boundType.element]; |
| } else { |
| current = null; |
| } |
| if (step == parameters.length) { |
| var element = parameter.declaredElement!; |
| // This error can only occur if there is a bound, so we can safely |
| // assume `element.bound` is non-`null`. |
| errorReporter.atToken( |
| parameter.name, |
| CompileTimeErrorCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND, |
| arguments: [element.displayName, element.bound!], |
| ); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| void _checkForTypeParameterReferencedByStatic({ |
| required Token name, |
| required Element? element, |
| }) { |
| if (_enclosingExecutable.inStaticMethod || _isInStaticVariableDeclaration) { |
| if (element is TypeParameterElement && |
| element.enclosingElement is InstanceElement) { |
| // The class's type parameters are not in scope for static methods. |
| // However all other type parameters are legal (e.g. the static method's |
| // type parameters, or a local function's type parameters). |
| errorReporter.atToken( |
| name, |
| CompileTimeErrorCode.TYPE_PARAMETER_REFERENCED_BY_STATIC, |
| ); |
| } |
| } |
| } |
| |
| /// Check that if the given generative [constructor] has neither an explicit |
| /// super constructor invocation nor a redirecting constructor invocation, |
| /// that the superclass has a default generative constructor. |
| /// |
| /// See [CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT], |
| /// [CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR], and |
| /// [CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT]. |
| void _checkForUndefinedConstructorInInitializerImplicit( |
| ConstructorDeclaration constructor) { |
| if (_enclosingClass == null) { |
| return; |
| } |
| |
| // Ignore if the constructor is not generative. |
| if (constructor.factoryKeyword != null) { |
| return; |
| } |
| |
| // Ignore if the constructor is external. See |
| // https://github.com/dart-lang/language/issues/869. |
| if (constructor.externalKeyword != null) { |
| return; |
| } |
| |
| // Ignore if the constructor has either an implicit super constructor |
| // invocation or a redirecting constructor invocation. |
| for (ConstructorInitializer constructorInitializer |
| in constructor.initializers) { |
| if (constructorInitializer is SuperConstructorInvocation || |
| constructorInitializer is RedirectingConstructorInvocation) { |
| return; |
| } |
| } |
| |
| // Check to see whether the superclass has a non-factory unnamed |
| // constructor. |
| var superType = _enclosingClass!.supertype; |
| if (superType == null) { |
| return; |
| } |
| var superElement = superType.element; |
| |
| if (superElement.constructors |
| .every((constructor) => constructor.isFactory)) { |
| // Already reported [NO_GENERATIVE_CONSTRUCTORS_IN_SUPERCLASS]. |
| return; |
| } |
| |
| var superUnnamedConstructor = superElement.unnamedConstructor; |
| if (superUnnamedConstructor == null) { |
| errorReporter.atNode( |
| constructor.returnType, |
| CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, |
| arguments: [superElement.name], |
| ); |
| return; |
| } |
| |
| if (superUnnamedConstructor.isFactory) { |
| errorReporter.atNode( |
| constructor.returnType, |
| CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, |
| arguments: [superUnnamedConstructor], |
| ); |
| return; |
| } |
| |
| var requiredPositionalParameterCount = superUnnamedConstructor.parameters |
| .where((parameter) => parameter.isRequiredPositional) |
| .length; |
| var requiredNamedParameters = superUnnamedConstructor.parameters |
| .where((parameter) => parameter.isRequiredNamed) |
| .map((parameter) => parameter.name) |
| .toSet(); |
| |
| void reportError(ErrorCode errorCode, List<Object> arguments) { |
| Identifier returnType = constructor.returnType; |
| var name = constructor.name; |
| int offset = returnType.offset; |
| int length = (name != null ? name.end : returnType.end) - offset; |
| errorReporter.atOffset( |
| offset: offset, |
| length: length, |
| errorCode: errorCode, |
| arguments: arguments, |
| ); |
| } |
| |
| if (!_currentLibrary.featureSet.isEnabled(Feature.super_parameters)) { |
| if (requiredPositionalParameterCount != 0 || |
| requiredNamedParameters.isNotEmpty) { |
| reportError( |
| CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT, |
| [superType], |
| ); |
| } |
| return; |
| } |
| |
| var superParametersResult = verifySuperFormalParameters( |
| constructor: constructor, |
| errorReporter: errorReporter, |
| ); |
| requiredNamedParameters.removeAll( |
| superParametersResult.namedArgumentNames, |
| ); |
| |
| if (requiredPositionalParameterCount > |
| superParametersResult.positionalArgumentCount || |
| requiredNamedParameters.isNotEmpty) { |
| reportError( |
| CompileTimeErrorCode.IMPLICIT_SUPER_INITIALIZER_MISSING_ARGUMENTS, |
| [superType], |
| ); |
| } |
| } |
| |
| void _checkForUnnecessaryNullAware(Expression target, Token operator) { |
| if (target is SuperExpression) { |
| return; |
| } |
| |
| ErrorCode errorCode; |
| Token endToken = operator; |
| List<Object> arguments = const []; |
| if (operator.type == TokenType.QUESTION) { |
| errorCode = StaticWarningCode.INVALID_NULL_AWARE_OPERATOR; |
| endToken = operator.next!; |
| arguments = ['?[', '[']; |
| } else if (operator.type == TokenType.QUESTION_PERIOD) { |
| errorCode = StaticWarningCode.INVALID_NULL_AWARE_OPERATOR; |
| arguments = [operator.lexeme, '.']; |
| } else if (operator.type == TokenType.QUESTION_PERIOD_PERIOD) { |
| errorCode = StaticWarningCode.INVALID_NULL_AWARE_OPERATOR; |
| arguments = [operator.lexeme, '..']; |
| } else if (operator.type == TokenType.PERIOD_PERIOD_PERIOD_QUESTION) { |
| errorCode = StaticWarningCode.INVALID_NULL_AWARE_OPERATOR; |
| arguments = [operator.lexeme, '...']; |
| } else if (operator.type == TokenType.BANG) { |
| errorCode = StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION; |
| } else { |
| return; |
| } |
| |
| /// If the operator is not valid because the target already makes use of a |
| /// null aware operator, return the null aware operator from the target. |
| Token? previousShortCircuitingOperator(Expression? target) { |
| if (target is PropertyAccess) { |
| var operator = target.operator; |
| var type = operator.type; |
| if (type == TokenType.QUESTION_PERIOD) { |
| var realTarget = target.realTarget; |
| return previousShortCircuitingOperator(realTarget) ?? operator; |
| } |
| } else if (target is IndexExpression) { |
| if (target.question != null) { |
| var realTarget = target.realTarget; |
| return previousShortCircuitingOperator(realTarget) ?? target.question; |
| } |
| } else if (target is MethodInvocation) { |
| var operator = target.operator; |
| var type = operator?.type; |
| if (type == TokenType.QUESTION_PERIOD) { |
| var realTarget = target.realTarget; |
| return previousShortCircuitingOperator(realTarget) ?? operator; |
| } |
| } |
| return null; |
| } |
| |
| var targetType = target.staticType; |
| if (target is ExtensionOverride) { |
| var arguments = target.argumentList.arguments; |
| if (arguments.length == 1) { |
| targetType = arguments[0].typeOrThrow; |
| } else { |
| return; |
| } |
| } else if (targetType == null) { |
| if (target is Identifier) { |
| var targetElement = target.staticElement; |
| if (targetElement is InterfaceElement || |
| targetElement is ExtensionElement || |
| targetElement is TypeAliasElement) { |
| errorReporter.atOffset( |
| offset: operator.offset, |
| length: endToken.end - operator.offset, |
| errorCode: errorCode, |
| arguments: arguments, |
| ); |
| } |
| } |
| return; |
| } |
| |
| if (typeSystem.isStrictlyNonNullable(targetType)) { |
| if (errorCode == StaticWarningCode.INVALID_NULL_AWARE_OPERATOR) { |
| var previousOperator = previousShortCircuitingOperator(target); |
| if (previousOperator != null) { |
| errorReporter.reportError(DiagnosticFactory() |
| .invalidNullAwareAfterShortCircuit( |
| errorReporter.source, |
| operator.offset, |
| endToken.end - operator.offset, |
| arguments, |
| previousOperator)); |
| return; |
| } |
| } |
| errorReporter.atOffset( |
| offset: operator.offset, |
| length: endToken.end - operator.offset, |
| errorCode: errorCode, |
| arguments: arguments, |
| ); |
| } |
| } |
| |
| /// Check that if the given [name] is a reference to a static member it is |
| /// defined in the enclosing class rather than in a superclass. |
| /// |
| /// See |
| /// [CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER]. |
| void _checkForUnqualifiedReferenceToNonLocalStaticMember( |
| SimpleIdentifier name) { |
| var element = name.writeOrReadElement; |
| if (element == null || element is TypeParameterElement) { |
| return; |
| } |
| |
| var enclosingElement = element.enclosingElement; |
| if (enclosingElement == null) { |
| return; |
| } |
| |
| if (identical(enclosingElement.augmentedDeclaration, _enclosingClass)) { |
| return; |
| } |
| if (enclosingElement is! InterfaceElement) { |
| return; |
| } |
| if (element is ExecutableElement && !element.isStatic) { |
| return; |
| } |
| if (element is MethodElement) { |
| // Invalid methods are reported in |
| // [MethodInvocationResolver._resolveReceiverNull]. |
| return; |
| } |
| if (_enclosingExtension != null) { |
| errorReporter.atNode( |
| name, |
| CompileTimeErrorCode |
| .UNQUALIFIED_REFERENCE_TO_STATIC_MEMBER_OF_EXTENDED_TYPE, |
| arguments: [enclosingElement.displayName], |
| ); |
| } else { |
| errorReporter.atNode( |
| name, |
| CompileTimeErrorCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER, |
| arguments: [enclosingElement.displayName], |
| ); |
| } |
| } |
| |
| void _checkForValidField(FieldFormalParameter parameter) { |
| var parent2 = parameter.parent?.parent; |
| if (parent2 is! ConstructorDeclaration && |
| parent2?.parent is! ConstructorDeclaration) { |
| return; |
| } |
| ParameterElement element = parameter.declaredElement!; |
| if (element is FieldFormalParameterElement) { |
| var fieldElement = element.field; |
| if (fieldElement == null || fieldElement.isSynthetic) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
| arguments: [parameter.name.lexeme], |
| ); |
| } else { |
| var parameterElement = parameter.declaredElement!; |
| if (parameterElement is FieldFormalParameterElementImpl) { |
| DartType declaredType = parameterElement.type; |
| DartType fieldType = fieldElement.type; |
| if (fieldElement.isSynthetic) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
| arguments: [parameter.name.lexeme], |
| ); |
| } else if (fieldElement.isStatic) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, |
| arguments: [parameter.name.lexeme], |
| ); |
| } else if (!typeSystem.isSubtypeOf(declaredType, fieldType)) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, |
| arguments: [declaredType, fieldType], |
| ); |
| } |
| } else { |
| if (fieldElement.isSynthetic) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD, |
| arguments: [parameter.name.lexeme], |
| ); |
| } else if (fieldElement.isStatic) { |
| errorReporter.atNode( |
| parameter, |
| CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, |
| arguments: [parameter.name.lexeme], |
| ); |
| } |
| } |
| } |
| } |
| // else { |
| // TODO(jwren): Report error, constructor initializer variable is a top level element |
| // (Either here or in ErrorVerifier.checkForAllFinalInitializedErrorCodes) |
| // } |
| } |
| |
| /// Verify the given operator-method [declaration], has correct number of |
| /// parameters. |
| /// |
| /// This method assumes that the method declaration was tested to be an |
| /// operator declaration before being called. |
| /// |
| /// See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR]. |
| bool _checkForWrongNumberOfParametersForOperator( |
| MethodDeclaration declaration) { |
| // prepare number of parameters |
| var parameterList = declaration.parameters; |
| if (parameterList == null) { |
| return false; |
| } |
| int numParameters = parameterList.parameters.length; |
| // prepare operator name |
| var nameToken = declaration.name; |
| var name = nameToken.lexeme; |
| // check for exact number of parameters |
| int expected = -1; |
| if ("[]=" == name) { |
| expected = 2; |
| } else if ("<" == name || |
| ">" == name || |
| "<=" == name || |
| ">=" == name || |
| "==" == name || |
| "+" == name || |
| "/" == name || |
| "~/" == name || |
| "*" == name || |
| "%" == name || |
| "|" == name || |
| "^" == name || |
| "&" == name || |
| "<<" == name || |
| ">>" == name || |
| ">>>" == name || |
| "[]" == name) { |
| expected = 1; |
| } else if ("~" == name) { |
| expected = 0; |
| } |
| if (expected != -1 && numParameters != expected) { |
| errorReporter.atToken( |
| nameToken, |
| CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR, |
| arguments: [name, expected, numParameters], |
| ); |
| return true; |
| } else if ("-" == name && numParameters > 1) { |
| errorReporter.atToken( |
| nameToken, |
| CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS, |
| arguments: [numParameters], |
| ); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Verify that the given setter [parameterList] has only one required |
| /// parameter. The [setterName] is the name of the setter to report problems |
| /// on. |
| /// |
| /// This method assumes that the method declaration was tested to be a setter |
| /// before being called. |
| /// |
| /// See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER]. |
| void _checkForWrongNumberOfParametersForSetter( |
| Token setterName, FormalParameterList? parameterList) { |
| if (parameterList == null) { |
| return; |
| } |
| |
| NodeList<FormalParameter> parameters = parameterList.parameters; |
| if (parameters.length != 1 || !parameters[0].isRequiredPositional) { |
| errorReporter.atToken( |
| setterName, |
| CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER, |
| ); |
| } |
| } |
| |
| void _checkForWrongTypeParameterVarianceInField(FieldDeclaration node) { |
| if (_enclosingClass != null) { |
| for (var typeParameter in _enclosingClass!.typeParameters) { |
| // TODO(kallentu): : Clean up TypeParameterElementImpl casting once |
| // variance is added to the interface. |
| if (!(typeParameter as TypeParameterElementImpl).isLegacyCovariant) { |
| var fields = node.fields; |
| var fieldElement = fields.variables.first.declaredElement!; |
| var fieldName = fields.variables.first.name; |
| Variance fieldVariance = |
| typeParameter.computeVarianceInType(fieldElement.type); |
| |
| _checkForWrongVariancePosition( |
| fieldVariance, typeParameter, fieldName); |
| if (!fields.isFinal && node.covariantKeyword == null) { |
| _checkForWrongVariancePosition( |
| Variance.contravariant.combine(fieldVariance), |
| typeParameter, |
| fieldName); |
| } |
| } |
| } |
| } |
| } |
| |
| void _checkForWrongTypeParameterVarianceInMethod(MethodDeclaration method) { |
| // Only need to report errors for parameters with explicitly defined type |
| // parameters in classes or mixins. |
| if (_enclosingClass == null) { |
| return; |
| } |
| |
| for (var typeParameter in _enclosingClass!.typeParameters) { |
| // TODO(kallentu): : Clean up TypeParameterElementImpl casting once |
| // variance is added to the interface. |
| if ((typeParameter as TypeParameterElementImpl).isLegacyCovariant) { |
| continue; |
| } |
| |
| var methodTypeParameters = method.typeParameters?.typeParameters; |
| if (methodTypeParameters != null) { |
| for (var methodTypeParameter in methodTypeParameters) { |
| if (methodTypeParameter.bound == null) { |
| continue; |
| } |
| var methodTypeParameterVariance = Variance.invariant.combine( |
| typeParameter |
| .computeVarianceInType(methodTypeParameter.bound!.typeOrThrow), |
| ); |
| _checkForWrongVariancePosition( |
| methodTypeParameterVariance, typeParameter, methodTypeParameter); |
| } |
| } |
| |
| var methodParameters = method.parameters?.parameters; |
| if (methodParameters != null) { |
| for (var methodParameter in methodParameters) { |
| var methodParameterElement = methodParameter.declaredElement!; |
| if (methodParameterElement.isCovariant) { |
| continue; |
| } |
| var methodParameterVariance = Variance.contravariant.combine( |
| typeParameter.computeVarianceInType(methodParameterElement.type), |
| ); |
| _checkForWrongVariancePosition( |
| methodParameterVariance, typeParameter, methodParameter); |
| } |
| } |
| |
| var returnType = method.returnType; |
| if (returnType != null) { |
| var methodReturnTypeVariance = |
| typeParameter.computeVarianceInType(returnType.typeOrThrow); |
| _checkForWrongVariancePosition( |
| methodReturnTypeVariance, typeParameter, returnType); |
| } |
| } |
| } |
| |
| void _checkForWrongTypeParameterVarianceInSuperinterfaces() { |
| void checkOne(DartType? superInterface) { |
| if (superInterface != null) { |
| for (var typeParameter in _enclosingClass!.typeParameters) { |
| // TODO(kallentu): : Clean up TypeParameterElementImpl casting once |
| // variance is added to the interface. |
| var typeParameterElementImpl = |
| typeParameter as TypeParameterElementImpl; |
| var superVariance = |
| typeParameterElementImpl.computeVarianceInType(superInterface); |
| // Let `D` be a class or mixin declaration, let `S` be a direct |
| // superinterface of `D`, and let `X` be a type parameter declared by |
| // `D`. |
| // If `X` is an `out` type parameter, it can only occur in `S` in an |
| // covariant or unrelated position. |
| // If `X` is an `in` type parameter, it can only occur in `S` in an |
| // contravariant or unrelated position. |
| // If `X` is an `inout` type parameter, it can occur in `S` in any |
| // position. |
| if (!superVariance |
| .greaterThanOrEqual(typeParameterElementImpl.variance)) { |
| if (!typeParameterElementImpl.isLegacyCovariant) { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode |
| .WRONG_EXPLICIT_TYPE_PARAMETER_VARIANCE_IN_SUPERINTERFACE, |
| arguments: [ |
| typeParameter.name, |
| typeParameterElementImpl.variance.keyword, |
| superVariance.keyword, |
| superInterface, |
| ], |
| ); |
| } else { |
| errorReporter.atElement( |
| typeParameter, |
| CompileTimeErrorCode |
| .WRONG_TYPE_PARAMETER_VARIANCE_IN_SUPERINTERFACE, |
| arguments: [typeParameter.name, superInterface], |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| checkOne(_enclosingClass!.supertype); |
| _enclosingClass!.interfaces.forEach(checkOne); |
| _enclosingClass!.mixins.forEach(checkOne); |
| |
| var enclosingClass = _enclosingClass; |
| if (enclosingClass is MixinElement) { |
| enclosingClass.superclassConstraints.forEach(checkOne); |
| } |
| } |
| |
| /// Check for invalid variance positions in members of a class or mixin. |
| /// |
| /// Let `C` be a class or mixin declaration with type parameter `T`. |
| /// If `T` is an `out` type parameter then `T` can only appear in covariant |
| /// positions within the accessors and methods of `C`. |
| /// If `T` is an `in` type parameter then `T` can only appear in contravariant |
| /// positions within the accessors and methods of `C`. |
| /// If `T` is an `inout` type parameter or a type parameter with no explicit |
| /// variance modifier then `T` can appear in any variant position within the |
| /// accessors and methods of `C`. |
| /// |
| /// Errors should only be reported in classes and mixins since those are the |
| /// only components that allow explicit variance modifiers. |
| void _checkForWrongVariancePosition(Variance variance, |
| TypeParameterElement typeParameter, SyntacticEntity errorTarget) { |
| TypeParameterElementImpl typeParameterImpl = |
| typeParameter as TypeParameterElementImpl; |
| if (!variance.greaterThanOrEqual(typeParameterImpl.variance)) { |
| errorReporter.atOffset( |
| offset: errorTarget.offset, |
| length: errorTarget.length, |
| errorCode: CompileTimeErrorCode.WRONG_TYPE_PARAMETER_VARIANCE_POSITION, |
| arguments: [ |
| typeParameterImpl.variance.keyword, |
| typeParameterImpl.name, |
| variance.keyword |
| ], |
| ); |
| } |
| } |
| |
| /// Verify that the current class does not have the same class in the |
| /// 'extends' and 'implements' clauses. |
| /// |
| /// See [CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS]. |
| void _checkImplementsSuperClass(ImplementsClause? implementsClause) { |
| if (implementsClause == null) { |
| return; |
| } |
| |
| var superElement = _enclosingClass!.supertype?.element; |
| if (superElement == null) { |
| return; |
| } |
| |
| for (var interfaceNode in implementsClause.interfaces) { |
| var type = interfaceNode.type; |
| if (type is InterfaceType && type.element == superElement) { |
| errorReporter.atNode( |
| interfaceNode, |
| CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS, |
| arguments: [superElement], |
| ); |
| } |
| } |
| } |
| |
| /// Checks the class for problems with the superclass, mixins, or implemented |
| /// interfaces. |
| void _checkMixinInheritance(MixinDeclaration node, MixinOnClause? onClause, |
| ImplementsClause? implementsClause) { |
| // Only check for all of the inheritance logic around clauses if there |
| // isn't an error code such as "Cannot implement double" already. |
| if (!_checkForOnClauseErrorCodes(onClause) && |
| !_checkForImplementsClauseErrorCodes(implementsClause)) { |
| // _checkForImplicitDynamicType(superclass); |
| _checkForRepeatedType( |
| onClause?.superclassConstraints, |
| CompileTimeErrorCode.ON_REPEATED, |
| ); |
| _checkForRepeatedType( |
| implementsClause?.interfaces, |
| CompileTimeErrorCode.IMPLEMENTS_REPEATED, |
| ); |
| _checkForConflictingGenerics(node); |
| _checkForBaseClassOrMixinImplementedOutsideOfLibrary(implementsClause); |
| _checkForFinalSupertypeOutsideOfLibrary( |
| null, null, implementsClause, onClause); |
| _checkForSealedSupertypeOutsideOfLibrary( |
| null, null, implementsClause, onClause); |
| } |
| } |
| |
| /// Verify that the current class does not have the same class in the |
| /// 'extends' and 'with' clauses. |
| /// |
| /// See [CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS]. |
| void _checkMixinsSuperClass(WithClause? withClause) { |
| if (withClause == null) { |
| return; |
| } |
| |
| var superElement = _enclosingClass!.supertype?.element; |
| if (superElement == null) { |
| return; |
| } |
| |
| for (var mixinNode in withClause.mixinTypes) { |
| var type = mixinNode.type; |
| if (type is InterfaceType && type.element == superElement) { |
| errorReporter.atNode( |
| mixinNode, |
| CompileTimeErrorCode.MIXINS_SUPER_CLASS, |
| arguments: [superElement], |
| ); |
| } |
| } |
| } |
| |
| void _checkUseOfCovariantInParameters(FormalParameterList node) { |
| var parent = node.parent; |
| if (_enclosingClass != null && parent is MethodDeclaration) { |
| // Either [parent] is a static method, in which case `EXTRANEOUS_MODIFIER` |
| // is reported by the parser, or [parent] is an instance method, in which |
| // case any use of `covariant` is legal. |
| return; |
| } |
| |
| if (_enclosingExtension != null) { |
| // `INVALID_USE_OF_COVARIANT_IN_EXTENSION` is reported by the parser. |
| return; |
| } |
| |
| if (parent is FunctionExpression) { |
| var parent2 = parent.parent; |
| if (parent2 is FunctionDeclaration && parent2.parent is CompilationUnit) { |
| // `EXTRANEOUS_MODIFIER` is reported by the parser, for library-level |
| // functions. |
| return; |
| } |
| } |
| |
| NodeList<FormalParameter> parameters = node.parameters; |
| int length = parameters.length; |
| for (int i = 0; i < length; i++) { |
| var parameter = parameters[i].notDefault; |
| var keyword = parameter.covariantKeyword; |
| if (keyword != null) { |
| errorReporter.atToken( |
| keyword, |
| CompileTimeErrorCode.INVALID_USE_OF_COVARIANT, |
| ); |
| } |
| } |
| } |
| |
| void _checkUseOfDefaultValuesInParameters(FormalParameterList node) { |
| var defaultValuesAreExpected = () { |
| var parent = node.parent; |
| if (parent is ConstructorDeclaration) { |
| if (parent.externalKeyword != null) { |
| return false; |
| } else if (parent.factoryKeyword != null && |
| parent.redirectedConstructor != null) { |
| return false; |
| } |
| return true; |
| } else if (parent is FunctionExpression) { |
| var parent2 = parent.parent; |
| if (parent2 is FunctionDeclaration && parent2.externalKeyword != null) { |
| return false; |
| } else if (parent.body is NativeFunctionBody) { |
| return false; |
| } |
| return true; |
| } else if (parent is MethodDeclaration) { |
| if (parent.isAbstract) { |
| return false; |
| } else if (parent.externalKeyword != null) { |
| return false; |
| } else if (parent.body is NativeFunctionBody) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| }(); |
| |
| for (var parameter in node.parameters) { |
| if (parameter is DefaultFormalParameter) { |
| if (parameter.isRequiredNamed) { |
| if (parameter.defaultValue != null) { |
| var errorTarget = _parameterName(parameter) ?? parameter; |
| errorReporter.atOffset( |
| offset: errorTarget.offset, |
| length: errorTarget.length, |
| errorCode: |
| CompileTimeErrorCode.DEFAULT_VALUE_ON_REQUIRED_PARAMETER, |
| ); |
| } |
| } else if (defaultValuesAreExpected) { |
| var parameterElement = parameter.declaredElement!; |
| if (!parameterElement.hasDefaultValue) { |
| var type = parameterElement.type; |
| if (typeSystem.isPotentiallyNonNullable(type)) { |
| var parameterName = _parameterName(parameter); |
| var errorTarget = parameterName ?? parameter; |
| |
| List<Object> arguments = const []; |
| ErrorCode errorCode; |
| if (parameterElement.hasRequired) { |
| errorCode = CompileTimeErrorCode |
| .MISSING_DEFAULT_VALUE_FOR_PARAMETER_WITH_ANNOTATION; |
| } else { |
| errorCode = parameterElement.isPositional |
| ? CompileTimeErrorCode |
| .MISSING_DEFAULT_VALUE_FOR_PARAMETER_POSITIONAL |
| : CompileTimeErrorCode.MISSING_DEFAULT_VALUE_FOR_PARAMETER; |
| arguments = [parameterName?.lexeme ?? '?']; |
| } |
| errorReporter.atOffset( |
| offset: errorTarget.offset, |
| length: errorTarget.length, |
| errorCode: errorCode, |
| arguments: arguments, |
| ); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| bool _computeThisAccessForFunctionBody(FunctionBody node) => |
| switch (node.parent) { |
| ConstructorDeclaration(:var factoryKeyword) => factoryKeyword == null, |
| MethodDeclaration(:var isStatic) => !isStatic, |
| _ => _hasAccessToThis |
| }; |
| |
| /// Given an [expression] in a switch case whose value is expected to be an |
| /// enum constant, return the name of the constant. |
| String? _getConstantName(Expression expression) { |
| // TODO(brianwilkerson): Convert this to return the element representing the |
| // constant. |
| if (expression is SimpleIdentifier) { |
| return expression.name; |
| } else if (expression is PrefixedIdentifier) { |
| return expression.identifier.name; |
| } else if (expression is PropertyAccess) { |
| return expression.propertyName.name; |
| } |
| return null; |
| } |
| |
| /// Return the name of the library that defines given [element]. |
| String _getLibraryName(Element? element) { |
| if (element == null) { |
| return ''; |
| } |
| var library = element.library; |
| if (library == null) { |
| return ''; |
| } |
| var imports = _currentLibrary.libraryImports; |
| int count = imports.length; |
| for (int i = 0; i < count; i++) { |
| if (identical(imports[i].importedLibrary, library)) { |
| return library.definingCompilationUnit.source.uri.toString(); |
| } |
| } |
| List<String> indirectSources = <String>[]; |
| for (int i = 0; i < count; i++) { |
| var importedLibrary = imports[i].importedLibrary; |
| if (importedLibrary != null) { |
| for (LibraryElement exportedLibrary |
| in importedLibrary.exportedLibraries) { |
| if (identical(exportedLibrary, library)) { |
| indirectSources.add( |
| importedLibrary.definingCompilationUnit.source.uri.toString()); |
| } |
| } |
| } |
| } |
| int indirectCount = indirectSources.length; |
| StringBuffer buffer = StringBuffer(); |
| buffer.write(library.definingCompilationUnit.source.uri.toString()); |
| if (indirectCount > 0) { |
| buffer.write(" (via "); |
| if (indirectCount > 1) { |
| indirectSources.sort(); |
| buffer.write(indirectSources.quotedAndCommaSeparatedWithAnd); |
| } else { |
| buffer.write(indirectSources[0]); |
| } |
| buffer.write(")"); |
| } |
| return buffer.toString(); |
| } |
| |
| /// Return `true` if the given [constructor] redirects to itself, directly or |
| /// indirectly. |
| bool _hasRedirectingFactoryConstructorCycle(ConstructorElement constructor) { |
| Set<ConstructorElement> constructors = HashSet<ConstructorElement>(); |
| ConstructorElement? current = constructor; |
| while (current != null) { |
| if (constructors.contains(current)) { |
| return identical(current, constructor); |
| } |
| constructors.add(current); |
| current = current.redirectedConstructor?.declaration; |
| } |
| return false; |
| } |
| |
| /// Returns `true` if the given [library] is the `dart:ffi` library. |
| bool _isDartFfiLibrary(LibraryElement library) => library.name == 'dart.ffi'; |
| |
| /// Return `true` if the given [identifier] is in a location where it is |
| /// allowed to resolve to a static member of a supertype. |
| bool _isUnqualifiedReferenceToNonLocalStaticMemberAllowed( |
| SimpleIdentifier identifier) { |
| if (identifier.inDeclarationContext()) { |
| return true; |
| } |
| var parent = identifier.parent; |
| if (parent is Annotation) { |
| return identical(parent.constructorName, identifier); |
| } |
| if (parent is CommentReference) { |
| return true; |
| } |
| if (parent is ConstructorName) { |
| return identical(parent.name, identifier); |
| } |
| if (parent is MethodInvocation) { |
| return identical(parent.methodName, identifier); |
| } |
| if (parent is PrefixedIdentifier) { |
| return identical(parent.identifier, identifier); |
| } |
| if (parent is PropertyAccess) { |
| return identical(parent.propertyName, identifier); |
| } |
| if (parent is SuperConstructorInvocation) { |
| return identical(parent.constructorName, identifier); |
| } |
| return false; |
| } |
| |
| /// Return `true` if the [importElement] is the internal library `dart:_wasm` |
| /// and the current library is either `package:js/js.dart` or is in |
| /// `package:ui`. |
| bool _isWasm(LibraryImportElement importElement) { |
| var importedUri = importElement.importedLibrary?.source.uri.toString(); |
| if (importedUri != 'dart:_wasm') { |
| return false; |
| } |
| var importingUri = _currentLibrary.source.uri.toString(); |
| if (importingUri == 'package:js/js.dart') { |
| return true; |
| } else if (importingUri.startsWith('package:ui/')) { |
| return true; |
| } |
| return false; |
| } |
| |
| /// Checks whether a `final`, `base` or `interface` modifier can be ignored. |
| /// |
| /// Checks whether a subclass in the current library |
| /// can ignore a class modifier of a declaration in [superLibrary]. |
| /// |
| /// Only true if the supertype library is a platform library, and |
| /// either the current library is also a platform library, |
| /// or the current library has a language version which predates |
| /// class modifiers |
| bool _mayIgnoreClassModifiers(LibraryElement superLibrary) { |
| // Only modifiers in platform libraries can be ignored. |
| if (!superLibrary.isInSdk) return false; |
| |
| // Modifiers in 'dart:ffi' can't be ignored in pre-feature code. |
| if (_isDartFfiLibrary(superLibrary)) { |
| return false; |
| } |
| |
| // Other platform libraries can ignore modifiers. |
| if (_currentLibrary.isInSdk) return true; |
| |
| // Libraries predating class modifiers can ignore platform modifiers. |
| return !_currentLibrary.featureSet.isEnabled(Feature.class_modifiers); |
| } |
| |
| /// Return the name of the [parameter], or `null` if the parameter does not |
| /// have a name. |
| Token? _parameterName(FormalParameter parameter) { |
| if (parameter is NormalFormalParameter) { |
| return parameter.name; |
| } else if (parameter is DefaultFormalParameter) { |
| return parameter.parameter.name; |
| } |
| return null; |
| } |
| |
| void _reportMacroDiagnostics(MacroTargetElement element) { |
| _MacroDiagnosticsReporter( |
| libraryContext: libraryVerificationContext, |
| errorReporter: errorReporter, |
| element: element, |
| ).report(); |
| } |
| |
| void _withEnclosingExecutable( |
| ExecutableElement element, |
| void Function() operation, |
| ) { |
| var current = _enclosingExecutable; |
| try { |
| _enclosingExecutable = EnclosingExecutableContext(element); |
| _returnTypeVerifier.enclosingExecutable = _enclosingExecutable; |
| operation(); |
| } finally { |
| _enclosingExecutable = current; |
| _returnTypeVerifier.enclosingExecutable = _enclosingExecutable; |
| } |
| } |
| |
| void _withHiddenElements(List<Statement> statements, void Function() f) { |
| _hiddenElements = HiddenElements(_hiddenElements, statements); |
| try { |
| f(); |
| } finally { |
| _hiddenElements = _hiddenElements!.outerElements; |
| } |
| } |
| |
| void _withHiddenElementsGuardedPattern( |
| GuardedPatternImpl guardedPattern, void Function() f) { |
| _hiddenElements = |
| HiddenElements.forGuardedPattern(_hiddenElements, guardedPattern); |
| try { |
| f(); |
| } finally { |
| _hiddenElements = _hiddenElements!.outerElements; |
| } |
| } |
| |
| /// Return [FieldElement]s that are declared in the [ClassDeclaration] with |
| /// the given [constructor], but are not initialized. |
| static List<FieldElement> computeNotInitializedFields( |
| ConstructorDeclaration constructor) { |
| Set<FieldElement> fields = <FieldElement>{}; |
| var classDeclaration = constructor.parent as ClassDeclaration; |
| for (ClassMember fieldDeclaration in classDeclaration.members) { |
| if (fieldDeclaration is FieldDeclaration) { |
| for (VariableDeclaration field in fieldDeclaration.fields.variables) { |
| if (field.initializer == null) { |
| fields.add(field.declaredElement as FieldElement); |
| } |
| } |
| } |
| } |
| |
| List<FormalParameter> parameters = constructor.parameters.parameters; |
| for (FormalParameter parameter in parameters) { |
| parameter = parameter.notDefault; |
| if (parameter is FieldFormalParameter) { |
| var element = parameter.declaredElement as FieldFormalParameterElement; |
| fields.remove(element.field); |
| } |
| } |
| |
| for (ConstructorInitializer initializer in constructor.initializers) { |
| if (initializer is ConstructorFieldInitializer) { |
| fields.remove(initializer.fieldName.staticElement); |
| } |
| } |
| |
| return fields.toList(); |
| } |
| } |
| |
| /// A record of the elements that will be declared in some scope (block), but |
| /// are not yet declared. |
| class HiddenElements { |
| /// The elements hidden in outer scopes, or `null` if this is the outermost |
| /// scope. |
| final HiddenElements? outerElements; |
| |
| /// A set containing the elements that will be declared in this scope, but are |
| /// not yet declared. |
| final Set<Element> _elements = HashSet<Element>(); |
| |
| /// Initialize a newly created set of hidden elements to include all of the |
| /// elements defined in the set of [outerElements] and all of the elements |
| /// declared in the given [statements]. |
| HiddenElements(this.outerElements, List<Statement> statements) { |
| _initializeElements(statements); |
| } |
| |
| /// Initialize a newly created set of hidden elements to include all of the |
| /// elements defined in the set of [outerElements] and all of the elements |
| /// declared in the given [guardedPattern]. |
| HiddenElements.forGuardedPattern( |
| this.outerElements, |
| GuardedPatternImpl guardedPattern, |
| ) { |
| _elements.addAll(guardedPattern.variables.values); |
| } |
| |
| /// Return `true` if this set of elements contains the given [element]. |
| bool contains(Element element) { |
| if (_elements.contains(element)) { |
| return true; |
| } else if (outerElements != null) { |
| return outerElements!.contains(element); |
| } |
| return false; |
| } |
| |
| /// Record that the given [element] has been declared, so it is no longer |
| /// hidden. |
| void declare(Element element) { |
| _elements.remove(element); |
| } |
| |
| /// Initialize the list of elements that are not yet declared to be all of the |
| /// elements declared somewhere in the given [statements]. |
| void _initializeElements(List<Statement> statements) { |
| _elements.addAll(BlockScope.elementsInStatements(statements)); |
| } |
| } |
| |
| /// Information to pass from from the defining unit to augmentations. |
| class LibraryVerificationContext { |
| final duplicationDefinitionContext = DuplicationDefinitionContext(); |
| final LibraryFileKind libraryKind; |
| final ConstructorFieldsVerifier constructorFieldsVerifier; |
| final Map<FileState, UnitAnalysis> units; |
| |
| LibraryVerificationContext({ |
| required this.libraryKind, |
| required this.constructorFieldsVerifier, |
| required this.units, |
| }); |
| |
| _MacroSyntacticTypeAnnotationLocation? declarationByElement(Element element) { |
| var unitElement = element.thisOrAncestorOfType<CompilationUnitElement>(); |
| if (unitElement == null) { |
| return null; |
| } |
| |
| var uri = unitElement.source.uri; |
| var unitAnalysis = units.entries.firstWhereOrNull((entry) { |
| return entry.key.uri == uri; |
| })?.value; |
| if (unitAnalysis == null) { |
| return null; |
| } |
| |
| var locator = DeclarationByElementLocator(element); |
| unitAnalysis.unit.accept(locator); |
| |
| var node = locator.result; |
| if (node == null) { |
| return null; |
| } |
| |
| return _MacroSyntacticTypeAnnotationLocation( |
| unitAnalysis: unitAnalysis, |
| entity: node, |
| ); |
| } |
| |
| bool libraryCycleContains(Uri uri) { |
| return libraryKind.libraryCycle.libraryUris.contains(uri); |
| } |
| } |
| |
| class _MacroDiagnosticsReporter { |
| final LibraryVerificationContext libraryContext; |
| final ErrorReporter errorReporter; |
| final MacroTargetElement element; |
| |
| _MacroDiagnosticsReporter({ |
| required this.libraryContext, |
| required this.errorReporter, |
| required this.element, |
| }); |
| |
| void report() { |
| _reportApplicationFromSameLibraryCycle(); |
| |
| for (var diagnostic in element.macroDiagnostics) { |
| switch (diagnostic) { |
| case ArgumentMacroDiagnostic(): |
| _reportArgument(diagnostic); |
| case DeclarationsIntrospectionCycleDiagnostic(): |
| _reportIntrospectionCycle(diagnostic); |
| case ExceptionMacroDiagnostic(): |
| _reportException(diagnostic); |
| case InvalidMacroTargetDiagnostic(): |
| _reportInvalidTarget(diagnostic); |
| case MacroDiagnostic(): |
| _reportCustom(diagnostic); |
| } |
| } |
| } |
| |
| DiagnosticMessage _convertMessage(MacroDiagnosticMessage object) { |
| var target = object.target; |
| switch (target) { |
| case ApplicationMacroDiagnosticTarget(): |
| var node = element.annotationAst(target.annotationIndex); |
| return DiagnosticMessageImpl( |
| filePath: element.source!.fullName, |
| length: node.length, |
| message: object.message, |
| offset: node.offset, |
| url: null, |
| ); |
| case ElementMacroDiagnosticTarget(): |
| var element = target.element; |
| return DiagnosticMessageImpl( |
| filePath: element.source!.fullName, |
| length: element.nameLength, |
| message: object.message, |
| offset: element.nameOffset, |
| url: null, |
| ); |
| case TypeAnnotationMacroDiagnosticTarget(): |
| // TODO(scheglov): Handle this case. |
| throw UnimplementedError(); |
| case ElementAnnotationMacroDiagnosticTarget(): |
| // TODO(scheglov): Handle this case. |
| throw UnimplementedError(); |
| } |
| } |
| |
| void _reportApplicationFromSameLibraryCycle() { |
| for (var annotation in element.metadata) { |
| var element = annotation.element; |
| if (element is! ConstructorElementImpl) continue; |
| |
| var macroElement = element.enclosingElement; |
| if (macroElement is! ClassElementImpl) continue; |
| if (!macroElement.isMacro) continue; |
| |
| var macroUri = macroElement.library.source.uri; |
| if (!libraryContext.libraryCycleContains(macroUri)) continue; |
| |
| errorReporter.atNode( |
| _annotationNameIdentifier(annotation), |
| CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE, |
| arguments: [ |
| macroElement.name, |
| ], |
| ); |
| } |
| } |
| |
| void _reportArgument(ArgumentMacroDiagnostic diagnostic) { |
| var annotation = element.annotationAst(diagnostic.annotationIndex); |
| var arguments = annotation.arguments!.arguments; |
| errorReporter.atNode( |
| arguments[diagnostic.argumentIndex], |
| CompileTimeErrorCode.MACRO_APPLICATION_ARGUMENT_ERROR, |
| arguments: [diagnostic.message], |
| ); |
| } |
| |
| void _reportCustom(MacroDiagnostic diagnostic) { |
| var errorCode = switch (diagnostic.severity) { |
| macro.Severity.info => HintCode.MACRO_INFO, |
| macro.Severity.warning => WarningCode.MACRO_WARNING, |
| macro.Severity.error => CompileTimeErrorCode.MACRO_ERROR, |
| }; |
| |
| var contextMessages = |
| diagnostic.contextMessages.map(_convertMessage).toList(); |
| |
| var target = diagnostic.message.target; |
| switch (target) { |
| case ApplicationMacroDiagnosticTarget(): |
| var node = element.annotationAst(target.annotationIndex); |
| errorReporter.reportError( |
| AnalysisError.forValues( |
| source: element.source!, |
| offset: node.offset, |
| length: node.length, |
| errorCode: errorCode, |
| message: diagnostic.message.message, |
| correctionMessage: diagnostic.correctionMessage, |
| contextMessages: contextMessages, |
| ), |
| ); |
| case ElementMacroDiagnosticTarget(): |
| errorReporter.reportError( |
| AnalysisError.forValues( |
| source: target.element.source!, |
| offset: target.element.nameOffset, |
| length: target.element.nameLength, |
| errorCode: errorCode, |
| message: diagnostic.message.message, |
| correctionMessage: diagnostic.correctionMessage, |
| contextMessages: contextMessages, |
| ), |
| ); |
| case ElementAnnotationMacroDiagnosticTarget(): |
| var location = libraryContext.declarationByElement( |
| target.element, |
| ); |
| if (location == null) { |
| return; |
| } |
| var node = target.element.annotationAst(target.annotationIndex); |
| location.unitAnalysis.errorReporter.reportError( |
| AnalysisError.forValues( |
| source: target.element.source!, |
| offset: node.offset, |
| length: node.length, |
| errorCode: errorCode, |
| message: diagnostic.message.message, |
| correctionMessage: diagnostic.correctionMessage, |
| contextMessages: contextMessages, |
| ), |
| ); |
| case TypeAnnotationMacroDiagnosticTarget(): |
| var nodeLocation = _MacroTypeAnnotationLocationConverter( |
| libraryVerificationContext: libraryContext, |
| ).convert(target.location); |
| var unitAnalysis = nodeLocation?.unitAnalysis; |
| var errorEntity = nodeLocation?.entity; |
| if (unitAnalysis != null && errorEntity != null) { |
| unitAnalysis.errorReporter.reportError( |
| AnalysisError.forValues( |
| source: unitAnalysis.element.source, |
| offset: errorEntity.offset, |
| length: errorEntity.length, |
| errorCode: errorCode, |
| message: diagnostic.message.message, |
| correctionMessage: diagnostic.correctionMessage, |
| contextMessages: contextMessages, |
| ), |
| ); |
| } |
| } |
| } |
| |
| void _reportException(ExceptionMacroDiagnostic diagnostic) { |
| errorReporter.atNode( |
| element.annotationAst(diagnostic.annotationIndex), |
| CompileTimeErrorCode.MACRO_INTERNAL_EXCEPTION, |
| arguments: [ |
| diagnostic.message, |
| diagnostic.stackTrace, |
| ], |
| ); |
| } |
| |
| void _reportIntrospectionCycle( |
| DeclarationsIntrospectionCycleDiagnostic diagnostic, |
| ) { |
| var messages = diagnostic.components.map<DiagnosticMessage>( |
| (component) { |
| var target = _macroAnnotationNameIdentifier( |
| element: component.element, |
| annotationIndex: component.annotationIndex, |
| ); |
| var introspectedName = component.introspectedElement.name; |
| return DiagnosticMessageImpl( |
| filePath: component.element.source!.fullName, |
| length: target.length, |
| message: "The macro application introspects '$introspectedName'.", |
| offset: target.offset, |
| url: null, |
| ); |
| }, |
| ).toList(); |
| |
| errorReporter.atNode( |
| _macroAnnotationNameIdentifier( |
| element: element, |
| annotationIndex: diagnostic.annotationIndex, |
| ), |
| CompileTimeErrorCode.MACRO_DECLARATIONS_PHASE_INTROSPECTION_CYCLE, |
| arguments: [diagnostic.introspectedElement.name!], |
| contextMessages: messages, |
| ); |
| } |
| |
| void _reportInvalidTarget(InvalidMacroTargetDiagnostic diagnostic) { |
| errorReporter.atNode( |
| element.annotationAst(diagnostic.annotationIndex), |
| CompileTimeErrorCode.INVALID_MACRO_APPLICATION_TARGET, |
| arguments: [ |
| diagnostic.supportedKinds.commaSeparatedWithOr, |
| ], |
| ); |
| } |
| |
| static SimpleIdentifier _annotationNameIdentifier( |
| ElementAnnotationImpl annotation, |
| ) { |
| var fullName = annotation.annotationAst.name; |
| if (fullName is PrefixedIdentifierImpl) { |
| return fullName.identifier; |
| } else { |
| return fullName as SimpleIdentifierImpl; |
| } |
| } |
| |
| static SimpleIdentifier _macroAnnotationNameIdentifier({ |
| required ElementImpl element, |
| required int annotationIndex, |
| }) { |
| var annotationNode = element.annotationAst(annotationIndex); |
| var fullName = annotationNode.name; |
| if (fullName is PrefixedIdentifierImpl) { |
| return fullName.identifier; |
| } else { |
| return fullName as SimpleIdentifierImpl; |
| } |
| } |
| } |
| |
| class _MacroSyntacticTypeAnnotationLocation { |
| final UnitAnalysis unitAnalysis; |
| |
| /// Usually a [AstNode], sometimes [Token] if the type is omitted. |
| final SyntacticEntity entity; |
| |
| _MacroSyntacticTypeAnnotationLocation({ |
| required this.unitAnalysis, |
| required this.entity, |
| }); |
| |
| _MacroSyntacticTypeAnnotationLocation next(SyntacticEntity entity) { |
| return _MacroSyntacticTypeAnnotationLocation( |
| unitAnalysis: unitAnalysis, |
| entity: entity, |
| ); |
| } |
| } |
| |
| class _MacroTypeAnnotationLocationConverter { |
| final LibraryVerificationContext libraryVerificationContext; |
| |
| _MacroTypeAnnotationLocationConverter({ |
| required this.libraryVerificationContext, |
| }); |
| |
| /// Returns the syntactic location for the offset independent [location]; |
| _MacroSyntacticTypeAnnotationLocation? convert( |
| TypeAnnotationLocation location, |
| ) { |
| switch (location) { |
| case AliasedTypeLocation(): |
| return _aliasedType(location); |
| case ElementTypeLocation(): |
| var element = location.element; |
| return libraryVerificationContext.declarationByElement(element); |
| case ExtendsClauseTypeLocation(): |
| return _extendsClause(location); |
| case FormalParameterTypeLocation(): |
| return _formalParameter(location); |
| case ListIndexTypeLocation(): |
| return _listIndex(location); |
| case RecordNamedFieldTypeLocation(): |
| return _recordNamedField(location); |
| case RecordPositionalFieldTypeLocation(): |
| return _recordPositionalField(location); |
| case ReturnTypeLocation(): |
| return _returnType(location); |
| case VariableTypeLocation(): |
| return _variableType(location); |
| default: |
| throw UnimplementedError('${location.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _aliasedType( |
| AliasedTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case GenericTypeAlias(): |
| return nodeLocation.next(node.type); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _extendsClause( |
| ExtendsClauseTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case ClassDeclaration(): |
| var next = node.extendsClause!.superclass; |
| return nodeLocation.next(next); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _formalParameter( |
| FormalParameterTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case FunctionDeclaration(): |
| var parameterList = node.functionExpression.parameters; |
| var next = parameterList!.parameters[location.index]; |
| return nodeLocation.next(next); |
| case GenericFunctionType(): |
| var parameterList = node.parameters; |
| var parameter = parameterList.parameters[location.index]; |
| parameter = parameter.notDefault; |
| return nodeLocation.next(parameter.typeOrSelf); |
| case MethodDeclaration(): |
| var parameterList = node.parameters; |
| var next = parameterList!.parameters[location.index]; |
| return nodeLocation.next(next); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _listIndex( |
| ListIndexTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case NamedType(): |
| var argument = node.typeArguments?.arguments[location.index]; |
| if (argument == null) { |
| return null; |
| } |
| return nodeLocation.next(argument); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _recordNamedField( |
| RecordNamedFieldTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case RecordTypeAnnotation(): |
| var field = node.namedFields?.fields[location.index].type; |
| if (field == null) { |
| return null; |
| } |
| return nodeLocation.next(field); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _recordPositionalField( |
| RecordPositionalFieldTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case RecordTypeAnnotation(): |
| var field = node.positionalFields[location.index]; |
| return nodeLocation.next(field); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _returnType( |
| ReturnTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| switch (node) { |
| case FunctionDeclaration(): |
| var next = node.returnType ?? node.name; |
| return nodeLocation.next(next); |
| case GenericFunctionType(): |
| var next = node.returnType ?? node; |
| return nodeLocation.next(next); |
| case MethodDeclaration(): |
| var next = node.returnType ?? node.name; |
| return nodeLocation.next(next); |
| default: |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| _MacroSyntacticTypeAnnotationLocation? _variableType( |
| VariableTypeLocation location, |
| ) { |
| var nodeLocation = convert(location.parent); |
| if (nodeLocation == null) { |
| return null; |
| } |
| var node = nodeLocation.entity; |
| if (node is DefaultFormalParameter) { |
| node = node.parameter; |
| } |
| var parent = node.ifTypeOrNull<AstNode>()?.parent; |
| switch (node) { |
| case SimpleFormalParameter(): |
| var next = node.type ?? node.name; |
| if (next == null) { |
| return null; |
| } |
| return nodeLocation.next(next); |
| case VariableDeclaration(): |
| if (parent is VariableDeclarationList) { |
| var next = parent.type ?? node.name; |
| return nodeLocation.next(next); |
| } |
| } |
| throw UnimplementedError( |
| '${node.runtimeType} ${parent.runtimeType}', |
| ); |
| } |
| } |
| |
| /// Recursively visits a type annotation, looking uninstantiated bounds. |
| class _UninstantiatedBoundChecker extends RecursiveAstVisitor<void> { |
| final ErrorReporter _errorReporter; |
| |
| _UninstantiatedBoundChecker(this._errorReporter); |
| |
| @override |
| void visitNamedType(NamedType node) { |
| var typeArgs = node.typeArguments; |
| if (typeArgs != null) { |
| typeArgs.accept(this); |
| return; |
| } |
| |
| var element = node.element; |
| if (element is TypeParameterizedElement && !element.isSimplyBounded) { |
| // TODO(srawlins): Don't report this if TYPE_ALIAS_CANNOT_REFERENCE_ITSELF |
| // has been reported. |
| _errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.NOT_INSTANTIATED_BOUND, |
| ); |
| } |
| } |
| } |