| // 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 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; |
| import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis_operations.dart' |
| as shared; |
| import 'package:_fe_analyzer_shared/src/type_inference/null_shorting.dart'; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart' |
| as shared; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analysis_result.dart'; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart' |
| as shared; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart'; |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer_operations.dart' |
| as shared; |
| import 'package:_fe_analyzer_shared/src/types/shared_type.dart'; |
| import 'package:analyzer/dart/analysis/analysis_options.dart'; |
| 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/scope.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/source/source.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/ast/extensions.dart'; |
| import 'package:analyzer/src/dart/ast/utilities.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/extensions.dart'; |
| import 'package:analyzer/src/dart/element/generic_inferrer.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/scope.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_schema.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/resolver/annotation_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/binary_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/body_inference_context.dart'; |
| import 'package:analyzer/src/dart/resolver/constructor_reference_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; |
| import 'package:analyzer/src/dart/resolver/for_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/function_expression_invocation_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/function_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/function_reference_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/instance_creation_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart'; |
| import 'package:analyzer/src/dart/resolver/invocation_inferrer.dart'; |
| import 'package:analyzer/src/dart/resolver/lexical_lookup.dart'; |
| import 'package:analyzer/src/dart/resolver/list_pattern_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/postfix_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/prefix_expression_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/prefixed_identifier_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/property_element_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/record_literal_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/dart/resolver/shared_type_analyzer.dart'; |
| import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/this_lookup.dart'; |
| import 'package:analyzer/src/dart/resolver/type_property_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/variable_declaration_resolver.dart'; |
| import 'package:analyzer/src/dart/resolver/yield_statement_resolver.dart'; |
| import 'package:analyzer/src/diagnostic/diagnostic.dart'; |
| import 'package:analyzer/src/error/base_or_final_type_verifier.dart'; |
| import 'package:analyzer/src/error/bool_expression_verifier.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/error/dead_code_verifier.dart'; |
| import 'package:analyzer/src/error/inference_error.dart'; |
| import 'package:analyzer/src/error/nullable_dereference_verifier.dart'; |
| import 'package:analyzer/src/error/super_formal_parameters_verifier.dart'; |
| import 'package:analyzer/src/generated/element_resolver.dart'; |
| import 'package:analyzer/src/generated/error_detection_helpers.dart'; |
| import 'package:analyzer/src/generated/inference_log.dart'; |
| import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/generated/variable_type_provider.dart'; |
| import 'package:analyzer/src/util/ast_data_extractor.dart'; |
| import 'package:analyzer/src/utilities/extensions/element.dart'; |
| import 'package:analyzer/src/utilities/extensions/object.dart'; |
| |
| /// Function determining which source files should have inference logging |
| /// enabled. |
| /// |
| /// By default, no files have inference logging enabled. |
| bool Function(Source) inferenceLoggingPredicate = (_) => false; |
| |
| typedef SharedMatchContext = |
| shared.MatchContext< |
| AstNodeImpl, |
| ExpressionImpl, |
| DartPatternImpl, |
| SharedTypeView, |
| PromotableElementImpl2 |
| >; |
| |
| typedef SharedPatternField = |
| shared.RecordPatternField<PatternFieldImpl, DartPatternImpl>; |
| |
| /// A function which returns [NonPromotionReason]s that various types are not |
| /// promoted. |
| typedef WhyNotPromotedGetter = |
| Map<SharedTypeView, NonPromotionReason> Function(); |
| |
| /// The context shared between different units of the same library. |
| final class LibraryResolutionContext { |
| /// The declarations for [VariableFragment]s. |
| final Map<VariableFragment, VariableDeclaration> _variableNodes = |
| Map.identity(); |
| } |
| |
| /// Instances of the class `ResolverVisitor` are used to resolve the nodes |
| /// within a single compilation unit. |
| class ResolverVisitor extends ThrowingAstVisitor<void> |
| with |
| ErrorDetectionHelpers, |
| TypeAnalyzer< |
| AstNodeImpl, |
| StatementImpl, |
| ExpressionImpl, |
| PromotableElementImpl2, |
| DartPatternImpl, |
| void, |
| InterfaceTypeImpl, |
| InterfaceElementImpl2 |
| >, |
| // TODO(paulberry): not yet used. |
| NullShortingMixin< |
| Null, |
| ExpressionImpl, |
| PromotableElementImpl2, |
| SharedTypeView |
| > { |
| /// Debug-only: if `true`, manipulations of [_rewriteStack] performed by |
| /// [popRewrite], [pushRewrite], and [replaceExpression] will be printed. |
| static const bool _debugRewriteStack = false; |
| |
| /// The element for the library containing the compilation unit being visited. |
| final LibraryElementImpl definingLibrary; |
| |
| /// The library fragment being visited. |
| final LibraryFragmentImpl libraryFragment; |
| |
| /// The context shared between different units of the same library. |
| final LibraryResolutionContext libraryResolutionContext; |
| |
| /// If the resolver visitor is visiting a switch statement and patterns |
| /// support is disabled, the tracker that determines whether the switch is |
| /// exhaustive. |
| SwitchExhaustiveness? legacySwitchExhaustiveness; |
| |
| @override |
| final TypeAnalyzerOptions typeAnalyzerOptions; |
| |
| @override |
| late final SharedTypeAnalyzerErrors errors = SharedTypeAnalyzerErrors( |
| errorReporter, |
| ); |
| |
| /// The source representing the compilation unit being visited. |
| final Source source; |
| |
| /// The object used to access the types from the core library. |
| final TypeProviderImpl typeProvider; |
| |
| @override |
| final ErrorReporter errorReporter; |
| |
| /// The analysis options used by this resolver. |
| final AnalysisOptions analysisOptions; |
| |
| /// The class containing the AST nodes being visited, |
| /// or `null` if we are not in the scope of a class. |
| InterfaceElementImpl2? enclosingClass; |
| |
| /// The element representing the extension containing the AST nodes being |
| /// visited, or `null` if we are not in the scope of an extension. |
| ExtensionElementImpl2? enclosingExtension; |
| |
| /// The element representing the function containing the current node, or |
| /// `null` if the current node is not contained in a function. |
| ExecutableElementImpl2? enclosingFunction; |
| |
| /// The manager for the inheritance mappings. |
| @override |
| final InheritanceManager3 inheritance; |
| |
| /// The feature set that is enabled for the current unit. |
| final FeatureSet _featureSet; |
| |
| /// Helper for checking that subtypes of a base or final type must be base, |
| /// final, or sealed. |
| late final BaseOrFinalTypeVerifier baseOrFinalTypeVerifier; |
| |
| /// Helper for checking expression that should have the `bool` type. |
| late final BoolExpressionVerifier boolExpressionVerifier = |
| BoolExpressionVerifier( |
| resolver: this, |
| errorReporter: errorReporter, |
| nullableDereferenceVerifier: nullableDereferenceVerifier, |
| ); |
| |
| /// Helper for checking potentially nullable dereferences. |
| late final NullableDereferenceVerifier nullableDereferenceVerifier = |
| NullableDereferenceVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| resolver: this, |
| ); |
| |
| /// Helper for extension method resolution. |
| late final ExtensionMemberResolver extensionResolver = |
| ExtensionMemberResolver(this); |
| |
| /// Helper for resolving properties on types. |
| late final TypePropertyResolver typePropertyResolver = TypePropertyResolver( |
| this, |
| ); |
| |
| /// Helper for resolving [ListLiteral] and [SetOrMapLiteral]. |
| late final TypedLiteralResolver _typedLiteralResolver = TypedLiteralResolver( |
| this, |
| typeSystem, |
| typeProvider, |
| analysisOptions, |
| ); |
| |
| late final AssignmentExpressionResolver _assignmentExpressionResolver = |
| AssignmentExpressionResolver(resolver: this); |
| late final BinaryExpressionResolver _binaryExpressionResolver; |
| late final ConstructorReferenceResolver _constructorReferenceResolver = |
| ConstructorReferenceResolver(this); |
| late final FunctionExpressionInvocationResolver |
| _functionExpressionInvocationResolver; |
| late final FunctionExpressionResolver _functionExpressionResolver; |
| late final ForResolver _forResolver; |
| late final PostfixExpressionResolver _postfixExpressionResolver; |
| late final PrefixedIdentifierResolver _prefixedIdentifierResolver; |
| late final PrefixExpressionResolver _prefixExpressionResolver; |
| late final VariableDeclarationResolver _variableDeclarationResolver; |
| late final YieldStatementResolver _yieldStatementResolver; |
| |
| late final NullSafetyDeadCodeVerifier nullSafetyDeadCodeVerifier; |
| |
| late final InvocationInferenceHelper inferenceHelper; |
| |
| /// The object used to resolve the element associated with the current node. |
| late final ElementResolver elementResolver; |
| |
| /// The object used to compute the type associated with the current node. |
| late final StaticTypeAnalyzer typeAnalyzer; |
| |
| /// The type system in use during resolution. |
| @override |
| final TypeSystemImpl typeSystem; |
| |
| /// Inference context information for the current function body, if the |
| /// current node is inside a function body. |
| BodyInferenceContext? _bodyContext; |
| |
| /// If a class, or mixin, is being resolved, the type of the class. |
| /// Otherwise `null`. |
| TypeImpl? _thisType; |
| |
| final FlowAnalysisHelper flowAnalysis; |
| |
| /// Stack of expressions which we have not yet finished visiting, that should |
| /// terminate a null-shorting expression. |
| /// |
| /// The stack contains a `null` sentinel as its first entry so that it is |
| /// always safe to use `.last` to examine the top of the stack. |
| final List<Expression?> _unfinishedNullShorts = [null]; |
| |
| late final FunctionReferenceResolver _functionReferenceResolver; |
| |
| late final InstanceCreationExpressionResolver |
| _instanceCreationExpressionResolver = InstanceCreationExpressionResolver( |
| this, |
| ); |
| |
| late final SimpleIdentifierResolver _simpleIdentifierResolver = |
| SimpleIdentifierResolver(this); |
| |
| late final PropertyElementResolver _propertyElementResolver = |
| PropertyElementResolver(this); |
| |
| late final RecordLiteralResolver _recordLiteralResolver = |
| RecordLiteralResolver(resolver: this); |
| |
| late final AnnotationResolver _annotationResolver = AnnotationResolver(this); |
| |
| late final ListPatternResolver listPatternResolver = ListPatternResolver( |
| this, |
| ); |
| |
| final bool genericMetadataIsEnabled; |
| |
| final bool inferenceUsingBoundsIsEnabled; |
| |
| /// Stack for obtaining rewritten expressions. Prior to visiting an |
| /// expression, a caller may push the expression on this stack; if |
| /// [replaceExpression] is later called, it will update the top of the stack |
| /// to point to the rewritten expression. |
| /// |
| /// The stack sometimes contains `null`s. These account for situations where |
| /// it's necessary to push a value onto the stack to balance a later pop, but |
| /// there is no suitable expression to push. |
| final List<ExpressionImpl?> _rewriteStack = []; |
| |
| /// Debug-only expando mapping AST nodes to the nodes they were replaced with |
| /// by [replaceExpression]. This is used by [dispatchExpression] as a sanity |
| /// check to make sure the expression it pops off the [_rewriteStack] is |
| /// actually correct. |
| late final Expando<AstNode> _replacements = Expando(); |
| |
| /// Initialize a newly created visitor to resolve the nodes in an AST node. |
| /// |
| /// The [definingLibrary] is the element for the library containing the node |
| /// being visited. The [source] is the source representing the compilation |
| /// unit containing the node being visited. The [typeProvider] is the object |
| /// used to access the types from the core library. The [errorListener] is the |
| /// error listener that will be informed of any errors that are found during |
| /// resolution. |
| /// |
| // TODO(paulberry): make [featureSet] a required parameter (this will be a |
| // breaking change). |
| ResolverVisitor( |
| InheritanceManager3 inheritanceManager, |
| LibraryElementImpl definingLibrary, |
| LibraryResolutionContext libraryResolutionContext, |
| Source source, |
| TypeProvider typeProvider, |
| AnalysisErrorListener errorListener, { |
| required LibraryFragmentImpl libraryFragment, |
| required FeatureSet featureSet, |
| required AnalysisOptions analysisOptions, |
| required FlowAnalysisHelper flowAnalysisHelper, |
| required TypeAnalyzerOptions typeAnalyzerOptions, |
| }) : this._( |
| inheritanceManager, |
| definingLibrary, |
| libraryResolutionContext, |
| source, |
| definingLibrary.typeSystem, |
| typeProvider as TypeProviderImpl, |
| ErrorReporter(errorListener, source), |
| featureSet, |
| analysisOptions, |
| flowAnalysisHelper, |
| libraryFragment: libraryFragment, |
| typeAnalyzerOptions: typeAnalyzerOptions, |
| ); |
| |
| ResolverVisitor._( |
| this.inheritance, |
| this.definingLibrary, |
| this.libraryResolutionContext, |
| this.source, |
| this.typeSystem, |
| this.typeProvider, |
| this.errorReporter, |
| FeatureSet featureSet, |
| this.analysisOptions, |
| this.flowAnalysis, { |
| required this.libraryFragment, |
| required this.typeAnalyzerOptions, |
| }) : _featureSet = featureSet, |
| genericMetadataIsEnabled = definingLibrary.featureSet.isEnabled( |
| Feature.generic_metadata, |
| ), |
| inferenceUsingBoundsIsEnabled = definingLibrary.featureSet.isEnabled( |
| Feature.inference_using_bounds, |
| ), |
| baseOrFinalTypeVerifier = BaseOrFinalTypeVerifier( |
| definingLibrary: definingLibrary, |
| errorReporter: errorReporter, |
| ) { |
| inferenceHelper = InvocationInferenceHelper( |
| resolver: this, |
| errorReporter: errorReporter, |
| typeSystem: typeSystem, |
| dataForTesting: |
| flowAnalysis.dataForTesting != null |
| ? TypeConstraintGenerationDataForTesting() |
| : null, |
| ); |
| _binaryExpressionResolver = BinaryExpressionResolver(resolver: this); |
| _functionExpressionInvocationResolver = |
| FunctionExpressionInvocationResolver(resolver: this); |
| _functionExpressionResolver = FunctionExpressionResolver(resolver: this); |
| _forResolver = ForResolver(resolver: this); |
| _postfixExpressionResolver = PostfixExpressionResolver(resolver: this); |
| _prefixedIdentifierResolver = PrefixedIdentifierResolver(this); |
| _prefixExpressionResolver = PrefixExpressionResolver(resolver: this); |
| _variableDeclarationResolver = VariableDeclarationResolver( |
| resolver: this, |
| strictInference: analysisOptions.strictInference, |
| ); |
| _yieldStatementResolver = YieldStatementResolver(resolver: this); |
| nullSafetyDeadCodeVerifier = NullSafetyDeadCodeVerifier( |
| typeSystem, |
| errorReporter, |
| flowAnalysis, |
| ); |
| elementResolver = ElementResolver(this); |
| typeAnalyzer = StaticTypeAnalyzer(this); |
| _functionReferenceResolver = FunctionReferenceResolver(this); |
| } |
| |
| /// Inference context information for the current function body, if the |
| /// current node is inside a function body. |
| BodyInferenceContext? get bodyContext => _bodyContext; |
| |
| @override |
| FlowAnalysis< |
| AstNodeImpl, |
| StatementImpl, |
| ExpressionImpl, |
| PromotableElementImpl2, |
| SharedTypeView |
| > |
| get flow => flowAnalysis.flow!; |
| |
| bool get isConstructorTearoffsEnabled => |
| _featureSet.isEnabled(Feature.constructor_tearoffs); |
| |
| bool get isInferenceUpdate1Enabled => |
| _featureSet.isEnabled(Feature.inference_update_1); |
| |
| /// Return the object providing promoted or declared types of variables. |
| LocalVariableTypeProvider get localVariableTypeProvider { |
| return flowAnalysis.localVariableTypeProvider; |
| } |
| |
| @override |
| shared.TypeAnalyzerOperations< |
| PromotableElementImpl2, |
| InterfaceTypeImpl, |
| InterfaceElementImpl2 |
| > |
| get operations => flowAnalysis.typeOperations; |
| |
| /// Gets the current depth of the [_rewriteStack]. This may be used in |
| /// assertions to verify that pushes and pops are properly balanced. |
| int get rewriteStackDepth => _rewriteStack.length; |
| |
| @override |
| bool get strictCasts => analysisOptions.strictCasts; |
| |
| /// If a class, or mixin, is being resolved, the type of the class. |
| /// |
| /// If an extension is being resolved, the type of `this`, the declared |
| /// extended type, or promoted. |
| /// |
| /// Otherwise `null`. |
| TypeImpl? get thisType { |
| return _thisType; |
| } |
| |
| @override |
| SharedTypeView analyzeExpression( |
| ExpressionImpl node, |
| SharedTypeSchemaView schema, { |
| bool continueNullShorting = false, |
| }) { |
| inferenceLogWriter?.setExpressionVisitCodePath( |
| node, |
| ExpressionVisitCodePath.analyzeExpression, |
| ); |
| return super.analyzeExpression( |
| node, |
| schema, |
| continueNullShorting: continueNullShorting, |
| ); |
| } |
| |
| List<SharedPatternField> buildSharedPatternFields( |
| List<PatternFieldImpl> fields, { |
| required bool mustBeNamed, |
| }) { |
| return fields.map((field) { |
| Token? nameToken; |
| var fieldName = field.name; |
| if (fieldName != null) { |
| nameToken = fieldName.name; |
| if (nameToken == null) { |
| var variablePattern = field.pattern.variablePattern; |
| if (variablePattern != null) { |
| variablePattern.fieldNameWithImplicitName = fieldName; |
| nameToken = variablePattern.name; |
| } else { |
| errorReporter.atNode( |
| field, |
| CompileTimeErrorCode.MISSING_NAMED_PATTERN_FIELD_NAME, |
| ); |
| } |
| } |
| } else if (mustBeNamed) { |
| errorReporter.atNode( |
| field, |
| CompileTimeErrorCode.POSITIONAL_FIELD_IN_OBJECT_PATTERN, |
| ); |
| } |
| return shared.RecordPatternField( |
| node: field, |
| name: nameToken?.lexeme, |
| pattern: field.pattern, |
| ); |
| }).toList(); |
| } |
| |
| /// Verify that the arguments in the given [argumentList] can be assigned to |
| /// their corresponding parameters. |
| /// |
| /// See [CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| void checkForArgumentTypesNotAssignableInList( |
| ArgumentListImpl argumentList, |
| List<WhyNotPromotedGetter> whyNotPromotedArguments, |
| ) { |
| var arguments = argumentList.arguments; |
| for (int i = 0; i < arguments.length; i++) { |
| checkForArgumentTypeNotAssignableForArgument( |
| arguments[i], |
| whyNotPromoted: |
| flowAnalysis.flow == null ? null : whyNotPromotedArguments[i], |
| ); |
| } |
| } |
| |
| void checkForBodyMayCompleteNormally({ |
| required FunctionBodyImpl body, |
| required SyntacticEntity errorNode, |
| }) { |
| var bodyContext = body.bodyContext; |
| if (bodyContext == null) { |
| return; |
| } |
| |
| if (!flowAnalysis.flow!.isReachable) { |
| bodyContext.mayCompleteNormally = false; |
| return; |
| } |
| |
| var returnType = bodyContext.contextType; |
| if (returnType == null) { |
| if (errorNode is BlockFunctionBody) { |
| _checkForFutureCatchErrorOnError(errorNode); |
| } |
| return; |
| } |
| |
| if (body is BlockFunctionBody) { |
| if (body.isGenerator) { |
| return; |
| } |
| |
| if (body.isAsynchronous) { |
| // Check whether the return type is legal. If not, return rather than |
| // reporting a second error. |
| |
| // This is the same check as [ReturnTypeVerifier._isLegalReturnType]. |
| // TODO(srawlins): When this check is moved into the resolution stage, |
| // use the result of that check to determine whether this check should |
| // be done. |
| var lowerBound = typeProvider.futureElement2.instantiateImpl( |
| typeArguments: fixedTypeList(NeverTypeImpl.instance), |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| var imposedType = bodyContext.imposedType; |
| if (imposedType != null && |
| !typeSystem.isSubtypeOf(lowerBound, imposedType)) { |
| // [imposedType] is an illegal return type for an asynchronous |
| // non-generator function; do not report an additional error here. |
| return; |
| } |
| } |
| |
| DiagnosticCode diagnosticCode; |
| if (typeSystem.isPotentiallyNonNullable(returnType)) { |
| diagnosticCode = CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY; |
| } else { |
| var returnTypeBase = typeSystem.futureOrBase(returnType); |
| if (returnTypeBase is DynamicType || |
| returnTypeBase is InvalidType || |
| returnTypeBase is UnknownInferredType || |
| returnTypeBase is VoidType || |
| returnTypeBase.isDartCoreNull) { |
| return; |
| } else { |
| diagnosticCode = WarningCode.BODY_MIGHT_COMPLETE_NORMALLY_NULLABLE; |
| } |
| } |
| if (errorNode is ConstructorDeclaration) { |
| errorReporter.atConstructorDeclaration( |
| errorNode, |
| diagnosticCode, |
| arguments: [returnType], |
| ); |
| } else if (errorNode is BlockFunctionBody) { |
| errorReporter.atToken( |
| errorNode.block.leftBracket, |
| diagnosticCode, |
| arguments: [returnType], |
| ); |
| } else if (errorNode is Token) { |
| errorReporter.atToken( |
| errorNode, |
| diagnosticCode, |
| arguments: [returnType], |
| ); |
| } |
| } |
| } |
| |
| /// The client of the resolver should call this method after asking the |
| /// resolver to visit an AST node. This performs assertions to make sure that |
| /// temporary resolver state has been properly cleaned up. |
| void checkIdle() { |
| assert(_rewriteStack.isEmpty); |
| inferenceLogWriter?.assertIdle(); |
| } |
| |
| /// Reports an error if the [pattern] with the [requiredType] cannot |
| /// match the [DartPatternImpl.matchedValueType]. |
| void checkPatternNeverMatchesValueType({ |
| required SharedMatchContext context, |
| required DartPatternImpl pattern, |
| required TypeImpl requiredType, |
| required TypeImpl matchedValueType, |
| }) { |
| if (context.irrefutableContext == null) { |
| if (!typeSystem.canBeSubtypeOf(matchedValueType, requiredType)) { |
| AstNodeImpl? errorNode; |
| if (pattern is CastPatternImpl) { |
| errorNode = pattern.type; |
| } else if (pattern is DeclaredVariablePatternImpl) { |
| errorNode = pattern.type; |
| } else if (pattern is ObjectPatternImpl) { |
| errorNode = pattern.type; |
| } else if (pattern is WildcardPatternImpl) { |
| errorNode = pattern.type; |
| } |
| errorNode ??= pattern; |
| errorReporter.atNode( |
| errorNode, |
| WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, |
| arguments: [matchedValueType, requiredType], |
| ); |
| } |
| } |
| } |
| |
| void checkReadOfNotAssignedLocalVariable( |
| SimpleIdentifier node, |
| Element? element, |
| ) { |
| if (flowAnalysis.flow == null) { |
| return; |
| } |
| |
| if (!node.inGetterContext()) { |
| return; |
| } |
| |
| if (element is PromotableElementImpl2) { |
| var assigned = flowAnalysis.isDefinitelyAssigned(node, element); |
| var unassigned = flowAnalysis.isDefinitelyUnassigned(node, element); |
| |
| if (element.isLate) { |
| if (unassigned) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.DEFINITELY_UNASSIGNED_LATE_LOCAL_VARIABLE, |
| arguments: [node.name], |
| ); |
| } |
| return; |
| } |
| |
| if (!assigned) { |
| if (element.isFinal) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.READ_POTENTIALLY_UNASSIGNED_FINAL, |
| arguments: [node.name], |
| ); |
| return; |
| } |
| |
| if (typeSystem.isPotentiallyNonNullable(element.type)) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode |
| .NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE, |
| arguments: [node.name], |
| ); |
| return; |
| } |
| } |
| } |
| } |
| |
| void checkUnreachableNode(AstNode node) { |
| nullSafetyDeadCodeVerifier.visitNode(node); |
| } |
| |
| @override |
| List<DiagnosticMessage> computeWhyNotPromotedMessages( |
| SyntacticEntity errorEntity, |
| Map<SharedTypeView, NonPromotionReason>? whyNotPromoted, |
| ) { |
| List<DiagnosticMessage> messages = []; |
| if (whyNotPromoted != null) { |
| for (var entry in whyNotPromoted.entries) { |
| var whyNotPromotedVisitor = _WhyNotPromotedVisitor( |
| source, |
| errorEntity, |
| flowAnalysis.dataForTesting, |
| ); |
| if (typeSystem.isPotentiallyNullable( |
| // TODO(paulberry): make this type argument unnecessary by changing |
| // the parameter of `TypeSystemImpl.isPotentiallyNullable` to |
| // (covariant) `TypeImpl`. |
| entry.key.unwrapTypeView<TypeImpl>(), |
| )) { |
| continue; |
| } |
| messages = entry.value.accept(whyNotPromotedVisitor); |
| // `messages` will be passed to the ErrorReporter, which might add |
| // additional entries. So make sure that it's not a `const []`. |
| assert(_isModifiableList(messages)); |
| if (messages.isNotEmpty) { |
| if (flowAnalysis.dataForTesting != null) { |
| var nonPromotionReasonText = entry.value.shortName; |
| var args = <String>[]; |
| var propertyReference = whyNotPromotedVisitor.propertyReference; |
| if (propertyReference != null) { |
| var id = computeMemberId(propertyReference); |
| args.add('target: $id'); |
| } |
| if (args.isNotEmpty) { |
| nonPromotionReasonText += '(${args.join(', ')})'; |
| } |
| flowAnalysis.dataForTesting!.nonPromotionReasons[errorEntity] = |
| nonPromotionReasonText; |
| } |
| } |
| break; |
| } |
| } |
| return messages; |
| } |
| |
| @override |
| void dispatchCollectionElement( |
| covariant CollectionElementImpl element, |
| covariant CollectionLiteralContext? context, |
| ) { |
| element.resolveElement(this, context); |
| popRewrite(); |
| } |
| |
| @override |
| ExpressionTypeAnalysisResult dispatchExpression( |
| covariant ExpressionImpl expression, |
| SharedTypeSchemaView context, |
| ) { |
| int? stackDepth; |
| assert(() { |
| stackDepth = rewriteStackDepth; |
| return true; |
| }()); |
| // TODO(paulberry): implement null shorting |
| // Stack: () |
| pushRewrite(expression); |
| // Stack: (Expression) |
| expression.resolveExpression(this, context.unwrapTypeSchemaView()); |
| inferenceLogWriter?.assertExpressionWasRecorded(expression); |
| assert(rewriteStackDepth == stackDepth! + 1); |
| var replacementExpression = peekRewrite()!; |
| assert( |
| identical(_replacements[expression] ?? expression, replacementExpression), |
| ); |
| var staticType = replacementExpression.staticType; |
| if (staticType == null) { |
| var shouldHaveType = true; |
| if (replacementExpression is ExtensionOverride) { |
| shouldHaveType = false; |
| } else if (replacementExpression is IdentifierImpl) { |
| var element = replacementExpression.element; |
| if (element is ExtensionElement || |
| element is InterfaceElement || |
| element is PrefixElement || |
| element is TypeAliasElement) { |
| shouldHaveType = false; |
| } |
| } |
| if (shouldHaveType) { |
| assert( |
| false, |
| 'No static type for: ' |
| '(${replacementExpression.runtimeType}) $replacementExpression', |
| ); |
| } |
| staticType = operations.unknownType.unwrapTypeSchemaView(); |
| } |
| return ExpressionTypeAnalysisResult(type: SharedTypeView(staticType)); |
| } |
| |
| @override |
| PatternResult dispatchPattern(SharedMatchContext context, AstNodeImpl node) { |
| shared.PatternResult analysisResult; |
| if (node is DartPatternImpl) { |
| analysisResult = node.resolvePattern(this, context); |
| node.matchedValueType = analysisResult.matchedValueType.unwrapTypeView(); |
| } else { |
| // This can occur inside conventional switch statements, since |
| // [SwitchCase] points directly to an [Expression] rather than to a |
| // [ConstantPattern]. So we mimic what |
| // [ConstantPatternImpl.resolvePattern] would do. |
| analysisResult = analyzeConstantPattern( |
| context, |
| node, |
| node as ExpressionImpl, |
| ); |
| // Stack: (Expression) |
| popRewrite(); |
| // Stack: () |
| } |
| return analysisResult; |
| } |
| |
| @override |
| SharedTypeSchemaView dispatchPatternSchema(covariant DartPatternImpl node) { |
| return SharedTypeSchemaView(node.computePatternSchema(this)); |
| } |
| |
| @override |
| void dispatchStatement(Statement statement) { |
| statement.accept(this); |
| } |
| |
| @override |
| SharedTypeView downwardInferObjectPatternRequiredType({ |
| required SharedTypeView matchedType, |
| required covariant ObjectPatternImpl pattern, |
| }) { |
| var typeNode = pattern.type; |
| if (typeNode.typeArguments == null) { |
| var typeNameElement = typeNode.element2; |
| if (typeNameElement is InterfaceElementImpl2) { |
| var typeParameters = typeNameElement.typeParameters2; |
| if (typeParameters.isNotEmpty) { |
| var typeArguments = _inferTypeArguments( |
| typeParameters: typeParameters, |
| errorNode: typeNode, |
| declaredType: typeNameElement.thisType, |
| contextType: matchedType.unwrapTypeView(), |
| nodeForTesting: pattern, |
| ); |
| return SharedTypeView( |
| typeNode.type = typeNameElement.instantiateImpl( |
| typeArguments: typeArguments, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ), |
| ); |
| } |
| } else if (typeNameElement is TypeAliasElementImpl2) { |
| var typeParameters = typeNameElement.typeParameters2; |
| if (typeParameters.isNotEmpty) { |
| var typeArguments = _inferTypeArguments( |
| typeParameters: typeParameters, |
| errorNode: typeNode, |
| declaredType: typeNameElement.aliasedType, |
| contextType: matchedType.unwrapTypeView(), |
| nodeForTesting: pattern, |
| ); |
| return SharedTypeView( |
| typeNode.type = typeNameElement.instantiateImpl( |
| typeArguments: typeArguments, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ), |
| ); |
| } |
| } |
| } |
| return SharedTypeView(typeNode.typeOrThrow); |
| } |
| |
| @override |
| void finishExpressionCase( |
| covariant SwitchExpressionImpl node, |
| int caseIndex, |
| ) { |
| var case_ = node.cases[caseIndex]; |
| case_.expression = popRewrite()!; |
| nullSafetyDeadCodeVerifier.flowEnd(case_); |
| } |
| |
| @override |
| void finishJoinedPatternVariable( |
| covariant JoinPatternVariableElementImpl2 variable, { |
| required JoinedPatternVariableLocation location, |
| required shared.JoinedPatternVariableInconsistency inconsistency, |
| required bool isFinal, |
| required SharedTypeView type, |
| }) { |
| variable.inconsistency = variable.inconsistency.maxWith(inconsistency); |
| variable.isFinal = isFinal; |
| variable.type = type.unwrapTypeView(); |
| |
| if (location == JoinedPatternVariableLocation.sharedCaseScope) { |
| for (var reference in variable.references) { |
| if (variable.inconsistency == |
| shared.JoinedPatternVariableInconsistency.sharedCaseAbsent) { |
| errorReporter.atNode( |
| reference, |
| CompileTimeErrorCode |
| .PATTERN_VARIABLE_SHARED_CASE_SCOPE_NOT_ALL_CASES, |
| arguments: [variable.name3!], |
| ); |
| } else if (variable.inconsistency == |
| shared.JoinedPatternVariableInconsistency.sharedCaseHasLabel) { |
| errorReporter.atNode( |
| reference, |
| CompileTimeErrorCode.PATTERN_VARIABLE_SHARED_CASE_SCOPE_HAS_LABEL, |
| arguments: [variable.name3!], |
| ); |
| } else if (variable.inconsistency == |
| shared.JoinedPatternVariableInconsistency.differentFinalityOrType) { |
| errorReporter.atNode( |
| reference, |
| CompileTimeErrorCode |
| .PATTERN_VARIABLE_SHARED_CASE_SCOPE_DIFFERENT_FINALITY_OR_TYPE, |
| arguments: [variable.name3!], |
| ); |
| } |
| } |
| } |
| } |
| |
| @override |
| shared.MapPatternEntry<ExpressionImpl, DartPatternImpl>? getMapPatternEntry( |
| covariant MapPatternElementImpl element, |
| ) { |
| if (element is MapPatternEntryImpl) { |
| return shared.MapPatternEntry(key: element.key, value: element.value); |
| } |
| return null; |
| } |
| |
| @override |
| DartPatternImpl? getRestPatternElementPattern( |
| covariant RestPatternElementImpl element, |
| ) { |
| return element.pattern; |
| } |
| |
| @override |
| SwitchExpressionMemberInfo< |
| AstNodeImpl, |
| ExpressionImpl, |
| PromotableElementImpl2 |
| > |
| getSwitchExpressionMemberInfo( |
| covariant SwitchExpressionImpl node, |
| int index, |
| ) { |
| var case_ = node.cases[index]; |
| var guardedPattern = case_.guardedPattern; |
| return SwitchExpressionMemberInfo( |
| head: CaseHeadOrDefaultInfo( |
| pattern: guardedPattern.pattern, |
| guard: guardedPattern.whenClause?.expression, |
| variables: guardedPattern.variables, |
| ), |
| expression: case_.expression, |
| ); |
| } |
| |
| @override |
| SwitchStatementMemberInfo< |
| AstNodeImpl, |
| StatementImpl, |
| ExpressionImpl, |
| PromotableElementImpl2 |
| > |
| getSwitchStatementMemberInfo(covariant SwitchStatementImpl node, int index) { |
| CaseHeadOrDefaultInfo<AstNodeImpl, ExpressionImpl, PromotableElementImpl2> |
| ofMember(SwitchMemberImpl member) { |
| if (member is SwitchCaseImpl) { |
| return CaseHeadOrDefaultInfo(pattern: member.expression, variables: {}); |
| } else if (member is SwitchPatternCaseImpl) { |
| var guardedPattern = member.guardedPattern; |
| return CaseHeadOrDefaultInfo( |
| pattern: guardedPattern.pattern, |
| variables: guardedPattern.variables, |
| guard: guardedPattern.whenClause?.expression, |
| ); |
| } else { |
| return CaseHeadOrDefaultInfo(pattern: null, variables: {}); |
| } |
| } |
| |
| var group = node.memberGroups[index]; |
| return SwitchStatementMemberInfo( |
| heads: group.members.map(ofMember).toList(), |
| body: group.statements, |
| variables: group.variables, |
| hasLabels: group.hasLabels, |
| ); |
| } |
| |
| @override |
| void handle_ifElement_conditionEnd(covariant IfElementImpl node) { |
| // Stack: (Expression condition) |
| var condition = popRewrite()!; |
| |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition( |
| condition, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| } |
| |
| @override |
| void handle_ifElement_elseEnd( |
| covariant IfElementImpl node, |
| covariant CollectionElementImpl ifFalse, |
| ) { |
| nullSafetyDeadCodeVerifier.flowEnd(ifFalse); |
| } |
| |
| @override |
| void handle_ifElement_thenEnd( |
| covariant IfElementImpl node, |
| covariant CollectionElementImpl ifTrue, |
| ) { |
| nullSafetyDeadCodeVerifier.flowEnd(ifTrue); |
| } |
| |
| @override |
| void handle_ifStatement_conditionEnd(Statement node) { |
| // Stack: (Expression condition) |
| var condition = popRewrite()!; |
| |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition( |
| condition, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| } |
| |
| @override |
| void handle_ifStatement_elseEnd(Statement node, Statement ifFalse) { |
| nullSafetyDeadCodeVerifier.flowEnd(ifFalse); |
| } |
| |
| @override |
| void handle_ifStatement_thenEnd(Statement node, Statement ifTrue) { |
| nullSafetyDeadCodeVerifier.flowEnd(ifTrue); |
| } |
| |
| @override |
| void handle_logicalOrPattern_afterLhs(covariant LogicalOrPatternImpl node) { |
| checkUnreachableNode(node.rightOperand); |
| } |
| |
| @override |
| void handleCase_afterCaseHeads( |
| AstNode node, |
| int caseIndex, |
| Iterable<PromotableElement> variables, |
| ) {} |
| |
| @override |
| void handleCaseHead( |
| covariant AstNodeImpl node, { |
| required int caseIndex, |
| required int subIndex, |
| }) { |
| // Stack: (Expression) |
| popRewrite(); // "when" expression |
| // Stack: () |
| if (node is SwitchStatementImpl) { |
| var group = node.memberGroups[caseIndex]; |
| legacySwitchExhaustiveness?.visitSwitchMember(group); |
| nullSafetyDeadCodeVerifier.flowEnd(group.members[subIndex]); |
| } else if (node is SwitchExpressionImpl) { |
| legacySwitchExhaustiveness?.visitSwitchExpressionCase( |
| node.cases[caseIndex], |
| ); |
| } |
| } |
| |
| @override |
| void handleDefault( |
| covariant SwitchStatementImpl node, { |
| required int caseIndex, |
| required int subIndex, |
| }) { |
| var group = node.memberGroups[caseIndex]; |
| legacySwitchExhaustiveness?.visitSwitchMember(group); |
| nullSafetyDeadCodeVerifier.flowEnd(group.members[subIndex]); |
| } |
| |
| @override |
| void handleListPatternRestElement( |
| DartPattern container, |
| covariant RestPatternElementImpl restElement, |
| ) {} |
| |
| @override |
| void handleMapPatternEntry( |
| DartPattern container, |
| covariant MapPatternEntryImpl entry, |
| SharedTypeView keyType, |
| ) { |
| entry.key = popRewrite()!; |
| } |
| |
| @override |
| void handleMapPatternRestElement( |
| DartPattern container, |
| covariant RestPatternElementImpl restElement, |
| ) {} |
| |
| @override |
| void handleMergedStatementCase( |
| covariant SwitchStatementImpl node, { |
| required int caseIndex, |
| required bool isTerminating, |
| }) { |
| nullSafetyDeadCodeVerifier.flowEnd( |
| node.memberGroups[caseIndex].members.last, |
| ); |
| } |
| |
| @override |
| void handleNoCollectionElement(AstNode node) {} |
| |
| @override |
| void handleNoGuard(AstNode node, int caseIndex) { |
| // Stack: () |
| // We can push `null` here because there is no actual expression associated |
| // with the lack of a guard, so there's nothing that will need rewriting. |
| pushRewrite(null); |
| // Stack: (Expression) |
| } |
| |
| @override |
| void handleNoStatement(Statement node) {} |
| |
| @override |
| void handleSwitchBeforeAlternative( |
| covariant AstNodeImpl node, { |
| required int caseIndex, |
| required int subIndex, |
| }) { |
| if (node is SwitchExpressionImpl) { |
| var case_ = node.cases[caseIndex]; |
| checkUnreachableNode(case_); |
| } else if (node is SwitchStatementImpl) { |
| var member = node.memberGroups[caseIndex].members[subIndex]; |
| checkUnreachableNode(member); |
| } |
| } |
| |
| @override |
| void handleSwitchScrutinee(SharedTypeView type) { |
| if (!typeAnalyzerOptions.patternsEnabled) { |
| legacySwitchExhaustiveness = SwitchExhaustiveness(type.unwrapTypeView()); |
| } |
| } |
| |
| /// If generic function instantiation should be performed on `expression`, |
| /// inserts a [FunctionReference] node which wraps [expression]. |
| /// |
| /// If an [FunctionReference] is inserted, returns it; otherwise, returns |
| /// [expression]. |
| ExpressionImpl insertGenericFunctionInstantiation( |
| Expression expression, { |
| required TypeImpl contextType, |
| }) { |
| expression as ExpressionImpl; |
| if (!isConstructorTearoffsEnabled) { |
| // Temporarily, only create [ImplicitCallReference] nodes under the |
| // 'constructor-tearoffs' feature. |
| // TODO(srawlins): When we are ready to make a breaking change release to |
| // the analyzer package, remove this exception. |
| return expression; |
| } |
| |
| // Don't rewrite function declarations. |
| if (expression.parent is FunctionDeclaration) { |
| return expression; |
| } |
| |
| var staticType = expression.staticType; |
| if (staticType is! FunctionTypeImpl || staticType.typeFormals.isEmpty) { |
| return expression; |
| } |
| |
| var context = typeSystem.flatten(contextType); |
| if (context is! FunctionTypeImpl || context.typeFormals.isNotEmpty) { |
| return expression; |
| } |
| |
| var typeArgumentTypes = typeSystem.inferFunctionTypeInstantiation( |
| context, |
| staticType, |
| errorReporter: errorReporter, |
| errorNode: expression, |
| // If the constructor-tearoffs feature is enabled, then so is |
| // generic-metadata. |
| genericMetadataIsEnabled: true, |
| inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled, |
| strictInference: analysisOptions.strictInference, |
| strictCasts: analysisOptions.strictCasts, |
| typeSystemOperations: flowAnalysis.typeOperations, |
| dataForTesting: inferenceHelper.dataForTesting, |
| nodeForTesting: expression, |
| ); |
| if (typeArgumentTypes.isNotEmpty) { |
| staticType = staticType.instantiate(typeArgumentTypes); |
| } |
| |
| var parent = expression.parent; |
| var genericFunctionInstantiation = FunctionReferenceImpl( |
| function: expression, |
| typeArguments: null, |
| ); |
| replaceExpression(expression, genericFunctionInstantiation, parent: parent); |
| |
| genericFunctionInstantiation.typeArgumentTypes = typeArgumentTypes; |
| genericFunctionInstantiation.setPseudoExpressionStaticType(staticType); |
| |
| return genericFunctionInstantiation; |
| } |
| |
| @override |
| bool isDotShorthand(ExpressionImpl node) { |
| if (node is DotShorthandMixin) { |
| return node.isDotShorthand; |
| } |
| return false; |
| } |
| |
| @override |
| bool isLegacySwitchExhaustive(AstNode node, SharedTypeView expressionType) => |
| legacySwitchExhaustiveness!.isExhaustive; |
| |
| @override |
| bool isRestPatternElement(AstNode node) { |
| return node is RestPatternElementImpl; |
| } |
| |
| @override |
| bool isVariablePattern(AstNode pattern) => pattern is DeclaredVariablePattern; |
| |
| /// If we reached a null-shorting termination, and the [node] has null |
| /// shorting, make the type of the [node] nullable. |
| /// |
| /// [node] should be the original expression node (before resolution). If the |
| /// resolution process rewrote [node] to some other expression, that |
| /// expression should be passed in as [rewrittenExpression]. |
| void nullShortingTermination( |
| ExpressionImpl node, { |
| ExpressionImpl? rewrittenExpression, |
| }) { |
| // Verify that `rewrittenExpression` properly reflects any rewrites that |
| // were performed on `node`. |
| assert(identical(rewrittenExpression ?? node, _replacements[node] ?? node)); |
| |
| if (identical(_unfinishedNullShorts.last, node)) { |
| do { |
| _unfinishedNullShorts.removeLast(); |
| flowAnalysis.flow!.nullAwareAccess_end(); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } while (identical(_unfinishedNullShorts.last, node)); |
| if (node is! CascadeExpression) { |
| // Make the static type of `node` (or whatever it was rewritten to) |
| // nullable. |
| rewrittenExpression ??= node; |
| rewrittenExpression.setPseudoExpressionStaticType( |
| typeSystem.makeNullable(rewrittenExpression.typeOrThrow), |
| ); |
| } |
| } |
| } |
| |
| /// If it is appropriate to do so, override the current type of the static |
| /// element associated with the given expression with the given type. |
| /// Generally speaking, it is appropriate if the given type is more specific |
| /// than the current type. |
| /// |
| /// @param expression the expression used to access the static element whose |
| /// types might be overridden |
| /// @param potentialType the potential type of the elements |
| /// @param allowPrecisionLoss see @{code overrideVariable} docs |
| void overrideExpression( |
| Expression expression, |
| DartType potentialType, |
| bool allowPrecisionLoss, |
| bool setExpressionType, |
| ) { |
| // TODO(brianwilkerson): Remove this method. |
| } |
| |
| /// Examines the top entry of [_rewriteStack] but does not pop it. |
| ExpressionImpl? peekRewrite() => _rewriteStack.last; |
| |
| /// Pops the top entry off of [_rewriteStack]. |
| ExpressionImpl? popRewrite() { |
| var expression = _rewriteStack.removeLast(); |
| if (_debugRewriteStack) { |
| assert(_debugPrint('POP ${expression.runtimeType} $expression')); |
| } |
| return expression; |
| } |
| |
| /// Set information about enclosing declarations. |
| void prepareEnclosingDeclarations({ |
| InterfaceElementImpl2? enclosingClassElement, |
| ExecutableElementImpl2? enclosingExecutableElement, |
| }) { |
| enclosingClass = enclosingClassElement; |
| _setupThisType(); |
| enclosingFunction = enclosingExecutableElement; |
| } |
| |
| /// We are going to resolve [node], without visiting its parent. |
| /// Do necessary preparations - set enclosing elements, scopes, etc. |
| /// This [ResolverVisitor] instance is fresh, just created. |
| /// |
| /// Return `true` if we were able to do this, or `false` if it is not |
| /// possible to resolve only [node]. |
| bool prepareForResolving(AstNode node) { |
| var parent = node.parent; |
| |
| if (parent is CompilationUnit) { |
| return node is ClassDeclaration || |
| node is Directive || |
| node is ExtensionDeclaration || |
| node is FunctionDeclaration || |
| node is TopLevelVariableDeclaration; |
| } |
| |
| if (parent is ClassDeclarationImpl) { |
| enclosingClass = parent.declaredFragment!.element; |
| return true; |
| } |
| |
| if (parent is ExtensionDeclarationImpl) { |
| enclosingExtension = parent.declaredFragment!.element; |
| return true; |
| } |
| |
| if (parent is MixinDeclarationImpl) { |
| enclosingClass = parent.declaredFragment!.element; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Pushes an entry onto [_rewriteStack]. |
| void pushRewrite(ExpressionImpl? expression) { |
| if (_debugRewriteStack) { |
| assert(_debugPrint('PUSH ${expression.runtimeType} $expression')); |
| } |
| _rewriteStack.add(expression); |
| } |
| |
| /// Replaces the expression [oldNode] with [newNode], updating the node's |
| /// parent as appropriate. |
| /// |
| /// If [newNode] is the parent of [oldNode] already (because [newNode] became |
| /// the parent of [oldNode] in its constructor), this action will loop |
| /// infinitely; pass [oldNode]'s previous parent as [parent] to avoid this. |
| void replaceExpression( |
| Expression oldNode, |
| ExpressionImpl newNode, { |
| AstNode? parent, |
| }) { |
| assert(() { |
| assert(_replacements[oldNode] == null); |
| _replacements[oldNode] = newNode; |
| return true; |
| }()); |
| if (_rewriteStack.isNotEmpty && identical(peekRewrite(), oldNode)) { |
| if (_debugRewriteStack) { |
| assert(_debugPrint('REPLACE ${newNode.runtimeType} $newNode')); |
| } |
| _rewriteStack.last = newNode; |
| } |
| inferenceLogWriter?.recordExpressionRewrite( |
| oldExpression: oldNode, |
| newExpression: newNode, |
| ); |
| NodeReplacer.replace(oldNode, newNode, parent: parent); |
| nullSafetyDeadCodeVerifier.maybeRewriteFirstDeadNode(oldNode, newNode); |
| } |
| |
| PatternResult resolveAssignedVariablePattern({ |
| required AssignedVariablePatternImpl node, |
| required SharedMatchContext context, |
| }) { |
| var element = node.element2; |
| if (element is! PromotableElementImpl2) { |
| return PatternResult( |
| matchedValueType: SharedTypeView(InvalidTypeImpl.instance), |
| ); |
| } |
| |
| if (element.isFinal) { |
| var flow = this.flow; |
| if (element.isLate) { |
| if (flow.isAssigned(element)) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED, |
| ); |
| } |
| } else { |
| if (!flow.isUnassigned(element)) { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.ASSIGNMENT_TO_FINAL_LOCAL, |
| arguments: [node.name.lexeme], |
| ); |
| } |
| } |
| } |
| |
| return analyzeAssignedVariablePattern(context, node, element); |
| } |
| |
| /// Resolve LHS [node] of an assignment, an explicit [AssignmentExpression], |
| /// or implicit [PrefixExpression] or [PostfixExpression]. |
| PropertyElementResolverResult resolveForWrite({ |
| required ExpressionImpl node, |
| required bool hasRead, |
| }) { |
| inferenceLogWriter?.enterLValue(node); |
| if (node is IndexExpressionImpl) { |
| var target = node.target; |
| if (target != null) { |
| if (isDotShorthand(node)) { |
| // Recovery. |
| // It's a compile-time error to use postfix or prefix operators with |
| // dot shorthands. We provide an unknown type since this shouldn't be |
| // valid code, but we want to prevent any crashes. |
| pushDotShorthandContext(target, operations.unknownType); |
| } |
| analyzeExpression(target, operations.unknownType); |
| popRewrite(); |
| } |
| |
| if (node.isNullAware) { |
| _startNullAwareAccess(node, node.target); |
| nullSafetyDeadCodeVerifier.visitNode(node.index); |
| } |
| |
| var result = _propertyElementResolver.resolveIndexExpression( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| |
| analyzeExpression( |
| node.index, |
| SharedTypeSchemaView(result.indexContextType), |
| ); |
| popRewrite(); |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index); |
| checkIndexExpressionIndex( |
| node.index, |
| readElement: |
| hasRead ? result.readElement2 as ExecutableElement2OrMember? : null, |
| writeElement: result.writeElement2 as ExecutableElement2OrMember?, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| inferenceLogWriter?.exitLValue(node); |
| return result; |
| } else if (node is PrefixedIdentifierImpl) { |
| var prefix = node.prefix; |
| analyzeExpression(prefix, operations.unknownType); |
| popRewrite(); |
| |
| // TODO(scheglov): It would be nice to rewrite all such cases. |
| if (prefix.staticType is RecordType) { |
| var propertyAccess = PropertyAccessImpl( |
| target: prefix, |
| operator: node.period, |
| propertyName: node.identifier, |
| ); |
| NodeReplacer.replace(node, propertyAccess); |
| inferenceLogWriter?.exitLValue(node); |
| return _propertyElementResolver.resolvePropertyAccess( |
| node: propertyAccess, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| } |
| |
| inferenceLogWriter?.exitLValue(node); |
| return _propertyElementResolver.resolvePrefixedIdentifier( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| } else if (node is PropertyAccessImpl) { |
| if (node.target case var target?) { |
| analyzeExpression(target, operations.unknownType); |
| popRewrite(); |
| } |
| if (node.isNullAware) { |
| _startNullAwareAccess(node, node.target); |
| nullSafetyDeadCodeVerifier.visitNode(node.propertyName); |
| } |
| |
| inferenceLogWriter?.exitLValue(node); |
| return _propertyElementResolver.resolvePropertyAccess( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| } else if (node is SimpleIdentifierImpl) { |
| var result = _propertyElementResolver.resolveSimpleIdentifier( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| |
| if (hasRead && result.readElementRequested2 == null) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.UNDEFINED_IDENTIFIER, |
| arguments: [node.name], |
| ); |
| } |
| |
| inferenceLogWriter?.exitLValue(node); |
| return result; |
| } else { |
| inferenceLogWriter?.exitLValue(node, reanalyzeAsRValue: true); |
| analyzeExpression( |
| node, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| return PropertyElementResolverResult(); |
| } |
| } |
| |
| PatternResult resolveMapPattern({ |
| required MapPatternImpl node, |
| required SharedMatchContext context, |
| }) { |
| inferenceLogWriter?.enterPattern(node); |
| ({SharedTypeView keyType, SharedTypeView valueType})? typeArguments; |
| var typeArgumentsList = node.typeArguments; |
| if (typeArgumentsList != null) { |
| typeArgumentsList.accept(this); |
| // Check that we have exactly two type arguments. |
| var length = typeArgumentsList.arguments.length; |
| if (length == 2) { |
| typeArguments = ( |
| keyType: SharedTypeView(typeArgumentsList.arguments[0].typeOrThrow), |
| valueType: SharedTypeView(typeArgumentsList.arguments[1].typeOrThrow), |
| ); |
| } else { |
| errorReporter.atNode( |
| typeArgumentsList, |
| CompileTimeErrorCode.EXPECTED_TWO_MAP_PATTERN_TYPE_ARGUMENTS, |
| arguments: [length], |
| ); |
| } |
| } |
| |
| var result = analyzeMapPattern( |
| context, |
| node, |
| typeArguments: typeArguments, |
| elements: node.elements, |
| ); |
| node.requiredType = result.requiredType.unwrapTypeView(); |
| |
| checkPatternNeverMatchesValueType( |
| context: context, |
| pattern: node, |
| requiredType: result.requiredType.unwrapTypeView(), |
| matchedValueType: result.matchedValueType.unwrapTypeView(), |
| ); |
| inferenceLogWriter?.exitPattern(node); |
| |
| return result; |
| } |
| |
| @override |
| (ExecutableElement?, SharedTypeView) resolveObjectPatternPropertyGet({ |
| required covariant ObjectPatternImpl objectPattern, |
| required SharedTypeView receiverType, |
| required covariant SharedPatternField field, |
| }) { |
| var fieldNode = field.node; |
| var nameToken = fieldNode.name?.name; |
| nameToken ??= field.pattern.variablePattern?.name; |
| if (nameToken == null) { |
| return (null, SharedTypeView(typeProvider.dynamicType)); |
| } |
| |
| var result = typePropertyResolver.resolve( |
| receiver: null, |
| receiverType: receiverType.unwrapTypeView(), |
| name: nameToken.lexeme, |
| hasRead: true, |
| hasWrite: false, |
| propertyErrorEntity: objectPattern.type, |
| nameErrorEntity: nameToken, |
| ); |
| |
| if (result.needsGetterError) { |
| errorReporter.atToken( |
| nameToken, |
| CompileTimeErrorCode.UNDEFINED_GETTER, |
| arguments: [nameToken.lexeme, receiverType], |
| ); |
| } |
| |
| var getter = result.getter2; |
| if (getter != null) { |
| fieldNode.element2 = getter; |
| if (getter is PropertyAccessorElement2OrMember) { |
| return (getter, SharedTypeView(getter.returnType)); |
| } else { |
| return (getter, SharedTypeView(getter.type)); |
| } |
| } |
| |
| var recordField = result.recordField; |
| if (recordField != null) { |
| return (null, SharedTypeView(recordField.type)); |
| } |
| |
| return (null, SharedTypeView(typeProvider.dynamicType)); |
| } |
| |
| @override |
| RelationalOperatorResolution? resolveRelationalPatternOperator( |
| covariant RelationalPatternImpl node, |
| SharedTypeView matchedType, |
| ) { |
| var operatorLexeme = node.operator.lexeme; |
| RelationalOperatorKind kind; |
| String methodName; |
| if (operatorLexeme == '==') { |
| kind = RelationalOperatorKind.equals; |
| methodName = '=='; |
| } else if (operatorLexeme == '!=') { |
| kind = RelationalOperatorKind.notEquals; |
| methodName = '=='; |
| } else { |
| kind = RelationalOperatorKind.other; |
| methodName = operatorLexeme; |
| } |
| |
| var result = typePropertyResolver.resolve( |
| receiver: null, |
| receiverType: matchedType.unwrapTypeView(), |
| name: methodName, |
| hasRead: true, |
| hasWrite: false, |
| propertyErrorEntity: node.operator, |
| nameErrorEntity: node, |
| parentNode: node, |
| ); |
| |
| if (result.needsGetterError) { |
| errorReporter.atToken( |
| node.operator, |
| CompileTimeErrorCode.UNDEFINED_OPERATOR, |
| arguments: [methodName, matchedType], |
| ); |
| } |
| |
| var element = result.getter2 as MethodElement2OrMember?; |
| node.element2 = element; |
| if (element == null) { |
| return null; |
| } |
| |
| var parameterType = element.firstParameterType; |
| if (parameterType == null) { |
| return null; |
| } |
| |
| return RelationalOperatorResolution( |
| kind: kind, |
| parameterType: SharedTypeView(parameterType), |
| returnType: SharedTypeView(element.returnType), |
| ); |
| } |
| |
| void setReadElement( |
| Expression node, |
| Element? element, { |
| required bool atDynamicTarget, |
| }) { |
| var readType = |
| atDynamicTarget ? DynamicTypeImpl.instance : InvalidTypeImpl.instance; |
| if (node is IndexExpression) { |
| if (element is MethodElement2OrMember) { |
| readType = element.returnType; |
| } |
| } else if (node is PrefixedIdentifier || |
| node is PropertyAccess || |
| node is SimpleIdentifier) { |
| if (element is GetterElement2OrMember) { |
| readType = element.returnType; |
| } else if (element is VariableElement) { |
| readType = localVariableTypeProvider.getType( |
| node as SimpleIdentifierImpl, |
| isRead: true, |
| ); |
| } |
| } |
| |
| var parent = node.parent; |
| if (parent is AssignmentExpressionImpl && parent.leftHandSide == node) { |
| parent.readElement2 = element; |
| parent.readType = readType; |
| } else if (parent is PostfixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.readElement2 = element; |
| parent.readType = readType; |
| } else if (parent is PrefixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.readElement2 = element; |
| parent.readType = readType; |
| } |
| } |
| |
| @override |
| void setVariableType(PromotableElement variable, SharedTypeView type) { |
| if (variable is LocalVariableElementImpl2) { |
| variable.type = type.unwrapTypeView(); |
| } else { |
| throw UnimplementedError('TODO(paulberry)'); |
| } |
| } |
| |
| void setWriteElement( |
| Expression node, |
| Element? element, { |
| required bool atDynamicTarget, |
| }) { |
| var writeType = |
| atDynamicTarget ? DynamicTypeImpl.instance : InvalidTypeImpl.instance; |
| if (node is AugmentedExpression) { |
| if (element is SetterElement2OrMember) { |
| if (element.formalParameters case [var valueParameter]) { |
| writeType = valueParameter.type; |
| } |
| } |
| } else if (node is IndexExpression) { |
| if (element is MethodElement2OrMember) { |
| var parameters = element.formalParameters; |
| if (parameters.length == 2) { |
| writeType = parameters[1].type; |
| } |
| } |
| } else if (node is PrefixedIdentifier || |
| node is PropertyAccess || |
| node is SimpleIdentifier) { |
| if (element is SetterElement2OrMember) { |
| if (element.isSynthetic) { |
| var variable = element.variable3; |
| if (variable != null) { |
| writeType = variable.type; |
| } |
| } else { |
| var parameters = element.formalParameters; |
| if (parameters.length == 1) { |
| writeType = parameters[0].type; |
| } |
| } |
| } else if (element is VariableElement2OrMember) { |
| writeType = element.type; |
| } |
| } |
| |
| var parent = node.parent; |
| if (parent is AssignmentExpressionImpl && parent.leftHandSide == node) { |
| parent.writeElement2 = element; |
| parent.writeType = writeType; |
| } else if (parent is PostfixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.writeElement2 = element; |
| parent.writeType = writeType; |
| } else if (parent is PrefixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.writeElement2 = element; |
| parent.writeType = writeType; |
| } |
| } |
| |
| /// Returns the result of an implicit `this.` lookup for the identifier [node] |
| /// in a getter context, or `null` if no match was found. |
| LexicalLookupResult? thisLookupGetter(SimpleIdentifier node) { |
| return ThisLookup.lookupGetter(this, node); |
| } |
| |
| /// Returns the result of an implicit `this.` lookup for the identifier [node] |
| /// in a setter context, or `null` if no match was found. |
| LexicalLookupResult? thisLookupSetter(SimpleIdentifier node) { |
| return ThisLookup.lookupSetter(this, node); |
| } |
| |
| @override |
| SharedTypeView variableTypeFromInitializerType(SharedTypeView type) { |
| TypeImpl unwrapped = type.unwrapTypeView(); |
| if (unwrapped.isDartCoreNull) { |
| return SharedTypeView(DynamicTypeImpl.instance); |
| } |
| return SharedTypeView(typeSystem.demoteType(unwrapped)); |
| } |
| |
| @override |
| void visitAdjacentStrings( |
| covariant AdjacentStringsImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| for (var string in node.strings) { |
| analyzeExpression(string, operations.unknownType); |
| popRewrite(); |
| } |
| typeAnalyzer.visitAdjacentStrings(node); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitAnnotation(covariant AnnotationImpl node) { |
| inferenceLogWriter?.enterAnnotation(node); |
| // Annotations can contain expressions, so we need flow analysis to be |
| // available to process those expressions. |
| var isTopLevel = flowAnalysis.flow == null; |
| if (isTopLevel) { |
| flowAnalysis.bodyOrInitializer_enter(node, null); |
| } |
| assert(flowAnalysis.flow != null); |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| _annotationResolver.resolve(node, whyNotPromotedArguments); |
| var arguments = node.arguments; |
| if (arguments != null) { |
| checkForArgumentTypesNotAssignableInList( |
| arguments, |
| whyNotPromotedArguments, |
| ); |
| } |
| if (isTopLevel) { |
| flowAnalysis.bodyOrInitializer_exit(); |
| } |
| inferenceLogWriter?.exitAnnotation(node); |
| } |
| |
| @override |
| void visitAsExpression( |
| covariant AsExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| checkUnreachableNode(node.type); |
| node.type.accept(this); |
| |
| typeAnalyzer.visitAsExpression(node); |
| flowAnalysis.asExpression(node); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| |
| var expression = node.expression; |
| var staticType = node.staticType; |
| if (staticType != null && expression is SimpleIdentifier) { |
| var simpleIdentifier = expression as SimpleIdentifier; |
| var element = simpleIdentifier.element; |
| if (element is PromotableElementImpl2 && |
| !expression.typeOrThrow.isDartCoreNull && |
| typeSystem.isNullable(element.type) && |
| typeSystem.isNonNullable(staticType) && |
| flowAnalysis.isDefinitelyUnassigned(simpleIdentifier, element)) { |
| errorReporter.atNode( |
| simpleIdentifier, |
| WarningCode.CAST_FROM_NULLABLE_ALWAYS_FAILS, |
| arguments: [simpleIdentifier.name], |
| ); |
| } |
| } |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitAssertInitializer(covariant AssertInitializerImpl node) { |
| flowAnalysis.flow?.assert_begin(); |
| analyzeExpression( |
| node.condition, |
| SharedTypeSchemaView(typeProvider.boolType), |
| ); |
| popRewrite(); |
| boolExpressionVerifier.checkForNonBoolExpression( |
| node.condition, |
| diagnosticCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION, |
| whyNotPromoted: flowAnalysis.flow?.whyNotPromoted(node.condition), |
| ); |
| flowAnalysis.flow?.assert_afterCondition(node.condition); |
| if (node.message case var message?) { |
| analyzeExpression(message, operations.unknownType); |
| popRewrite(); |
| } |
| flowAnalysis.flow?.assert_end(); |
| } |
| |
| @override |
| void visitAssertStatement(covariant AssertStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| flowAnalysis.flow?.assert_begin(); |
| analyzeExpression( |
| node.condition, |
| SharedTypeSchemaView(typeProvider.boolType), |
| ); |
| popRewrite(); |
| boolExpressionVerifier.checkForNonBoolExpression( |
| node.condition, |
| diagnosticCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION, |
| whyNotPromoted: flowAnalysis.flow?.whyNotPromoted(node.condition), |
| ); |
| flowAnalysis.flow?.assert_afterCondition(node.condition); |
| if (node.message case var message?) { |
| analyzeExpression(message, operations.unknownType); |
| popRewrite(); |
| } |
| flowAnalysis.flow?.assert_end(); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitAssignmentExpression( |
| AssignmentExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _assignmentExpressionResolver.resolve( |
| node as AssignmentExpressionImpl, |
| contextType: contextType, |
| ); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitAugmentedExpression( |
| covariant AugmentedExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| super.visitAugmentedExpression(node); |
| } |
| |
| @override |
| void visitAugmentedInvocation( |
| covariant AugmentedInvocationImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| super.visitAugmentedInvocation(node); |
| } |
| |
| @override |
| void visitAwaitExpression( |
| covariant AwaitExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(_createFutureOr(contextType)), |
| ); |
| popRewrite(); |
| typeAnalyzer.visitAwaitExpression(node); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitBinaryExpression( |
| BinaryExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _binaryExpressionResolver.resolve( |
| node as BinaryExpressionImpl, |
| contextType: contextType, |
| ); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitBlock(Block node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| TypeImpl visitBlockFunctionBody( |
| covariant BlockFunctionBodyImpl node, { |
| TypeImpl? imposedType, |
| }) { |
| var oldBodyContext = _bodyContext; |
| try { |
| _bodyContext = BodyInferenceContext( |
| typeSystem: typeSystem, |
| node: node, |
| imposedType: imposedType, |
| ); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| return _finishFunctionBodyInference(); |
| } finally { |
| _bodyContext = oldBodyContext; |
| } |
| } |
| |
| @override |
| void visitBooleanLiteral( |
| covariant BooleanLiteralImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| flowAnalysis.flow?.booleanLiteral(node, node.value); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitBooleanLiteral(node); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitBreakStatement(BreakStatement node) { |
| // |
| // We do not visit the label because it needs to be visited in the context |
| // of the statement. |
| // |
| checkUnreachableNode(node); |
| flowAnalysis.breakStatement(node); |
| } |
| |
| @override |
| void visitCascadeExpression( |
| covariant CascadeExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| analyzeExpression(node.target, SharedTypeSchemaView(contextType)); |
| var targetType = node.target.staticType ?? typeProvider.dynamicType; |
| popRewrite(); |
| |
| flowAnalysis.flow!.cascadeExpression_afterTarget( |
| node.target, |
| SharedTypeView(targetType), |
| isNullAware: node.isNullAware, |
| ); |
| |
| if (node.isNullAware) { |
| flowAnalysis.flow!.nullAwareAccess_rightBegin( |
| node.target, |
| SharedTypeView(targetType), |
| ); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| |
| for (var cascadeSection in node.cascadeSections) { |
| analyzeExpression(cascadeSection, operations.unknownType); |
| popRewrite(); |
| } |
| |
| typeAnalyzer.visitCascadeExpression(node); |
| |
| nullShortingTermination(node); |
| flowAnalysis.flow!.cascadeExpression_end(node); |
| _insertImplicitCallReference(node, contextType: contextType); |
| nullSafetyDeadCodeVerifier.verifyCascadeExpression(node); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitCatchClauseParameter(CatchClauseParameter node) { |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitClassDeclaration(covariant ClassDeclarationImpl node) { |
| var declaredFragment = node.declaredFragment!; |
| var declaredElement = declaredFragment.element; |
| |
| // |
| // Continue the class resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| enclosingClass = declaredElement; |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitClassDeclaration(node); |
| } finally { |
| enclosingClass = outerType; |
| } |
| |
| baseOrFinalTypeVerifier.checkElement( |
| declaredElement, |
| node.implementsClause, |
| ); |
| } |
| |
| @override |
| void visitClassTypeAlias(covariant ClassTypeAliasImpl node) { |
| var declaredFragment = node.declaredFragment!; |
| var declaredElement = declaredFragment.element; |
| |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitClassTypeAlias(node); |
| baseOrFinalTypeVerifier.checkElement( |
| declaredElement, |
| node.implementsClause, |
| ); |
| } |
| |
| @override |
| void visitComment(Comment node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitCommentReference(CommentReference node) { |
| // |
| // We do not visit the expression because it needs to be visited in the |
| // context of the reference. |
| // |
| elementResolver.visitCommentReference(node); |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| conditionallyStartInferenceLogging(dump: inferenceLoggingPredicate(source)); |
| try { |
| NodeList<Directive> directives = node.directives; |
| int directiveCount = directives.length; |
| for (int i = 0; i < directiveCount; i++) { |
| directives[i].accept(this); |
| } |
| NodeList<CompilationUnitMember> declarations = node.declarations; |
| int declarationCount = declarations.length; |
| for (int i = 0; i < declarationCount; i++) { |
| declarations[i].accept(this); |
| } |
| checkIdle(); |
| } finally { |
| stopInferenceLogging(); |
| } |
| } |
| |
| @override |
| void visitConditionalExpression( |
| covariant ConditionalExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| ExpressionImpl condition = node.condition; |
| var flow = flowAnalysis.flow; |
| flow?.conditional_conditionBegin(); |
| |
| analyzeExpression( |
| node.condition, |
| SharedTypeSchemaView(typeProvider.boolType), |
| ); |
| condition = popRewrite()!; |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition( |
| condition, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| if (flow != null) { |
| flow.conditional_thenBegin(condition, node); |
| checkUnreachableNode(node.thenExpression); |
| } |
| analyzeExpression(node.thenExpression, SharedTypeSchemaView(contextType)); |
| popRewrite(); |
| nullSafetyDeadCodeVerifier.flowEnd(node.thenExpression); |
| |
| ExpressionImpl elseExpression = node.elseExpression; |
| |
| if (flow != null) { |
| flow.conditional_elseBegin( |
| node.thenExpression, |
| SharedTypeView(node.thenExpression.typeOrThrow), |
| ); |
| checkUnreachableNode(elseExpression); |
| analyzeExpression(elseExpression, SharedTypeSchemaView(contextType)); |
| } else { |
| analyzeExpression(elseExpression, SharedTypeSchemaView(contextType)); |
| } |
| elseExpression = popRewrite()!; |
| |
| typeAnalyzer.visitConditionalExpression(node, contextType: contextType); |
| if (flow != null) { |
| flow.conditional_end( |
| node, |
| SharedTypeView(node.typeOrThrow), |
| elseExpression, |
| SharedTypeView(elseExpression.typeOrThrow), |
| ); |
| nullSafetyDeadCodeVerifier.flowEnd(elseExpression); |
| } |
| _insertImplicitCallReference(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitConfiguration(Configuration node) { |
| // Don't visit the children. For the time being we don't resolve anything |
| // inside the configuration. |
| } |
| |
| @override |
| void visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) { |
| var fragment = node.declaredFragment!; |
| var element = fragment.element; |
| var returnType = element.type.returnType; |
| var outerFunction = enclosingFunction; |
| |
| try { |
| enclosingFunction = element; |
| assert(_thisType == null); |
| _setupThisType(); |
| checkUnreachableNode(node); |
| node.documentationComment?.accept(this); |
| node.metadata.accept(this); |
| node.returnType.accept(this); |
| node.parameters.accept(this); |
| |
| flowAnalysis.bodyOrInitializer_enter(node, node.parameters); |
| flowAnalysis.executableDeclaration_enter( |
| node, |
| node.parameters, |
| isClosure: false, |
| ); |
| |
| node.initializers.accept(this); |
| node.redirectedConstructor?.accept(this); |
| node.body.resolve(this, returnType is DynamicType ? null : returnType); |
| elementResolver.visitConstructorDeclaration(node); |
| |
| if (node.factoryKeyword != null) { |
| checkForBodyMayCompleteNormally(body: node.body, errorNode: node); |
| } |
| flowAnalysis.executableDeclaration_exit(node.body, false); |
| flowAnalysis.bodyOrInitializer_exit(); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } finally { |
| enclosingFunction = outerFunction; |
| _thisType = null; |
| } |
| } |
| |
| @override |
| void visitConstructorFieldInitializer( |
| covariant ConstructorFieldInitializerImpl node, |
| ) { |
| // |
| // We visit the expression, but do not visit the field name because it needs |
| // to be visited in the context of the constructor field initializer node. |
| // |
| var fieldName = node.fieldName; |
| var fieldElement = enclosingClass!.getField(fieldName.name); |
| fieldName.element = fieldElement; |
| var fieldType = fieldElement?.type ?? UnknownInferredType.instance; |
| var expression = node.expression; |
| analyzeExpression(expression, SharedTypeSchemaView(fieldType)); |
| expression = popRewrite()!; |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(expression); |
| if (fieldElement != null) { |
| var enclosingConstructor = enclosingFunction as ConstructorElementImpl2; |
| checkForFieldInitializerNotAssignable( |
| node, |
| fieldElement, |
| isConstConstructor: enclosingConstructor.isConst, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| } |
| } |
| |
| @override |
| void visitConstructorName(ConstructorName node) { |
| node.type.accept(this); |
| elementResolver.visitConstructorName(node as ConstructorNameImpl); |
| } |
| |
| @override |
| void visitConstructorReference( |
| covariant ConstructorReferenceImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| _constructorReferenceResolver.resolve(node, contextType: contextType); |
| _insertImplicitCallReference(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitConstructorSelector(ConstructorSelector node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitContinueStatement(ContinueStatement node) { |
| // |
| // We do not visit the label because it needs to be visited in the context |
| // of the statement. |
| // |
| checkUnreachableNode(node); |
| flowAnalysis.continueStatement(node); |
| } |
| |
| @override |
| void visitDeclaredIdentifier(DeclaredIdentifier node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitDeclaredIdentifier(node); |
| } |
| |
| @override |
| void visitDefaultFormalParameter(covariant DefaultFormalParameterImpl node) { |
| var fragment = node.declaredFragment!; |
| checkUnreachableNode(node); |
| node.parameter.accept(this); |
| var defaultValue = node.defaultValue; |
| if (defaultValue != null) { |
| analyzeExpression( |
| defaultValue, |
| SharedTypeSchemaView(fragment.element.type), |
| ); |
| popRewrite(); |
| } |
| |
| if (fragment is DefaultParameterFragmentImpl && node.isOfLocalFunction) { |
| fragment.constantInitializer = defaultValue; |
| } |
| } |
| |
| @override |
| void visitDoStatement(covariant DoStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| |
| var condition = node.condition; |
| |
| flowAnalysis.flow?.doStatement_bodyBegin(node); |
| node.body.accept(this); |
| |
| flowAnalysis.flow?.doStatement_conditionBegin(); |
| analyzeExpression(condition, SharedTypeSchemaView(typeProvider.boolType)); |
| condition = popRewrite()!; |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition( |
| condition, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| flowAnalysis.flow?.doStatement_end(condition); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitDotShorthandConstructorInvocation( |
| covariant DotShorthandConstructorInvocationImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| _instanceCreationExpressionResolver.resolveDotShorthand( |
| node, |
| contextType: contextType, |
| ); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitDotShorthandInvocation( |
| covariant DotShorthandInvocationImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| |
| node.typeArguments?.accept(this); |
| var rewrittenExpression = elementResolver.visitDotShorthandInvocation( |
| node, |
| whyNotPromotedArguments: whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| switch (rewrittenExpression) { |
| case null: |
| // In this case, we didn't rewrite anything. The [node] is a static |
| // method invocation. |
| break; |
| case FunctionExpressionInvocationImpl(): |
| _resolveRewrittenFunctionExpressionInvocation( |
| rewrittenExpression, |
| whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| case DotShorthandConstructorInvocationImpl(): |
| _instanceCreationExpressionResolver.resolveDotShorthand( |
| rewrittenExpression, |
| contextType: contextType, |
| ); |
| } |
| |
| if (rewrittenExpression is FunctionExpressionInvocationImpl || |
| rewrittenExpression == null) { |
| var replacement = insertGenericFunctionInstantiation( |
| node, |
| contextType: contextType, |
| ); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, |
| whyNotPromotedArguments, |
| ); |
| _insertImplicitCallReference(replacement, contextType: contextType); |
| } |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitDotShorthandPropertyAccess( |
| covariant DotShorthandPropertyAccessImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| var result = _propertyElementResolver.resolveDotShorthand( |
| node, |
| contextType: contextType, |
| ); |
| _resolvePropertyAccessRhs_common( |
| result, |
| node, |
| node.propertyName, |
| contextType, |
| ); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitDoubleLiteral( |
| DoubleLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitDoubleLiteral(node as DoubleLiteralImpl); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| TypeImpl visitEmptyFunctionBody( |
| EmptyFunctionBody node, { |
| TypeImpl? imposedType, |
| }) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| return imposedType ?? typeProvider.dynamicType; |
| } |
| |
| @override |
| void visitEmptyStatement(EmptyStatement node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitEnumConstantArguments(EnumConstantArguments node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitEnumConstantDeclaration( |
| covariant EnumConstantDeclarationImpl node, |
| ) { |
| node.documentationComment?.accept(this); |
| node.metadata.accept(this); |
| checkUnreachableNode(node); |
| |
| var fragment = node.declaredFragment!; |
| var initializer = fragment.constantInitializer; |
| if (initializer is InstanceCreationExpressionImpl) { |
| var constructorName = initializer.constructorName; |
| var constructorElement = constructorName.element; |
| if (constructorElement != null) { |
| node.constructorElement2 = constructorElement; |
| if (constructorElement.isFactory) { |
| var constructorName = node.arguments?.constructorSelector?.name; |
| var errorTarget = constructorName ?? node.name; |
| errorReporter.atEntity( |
| errorTarget, |
| CompileTimeErrorCode.ENUM_CONSTANT_INVOKES_FACTORY_CONSTRUCTOR, |
| ); |
| } |
| } else { |
| if (constructorName.type.element2 is EnumElementImpl2) { |
| var nameNode = node.arguments?.constructorSelector?.name; |
| if (nameNode != null) { |
| errorReporter.atNode( |
| nameNode, |
| CompileTimeErrorCode.UNDEFINED_ENUM_CONSTRUCTOR_NAMED, |
| arguments: [nameNode.name], |
| ); |
| } else { |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.UNDEFINED_ENUM_CONSTRUCTOR_UNNAMED, |
| ); |
| } |
| } |
| } |
| if (constructorElement != null) { |
| var arguments = node.arguments; |
| if (arguments != null) { |
| var argumentList = arguments.argumentList; |
| argumentList.correspondingStaticParameters2 = |
| ResolverVisitor.resolveArgumentsToParameters( |
| argumentList: argumentList, |
| formalParameters: constructorElement.formalParameters, |
| errorReporter: errorReporter, |
| ); |
| } else if (definingLibrary.featureSet.isEnabled( |
| Feature.enhanced_enums, |
| )) { |
| var requiredParameterCount = |
| constructorElement.formalParameters |
| .where((e) => e.isRequiredPositional) |
| .length; |
| if (requiredParameterCount != 0) { |
| _reportNotEnoughPositionalArguments( |
| token: node.name, |
| requiredParameterCount: requiredParameterCount, |
| actualArgumentCount: 0, |
| nameNode: node, |
| errorReporter: errorReporter, |
| ); |
| } |
| } |
| } |
| } |
| |
| var arguments = node.arguments; |
| if (arguments != null) { |
| var argumentList = arguments.argumentList; |
| for (var argument in argumentList.arguments) { |
| analyzeExpression( |
| argument, |
| SharedTypeSchemaView( |
| argument.correspondingParameter?.type ?? |
| UnknownInferredType.instance, |
| ), |
| ); |
| popRewrite(); |
| } |
| arguments.typeArguments?.accept(this); |
| |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| checkForArgumentTypesNotAssignableInList( |
| argumentList, |
| whyNotPromotedArguments, |
| ); |
| } |
| |
| elementResolver.visitEnumConstantDeclaration(node); |
| } |
| |
| @override |
| void visitEnumDeclaration(covariant EnumDeclarationImpl node) { |
| // |
| // Continue the enum resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| enclosingClass = node.declaredFragment!.element; |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitEnumDeclaration(node); |
| } finally { |
| enclosingClass = outerType; |
| } |
| } |
| |
| @override |
| void visitExportDirective(covariant ExportDirectiveImpl node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitExportDirective(node); |
| } |
| |
| @override |
| TypeImpl visitExpressionFunctionBody( |
| covariant ExpressionFunctionBodyImpl node, { |
| TypeImpl? imposedType, |
| }) { |
| var oldBodyContext = _bodyContext; |
| try { |
| var bodyContext = |
| _bodyContext = BodyInferenceContext( |
| typeSystem: typeSystem, |
| node: node, |
| imposedType: imposedType, |
| ); |
| |
| checkUnreachableNode(node); |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView( |
| bodyContext.contextType ?? UnknownInferredType.instance, |
| ), |
| ); |
| popRewrite(); |
| |
| flowAnalysis.flow?.handleExit(); |
| |
| bodyContext.addReturnExpression(node.expression); |
| return _finishFunctionBodyInference(); |
| } finally { |
| _bodyContext = oldBodyContext; |
| } |
| } |
| |
| @override |
| void visitExpressionStatement(covariant ExpressionStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| analyzeExpression(node.expression, operations.unknownType); |
| popRewrite(); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitExtendsClause(ExtendsClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) { |
| var outerExtension = enclosingExtension; |
| try { |
| enclosingExtension = node.declaredFragment!.element; |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitExtensionDeclaration(node); |
| } finally { |
| enclosingExtension = outerExtension; |
| } |
| } |
| |
| @override |
| void visitExtensionOnClause(ExtensionOnClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitExtensionOverride( |
| covariant ExtensionOverrideImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExtensionOverride(node, contextType); |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| node.typeArguments?.accept(this); |
| |
| var receiverContextType = ExtensionMemberResolver( |
| this, |
| ).computeOverrideReceiverContextType(node); |
| InvocationInferrer<ExtensionOverrideImpl>( |
| resolver: this, |
| node: node, |
| argumentList: node.argumentList, |
| contextType: UnknownInferredType.instance, |
| whyNotPromotedArguments: whyNotPromotedArguments, |
| ).resolveInvocation( |
| rawType: |
| receiverContextType == null |
| ? null |
| : FunctionTypeImpl.v2( |
| typeParameters: const [], |
| formalParameters: [ |
| FormalParameterElementImpl.synthetic( |
| null, |
| receiverContextType, |
| ParameterKind.REQUIRED, |
| ), |
| ], |
| returnType: DynamicTypeImpl.instance, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ), |
| ); |
| |
| extensionResolver.resolveOverride(node, whyNotPromotedArguments); |
| inferenceLogWriter?.exitExtensionOverride(node); |
| } |
| |
| @override |
| void visitExtensionTypeDeclaration( |
| covariant ExtensionTypeDeclarationImpl node, |
| ) { |
| var outerType = enclosingClass; |
| try { |
| enclosingClass = node.declaredFragment!.element; |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitExtensionTypeDeclaration(node); |
| } finally { |
| enclosingClass = outerType; |
| } |
| } |
| |
| @override |
| void visitFieldDeclaration(FieldDeclaration node) { |
| try { |
| assert(_thisType == null); |
| _setupThisType(); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitFieldDeclaration(node); |
| } finally { |
| _thisType = null; |
| } |
| } |
| |
| @override |
| void visitFieldFormalParameter(FieldFormalParameter node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitFieldFormalParameter(node); |
| } |
| |
| @override |
| void visitForElement( |
| covariant ForElementImpl node, { |
| CollectionLiteralContext? context, |
| }) { |
| inferenceLogWriter?.enterElement(node); |
| _forResolver.resolveElement(node, context); |
| inferenceLogWriter?.exitElement(node); |
| } |
| |
| @override |
| void visitFormalParameterList(covariant FormalParameterListImpl node) { |
| // Formal parameter lists can contain default values, which in turn contain |
| // expressions, so we need flow analysis to be available to process those |
| // expressions. |
| var isTopLevel = flowAnalysis.flow == null; |
| if (isTopLevel) { |
| flowAnalysis.bodyOrInitializer_enter(node, null); |
| } |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| if (isTopLevel) { |
| flowAnalysis.bodyOrInitializer_exit(); |
| } |
| } |
| |
| @override |
| void visitForStatement(ForStatement node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| _forResolver.resolveStatement(node as ForStatementImpl); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) { |
| bool isLocal = node.parent is FunctionDeclarationStatement; |
| var fragment = node.declaredFragment!; |
| var element = fragment.element; |
| var functionType = element.type; |
| var outerFunction = enclosingFunction; |
| |
| try { |
| enclosingFunction = element; |
| checkUnreachableNode(node); |
| node.documentationComment?.accept(this); |
| node.metadata.accept(this); |
| node.returnType?.accept(this); |
| |
| if (isLocal) { |
| flowAnalysis.flow!.functionExpression_begin(node); |
| } else { |
| flowAnalysis.bodyOrInitializer_enter( |
| node, |
| node.functionExpression.parameters, |
| ); |
| } |
| flowAnalysis.executableDeclaration_enter( |
| node, |
| node.functionExpression.parameters, |
| isClosure: isLocal, |
| ); |
| |
| analyzeExpression( |
| node.functionExpression, |
| SharedTypeSchemaView(functionType), |
| ); |
| popRewrite(); |
| elementResolver.visitFunctionDeclaration(node); |
| |
| if (!node.isSetter) { |
| checkForBodyMayCompleteNormally( |
| body: node.functionExpression.body, |
| errorNode: node.name, |
| ); |
| } |
| flowAnalysis.executableDeclaration_exit( |
| node.functionExpression.body, |
| isLocal, |
| ); |
| if (isLocal) { |
| flowAnalysis.flow!.functionExpression_end(); |
| } else { |
| flowAnalysis.bodyOrInitializer_exit(); |
| } |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } finally { |
| enclosingFunction = outerFunction; |
| } |
| } |
| |
| @override |
| void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitFunctionExpression( |
| covariant FunctionExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| var outerFunction = enclosingFunction; |
| enclosingFunction = node.declaredFragment!.element; |
| |
| _functionExpressionResolver.resolve(node, contextType: contextType); |
| insertGenericFunctionInstantiation(node, contextType: contextType); |
| |
| enclosingFunction = outerFunction; |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitFunctionExpressionInvocation( |
| covariant FunctionExpressionInvocationImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| analyzeExpression( |
| node.function, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| node.function = popRewrite()!; |
| |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| _functionExpressionInvocationResolver.resolve( |
| node, |
| whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| nullShortingTermination(node); |
| var replacement = insertGenericFunctionInstantiation( |
| node, |
| contextType: contextType, |
| ); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, |
| whyNotPromotedArguments, |
| ); |
| _insertImplicitCallReference(replacement, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitFunctionReference( |
| covariant FunctionReferenceImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| _functionReferenceResolver.resolve(node); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitFunctionTypeAlias(node); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitFunctionTypedFormalParameter(node); |
| } |
| |
| @override |
| void visitGenericFunctionType(GenericFunctionType node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitGenericTypeAlias(node); |
| } |
| |
| @override |
| void visitHideCombinator(HideCombinator node) {} |
| |
| @override |
| void visitIfElement( |
| covariant IfElementImpl node, { |
| CollectionLiteralContext? context, |
| }) { |
| inferenceLogWriter?.enterElement(node); |
| var caseClause = node.caseClause; |
| if (caseClause != null) { |
| var guardedPattern = caseClause.guardedPattern; |
| analyzeIfCaseElement( |
| node: node, |
| expression: node.expression, |
| pattern: guardedPattern.pattern, |
| variables: guardedPattern.variables, |
| guard: guardedPattern.whenClause?.expression, |
| ifTrue: node.thenElement, |
| ifFalse: node.elseElement, |
| context: context, |
| ); |
| // Stack: (Expression, Guard) |
| popRewrite(); // guard |
| popRewrite()!; // expression |
| } else { |
| analyzeIfElement( |
| node: node, |
| condition: node.expression, |
| ifTrue: node.thenElement, |
| ifFalse: node.elseElement, |
| context: context, |
| ); |
| } |
| inferenceLogWriter?.exitElement(node); |
| } |
| |
| @override |
| void visitIfStatement(covariant IfStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| |
| var caseClause = node.caseClause; |
| if (caseClause != null) { |
| var guardedPattern = caseClause.guardedPattern; |
| analyzeIfCaseStatement( |
| node, |
| node.expression, |
| guardedPattern.pattern, |
| guardedPattern.whenClause?.expression, |
| node.thenStatement, |
| node.elseStatement, |
| guardedPattern.variables, |
| ); |
| // Stack: (Expression, Guard) |
| popRewrite(); // guard |
| popRewrite()!; // expression |
| } else { |
| analyzeIfStatement( |
| node, |
| node.expression, |
| node.thenStatement, |
| node.elseStatement, |
| ); |
| } |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitImplementsClause(ImplementsClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitImplicitCallReference( |
| covariant ImplicitCallReferenceImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| checkUnreachableNode(node); |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| node.typeArguments?.accept(this); |
| } |
| |
| @override |
| void visitImportDirective(ImportDirective node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitImportDirective(node as ImportDirectiveImpl); |
| } |
| |
| @override |
| void visitIndexExpression( |
| covariant IndexExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| |
| var target = node.target; |
| if (target != null) { |
| analyzeExpression( |
| target, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| } |
| var targetType = node.realTarget.staticType; |
| |
| if (node.isNullAware) { |
| _startNullAwareAccess(node, node.target); |
| nullSafetyDeadCodeVerifier.visitNode(node.index); |
| } |
| |
| var result = _propertyElementResolver.resolveIndexExpression( |
| node: node, |
| hasRead: true, |
| hasWrite: false, |
| ); |
| |
| var element = result.readElement2; |
| node.element = element as MethodElement?; |
| |
| analyzeExpression( |
| node.index, |
| SharedTypeSchemaView(result.indexContextType), |
| ); |
| popRewrite(); |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(node.index); |
| checkIndexExpressionIndex( |
| node.index, |
| readElement: result.readElement2 as ExecutableElement2OrMember?, |
| writeElement: null, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| DartType type; |
| if (identical(targetType, NeverTypeImpl.instance)) { |
| type = NeverTypeImpl.instance; |
| } else if (element is MethodElement) { |
| type = element.returnType; |
| } else if (targetType is DynamicType) { |
| type = DynamicTypeImpl.instance; |
| } else { |
| type = InvalidTypeImpl.instance; |
| } |
| node.recordStaticType(type, resolver: this); |
| var replacement = insertGenericFunctionInstantiation( |
| node, |
| contextType: contextType, |
| ); |
| |
| nullShortingTermination(node, rewrittenExpression: replacement); |
| _insertImplicitCallReference(replacement, contextType: contextType); |
| nullSafetyDeadCodeVerifier.verifyIndexExpression(node); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitInstanceCreationExpression( |
| covariant InstanceCreationExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _instanceCreationExpressionResolver.resolve(node, contextType: contextType); |
| _insertImplicitCallReference(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitIntegerLiteral( |
| IntegerLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitIntegerLiteral( |
| node as IntegerLiteralImpl, |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitInterpolationExpression( |
| covariant InterpolationExpressionImpl node, |
| ) { |
| checkUnreachableNode(node); |
| analyzeExpression(node.expression, operations.unknownType); |
| popRewrite(); |
| } |
| |
| @override |
| void visitInterpolationString(InterpolationString node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitIsExpression( |
| covariant IsExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| checkUnreachableNode(node.type); |
| node.type.accept(this); |
| |
| typeAnalyzer.visitIsExpression(node); |
| flowAnalysis.isExpression(node); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitLabel(Label node) {} |
| |
| @override |
| void visitLabeledStatement(covariant LabeledStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| flowAnalysis.labeledStatement_enter(node); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| flowAnalysis.labeledStatement_exit(node); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitLibraryDirective(LibraryDirective node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitLibraryDirective(node); |
| } |
| |
| @override |
| void visitLibraryIdentifier( |
| LibraryIdentifier node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) {} |
| |
| @override |
| void visitListLiteral( |
| covariant ListLiteralImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _typedLiteralResolver.resolveListLiteral(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitMapLiteralEntry( |
| covariant MapLiteralEntryImpl node, { |
| CollectionLiteralContext? context, |
| }) { |
| inferenceLogWriter?.enterElement(node); |
| checkUnreachableNode(node); |
| |
| // If the key is null-aware, the context of the expression under `?` should |
| // be changed to the nullable version of the downwards context. |
| var keyTypeContext = context?.keyType; |
| if (keyTypeContext != null && node.keyQuestion != null) { |
| keyTypeContext = typeSystem.makeNullable(keyTypeContext); |
| } |
| var keyType = analyzeExpression( |
| node.key, |
| SharedTypeSchemaView(keyTypeContext ?? UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| flowAnalysis.flow?.nullAwareMapEntry_valueBegin( |
| node.key, |
| keyType, |
| isKeyNullAware: node.keyQuestion != null, |
| ); |
| |
| // If the value is null-aware, the context of the expression under `?` |
| // should be changed to the nullable version of the downwards context. |
| var valueTypeContext = context?.valueType; |
| if (valueTypeContext != null && node.valueQuestion != null) { |
| valueTypeContext = typeSystem.makeNullable(valueTypeContext); |
| } |
| analyzeExpression( |
| node.value, |
| SharedTypeSchemaView(valueTypeContext ?? UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| flowAnalysis.flow?.nullAwareMapEntry_end( |
| isKeyNullAware: node.keyQuestion != null, |
| ); |
| inferenceLogWriter?.exitElement(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(covariant MethodDeclarationImpl node) { |
| var fragment = node.declaredFragment!; |
| var element = fragment.element; |
| var returnType = element.returnType; |
| var outerFunction = enclosingFunction; |
| |
| try { |
| enclosingFunction = element; |
| assert(_thisType == null); |
| _setupThisType(); |
| checkUnreachableNode(node); |
| node.documentationComment?.accept(this); |
| node.metadata.accept(this); |
| node.returnType?.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters?.accept(this); |
| |
| flowAnalysis.bodyOrInitializer_enter(node, node.parameters); |
| flowAnalysis.executableDeclaration_enter( |
| node, |
| node.parameters, |
| isClosure: false, |
| ); |
| |
| node.body.resolve(this, returnType is DynamicType ? null : returnType); |
| elementResolver.visitMethodDeclaration(node); |
| |
| if (!node.isSetter) { |
| checkForBodyMayCompleteNormally(body: node.body, errorNode: node.name); |
| } |
| flowAnalysis.executableDeclaration_exit(node.body, false); |
| flowAnalysis.bodyOrInitializer_exit(); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } finally { |
| enclosingFunction = outerFunction; |
| _thisType = null; |
| } |
| } |
| |
| @override |
| void visitMethodInvocation( |
| covariant MethodInvocationImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| var target = node.target; |
| if (target != null) { |
| analyzeExpression(target, operations.unknownType); |
| target = popRewrite(); |
| } |
| |
| if (node.isNullAware) { |
| _startNullAwareAccess(node, target); |
| nullSafetyDeadCodeVerifier.visitNode(node.methodName); |
| } |
| |
| node.typeArguments?.accept(this); |
| var functionRewrite = elementResolver.visitMethodInvocation( |
| node, |
| whyNotPromotedArguments: whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| |
| if (functionRewrite != null) { |
| _resolveRewrittenFunctionExpressionInvocation( |
| functionRewrite, |
| whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| } |
| nullShortingTermination(node, rewrittenExpression: functionRewrite); |
| var replacement = insertGenericFunctionInstantiation( |
| node, |
| contextType: contextType, |
| ); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, |
| whyNotPromotedArguments, |
| ); |
| _insertImplicitCallReference(replacement, contextType: contextType); |
| nullSafetyDeadCodeVerifier.verifyMethodInvocation(node); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitMixinDeclaration(covariant MixinDeclarationImpl node) { |
| var declaredFragment = node.declaredFragment!; |
| var declaredElement = declaredFragment.element; |
| |
| // |
| // Continue the class resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| enclosingClass = node.declaredFragment!.element; |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitMixinDeclaration(node); |
| } finally { |
| enclosingClass = outerType; |
| } |
| |
| baseOrFinalTypeVerifier.checkElement( |
| declaredElement, |
| node.implementsClause, |
| ); |
| } |
| |
| @override |
| void visitMixinOnClause(MixinOnClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitNamedExpression( |
| covariant NamedExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.name.accept(this); |
| analyzeExpression(node.expression, SharedTypeSchemaView(contextType)); |
| popRewrite(); |
| typeAnalyzer.visitNamedExpression(node); |
| // Any "why not promoted" information that flow analysis had associated with |
| // `node.expression` now needs to be forwarded to `node`, so that when |
| // `visitArgumentList` iterates through the arguments, it will find it. |
| flowAnalysis.flow?.forwardExpression(node, node.expression); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitNamedType(NamedType node) { |
| // All TypeName(s) are already resolved, so we don't resolve it here. |
| // But there might be type arguments with Expression(s), such as default |
| // values for formal parameters of GenericFunctionType(s). These are |
| // invalid, but if they exist, they should be resolved. |
| node.typeArguments?.accept(this); |
| } |
| |
| @override |
| void visitNativeClause(NativeClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| TypeImpl visitNativeFunctionBody( |
| covariant NativeFunctionBodyImpl node, { |
| TypeImpl? imposedType, |
| }) { |
| checkUnreachableNode(node); |
| if (node.stringLiteral case var stringLiteral?) { |
| analyzeExpression(stringLiteral, operations.unknownType); |
| popRewrite(); |
| } |
| return imposedType ?? typeProvider.dynamicType; |
| } |
| |
| @override |
| void visitNullAwareElement( |
| covariant NullAwareElementImpl node, { |
| CollectionLiteralContext? context, |
| }) { |
| inferenceLogWriter?.enterElement(node); |
| |
| var elementType = context?.elementType; |
| if (elementType != null) { |
| elementType = typeSystem.makeNullable(elementType); |
| } |
| |
| analyzeExpression( |
| node.value, |
| SharedTypeSchemaView(elementType ?? UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| inferenceLogWriter?.exitElement(node); |
| } |
| |
| @override |
| void visitNullLiteral( |
| NullLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| node.visitChildren(this); |
| typeAnalyzer.visitNullLiteral(node as NullLiteralImpl); |
| flowAnalysis.flow?.nullLiteral(node, SharedTypeView(node.typeOrThrow)); |
| checkUnreachableNode(node); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitParenthesizedExpression( |
| covariant ParenthesizedExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| analyzeExpression(node.expression, SharedTypeSchemaView(contextType)); |
| popRewrite(); |
| typeAnalyzer.visitParenthesizedExpression(node); |
| flowAnalysis.flow?.parenthesizedExpression(node, node.expression); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitPartDirective(PartDirective node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitPartDirective(node); |
| } |
| |
| @override |
| void visitPartOfDirective(PartOfDirective node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitPartOfDirective(node); |
| } |
| |
| @override |
| void visitPatternAssignment( |
| covariant PatternAssignmentImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| var analysisResult = analyzePatternAssignment( |
| node, |
| node.pattern, |
| node.expression, |
| ); |
| node.patternTypeSchema = |
| analysisResult.patternSchema.unwrapTypeSchemaView(); |
| node.recordStaticType( |
| // TODO(paulberry): make this type argument unnecessary by changing the |
| // parameter of `ExpressionImpl.recordStaticType` to `TypeImpl`. |
| analysisResult.type.unwrapTypeView<TypeImpl>(), |
| resolver: this, |
| ); |
| popRewrite(); // expression |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitPatternVariableDeclaration( |
| covariant PatternVariableDeclarationImpl node, |
| ) { |
| var patternSchema = |
| analyzePatternVariableDeclaration( |
| node, |
| node.pattern, |
| node.expression, |
| isFinal: node.keyword.keyword == Keyword.FINAL, |
| ).patternSchema; |
| node.patternTypeSchema = patternSchema.unwrapTypeSchemaView(); |
| popRewrite(); // expression |
| } |
| |
| @override |
| void visitPatternVariableDeclarationStatement( |
| PatternVariableDeclarationStatement node, |
| ) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| node.declaration.accept(this); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitPostfixExpression( |
| covariant PostfixExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| _postfixExpressionResolver.resolve(node, contextType: contextType); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitPrefixedIdentifier( |
| covariant PrefixedIdentifierImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| var rewrittenPropertyAccess = _prefixedIdentifierResolver.resolve( |
| node, |
| contextType: contextType, |
| ); |
| if (rewrittenPropertyAccess != null) { |
| _resolvePropertyAccessRhs(rewrittenPropertyAccess, contextType); |
| // We did record that `node` was replaced with `rewrittenPropertyAccess`. |
| // But if `rewrittenPropertyAccess` was itself rewritten, replace the |
| // rewrite result of `node`. |
| assert(() { |
| var rewrite = _replacements[rewrittenPropertyAccess]; |
| if (rewrite != null) { |
| _replacements[node] = rewrite; |
| } |
| return true; |
| }()); |
| inferenceLogWriter?.exitExpression(node); |
| return; |
| } |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitPrefixExpression( |
| PrefixExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _prefixExpressionResolver.resolve( |
| node as PrefixExpressionImpl, |
| contextType: contextType, |
| ); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitPropertyAccess( |
| covariant PropertyAccessImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| |
| // If [isDotShorthand] is set, cache the context type for resolution. |
| if (isDotShorthand(node)) { |
| pushDotShorthandContext(node, SharedTypeSchemaView(contextType)); |
| } |
| |
| checkUnreachableNode(node); |
| |
| var target = node.target; |
| if (target != null) { |
| analyzeExpression( |
| target, |
| SharedTypeSchemaView(UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| } |
| |
| checkUnreachableNode(node.propertyName); |
| _resolvePropertyAccessRhs(node, contextType); |
| |
| if (isDotShorthand(node)) { |
| popDotShorthandContext(); |
| } |
| |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitRecordLiteral( |
| covariant RecordLiteralImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _recordLiteralResolver.resolve(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitRecordTypeAnnotation(covariant RecordTypeAnnotationImpl node) { |
| // All RecordTypeAnnotation(s) are already resolved, so we don't resolve |
| // it here. But there might be types with Expression(s), such as default |
| // values for formal parameters of GenericFunctionType(s). These are |
| // invalid, but if they exist, they should be resolved. |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitRecordTypeAnnotationNamedField( |
| RecordTypeAnnotationNamedField node, |
| ) { |
| node.visitChildren(this); |
| elementResolver.visitRecordTypeAnnotationNamedField(node); |
| } |
| |
| @override |
| void visitRecordTypeAnnotationNamedFields( |
| RecordTypeAnnotationNamedFields node, |
| ) { |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitRecordTypeAnnotationPositionalField( |
| RecordTypeAnnotationPositionalField node, |
| ) { |
| node.visitChildren(this); |
| elementResolver.visitRecordTypeAnnotationPositionalField(node); |
| } |
| |
| @override |
| void visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node, |
| ) { |
| // |
| // We visit the argument list, but do not visit the optional identifier |
| // because it needs to be visited in the context of the constructor |
| // invocation. |
| // |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| elementResolver.visitRedirectingConstructorInvocation( |
| node as RedirectingConstructorInvocationImpl, |
| ); |
| InvocationInferrer<RedirectingConstructorInvocationImpl>( |
| resolver: this, |
| node: node, |
| argumentList: node.argumentList, |
| contextType: UnknownInferredType.instance, |
| whyNotPromotedArguments: whyNotPromotedArguments, |
| ).resolveInvocation(rawType: node.element?.type); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, |
| whyNotPromotedArguments, |
| ); |
| } |
| |
| @override |
| void visitRepresentationConstructorName(RepresentationConstructorName node) {} |
| |
| @override |
| void visitRepresentationDeclaration(RepresentationDeclaration node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitRepresentationDeclaration(node); |
| } |
| |
| @override |
| void visitRethrowExpression( |
| RethrowExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitRethrowExpression(node as RethrowExpressionImpl); |
| flowAnalysis.flow?.handleExit(); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitReturnStatement(covariant ReturnStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| var expression = node.expression; |
| if (expression != null) { |
| analyzeExpression( |
| expression, |
| SharedTypeSchemaView( |
| bodyContext?.contextType ?? UnknownInferredType.instance, |
| ), |
| ); |
| // Pick up the expression again in case it was rewritten. |
| expression = popRewrite(); |
| } |
| |
| bodyContext?.addReturnExpression(expression); |
| flowAnalysis.flow?.handleExit(); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitSetOrMapLiteral( |
| SetOrMapLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| _typedLiteralResolver.resolveSetOrMapLiteral( |
| node, |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitShowCombinator(ShowCombinator node) {} |
| |
| @override |
| void visitSimpleFormalParameter(SimpleFormalParameter node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitSimpleFormalParameter(node); |
| } |
| |
| @override |
| void visitSimpleIdentifier( |
| covariant SimpleIdentifierImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| _simpleIdentifierResolver.resolve(node, contextType: contextType); |
| _insertImplicitCallReference( |
| insertGenericFunctionInstantiation(node, contextType: contextType), |
| contextType: contextType, |
| ); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitSimpleStringLiteral( |
| SimpleStringLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitSimpleStringLiteral(node as SimpleStringLiteralImpl); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitSpreadElement( |
| covariant SpreadElementImpl node, { |
| CollectionLiteralContext? context, |
| }) { |
| inferenceLogWriter?.enterElement(node); |
| var iterableType = context?.iterableType; |
| if (iterableType != null && node.isNullAware) { |
| iterableType = typeSystem.makeNullable(iterableType); |
| } |
| checkUnreachableNode(node); |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(iterableType ?? UnknownInferredType.instance), |
| ); |
| popRewrite(); |
| |
| if (!node.isNullAware) { |
| nullableDereferenceVerifier.expression( |
| CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_SPREAD, |
| node.expression, |
| ); |
| } |
| |
| inferenceLogWriter?.exitElement(node); |
| } |
| |
| @override |
| void visitStringInterpolation( |
| StringInterpolation node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitStringInterpolation(node as StringInterpolationImpl); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| // |
| // We visit the argument list, but do not visit the optional identifier |
| // because it needs to be visited in the context of the constructor |
| // invocation. |
| // |
| var whyNotPromotedArguments = |
| <Map<SharedTypeView, NonPromotionReason> Function()>[]; |
| elementResolver.visitSuperConstructorInvocation( |
| node as SuperConstructorInvocationImpl, |
| ); |
| InvocationInferrer<SuperConstructorInvocationImpl>( |
| resolver: this, |
| node: node, |
| argumentList: node.argumentList, |
| contextType: UnknownInferredType.instance, |
| whyNotPromotedArguments: whyNotPromotedArguments, |
| ).resolveInvocation(rawType: node.element?.type); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, |
| whyNotPromotedArguments, |
| ); |
| } |
| |
| @override |
| void visitSuperExpression( |
| SuperExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitSuperExpression(node); |
| typeAnalyzer.visitSuperExpression(node as SuperExpressionImpl); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitSuperFormalParameter(SuperFormalParameter node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitSwitchExpression( |
| covariant SwitchExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| var previousExhaustiveness = legacySwitchExhaustiveness; |
| var staticType = |
| analyzeSwitchExpression( |
| node, |
| node.expression, |
| node.cases.length, |
| SharedTypeSchemaView(contextType), |
| ).type.unwrapTypeView<TypeImpl>(); |
| node.recordStaticType(staticType, resolver: this); |
| popRewrite(); |
| legacySwitchExhaustiveness = previousExhaustiveness; |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitSwitchStatement(covariant SwitchStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| // Stack: () |
| checkUnreachableNode(node); |
| |
| var previousExhaustiveness = legacySwitchExhaustiveness; |
| analyzeSwitchStatement(node, node.expression, node.memberGroups.length); |
| // Stack: (Expression) |
| popRewrite(); |
| // Stack: () |
| legacySwitchExhaustiveness = previousExhaustiveness; |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitSymbolLiteral( |
| SymbolLiteral node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitSymbolLiteral(node as SymbolLiteralImpl); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitThisExpression( |
| ThisExpression node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| typeAnalyzer.visitThisExpression(node as ThisExpressionImpl); |
| _insertImplicitCallReference(node, contextType: contextType); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitThrowExpression( |
| covariant ThrowExpressionImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| analyzeExpression( |
| node.expression, |
| SharedTypeSchemaView(typeProvider.objectType), |
| ); |
| popRewrite(); |
| typeAnalyzer.visitThrowExpression(node); |
| flowAnalysis.flow?.handleExit(); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitTopLevelVariableDeclaration(node); |
| } |
| |
| @override |
| void visitTryStatement(covariant TryStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| var flow = flowAnalysis.flow!; |
| |
| var body = node.body; |
| var catchClauses = node.catchClauses; |
| var finallyBlock = node.finallyBlock; |
| |
| if (finallyBlock != null) { |
| flow.tryFinallyStatement_bodyBegin(); |
| } |
| |
| if (catchClauses.isNotEmpty) { |
| flow.tryCatchStatement_bodyBegin(); |
| } |
| body.accept(this); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| nullSafetyDeadCodeVerifier.tryStatementEnter(node); |
| if (catchClauses.isNotEmpty) { |
| flow.tryCatchStatement_bodyEnd(body); |
| |
| var catchLength = catchClauses.length; |
| for (var i = 0; i < catchLength; ++i) { |
| var catchClause = catchClauses[i]; |
| nullSafetyDeadCodeVerifier.verifyCatchClause(catchClause); |
| // TODO(paulberry): try to remove these casts by changing `node` to a |
| // `TryStatementImpl` |
| flow.tryCatchStatement_catchBegin( |
| catchClause.exceptionParameter?.declaredElement2 |
| as PromotableElementImpl2?, |
| catchClause.stackTraceParameter?.declaredElement2 |
| as PromotableElementImpl2?, |
| ); |
| catchClause.accept(this); |
| flow.tryCatchStatement_catchEnd(); |
| nullSafetyDeadCodeVerifier.flowEnd(catchClause.body); |
| } |
| |
| flow.tryCatchStatement_end(); |
| } |
| nullSafetyDeadCodeVerifier.tryStatementExit(node); |
| |
| if (finallyBlock != null) { |
| flow.tryFinallyStatement_finallyBegin( |
| catchClauses.isNotEmpty ? node : body, |
| ); |
| finallyBlock.accept(this); |
| flow.tryFinallyStatement_end(); |
| } |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitTypeArgumentList(TypeArgumentList node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitTypeLiteral( |
| covariant TypeLiteralImpl node, { |
| TypeImpl contextType = UnknownInferredType.instance, |
| }) { |
| inferenceLogWriter?.enterExpression(node, contextType); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| node.recordStaticType(typeProvider.typeType, resolver: this); |
| inferenceLogWriter?.exitExpression(node); |
| } |
| |
| @override |
| void visitTypeParameter(TypeParameter node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitTypeParameter(node); |
| } |
| |
| @override |
| void visitTypeParameterList(TypeParameterList node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitVariableDeclaration(covariant VariableDeclarationImpl node) { |
| var fragment = node.declaredFragment!; |
| |
| libraryResolutionContext._variableNodes[fragment] = node; |
| _variableDeclarationResolver.resolve(node); |
| |
| var initializer = node.initializer; |
| if (initializer != null) { |
| var parent = node.parent as VariableDeclarationList; |
| var declaredType = parent.type; |
| var initializerStaticType = initializer.typeOrThrow; |
| flowAnalysis.flow?.initialize( |
| node.declaredElement2 as PromotableElementImpl2, |
| SharedTypeView(initializerStaticType), |
| initializer, |
| isFinal: parent.isFinal, |
| isLate: parent.isLate, |
| isImplicitlyTyped: declaredType == null, |
| ); |
| } |
| |
| _checkTopLevelCycle(node); |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList node) { |
| flowAnalysis.variableDeclarationList(node); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| elementResolver.visitVariableDeclarationList(node); |
| } |
| |
| @override |
| void visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitWhileStatement(covariant WhileStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| |
| ExpressionImpl condition = node.condition; |
| |
| flowAnalysis.flow?.whileStatement_conditionBegin(node); |
| analyzeExpression(condition, SharedTypeSchemaView(typeProvider.boolType)); |
| condition = popRewrite()!; |
| var whyNotPromoted = flowAnalysis.flow?.whyNotPromoted(condition); |
| |
| boolExpressionVerifier.checkForNonBoolCondition( |
| node.condition, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| flowAnalysis.flow?.whileStatement_bodyBegin(node, condition); |
| node.body.accept(this); |
| flowAnalysis.flow?.whileStatement_end(); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| // TODO(brianwilkerson): If the loop can only be exited because the condition |
| // is false, then propagateFalseState(condition); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| @override |
| void visitWithClause(WithClause node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| } |
| |
| @override |
| void visitYieldStatement(covariant YieldStatementImpl node) { |
| inferenceLogWriter?.enterStatement(node); |
| checkUnreachableNode(node); |
| _yieldStatementResolver.resolve(node); |
| inferenceLogWriter?.exitStatement(node); |
| } |
| |
| /// Check whether [errorNode] is an `onError` callback in a |
| /// [Future.catchError] call, which might return an implicit `null`. |
| void _checkForFutureCatchErrorOnError(BlockFunctionBody errorNode) { |
| // Check for "body might complete normally" in a `Future.catchError`'s |
| //`onError` callback. |
| var parent = errorNode.parent?.parent; |
| if (parent is! ArgumentList) { |
| return; |
| } |
| var invocation = parent.parent; |
| if (invocation is! MethodInvocation) { |
| return; |
| } |
| var targetType = invocation.realTarget?.staticType; |
| if (invocation.methodName.name == 'catchError' && |
| targetType is InterfaceTypeImpl) { |
| var instanceOfFuture = targetType.asInstanceOf2( |
| typeProvider.futureElement2, |
| ); |
| if (instanceOfFuture != null) { |
| var targetFutureType = instanceOfFuture.typeArguments.first; |
| var expectedReturnType = typeProvider.futureOrType(targetFutureType); |
| var returnTypeBase = typeSystem.futureOrBase(expectedReturnType); |
| if (returnTypeBase is DynamicType || |
| returnTypeBase is UnknownInferredType || |
| returnTypeBase is VoidType || |
| returnTypeBase.isDartCoreNull) { |
| return; |
| } |
| |
| errorReporter.atToken( |
| errorNode.block.leftBracket, |
| WarningCode.BODY_MIGHT_COMPLETE_NORMALLY_CATCH_ERROR, |
| arguments: [returnTypeBase], |
| ); |
| } |
| } |
| } |
| |
| void _checkTopLevelCycle(VariableDeclaration node) { |
| var fragment = node.declaredFragment; |
| if (fragment is! PropertyInducingElementImpl) { |
| return; |
| } |
| // Errors on const are reported separately with |
| // [CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT]. |
| if (fragment.isConst) { |
| return; |
| } |
| var error = fragment.typeInferenceError; |
| if (error == null) { |
| return; |
| } |
| if (error.kind == TopLevelInferenceErrorKind.dependencyCycle) { |
| var argumentsText = error.arguments.join(', '); |
| errorReporter.atToken( |
| node.name, |
| CompileTimeErrorCode.TOP_LEVEL_CYCLE, |
| arguments: [node.name.lexeme, argumentsText], |
| ); |
| } |
| } |
| |
| /// Creates a union of `T | Future<T>`, unless `T` is already a |
| /// future-union, in which case it simply returns `T`. |
| TypeImpl _createFutureOr(TypeImpl type) { |
| if (type.isDartAsyncFutureOr) { |
| return type; |
| } |
| return typeProvider.futureOrType(type); |
| } |
| |
| /// Helper function used to print information to the console in debug mode. |
| /// This method returns `true` so that it can be conveniently called inside of |
| /// an `assert` statement. |
| bool _debugPrint(String s) { |
| print(s); |
| return true; |
| } |
| |
| TypeImpl _finishFunctionBodyInference() { |
| var flow = flowAnalysis.flow; |
| |
| return _bodyContext!.computeInferredReturnType( |
| endOfBlockIsReachable: flow == null || flow.isReachable, |
| ); |
| } |
| |
| /// Infers type arguments corresponding to [typeParameters] used it the |
| /// [declaredType], so that thr resulting type is a subtype of [contextType]. |
| List<TypeImpl> _inferTypeArguments({ |
| required List<TypeParameterElementImpl2> typeParameters, |
| required AstNode errorNode, |
| required TypeImpl declaredType, |
| required TypeImpl contextType, |
| required AstNodeImpl? nodeForTesting, |
| }) { |
| inferenceLogWriter?.enterGenericInference(typeParameters, declaredType); |
| var inferrer = GenericInferrer( |
| typeSystem, |
| typeParameters, |
| errorEntity: errorNode, |
| genericMetadataIsEnabled: genericMetadataIsEnabled, |
| inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled, |
| strictInference: analysisOptions.strictInference, |
| typeSystemOperations: flowAnalysis.typeOperations, |
| dataForTesting: inferenceHelper.dataForTesting, |
| ); |
| inferrer.constrainReturnType( |
| declaredType, |
| contextType, |
| nodeForTesting: nodeForTesting, |
| ); |
| return inferrer.chooseFinalTypes(); |
| } |
| |
| /// If `expression` should be treated as `expression.call`, inserts an |
| /// [ImplicitCallReference] node which wraps [expression]. |
| void _insertImplicitCallReference( |
| ExpressionImpl expression, { |
| required TypeImpl contextType, |
| }) { |
| var parent = expression.parent; |
| if (_shouldSkipImplicitCallReferenceDueToForm(expression, parent)) { |
| return; |
| } |
| var staticType = expression.staticType; |
| if (staticType == null) { |
| return; |
| } |
| TypeImpl context; |
| if (parent is AssignmentExpressionImpl) { |
| if (parent.writeType == null) return; |
| context = parent.writeType!; |
| } else { |
| context = contextType; |
| } |
| var callMethod = getImplicitCallMethod(staticType, context, expression); |
| if (callMethod == null) { |
| return; |
| } |
| |
| // `expression` is to be treated as `expression.call`. |
| context = typeSystem.flatten(context); |
| var callMethodType = callMethod.type; |
| List<DartType> typeArgumentTypes; |
| if (isConstructorTearoffsEnabled && |
| callMethodType.typeFormals.isNotEmpty && |
| context is FunctionTypeImpl) { |
| typeArgumentTypes = typeSystem.inferFunctionTypeInstantiation( |
| context, |
| callMethodType, |
| errorReporter: errorReporter, |
| errorNode: expression, |
| // If the constructor-tearoffs feature is enabled, then so is |
| // generic-metadata. |
| genericMetadataIsEnabled: true, |
| inferenceUsingBoundsIsEnabled: inferenceUsingBoundsIsEnabled, |
| strictInference: analysisOptions.strictInference, |
| strictCasts: analysisOptions.strictCasts, |
| typeSystemOperations: flowAnalysis.typeOperations, |
| dataForTesting: inferenceHelper.dataForTesting, |
| nodeForTesting: expression, |
| ); |
| if (typeArgumentTypes.isNotEmpty) { |
| callMethodType = callMethodType.instantiate(typeArgumentTypes); |
| } |
| } else { |
| typeArgumentTypes = []; |
| } |
| |
| var callReference = ImplicitCallReferenceImpl( |
| expression: expression, |
| element: callMethod, |
| typeArguments: null, |
| typeArgumentTypes: typeArgumentTypes, |
| ); |
| replaceExpression(expression, callReference, parent: parent); |
| |
| callReference.setPseudoExpressionStaticType(callMethodType); |
| } |
| |
| void _resolvePropertyAccessRhs( |
| PropertyAccessImpl node, |
| TypeImpl contextType, |
| ) { |
| if (node.isNullAware) { |
| _startNullAwareAccess(node, node.target); |
| nullSafetyDeadCodeVerifier.visitNode(node.propertyName); |
| } |
| |
| var result = _propertyElementResolver.resolvePropertyAccess( |
| node: node, |
| hasRead: true, |
| hasWrite: false, |
| ); |
| |
| _resolvePropertyAccessRhs_common( |
| result, |
| node, |
| node.propertyName, |
| contextType, |
| ); |
| nullSafetyDeadCodeVerifier.verifyPropertyAccess(node); |
| } |
| |
| /// Common logic for resolving dot shorthands property accesses and |
| /// [_resolvePropertyAccessRhs]. |
| void _resolvePropertyAccessRhs_common( |
| PropertyElementResolverResult resolverResult, |
| ExpressionImpl node, |
| SimpleIdentifierImpl propertyName, |
| TypeImpl contextType, |
| ) { |
| var element = resolverResult.readElement2; |
| |
| propertyName.element = element; |
| |
| DartType type; |
| if (element is MethodElement) { |
| type = element.type; |
| } else if (element is ConstructorElementMixin2) { |
| type = element.type; |
| } else if (element is GetterElement) { |
| type = resolverResult.getType!; |
| } else if (resolverResult.functionTypeCallType != null) { |
| type = resolverResult.functionTypeCallType!; |
| } else if (resolverResult.recordField != null) { |
| type = resolverResult.recordField!.type; |
| } else if (resolverResult.atDynamicTarget) { |
| type = DynamicTypeImpl.instance; |
| } else { |
| type = InvalidTypeImpl.instance; |
| } |
| |
| if (!isConstructorTearoffsEnabled) { |
| // Only perform a generic function instantiation on a [PrefixedIdentifier] |
| // in pre-constructor-tearoffs code. In constructor-tearoffs-enabled code, |
| // generic function instantiation is performed at assignability check |
| // sites. |
| // TODO(srawlins): Switch all resolution to use the latter method, in a |
| // breaking change release. |
| type = inferenceHelper.inferTearOff( |
| node, |
| propertyName, |
| type, |
| contextType: contextType, |
| ); |
| } |
| |
| propertyName.setPseudoExpressionStaticType(type); |
| node.recordStaticType(type, resolver: this); |
| var replacement = insertGenericFunctionInstantiation( |
| node, |
| contextType: contextType, |
| ); |
| |
| nullShortingTermination(node, rewrittenExpression: replacement); |
| _insertImplicitCallReference(replacement, contextType: contextType); |
| } |
| |
| /// Continues resolution of a [FunctionExpressionInvocation] that was created |
| /// from a rewritten [MethodInvocation]. The target function is already |
| /// resolved. |
| /// |
| /// The specification says that `target.getter()` should be treated as an |
| /// ordinary method invocation. So, we need to perform the same null shorting |
| /// as for method invocations. |
| void _resolveRewrittenFunctionExpressionInvocation( |
| FunctionExpressionInvocationImpl node, |
| List<WhyNotPromotedGetter> whyNotPromotedArguments, { |
| required TypeImpl contextType, |
| }) { |
| var function = node.function; |
| |
| if (function is PropertyAccessImpl && function.isNullAware) { |
| _startNullAwareAccess(function, function.target); |
| nullSafetyDeadCodeVerifier.visitNode(node.argumentList); |
| } |
| |
| _functionExpressionInvocationResolver.resolve( |
| node, |
| whyNotPromotedArguments, |
| contextType: contextType, |
| ); |
| |
| nullShortingTermination(node); |
| } |
| |
| void _setupThisType() { |
| var enclosingClass = this.enclosingClass; |
| if (enclosingClass != null) { |
| _thisType = enclosingClass.thisType; |
| } else { |
| var enclosingExtension = this.enclosingExtension; |
| if (enclosingExtension != null) { |
| _thisType = enclosingExtension.extendedType; |
| } |
| } |
| } |
| |
| bool _shouldSkipImplicitCallReferenceDueToForm( |
| Expression expression, |
| AstNode? parent, |
| ) { |
| while (parent is ParenthesizedExpression) { |
| expression = parent; |
| parent = expression.parent; |
| } |
| if (parent is CascadeExpression && parent.target == expression) { |
| // Do not perform an "implicit tear-off conversion" here. It should only |
| // be performed on [parent]. See |
| // https://github.com/dart-lang/language/issues/1873. |
| return true; |
| } |
| if (parent is ConditionalExpression && |
| (parent.thenExpression == expression || |
| parent.elseExpression == expression)) { |
| // Do not perform an "implicit tear-off conversion" on the branches of a |
| // conditional expression. |
| return true; |
| } |
| if (parent is BinaryExpression && |
| parent.operator.type == TokenType.QUESTION_QUESTION) { |
| // Do not perform an "implicit tear-off conversion" on the branches of a |
| // `??` operator. |
| return true; |
| } |
| return false; |
| } |
| |
| void _startNullAwareAccess( |
| NullShortableExpression node, |
| ExpressionImpl? target, |
| ) { |
| var flow = flowAnalysis.flow; |
| if (flow != null) { |
| switch (target) { |
| case null: |
| // This means the property access target is the target of a cascade. |
| // For this case, `node.isNullAware=true` means that the cascade is |
| // null aware, but that has already been taken care of in |
| // `visitCascadeExpression`. So there is nothing further to do. |
| break; |
| case SimpleIdentifier(element: InterfaceElement()): |
| // `?.` to access static methods is equivalent to `.`, so do nothing. |
| break; |
| case ExtensionOverride( |
| argumentList: ArgumentListImpl(arguments: [var expression]), |
| ): |
| case var expression: |
| flow.nullAwareAccess_rightBegin( |
| expression, |
| SharedTypeView(expression.staticType ?? typeProvider.dynamicType), |
| ); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| } |
| } |
| |
| /// Given an [argumentList] and the [parameters] related to the element that |
| /// will be invoked using those arguments, compute the list of parameters that |
| /// correspond to the list of arguments. |
| /// |
| /// Returns the parameters that correspond to the arguments. If no parameter |
| /// matched an argument, that position will be `null` in the list. |
| static List<FormalParameterElementMixin?> resolveArgumentsToParameters({ |
| required ArgumentList argumentList, |
| required List<FormalParameterElement> formalParameters, |
| ErrorReporter? errorReporter, |
| ConstructorDeclaration? enclosingConstructor, |
| }) { |
| int requiredParameterCount = 0; |
| int unnamedParameterCount = 0; |
| var unnamedParameters = <FormalParameterElementMixin>[]; |
| Map<String, FormalParameterElementMixin>? namedParameters; |
| int length = formalParameters.length; |
| for (int i = 0; i < length; i++) { |
| var parameter = formalParameters[i] as FormalParameterElementMixin; |
| if (parameter.isRequiredPositional) { |
| unnamedParameters.add(parameter); |
| unnamedParameterCount++; |
| requiredParameterCount++; |
| } else if (parameter.isOptionalPositional) { |
| unnamedParameters.add(parameter); |
| unnamedParameterCount++; |
| } else { |
| namedParameters ??= {}; |
| namedParameters[parameter.name3 ?? ''] = parameter; |
| } |
| } |
| int unnamedIndex = 0; |
| NodeList<Expression> arguments = argumentList.arguments; |
| int argumentCount = arguments.length; |
| var resolvedParameters = List<FormalParameterElementMixin?>.filled( |
| argumentCount, |
| null, |
| ); |
| int positionalArgumentCount = 0; |
| bool noBlankArguments = true; |
| Expression? firstUnresolvedArgument; |
| Expression? lastPositionalArgument; |
| for (int i = 0; i < argumentCount; i++) { |
| Expression argument = arguments[i]; |
| if (argument is! NamedExpression) { |
| if (argument is SimpleIdentifier && argument.name.isEmpty) { |
| noBlankArguments = false; |
| } |
| positionalArgumentCount++; |
| if (unnamedIndex < unnamedParameterCount) { |
| resolvedParameters[i] = unnamedParameters[unnamedIndex++]; |
| } else { |
| firstUnresolvedArgument ??= argument; |
| } |
| lastPositionalArgument = argument; |
| } |
| } |
| |
| Set<String>? usedNames; |
| if (enclosingConstructor != null) { |
| var result = verifySuperFormalParameters( |
| constructor: enclosingConstructor, |
| hasExplicitPositionalArguments: positionalArgumentCount != 0, |
| errorReporter: errorReporter, |
| ); |
| positionalArgumentCount += result.positionalArgumentCount; |
| if (result.namedArgumentNames.isNotEmpty) { |
| usedNames = result.namedArgumentNames.toSet(); |
| } |
| } |
| |
| for (int i = 0; i < argumentCount; i++) { |
| Expression argument = arguments[i]; |
| if (argument is NamedExpressionImpl) { |
| var nameNode = argument.name.label; |
| String name = nameNode.name; |
| var element = namedParameters != null ? namedParameters[name] : null; |
| if (element == null) { |
| errorReporter?.atNode( |
| nameNode, |
| CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, |
| arguments: [name], |
| ); |
| } else { |
| resolvedParameters[i] = element; |
| nameNode.element = element; |
| } |
| usedNames ??= <String>{}; |
| if (!usedNames.add(name)) { |
| errorReporter?.atNode( |
| nameNode, |
| CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, |
| arguments: [name], |
| ); |
| } |
| } |
| } |
| |
| if (positionalArgumentCount < requiredParameterCount && noBlankArguments) { |
| var parent = argumentList.parent; |
| if (errorReporter != null && parent != null) { |
| var token = |
| lastPositionalArgument?.endToken.next ?? |
| argumentList.leftParenthesis.next ?? |
| argumentList.rightParenthesis; |
| _reportNotEnoughPositionalArguments( |
| token: token, |
| requiredParameterCount: requiredParameterCount, |
| actualArgumentCount: positionalArgumentCount, |
| nameNode: parent, |
| errorReporter: errorReporter, |
| ); |
| } |
| } else if (positionalArgumentCount > unnamedParameterCount && |
| noBlankArguments) { |
| DiagnosticCode diagnosticCode; |
| int namedParameterCount = namedParameters?.length ?? 0; |
| int namedArgumentCount = usedNames?.length ?? 0; |
| if (namedParameterCount > namedArgumentCount) { |
| diagnosticCode = |
| CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED; |
| } else { |
| diagnosticCode = CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS; |
| } |
| if (firstUnresolvedArgument != null) { |
| errorReporter?.atNode( |
| firstUnresolvedArgument, |
| diagnosticCode, |
| arguments: [unnamedParameterCount, positionalArgumentCount], |
| ); |
| } |
| } |
| return resolvedParameters; |
| } |
| |
| /// Debug-only: verifies that [list] is a modifiable list by setting its |
| /// length to itself. |
| /// |
| /// For a normal list this is a no-op; for an unmodifiable (i.e. const) list, |
| /// this will cause an exception to be thrown. |
| static bool _isModifiableList(List<Object?> list) { |
| try { |
| list.length = list.length; |
| } catch (_) { |
| return false; |
| } |
| return true; |
| } |
| |
| /// Reports [CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_SINGULAR] or |
| /// [CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_PLURAL] at the |
| /// specified [token], considering the name of the [nameNode]. |
| static void _reportNotEnoughPositionalArguments({ |
| required Token token, |
| required int requiredParameterCount, |
| required int actualArgumentCount, |
| required AstNode nameNode, |
| required ErrorReporter errorReporter, |
| }) { |
| String? name; |
| if (nameNode is InstanceCreationExpression) { |
| var constructorName = nameNode.constructorName; |
| name = |
| constructorName.name?.name ?? |
| '${constructorName.type.name.lexeme}.new'; |
| } else if (nameNode is RedirectingConstructorInvocation) { |
| name = nameNode.constructorName?.name; |
| if (name == null) { |
| var element = nameNode.element; |
| if (element != null) { |
| name = '${element.returnType.getDisplayString()}.new'; |
| } |
| } |
| } else if (nameNode is SuperConstructorInvocation) { |
| name = nameNode.constructorName?.name; |
| if (name == null) { |
| var element = nameNode.element; |
| if (element != null) { |
| name = '${element.returnType.getDisplayString()}.new'; |
| } |
| } |
| } else if (nameNode is MethodInvocation) { |
| name = nameNode.methodName.name; |
| } else if (nameNode is FunctionExpressionInvocation) { |
| var function = nameNode.function; |
| if (function is SimpleIdentifier) { |
| name = function.name; |
| } |
| } else if (nameNode is EnumConstantArguments) { |
| var parent = nameNode.parent; |
| if (parent is EnumConstantDeclaration) { |
| var declaredElement = parent.declaredFragment!.element; |
| name = declaredElement.type.getDisplayString(); |
| } |
| } else if (nameNode is EnumConstantDeclaration) { |
| var declaredElement = nameNode.declaredFragment!.element; |
| name = declaredElement.type.getDisplayString(); |
| } else if (nameNode is Annotation) { |
| var nameNodeName = nameNode.name; |
| name = |
| nameNodeName is PrefixedIdentifier |
| ? nameNodeName.identifier.name |
| : '${nameNodeName.name}.new'; |
| } else if (nameNode is DotShorthandConstructorInvocation) { |
| name = nameNode.constructorName.name; |
| } else if (nameNode is DotShorthandInvocation) { |
| name = nameNode.memberName.name; |
| } else { |
| throw UnimplementedError('(${nameNode.runtimeType}) $nameNode'); |
| } |
| |
| var isPlural = requiredParameterCount > 1; |
| var arguments = <Object>[]; |
| if (isPlural) { |
| arguments.add(requiredParameterCount); |
| arguments.add(actualArgumentCount); |
| } |
| DiagnosticCode diagnosticCode; |
| if (name == null) { |
| diagnosticCode = |
| isPlural |
| ? CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_PLURAL |
| : CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_SINGULAR; |
| } else { |
| diagnosticCode = |
| isPlural |
| ? CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS_NAME_PLURAL |
| : CompileTimeErrorCode |
| .NOT_ENOUGH_POSITIONAL_ARGUMENTS_NAME_SINGULAR; |
| arguments.add(name); |
| } |
| errorReporter.atToken(token, diagnosticCode, arguments: arguments); |
| } |
| } |
| |
| /// Instances of the class `ScopeResolverVisitor` are used to resolve |
| /// [SimpleIdentifier]s to declarations using scoping rules. |
| /// |
| // TODO(paulberry): migrate the responsibility for all scope resolution into |
| // this visitor. |
| class ScopeResolverVisitor extends UnifyingAstVisitor<void> { |
| /// The error reporter that will be informed of any errors that are found |
| /// during resolution. |
| final ErrorReporter errorReporter; |
| |
| /// The scope used to resolve identifiers. |
| Scope nameScope; |
| |
| /// The scope of libraries imported by `@docImport`s. |
| final DocumentationCommentScope _docImportScope; |
| |
| /// The scope used to resolve unlabeled `break` and `continue` statements. |
| ImplicitLabelScope _implicitLabelScope = ImplicitLabelScope.ROOT; |
| |
| /// The scope used to resolve labels for `break` and `continue` statements, or |
| /// `null` if no labels have been defined in the current context. |
| LabelScope? _labelScope; |
| |
| /// The container with information about local variables. |
| final LocalVariableInfo _localVariableInfo = LocalVariableInfo(); |
| |
| /// If the current function is contained within a closure (a local function or |
| /// function expression inside another executable declaration), the element |
| /// representing the closure; otherwise `null`. |
| LocalFunctionElement? _enclosingClosure; |
| |
| /// Initialize a newly created visitor to resolve the nodes in an AST node. |
| /// |
| /// [errorReporter] is the error reporter that will be informed of any errors |
| /// that are found during resolution. |
| /// [nameScope] is the scope used to resolve identifiers in the node that will |
| /// first be visited. |
| ScopeResolverVisitor( |
| this.errorReporter, { |
| required this.nameScope, |
| List<LibraryElement> docImportLibraries = const [], |
| }) : _docImportScope = DocumentationCommentScope( |
| nameScope, |
| docImportLibraries, |
| ); |
| |
| /// Return the implicit label scope in which the current node is being |
| /// resolved. |
| ImplicitLabelScope get implicitLabelScope => _implicitLabelScope; |
| |
| @override |
| void visitAssignedVariablePattern(AssignedVariablePattern node) { |
| var element = node.element2; |
| if (element is PromotableElement) { |
| _localVariableInfo.potentiallyMutatedInScope.add(element); |
| } |
| } |
| |
| @override |
| void visitBlock(covariant BlockImpl node) { |
| _withDeclaredLocals(node, node.statements, () { |
| super.visitBlock(node); |
| }); |
| } |
| |
| @override |
| void visitBlockFunctionBody(BlockFunctionBody node) { |
| ImplicitLabelScope implicitOuterScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = ImplicitLabelScope.ROOT; |
| super.visitBlockFunctionBody(node); |
| } finally { |
| _implicitLabelScope = implicitOuterScope; |
| } |
| } |
| |
| @override |
| void visitBreakStatement(covariant BreakStatementImpl node) { |
| node.target = _lookupBreakOrContinueTarget(node, node.label, false); |
| } |
| |
| @override |
| void visitCatchClause(CatchClause node) { |
| var exception = node.exceptionParameter; |
| if (exception != null) { |
| Scope outerScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| _define(exception.declaredElement2!); |
| var stackTrace = node.stackTraceParameter; |
| if (stackTrace != null) { |
| _define(stackTrace.declaredElement2!); |
| } |
| super.visitCatchClause(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } else { |
| super.visitCatchClause(node); |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(covariant ClassDeclarationImpl node) { |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.extendsClause?.accept(this); |
| node.withClause?.accept(this); |
| node.implementsClause?.accept(this); |
| node.nativeClause?.accept(this); |
| |
| nameScope = InstanceScope(nameScope, element); |
| _visitDocumentationComment(node.documentationComment); |
| node.members.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitClassTypeAlias(covariant ClassTypeAliasImpl node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = InstanceScope( |
| TypeParameterScope(nameScope, element.typeParameters2), |
| element, |
| ); |
| _visitDocumentationComment(node.documentationComment); |
| node.typeParameters?.accept(this); |
| node.superclass.accept(this); |
| node.withClause.accept(this); |
| node.implementsClause?.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitCompilationUnit(covariant CompilationUnitImpl node) { |
| node.nameScope = nameScope; |
| super.visitCompilationUnit(node); |
| } |
| |
| @override |
| void visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) { |
| node.body.localVariableInfo = _localVariableInfo; |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| |
| node.metadata.accept(this); |
| node.returnType.accept(this); |
| node.parameters.accept(this); |
| |
| try { |
| nameScope = ConstructorInitializerScope(nameScope, element); |
| node.initializers.accept(this); |
| _visitDocumentationComment(node.documentationComment); |
| } finally { |
| nameScope = outerScope; |
| } |
| |
| node.redirectedConstructor?.accept(this); |
| |
| nameScope = FormalParameterScope(nameScope, element.formalParameters); |
| node.body.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitContinueStatement(covariant ContinueStatementImpl node) { |
| node.target = _lookupBreakOrContinueTarget(node, node.label, true); |
| } |
| |
| @override |
| void visitDeclaredIdentifier(DeclaredIdentifier node) { |
| _define(node.declaredFragment!.element); |
| super.visitDeclaredIdentifier(node); |
| } |
| |
| @override |
| void visitDoStatement(DoStatement node) { |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| _visitStatementInScope(node.body); |
| node.condition.accept(this); |
| } finally { |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| @override |
| void visitEnumConstantDeclaration( |
| covariant EnumConstantDeclarationImpl node, |
| ) { |
| node.metadata.accept(this); |
| _visitDocumentationComment(node.documentationComment); |
| node.arguments?.accept(this); |
| } |
| |
| @override |
| void visitEnumDeclaration(covariant EnumDeclarationImpl node) { |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.withClause?.accept(this); |
| node.implementsClause?.accept(this); |
| |
| nameScope = InstanceScope(nameScope, element); |
| _visitDocumentationComment(node.documentationComment); |
| node.constants.accept(this); |
| node.members.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitExpressionFunctionBody(covariant ExpressionFunctionBodyImpl node) { |
| node.nameScope = nameScope; |
| super.visitExpressionFunctionBody(node); |
| } |
| |
| @override |
| void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) { |
| var outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.onClause?.accept(this); |
| |
| nameScope = ExtensionScope(nameScope, element); |
| _visitDocumentationComment(node.documentationComment); |
| node.members.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitExtensionTypeDeclaration( |
| covariant ExtensionTypeDeclarationImpl node, |
| ) { |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.representation.accept(this); |
| node.implementsClause?.accept(this); |
| |
| nameScope = InstanceScope(nameScope, element); |
| _visitDocumentationComment(node.documentationComment); |
| node.members.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitFieldDeclaration(covariant FieldDeclarationImpl node) { |
| node.metadata.accept(this); |
| _visitDocumentationComment(node.documentationComment); |
| node.fields.accept(this); |
| } |
| |
| @override |
| void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) { |
| // We visit the iterator before the loop variable because the loop variable |
| // cannot be in scope while visiting the iterator. |
| node.iterable.accept(this); |
| node.loopVariable.accept(this); |
| } |
| |
| @override |
| void visitForEachPartsWithPattern( |
| covariant ForEachPartsWithPatternImpl node, |
| ) { |
| // We visit the iterator before the pattern because the pattern variables |
| // cannot be in scope while visiting the iterator. |
| node.iterable.accept(this); |
| |
| for (var variable in node.variables) { |
| _define(variable.asElement2); |
| } |
| |
| node.pattern.accept(this); |
| } |
| |
| @override |
| void visitForElement(covariant ForElementImpl node) { |
| Scope outerNameScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| node.nameScope = nameScope; |
| node.forLoopParts.accept(this); |
| node.body.accept(this); |
| } finally { |
| nameScope = outerNameScope; |
| } |
| } |
| |
| @override |
| void visitFormalParameterList(FormalParameterList node) { |
| super.visitFormalParameterList(node); |
| // We finished resolving function signature, now include formal parameters |
| // scope. Note: we must not do this if the parent is a |
| // FunctionTypedFormalParameter, because in that case we aren't finished |
| // resolving the full function signature, just a part of it. |
| var parent = node.parent; |
| if (parent is FunctionExpression) { |
| var element = parent.declaredFragment!.element; |
| nameScope = FormalParameterScope(nameScope, element.formalParameters); |
| } else if (parent is FunctionTypeAlias) { |
| var aliasedElement = parent.declaredFragment!.element.aliasedElement2; |
| var functionElement = aliasedElement as GenericFunctionTypeElement; |
| nameScope = FormalParameterScope( |
| nameScope, |
| functionElement.formalParameters, |
| ); |
| } else if (parent is MethodDeclaration) { |
| var element = parent.declaredFragment!.element; |
| nameScope = FormalParameterScope(nameScope, element.formalParameters); |
| } |
| } |
| |
| @override |
| void visitForStatement(covariant ForStatementImpl node) { |
| Scope outerNameScope = nameScope; |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| node.nameScope = nameScope; |
| node.forLoopParts.accept(this); |
| _visitStatementInScope(node.body); |
| } finally { |
| nameScope = outerNameScope; |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| @override |
| void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) { |
| node.functionExpression.body.localVariableInfo = _localVariableInfo; |
| var outerClosure = _enclosingClosure; |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| _enclosingClosure = element.ifTypeOrNull<LocalFunctionElement>(); |
| node.metadata.accept(this); |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.returnType?.accept(this); |
| node.functionExpression.accept(this); |
| } finally { |
| nameScope = outerScope; |
| _enclosingClosure = outerClosure; |
| } |
| } |
| |
| @override |
| void visitFunctionExpression(FunctionExpression node) { |
| var outerClosure = _enclosingClosure; |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| if (node.parent is! FunctionDeclaration) { |
| (node.body as FunctionBodyImpl).localVariableInfo = _localVariableInfo; |
| _enclosingClosure = element as LocalFunctionElement; |
| } |
| var parent = node.parent; |
| if (parent is FunctionDeclarationImpl) { |
| // We have already created a function scope and don't need to do so again. |
| super.visitFunctionExpression(node); |
| _visitDocumentationComment(parent.documentationComment); |
| return; |
| } |
| |
| nameScope = FormalParameterScope( |
| TypeParameterScope(nameScope, element.typeParameters2), |
| element.formalParameters, |
| ); |
| super.visitFunctionExpression(node); |
| } finally { |
| nameScope = outerScope; |
| _enclosingClosure = outerClosure; |
| } |
| } |
| |
| @override |
| void visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.returnType?.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters.accept(this); |
| // Visiting the parameters added them to the scope as a side effect. So it |
| // is safe to visit the documentation comment now. |
| _visitDocumentationComment(node.documentationComment); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter( |
| covariant FunctionTypedFormalParameterImpl node, |
| ) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| _visitDocumentationComment(node.documentationComment); |
| node.returnType?.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitGenericFunctionType(covariant GenericFunctionTypeImpl node) { |
| var type = node.type; |
| if (type == null) { |
| // The function type hasn't been resolved yet, so we can't create a scope |
| // for its parameters. |
| super.visitGenericFunctionType(node); |
| return; |
| } |
| |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| super.visitGenericFunctionType(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(covariant GenericTypeAliasImpl node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.type.accept(this); |
| |
| var aliasedElement = element.aliasedElement2; |
| if (aliasedElement is GenericFunctionTypeElement) { |
| nameScope = FormalParameterScope( |
| TypeParameterScope(nameScope, aliasedElement.typeParameters2), |
| aliasedElement.formalParameters, |
| ); |
| } |
| _visitDocumentationComment(node.documentationComment); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitGuardedPattern(covariant GuardedPatternImpl node) { |
| var patternVariables = node.variables.values.toList(); |
| for (var variable in patternVariables) { |
| _define(variable); |
| } |
| |
| node.pattern.accept(this); |
| |
| for (var variable in patternVariables) { |
| variable.isVisitingWhenClause = true; |
| } |
| |
| node.whenClause?.accept(this); |
| |
| for (var variable in patternVariables) { |
| variable.isVisitingWhenClause = false; |
| } |
| } |
| |
| @override |
| void visitHideCombinator(HideCombinator node) { |
| var scope = nameScope.ifTypeOrNull<LibraryFragmentScope>(); |
| scope?.importsTrackingActive(false); |
| try { |
| super.visitHideCombinator(node); |
| } finally { |
| scope?.importsTrackingActive(true); |
| } |
| } |
| |
| @override |
| void visitIfElement(covariant IfElementImpl node) { |
| _visitIf(node); |
| } |
| |
| @override |
| void visitIfStatement(covariant IfStatementImpl node) { |
| _visitIf(node); |
| } |
| |
| @override |
| void visitLabeledStatement(LabeledStatement node) { |
| var outerScope = _addScopesFor(node.labels, node.unlabeled); |
| try { |
| super.visitLabeledStatement(node); |
| } finally { |
| _labelScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitLibraryDirective(covariant LibraryDirectiveImpl node) { |
| node.metadata.accept(this); |
| _visitDocumentationComment(node.documentationComment); |
| } |
| |
| @override |
| void visitLibraryIdentifier(LibraryIdentifier node) {} |
| |
| @override |
| void visitMethodDeclaration(covariant MethodDeclarationImpl node) { |
| node.body.localVariableInfo = _localVariableInfo; |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.returnType?.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters?.accept(this); |
| // Visiting the parameters added them to the scope as a side effect. So it |
| // is safe to visit the documentation comment now. |
| _visitDocumentationComment(node.documentationComment); |
| node.body.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitMethodInvocation(MethodInvocation node) { |
| // Only visit the method name if there's no real target (so this is an |
| // unprefixed function invocation, outside a cascade). This is the only |
| // circumstance in which the method name is meant to be looked up in the |
| // current scope. |
| node.target?.accept(this); |
| if (node.realTarget == null) { |
| node.methodName.accept(this); |
| } |
| node.typeArguments?.accept(this); |
| node.argumentList.accept(this); |
| } |
| |
| @override |
| void visitMixinDeclaration(covariant MixinDeclarationImpl node) { |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredFragment!.element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters2); |
| node.nameScope = nameScope; |
| node.typeParameters?.accept(this); |
| node.onClause?.accept(this); |
| node.implementsClause?.accept(this); |
| |
| nameScope = InstanceScope(nameScope, element); |
| _visitDocumentationComment(node.documentationComment); |
| node.members.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitNamedType(NamedType node) { |
| // All TypeName(s) are already resolved, so we don't resolve it here. |
| // But there might be type arguments with Expression(s), such as |
| // annotations on formal parameters of GenericFunctionType(s). |
| node.typeArguments?.accept(this); |
| } |
| |
| @override |
| void visitPatternVariableDeclaration( |
| covariant PatternVariableDeclarationImpl node, |
| ) { |
| for (var variable in node.elements) { |
| _define(variable); |
| } |
| |
| super.visitPatternVariableDeclaration(node); |
| } |
| |
| @override |
| void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| // Do not visit the identifier after the `.`, since it is not meant to be |
| // looked up in the current scope. |
| node.prefix.accept(this); |
| } |
| |
| @override |
| void visitPropertyAccess(PropertyAccess node) { |
| // Do not visit the property name, since it is not meant to be looked up in |
| // the current scope. |
| node.target?.accept(this); |
| } |
| |
| @override |
| void visitShowCombinator(ShowCombinator node) { |
| var scope = nameScope.ifTypeOrNull<LibraryFragmentScope>(); |
| scope?.importsTrackingActive(false); |
| try { |
| super.visitShowCombinator(node); |
| } finally { |
| scope?.importsTrackingActive(true); |
| } |
| } |
| |
| @override |
| void visitSimpleIdentifier(covariant SimpleIdentifierImpl node) { |
| // Ignore if already resolved - declaration or type. |
| if (node.inDeclarationContext()) { |
| return; |
| } |
| // Ignore if qualified. |
| var parent = node.parent; |
| if (parent is ConstructorName && parent.name == node) { |
| return; |
| } |
| if (parent is Label && parent.parent is NamedExpression) { |
| return; |
| } |
| var scopeLookupResult = nameScope.lookup(node.name); |
| node.scopeLookupResult = scopeLookupResult; |
| // Ignore if it cannot be a reference to a local variable. |
| if (parent is FieldFormalParameter) { |
| return; |
| } else if (parent is ConstructorDeclaration && parent.returnType == node) { |
| return; |
| } else if (parent is ConstructorFieldInitializer && |
| parent.fieldName == node) { |
| return; |
| } |
| if (parent is Label) { |
| return; |
| } |
| // Prepare VariableElement. |
| var element = scopeLookupResult.getter2; |
| if (element is! VariableElement) { |
| return; |
| } |
| // Must be local or parameter. |
| ElementKind kind = element.kind; |
| if (kind == ElementKind.LOCAL_VARIABLE || kind == ElementKind.PARAMETER) { |
| node.element = element; |
| if (node.inSetterContext()) { |
| if (element is PatternVariableElementImpl2 && |
| element.isVisitingWhenClause) { |
| errorReporter.atNode( |
| node, |
| CompileTimeErrorCode.PATTERN_VARIABLE_ASSIGNMENT_INSIDE_GUARD, |
| ); |
| } |
| _localVariableInfo.potentiallyMutatedInScope.add(element); |
| } |
| } |
| if (element is JoinPatternVariableElementImpl2) { |
| element.references.add(node); |
| } |
| } |
| |
| @override |
| void visitSwitchExpression(covariant SwitchExpressionImpl node) { |
| node.expression.accept(this); |
| |
| for (var case_ in node.cases) { |
| _withNameScope(() { |
| case_.nameScope = nameScope; |
| var guardedPattern = case_.guardedPattern; |
| var variables = guardedPattern.variables; |
| for (var variable in variables.values) { |
| _define(variable); |
| } |
| case_.accept(this); |
| }); |
| } |
| } |
| |
| @override |
| void visitSwitchStatement(covariant SwitchStatementImpl node) { |
| var outerScope = _labelScope; |
| var outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| for (var member in node.members) { |
| for (var label in member.labels) { |
| var labelName = label.label; |
| var labelElement = labelName.element as LabelElement; |
| _labelScope = LabelScope( |
| _labelScope, |
| labelName.name, |
| member, |
| labelElement, |
| ); |
| } |
| } |
| node.expression.accept(this); |
| for (var group in node.memberGroups) { |
| for (var member in group.members) { |
| if (member is SwitchCaseImpl) { |
| member.expression.accept(this); |
| } else if (member is SwitchPatternCaseImpl) { |
| _withNameScope(() { |
| member.guardedPattern.accept(this); |
| }); |
| } |
| } |
| if (group.members.isEmpty) { |
| return; |
| } |
| var lastMember = group.members.last; |
| _withDeclaredLocals(lastMember, lastMember.statements, () { |
| for (var variable in group.variables.values) { |
| _define(variable); |
| } |
| lastMember.statements.accept(this); |
| }); |
| } |
| } finally { |
| _labelScope = outerScope; |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| @override |
| void visitTopLevelVariableDeclaration( |
| covariant TopLevelVariableDeclarationImpl node, |
| ) { |
| node.metadata.accept(this); |
| _visitDocumentationComment(node.documentationComment); |
| node.variables.accept(this); |
| } |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| super.visitVariableDeclaration(node); |
| |
| if (node.parent!.parent is ForParts) { |
| _define(node.declaredElement2!); |
| } |
| } |
| |
| @override |
| void visitWhileStatement(WhileStatement node) { |
| node.condition.accept(this); |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| _visitStatementInScope(node.body); |
| } finally { |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| /// Adds scopes for each of the given [labels]. |
| /// |
| /// Returns the scope that was in effect before the new scopes were added. |
| LabelScope? _addScopesFor(NodeList<Label> labels, AstNode node) { |
| var outerScope = _labelScope; |
| for (var label in labels) { |
| var labelNameNode = label.label; |
| var labelName = labelNameNode.name; |
| var labelElement = labelNameNode.element as LabelElement; |
| _labelScope = LabelScope(_labelScope, labelName, node, labelElement); |
| } |
| return outerScope; |
| } |
| |
| void _define(Element element) { |
| (nameScope as LocalScope).add(element); |
| } |
| |
| /// Return the target of a break or continue statement, and update the static |
| /// element of its label (if any). The [parentNode] is the AST node of the |
| /// break or continue statement. The [labelNode] is the label contained in |
| /// that statement (if any). The flag [isContinue] is `true` if the node being |
| /// visited is a continue statement. |
| AstNode? _lookupBreakOrContinueTarget( |
| AstNode parentNode, |
| SimpleIdentifierImpl? labelNode, |
| bool isContinue, |
| ) { |
| if (labelNode == null) { |
| return implicitLabelScope.getTarget(isContinue); |
| } else { |
| var labelScope = _labelScope; |
| if (labelScope == null) { |
| // There are no labels in scope, so by definition the label is |
| // undefined. |
| errorReporter.atNode( |
| labelNode, |
| CompileTimeErrorCode.LABEL_UNDEFINED, |
| arguments: [labelNode.name], |
| ); |
| return null; |
| } |
| var definingScope = labelScope.lookup(labelNode.name); |
| if (definingScope == null) { |
| // No definition of the given label name could be found in any |
| // enclosing scope. |
| errorReporter.atNode( |
| labelNode, |
| CompileTimeErrorCode.LABEL_UNDEFINED, |
| arguments: [labelNode.name], |
| ); |
| return null; |
| } |
| // The target has been found. |
| labelNode.element = definingScope.element; |
| if (_enclosingClosure case var enclosingClosure?) { |
| var labelFragment = definingScope.element.firstFragment; |
| var labelContainer = labelFragment.enclosingFragment; |
| if (!identical(labelContainer, enclosingClosure.firstFragment)) { |
| errorReporter.atNode( |
| labelNode, |
| CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE, |
| arguments: [labelNode.name], |
| ); |
| } |
| } |
| var node = definingScope.node; |
| if (isContinue && |
| node is! DoStatement && |
| node is! ForStatement && |
| node is! SwitchMember && |
| node is! WhileStatement) { |
| errorReporter.atNode( |
| parentNode, |
| CompileTimeErrorCode.CONTINUE_LABEL_INVALID, |
| ); |
| } |
| return node; |
| } |
| } |
| |
| /// Visits a documentation comment with a [DocumentationCommentScope] that encloses the |
| /// current [nameScope]. |
| void _visitDocumentationComment(CommentImpl? node) { |
| if (node == null) return; |
| |
| Scope outerScope = nameScope; |
| Scope docImportInnerScope = _docImportScope.innerScope; |
| try { |
| _docImportScope.innerScope = nameScope; |
| nameScope = _docImportScope; |
| |
| node.nameScope = nameScope; |
| node.accept(this); |
| } finally { |
| nameScope = outerScope; |
| _docImportScope.innerScope = docImportInnerScope; |
| } |
| } |
| |
| void _visitIf(IfElementOrStatementImpl node) { |
| node.expression.accept(this); |
| |
| var caseClause = node.caseClause; |
| if (caseClause != null) { |
| var guardedPattern = caseClause.guardedPattern; |
| _withNameScope(() { |
| caseClause.nameScope = nameScope; |
| var variables = guardedPattern.variables; |
| for (var variable in variables.values) { |
| _define(variable); |
| } |
| guardedPattern.accept(this); |
| node.ifTrue.accept(this); |
| }); |
| node.ifFalse?.accept(this); |
| } else { |
| node.ifTrue.accept(this); |
| node.ifFalse?.accept(this); |
| } |
| } |
| |
| /// Visits the given statement. |
| /// |
| /// This is used by [ResolverVisitor] to correctly visit the 'then' and 'else' |
| /// statements of an 'if' statement. |
| void _visitStatementInScope(Statement? node) { |
| if (node is BlockImpl) { |
| // Don't create a scope around a block because the block will create it's |
| // own scope. |
| visitBlock(node); |
| } else if (node != null) { |
| var outerNameScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| node.accept(this); |
| } finally { |
| nameScope = outerNameScope; |
| } |
| } |
| } |
| |
| void _withDeclaredLocals( |
| AstNodeWithNameScopeMixin node, |
| List<Statement> statements, |
| void Function() f, |
| ) { |
| var outerScope = nameScope; |
| try { |
| var enclosedScope = LocalScope(nameScope); |
| for (var statement in BlockScope.elementsInStatements(statements)) { |
| if (!statement.isWildcardFunction) { |
| enclosedScope.add(statement); |
| } |
| } |
| |
| nameScope = enclosedScope; |
| node.nameScope = nameScope; |
| |
| f(); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| /// Run [f] with the new name scope. |
| void _withNameScope(void Function() f) { |
| var current = nameScope; |
| try { |
| nameScope = LocalScope(current); |
| f(); |
| } finally { |
| nameScope = current; |
| } |
| } |
| |
| /// Return the [Scope] to use while resolving inside the [node]. |
| /// |
| /// Not every node has the scope set, for example we set the scopes for |
| /// blocks, but statements don't have separate scopes. The compilation unit |
| /// has the library scope. |
| static Scope? getNodeNameScope(AstNode node) => |
| node is AstNodeWithNameScopeMixin ? node.nameScope : null; |
| } |
| |
| /// Tracker for whether a `switch` statement has `default` or is on an |
| /// enumeration, and all the enum constants are covered. |
| class SwitchExhaustiveness { |
| /// If the switch is on an enumeration, the set of enum constants to cover. |
| /// Otherwise `null`. |
| final Set<FieldElement>? _enumConstants; |
| |
| /// If the switch is on an enumeration, is `true` if the null value is |
| /// covered, because the switch expression type is non-nullable, or `null` |
| /// was covered explicitly. |
| bool _isNullEnumValueCovered = false; |
| |
| bool isExhaustive = false; |
| |
| factory SwitchExhaustiveness(TypeImpl expressionType) { |
| if (expressionType is InterfaceType) { |
| var enum_ = expressionType.element3; |
| if (enum_ is EnumElementImpl2) { |
| return SwitchExhaustiveness._( |
| enum_.constants2.toSet(), |
| expressionType.nullabilitySuffix == NullabilitySuffix.none, |
| ); |
| } |
| } |
| return SwitchExhaustiveness._(null, false); |
| } |
| |
| SwitchExhaustiveness._(this._enumConstants, this._isNullEnumValueCovered); |
| |
| void visitSwitchExpressionCase(SwitchExpressionCaseImpl node) { |
| if (_enumConstants != null) { |
| ExpressionImpl? caseConstant; |
| var guardedPattern = node.guardedPattern; |
| if (guardedPattern.whenClause == null) { |
| var pattern = guardedPattern.pattern.unParenthesized; |
| if (pattern is ConstantPatternImpl) { |
| caseConstant = pattern.expression; |
| } |
| } |
| _handleCaseConstant(caseConstant); |
| } |
| } |
| |
| void visitSwitchMember(SwitchStatementCaseGroup group) { |
| for (var node in group.members) { |
| if (_enumConstants != null) { |
| ExpressionImpl? caseConstant; |
| if (node is SwitchCaseImpl) { |
| caseConstant = node.expression; |
| } else if (node is SwitchPatternCaseImpl) { |
| var guardedPattern = node.guardedPattern; |
| if (guardedPattern.whenClause == null) { |
| var pattern = guardedPattern.pattern.unParenthesized; |
| if (pattern is ConstantPatternImpl) { |
| caseConstant = pattern.expression; |
| } |
| } |
| } |
| _handleCaseConstant(caseConstant); |
| } else if (node is SwitchDefault) { |
| isExhaustive = true; |
| } |
| } |
| } |
| |
| void _handleCaseConstant(ExpressionImpl? caseConstant) { |
| if (caseConstant != null) { |
| var element = _referencedElement(caseConstant); |
| if (element is PropertyAccessorElement) { |
| _enumConstants!.remove(element.variable3); |
| } |
| if (caseConstant is NullLiteral) { |
| _isNullEnumValueCovered = true; |
| } |
| if (_enumConstants!.isEmpty && _isNullEnumValueCovered) { |
| isExhaustive = true; |
| } |
| } |
| } |
| |
| static Element? _referencedElement(Expression expression) { |
| if (expression is ParenthesizedExpression) { |
| return _referencedElement(expression.expression); |
| } else if (expression is PrefixedIdentifier) { |
| return expression.element; |
| } else if (expression is PropertyAccess) { |
| return expression.propertyName.element; |
| } else if (expression is SimpleIdentifier) { |
| return expression.element; |
| } |
| return null; |
| } |
| } |
| |
| class _WhyNotPromotedVisitor |
| implements |
| NonPromotionReasonVisitor< |
| List<DiagnosticMessage>, |
| AstNode, |
| PromotableElement |
| > { |
| final Source source; |
| |
| final SyntacticEntity _errorEntity; |
| |
| final FlowAnalysisDataForTesting? _dataForTesting; |
| |
| PropertyAccessorElement? propertyReference; |
| |
| _WhyNotPromotedVisitor(this.source, this._errorEntity, this._dataForTesting); |
| |
| @override |
| List<DiagnosticMessage> visitDemoteViaExplicitWrite( |
| DemoteViaExplicitWrite<PromotableElement> reason, |
| ) { |
| var node = reason.node as AstNode; |
| if (node is ForEachPartsWithIdentifier) { |
| node = node.identifier; |
| } |
| if (_dataForTesting != null) { |
| _dataForTesting.nonPromotionReasonTargets[node] = reason.shortName; |
| } |
| var variableName = reason.variable.name3; |
| return [_contextMessageForWrite(variableName, node, reason)]; |
| } |
| |
| @override |
| List<DiagnosticMessage> visitPropertyNotPromotedForInherentReason( |
| PropertyNotPromotedForInherentReason reason, |
| ) { |
| var receiverElement = reason.propertyMember; |
| if (receiverElement is PropertyAccessorElement) { |
| var property = propertyReference = receiverElement; |
| var propertyName = reason.propertyName; |
| String message = switch (reason.whyNotPromotable) { |
| shared.PropertyNonPromotabilityReason.isNotField => |
| "'$propertyName' refers to a getter so it couldn't be promoted.", |
| shared.PropertyNonPromotabilityReason.isNotPrivate => |
| "'$propertyName' refers to a public property so it couldn't be " |
| "promoted.", |
| shared.PropertyNonPromotabilityReason.isExternal => |
| "'$propertyName' refers to an external field so it couldn't be " |
| "promoted.", |
| shared.PropertyNonPromotabilityReason.isNotFinal => |
| "'$propertyName' refers to a non-final field so it couldn't be " |
| "promoted.", |
| }; |
| return [ |
| DiagnosticMessageImpl( |
| filePath: property.firstFragment.libraryFragment.source.fullName, |
| message: message, |
| offset: property.nonSynthetic2.firstFragment.nameOffset2!, |
| length: property.name3!.length, |
| url: reason.documentationLink.url, |
| ), |
| if (!reason.fieldPromotionEnabled) |
| _fieldPromotionUnavailableMessage(property, propertyName), |
| ]; |
| } else { |
| assert( |
| receiverElement == null, |
| 'Unrecognized property element: ${receiverElement.runtimeType}', |
| ); |
| return []; |
| } |
| } |
| |
| @override |
| List<DiagnosticMessage> visitPropertyNotPromotedForNonInherentReason( |
| PropertyNotPromotedForNonInherentReason reason, |
| ) { |
| var receiverElement = reason.propertyMember; |
| if (receiverElement is PropertyAccessorElement) { |
| var property = propertyReference = receiverElement; |
| var propertyName = reason.propertyName; |
| var library = receiverElement.library2 as LibraryElementImpl; |
| var fieldNonPromotabilityInfo = library.fieldNameNonPromotabilityInfo; |
| var fieldNameInfo = fieldNonPromotabilityInfo[reason.propertyName]; |
| var messages = <DiagnosticMessage>[]; |
| void addConflictMessage({ |
| required Element conflictingElement, |
| required String kind, |
| required Element enclosingElement, |
| required NonPromotionDocumentationLink link, |
| }) { |
| var enclosingKindName = enclosingElement.kind.displayName; |
| var enclosingName = enclosingElement.name3; |
| var message = |
| "'$propertyName' couldn't be promoted because there is a " |
| "conflicting $kind in $enclosingKindName '$enclosingName'"; |
| var nonSyntheticElement = conflictingElement.nonSynthetic2; |
| var nonSyntheticFragment = nonSyntheticElement.firstFragment; |
| var source = nonSyntheticFragment.libraryFragment?.source; |
| messages.add( |
| DiagnosticMessageImpl( |
| filePath: source!.fullName, |
| message: message, |
| offset: nonSyntheticFragment.nameOffset2!, |
| length: nonSyntheticElement.name3!.length, |
| url: link.url, |
| ), |
| ); |
| } |
| |
| if (fieldNameInfo != null) { |
| for (var field in fieldNameInfo.conflictingFields) { |
| addConflictMessage( |
| conflictingElement: field, |
| kind: 'non-promotable field', |
| enclosingElement: field.enclosingElement, |
| link: NonPromotionDocumentationLink.conflictingNonPromotableField, |
| ); |
| } |
| for (var getter in fieldNameInfo.conflictingGetters) { |
| addConflictMessage( |
| conflictingElement: getter, |
| kind: 'getter', |
| enclosingElement: getter.enclosingElement, |
| link: NonPromotionDocumentationLink.conflictingGetter, |
| ); |
| } |
| for (var nsmClass in fieldNameInfo.conflictingNsmClasses) { |
| addConflictMessage( |
| conflictingElement: nsmClass, |
| kind: 'noSuchMethod forwarder', |
| enclosingElement: nsmClass, |
| link: |
| NonPromotionDocumentationLink.conflictingNoSuchMethodForwarder, |
| ); |
| } |
| } |
| if (reason.fieldPromotionEnabled) { |
| // The only possible non-inherent reasons for field promotion to fail |
| // are because of conflicts and because field promotion is disabled. So |
| // if field promotion is enabled, the loops above should have found a |
| // conflict. |
| assert(messages.isNotEmpty); |
| } else { |
| messages.add(_fieldPromotionUnavailableMessage(property, propertyName)); |
| } |
| return messages; |
| } else { |
| assert( |
| receiverElement == null, |
| 'Unrecognized property element: ${receiverElement.runtimeType}', |
| ); |
| return []; |
| } |
| } |
| |
| @override |
| List<DiagnosticMessage> visitThisNotPromoted(ThisNotPromoted reason) { |
| return [ |
| DiagnosticMessageImpl( |
| filePath: source.fullName, |
| message: "'this' can't be promoted", |
| offset: _errorEntity.offset, |
| length: _errorEntity.length, |
| url: reason.documentationLink.url, |
| ), |
| ]; |
| } |
| |
| DiagnosticMessageImpl _contextMessageForWrite( |
| String? variableName, |
| AstNode node, |
| DemoteViaExplicitWrite<PromotableElement> reason, |
| ) { |
| return DiagnosticMessageImpl( |
| filePath: source.fullName, |
| message: |
| "Variable '${variableName!}' could not be promoted due to an " |
| "assignment", |
| offset: node.offset, |
| length: node.length, |
| url: reason.documentationLink.url, |
| ); |
| } |
| |
| DiagnosticMessageImpl _fieldPromotionUnavailableMessage( |
| PropertyAccessorElement property, |
| String propertyName, |
| ) { |
| return DiagnosticMessageImpl( |
| filePath: property.firstFragment.libraryFragment.source.fullName, |
| message: |
| "'$propertyName' couldn't be promoted " |
| "because field promotion is only available in Dart 3.2 and " |
| "above.", |
| offset: property.nonSynthetic2.firstFragment.nameOffset2!, |
| length: property.name3!.length, |
| url: NonPromotionDocumentationLink.fieldPromotionUnavailable.url, |
| ); |
| } |
| } |
| |
| extension on Element { |
| bool get isWildcardFunction => |
| this is LocalFunctionElement && |
| name3 == '_' && |
| library2.hasWildcardVariablesFeatureEnabled; |
| } |