| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:_fe_analyzer_shared/src/type_inference/type_analyzer.dart' | |
| as shared; | |
| import 'package:_fe_analyzer_shared/src/type_inference/variable_bindings.dart'; | |
| import 'package:analyzer/dart/ast/syntactic_entity.dart'; | |
| import 'package:analyzer/dart/ast/token.dart'; | |
| import 'package:analyzer/dart/ast/visitor.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/dart/element/scope.dart'; | |
| import 'package:analyzer/dart/element/type_provider.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/scope.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart'; | |
| import 'package:analyzer/src/dart/element/type_provider.dart'; | |
| import 'package:analyzer/src/dart/error/lint_codes.dart'; | |
| import 'package:analyzer/src/dart/resolver/ast_rewrite.dart'; | |
| import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; | |
| import 'package:analyzer/src/dart/resolver/named_type_resolver.dart'; | |
| import 'package:analyzer/src/dart/resolver/record_type_annotation_resolver.dart'; | |
| import 'package:analyzer/src/dart/resolver/scope.dart'; | |
| import 'package:analyzer/src/dart/resolver/scope_context.dart'; | |
| import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag; | |
| import 'package:analyzer/src/diagnostic/diagnostic_factory.dart'; | |
| import 'package:analyzer/src/error/listener.dart'; | |
| import 'package:analyzer/src/error/lookup_failure_reporter.dart'; | |
| import 'package:analyzer/src/utilities/extensions/object.dart'; | |
| /// Builds scopes, and resolves everything found by scope lookup: local and | |
| /// top-level names, import prefixes, type annotations, labels, and pattern | |
| /// variables. Also rewrites parsed nodes whose meaning follows from what their | |
| /// names resolve to. | |
| class ScopeAnalyzer extends RecursiveAstVisitor2<void> { | |
| final LibraryElementImpl _libraryElement; | |
| final TypeProviderImpl _typeProvider; | |
| final LibraryFragmentImpl _libraryFragment; | |
| final DiagnosticReporter _diagnosticReporter; | |
| final AstRewriter _astRewriter; | |
| final NamedTypeResolver _namedTypeResolver; | |
| final RecordTypeAnnotationResolver _recordTypeResolver; | |
| /// Data structure for tracking declared pattern variables. | |
| late final _VariableBinder _patternVariables = _VariableBinder( | |
| errors: _VariableBinderErrors(this), | |
| typeProvider: _typeProvider, | |
| ); | |
| /// The set of required operations on types. | |
| final TypeSystemOperations typeSystemOperations; | |
| final TypeConstraintGenerationDataForTesting? dataForTesting; | |
| final ScopeContext _scopeContext; | |
| /// The container with information about local variables. | |
| final LocalVariableInfo _localVariableInfo = LocalVariableInfo(); | |
| LabelScope? _labelScope; | |
| UnlabeledBreakContinueContext _unlabeledBreakContinueContext = | |
| UnlabeledBreakContinueContext.root; | |
| LocalFunctionElement? _enclosingClosure; | |
| int _libraryDirectiveIndex = 0; | |
| factory ScopeAnalyzer({ | |
| required LibraryFragmentImpl libraryFragment, | |
| required DiagnosticListener diagnosticListener, | |
| required Scope nameScope, | |
| required DocImportScope? docImportScope, | |
| required bool strictInference, | |
| required bool strictCasts, | |
| required TypeConstraintGenerationDataForTesting? dataForTesting, | |
| }) { | |
| var libraryElement = libraryFragment.library; | |
| var typeProvider = libraryElement.typeProvider; | |
| var unitSource = libraryFragment.source; | |
| var diagnosticReporter = DiagnosticReporter(diagnosticListener, unitSource); | |
| var typeSystemOperations = TypeSystemOperations( | |
| libraryFragment.library.typeSystem, | |
| strictCasts: strictCasts, | |
| ); | |
| var scopeContext = ScopeContext( | |
| libraryFragment: libraryFragment, | |
| nameScope: nameScope, | |
| docImportScope: docImportScope, | |
| ); | |
| var namedTypeResolver = NamedTypeResolver( | |
| libraryElement, | |
| libraryFragment, | |
| scopeContext, | |
| diagnosticReporter, | |
| strictInference: strictInference, | |
| strictCasts: strictCasts, | |
| typeSystemOperations: typeSystemOperations, | |
| ); | |
| var recordTypeResolver = RecordTypeAnnotationResolver( | |
| typeProvider: typeProvider, | |
| diagnosticReporter: diagnosticReporter, | |
| libraryElement: libraryElement, | |
| ); | |
| return ScopeAnalyzer._( | |
| libraryElement, | |
| typeProvider, | |
| libraryFragment, | |
| diagnosticReporter, | |
| AstRewriter(diagnosticReporter, scopeContext, libraryElement), | |
| namedTypeResolver, | |
| recordTypeResolver, | |
| scopeContext, | |
| typeSystemOperations, | |
| dataForTesting, | |
| ); | |
| } | |
| ScopeAnalyzer._( | |
| this._libraryElement, | |
| this._typeProvider, | |
| this._libraryFragment, | |
| this._diagnosticReporter, | |
| this._astRewriter, | |
| this._namedTypeResolver, | |
| this._recordTypeResolver, | |
| this._scopeContext, | |
| this.typeSystemOperations, | |
| this.dataForTesting, | |
| ); | |
| Scope get nameScope => _scopeContext.nameScope; | |
| /// Set information about enclosing declarations. | |
| void prepareEnclosingDeclarations({ | |
| InterfaceElementImpl? enclosingClassElement, | |
| }) { | |
| _namedTypeResolver.enclosingClass = enclosingClassElement; | |
| } | |
| @override | |
| void visitAnonymousMethodInvocation( | |
| covariant AnonymousMethodInvocationImpl node, | |
| ) { | |
| node.target2?.accept2(this); | |
| _scopeContext.withLocalScope((scope) { | |
| if (node.parameters case var parameters?) { | |
| scope.addFormalParameterList(parameters); | |
| parameters.accept2(this); | |
| } | |
| node.body.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitAssignedVariablePattern( | |
| covariant AssignedVariablePatternImpl node, | |
| ) { | |
| var name = node.name.lexeme; | |
| var element = nameScope.lookup(name).getter; | |
| node.element = element; | |
| if (element is PromotableElementImpl) { | |
| _localVariableInfo.potentiallyMutatedInScope.add(element); | |
| } | |
| if (element == null) { | |
| // Recovery: the code might try to refer to an instance field. | |
| if (_scopeContext.enclosingInstanceElement | |
| case InterfaceElementImpl enclosingInterfaceElement?) { | |
| var element = enclosingInterfaceElement.inheritanceManager.getMember( | |
| enclosingInterfaceElement, | |
| Name.forLibrary(_libraryElement, name).forSetter, | |
| ); | |
| if (element != null) { | |
| node.element = element; | |
| _diagnosticReporter.report( | |
| diag.patternAssignmentNotLocalVariable.at(node.name), | |
| ); | |
| return; | |
| } | |
| } | |
| LookupFailureReporter( | |
| _diagnosticReporter, | |
| _libraryFragment, | |
| ).reportWriteFailure( | |
| domain: UnqualifiedLookupDomain(), | |
| name: node.name, | |
| foundInstead: null, | |
| ); | |
| } else if (!(element is LocalVariableElement || | |
| element is FormalParameterElement)) { | |
| _diagnosticReporter.report( | |
| diag.patternAssignmentNotLocalVariable.at(node.name), | |
| ); | |
| } | |
| } | |
| @override | |
| void visitBlock(covariant BlockImpl node) { | |
| _scopeContext.withLocalScope((scope) { | |
| node.nameScope = scope; | |
| _defineLocalElements(scope, node.statements); | |
| node.statements.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitBlockFunctionBody(covariant BlockFunctionBodyImpl node) { | |
| node.localVariableInfo = _localVariableInfo; | |
| _withUnlabeledBreakContinueContext(UnlabeledBreakContinueContext.root, () { | |
| super.visitBlockFunctionBody(node); | |
| }); | |
| } | |
| @override | |
| void visitBreakStatement(covariant BreakStatementImpl node) { | |
| node.target = _lookupBreakOrContinueTarget( | |
| node, | |
| node.label, | |
| isContinue: false, | |
| ); | |
| } | |
| @override | |
| void visitCatchClause(covariant CatchClauseImpl node) { | |
| var exceptionTypeNode = node.exceptionType; | |
| exceptionTypeNode?.accept2(this); | |
| _scopeContext.withLocalScope((scope) { | |
| if (node.exceptionParameter case var exceptionNode?) { | |
| var fragment = exceptionNode.declaredFragment!; | |
| var element = fragment.element; | |
| scope.add(element); | |
| if (node.exceptionType case var typeNode?) { | |
| element.type = typeNode.typeOrThrow; | |
| } else { | |
| element.type = _typeProvider.objectType; | |
| } | |
| } | |
| var stackTraceNode = node.stackTraceParameter; | |
| if (stackTraceNode != null) { | |
| var fragment = stackTraceNode.declaredFragment!; | |
| var element = fragment.element; | |
| scope.add(element); | |
| element.type = _typeProvider.stackTraceType; | |
| } | |
| node.body.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitClassDeclaration(covariant ClassDeclarationImpl node) { | |
| var fragment = node.declaredFragment!; | |
| var element = fragment.element; | |
| _namedTypeResolver.enclosingClass = element; | |
| _scopeContext.visitClassDeclaration(node, visitor: this); | |
| _namedTypeResolver.enclosingClass = null; | |
| } | |
| @override | |
| void visitClassTypeAlias(covariant ClassTypeAliasImpl node) { | |
| var fragment = node.declaredFragment!; | |
| _namedTypeResolver.enclosingClass = fragment.element; | |
| _scopeContext.visitClassTypeAlias( | |
| node, | |
| visitor: this, | |
| visitSuperclass: (superclass) { | |
| _resolveType(declaration: node, clause: null, namedType: superclass); | |
| }, | |
| ); | |
| _namedTypeResolver.enclosingClass = null; | |
| } | |
| @override | |
| void visitComment(covariant CommentImpl node) { | |
| _scopeContext.visitDocumentationComment(node, this); | |
| } | |
| @override | |
| void visitCommentReference(covariant CommentReferenceImpl node) { | |
| var first = node.components.first; | |
| first.scopeLookupResult = nameScope.lookup(first.name.lexeme); | |
| } | |
| @override | |
| void visitCompilationUnit(covariant CompilationUnitImpl node) { | |
| node.nameScope = nameScope; | |
| super.visitCompilationUnit(node); | |
| } | |
| @override | |
| void visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) { | |
| _scopeContext.visitConstructorDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitConstructorFieldInitializer( | |
| covariant ConstructorFieldInitializerImpl node, | |
| ) { | |
| node.visitChildren2(this); | |
| } | |
| @override | |
| void visitConstructorReference2(covariant ConstructorReference2Impl node) { | |
| node.typeReference.accept2(this); | |
| } | |
| @override | |
| void visitConstructorTearOff(covariant ConstructorTearOffImpl node) { | |
| node.typeReference.accept2(this); | |
| } | |
| @override | |
| void visitConstructorTypeReference( | |
| covariant ConstructorTypeReferenceImpl node, | |
| ) { | |
| node.typeArguments?.accept2(this); | |
| _namedTypeResolver.resolveConstructorTypeReference( | |
| node, | |
| dataForTesting: dataForTesting, | |
| ); | |
| } | |
| @override | |
| void visitContinueStatement(covariant ContinueStatementImpl node) { | |
| node.target = _lookupBreakOrContinueTarget( | |
| node, | |
| node.label, | |
| isContinue: true, | |
| ); | |
| } | |
| @override | |
| void visitDeclaredIdentifier(covariant DeclaredIdentifierImpl node) { | |
| super.visitDeclaredIdentifier(node); | |
| if (node.type != null) { | |
| node.declaredFragment!.element.type = node.type!.typeOrThrow; | |
| } else { | |
| node.declaredFragment!.element.type = _typeProvider.dynamicType; | |
| } | |
| } | |
| @override | |
| void visitDeclaredVariablePattern( | |
| covariant DeclaredVariablePatternImpl node, | |
| ) { | |
| var fragment = node.declaredFragment!; | |
| var element = fragment.element; | |
| node.type?.accept2(this); | |
| if (node.type != null) { | |
| element.type = node.type!.typeOrThrow; | |
| } else { | |
| fragment.hasImplicitType = true; | |
| } | |
| var patternContext = node.patternContext; | |
| if (patternContext is ForEachPartsWithPatternImpl) { | |
| fragment.isFinal = patternContext.keyword.keyword == Keyword.FINAL; | |
| } else if (patternContext is PatternVariableDeclarationImpl) { | |
| fragment.isFinal = patternContext.keyword.keyword == Keyword.FINAL; | |
| } else { | |
| fragment.isFinal = node.keyword?.keyword == Keyword.FINAL; | |
| } | |
| } | |
| @override | |
| void visitDoStatement(covariant DoStatementImpl node) { | |
| _withUnlabeledBreakContinueContextNested(node, () { | |
| _visitStatementInScope(node.body); | |
| node.condition2.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitEmptyClassBody(EmptyClassBody node) {} | |
| @override | |
| void visitEmptyEnumBody(EmptyEnumBody node) {} | |
| @override | |
| void visitEmptyFunctionBody(covariant EmptyFunctionBodyImpl node) { | |
| node.localVariableInfo = _localVariableInfo; | |
| super.visitEmptyFunctionBody(node); | |
| } | |
| @override | |
| void visitEnumDeclaration(covariant EnumDeclarationImpl node) { | |
| var fragment = node.declaredFragment!; | |
| var element = fragment.element; | |
| _namedTypeResolver.enclosingClass = element; | |
| _scopeContext.visitEnumDeclaration(node, visitor: this); | |
| _namedTypeResolver.enclosingClass = null; | |
| } | |
| @override | |
| void visitExportDirective(covariant ExportDirectiveImpl node) { | |
| super.visitExportDirective(node); | |
| } | |
| @override | |
| void visitExpressionFunctionBody(covariant ExpressionFunctionBodyImpl node) { | |
| node.localVariableInfo = _localVariableInfo; | |
| node.nameScope = nameScope; | |
| super.visitExpressionFunctionBody(node); | |
| } | |
| @override | |
| void visitExtendsClause(covariant ExtendsClauseImpl node) { | |
| _resolveType( | |
| declaration: node.parent2 as Declaration?, | |
| clause: node, | |
| namedType: node.superclass, | |
| ); | |
| } | |
| @override | |
| void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) { | |
| _scopeContext.visitExtensionDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitExtensionTypeDeclaration( | |
| covariant ExtensionTypeDeclarationImpl node, | |
| ) { | |
| var fragment = node.declaredFragment!; | |
| var element = fragment.element; | |
| _namedTypeResolver.enclosingClass = element; | |
| _scopeContext.visitExtensionTypeDeclaration(node, visitor: this); | |
| _namedTypeResolver.enclosingClass = null; | |
| } | |
| @override | |
| void visitFieldDeclaration(covariant FieldDeclarationImpl node) { | |
| _scopeContext.visitFieldDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitFieldFormalParameter(covariant FieldFormalParameterImpl node) { | |
| _scopeContext.visitFormalParameter(node, visitor: this); | |
| _setExplicitFormalParameterType(node); | |
| } | |
| @override | |
| void visitForEachPartsWithDeclaration( | |
| covariant ForEachPartsWithDeclarationImpl node, | |
| ) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitForEachPartsWithPattern( | |
| covariant ForEachPartsWithPatternImpl node, | |
| ) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitForElement(covariant ForElementImpl node) { | |
| _scopeContext.withLocalScope((scope) { | |
| node.nameScope = scope; | |
| _visitForLoopParts(scope, node.forLoopParts); | |
| _scopeContext.withLocalScope((_) { | |
| node.body2.accept2(this); | |
| }); | |
| }); | |
| } | |
| @override | |
| void visitForPartsWithPattern(covariant ForPartsWithPatternImpl node) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitForStatement(covariant ForStatementImpl node) { | |
| _withUnlabeledBreakContinueContextNested(node, () { | |
| _scopeContext.withLocalScope((scope) { | |
| node.nameScope = scope; | |
| _visitForLoopParts(scope, node.forLoopParts); | |
| _visitStatementInScope(node.body); | |
| }); | |
| }); | |
| } | |
| @override | |
| void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) { | |
| var element = node.declaredFragment!.element; | |
| var closure = element.tryCast<LocalFunctionElementImpl>(); | |
| _withEnclosingClosure(closure, () { | |
| _scopeContext.visitFunctionDeclaration(node, visitor: this); | |
| }); | |
| if (element is LocalFunctionElementImpl) { | |
| element.returnType = node.returnType?.type ?? _typeProvider.dynamicType; | |
| } | |
| } | |
| @override | |
| void visitFunctionExpression(covariant FunctionExpressionImpl node) { | |
| var element = node.declaredFragment!.element; | |
| var closure = element.tryCast<LocalFunctionElementImpl>(); | |
| _withEnclosingClosure(closure, () { | |
| _scopeContext.visitFunctionExpression(node, visitor: this); | |
| }); | |
| } | |
| @override | |
| void visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) { | |
| _scopeContext.visitFunctionTypeAlias(node, visitor: this); | |
| } | |
| @override | |
| void visitGenericFunctionType(covariant GenericFunctionTypeImpl node) { | |
| _scopeContext.visitGenericFunctionType(node, visitor: this); | |
| var element = node.declaredFragment!.element; | |
| element.returnType = node.returnType?.type ?? _typeProvider.dynamicType; | |
| var type = FunctionTypeImpl( | |
| typeParameters: element.typeParameters, | |
| formalParameters: element.formalParameters, | |
| returnType: element.returnType, | |
| nullabilitySuffix: _getNullability(node.question != null), | |
| ); | |
| element.type = type; | |
| node.type = type; | |
| } | |
| @override | |
| void visitGenericTypeAlias(covariant GenericTypeAliasImpl node) { | |
| _scopeContext.visitGenericTypeAlias( | |
| node, | |
| visitor: this, | |
| enterTypeParameterScope: () { | |
| node.nameScope = nameScope; | |
| }, | |
| ); | |
| } | |
| @override | |
| void visitHideCombinator(HideCombinator node) { | |
| var scope = nameScope.tryCast<LibraryFragmentScope>(); | |
| scope?.importsTrackingActive(false); | |
| try { | |
| super.visitHideCombinator(node); | |
| } finally { | |
| scope?.importsTrackingActive(true); | |
| } | |
| } | |
| @override | |
| void visitIfElement(covariant IfElementImpl node) { | |
| if (node.caseClause case var caseClause?) { | |
| node.expression2.accept2(this); | |
| _resolveGuardedPattern( | |
| caseClause.guardedPattern, | |
| then: () { | |
| caseClause.nameScope = nameScope; | |
| node.ifTrue2.accept2(this); | |
| }, | |
| ); | |
| node.ifFalse2?.accept2(this); | |
| } else { | |
| node.visitChildren2(this); | |
| } | |
| } | |
| @override | |
| void visitIfStatement(covariant IfStatementImpl node) { | |
| if (node.caseClause case var caseClause?) { | |
| node.expression2.accept2(this); | |
| _resolveGuardedPattern( | |
| caseClause.guardedPattern, | |
| then: () { | |
| caseClause.nameScope = nameScope; | |
| _visitStatementInScope(node.ifTrue2); | |
| }, | |
| ); | |
| _visitStatementInScope(node.ifFalse2); | |
| } else { | |
| node.expression2.accept2(this); | |
| _visitStatementInScope(node.ifTrue2); | |
| _visitStatementInScope(node.ifFalse2); | |
| } | |
| } | |
| @override | |
| void visitImplementsClause(covariant ImplementsClauseImpl node) { | |
| _resolveImplementsClause( | |
| declaration: node.parent2 as Declaration, | |
| clause: node, | |
| ); | |
| } | |
| @override | |
| void visitImportDirective(covariant ImportDirectiveImpl node) { | |
| var element = node.libraryImport; | |
| if (element != null) { | |
| _setElementAnnotations(node.metadata, element.metadata.annotations); | |
| } | |
| node.visitChildren2(this); | |
| } | |
| @override | |
| void visitLabeledStatement(LabeledStatement node) { | |
| var unlabeled = node.unlabeled; | |
| var labelScope = _nestLabelScopes(_labelScope, node.labels, unlabeled); | |
| _withLabelScope(labelScope, () { | |
| unlabeled.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitLibraryDirective(covariant LibraryDirectiveImpl node) { | |
| ++_libraryDirectiveIndex; | |
| var element = node.element; | |
| if (element is LibraryElementImpl && _libraryDirectiveIndex == 1) { | |
| _setElementAnnotations(node.metadata, element.metadata.annotations); | |
| } | |
| super.visitLibraryDirective(node); | |
| } | |
| @override | |
| void visitMethodDeclaration(covariant MethodDeclarationImpl node) { | |
| _scopeContext.visitMethodDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitMixinDeclaration(covariant MixinDeclarationImpl node) { | |
| _scopeContext.visitMixinDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitMixinOnClause(covariant MixinOnClauseImpl node) { | |
| _resolveMixinOnClause( | |
| declaration: node.parent2 as Declaration, | |
| clause: node, | |
| ); | |
| } | |
| @override | |
| void visitNamedArgument(covariant NamedArgumentImpl node) { | |
| node.visitChildren2(this); | |
| } | |
| @override | |
| void visitNamedType(covariant NamedTypeImpl node) { | |
| node.typeArguments?.accept2(this); | |
| _namedTypeResolver.resolve(node, dataForTesting: dataForTesting); | |
| } | |
| @override | |
| void visitNativeFunctionBody(covariant NativeFunctionBodyImpl node) { | |
| node.localVariableInfo = _localVariableInfo; | |
| super.visitNativeFunctionBody(node); | |
| } | |
| @override | |
| void visitParsedCascadeName(covariant ParsedCascadeNameImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitParsedDotShorthandName(covariant ParsedDotShorthandNameImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitParsedNameAccess(covariant ParsedNameAccessImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitParsedNameAccessAssignmentTarget( | |
| covariant ParsedNameAccessAssignmentTargetImpl node, | |
| ) { | |
| _astRewriter.parsedAssignmentTarget(nameScope, node).accept2(this); | |
| } | |
| @override | |
| void visitParsedTypeArguments(covariant ParsedTypeArgumentsImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitParsedUnqualifiedName(covariant ParsedUnqualifiedNameImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitParsedUnqualifiedNameAssignmentTarget( | |
| covariant ParsedUnqualifiedNameAssignmentTargetImpl node, | |
| ) { | |
| _astRewriter.parsedAssignmentTarget(nameScope, node).accept2(this); | |
| } | |
| @override | |
| void visitParsedValueArguments(covariant ParsedValueArgumentsImpl node) => | |
| _visitParsedExpression(node); | |
| @override | |
| void visitPartDirective(covariant PartDirectiveImpl node) { | |
| var partInclude = node.partInclude; | |
| if (partInclude != null) { | |
| _setElementAnnotations(node.metadata, partInclude.metadata.annotations); | |
| } | |
| super.visitPartDirective(node); | |
| } | |
| @override | |
| void visitPatternAssignment(covariant PatternAssignmentImpl node) { | |
| _scopeContext.withLocalScope((scope) { | |
| var variables = _computeDeclaredPatternVariables(node.pattern); | |
| scope.addAll(variables); | |
| node.pattern.accept2(this); | |
| node.expression2.accept2(this); | |
| }); | |
| } | |
| @override | |
| void visitPatternVariableDeclarationStatement( | |
| covariant PatternVariableDeclarationStatementImpl node, | |
| ) { | |
| node.declaration.accept2(this); | |
| } | |
| @override | |
| void visitPrimaryConstructorBody(covariant PrimaryConstructorBodyImpl node) { | |
| _scopeContext.visitPrimaryConstructorBody(node, visitor: this); | |
| } | |
| @override | |
| void visitReceiverPropertyAssignmentTarget( | |
| covariant ReceiverPropertyAssignmentTargetImpl node, | |
| ) { | |
| node.visitChildren2(this); | |
| } | |
| @override | |
| void visitRecordTypeAnnotation(covariant RecordTypeAnnotationImpl node) { | |
| node.visitChildren2(this); | |
| _recordTypeResolver.resolve(node); | |
| } | |
| @override | |
| void visitRegularFormalParameter(covariant RegularFormalParameterImpl node) { | |
| var fragment = node.declaredFragment!; | |
| var element = fragment.element; | |
| if (node.functionTypedSuffix != null) { | |
| _scopeContext.visitFormalParameter(node, visitor: this); | |
| } else { | |
| node.visitChildren2(this); | |
| } | |
| var explicitFragmentType = _setExplicitFormalParameterType(node); | |
| if (fragment.previousFragment == null) { | |
| if (explicitFragmentType != null) { | |
| element.type = explicitFragmentType; | |
| } else if (node.type != null) { | |
| element.type = _typeProvider.dynamicType; | |
| } else if (element.type is InvalidTypeImpl) { | |
| // TODO(scheglov): review and improve resolution to not rely on dynamic. | |
| element.type = _typeProvider.dynamicType; | |
| } | |
| } | |
| } | |
| @override | |
| void visitShowCombinator(ShowCombinator node) { | |
| var scope = nameScope.tryCast<LibraryFragmentScope>(); | |
| scope?.importsTrackingActive(false); | |
| try { | |
| super.visitShowCombinator(node); | |
| } finally { | |
| scope?.importsTrackingActive(true); | |
| } | |
| } | |
| @override | |
| void visitSuperFormalParameter(covariant SuperFormalParameterImpl node) { | |
| _scopeContext.visitFormalParameter(node, visitor: this); | |
| _setExplicitFormalParameterType(node); | |
| } | |
| @override | |
| void visitSwitchCase(SwitchCase node) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitSwitchDefault(SwitchDefault node) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitSwitchExpression(covariant SwitchExpressionImpl node) { | |
| node.expression2.accept2(this); | |
| for (var case_ in node.cases) { | |
| _resolveGuardedPattern( | |
| case_.guardedPattern, | |
| then: () { | |
| case_.nameScope = nameScope; | |
| case_.expression2.accept2(this); | |
| }, | |
| ); | |
| } | |
| } | |
| @override | |
| void visitSwitchExpressionCase(SwitchExpressionCase node) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitSwitchPatternCase(SwitchPatternCase node) { | |
| throw StateError('Should not be invoked'); | |
| } | |
| @override | |
| void visitSwitchStatement(covariant SwitchStatementImpl node) { | |
| var labelScope = _labelScope; | |
| for (var member in node.members) { | |
| labelScope = _nestLabelScopes(labelScope, member.labels, member); | |
| } | |
| _withUnlabeledBreakContinueContextNested(node, () { | |
| _withLabelScope(labelScope, () { | |
| node.expression2.accept2(this); | |
| for (var group in node.memberGroups) { | |
| _patternVariables.switchStatementSharedCaseScopeStart(group); | |
| for (var member in group.members) { | |
| if (member is SwitchCaseImpl) { | |
| member.expression2.accept2(this); | |
| } else if (member is SwitchDefaultImpl) { | |
| _patternVariables.switchStatementSharedCaseScopeEmpty(group); | |
| } else if (member is SwitchPatternCaseImpl) { | |
| _resolveGuardedPattern( | |
| member.guardedPattern, | |
| sharedCaseScopeKey: group, | |
| ); | |
| } else { | |
| throw UnimplementedError('(${member.runtimeType}) $member'); | |
| } | |
| } | |
| if (group.hasLabels) { | |
| _patternVariables.switchStatementSharedCaseScopeEmpty(group); | |
| } | |
| group.variables = _patternVariables | |
| .switchStatementSharedCaseScopeFinish(group); | |
| _scopeContext.withLocalScope((scope) { | |
| group.members.lastOrNull?.nameScope = scope; | |
| _defineLocalElements(scope, group.statements); | |
| scope.addAll(group.variables.values); | |
| group.statements.accept2(this); | |
| }); | |
| } | |
| }); | |
| }); | |
| } | |
| @override | |
| void visitTopLevelGetterDeclaration( | |
| covariant TopLevelGetterDeclarationImpl node, | |
| ) { | |
| _scopeContext.visitTopLevelGetterDeclaration(node, visitor: this); | |
| } | |
| @override | |
| void visitTypeParameter(covariant TypeParameterImpl node) { | |
| var fragment = node.declaredFragment!; | |
| node.metadata.accept2(this); | |
| var boundNode = node.bound; | |
| if (boundNode != null) { | |
| boundNode.accept2(this); | |
| if (fragment.previousFragment == null) { | |
| fragment.element.bound = boundNode.type; | |
| } | |
| } | |
| } | |
| @override | |
| void visitUnqualifiedFunctionInvocation( | |
| covariant UnqualifiedFunctionInvocationImpl node, | |
| ) { | |
| var lookup = node.scopeLookupResult ??= nameScope.lookup(node.name.lexeme); | |
| if (lookup.getter case JoinPatternVariableElementImpl element) { | |
| element.references.add(node.name); | |
| } | |
| node.visitChildren2(this); | |
| } | |
| @override | |
| void visitUnqualifiedNameAssignmentTarget( | |
| covariant UnqualifiedNameAssignmentTargetImpl node, | |
| ) { | |
| var scopeLookupResult = nameScope.lookup(node.name.lexeme); | |
| node.scopeLookupResult = scopeLookupResult; | |
| if (scopeLookupResult.getter case JoinPatternVariableElementImpl element) { | |
| element.references.add(node.name); | |
| } | |
| _recordUnqualifiedWrite(scopeLookupResult, node); | |
| } | |
| @override | |
| void visitUnqualifiedNameExpression( | |
| covariant UnqualifiedNameExpressionImpl node, | |
| ) { | |
| var scopeLookupResult = node.scopeLookupResult ??= nameScope.lookup( | |
| node.name.lexeme, | |
| ); | |
| if (scopeLookupResult.getter case JoinPatternVariableElementImpl element) { | |
| element.references.add(node.name); | |
| } | |
| } | |
| @override | |
| void visitVariableDeclaration(covariant VariableDeclarationImpl node) { | |
| var element = node.declaredFragment!.element; | |
| if (element is LocalVariableElementImpl) { | |
| var varList = node.parent2 as VariableDeclarationListImpl; | |
| if (varList.type case var typeNode?) { | |
| element.type = typeNode.typeOrThrow; | |
| } else { | |
| // TODO(scheglov): review and improve resolution to not rely on dynamic. | |
| element.type = _typeProvider.dynamicType; | |
| } | |
| } | |
| node.initializer2?.accept2(this); | |
| } | |
| @override | |
| void visitVariableDeclarationList( | |
| covariant VariableDeclarationListImpl node, | |
| ) { | |
| _scopeContext.visitVariableDeclarationList(node, visitor: this); | |
| } | |
| @override | |
| void visitWhileStatement(covariant WhileStatementImpl node) { | |
| _withUnlabeledBreakContinueContextNested(node, () { | |
| node.condition2.accept2(this); | |
| _visitStatementInScope(node.body); | |
| }); | |
| } | |
| @override | |
| void visitWithClause(covariant WithClauseImpl node) { | |
| _resolveWithClause(declaration: node.parent2 as Declaration?, clause: node); | |
| } | |
| List<BindPatternVariableElementImpl> _computeDeclaredPatternVariables( | |
| DartPatternImpl pattern, | |
| ) { | |
| var variables = _computePatternVariables(pattern); | |
| return variables.values | |
| .whereType<BindPatternVariableElementImpl>() | |
| .toList(); | |
| } | |
| Map<String, PatternVariableElementImpl> _computePatternVariables( | |
| DartPatternImpl pattern, { | |
| Object? sharedCaseScopeKey, | |
| }) { | |
| _patternVariables.casePatternStart(); | |
| pattern.accept2(_PatternVariableBinderVisitor(_patternVariables)); | |
| return _patternVariables.casePatternFinish( | |
| sharedCaseScopeKey: sharedCaseScopeKey, | |
| ); | |
| } | |
| void _defineLocalElements(LocalScope scope, List<Statement> statements) { | |
| for (var statement in statements) { | |
| statement = statement.unlabeled; | |
| switch (statement) { | |
| case FunctionDeclarationStatementImpl(): | |
| var declaration = statement.functionDeclaration; | |
| var element = declaration.declaredFragment!.element; | |
| scope.add(element); | |
| case PatternVariableDeclarationStatementImpl(): | |
| var declaration = statement.declaration; | |
| _definePatternVariableDeclarationElements(scope, declaration); | |
| case VariableDeclarationStatementImpl(): | |
| scope.addAll(statement.variables.declaredElements); | |
| } | |
| } | |
| } | |
| void _definePatternVariableDeclarationElements( | |
| LocalScope scope, | |
| PatternVariableDeclarationImpl declaration, | |
| ) { | |
| var pattern = declaration.pattern; | |
| var variables = _computeDeclaredPatternVariables(pattern); | |
| declaration.elements = variables; | |
| scope.addAll(variables); | |
| } | |
| NullabilitySuffix _getNullability(bool hasQuestion) { | |
| if (hasQuestion) { | |
| return NullabilitySuffix.question; | |
| } else { | |
| return NullabilitySuffix.none; | |
| } | |
| } | |
| AstNode? _lookupBreakOrContinueTarget( | |
| AstNode parentNode, | |
| LabelReferenceImpl? labelNode, { | |
| required bool isContinue, | |
| }) { | |
| if (labelNode == null) { | |
| return isContinue | |
| ? _unlabeledBreakContinueContext.continueTarget | |
| : _unlabeledBreakContinueContext.breakTarget; | |
| } else { | |
| var labelName = labelNode.name.lexeme; | |
| var definingScope = _labelScope?.lookup(labelName); | |
| if (definingScope == null) { | |
| _diagnosticReporter.report( | |
| diag.labelUndefined.withArguments(name: labelName).at(labelNode), | |
| ); | |
| return null; | |
| } | |
| labelNode.element = definingScope.element; | |
| if (_enclosingClosure case var enclosingClosure?) { | |
| var labelFragment = definingScope.element.firstFragment; | |
| var labelContainer = labelFragment.enclosingFragment; | |
| if (!identical(labelContainer, enclosingClosure.firstFragment)) { | |
| _diagnosticReporter.report( | |
| diag.labelInOuterScope.withArguments(name: labelName).at(labelNode), | |
| ); | |
| } | |
| } | |
| var node = definingScope.node; | |
| if (isContinue && | |
| node is! DoStatement && | |
| node is! ForStatement && | |
| node is! SwitchMember && | |
| node is! WhileStatement) { | |
| _diagnosticReporter.report(diag.continueLabelInvalid.at(parentNode)); | |
| } | |
| return node; | |
| } | |
| } | |
| LabelScope? _nestLabelScopes( | |
| LabelScope? outer, | |
| List<Label> labels, | |
| AstNode node, | |
| ) { | |
| var current = outer; | |
| for (var label in labels) { | |
| var labelElement = label.declaredFragment!.element; | |
| current = LabelScope(current, labelElement, node); | |
| } | |
| return current; | |
| } | |
| void _recordUnqualifiedWrite( | |
| ScopeLookupResult scopeLookupResult, | |
| SyntacticEntity errorEntity, | |
| ) { | |
| var element = scopeLookupResult.getter; | |
| if (element is PromotableElementImpl) { | |
| _localVariableInfo.potentiallyMutatedInScope.add(element); | |
| if (element is PatternVariableElementImpl && | |
| element.isVisitingWhenClause) { | |
| _diagnosticReporter.report( | |
| diag.patternVariableAssignmentInsideGuard.at(errorEntity), | |
| ); | |
| } | |
| } | |
| } | |
| void _resolveGuardedPattern( | |
| GuardedPatternImpl guardedPattern, { | |
| Object? sharedCaseScopeKey, | |
| void Function()? then, | |
| }) { | |
| var variables = _computePatternVariables( | |
| guardedPattern.pattern, | |
| sharedCaseScopeKey: sharedCaseScopeKey, | |
| ); | |
| // Matched variables are available in `whenClause`. | |
| _scopeContext.withLocalScope((scope) { | |
| scope.addAll(variables.values); | |
| guardedPattern.variables = variables; | |
| guardedPattern.pattern.accept2(this); | |
| for (var variable in variables.values) { | |
| variable.isVisitingWhenClause = true; | |
| } | |
| try { | |
| guardedPattern.whenClause?.accept2(this); | |
| } finally { | |
| for (var variable in variables.values) { | |
| variable.isVisitingWhenClause = false; | |
| } | |
| } | |
| if (then != null) { | |
| then(); | |
| } | |
| }); | |
| } | |
| void _resolveImplementsClause({ | |
| required Declaration? declaration, | |
| required ImplementsClauseImpl? clause, | |
| }) { | |
| if (clause == null) return; | |
| _resolveTypes( | |
| declaration: declaration, | |
| clause: clause, | |
| namedTypes: clause.interfaces, | |
| ); | |
| } | |
| void _resolveMixinOnClause({ | |
| required Declaration? declaration, | |
| required MixinOnClauseImpl clause, | |
| }) { | |
| _resolveTypes( | |
| declaration: declaration, | |
| clause: clause, | |
| namedTypes: clause.superclassConstraints, | |
| ); | |
| } | |
| /// Resolves the given [namedType], reports errors if the resulting type | |
| /// is not valid in the context of the [declaration] and [clause]. | |
| void _resolveType({ | |
| required Declaration? declaration, | |
| required AstNode? clause, | |
| required NamedTypeImpl namedType, | |
| }) { | |
| _namedTypeResolver.classHierarchy_namedType = namedType; | |
| visitNamedType(namedType); | |
| _namedTypeResolver.classHierarchy_namedType = null; | |
| if (_namedTypeResolver.hasErrorReported) { | |
| return; | |
| } | |
| var type = namedType.typeOrThrow; | |
| var enclosingElement = _namedTypeResolver.enclosingClass; | |
| if (enclosingElement is ExtensionTypeElementImpl) { | |
| _verifyExtensionElementImplements(enclosingElement, namedType, type); | |
| return; | |
| } | |
| if (namedType.isSynthetic) { | |
| return; | |
| } | |
| switch (type.element) { | |
| case ClassElement(): | |
| return; | |
| case MixinElement(): | |
| if (clause is ImplementsClause || | |
| clause is MixinOnClause || | |
| clause is WithClause) { | |
| return; | |
| } | |
| } | |
| if (_libraryFragment.shouldIgnoreUndefinedNamedType(namedType)) { | |
| return; | |
| } | |
| LocatableDiagnostic? diagnosticCode; | |
| switch (clause) { | |
| case null: | |
| if (declaration is ClassTypeAlias) { | |
| diagnosticCode = diag.mixinWithNonClassSuperclass; | |
| } | |
| case ExtendsClause(): | |
| if (declaration is ClassDeclaration) { | |
| diagnosticCode = declaration.withClause == null | |
| ? diag.extendsNonClass | |
| : diag.mixinWithNonClassSuperclass; | |
| } | |
| case ImplementsClause(): | |
| diagnosticCode = diag.implementsNonClass; | |
| case MixinOnClause(): | |
| diagnosticCode = diag.mixinSuperClassConstraintNonInterface; | |
| case WithClause(): | |
| diagnosticCode = diag.mixinOfNonClass; | |
| } | |
| // Should not happen. | |
| if (diagnosticCode == null) { | |
| assert(false); | |
| return; | |
| } | |
| var firstToken = namedType.importPrefix?.name ?? namedType.name; | |
| var offset = firstToken.offset; | |
| var length = namedType.name.end - offset; | |
| _diagnosticReporter.report( | |
| diagnosticCode.atOffset(offset: offset, length: length), | |
| ); | |
| } | |
| /// Resolve the types in the given list of type names. | |
| /// | |
| /// @param typeNames the type names to be resolved | |
| /// @param nonTypeError the error to produce if the type name is defined to be | |
| /// something other than a type | |
| /// @param enumTypeError the error to produce if the type name is defined to | |
| /// be an enum | |
| /// @param dynamicTypeError the error to produce if the type name is "dynamic" | |
| /// @return an array containing all of the types that were resolved. | |
| void _resolveTypes({ | |
| required Declaration? declaration, | |
| required AstNode clause, | |
| required NodeList<NamedTypeImpl> namedTypes, | |
| }) { | |
| for (var namedType in namedTypes) { | |
| _resolveType( | |
| declaration: declaration, | |
| clause: clause, | |
| namedType: namedType, | |
| ); | |
| } | |
| } | |
| void _resolveWithClause({ | |
| required Declaration? declaration, | |
| required WithClauseImpl? clause, | |
| }) { | |
| if (clause == null) return; | |
| for (var namedType in clause.mixinTypes) { | |
| _namedTypeResolver.withClause_namedType = namedType; | |
| _resolveType( | |
| declaration: declaration, | |
| clause: clause, | |
| namedType: namedType, | |
| ); | |
| _namedTypeResolver.withClause_namedType = null; | |
| } | |
| } | |
| TypeImpl? _setExplicitFormalParameterType(FormalParameterImpl node) { | |
| if (node.functionTypedSuffix case var functionTypedSuffix?) { | |
| return node.explicitFragmentType = FunctionTypeImpl( | |
| typeParameters: [ | |
| for (var typeParameter | |
| in functionTypedSuffix.typeParameters?.typeParameters ?? | |
| const <TypeParameterImpl>[]) | |
| typeParameter.declaredFragment!.element, | |
| ], | |
| formalParameters: [ | |
| for (var parameter | |
| in functionTypedSuffix.formalParameters.allFormalParameters) | |
| parameter.declaredFragment!.element, | |
| ], | |
| returnType: node.type?.type ?? _typeProvider.dynamicType, | |
| nullabilitySuffix: _getNullability( | |
| functionTypedSuffix.question != null, | |
| ), | |
| ); | |
| } | |
| if (node.type case var type?) { | |
| return node.explicitFragmentType = type.type; | |
| } | |
| return node.explicitFragmentType = null; | |
| } | |
| void _verifyExtensionElementImplements( | |
| ExtensionTypeElementImpl declaredElement, | |
| NamedTypeImpl node, | |
| TypeImpl type, | |
| ) { | |
| var typeSystem = _libraryElement.typeSystem; | |
| if (!typeSystem.isValidExtensionTypeSuperinterface(type)) { | |
| _diagnosticReporter.report( | |
| diag.extensionTypeImplementsDisallowedType | |
| .withArguments(type: type) | |
| .at(node), | |
| ); | |
| return; | |
| } | |
| var declaredRepresentation = declaredElement.representation.type; | |
| if (typeSystem.isSubtypeOf(declaredRepresentation, type)) { | |
| return; | |
| } | |
| // When `type` is an extension type. | |
| if (type is InterfaceTypeImpl) { | |
| var implementedRepresentation = type.representationType; | |
| if (implementedRepresentation != null) { | |
| if (!typeSystem.isSubtypeOf( | |
| declaredRepresentation, | |
| implementedRepresentation, | |
| )) { | |
| _diagnosticReporter.report( | |
| diag.extensionTypeImplementsRepresentationNotSupertype | |
| .withArguments( | |
| implementedRepresentationType: implementedRepresentation, | |
| implementedExtensionTypeName: type.element.name ?? '', | |
| representationType: declaredRepresentation, | |
| extensionTypeName: declaredElement.name ?? '', | |
| ) | |
| .at(node), | |
| ); | |
| } | |
| return; | |
| } | |
| } | |
| _diagnosticReporter.report( | |
| diag.extensionTypeImplementsNotSupertype | |
| .withArguments(type: type, representationType: declaredRepresentation) | |
| .at(node), | |
| ); | |
| } | |
| void _visitForLoopParts(LocalScope scope, ForLoopPartsImpl node) { | |
| switch (node) { | |
| case ForEachPartsWithDeclarationImpl(): | |
| node.iterable2.accept2(this); | |
| var element = node.loopVariable.declaredFragment!.element; | |
| scope.add(element); | |
| node.loopVariable.accept2(this); | |
| case ForEachPartsWithIdentifierImpl(): | |
| node.iterable2.accept2(this); | |
| node.target.accept2(this); | |
| case ForEachPartsWithPatternImpl(): | |
| node.iterable2.accept2(this); | |
| var variables = _computeDeclaredPatternVariables(node.pattern); | |
| node.variables = variables; | |
| scope.addAll(variables); | |
| node.pattern.accept2(this); | |
| node.metadata.accept2(this); | |
| case ForPartsWithDeclarationsImpl(): | |
| scope.addAll(node.variables.declaredElements); | |
| node.variables.accept2(this); | |
| node.condition2?.accept2(this); | |
| node.updaters2.accept2(this); | |
| case ForPartsWithExpressionImpl(): | |
| node.initialization2?.accept2(this); | |
| node.condition2?.accept2(this); | |
| node.updaters2.accept2(this); | |
| case ForPartsWithPatternImpl(): | |
| _definePatternVariableDeclarationElements(scope, node.variables); | |
| node.variables.accept2(this); | |
| node.condition2?.accept2(this); | |
| node.updaters2.accept2(this); | |
| } | |
| } | |
| void _visitParsedExpression(ParsedExpressionImpl node) { | |
| switch (_astRewriter.parsedExpression(nameScope, node)) { | |
| case PreparedCascadeInvocation(:var valueArguments): | |
| valueArguments.cascadeInvocationParts!.typeArguments?.accept2(this); | |
| valueArguments.argumentList.accept2(this); | |
| case PreparedDotShorthandInvocation(:var valueArguments): | |
| valueArguments.dotShorthandInvocationParts!.typeArguments?.accept2( | |
| this, | |
| ); | |
| valueArguments.argumentList.accept2(this); | |
| case PreparedReceiverInvocation( | |
| :var receiver, | |
| :var typeArguments, | |
| :var valueArguments, | |
| ): | |
| if (receiver is ExpressionImpl) { | |
| receiver.accept2(this); | |
| } | |
| typeArguments?.accept2(this); | |
| valueArguments.argumentList.accept2(this); | |
| case RewrittenParsedExpression(:var expression): | |
| expression.accept2(this); | |
| } | |
| } | |
| /// Visits [statement], ensuring that if it is a block it is visited as such, | |
| /// and if it is not, it is wrapped in an implicit block scope. | |
| /// | |
| /// This implements the requirement from the specification that sub-statements | |
| /// of control flow statements (like `if`, `while`, `do`, `for`) introduce | |
| /// a new scope, even if they are not explicitly blocks. | |
| void _visitStatementInScope(StatementImpl? statement) { | |
| if (statement != null) { | |
| if (statement is BlockImpl) { | |
| visitBlock(statement); | |
| } else { | |
| _scopeContext.withLocalScope((scope) { | |
| _defineLocalElements(scope, [statement]); | |
| statement.accept2(this); | |
| }); | |
| } | |
| } | |
| } | |
| void _withEnclosingClosure( | |
| LocalFunctionElement? scope, | |
| void Function() callback, | |
| ) { | |
| var previous = _enclosingClosure; | |
| try { | |
| _enclosingClosure = scope; | |
| callback(); | |
| } finally { | |
| _enclosingClosure = previous; | |
| } | |
| } | |
| void _withLabelScope(LabelScope? scope, void Function() callback) { | |
| var previous = _labelScope; | |
| try { | |
| _labelScope = scope; | |
| callback(); | |
| } finally { | |
| _labelScope = previous; | |
| } | |
| } | |
| void _withUnlabeledBreakContinueContext( | |
| UnlabeledBreakContinueContext context, | |
| void Function() callback, | |
| ) { | |
| var previous = _unlabeledBreakContinueContext; | |
| try { | |
| _unlabeledBreakContinueContext = context; | |
| callback(); | |
| } finally { | |
| _unlabeledBreakContinueContext = previous; | |
| } | |
| } | |
| void _withUnlabeledBreakContinueContextNested( | |
| Statement statement, | |
| void Function() callback, | |
| ) { | |
| _withUnlabeledBreakContinueContext( | |
| _unlabeledBreakContinueContext.nest(statement), | |
| callback, | |
| ); | |
| } | |
| /// We always build local elements for [VariableDeclarationStatement]s and | |
| /// [FunctionDeclarationStatement]s in blocks, because invalid code might try | |
| /// to use forward references. | |
| /// Associate each of the annotation [nodes] with the corresponding | |
| /// [ElementAnnotation] in [annotations]. | |
| static void _setElementAnnotations( | |
| List<AnnotationImpl> nodes, | |
| List<ElementAnnotationImpl> annotations, | |
| ) { | |
| int nodeCount = nodes.length; | |
| if (nodeCount != annotations.length) { | |
| throw StateError( | |
| 'Found $nodeCount annotation nodes and ' | |
| '${annotations.length} element annotations', | |
| ); | |
| } | |
| for (int i = 0; i < nodeCount; i++) { | |
| nodes[i].elementAnnotation = annotations[i]; | |
| } | |
| } | |
| } | |
| class _PatternVariableBinderVisitor extends ThrowingAstVisitor2<void> { | |
| final VariableBinder<DartPatternImpl, PatternVariableElementImpl> _binder; | |
| _PatternVariableBinderVisitor(this._binder); | |
| @override | |
| void visitAssignedVariablePattern(AssignedVariablePattern node) {} | |
| @override | |
| void visitCastPattern(CastPattern node) { | |
| node.pattern.accept2(this); | |
| } | |
| @override | |
| void visitConstantPattern(ConstantPattern node) {} | |
| @override | |
| void visitDeclaredVariablePattern( | |
| covariant DeclaredVariablePatternImpl node, | |
| ) { | |
| var element = node.declaredFragment!.element; | |
| _binder.add(node.name.lexeme, element); | |
| } | |
| @override | |
| void visitListPattern(ListPattern node) { | |
| node.elements.accept2(this); | |
| } | |
| @override | |
| void visitLogicalAndPattern(LogicalAndPattern node) { | |
| node.leftOperand.accept2(this); | |
| node.rightOperand.accept2(this); | |
| } | |
| @override | |
| void visitLogicalOrPattern(covariant LogicalOrPatternImpl node) { | |
| _binder.logicalOrPatternStart(); | |
| node.leftOperand.accept2(this); | |
| _binder.logicalOrPatternFinishLeft(); | |
| node.rightOperand.accept2(this); | |
| _binder.logicalOrPatternFinish(node); | |
| } | |
| @override | |
| void visitMapPattern(MapPattern node) { | |
| node.elements.accept2(this); | |
| } | |
| @override | |
| void visitMapPatternEntry(MapPatternEntry node) { | |
| node.value.accept2(this); | |
| } | |
| @override | |
| void visitNullAssertPattern(NullAssertPattern node) { | |
| node.pattern.accept2(this); | |
| } | |
| @override | |
| void visitNullCheckPattern(NullCheckPattern node) { | |
| node.pattern.accept2(this); | |
| } | |
| @override | |
| void visitObjectPattern(ObjectPattern node) { | |
| node.fields.accept2(this); | |
| } | |
| @override | |
| void visitParenthesizedPattern(ParenthesizedPattern node) { | |
| node.pattern.accept2(this); | |
| } | |
| @override | |
| void visitPatternField(PatternField node) { | |
| node.pattern.accept2(this); | |
| } | |
| @override | |
| void visitRecordPattern(RecordPattern node) { | |
| node.fields.accept2(this); | |
| } | |
| @override | |
| void visitRelationalPattern(RelationalPattern node) {} | |
| @override | |
| void visitRestPatternElement(RestPatternElement node) { | |
| node.pattern?.accept2(this); | |
| } | |
| @override | |
| void visitWildcardPattern(WildcardPattern node) {} | |
| } | |
| class _VariableBinder | |
| extends VariableBinder<DartPatternImpl, PatternVariableElementImpl> { | |
| final TypeProvider typeProvider; | |
| _VariableBinder({required super.errors, required this.typeProvider}); | |
| @override | |
| JoinPatternVariableElementImpl joinPatternVariables({ | |
| required Object key, | |
| required List<PatternVariableElementImpl> components, | |
| required shared.JoinedPatternVariableInconsistency inconsistency, | |
| }) { | |
| var first = components.first; | |
| List<PatternVariableElementImpl> expandedVariables; | |
| if (key is LogicalOrPatternImpl) { | |
| expandedVariables = components | |
| .expand((variable) { | |
| if (variable is JoinPatternVariableElementImpl) { | |
| return variable.variables; | |
| } else { | |
| return [variable]; | |
| } | |
| }) | |
| .toList(growable: false); | |
| } else if (key is SwitchStatementCaseGroup) { | |
| expandedVariables = components; | |
| } else { | |
| throw UnimplementedError('(${key.runtimeType}) $key'); | |
| } | |
| var resultFragment = JoinPatternVariableFragmentImpl( | |
| name: first.name, | |
| firstTokenOffset: null, | |
| variables: expandedVariables.map((e) => e.firstFragment).toList(), | |
| inconsistency: inconsistency.maxWithAll( | |
| components.whereType<JoinPatternVariableElementImpl>().map( | |
| (e) => e.inconsistency, | |
| ), | |
| ), | |
| ); | |
| resultFragment.enclosingFragment = first.firstFragment.enclosingFragment; | |
| return resultFragment.element; | |
| } | |
| } | |
| class _VariableBinderErrors | |
| implements | |
| VariableBinderErrors<DartPatternImpl, PatternVariableElementImpl> { | |
| final ScopeAnalyzer scopeAnalyzer; | |
| _VariableBinderErrors(this.scopeAnalyzer); | |
| @override | |
| void assertInErrorRecovery() { | |
| // TODO(scheglov): implement assertInErrorRecovery | |
| throw UnimplementedError(); | |
| } | |
| @override | |
| void duplicateVariablePattern({ | |
| required String name, | |
| required covariant BindPatternVariableElementImpl original, | |
| required covariant BindPatternVariableElementImpl duplicate, | |
| }) { | |
| scopeAnalyzer._diagnosticReporter.report( | |
| DiagnosticFactory().duplicateDefinitionForNodes( | |
| scopeAnalyzer._diagnosticReporter.source, | |
| diag.duplicateVariablePattern.withArguments(name: name), | |
| duplicate.node.name, | |
| original.node.name, | |
| ), | |
| ); | |
| duplicate.isDuplicate = true; | |
| } | |
| @override | |
| void logicalOrPatternBranchMissingVariable({ | |
| required covariant LogicalOrPatternImpl node, | |
| required bool hasInLeft, | |
| required String name, | |
| required PromotableElementImpl variable, | |
| }) { | |
| scopeAnalyzer._diagnosticReporter.report( | |
| diag.missingVariablePattern | |
| .withArguments(name: name) | |
| .at(hasInLeft ? node.rightOperand : node.leftOperand), | |
| ); | |
| } | |
| } | |
| extension _VariableDeclarationList on VariableDeclarationList { | |
| List<LocalVariableElementImpl> get declaredElements { | |
| return variables | |
| .map((v) => v.declaredFragment!.element) | |
| .cast<LocalVariableElementImpl>() | |
| .toList(); | |
| } | |
| } |