| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:collection'; |
| |
| import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/syntactic_entity.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/nullability_suffix.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/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/ast/extensions.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; |
| import 'package:analyzer/src/dart/element/member.dart' |
| show ConstructorMember, Member; |
| import 'package:analyzer/src/dart/element/nullability_eliminator.dart'; |
| import 'package:analyzer/src/dart/element/scope.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/dart/resolver/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/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/invocation_inference_helper.dart'; |
| import 'package:analyzer/src/dart/resolver/lexical_lookup.dart'; |
| import 'package:analyzer/src/dart/resolver/method_invocation_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/scope.dart'; |
| import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.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/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/nullable_dereference_verifier.dart'; |
| import 'package:analyzer/src/generated/element_resolver.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/error_detection_helpers.dart'; |
| import 'package:analyzer/src/generated/migratable_ast_info_provider.dart'; |
| import 'package:analyzer/src/generated/migration.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
| import 'package:analyzer/src/generated/this_access_tracker.dart'; |
| import 'package:analyzer/src/generated/variable_type_provider.dart'; |
| import 'package:analyzer/src/util/ast_data_extractor.dart'; |
| import 'package:meta/meta.dart'; |
| |
| /// A function which returns [NonPromotionReason]s that various types are not |
| /// promoted. |
| typedef WhyNotPromotedGetter = Map<DartType, NonPromotionReason> Function(); |
| |
| /// Maintains and manages contextual type information used for |
| /// inferring types. |
| class InferenceContext { |
| // TODO(leafp): Consider replacing these node properties with a |
| // hash table help in an instance of this class. |
| static const String _typeProperty = |
| 'analyzer.src.generated.InferenceContext.contextType'; |
| |
| final ResolverVisitor _resolver; |
| |
| /// The type system in use. |
| final TypeSystemImpl _typeSystem; |
| |
| /// The stack of contexts for nested function bodies. |
| final List<BodyInferenceContext> _bodyContexts = []; |
| |
| InferenceContext._(ResolverVisitor resolver) |
| : _resolver = resolver, |
| _typeSystem = resolver.typeSystem; |
| |
| BodyInferenceContext? get bodyContext { |
| if (_bodyContexts.isNotEmpty) { |
| return _bodyContexts.last; |
| } else { |
| return null; |
| } |
| } |
| |
| void popFunctionBodyContext(FunctionBody node) { |
| var context = _bodyContexts.removeLast(); |
| |
| var flow = _resolver.flowAnalysis?.flow; |
| |
| var resultType = context.computeInferredReturnType( |
| endOfBlockIsReachable: flow == null || flow.isReachable, |
| ); |
| |
| setType(node, resultType); |
| } |
| |
| void pushFunctionBodyContext(FunctionBody node) { |
| var imposedType = getContext(node); |
| _bodyContexts.add( |
| BodyInferenceContext( |
| typeSystem: _typeSystem, |
| node: node, |
| imposedType: imposedType, |
| ), |
| ); |
| } |
| |
| /// Clear the type information associated with [node]. |
| static void clearType(AstNode? node) { |
| node?.setProperty(_typeProperty, null); |
| } |
| |
| /// Look for contextual type information attached to [node], and returns |
| /// the type if found. |
| /// |
| /// The returned type may be partially or completely unknown, denoted with an |
| /// unknown type `_`, for example `List<_>` or `(_, int) -> void`. |
| /// You can use [TypeSystemImpl.upperBoundForType] or |
| /// [TypeSystemImpl.lowerBoundForType] if you would prefer a known type |
| /// that represents the bound of the context type. |
| static DartType? getContext(AstNode? node) => |
| node?.getProperty(_typeProperty); |
| |
| /// Attach contextual type information [type] to [node] for use during |
| /// inference. |
| static void setType(AstNode? node, DartType? type) { |
| if (type == null || type.isDynamic) { |
| clearType(node); |
| } else { |
| node?.setProperty(_typeProperty, type); |
| } |
| } |
| |
| /// Attach contextual type information [type] to [node] for use during |
| /// inference. |
| static void setTypeFromNode(AstNode innerNode, AstNode outerNode) { |
| setType(innerNode, getContext(outerNode)); |
| } |
| } |
| |
| /// Instances of the class `ResolverVisitor` are used to resolve the nodes |
| /// within a single compilation unit. |
| class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers { |
| /// The manager for the inheritance mappings. |
| final InheritanceManager3 inheritance; |
| |
| /// The feature set that is enabled for the current unit. |
| final FeatureSet _featureSet; |
| |
| final MigratableAstInfoProvider _migratableAstInfoProvider; |
| |
| final MigrationResolutionHooks? migrationResolutionHooks; |
| |
| /// Helper for checking expression that should have the `bool` type. |
| late final BoolExpressionVerifier boolExpressionVerifier; |
| |
| /// Helper for checking potentially nullable dereferences. |
| late final NullableDereferenceVerifier nullableDereferenceVerifier; |
| |
| /// Helper for extension method resolution. |
| late final ExtensionMemberResolver extensionResolver; |
| |
| /// Helper for resolving properties on types. |
| late final TypePropertyResolver typePropertyResolver; |
| |
| /// Helper for resolving [ListLiteral] and [SetOrMapLiteral]. |
| late final TypedLiteralResolver _typedLiteralResolver; |
| |
| late final AssignmentExpressionResolver _assignmentExpressionResolver; |
| late final BinaryExpressionResolver _binaryExpressionResolver; |
| 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; |
| |
| /// The element representing the function containing the current node, or |
| /// `null` if the current node is not contained in a function. |
| ExecutableElement? _enclosingFunction; |
| |
| /// The helper for tracking if the current location has access to `this`. |
| final ThisAccessTracker _thisAccessTracker = ThisAccessTracker.unit(); |
| |
| late final InferenceContext inferenceContext; |
| |
| /// If a class, or mixin, is being resolved, the type of the class. |
| /// Otherwise `null`. |
| DartType? _thisType; |
| |
| final FlowAnalysisHelper? flowAnalysis; |
| |
| /// A comment before a function should be resolved in the context of the |
| /// function. But when we incrementally resolve a comment, we don't want to |
| /// resolve the whole function. |
| /// |
| /// So, this flag is set to `true`, when just context of the function should |
| /// be built and the comment resolved. |
| bool resolveOnlyCommentInFunctionBody = false; |
| |
| /// The type of the expression of the immediately enclosing [SwitchStatement], |
| /// or `null` if not in a [SwitchStatement]. |
| DartType? _enclosingSwitchStatementExpressionType; |
| |
| /// 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]; |
| |
| /// During resolution we clone annotation ASTs into the element model. |
| /// But we should not do this during linking element models, moreover |
| /// currently by doing this we will lose elements for parameters of |
| /// generic function types. |
| /// TODO(scheglov) Stop cloning altogether. |
| bool shouldCloneAnnotations = true; |
| |
| late final FunctionReferenceResolver _functionReferenceResolver; |
| |
| /// 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. The [nameScope] is the scope used to resolve identifiers in |
| /// the node that will first be visited. If `null` or unspecified, a new |
| /// [LibraryScope] will be created based on [definingLibrary] and |
| /// [typeProvider]. |
| /// |
| /// TODO(paulberry): make [featureSet] a required parameter (this will be a |
| /// breaking change). |
| ResolverVisitor( |
| InheritanceManager3 inheritanceManager, |
| LibraryElement definingLibrary, |
| Source source, |
| TypeProvider typeProvider, |
| AnalysisErrorListener errorListener, |
| {FeatureSet? featureSet, |
| Scope? nameScope, |
| FlowAnalysisHelper? flowAnalysisHelper}) |
| : this._( |
| inheritanceManager, |
| definingLibrary, |
| source, |
| definingLibrary.typeSystem as TypeSystemImpl, |
| typeProvider, |
| errorListener, |
| featureSet ?? |
| definingLibrary.context.analysisOptions.contextFeatures, |
| nameScope, |
| flowAnalysisHelper, |
| const MigratableAstInfoProvider(), |
| null); |
| |
| ResolverVisitor._( |
| this.inheritance, |
| LibraryElement definingLibrary, |
| Source source, |
| this.typeSystem, |
| TypeProvider typeProvider, |
| AnalysisErrorListener errorListener, |
| FeatureSet featureSet, |
| Scope? nameScope, |
| this.flowAnalysis, |
| this._migratableAstInfoProvider, |
| MigrationResolutionHooks? migrationResolutionHooks) |
| : _featureSet = featureSet, |
| migrationResolutionHooks = migrationResolutionHooks, |
| super(definingLibrary, source, typeProvider as TypeProviderImpl, |
| errorListener, |
| nameScope: nameScope) { |
| var analysisOptions = |
| definingLibrary.context.analysisOptions as AnalysisOptionsImpl; |
| |
| nullableDereferenceVerifier = NullableDereferenceVerifier( |
| typeSystem: typeSystem, |
| errorReporter: errorReporter, |
| resolver: this, |
| ); |
| boolExpressionVerifier = BoolExpressionVerifier( |
| resolver: this, |
| errorReporter: errorReporter, |
| nullableDereferenceVerifier: nullableDereferenceVerifier, |
| ); |
| _typedLiteralResolver = TypedLiteralResolver( |
| this, _featureSet, typeSystem, typeProvider, |
| migratableAstInfoProvider: _migratableAstInfoProvider); |
| extensionResolver = ExtensionMemberResolver(this); |
| typePropertyResolver = TypePropertyResolver(this); |
| inferenceHelper = InvocationInferenceHelper( |
| resolver: this, |
| errorReporter: errorReporter, |
| typeSystem: typeSystem, |
| migrationResolutionHooks: migrationResolutionHooks, |
| ); |
| _assignmentExpressionResolver = AssignmentExpressionResolver( |
| resolver: this, |
| ); |
| _binaryExpressionResolver = BinaryExpressionResolver( |
| resolver: this, |
| ); |
| _functionExpressionInvocationResolver = |
| FunctionExpressionInvocationResolver( |
| resolver: this, |
| ); |
| _functionExpressionResolver = FunctionExpressionResolver( |
| resolver: this, |
| migrationResolutionHooks: migrationResolutionHooks, |
| ); |
| _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, |
| migratableAstInfoProvider: _migratableAstInfoProvider); |
| inferenceContext = InferenceContext._(this); |
| typeAnalyzer = StaticTypeAnalyzer(this, migrationResolutionHooks); |
| _functionReferenceResolver = |
| FunctionReferenceResolver(this, _isNonNullableByDefault); |
| } |
| |
| /// Return the element representing the function containing the current node, |
| /// or `null` if the current node is not contained in a function. |
| /// |
| /// @return the element representing the function containing the current node |
| ExecutableElement? get enclosingFunction => _enclosingFunction; |
| |
| /// Return the object providing promoted or declared types of variables. |
| LocalVariableTypeProvider get localVariableTypeProvider { |
| if (flowAnalysis != null) { |
| return flowAnalysis!.localVariableTypeProvider; |
| } else { |
| return const NonPromotingLocalVariableTypeProvider(); |
| } |
| } |
| |
| NullabilitySuffix get noneOrStarSuffix { |
| return _isNonNullableByDefault |
| ? NullabilitySuffix.none |
| : NullabilitySuffix.star; |
| } |
| |
| /// 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`. |
| DartType? get thisType { |
| return _thisType; |
| } |
| |
| /// Return `true` if NNBD is enabled for this compilation unit. |
| bool get _isNonNullableByDefault => |
| _featureSet.isEnabled(Feature.non_nullable); |
| |
| /// Verify that the arguments in the given [argumentList] can be assigned to |
| /// their corresponding parameters. |
| /// |
| /// This method corresponds to |
| /// [BestPracticesVerifier.checkForArgumentTypesNotAssignableInList]. |
| /// |
| /// See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| void checkForArgumentTypesNotAssignableInList(ArgumentList argumentList, |
| List<WhyNotPromotedGetter> whyNotPromotedList) { |
| var arguments = argumentList.arguments; |
| for (int i = 0; i < arguments.length; i++) { |
| checkForArgumentTypeNotAssignableForArgument(arguments[i], |
| whyNotPromoted: |
| flowAnalysis?.flow == null ? null : whyNotPromotedList[i]); |
| } |
| } |
| |
| void checkForBodyMayCompleteNormally({ |
| required DartType? returnType, |
| required FunctionBody body, |
| required AstNode errorNode, |
| }) { |
| if (!_isNonNullableByDefault) return; |
| if (!flowAnalysis!.flow!.isReachable) { |
| return; |
| } |
| |
| if (returnType == null) { |
| return; |
| } |
| |
| if (body is BlockFunctionBody) { |
| if (body.isGenerator) { |
| return; |
| } |
| |
| if (typeSystem.isPotentiallyNonNullable(returnType)) { |
| if (errorNode is ConstructorDeclaration) { |
| errorReporter.reportErrorForName( |
| CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, |
| errorNode, |
| ); |
| } else if (errorNode is BlockFunctionBody) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, |
| errorNode.block.leftBracket, |
| ); |
| } else { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, |
| errorNode, |
| ); |
| } |
| } |
| } |
| } |
| |
| void checkReadOfNotAssignedLocalVariable( |
| SimpleIdentifier node, |
| Element? element, |
| ) { |
| if (flowAnalysis?.flow == null) { |
| return; |
| } |
| |
| if (!node.inGetterContext()) { |
| return; |
| } |
| |
| if (element is VariableElement) { |
| var assigned = flowAnalysis! |
| .isDefinitelyAssigned(node, element as PromotableElement); |
| var unassigned = flowAnalysis!.isDefinitelyUnassigned(node, element); |
| |
| if (element.isLate) { |
| if (unassigned) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.DEFINITELY_UNASSIGNED_LATE_LOCAL_VARIABLE, |
| node, |
| [node.name], |
| ); |
| } |
| return; |
| } |
| |
| if (!assigned) { |
| if (element.isFinal) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.READ_POTENTIALLY_UNASSIGNED_FINAL, |
| node, |
| [node.name], |
| ); |
| return; |
| } |
| |
| if (typeSystem.isPotentiallyNonNullable(element.type)) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode |
| .NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE, |
| node, |
| [node.name], |
| ); |
| return; |
| } |
| } |
| } |
| } |
| |
| void checkUnreachableNode(AstNode node) { |
| nullSafetyDeadCodeVerifier.visitNode(node); |
| } |
| |
| @override |
| List<DiagnosticMessage> computeWhyNotPromotedMessages( |
| SyntacticEntity errorEntity, |
| Map<DartType, NonPromotionReason>? whyNotPromoted) { |
| List<DiagnosticMessage> messages = []; |
| if (whyNotPromoted != null) { |
| for (var entry in whyNotPromoted.entries) { |
| var whyNotPromotedVisitor = _WhyNotPromotedVisitor( |
| source, errorEntity, flowAnalysis!.dataForTesting); |
| if (typeSystem.isPotentiallyNullable(entry.key)) continue; |
| var message = entry.value.accept(whyNotPromotedVisitor); |
| if (message != null) { |
| if (flowAnalysis!.dataForTesting != null) { |
| var nonPromotionReasonText = entry.value.shortName; |
| var args = <String>[]; |
| if (whyNotPromotedVisitor.propertyReference != null) { |
| var id = |
| computeMemberId(whyNotPromotedVisitor.propertyReference!); |
| args.add('target: $id'); |
| } |
| if (whyNotPromotedVisitor.propertyType != null) { |
| args.add('type: ${whyNotPromotedVisitor.propertyType}'); |
| } |
| if (args.isNotEmpty) { |
| nonPromotionReasonText += '(${args.join(', ')})'; |
| } |
| flowAnalysis!.dataForTesting!.nonPromotionReasons[errorEntity] = |
| nonPromotionReasonText; |
| } |
| messages = [message]; |
| } |
| break; |
| } |
| } |
| return messages; |
| } |
| |
| /// Return the static element associated with the given expression whose type |
| /// can be overridden, or `null` if there is no element whose type can be |
| /// overridden. |
| /// |
| /// @param expression the expression with which the element is associated |
| /// @return the element associated with the given expression |
| VariableElement? getOverridableStaticElement(Expression expression) { |
| Element? element; |
| if (expression is SimpleIdentifier) { |
| element = expression.staticElement; |
| } else if (expression is PrefixedIdentifier) { |
| element = expression.staticElement; |
| } else if (expression is PropertyAccess) { |
| element = expression.propertyName.staticElement; |
| } |
| if (element is VariableElement) { |
| return element; |
| } |
| return null; |
| } |
| |
| /// Return the result of lexical lookup for the [node], not `null`. |
| /// |
| /// Implements `16.35 Lexical Lookup` from the language specification. |
| LexicalLookupResult lexicalLookup({ |
| required SimpleIdentifier node, |
| required bool setter, |
| }) { |
| return LexicalLookup(this).perform(node: node, setter: setter); |
| } |
| |
| /// If we reached a null-shorting termination, and the [node] has null |
| /// shorting, make the type of the [node] nullable. |
| void nullShortingTermination(ExpressionImpl node, |
| {bool discardType = false}) { |
| if (!_isNonNullableByDefault) return; |
| |
| if (identical(_unfinishedNullShorts.last, node)) { |
| do { |
| _unfinishedNullShorts.removeLast(); |
| flowAnalysis!.flow!.nullAwareAccess_end(); |
| } while (identical(_unfinishedNullShorts.last, node)); |
| if (node is! CascadeExpression && !discardType) { |
| node.staticType = typeSystem.makeNullable(node.staticType as TypeImpl); |
| } |
| } |
| } |
| |
| /// 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. |
| } |
| |
| /// Set information about enclosing declarations. |
| void prepareEnclosingDeclarations({ |
| ClassElement? enclosingClassElement, |
| ExecutableElement? enclosingExecutableElement, |
| }) { |
| enclosingClass = enclosingClassElement; |
| _thisType = enclosingClass?.thisType; |
| _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 ExtensionDeclaration || |
| node is FunctionDeclaration; |
| } |
| |
| void forClassElement(ClassElement parentElement) { |
| enclosingClass = parentElement; |
| nameScope = ClassScope( |
| TypeParameterScope( |
| nameScope, |
| parentElement.typeParameters, |
| ), |
| parentElement, |
| ); |
| _thisType = parentElement.thisType; |
| } |
| |
| if (parent is ClassDeclaration) { |
| forClassElement(parent.declaredElement!); |
| return true; |
| } |
| |
| if (parent is MixinDeclaration) { |
| forClassElement(parent.declaredElement!); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Resolve LHS [node] of an assignment, an explicit [AssignmentExpression], |
| /// or implicit [PrefixExpression] or [PostfixExpression]. |
| PropertyElementResolverResult resolveForWrite({ |
| required AstNode node, |
| required bool hasRead, |
| }) { |
| if (node is IndexExpression) { |
| node.target?.accept(this); |
| startNullAwareIndexExpression(node); |
| |
| var resolver = PropertyElementResolver(this); |
| var result = resolver.resolveIndexExpression( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| |
| InferenceContext.setType(node.index, result.indexContextType); |
| node.index.accept(this); |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.index); |
| checkIndexExpressionIndex( |
| node.index, |
| readElement: result.readElement as ExecutableElement?, |
| writeElement: result.writeElement as ExecutableElement?, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| return result; |
| } else if (node is PrefixedIdentifier) { |
| node.prefix.accept(this); |
| |
| var resolver = PropertyElementResolver(this); |
| return resolver.resolvePrefixedIdentifier( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| } else if (node is PropertyAccess) { |
| node.target?.accept(this); |
| startNullAwarePropertyAccess(node); |
| |
| var resolver = PropertyElementResolver(this); |
| return resolver.resolvePropertyAccess( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| } else if (node is SimpleIdentifier) { |
| var resolver = PropertyElementResolver(this); |
| var result = resolver.resolveSimpleIdentifier( |
| node: node, |
| hasRead: hasRead, |
| hasWrite: true, |
| ); |
| |
| if (hasRead && result.readElementRequested == null) { |
| errorReporter.reportErrorForNode( |
| CompileTimeErrorCode.UNDEFINED_IDENTIFIER, |
| node, |
| [node.name], |
| ); |
| } |
| |
| return result; |
| } else { |
| node.accept(this); |
| return PropertyElementResolverResult(); |
| } |
| } |
| |
| /// Visit the given [comment] if it is not `null`. |
| void safelyVisitComment(Comment? comment) { |
| if (comment != null) { |
| super.visitComment(comment); |
| } |
| } |
| |
| void setReadElement(Expression node, Element? element) { |
| DartType readType = DynamicTypeImpl.instance; |
| if (node is IndexExpression) { |
| if (element is MethodElement) { |
| readType = element.returnType; |
| } |
| } else if (node is PrefixedIdentifier || |
| node is PropertyAccess || |
| node is SimpleIdentifier) { |
| if (element is PropertyAccessorElement && element.isGetter) { |
| readType = element.returnType; |
| } else if (element is VariableElement) { |
| readType = localVariableTypeProvider.getType(node as SimpleIdentifier, |
| isRead: true); |
| } |
| } |
| |
| var parent = node.parent; |
| if (parent is AssignmentExpressionImpl && parent.leftHandSide == node) { |
| parent.readElement = element; |
| parent.readType = readType; |
| } else if (parent is PostfixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.readElement = element; |
| parent.readType = readType; |
| } else if (parent is PrefixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.readElement = element; |
| parent.readType = readType; |
| } |
| } |
| |
| @visibleForTesting |
| void setThisInterfaceType(InterfaceType thisType) { |
| _thisType = thisType; |
| } |
| |
| void setWriteElement(Expression node, Element? element) { |
| DartType writeType = DynamicTypeImpl.instance; |
| if (node is IndexExpression) { |
| if (element is MethodElement) { |
| var parameters = element.parameters; |
| if (parameters.length == 2) { |
| writeType = parameters[1].type; |
| } |
| } |
| } else if (node is PrefixedIdentifier || |
| node is PropertyAccess || |
| node is SimpleIdentifier) { |
| if (element is PropertyAccessorElement && element.isSetter) { |
| if (element.isSynthetic) { |
| writeType = element.variable.type; |
| } else { |
| var parameters = element.parameters; |
| if (parameters.length == 1) { |
| writeType = parameters[0].type; |
| } |
| } |
| } else if (element is VariableElement) { |
| writeType = element.type; |
| } |
| } |
| |
| var parent = node.parent; |
| if (parent is AssignmentExpressionImpl && parent.leftHandSide == node) { |
| parent.writeElement = element; |
| parent.writeType = writeType; |
| } else if (parent is PostfixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.writeElement = element; |
| parent.writeType = writeType; |
| } else if (parent is PrefixExpressionImpl && |
| parent.operator.type.isIncrementOperator) { |
| parent.writeElement = element; |
| parent.writeType = writeType; |
| } |
| } |
| |
| void startNullAwareIndexExpression(IndexExpression node) { |
| if (_migratableAstInfoProvider.isIndexExpressionNullAware(node)) { |
| var flow = flowAnalysis?.flow; |
| if (flow != null) { |
| flow.nullAwareAccess_rightBegin(node.target, |
| node.realTarget.staticType ?? typeProvider.dynamicType); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| } |
| } |
| |
| void startNullAwarePropertyAccess(PropertyAccess node) { |
| if (_migratableAstInfoProvider.isPropertyAccessNullAware(node)) { |
| var flow = flowAnalysis?.flow; |
| if (flow != null) { |
| var target = node.target; |
| if (target is SimpleIdentifier && |
| target.staticElement is ClassElement) { |
| // `?.` to access static methods is equivalent to `.`, so do nothing. |
| } else { |
| flow.nullAwareAccess_rightBegin( |
| target, node.realTarget.staticType ?? typeProvider.dynamicType); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| } |
| } |
| } |
| |
| /// If in a legacy library, return the legacy view on the [element]. |
| /// Otherwise, return the original element. |
| T toLegacyElement<T extends Element?>(T element) { |
| if (_isNonNullableByDefault) return element; |
| if (element == null) return element; |
| return Member.legacy(element) as T; |
| } |
| |
| /// If in a legacy library, return the legacy version of the [type]. |
| /// Otherwise, return the original type. |
| DartType toLegacyTypeIfOptOut(DartType type) { |
| if (_isNonNullableByDefault) return type; |
| return NullabilityEliminator.perform(typeProvider, type); |
| } |
| |
| @override |
| void visitAnnotation(covariant AnnotationImpl node) { |
| var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| AnnotationResolver(this).resolve(node, whyNotPromotedList); |
| var arguments = node.arguments; |
| if (arguments != null) { |
| checkForArgumentTypesNotAssignableInList(arguments, whyNotPromotedList); |
| } |
| } |
| |
| @override |
| void visitArgumentList(ArgumentList node, |
| {bool isIdentical = false, |
| List<WhyNotPromotedGetter>? whyNotPromotedList}) { |
| whyNotPromotedList ??= []; |
| var callerType = InferenceContext.getContext(node); |
| NodeList<Expression> arguments = node.arguments; |
| if (callerType is FunctionType) { |
| Map<String, DartType> namedParameterTypes = |
| callerType.namedParameterTypes; |
| List<DartType> normalParameterTypes = callerType.normalParameterTypes; |
| List<DartType> optionalParameterTypes = callerType.optionalParameterTypes; |
| int normalCount = normalParameterTypes.length; |
| int optionalCount = optionalParameterTypes.length; |
| |
| Iterable<Expression> positional = |
| arguments.takeWhile((l) => l is! NamedExpression); |
| Iterable<Expression> required = positional.take(normalCount); |
| Iterable<Expression> optional = |
| positional.skip(normalCount).take(optionalCount); |
| Iterable<Expression> named = |
| arguments.skipWhile((l) => l is! NamedExpression); |
| var parent = node.parent; |
| DartType? targetType; |
| Element? methodElement; |
| DartType? invocationContext; |
| if (parent is MethodInvocation) { |
| targetType = parent.realTarget?.staticType; |
| methodElement = parent.methodName.staticElement; |
| invocationContext = InferenceContext.getContext(parent); |
| } |
| |
| //TODO(leafp): Consider using the parameter elements here instead. |
| //TODO(leafp): Make sure that the parameter elements are getting |
| // setup correctly with inference. |
| int index = 0; |
| for (Expression argument in required) { |
| var parameterType = normalParameterTypes[index++]; |
| if (targetType != null) { |
| InferenceContext.setType( |
| argument, |
| typeSystem.refineNumericInvocationContext( |
| targetType, methodElement, invocationContext, parameterType)); |
| } else { |
| InferenceContext.setType(argument, parameterType); |
| } |
| } |
| index = 0; |
| for (Expression argument in optional) { |
| InferenceContext.setType(argument, optionalParameterTypes[index++]); |
| } |
| |
| for (Expression argument in named) { |
| if (argument is NamedExpression) { |
| var type = namedParameterTypes[argument.name.label.name]; |
| if (type != null) { |
| InferenceContext.setType(argument, type); |
| } |
| } |
| } |
| } |
| checkUnreachableNode(node); |
| int length = arguments.length; |
| var flow = flowAnalysis?.flow; |
| for (var i = 0; i < length; i++) { |
| if (isIdentical && length > 1 && i == 1) { |
| var firstArg = arguments[0]; |
| flow?.equalityOp_rightBegin(firstArg, firstArg.typeOrThrow); |
| } |
| arguments[i].accept(this); |
| if (flow != null) { |
| whyNotPromotedList.add(flow.whyNotPromoted(arguments[i])); |
| } |
| } |
| if (isIdentical && length > 1) { |
| var secondArg = arguments[1]; |
| flow?.equalityOp_end( |
| node.parent as Expression, secondArg, secondArg.typeOrThrow); |
| } |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitAsExpression(AsExpression node) { |
| super.visitAsExpression(node); |
| flowAnalysis?.asExpression(node); |
| } |
| |
| @override |
| void visitAssertInitializer(AssertInitializer node) { |
| InferenceContext.setType(node.condition, typeProvider.boolType); |
| flowAnalysis?.flow?.assert_begin(); |
| node.condition.accept(this); |
| boolExpressionVerifier.checkForNonBoolExpression( |
| node.condition, |
| errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION, |
| whyNotPromoted: flowAnalysis?.flow?.whyNotPromoted(node.condition), |
| ); |
| flowAnalysis?.flow?.assert_afterCondition(node.condition); |
| node.message?.accept(this); |
| flowAnalysis?.flow?.assert_end(); |
| } |
| |
| @override |
| void visitAssertStatement(AssertStatement node) { |
| InferenceContext.setType(node.condition, typeProvider.boolType); |
| flowAnalysis?.flow?.assert_begin(); |
| node.condition.accept(this); |
| boolExpressionVerifier.checkForNonBoolExpression( |
| node.condition, |
| errorCode: CompileTimeErrorCode.NON_BOOL_EXPRESSION, |
| whyNotPromoted: flowAnalysis?.flow?.whyNotPromoted(node.condition), |
| ); |
| flowAnalysis?.flow?.assert_afterCondition(node.condition); |
| node.message?.accept(this); |
| flowAnalysis?.flow?.assert_end(); |
| } |
| |
| @override |
| void visitAssignmentExpression(AssignmentExpression node) { |
| _assignmentExpressionResolver.resolve(node as AssignmentExpressionImpl); |
| } |
| |
| @override |
| void visitAwaitExpression(AwaitExpression node) { |
| var contextType = InferenceContext.getContext(node); |
| if (contextType != null) { |
| var futureUnion = _createFutureOr(contextType); |
| InferenceContext.setType(node.expression, futureUnion); |
| } |
| super.visitAwaitExpression(node); |
| } |
| |
| @override |
| void visitBinaryExpression(BinaryExpression node) { |
| _binaryExpressionResolver.resolve(node as BinaryExpressionImpl); |
| } |
| |
| @override |
| void visitBlockFunctionBody(BlockFunctionBody node) { |
| try { |
| inferenceContext.pushFunctionBodyContext(node); |
| _thisAccessTracker.enterFunctionBody(node); |
| super.visitBlockFunctionBody(node); |
| } finally { |
| _thisAccessTracker.exitFunctionBody(node); |
| inferenceContext.popFunctionBodyContext(node); |
| } |
| } |
| |
| @override |
| void visitBooleanLiteral(BooleanLiteral node) { |
| flowAnalysis?.flow?.booleanLiteral(node, node.value); |
| super.visitBooleanLiteral(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); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| flowAnalysis?.breakStatement(node); |
| } |
| |
| @override |
| void visitCascadeExpression(covariant CascadeExpressionImpl node) { |
| InferenceContext.setTypeFromNode(node.target, node); |
| node.target.accept(this); |
| |
| if (node.isNullAware && flowAnalysis != null) { |
| flowAnalysis!.flow!.nullAwareAccess_rightBegin( |
| node.target, node.target.staticType ?? typeProvider.dynamicType); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| |
| node.cascadeSections.accept(this); |
| |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| |
| nullShortingTermination(node); |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| // |
| // Continue the class resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| super.visitClassDeclaration(node); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } finally { |
| _thisType = outerType?.thisType; |
| enclosingClass = outerType; |
| } |
| } |
| |
| @override |
| void visitClassDeclarationInScope(ClassDeclaration node) { |
| enclosingClass = node.declaredElement; |
| _thisType = enclosingClass?.thisType; |
| super.visitClassDeclarationInScope(node); |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| super.visitClassTypeAlias(node); |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitClassTypeAlias. |
| } |
| |
| @override |
| void visitComment(Comment node) { |
| var parent = node.parent; |
| if (parent is FunctionDeclaration || |
| parent is FunctionTypeAlias || |
| parent is ConstructorDeclaration) { |
| return; |
| } |
| |
| // TODO(scheglov) Change corresponding visiting places to visit comments |
| // with name scopes set for correct comments resolution. |
| if (parent is GenericTypeAlias) { |
| var element = parent.declaredElement as TypeAliasElement; |
| var outerScope = nameScope; |
| try { |
| nameScope = TypeParameterScope(nameScope, element.typeParameters); |
| |
| var aliasedElement = element.aliasedElement; |
| if (aliasedElement is GenericFunctionTypeElement) { |
| nameScope = FormalParameterScope( |
| TypeParameterScope(nameScope, aliasedElement.typeParameters), |
| aliasedElement.parameters, |
| ); |
| } |
| |
| super.visitComment(node); |
| return; |
| } finally { |
| nameScope = outerScope; |
| } |
| } else if (parent is MethodDeclaration) { |
| var outerScope = nameScope; |
| try { |
| var element = parent.declaredElement!; |
| nameScope = FormalParameterScope(nameScope, element.parameters); |
| |
| super.visitComment(node); |
| return; |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| super.visitComment(node); |
| } |
| |
| @override |
| void visitCommentReference(CommentReference node) { |
| // |
| // We do not visit the identifier because it needs to be visited in the |
| // context of the reference. |
| // |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| 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); |
| } |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitConditionalExpression(ConditionalExpression node) { |
| Expression condition = node.condition; |
| var flow = flowAnalysis?.flow; |
| flow?.conditional_conditionBegin(); |
| |
| // TODO(scheglov) Do we need these checks for null? |
| condition.accept(this); |
| condition = node.condition; |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition(condition, |
| whyNotPromoted: whyNotPromoted); |
| |
| Expression thenExpression = node.thenExpression; |
| InferenceContext.setTypeFromNode(thenExpression, node); |
| |
| if (flow != null) { |
| flow.conditional_thenBegin(condition, node); |
| checkUnreachableNode(thenExpression); |
| } |
| thenExpression.accept(this); |
| nullSafetyDeadCodeVerifier.flowEnd(thenExpression); |
| |
| Expression elseExpression = node.elseExpression; |
| InferenceContext.setTypeFromNode(elseExpression, node); |
| |
| if (flow != null) { |
| flow.conditional_elseBegin(thenExpression); |
| checkUnreachableNode(elseExpression); |
| elseExpression.accept(this); |
| flow.conditional_end(node, elseExpression); |
| nullSafetyDeadCodeVerifier.flowEnd(elseExpression); |
| } else { |
| elseExpression.accept(this); |
| } |
| |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @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(ConstructorDeclaration node) { |
| var outerFunction = _enclosingFunction; |
| _enclosingFunction = node.declaredElement; |
| |
| flowAnalysis!.topLevelDeclaration_enter(node, node.parameters); |
| flowAnalysis!.executableDeclaration_enter(node, node.parameters, false); |
| |
| var returnType = _enclosingFunction!.type.returnType; |
| InferenceContext.setType(node.body, returnType); |
| |
| super.visitConstructorDeclaration(node); |
| |
| if (node.factoryKeyword != null) { |
| var bodyContext = BodyInferenceContext.of(node.body); |
| checkForBodyMayCompleteNormally( |
| returnType: bodyContext?.contextType, |
| body: node.body, |
| errorNode: node, |
| ); |
| } |
| flowAnalysis!.executableDeclaration_exit(node.body, false); |
| flowAnalysis!.topLevelDeclaration_exit(); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| |
| _enclosingFunction = outerFunction; |
| } |
| |
| @override |
| void visitConstructorDeclarationInScope(ConstructorDeclaration node) { |
| super.visitConstructorDeclarationInScope(node); |
| // Because of needing a different scope for the initializer list, the |
| // overridden implementation of this method cannot cause the visitNode |
| // method to be invoked. As a result, we have to hard-code using the |
| // element resolver and type analyzer to visit the constructor declaration. |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| safelyVisitComment(node.documentationComment); |
| } |
| |
| @override |
| void visitConstructorFieldInitializer(ConstructorFieldInitializer 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 fieldElement = enclosingClass!.getField(node.fieldName.name); |
| InferenceContext.setType(node.expression, fieldElement?.type); |
| node.expression.accept(this); |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.expression); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| var enclosingConstructor = enclosingFunction as ConstructorElement; |
| if (fieldElement != null) { |
| checkForFieldInitializerNotAssignable(node, fieldElement, |
| isConstConstructor: enclosingConstructor.isConst, |
| whyNotPromoted: whyNotPromoted); |
| } |
| } |
| |
| @override |
| void visitConstructorName(ConstructorName node) { |
| // |
| // We do not visit either the type name, because it won't be visited anyway, |
| // or the name, because it needs to be visited in the context of the |
| // constructor name. |
| // |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @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); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| flowAnalysis?.continueStatement(node); |
| } |
| |
| @override |
| void visitDefaultFormalParameter(DefaultFormalParameter node) { |
| InferenceContext.setType(node.defaultValue, node.declaredElement?.type); |
| super.visitDefaultFormalParameter(node); |
| ParameterElement element = node.declaredElement!; |
| |
| if (element is DefaultParameterElementImpl && node.isOfLocalFunction) { |
| element.constantInitializer = node.defaultValue; |
| } |
| } |
| |
| @override |
| void visitDoStatementInScope(DoStatement node) { |
| checkUnreachableNode(node); |
| |
| var body = node.body; |
| var condition = node.condition; |
| |
| flowAnalysis?.flow?.doStatement_bodyBegin(node); |
| visitStatementInScope(body); |
| |
| flowAnalysis?.flow?.doStatement_conditionBegin(); |
| InferenceContext.setType(condition, typeProvider.boolType); |
| condition.accept(this); |
| condition = node.condition; |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition); |
| boolExpressionVerifier.checkForNonBoolCondition(condition, |
| whyNotPromoted: whyNotPromoted); |
| |
| flowAnalysis?.flow?.doStatement_end(condition); |
| } |
| |
| @override |
| void visitEmptyFunctionBody(EmptyFunctionBody node) { |
| if (resolveOnlyCommentInFunctionBody) { |
| return; |
| } |
| super.visitEmptyFunctionBody(node); |
| } |
| |
| @override |
| void visitEnumConstantDeclaration(EnumConstantDeclaration node) { |
| node.metadata.accept(this); |
| super.visitEnumConstantDeclaration(node); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| // |
| // Continue the enum resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| enclosingClass = node.declaredElement; |
| _thisType = enclosingClass?.thisType; |
| super.visitEnumDeclaration(node); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } finally { |
| _thisType = outerType?.thisType; |
| enclosingClass = outerType; |
| } |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| if (resolveOnlyCommentInFunctionBody) { |
| return; |
| } |
| |
| try { |
| inferenceContext.pushFunctionBodyContext(node); |
| InferenceContext.setType( |
| node.expression, |
| inferenceContext.bodyContext!.contextType, |
| ); |
| _thisAccessTracker.enterFunctionBody(node); |
| |
| super.visitExpressionFunctionBody(node); |
| |
| flowAnalysis?.flow?.handleExit(); |
| |
| inferenceContext.bodyContext!.addReturnExpression(node.expression); |
| } finally { |
| _thisAccessTracker.exitFunctionBody(node); |
| inferenceContext.popFunctionBodyContext(node); |
| } |
| } |
| |
| @override |
| void visitExtensionDeclaration(ExtensionDeclaration node) { |
| try { |
| super.visitExtensionDeclaration(node); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } finally { |
| _thisType = null; |
| } |
| } |
| |
| @override |
| void visitExtensionDeclarationInScope(ExtensionDeclaration node) { |
| _thisType = node.declaredElement!.extendedType; |
| super.visitExtensionDeclarationInScope(node); |
| } |
| |
| @override |
| void visitExtensionOverride(ExtensionOverride node) { |
| var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| node.extensionName.accept(this); |
| node.typeArguments?.accept(this); |
| |
| ExtensionMemberResolver(this).setOverrideReceiverContextType(node); |
| visitArgumentList(node.argumentList, |
| whyNotPromotedList: whyNotPromotedList); |
| |
| node.accept(elementResolver); |
| extensionResolver.resolveOverride(node, whyNotPromotedList); |
| } |
| |
| @override |
| void visitFieldDeclaration(FieldDeclaration node) { |
| _thisAccessTracker.enterFieldDeclaration(node); |
| try { |
| super.visitFieldDeclaration(node); |
| } finally { |
| _thisAccessTracker.exitFieldDeclaration(node); |
| } |
| } |
| |
| @override |
| void visitForElementInScope(ForElement node) { |
| _forResolver.resolveElement(node as ForElementImpl); |
| } |
| |
| @override |
| void visitForStatementInScope(ForStatement node) { |
| _forResolver.resolveStatement(node as ForStatementImpl); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| var outerFunction = _enclosingFunction; |
| _enclosingFunction = node.declaredElement; |
| |
| bool isLocal = node.parent is FunctionDeclarationStatement; |
| |
| if (isLocal) { |
| flowAnalysis!.flow!.functionExpression_begin(node); |
| } else { |
| flowAnalysis! |
| .topLevelDeclaration_enter(node, node.functionExpression.parameters); |
| } |
| flowAnalysis!.executableDeclaration_enter( |
| node, |
| node.functionExpression.parameters, |
| isLocal, |
| ); |
| |
| var functionType = _enclosingFunction!.type; |
| InferenceContext.setType(node.functionExpression, functionType); |
| |
| super.visitFunctionDeclaration(node); |
| |
| // TODO(scheglov) encapsulate |
| var bodyContext = BodyInferenceContext.of( |
| node.functionExpression.body, |
| ); |
| checkForBodyMayCompleteNormally( |
| returnType: bodyContext?.contextType, |
| body: node.functionExpression.body, |
| errorNode: node.name, |
| ); |
| flowAnalysis!.executableDeclaration_exit( |
| node.functionExpression.body, |
| isLocal, |
| ); |
| if (isLocal) { |
| flowAnalysis!.flow!.functionExpression_end(); |
| } else { |
| flowAnalysis!.topLevelDeclaration_exit(); |
| } |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| |
| _enclosingFunction = outerFunction; |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitFunctionDeclaration |
| } |
| |
| @override |
| void visitFunctionDeclarationInScope(FunctionDeclaration node) { |
| super.visitFunctionDeclarationInScope(node); |
| safelyVisitComment(node.documentationComment); |
| } |
| |
| @override |
| void visitFunctionExpression(covariant FunctionExpressionImpl node) { |
| var outerFunction = _enclosingFunction; |
| _enclosingFunction = node.declaredElement; |
| |
| if (node.parent is FunctionDeclaration) { |
| _functionExpressionResolver.resolve(node); |
| } else { |
| Scope outerScope = nameScope; |
| try { |
| ExecutableElement element = node.declaredElement!; |
| nameScope = FormalParameterScope( |
| TypeParameterScope(nameScope, element.typeParameters), |
| element.parameters, |
| ); |
| _functionExpressionResolver.resolve(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| _enclosingFunction = outerFunction; |
| } |
| |
| @override |
| void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| node.function.accept(this); |
| _functionExpressionInvocationResolver.resolve( |
| node as FunctionExpressionInvocationImpl, whyNotPromotedList); |
| nullShortingTermination(node); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, whyNotPromotedList); |
| } |
| |
| @override |
| void visitFunctionReference(FunctionReference node) { |
| _functionReferenceResolver.resolve(node as FunctionReferenceImpl); |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| super.visitFunctionTypeAlias(node); |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitFunctionTypeAlias. |
| } |
| |
| @override |
| void visitFunctionTypeAliasInScope(FunctionTypeAlias node) { |
| super.visitFunctionTypeAliasInScope(node); |
| safelyVisitComment(node.documentationComment); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| super.visitFunctionTypedFormalParameter(node); |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitFunctionTypedFormalParameter. |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| super.visitGenericTypeAlias(node); |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitGenericTypeAlias. |
| } |
| |
| @override |
| void visitGenericTypeAliasInFunctionScope(GenericTypeAlias node) { |
| super.visitGenericTypeAliasInFunctionScope(node); |
| safelyVisitComment(node.documentationComment); |
| } |
| |
| @override |
| void visitHideCombinator(HideCombinator node) {} |
| |
| @override |
| void visitIfElement(IfElement node) { |
| flowAnalysis?.flow?.ifStatement_conditionBegin(); |
| Expression condition = node.condition; |
| InferenceContext.setType(condition, typeProvider.boolType); |
| condition.accept(this); |
| condition = node.condition; |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition); |
| |
| boolExpressionVerifier.checkForNonBoolCondition(condition, |
| whyNotPromoted: whyNotPromoted); |
| |
| CollectionElement thenElement = node.thenElement; |
| flowAnalysis!.flow?.ifStatement_thenBegin(condition, node); |
| thenElement.accept(this); |
| |
| var elseElement = node.elseElement; |
| if (elseElement != null) { |
| flowAnalysis?.flow?.ifStatement_elseBegin(); |
| elseElement.accept(this); |
| } |
| |
| flowAnalysis?.flow?.ifStatement_end(elseElement != null); |
| |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitIfStatement(IfStatement node) { |
| checkUnreachableNode(node); |
| flowAnalysis?.flow?.ifStatement_conditionBegin(); |
| |
| Expression condition = node.condition; |
| |
| InferenceContext.setType(condition, typeProvider.boolType); |
| condition.accept(this); |
| condition = node.condition; |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition); |
| |
| boolExpressionVerifier.checkForNonBoolCondition(condition, |
| whyNotPromoted: whyNotPromoted); |
| |
| Statement thenStatement = node.thenStatement; |
| flowAnalysis!.flow?.ifStatement_thenBegin(condition, node); |
| visitStatementInScope(thenStatement); |
| nullSafetyDeadCodeVerifier.flowEnd(thenStatement); |
| |
| var elseStatement = node.elseStatement; |
| if (elseStatement != null) { |
| flowAnalysis?.flow?.ifStatement_elseBegin(); |
| visitStatementInScope(elseStatement); |
| nullSafetyDeadCodeVerifier.flowEnd(elseStatement); |
| } |
| |
| flowAnalysis?.flow?.ifStatement_end(elseStatement != null); |
| |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitIndexExpression(covariant IndexExpressionImpl node) { |
| node.target?.accept(this); |
| startNullAwareIndexExpression(node); |
| |
| var resolver = PropertyElementResolver(this); |
| var result = resolver.resolveIndexExpression( |
| node: node, |
| hasRead: true, |
| hasWrite: false, |
| ); |
| |
| var element = result.readElement; |
| node.staticElement = element as MethodElement?; |
| |
| InferenceContext.setType(node.index, result.indexContextType); |
| node.index.accept(this); |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.index); |
| checkIndexExpressionIndex( |
| node.index, |
| readElement: result.readElement as ExecutableElement?, |
| writeElement: null, |
| whyNotPromoted: whyNotPromoted, |
| ); |
| |
| DartType type; |
| if (identical(node.realTarget.staticType, NeverTypeImpl.instance)) { |
| type = NeverTypeImpl.instance; |
| } else if (element is MethodElement) { |
| type = element.returnType; |
| } else { |
| type = DynamicTypeImpl.instance; |
| } |
| inferenceHelper.recordStaticType(node, type); |
| |
| nullShortingTermination(node); |
| } |
| |
| @override |
| void visitInstanceCreationExpression( |
| covariant InstanceCreationExpressionImpl node) { |
| var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| node.constructorName.accept(this); |
| _inferArgumentTypesForInstanceCreate(node); |
| visitArgumentList(node.argumentList, |
| whyNotPromotedList: whyNotPromotedList); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, whyNotPromotedList); |
| } |
| |
| @override |
| void visitIsExpression(IsExpression node) { |
| super.visitIsExpression(node); |
| flowAnalysis?.isExpression(node); |
| } |
| |
| @override |
| void visitLabel(Label node) {} |
| |
| @override |
| void visitLabeledStatement(LabeledStatement node) { |
| flowAnalysis?.labeledStatement_enter(node); |
| super.visitLabeledStatement(node); |
| flowAnalysis?.labeledStatement_exit(node); |
| } |
| |
| @override |
| void visitLibraryIdentifier(LibraryIdentifier node) {} |
| |
| @override |
| void visitListLiteral(covariant ListLiteralImpl node) { |
| checkUnreachableNode(node); |
| _typedLiteralResolver.resolveListLiteral(node); |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| var outerFunction = _enclosingFunction; |
| _enclosingFunction = node.declaredElement; |
| |
| flowAnalysis!.topLevelDeclaration_enter(node, node.parameters); |
| flowAnalysis!.executableDeclaration_enter(node, node.parameters, false); |
| |
| DartType returnType = _enclosingFunction!.returnType; |
| InferenceContext.setType(node.body, returnType); |
| |
| super.visitMethodDeclaration(node); |
| |
| // TODO(scheglov) encapsulate |
| var bodyContext = BodyInferenceContext.of(node.body); |
| checkForBodyMayCompleteNormally( |
| returnType: bodyContext?.contextType, |
| body: node.body, |
| errorNode: node.name, |
| ); |
| flowAnalysis!.executableDeclaration_exit(node.body, false); |
| flowAnalysis!.topLevelDeclaration_exit(); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| |
| _enclosingFunction = outerFunction; |
| node.accept(elementResolver); |
| // Note: no need to call the typeAnalyzer since it does not override |
| // visitMethodDeclaration. |
| } |
| |
| @override |
| void visitMethodInvocation(covariant MethodInvocationImpl node) { |
| var whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| var target = node.target; |
| target?.accept(this); |
| |
| if (_migratableAstInfoProvider.isMethodInvocationNullAware(node)) { |
| var flow = flowAnalysis?.flow; |
| if (flow != null) { |
| if (target is SimpleIdentifierImpl && |
| target.staticElement is ClassElement) { |
| // `?.` to access static methods is equivalent to `.`, so do nothing. |
| } else { |
| flow.nullAwareAccess_rightBegin( |
| target, node.realTarget!.staticType ?? typeProvider.dynamicType); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| } |
| } |
| |
| node.typeArguments?.accept(this); |
| elementResolver.visitMethodInvocation(node, |
| whyNotPromotedList: whyNotPromotedList); |
| |
| var functionRewrite = MethodInvocationResolver.getRewriteResult(node); |
| if (functionRewrite != null) { |
| nullShortingTermination(node, discardType: true); |
| _resolveRewrittenFunctionExpressionInvocation( |
| functionRewrite, whyNotPromotedList); |
| } else { |
| nullShortingTermination(node); |
| } |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, whyNotPromotedList); |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| // |
| // Continue the class resolution. |
| // |
| var outerType = enclosingClass; |
| try { |
| super.visitMixinDeclaration(node); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } finally { |
| _thisType = outerType?.thisType; |
| enclosingClass = outerType; |
| } |
| } |
| |
| @override |
| void visitMixinDeclarationInScope(MixinDeclaration node) { |
| enclosingClass = node.declaredElement; |
| _thisType = enclosingClass?.thisType; |
| super.visitMixinDeclarationInScope(node); |
| } |
| |
| @override |
| void visitNamedExpression(NamedExpression node) { |
| InferenceContext.setTypeFromNode(node.expression, node); |
| super.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); |
| } |
| |
| @override |
| void visitNode(AstNode node) { |
| checkUnreachableNode(node); |
| node.visitChildren(this); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitNullLiteral(NullLiteral node) { |
| flowAnalysis?.flow?.nullLiteral(node); |
| super.visitNullLiteral(node); |
| } |
| |
| @override |
| void visitParenthesizedExpression(ParenthesizedExpression node) { |
| InferenceContext.setTypeFromNode(node.expression, node); |
| super.visitParenthesizedExpression(node); |
| flowAnalysis?.flow?.parenthesizedExpression(node, node.expression); |
| } |
| |
| @override |
| void visitPostfixExpression(PostfixExpression node) { |
| _postfixExpressionResolver.resolve(node as PostfixExpressionImpl); |
| } |
| |
| @override |
| void visitPrefixedIdentifier(covariant PrefixedIdentifierImpl node) { |
| _prefixedIdentifierResolver.resolve(node); |
| } |
| |
| @override |
| void visitPrefixExpression(PrefixExpression node) { |
| _prefixExpressionResolver.resolve(node as PrefixExpressionImpl); |
| } |
| |
| @override |
| void visitPropertyAccess(covariant PropertyAccessImpl node) { |
| node.target?.accept(this); |
| startNullAwarePropertyAccess(node); |
| |
| var resolver = PropertyElementResolver(this); |
| var result = resolver.resolvePropertyAccess( |
| node: node, |
| hasRead: true, |
| hasWrite: false, |
| ); |
| |
| var element = result.readElement; |
| |
| var propertyName = node.propertyName; |
| propertyName.staticElement = element; |
| |
| DartType type; |
| if (element is MethodElement) { |
| type = element.type; |
| } else if (element is PropertyAccessorElement && element.isGetter) { |
| type = element.returnType; |
| } else if (result.functionTypeCallType != null) { |
| type = result.functionTypeCallType!; |
| } else { |
| type = DynamicTypeImpl.instance; |
| } |
| |
| type = inferenceHelper.inferTearOff(node, propertyName, type); |
| |
| inferenceHelper.recordStaticType(propertyName, type); |
| inferenceHelper.recordStaticType(node, type); |
| |
| nullShortingTermination(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 whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| node.accept(elementResolver); |
| InferenceContext.setType(node.argumentList, node.staticElement?.type); |
| visitArgumentList(node.argumentList, |
| whyNotPromotedList: whyNotPromotedList); |
| node.accept(typeAnalyzer); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, whyNotPromotedList); |
| } |
| |
| @override |
| void visitRethrowExpression(RethrowExpression node) { |
| super.visitRethrowExpression(node); |
| flowAnalysis?.flow?.handleExit(); |
| } |
| |
| @override |
| void visitReturnStatement(ReturnStatement node) { |
| InferenceContext.setType( |
| node.expression, |
| inferenceContext.bodyContext?.contextType, |
| ); |
| |
| super.visitReturnStatement(node); |
| |
| inferenceContext.bodyContext?.addReturnExpression(node.expression); |
| flowAnalysis?.flow?.handleExit(); |
| } |
| |
| @override |
| void visitSetOrMapLiteral(SetOrMapLiteral node) { |
| checkUnreachableNode(node); |
| _typedLiteralResolver.resolveSetOrMapLiteral(node); |
| } |
| |
| @override |
| void visitShowCombinator(ShowCombinator node) {} |
| |
| @override |
| void visitSimpleIdentifier(covariant SimpleIdentifierImpl node) { |
| SimpleIdentifierResolver(this, flowAnalysis).resolve(node); |
| } |
| |
| @override |
| void visitSpreadElement(SpreadElement node) { |
| super.visitSpreadElement(node); |
| |
| if (!node.isNullAware) { |
| nullableDereferenceVerifier.expression(node.expression, |
| errorCode: |
| CompileTimeErrorCode.UNCHECKED_USE_OF_NULLABLE_VALUE_IN_SPREAD); |
| } |
| } |
| |
| @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 whyNotPromotedList = <Map<DartType, NonPromotionReason> Function()>[]; |
| node.accept(elementResolver); |
| InferenceContext.setType(node.argumentList, node.staticElement?.type); |
| visitArgumentList(node.argumentList, |
| whyNotPromotedList: whyNotPromotedList); |
| node.accept(typeAnalyzer); |
| checkForArgumentTypesNotAssignableInList( |
| node.argumentList, whyNotPromotedList); |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| checkUnreachableNode(node); |
| |
| InferenceContext.setType( |
| node.expression, _enclosingSwitchStatementExpressionType); |
| super.visitSwitchCase(node); |
| |
| var flow = flowAnalysis?.flow; |
| if (flow != null && flow.isReachable && _isNonNullableByDefault) { |
| var switchStatement = node.parent as SwitchStatement; |
| if (switchStatement.members.last != node && node.statements.isNotEmpty) { |
| errorReporter.reportErrorForToken( |
| CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY, |
| node.keyword, |
| ); |
| } |
| } |
| |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } |
| |
| @override |
| void visitSwitchDefault(SwitchDefault node) { |
| super.visitSwitchDefault(node); |
| nullSafetyDeadCodeVerifier.flowEnd(node); |
| } |
| |
| @override |
| void visitSwitchStatementInScope(SwitchStatement node) { |
| checkUnreachableNode(node); |
| |
| var previousExpressionType = _enclosingSwitchStatementExpressionType; |
| try { |
| var expression = node.expression; |
| expression.accept(this); |
| expression = node.expression; |
| |
| _enclosingSwitchStatementExpressionType = expression.typeOrThrow; |
| |
| if (flowAnalysis != null) { |
| var flow = flowAnalysis!.flow!; |
| |
| flow.switchStatement_expressionEnd(node); |
| |
| var exhaustiveness = _SwitchExhaustiveness( |
| _enclosingSwitchStatementExpressionType!, |
| ); |
| |
| var members = node.members; |
| for (var member in members) { |
| flow.switchStatement_beginCase(member.labels.isNotEmpty, node); |
| member.accept(this); |
| |
| exhaustiveness.visitSwitchMember(member); |
| } |
| |
| flow.switchStatement_end(exhaustiveness.isExhaustive); |
| } else { |
| node.members.accept(this); |
| } |
| } finally { |
| _enclosingSwitchStatementExpressionType = previousExpressionType; |
| } |
| } |
| |
| @override |
| void visitThrowExpression(ThrowExpression node) { |
| super.visitThrowExpression(node); |
| flowAnalysis?.flow?.handleExit(); |
| } |
| |
| @override |
| void visitTryStatement(TryStatement node) { |
| if (flowAnalysis == null) { |
| return super.visitTryStatement(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); |
| if (catchClauses.isNotEmpty) { |
| flow.tryCatchStatement_bodyEnd(body); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| nullSafetyDeadCodeVerifier.tryStatementEnter(node); |
| |
| var catchLength = catchClauses.length; |
| for (var i = 0; i < catchLength; ++i) { |
| var catchClause = catchClauses[i]; |
| nullSafetyDeadCodeVerifier.verifyCatchClause(catchClause); |
| flow.tryCatchStatement_catchBegin( |
| catchClause.exceptionParameter?.staticElement as PromotableElement?, |
| catchClause.stackTraceParameter?.staticElement as PromotableElement?, |
| ); |
| 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(); |
| } |
| } |
| |
| @override |
| void visitTypeName(TypeName node) {} |
| |
| @override |
| void visitVariableDeclaration(VariableDeclaration node) { |
| _variableDeclarationResolver.resolve(node as VariableDeclarationImpl); |
| |
| var declaredElement = node.declaredElement!; |
| if (node.parent!.parent is ForParts) { |
| _define(declaredElement); |
| } |
| |
| var initializer = node.initializer; |
| var parent = node.parent as VariableDeclarationList; |
| var declaredType = parent.type; |
| if (initializer != null) { |
| var initializerStaticType = initializer.typeOrThrow; |
| if (declaredType == null) { |
| if (_isNonNullableByDefault && |
| initializerStaticType is TypeParameterType) { |
| flowAnalysis?.flow?.promote( |
| declaredElement as PromotableElement, initializerStaticType); |
| } |
| } else { |
| flowAnalysis?.flow?.initialize(declaredElement as PromotableElement, |
| initializerStaticType, initializer, |
| isFinal: parent.isFinal, isLate: parent.isLate); |
| } |
| } |
| } |
| |
| @override |
| void visitVariableDeclarationList(VariableDeclarationList node) { |
| flowAnalysis?.variableDeclarationList(node); |
| for (VariableDeclaration decl in node.variables) { |
| VariableElement variableElement = decl.declaredElement!; |
| InferenceContext.setType(decl, variableElement.type); |
| } |
| super.visitVariableDeclarationList(node); |
| } |
| |
| @override |
| void visitWhileStatement(WhileStatement node) { |
| checkUnreachableNode(node); |
| |
| // Note: since we don't call the base class, we have to maintain |
| // _implicitLabelScope ourselves. |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| |
| Expression condition = node.condition; |
| InferenceContext.setType(condition, typeProvider.boolType); |
| |
| flowAnalysis?.flow?.whileStatement_conditionBegin(node); |
| condition.accept(this); |
| condition = node.condition; |
| var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(condition); |
| |
| boolExpressionVerifier.checkForNonBoolCondition(node.condition, |
| whyNotPromoted: whyNotPromoted); |
| |
| Statement body = node.body; |
| flowAnalysis?.flow?.whileStatement_bodyBegin(node, condition); |
| visitStatementInScope(body); |
| flowAnalysis?.flow?.whileStatement_end(); |
| nullSafetyDeadCodeVerifier.flowEnd(node.body); |
| } finally { |
| _implicitLabelScope = outerImplicitScope; |
| } |
| // TODO(brianwilkerson) If the loop can only be exited because the condition |
| // is false, then propagateFalseState(condition); |
| node.accept(elementResolver); |
| node.accept(typeAnalyzer); |
| } |
| |
| @override |
| void visitYieldStatement(YieldStatement node) { |
| _yieldStatementResolver.resolve(node); |
| } |
| |
| /// Creates a union of `T | Future<T>`, unless `T` is already a |
| /// future-union, in which case it simply returns `T`. |
| DartType _createFutureOr(DartType type) { |
| if (type.isDartAsyncFutureOr) { |
| return type; |
| } |
| return typeProvider.futureOrType(type); |
| } |
| |
| void _inferArgumentTypesForInstanceCreate( |
| covariant InstanceCreationExpressionImpl node) { |
| var constructorName = node.constructorName; |
| |
| var typeName = constructorName.type; |
| var typeArguments = typeName.typeArguments; |
| |
| var elementToInfer = inferenceHelper.constructorElementToInfer( |
| constructorName: constructorName, |
| definingLibrary: definingLibrary, |
| ); |
| |
| FunctionType? inferred; |
| // If the constructor is generic, we'll have a ConstructorMember that |
| // substitutes in type arguments (possibly `dynamic`) from earlier in |
| // resolution. |
| // |
| // Otherwise we'll have a ConstructorElement, and we can skip inference |
| // because there's nothing to infer in a non-generic type. |
| if (elementToInfer != null) { |
| // TODO(leafp): Currently, we may re-infer types here, since we |
| // sometimes resolve multiple times. We should really check that we |
| // have not already inferred something. However, the obvious ways to |
| // check this don't work, since we may have been instantiated |
| // to bounds in an earlier phase, and we *do* want to do inference |
| // in that case. |
| |
| // Get back to the uninstantiated generic constructor. |
| // TODO(jmesserly): should we store this earlier in resolution? |
| // Or look it up, instead of jumping backwards through the Member? |
| var rawElement = elementToInfer.element; |
| var constructorType = elementToInfer.asType; |
| |
| inferred = inferenceHelper.inferArgumentTypesForGeneric( |
| node, constructorType, typeArguments, |
| isConst: node.isConst, errorNode: node.constructorName); |
| |
| if (inferred != null) { |
| var arguments = node.argumentList; |
| InferenceContext.setType(arguments, inferred); |
| // Fix up the parameter elements based on inferred method. |
| arguments.correspondingStaticParameters = |
| resolveArgumentsToParameters(arguments, inferred.parameters, null); |
| |
| constructorName.type.type = inferred.returnType; |
| |
| // Update the static element as well. This is used in some cases, such |
| // as computing constant values. It is stored in two places. |
| var constructorElement = ConstructorMember.from( |
| rawElement, |
| inferred.returnType as InterfaceType, |
| ); |
| constructorName.staticElement = constructorElement; |
| } |
| } |
| |
| if (inferred == null) { |
| var constructorElement = constructorName.staticElement; |
| if (constructorElement != null) { |
| var type = constructorElement.type; |
| type = toLegacyTypeIfOptOut(type) as FunctionType; |
| InferenceContext.setType(node.argumentList, type); |
| } |
| } |
| } |
| |
| /// 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( |
| FunctionExpressionInvocation node, |
| List<WhyNotPromotedGetter> whyNotPromotedList, |
| ) { |
| var function = node.function; |
| |
| if (function is PropertyAccess && |
| _migratableAstInfoProvider.isPropertyAccessNullAware(function) && |
| _isNonNullableByDefault) { |
| var target = function.target; |
| if (target is SimpleIdentifier && target.staticElement is ClassElement) { |
| // `?.` to access static methods is equivalent to `.`, so do nothing. |
| } else { |
| flowAnalysis!.flow!.nullAwareAccess_rightBegin(function, |
| function.realTarget.staticType ?? typeProvider.dynamicType); |
| _unfinishedNullShorts.add(node.nullShortingTermination); |
| } |
| } |
| |
| _functionExpressionInvocationResolver.resolve( |
| node as FunctionExpressionInvocationImpl, whyNotPromotedList); |
| |
| nullShortingTermination(node); |
| } |
| |
| /// 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. |
| /// |
| /// An error will be reported to [onError] if any of the arguments cannot be |
| /// matched to a parameter. onError will be provided the node of the first |
| /// argument that is not matched. onError can be null to ignore the error. |
| /// |
| /// Returns the parameters that correspond to the arguments. If no parameter |
| /// matched an argument, that position will be `null` in the list. |
| static List<ParameterElement?> resolveArgumentsToParameters( |
| ArgumentList argumentList, |
| List<ParameterElement> parameters, |
| void Function(ErrorCode errorCode, AstNode node, |
| [List<Object> arguments])? |
| onError) { |
| if (parameters.isEmpty && argumentList.arguments.isEmpty) { |
| return const <ParameterElement>[]; |
| } |
| int requiredParameterCount = 0; |
| int unnamedParameterCount = 0; |
| List<ParameterElement> unnamedParameters = <ParameterElement>[]; |
| Map<String, ParameterElement>? namedParameters; |
| int length = parameters.length; |
| for (int i = 0; i < length; i++) { |
| ParameterElement parameter = parameters[i]; |
| if (parameter.isRequiredPositional) { |
| unnamedParameters.add(parameter); |
| unnamedParameterCount++; |
| requiredParameterCount++; |
| } else if (parameter.isOptionalPositional) { |
| unnamedParameters.add(parameter); |
| unnamedParameterCount++; |
| } else { |
| namedParameters ??= HashMap<String, ParameterElement>(); |
| namedParameters[parameter.name] = parameter; |
| } |
| } |
| int unnamedIndex = 0; |
| NodeList<Expression> arguments = argumentList.arguments; |
| int argumentCount = arguments.length; |
| List<ParameterElement?> resolvedParameters = |
| List<ParameterElement?>.filled(argumentCount, null); |
| int positionalArgumentCount = 0; |
| HashSet<String>? usedNames; |
| bool noBlankArguments = true; |
| Expression? firstUnresolvedArgument; |
| 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) { |
| if (onError != null) { |
| onError(CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER, nameNode, |
| [name]); |
| } |
| } else { |
| resolvedParameters[i] = element; |
| nameNode.staticElement = element; |
| } |
| usedNames ??= HashSet<String>(); |
| if (!usedNames.add(name)) { |
| if (onError != null) { |
| onError(CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, |
| [name]); |
| } |
| } |
| } else { |
| if (argument is SimpleIdentifier && argument.name.isEmpty) { |
| noBlankArguments = false; |
| } |
| positionalArgumentCount++; |
| if (unnamedIndex < unnamedParameterCount) { |
| resolvedParameters[i] = unnamedParameters[unnamedIndex++]; |
| } else { |
| firstUnresolvedArgument ??= argument; |
| } |
| } |
| } |
| if (positionalArgumentCount < requiredParameterCount && noBlankArguments) { |
| if (onError != null) { |
| onError(CompileTimeErrorCode.NOT_ENOUGH_POSITIONAL_ARGUMENTS, |
| argumentList, [requiredParameterCount, positionalArgumentCount]); |
| } |
| } else if (positionalArgumentCount > unnamedParameterCount && |
| noBlankArguments) { |
| ErrorCode errorCode; |
| int namedParameterCount = namedParameters?.length ?? 0; |
| int namedArgumentCount = usedNames?.length ?? 0; |
| if (namedParameterCount > namedArgumentCount) { |
| errorCode = |
| CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS_COULD_BE_NAMED; |
| } else { |
| errorCode = CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS; |
| } |
| if (onError != null) { |
| onError(errorCode, firstUnresolvedArgument!, |
| [unnamedParameterCount, positionalArgumentCount]); |
| } |
| } |
| return resolvedParameters; |
| } |
| } |
| |
| /// Override of [ResolverVisitorForMigration] that invokes methods of |
| /// [MigrationResolutionHooks] when appropriate. |
| class ResolverVisitorForMigration extends ResolverVisitor { |
| final MigrationResolutionHooks _migrationResolutionHooks; |
| |
| ResolverVisitorForMigration( |
| InheritanceManager3 inheritanceManager, |
| LibraryElement definingLibrary, |
| Source source, |
| TypeProvider typeProvider, |
| AnalysisErrorListener errorListener, |
| TypeSystemImpl typeSystem, |
| FeatureSet featureSet, |
| MigrationResolutionHooks migrationResolutionHooks) |
| : _migrationResolutionHooks = migrationResolutionHooks, |
| super._( |
| inheritanceManager, |
| definingLibrary, |
| source, |
| typeSystem, |
| typeProvider, |
| errorListener, |
| featureSet, |
| null, |
| FlowAnalysisHelperForMigration( |
| typeSystem, migrationResolutionHooks, true), |
| migrationResolutionHooks, |
| migrationResolutionHooks); |
| |
| @override |
| void visitConditionalExpression(covariant ConditionalExpressionImpl node) { |
| var conditionalKnownValue = |
| _migrationResolutionHooks.getConditionalKnownValue(node); |
| if (conditionalKnownValue == null) { |
| super.visitConditionalExpression(node); |
| return; |
| } else { |
| var subexpressionToKeep = |
| conditionalKnownValue ? node.thenExpression : node.elseExpression; |
| subexpressionToKeep.accept(this); |
| typeAnalyzer.recordStaticType(node, subexpressionToKeep.typeOrThrow); |
| } |
| } |
| |
| @override |
| void visitIfElement(IfElement node) { |
| var conditionalKnownValue = |
| _migrationResolutionHooks.getConditionalKnownValue(node); |
| if (conditionalKnownValue == null) { |
| super.visitIfElement(node); |
| return; |
| } else { |
| (conditionalKnownValue ? node.thenElement : node.elseElement) |
| ?.accept(this); |
| } |
| } |
| |
| @override |
| void visitIfStatement(IfStatement node) { |
| var conditionalKnownValue = |
| _migrationResolutionHooks.getConditionalKnownValue(node); |
| if (conditionalKnownValue == null) { |
| super.visitIfStatement(node); |
| return; |
| } else { |
| (conditionalKnownValue ? node.thenStatement : node.elseStatement) |
| ?.accept(this); |
| } |
| } |
| } |
| |
| /// The abstract class `ScopedVisitor` maintains name and label scopes as an AST |
| /// structure is being visited. |
| abstract class ScopedVisitor extends UnifyingAstVisitor<void> { |
| static const _nameScopeProperty = 'nameScope'; |
| |
| /// The element for the library containing the compilation unit being visited. |
| final LibraryElement definingLibrary; |
| |
| /// 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; |
| |
| /// 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 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 class containing the AST nodes being visited, |
| /// or `null` if we are not in the scope of a class. |
| ClassElement? enclosingClass; |
| |
| /// The element representing the extension containing the AST nodes being |
| /// visited, or `null` if we are not in the scope of an extension. |
| ExtensionElement? enclosingExtension; |
| |
| /// Initialize a newly created visitor to resolve the nodes in a compilation |
| /// unit. |
| /// |
| /// [definingLibrary] is the element for the library containing the |
| /// compilation unit being visited. |
| /// [source] is the source representing the compilation unit being visited. |
| /// [typeProvider] is the object used to access the types from the core |
| /// library. |
| /// [errorListener] is the error listener 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. If `null` or unspecified, a new [LibraryScope] will be |
| /// created based on [definingLibrary] and [typeProvider]. |
| ScopedVisitor(this.definingLibrary, Source source, this.typeProvider, |
| AnalysisErrorListener errorListener, |
| {Scope? nameScope}) |
| : source = source, |
| errorReporter = ErrorReporter( |
| errorListener, |
| source, |
| isNonNullableByDefault: definingLibrary.isNonNullableByDefault, |
| ), |
| nameScope = nameScope ?? LibraryScope(definingLibrary); |
| |
| /// Return the implicit label scope in which the current node is being |
| /// resolved. |
| ImplicitLabelScope get implicitLabelScope => _implicitLabelScope; |
| |
| /// Replaces the current [Scope] with the enclosing [Scope]. |
| /// |
| /// @return the enclosing [Scope]. |
| Scope popNameScope() { |
| nameScope = (nameScope as EnclosedScope).parent; |
| return nameScope; |
| } |
| |
| /// Pushes a new [Scope] into the visitor. |
| /// |
| /// @return the new [Scope]. |
| Scope pushNameScope() { |
| Scope newScope = LocalScope(nameScope); |
| nameScope = newScope; |
| return nameScope; |
| } |
| |
| @override |
| void visitBlock(Block 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 visitCatchClause(CatchClause node) { |
| var exception = node.exceptionParameter; |
| if (exception != null) { |
| Scope outerScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| _define(exception.staticElement!); |
| var stackTrace = node.stackTraceParameter; |
| if (stackTrace != null) { |
| _define(stackTrace.staticElement!); |
| } |
| super.visitCatchClause(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } else { |
| super.visitCatchClause(node); |
| } |
| } |
| |
| @override |
| void visitClassDeclaration(ClassDeclaration node) { |
| Scope outerScope = nameScope; |
| var outerClass = enclosingClass; |
| try { |
| ClassElement element = node.declaredElement!; |
| enclosingClass = node.declaredElement; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope( |
| nameScope, |
| element.typeParameters, |
| ); |
| visitClassDeclarationInScope(node); |
| |
| nameScope = ClassScope(nameScope, element); |
| visitClassMembersInScope(node); |
| } finally { |
| enclosingClass = outerClass; |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitClassDeclarationInScope(ClassDeclaration node) { |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.extendsClause?.accept(this); |
| node.withClause?.accept(this); |
| node.implementsClause?.accept(this); |
| node.nativeClause?.accept(this); |
| } |
| |
| void visitClassMembersInScope(ClassDeclaration node) { |
| node.documentationComment?.accept(this); |
| node.members.accept(this); |
| } |
| |
| @override |
| void visitClassTypeAlias(ClassTypeAlias node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| ClassElement element = node.declaredElement!; |
| nameScope = ClassScope( |
| TypeParameterScope(nameScope, element.typeParameters), |
| element, |
| ); |
| visitClassTypeAliasInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitClassTypeAliasInScope(ClassTypeAlias node) { |
| // Note: we don't visit metadata because it's not inside the class type |
| // alias's type parameter scope. It was already visited in |
| // [visitClassTypeAlias]. |
| node.documentationComment?.accept(this); |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.superclass.accept(this); |
| node.withClause.accept(this); |
| node.implementsClause?.accept(this); |
| } |
| |
| @override |
| void visitCompilationUnit(CompilationUnit node) { |
| _setNodeNameScope(node, nameScope); |
| super.visitCompilationUnit(node); |
| } |
| |
| @override |
| void visitConstructorDeclaration(ConstructorDeclaration node) { |
| Scope outerScope = nameScope; |
| try { |
| ConstructorElement element = node.declaredElement!; |
| |
| node.documentationComment?.accept(this); |
| node.metadata.accept(this); |
| node.returnType.accept(this); |
| node.name?.accept(this); |
| node.parameters.accept(this); |
| |
| try { |
| nameScope = ConstructorInitializerScope( |
| nameScope, |
| element, |
| ); |
| node.initializers.accept(this); |
| } finally { |
| nameScope = outerScope; |
| } |
| |
| node.redirectedConstructor?.accept(this); |
| |
| nameScope = FormalParameterScope( |
| nameScope, |
| element.parameters, |
| ); |
| visitConstructorDeclarationInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitConstructorDeclarationInScope(ConstructorDeclaration node) { |
| node.body.accept(this); |
| } |
| |
| @override |
| void visitDeclaredIdentifier(DeclaredIdentifier node) { |
| _define(node.declaredElement!); |
| super.visitDeclaredIdentifier(node); |
| } |
| |
| @override |
| void visitDoStatement(DoStatement node) { |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| visitDoStatementInScope(node); |
| } finally { |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| void visitDoStatementInScope(DoStatement node) { |
| visitStatementInScope(node.body); |
| node.condition.accept(this); |
| } |
| |
| @override |
| void visitEnumDeclaration(EnumDeclaration node) { |
| Scope outerScope = nameScope; |
| var outerClass = enclosingClass; |
| try { |
| ClassElement element = node.declaredElement!; |
| enclosingClass = node.declaredElement; |
| node.metadata.accept(this); |
| |
| nameScope = ClassScope(nameScope, element); |
| visitEnumMembersInScope(node); |
| } finally { |
| enclosingClass = outerClass; |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitEnumMembersInScope(EnumDeclaration node) { |
| node.documentationComment?.accept(this); |
| node.constants.accept(this); |
| } |
| |
| @override |
| void visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| _setNodeNameScope(node, nameScope); |
| super.visitExpressionFunctionBody(node); |
| } |
| |
| @override |
| void visitExtensionDeclaration(ExtensionDeclaration node) { |
| Scope outerScope = nameScope; |
| var outerExtension = enclosingExtension; |
| try { |
| ExtensionElement element = node.declaredElement!; |
| enclosingExtension = element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope( |
| nameScope, |
| element.typeParameters, |
| ); |
| visitExtensionDeclarationInScope(node); |
| |
| nameScope = ExtensionScope(nameScope, element); |
| visitExtensionMembersInScope(node); |
| } finally { |
| enclosingExtension = outerExtension; |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitExtensionDeclarationInScope(ExtensionDeclaration node) { |
| node.name?.accept(this); |
| node.typeParameters?.accept(this); |
| node.extendedType.accept(this); |
| } |
| |
| void visitExtensionMembersInScope(ExtensionDeclaration node) { |
| node.documentationComment?.accept(this); |
| node.members.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 visitForElement(ForElement node) { |
| Scope outerNameScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| _setNodeNameScope(node, nameScope); |
| visitForElementInScope(node); |
| } finally { |
| nameScope = outerNameScope; |
| } |
| } |
| |
| /// Visit the given [node] after it's scope has been created. This replaces |
| /// the normal call to the inherited visit method so that ResolverVisitor can |
| /// intervene when type propagation is enabled. |
| void visitForElementInScope(ForElement node) { |
| // TODO(brianwilkerson) Investigate the possibility of removing the |
| // visit...InScope methods now that type propagation is no longer done. |
| node.forLoopParts.accept(this); |
| node.body.accept(this); |
| } |
| |
| @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) { |
| nameScope = FormalParameterScope( |
| nameScope, |
| parent.declaredElement!.parameters, |
| ); |
| } else if (parent is FunctionTypeAlias) { |
| var aliasedElement = parent.declaredElement!.aliasedElement; |
| var functionElement = aliasedElement as GenericFunctionTypeElement; |
| nameScope = FormalParameterScope( |
| nameScope, |
| functionElement.parameters, |
| ); |
| } else if (parent is MethodDeclaration) { |
| nameScope = FormalParameterScope( |
| nameScope, |
| parent.declaredElement!.parameters, |
| ); |
| } |
| } |
| |
| @override |
| void visitForStatement(ForStatement node) { |
| Scope outerNameScope = nameScope; |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| _setNodeNameScope(node, nameScope); |
| visitForStatementInScope(node); |
| } finally { |
| nameScope = outerNameScope; |
| _implicitLabelScope = outerImplicitScope; |
| } |
| } |
| |
| /// Visit the given [node] after it's scope has been created. This replaces |
| /// the normal call to the inherited visit method so that ResolverVisitor can |
| /// intervene when type propagation is enabled. |
| void visitForStatementInScope(ForStatement node) { |
| // TODO(brianwilkerson) Investigate the possibility of removing the |
| // visit...InScope methods now that type propagation is no longer done. |
| node.forLoopParts.accept(this); |
| visitStatementInScope(node.body); |
| } |
| |
| @override |
| void visitFunctionDeclaration(FunctionDeclaration node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredElement!; |
| nameScope = TypeParameterScope( |
| nameScope, |
| element.typeParameters, |
| ); |
| visitFunctionDeclarationInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitFunctionDeclarationInScope(FunctionDeclaration node) { |
| // Note: we don't visit metadata because it's not inside the function's type |
| // parameter scope. It was already visited in [visitFunctionDeclaration]. |
| node.documentationComment?.accept(this); |
| node.returnType?.accept(this); |
| node.name.accept(this); |
| node.functionExpression.accept(this); |
| } |
| |
| @override |
| void visitFunctionExpression(FunctionExpression node) { |
| if (node.parent is FunctionDeclaration) { |
| // We have already created a function scope and don't need to do so again. |
| super.visitFunctionExpression(node); |
| return; |
| } |
| |
| Scope outerScope = nameScope; |
| try { |
| ExecutableElement element = node.declaredElement!; |
| nameScope = FormalParameterScope( |
| TypeParameterScope(nameScope, element.typeParameters), |
| element.parameters, |
| ); |
| super.visitFunctionExpression(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitFunctionTypeAlias(FunctionTypeAlias node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredElement!; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters); |
| visitFunctionTypeAliasInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitFunctionTypeAliasInScope(FunctionTypeAlias node) { |
| // Note: we don't visit metadata because it's not inside the function type |
| // alias's type parameter scope. It was already visited in |
| // [visitFunctionTypeAlias]. |
| node.documentationComment?.accept(this); |
| node.returnType?.accept(this); |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters.accept(this); |
| } |
| |
| @override |
| void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| ParameterElement element = node.declaredElement!; |
| nameScope = TypeParameterScope( |
| nameScope, |
| element.typeParameters, |
| ); |
| visitFunctionTypedFormalParameterInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitFunctionTypedFormalParameterInScope( |
| FunctionTypedFormalParameter node) { |
| // Note: we don't visit metadata because it's not inside the function typed |
| // formal parameter's type parameter scope. It was already visited in |
| // [visitFunctionTypedFormalParameter]. |
| node.documentationComment?.accept(this); |
| node.returnType?.accept(this); |
| node.identifier.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters.accept(this); |
| } |
| |
| @override |
| void visitGenericFunctionType(GenericFunctionType 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 { |
| GenericFunctionTypeElement element = |
| (node as GenericFunctionTypeImpl).declaredElement!; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters); |
| super.visitGenericFunctionType(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitGenericTypeAlias(GenericTypeAlias node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| var element = node.declaredElement as TypeAliasElement; |
| nameScope = TypeParameterScope(nameScope, element.typeParameters); |
| visitGenericTypeAliasInScope(node); |
| |
| var aliasedElement = element.aliasedElement; |
| if (aliasedElement is GenericFunctionTypeElement) { |
| nameScope = FormalParameterScope(nameScope, aliasedElement.parameters); |
| visitGenericTypeAliasInFunctionScope(node); |
| } |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitGenericTypeAliasInFunctionScope(GenericTypeAlias node) {} |
| |
| void visitGenericTypeAliasInScope(GenericTypeAlias node) { |
| // Note: we don't visit metadata because it's not inside the generic type |
| // alias's type parameter scope. It was already visited in |
| // [visitGenericTypeAlias]. |
| node.documentationComment?.accept(this); |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.type.accept(this); |
| } |
| |
| @override |
| void visitIfStatement(IfStatement node) { |
| node.condition.accept(this); |
| visitStatementInScope(node.thenStatement); |
| visitStatementInScope(node.elseStatement); |
| } |
| |
| @override |
| void visitLabeledStatement(LabeledStatement node) { |
| var outerScope = _addScopesFor(node.labels, node.unlabeled); |
| try { |
| super.visitLabeledStatement(node); |
| } finally { |
| labelScope = outerScope; |
| } |
| } |
| |
| @override |
| void visitMethodDeclaration(MethodDeclaration node) { |
| node.metadata.accept(this); |
| Scope outerScope = nameScope; |
| try { |
| ExecutableElement element = node.declaredElement!; |
| nameScope = TypeParameterScope( |
| nameScope, |
| element.typeParameters, |
| ); |
| visitMethodDeclarationInScope(node); |
| } finally { |
| nameScope = outerScope; |
| } |
| } |
| |
| void visitMethodDeclarationInScope(MethodDeclaration node) { |
| // Note: we don't visit metadata because it's not inside the method's type |
| // parameter scope. It was already visited in [visitMethodDeclaration]. |
| node.documentationComment?.accept(this); |
| node.returnType?.accept(this); |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.parameters?.accept(this); |
| node.body.accept(this); |
| } |
| |
| @override |
| void visitMixinDeclaration(MixinDeclaration node) { |
| Scope outerScope = nameScope; |
| var outerClass = enclosingClass; |
| try { |
| ClassElement element = node.declaredElement!; |
| enclosingClass = element; |
| node.metadata.accept(this); |
| |
| nameScope = TypeParameterScope(nameScope, element.typeParameters); |
| visitMixinDeclarationInScope(node); |
| |
| nameScope = ClassScope(nameScope, element); |
| visitMixinMembersInScope(node); |
| } finally { |
| nameScope = outerScope; |
| enclosingClass = outerClass; |
| } |
| } |
| |
| void visitMixinDeclarationInScope(MixinDeclaration node) { |
| node.name.accept(this); |
| node.typeParameters?.accept(this); |
| node.onClause?.accept(this); |
| node.implementsClause?.accept(this); |
| } |
| |
| void visitMixinMembersInScope(MixinDeclaration node) { |
| node.documentationComment?.accept(this); |
| node.members.accept(this); |
| } |
| |
| /// Visit the given statement after it's scope has been created. This is used |
| /// by ResolverVisitor to correctly visit the 'then' and 'else' statements of |
| /// an 'if' statement. |
| /// |
| /// @param node the statement to be visited |
| void visitStatementInScope(Statement? node) { |
| if (node is Block) { |
| // Don't create a scope around a block because the block will create it's |
| // own scope. |
| visitBlock(node); |
| } else if (node != null) { |
| Scope outerNameScope = nameScope; |
| try { |
| nameScope = LocalScope(nameScope); |
| node.accept(this); |
| } finally { |
| nameScope = outerNameScope; |
| } |
| } |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| node.expression.accept(this); |
| |
| _withDeclaredLocals(node, node.statements, () { |
| node.statements.accept(this); |
| }); |
| } |
| |
| @override |
| void visitSwitchDefault(SwitchDefault node) { |
| _withDeclaredLocals(node, node.statements, () { |
| node.statements.accept(this); |
| }); |
| } |
| |
| @override |
| void visitSwitchStatement(SwitchStatement node) { |
| var outerScope = labelScope; |
| ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| try { |
| _implicitLabelScope = _implicitLabelScope.nest(node); |
| for (SwitchMember member in node.members) { |
| for (Label label in member.labels) { |
|