blob: af34eb41fe2ae2a0c3e52da395ddc4ab7a6a4332 [file]
// 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/type.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/scope_context.dart';
import 'package:analyzer/src/summary2/function_type_builder.dart';
import 'package:analyzer/src/summary2/link.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';
/// 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> {
/// The library fragment in which the AST nodes are being resolved.
final LibraryFragmentImpl libraryFragment;
final ScopeContext _scopeContext;
final Linker linker;
final TypeSystemImpl typeSystem;
final NodesToBuildType nodesToBuildType;
ReferenceResolver(
this.linker,
this.nodesToBuildType,
this.typeSystem,
Scope scope, {
required this.libraryFragment,
}) : _scopeContext = ScopeContext(
libraryFragment: libraryFragment,
nameScope: scope,
);
Scope get nameScope => _scopeContext.nameScope;
@override
void visitAnnotation(covariant AnnotationImpl node) {
if (node.arguments != null) {
var identifier = node.name;
if (identifier is PrefixedIdentifierImpl) {
var prefixNode = identifier.prefix;
var prefixElement = nameScope.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 = nameScope.lookup(identifier.name).getter;
identifier.element = element;
return;
}
}
}
@override
void visitBlockClassBody(BlockClassBody node) {
node.members.accept(this);
}
@override
void visitBlockEnumBody(BlockEnumBody node) {
node.constants.accept(this);
node.members.accept(this);
}
@override
void visitBlockFunctionBody(BlockFunctionBody node) {}
@override
void visitClassDeclaration(covariant ClassDeclarationImpl node) {
_scopeContext.visitClassDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitClassTypeAlias(covariant ClassTypeAliasImpl node) {
_scopeContext.visitClassTypeAlias(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitComment(Comment node) {}
@override
void visitCompilationUnit(CompilationUnit node) {
node.declarations.accept(this);
}
@override
void visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) {
_scopeContext.visitConstructorDeclaration(
node,
visitor: this,
visitTypeName: (_) {},
visitInitializers: (_) {},
visitRedirectedConstructor: (_) {},
);
}
@override
void visitEmptyClassBody(EmptyClassBody node) {}
@override
void visitEmptyEnumBody(EmptyEnumBody node) {}
@override
void visitEmptyFunctionBody(EmptyFunctionBody node) {}
@override
void visitEnumConstantDeclaration(EnumConstantDeclaration node) {}
@override
void visitEnumDeclaration(covariant EnumDeclarationImpl node) {
var fragment = node.declaredFragment!;
_scopeContext.visitEnumDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
for (var field in fragment.fields) {
if (field.isEnumConstant || field.isOriginEnumValues) {
var fieldNode = linker.getLinkingNode(field);
fieldNode as VariableDeclarationImpl;
fieldNode.initializerScope = node.bodyScope;
}
}
}
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {}
@override
void visitExtendsClause(ExtendsClause node) {
node.superclass.accept(this);
}
@override
void visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) {
_scopeContext.visitExtensionDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitExtensionOnClause(ExtensionOnClause node) {
node.extendedType.accept(this);
}
@override
void visitExtensionTypeDeclaration(
covariant ExtensionTypeDeclarationImpl node,
) {
_scopeContext.visitExtensionTypeDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitFieldDeclaration(covariant FieldDeclarationImpl node) {
_scopeContext.visitFieldDeclaration(node, visitor: this);
}
@override
void visitFieldFormalParameter(covariant FieldFormalParameterImpl node) {
_visitFormalParameter(node);
}
@override
void visitFormalParameterDefaultClause(FormalParameterDefaultClause node) {}
@override
void visitFormalParameterList(FormalParameterList node) {
node.parameters.accept(this);
}
@override
void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) {
_scopeContext.visitFunctionDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) {
_scopeContext.visitFunctionTypeAlias(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitGenericFunctionType(covariant GenericFunctionTypeImpl node) {
_scopeContext.visitGenericFunctionType(node, visitor: this);
var nullabilitySuffix = _getNullabilitySuffix(node.question != null);
var builder = FunctionTypeBuilder.of(node, nullabilitySuffix);
node.type = builder;
nodesToBuildType.addDeclaration(node);
nodesToBuildType.addTypeBuilder(builder);
}
@override
void visitGenericTypeAlias(covariant GenericTypeAliasImpl node) {
_scopeContext.visitGenericTypeAlias(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitImplementsClause(ImplementsClause node) {
node.interfaces.accept(this);
}
@override
void visitMethodDeclaration(covariant MethodDeclarationImpl node) {
_scopeContext.visitMethodDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitMixinDeclaration(covariant MixinDeclarationImpl node) {
_scopeContext.visitMixinDeclaration(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@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 = nameScope.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 = nameScope.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 = _scopeContext.instantiateTypeParameter(
element: element,
nullability: nullabilitySuffix,
);
} else {
var builder = NamedTypeBuilder.of(
linker: linker,
typeSystem: typeSystem,
node: node,
element: element,
nullabilitySuffix: nullabilitySuffix,
);
node.type = builder;
nodesToBuildType.addTypeBuilder(builder);
}
}
@override
void visitNameWithTypeParameters(NameWithTypeParameters node) {
node.typeParameters?.accept(this);
}
@override
void visitNativeClause(NativeClause node) {}
@override
void visitNativeFunctionBody(NativeFunctionBody node) {}
@override
void visitPrimaryConstructorBody(covariant PrimaryConstructorBodyImpl node) {
_scopeContext.visitPrimaryConstructorBody(
node,
visitor: this,
visitInitializers: (_) {},
);
}
@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 visitRegularFormalParameter(covariant RegularFormalParameterImpl node) {
_visitFormalParameter(node);
}
@override
void visitSuperFormalParameter(covariant SuperFormalParameterImpl node) {
_visitFormalParameter(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
node.visitChildren(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!;
if (fragment.previousFragment == null) {
fragment.element.bound = bound.type;
}
nodesToBuildType.addDeclaration(node);
}
}
@override
void visitTypeParameterList(TypeParameterList node) {
node.typeParameters.accept(this);
}
@override
void visitVariableDeclaration(covariant VariableDeclarationImpl node) {
node.initializerScope = nameScope;
}
@override
void visitVariableDeclarationList(
covariant VariableDeclarationListImpl node,
) {
_scopeContext.visitVariableDeclarationList(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
@override
void visitWithClause(WithClause node) {
node.mixinTypes.accept(this);
}
NullabilitySuffix _getNullabilitySuffix(bool hasQuestion) {
if (hasQuestion) {
return NullabilitySuffix.question;
} else {
return NullabilitySuffix.none;
}
}
void _visitFormalParameter(FormalParameterImpl node) {
_scopeContext.visitFormalParameter(node, visitor: this);
nodesToBuildType.addDeclaration(node);
}
}