blob: 12d1a56698072db509a7cfcc0ed8b8b4051b1069 [file] [log] [blame]
// 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.');
}
}