blob: c3a3bf9efce720679b8ba242a3a9872195f1df85 [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/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/src/dart/ast/ast.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_system.dart';
import 'package:analyzer/src/summary2/function_type_builder.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linking_node_scope.dart';
import 'package:analyzer/src/summary2/named_type_builder.dart';
import 'package:analyzer/src/summary2/record_type_builder.dart';
import 'package:analyzer/src/summary2/types_builder.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
/// Recursive visitor of LinkedNodes that resolves explicit type annotations
/// in outlines. This includes resolving element references in identifiers
/// in type annotation, and setting LinkedNodeTypes for corresponding type
/// annotation nodes.
///
/// Declarations that have type annotations, e.g. return types of methods, get
/// the corresponding type set (so, if there is an explicit type annotation,
/// the type is set, otherwise we keep it empty, so we will attempt to infer
/// it later).
class ReferenceResolver extends ThrowingAstVisitor<void> {
final Linker linker;
final TypeSystemImpl typeSystem;
final NodesToBuildType nodesToBuildType;
Scope scope;
ReferenceResolver(
this.linker,
this.nodesToBuildType,
this.typeSystem,
this.scope,
);
@override
void visitAnnotation(covariant AnnotationImpl node) {
if (node.arguments != null) {
var identifier = node.name;
if (identifier is PrefixedIdentifierImpl) {
var prefixNode = identifier.prefix;
var prefixElement = scope.lookup(prefixNode.name).getter;
prefixNode.element = prefixElement;
if (prefixElement is PrefixElement) {
var name = identifier.identifier.name;
var element = prefixElement.scope.lookup(name).getter;
identifier.identifier.element = element;
}
} else if (identifier is SimpleIdentifierImpl) {
var element = scope.lookup(identifier.name).getter;
identifier.element = element;
return;
}
}
}
@override
void visitBlockFunctionBody(BlockFunctionBody node) {}
@override
void visitClassDeclaration(covariant ClassDeclarationImpl node) {
var outerScope = scope;
var element = node.declaredFragment!;
scope = TypeParameterScope(
scope,
element.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.extendsClause?.accept(this);
node.withClause?.accept(this);
node.implementsClause?.accept(this);
scope = InstanceScope(scope, element.asElement2);
LinkingNodeContext(node, scope);
node.members.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitClassTypeAlias(covariant ClassTypeAliasImpl node) {
var outerScope = scope;
var element = node.declaredFragment!;
scope = TypeParameterScope(
scope,
element.typeParameters.map((e) => e.asElement2).toList(),
);
LinkingNodeContext(node, scope);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.superclass.accept(this);
node.withClause.accept(this);
node.implementsClause?.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitCompilationUnit(CompilationUnit node) {
LinkingNodeContext(node, scope);
node.declarations.accept(this);
}
@override
void visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) {
var outerScope = scope;
var element = node.declaredFragment!.element;
scope = TypeParameterScope(scope, element.typeParameters);
LinkingNodeContext(node, scope);
node.metadata.accept(this);
node.parameters.accept(this);
scope = outerScope;
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
LinkingNodeContext(node, scope);
node.parameter.accept(this);
}
@override
void visitEnumDeclaration(covariant EnumDeclarationImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
var element = fragment.element;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.implementsClause?.accept(this);
node.withClause?.accept(this);
scope = InstanceScope(scope, element);
LinkingNodeContext(node, scope);
node.members.accept(this);
nodesToBuildType.addDeclaration(node);
for (var field in fragment.fields) {
var node = linker.elementNodes[field];
if (node != null) {
LinkingNodeContext(node, scope);
}
}
scope = outerScope;
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {}
@override
void visitExtendsClause(ExtendsClause node) {
node.superclass.accept(this);
}
@override
void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.onClause?.accept(this);
scope = ExtensionScope(scope, fragment.asElement2);
LinkingNodeContext(node, scope);
node.members.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitExtensionOnClause(ExtensionOnClause node) {
node.extendedType.accept(this);
}
@override
void visitExtensionTypeDeclaration(
covariant ExtensionTypeDeclarationImpl node,
) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.representation.accept(this);
node.implementsClause?.accept(this);
scope = InstanceScope(scope, fragment.asElement2);
LinkingNodeContext(node, scope);
LinkingNodeContext(node.representation, scope);
node.members.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
node.metadata.accept(this);
node.fields.accept(this);
}
@override
void visitFieldFormalParameter(covariant FieldFormalParameterImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.type?.accept(this);
node.typeParameters?.accept(this);
node.parameters?.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitFormalParameterList(FormalParameterList node) {
node.parameters.accept(this);
}
@override
void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
outerScope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
LinkingNodeContext(node, scope);
node.metadata.accept(this);
node.returnType?.accept(this);
node.functionExpression.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitFunctionExpression(FunctionExpression node) {
node.typeParameters?.accept(this);
node.parameters?.accept(this);
}
@override
void visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
outerScope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitFunctionTypedFormalParameter(
covariant FunctionTypedFormalParameterImpl node,
) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitGenericFunctionType(covariant GenericFunctionTypeImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
outerScope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters.accept(this);
var nullabilitySuffix = _getNullabilitySuffix(node.question != null);
var builder = FunctionTypeBuilder.of(node, nullabilitySuffix);
node.type = builder;
nodesToBuildType.addDeclaration(node);
nodesToBuildType.addTypeBuilder(builder);
scope = outerScope;
}
@override
void visitGenericTypeAlias(covariant GenericTypeAliasImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
outerScope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.type.accept(this);
nodesToBuildType.addDeclaration(node);
var aliasedType = node.type;
if (aliasedType is GenericFunctionTypeImpl) {
fragment.encloseElement(
aliasedType.declaredFragment as GenericFunctionTypeFragmentImpl,
);
}
scope = outerScope;
}
@override
void visitImplementsClause(ImplementsClause node) {
node.interfaces.accept(this);
}
@override
void visitMethodDeclaration(covariant MethodDeclarationImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
LinkingNodeContext(node, scope);
node.metadata.accept(this);
node.returnType?.accept(this);
node.typeParameters?.accept(this);
node.parameters?.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitMixinDeclaration(covariant MixinDeclarationImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.metadata.accept(this);
node.typeParameters?.accept(this);
node.onClause?.accept(this);
node.implementsClause?.accept(this);
scope = InstanceScope(scope, fragment.asElement2);
LinkingNodeContext(node, scope);
node.members.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitMixinOnClause(MixinOnClause node) {
node.superclassConstraints.accept(this);
}
@override
void visitNamedType(covariant NamedTypeImpl node) {
Element? element;
var importPrefix = node.importPrefix;
if (importPrefix != null) {
var prefixToken = importPrefix.name;
var prefixName = prefixToken.lexeme;
var prefixElement = scope.lookup(prefixName).getter;
importPrefix.element = prefixElement;
if (prefixElement is PrefixElement) {
var name = node.name.lexeme;
element = prefixElement.scope.lookup(name).getter;
}
} else {
var name = node.name.lexeme;
if (name == 'void') {
node.type = VoidTypeImpl.instance;
return;
}
element = scope.lookup(name).getter;
}
node.element = element;
node.typeArguments?.accept(this);
var nullabilitySuffix = _getNullabilitySuffix(node.question != null);
if (element == null) {
node.type = InvalidTypeImpl.instance;
} else if (element is TypeParameterElementImpl) {
node.type = TypeParameterTypeImpl(
element: element,
nullabilitySuffix: nullabilitySuffix,
);
} else {
var builder = NamedTypeBuilder.of(
linker: linker,
typeSystem: typeSystem,
node: node,
element: element,
nullabilitySuffix: nullabilitySuffix,
);
node.type = builder;
nodesToBuildType.addTypeBuilder(builder);
}
}
@override
void visitRecordTypeAnnotation(covariant RecordTypeAnnotationImpl node) {
node.positionalFields.accept(this);
node.namedFields?.accept(this);
var builder = RecordTypeBuilder.of(typeSystem, node);
node.type = builder;
nodesToBuildType.addTypeBuilder(builder);
}
@override
void visitRecordTypeAnnotationNamedField(
RecordTypeAnnotationNamedField node,
) {
node.type.accept(this);
}
@override
void visitRecordTypeAnnotationNamedFields(
RecordTypeAnnotationNamedFields node,
) {
node.fields.accept(this);
}
@override
void visitRecordTypeAnnotationPositionalField(
RecordTypeAnnotationPositionalField node,
) {
node.type.accept(this);
}
@override
void visitRepresentationDeclaration(RepresentationDeclaration node) {
node.fieldType.accept(this);
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
node.type?.accept(this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitSuperFormalParameter(covariant SuperFormalParameterImpl node) {
var outerScope = scope;
var fragment = node.declaredFragment!;
scope = TypeParameterScope(
scope,
fragment.typeParameters.map((e) => e.asElement2).toList(),
);
node.type?.accept(this);
node.typeParameters?.accept(this);
node.parameters?.accept(this);
nodesToBuildType.addDeclaration(node);
scope = outerScope;
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
node.metadata.accept(this);
node.variables.accept(this);
}
@override
void visitTypeArgumentList(TypeArgumentList node) {
node.arguments.accept(this);
}
@override
void visitTypeParameter(covariant TypeParameterImpl node) {
var bound = node.bound;
if (bound != null) {
bound.accept(this);
var fragment = node.declaredFragment!;
fragment.bound = bound.type;
nodesToBuildType.addDeclaration(node);
}
}
@override
void visitTypeParameterList(TypeParameterList node) {
node.typeParameters.accept(this);
}
@override
void visitVariableDeclarationList(
covariant VariableDeclarationListImpl node,
) {
node.type?.accept(this);
nodesToBuildType.addDeclaration(node);
for (var variable in node.variables) {
var fragment = variable.declaredFragment!;
var node = linker.elementNodes[fragment]!;
LinkingNodeContext(node, scope);
}
}
@override
void visitWithClause(WithClause node) {
node.mixinTypes.accept(this);
}
NullabilitySuffix _getNullabilitySuffix(bool hasQuestion) {
if (hasQuestion) {
return NullabilitySuffix.question;
} else {
return NullabilitySuffix.none;
}
}
}