| // 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:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/summary2/bundle_reader.dart'; |
| import 'package:analyzer/src/summary2/linked_element_factory.dart'; |
| import 'package:analyzer/src/summary2/linked_library_context.dart'; |
| import 'package:analyzer/src/summary2/reference.dart'; |
| |
| /// The context of a unit - the context of the bundle, and the unit tokens. |
| class LinkedUnitContext { |
| final LinkedLibraryContext libraryContext; |
| final int indexInLibrary; |
| final String? partUriStr; |
| final String uriStr; |
| final Reference reference; |
| final bool isSynthetic; |
| final CompilationUnit? unit; |
| final UnitReader? unitReader; |
| |
| bool _hasDirectivesRead = false; |
| |
| LinkedUnitContext(this.libraryContext, this.indexInLibrary, this.partUriStr, |
| this.uriStr, this.reference, this.isSynthetic, |
| {required this.unit, required this.unitReader}); |
| |
| CompilationUnitElementImpl get element { |
| return reference.element as CompilationUnitElementImpl; |
| } |
| |
| LinkedElementFactory get elementFactory => libraryContext.elementFactory; |
| |
| bool get hasPartOfDirective { |
| for (var directive in unit_withDirectives.directives) { |
| if (directive is PartOfDirective) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Return `true` if this unit is a part of a bundle that is being linked. |
| bool get isLinking => unitReader == null; |
| |
| TypeProvider get typeProvider { |
| var libraryReference = libraryContext.reference; |
| var libraryElement = libraryReference.element as LibraryElementImpl; |
| return libraryElement.typeProvider; |
| } |
| |
| CompilationUnit get unit_withDeclarations { |
| unitReader?.readDeclarations(); |
| return unit!; |
| } |
| |
| /// Ensure that [unit] has directives ready (because we are linking, |
| /// and so always have full AST, or, if we are reading, we make sure |
| /// that we have them read). |
| CompilationUnit get unit_withDirectives { |
| if (unitReader != null && !_hasDirectivesRead) { |
| _hasDirectivesRead = true; |
| unitReader!.readDirectives(); |
| var libraryElement = |
| libraryContext.reference.element as LibraryElementImpl; |
| for (var directive in unit!.directives) { |
| if (directive is ExportDirective) { |
| if (directive.element == null) { |
| ExportElementImpl.forLinkedNode(libraryElement, directive); |
| } |
| } else if (directive is ImportDirective) { |
| if (directive.element == null) { |
| ImportElementImpl.forLinkedNode(libraryElement, directive); |
| } |
| } |
| } |
| } |
| return unit!; |
| } |
| |
| void applyResolution(AstNode node) { |
| if (node is VariableDeclaration) { |
| node = node.parent!.parent!; |
| } |
| if (node is HasAstLinkedContext) { |
| var astLinkedContext = (node as HasAstLinkedContext).linkedContext; |
| astLinkedContext?.applyResolution(this); |
| } |
| } |
| |
| void createGenericFunctionTypeElement(int id, GenericFunctionTypeImpl node) { |
| var containerRef = this.reference.getChild('@genericFunctionType'); |
| var reference = containerRef.getChild('$id'); |
| var element = GenericFunctionTypeElementImpl.forLinkedNode( |
| this.reference.element as ElementImpl, |
| reference, |
| node, |
| ); |
| node.declaredElement = element; |
| } |
| |
| int getCodeLength(AstNode node) { |
| if (node is HasAstLinkedContext) { |
| var linked = (node as HasAstLinkedContext).linkedContext; |
| return linked != null ? linked.codeLength : node.length; |
| } |
| |
| if (node is CompilationUnitImpl) { |
| var data = node.summaryData as SummaryDataForCompilationUnit?; |
| if (data != null) { |
| return data.codeLength; |
| } else { |
| return node.length; |
| } |
| } else if (node is EnumConstantDeclaration) { |
| return node.length; |
| } else if (node is FormalParameter) { |
| return node.length; |
| } else if (node is TypeParameter) { |
| return node.length; |
| } else if (node is VariableDeclaration) { |
| var parent2 = node.parent!.parent!; |
| var linked = (parent2 as HasAstLinkedContext).linkedContext!; |
| return linked.getVariableDeclarationCodeLength(node); |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| int getCodeOffset(AstNode node) { |
| if (node is HasAstLinkedContext) { |
| var linked = (node as HasAstLinkedContext).linkedContext; |
| return linked != null ? linked.codeOffset : node.offset; |
| } |
| |
| if (node is CompilationUnit) { |
| return 0; |
| } else if (node is EnumConstantDeclaration) { |
| return node.offset; |
| } else if (node is FormalParameter) { |
| return node.offset; |
| } else if (node is TypeParameter) { |
| return node.offset; |
| } else if (node is VariableDeclaration) { |
| var parent2 = node.parent!.parent!; |
| var linked = (parent2 as HasAstLinkedContext).linkedContext!; |
| return linked.getVariableDeclarationCodeOffset(node); |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| List<ConstructorInitializer> getConstructorInitializers( |
| ConstructorDeclaration node, |
| ) { |
| return node.initializers; |
| } |
| |
| ConstructorName? getConstructorRedirected(ConstructorDeclaration node) { |
| return node.redirectedConstructor; |
| } |
| |
| List<ConstructorDeclarationImpl> getConstructors(AstNode node) { |
| if (node is ClassOrMixinDeclaration) { |
| return _getClassOrExtensionOrMixinMembers(node) |
| .whereType<ConstructorDeclarationImpl>() |
| .toList(); |
| } |
| return const <ConstructorDeclarationImpl>[]; |
| } |
| |
| int getDirectiveOffset(Directive node) { |
| return node.keyword.offset; |
| } |
| |
| Comment? getDocumentationComment(AstNode node) { |
| if (node is HasAstLinkedContext) { |
| var linkedContext = (node as HasAstLinkedContext).linkedContext; |
| linkedContext?.readDocumentationComment(); |
| return (node as AnnotatedNode).documentationComment; |
| } else if (node is VariableDeclaration) { |
| return getDocumentationComment(node.parent!.parent!); |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| String getFieldFormalParameterName(AstNode node) { |
| if (node is DefaultFormalParameter) { |
| return getFieldFormalParameterName(node.parameter); |
| } else if (node is FieldFormalParameter) { |
| return node.identifier.name; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| List<VariableDeclaration> getFields(CompilationUnitMember node) { |
| var fields = <VariableDeclaration>[]; |
| var members = _getClassOrExtensionOrMixinMembers(node); |
| for (var member in members) { |
| if (member is FieldDeclaration) { |
| fields.addAll(member.fields.variables); |
| } |
| } |
| return fields; |
| } |
| |
| String getFormalParameterName(FormalParameter node) { |
| if (node is DefaultFormalParameter) { |
| return getFormalParameterName(node.parameter); |
| } else if (node is NormalFormalParameter) { |
| return node.identifier?.name ?? ''; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| List<FormalParameter>? getFormalParameters(AstNode node) { |
| if (node is ConstructorDeclaration) { |
| return node.parameters.parameters; |
| } else if (node is FunctionDeclaration) { |
| return getFormalParameters(node.functionExpression); |
| } else if (node is FunctionExpression) { |
| return node.parameters?.parameters; |
| } else if (node is FormalParameter) { |
| if (node is DefaultFormalParameter) { |
| return getFormalParameters(node.parameter); |
| } else if (node is FieldFormalParameter) { |
| return node.parameters?.parameters; |
| } else if (node is FunctionTypedFormalParameter) { |
| return node.parameters.parameters; |
| } else { |
| return null; |
| } |
| } else if (node is FunctionTypeAlias) { |
| return node.parameters.parameters; |
| } else if (node is GenericFunctionType) { |
| return node.parameters.parameters; |
| } else if (node is MethodDeclaration) { |
| return node.parameters?.parameters; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| ImplementsClause? getImplementsClause(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.implementsClause; |
| } else if (node is ClassTypeAlias) { |
| return node.implementsClause; |
| } else if (node is MixinDeclaration) { |
| return node.implementsClause; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| Expression? getInitializer(AstNode node) { |
| if (node is DefaultFormalParameter) { |
| return node.defaultValue; |
| } else if (node is VariableDeclaration) { |
| return node.initializer; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| LibraryLanguageVersion getLanguageVersion(CompilationUnit node) { |
| return (node as CompilationUnitImpl).languageVersion!; |
| } |
| |
| Comment? getLibraryDocumentationComment() { |
| for (var directive in unit_withDirectives.directives) { |
| if (directive is LibraryDirectiveImpl) { |
| var data = directive.summaryData as SummaryDataForLibraryDirective; |
| data.readDocumentationComment(); |
| return directive.documentationComment; |
| } |
| } |
| return null; |
| } |
| |
| List<Annotation> getLibraryMetadata() { |
| unit_withDirectives; |
| unitReader!.applyDirectivesResolution(this); |
| for (var directive in unit!.directives) { |
| if (directive is LibraryDirective) { |
| return directive.metadata; |
| } |
| } |
| return const <Annotation>[]; |
| } |
| |
| List<Annotation> getMetadata(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.metadata; |
| } else if (node is ClassTypeAlias) { |
| return node.metadata; |
| } else if (node is CompilationUnit) { |
| assert(node == unit); |
| if (indexInLibrary != 0) { |
| return _getPartDirectiveAnnotation(); |
| } else { |
| return const <Annotation>[]; |
| } |
| } else if (node is ConstructorDeclaration) { |
| return node.metadata; |
| } else if (node is DefaultFormalParameter) { |
| return getMetadata(node.parameter); |
| } else if (node is Directive) { |
| return node.metadata; |
| } else if (node is EnumConstantDeclaration) { |
| return node.metadata; |
| } else if (node is EnumDeclaration) { |
| return node.metadata; |
| } else if (node is ExtensionDeclaration) { |
| return node.metadata; |
| } else if (node is FormalParameter) { |
| return node.metadata; |
| } else if (node is FunctionDeclaration) { |
| return node.metadata; |
| } else if (node is FunctionTypeAlias) { |
| return node.metadata; |
| } else if (node is GenericTypeAlias) { |
| return node.metadata; |
| } else if (node is MethodDeclaration) { |
| return node.metadata; |
| } else if (node is MixinDeclaration) { |
| return node.metadata; |
| } else if (node is TypeParameter) { |
| return node.metadata; |
| } else if (node is VariableDeclaration) { |
| var parent2 = node.parent!.parent!; |
| if (parent2 is FieldDeclaration) { |
| return parent2.metadata; |
| } else if (parent2 is TopLevelVariableDeclaration) { |
| return parent2.metadata; |
| } |
| } |
| return const <Annotation>[]; |
| } |
| |
| List<MethodDeclaration> getMethods(CompilationUnitMember node) { |
| return _getClassOrExtensionOrMixinMembers(node) |
| .whereType<MethodDeclaration>() |
| .toList(); |
| } |
| |
| int getNameOffset(AstNode node) { |
| if (node is ConstructorDeclaration) { |
| if (node.name != null) { |
| return node.name!.offset; |
| } else { |
| return node.returnType.offset; |
| } |
| } else if (node is EnumConstantDeclaration) { |
| return node.name.offset; |
| } else if (node is ExtensionDeclaration) { |
| return node.name?.offset ?? -1; |
| } else if (node is FormalParameter) { |
| return node.identifier?.offset ?? -1; |
| } else if (node is MethodDeclaration) { |
| return node.name.offset; |
| } else if (node is NamedCompilationUnitMember) { |
| return node.name.offset; |
| } else if (node is TypeParameter) { |
| return node.name.offset; |
| } else if (node is VariableDeclaration) { |
| return node.name.offset; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| /// Return the actual return type for the [node] - explicit or inferred. |
| DartType getReturnType(AstNode node) { |
| if (node is GenericFunctionType) { |
| return node.returnType?.type ?? DynamicTypeImpl.instance; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| TypeName? getSuperclass(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.extendsClause?.superclass; |
| } else if (node is ClassTypeAlias) { |
| return node.superclass; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| TypeParameterList? getTypeParameters2(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.typeParameters; |
| } else if (node is ClassTypeAlias) { |
| return node.typeParameters; |
| } else if (node is ConstructorDeclaration) { |
| return null; |
| } else if (node is DefaultFormalParameter) { |
| return getTypeParameters2(node.parameter); |
| } else if (node is ExtensionDeclaration) { |
| return node.typeParameters; |
| } else if (node is FieldFormalParameter) { |
| return node.typeParameters; |
| } else if (node is FunctionDeclaration) { |
| return getTypeParameters2(node.functionExpression); |
| } else if (node is FunctionExpression) { |
| return node.typeParameters; |
| } else if (node is FunctionTypedFormalParameter) { |
| return node.typeParameters; |
| } else if (node is FunctionTypeAlias) { |
| return node.typeParameters; |
| } else if (node is GenericFunctionType) { |
| return node.typeParameters; |
| } else if (node is GenericTypeAlias) { |
| return node.typeParameters; |
| } else if (node is MethodDeclaration) { |
| return node.typeParameters; |
| } else if (node is MixinDeclaration) { |
| return node.typeParameters; |
| } else if (node is SimpleFormalParameter) { |
| return null; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| WithClause? getWithClause(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.withClause; |
| } else if (node is ClassTypeAlias) { |
| return node.withClause; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| bool hasImplicitReturnType(AstNode node) { |
| if (node is FunctionDeclaration) { |
| return node.returnType == null; |
| } |
| if (node is MethodDeclaration) { |
| return node.returnType == null; |
| } |
| return false; |
| } |
| |
| bool hasImplicitType(AstNode node) { |
| if (node is DefaultFormalParameter) { |
| return hasImplicitType(node.parameter); |
| } else if (node is SimpleFormalParameter) { |
| return node.type == null; |
| } else if (node is VariableDeclaration) { |
| var parent = node.parent as VariableDeclarationList; |
| return parent.type == null; |
| } |
| return false; |
| } |
| |
| bool hasInitializer(VariableDeclarationImpl node) { |
| return node.initializer != null || node.hasInitializer; |
| } |
| |
| bool isAbstract(AstNode node) { |
| if (node is ClassDeclaration) { |
| return node.isAbstract; |
| } else if (node is ClassTypeAlias) { |
| return node.isAbstract; |
| } else if (node is ConstructorDeclaration) { |
| return false; |
| } else if (node is FunctionDeclaration) { |
| return false; |
| } else if (node is MethodDeclaration) { |
| return node.isAbstract; |
| } else if (node is VariableDeclaration) { |
| var parent = node.parent; |
| if (parent is VariableDeclarationList) { |
| var grandParent = parent.parent; |
| if (grandParent is FieldDeclaration) { |
| return grandParent.abstractKeyword != null; |
| } else { |
| throw UnimplementedError('${grandParent.runtimeType}'); |
| } |
| } else { |
| throw UnimplementedError('${parent.runtimeType}'); |
| } |
| } else if (node is EnumConstantDeclaration) { |
| return false; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| bool isAsynchronous(AstNode node) { |
| if (node is ConstructorDeclaration) { |
| return false; |
| } else if (node is FunctionDeclaration) { |
| return isAsynchronous(node.functionExpression); |
| } else if (node is FunctionExpression) { |
| return node.body.isAsynchronous; |
| } else if (node is MethodDeclaration) { |
| return node.body.isAsynchronous; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isConst(AstNode node) { |
| if (node is FormalParameter) { |
| return node.isConst; |
| } |
| if (node is VariableDeclaration) { |
| var parent = node.parent as VariableDeclarationList; |
| return parent.isConst; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| bool isExplicitlyCovariant(AstNode node) { |
| if (node is DefaultFormalParameter) { |
| return isExplicitlyCovariant(node.parameter); |
| } else if (node is EnumConstantDeclaration) { |
| return false; |
| } else if (node is FormalParameter) { |
| return node.covariantKeyword != null; |
| } else if (node is VariableDeclaration) { |
| var parent2 = node.parent!.parent!; |
| return parent2 is FieldDeclaration && parent2.covariantKeyword != null; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isExternal(AstNode node) { |
| if (node is ConstructorDeclaration) { |
| return node.externalKeyword != null; |
| } else if (node is FunctionDeclaration) { |
| return node.externalKeyword != null; |
| } else if (node is MethodDeclaration) { |
| return node.externalKeyword != null || node.body is NativeFunctionBody; |
| } else if (node is VariableDeclaration) { |
| var parent = node.parent; |
| if (parent is VariableDeclarationList) { |
| var grandParent = parent.parent; |
| if (grandParent is FieldDeclaration) { |
| return grandParent.externalKeyword != null; |
| } else if (grandParent is TopLevelVariableDeclaration) { |
| return grandParent.externalKeyword != null; |
| } else { |
| throw UnimplementedError('${grandParent.runtimeType}'); |
| } |
| } else { |
| throw UnimplementedError('${parent.runtimeType}'); |
| } |
| } else if (node is EnumConstantDeclaration) { |
| return false; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isFinal(AstNode node) { |
| if (node is EnumConstantDeclaration) { |
| return false; |
| } |
| if (node is VariableDeclaration) { |
| var parent = node.parent as VariableDeclarationList; |
| return parent.isFinal; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| bool isGenerator(AstNode node) { |
| if (node is ConstructorDeclaration) { |
| return false; |
| } else if (node is FunctionDeclaration) { |
| return isGenerator(node.functionExpression); |
| } else if (node is FunctionExpression) { |
| return node.body.isGenerator; |
| } else if (node is MethodDeclaration) { |
| return node.body.isGenerator; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isGetter(AstNode node) { |
| if (node is FunctionDeclaration) { |
| return node.isGetter; |
| } else if (node is MethodDeclaration) { |
| return node.isGetter; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isLate(AstNode node) { |
| if (node is VariableDeclaration) { |
| return node.isLate; |
| } |
| if (node is EnumConstantDeclaration) { |
| return false; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| bool isNative(AstNode node) { |
| if (node is MethodDeclaration) { |
| return node.body is NativeFunctionBody; |
| } else { |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isSetter(AstNode node) { |
| if (node is FunctionDeclaration) { |
| return node.isSetter; |
| } else if (node is MethodDeclaration) { |
| return node.isSetter; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| |
| bool isStatic(AstNode node) { |
| if (node is FunctionDeclaration) { |
| return true; |
| } else if (node is MethodDeclaration) { |
| return node.modifierKeyword != null; |
| } else if (node is VariableDeclaration) { |
| var parent2 = node.parent!.parent!; |
| return parent2 is FieldDeclaration && parent2.isStatic; |
| } |
| throw UnimplementedError('${node.runtimeType}'); |
| } |
| |
| bool shouldBeConstFieldElement(AstNode node) { |
| if (node is VariableDeclaration) { |
| var variableList = node.parent as VariableDeclarationList; |
| if (variableList.isConst) return true; |
| |
| var fieldDeclaration = variableList.parent as FieldDeclaration; |
| if (fieldDeclaration.staticKeyword != null) return false; |
| |
| if (variableList.isFinal) { |
| var class_ = fieldDeclaration.parent; |
| if (class_ is ClassDeclaration) { |
| var hasLinkedContext = class_ as HasAstLinkedContext; |
| var linkedContext = hasLinkedContext.linkedContext; |
| // TODO(scheglov) Get rid of this check, exists only for linking. |
| // Maybe we should pre-create all elements before linking. |
| if (linkedContext != null) { |
| return linkedContext.isClassWithConstConstructor; |
| } else { |
| for (var member in class_.members) { |
| if (member is ConstructorDeclaration && |
| member.constKeyword != null) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| List<VariableDeclaration> topLevelVariables(CompilationUnit unit) { |
| var variables = <VariableDeclaration>[]; |
| for (var declaration in unit.declarations) { |
| if (declaration is TopLevelVariableDeclaration) { |
| variables.addAll(declaration.variables.variables); |
| } |
| } |
| return variables; |
| } |
| |
| List<ClassMember> _getClassOrExtensionOrMixinMembers( |
| CompilationUnitMember node, |
| ) { |
| var linkedContext = (node as HasAstLinkedContext).linkedContext; |
| if (linkedContext != null) { |
| return linkedContext.classMembers; |
| } else { |
| if (node is ClassDeclaration) { |
| return node.members; |
| } else if (node is ClassTypeAlias) { |
| return <ClassMember>[]; |
| } else if (node is ExtensionDeclaration) { |
| return node.members; |
| } else if (node is MixinDeclaration) { |
| return node.members; |
| } else { |
| throw StateError('${node.runtimeType}'); |
| } |
| } |
| } |
| |
| NodeList<Annotation> _getPartDirectiveAnnotation() { |
| var definingContext = libraryContext.definingUnit; |
| var definingUnit = definingContext.unit_withDirectives; |
| var partDirectiveIndex = 0; |
| for (var directive in definingUnit.directives) { |
| if (directive is PartDirective) { |
| partDirectiveIndex++; |
| if (partDirectiveIndex == indexInLibrary) { |
| return directive.metadata; |
| } |
| } |
| } |
| throw StateError('Expected to find $indexInLibrary part directive.'); |
| } |
| } |