blob: 5cebadf6ff44b57e9433843bb0e4ad212291a1c8 [file] [log] [blame]
// Copyright (c) 2021, 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/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
import 'package:analyzer/src/generated/resolver.dart';
class CommentReferenceResolver {
final TypeProviderImpl _typeProvider;
final ResolverVisitor _resolver;
/// Helper for resolving properties on types.
final TypePropertyResolver _typePropertyResolver;
CommentReferenceResolver(this._typeProvider, this._resolver)
: _typePropertyResolver = _resolver.typePropertyResolver;
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
/// Resolves [commentReference].
void resolve(CommentReference commentReference) {
_resolver.errorReporter.lockLevel++;
try {
var expression = commentReference.expression;
if (expression is SimpleIdentifierImpl) {
_resolveSimpleIdentifierReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
} else if (expression is PrefixedIdentifierImpl) {
_resolvePrefixedIdentifierReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
} else if (expression is PropertyAccessImpl) {
_resolvePropertyAccessReference(expression,
hasNewKeyword: commentReference.newKeyword != null);
}
} finally {
_resolver.errorReporter.lockLevel--;
}
}
void _resolvePrefixedIdentifierReference(
PrefixedIdentifierImpl expression, {
required bool hasNewKeyword,
}) {
var prefix = expression.prefix;
var prefixElement = _resolveSimpleIdentifier(prefix);
prefix.staticElement = prefixElement;
if (prefixElement == null) {
return;
}
var name = expression.identifier;
if (prefixElement is PrefixElement) {
var prefixScope = prefixElement.scope;
var lookupResult = prefixScope.lookup(name.name);
var element = lookupResult.getter ?? lookupResult.setter;
element = _resolver.toLegacyElement(element);
name.staticElement = element;
return;
}
if (!hasNewKeyword) {
if (prefixElement is ClassElement) {
name.staticElement = prefixElement.getMethod(name.name) ??
prefixElement.getGetter(name.name) ??
prefixElement.getSetter(name.name) ??
prefixElement.getNamedConstructor(name.name);
} else if (prefixElement is ExtensionElement) {
name.staticElement = prefixElement.getMethod(name.name) ??
prefixElement.getGetter(name.name) ??
prefixElement.getSetter(name.name);
} else {
// TODO(brianwilkerson) Report this error.
}
} else if (prefixElement is ClassElement) {
var constructor = prefixElement.getNamedConstructor(name.name);
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
name.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}
void _resolvePropertyAccessReference(
PropertyAccessImpl expression, {
required bool hasNewKeyword,
}) {
var target = expression.target;
if (target is! PrefixedIdentifierImpl) {
// A PropertyAccess with a target more complex than a
// [PrefixedIdentifier] is not a valid comment reference.
return;
}
var prefix = target.prefix;
var prefixElement = _resolveSimpleIdentifier(prefix);
prefix.staticElement = prefixElement;
if (prefixElement is! PrefixElement) {
// The only valid prefixElement is a PrefixElement; otherwise, this is
// not a comment reference.
return;
}
var name = target.identifier;
var prefixScope = prefixElement.scope;
var lookupResult = prefixScope.lookup(name.name);
var element = lookupResult.getter ?? lookupResult.setter;
element = _resolver.toLegacyElement(element);
name.staticElement = element;
var propertyName = expression.propertyName;
if (element is ClassElement) {
propertyName.staticElement = element.getMethod(propertyName.name) ??
element.getGetter(propertyName.name) ??
element.getSetter(propertyName.name) ??
element.getNamedConstructor(propertyName.name);
} else if (element is ExtensionElement) {
propertyName.staticElement = element.getMethod(propertyName.name) ??
element.getGetter(propertyName.name) ??
element.getSetter(propertyName.name);
}
}
/// Resolves the given simple [identifier] if possible.
///
/// Returns the resolved element, or `null` if the identifier could not be
/// resolved. This does not record the results of the resolution.
Element? _resolveSimpleIdentifier(SimpleIdentifierImpl identifier) {
var lookupResult = identifier.scopeLookupResult!;
var element = _resolver.toLegacyElement(lookupResult.getter) ??
_resolver.toLegacyElement(lookupResult.setter);
if (element == null) {
InterfaceType enclosingType;
var enclosingClass = _resolver.enclosingClass;
if (enclosingClass != null) {
enclosingType = enclosingClass.thisType;
} else {
var enclosingExtension = _resolver.enclosingExtension;
if (enclosingExtension == null) {
return null;
}
var extendedType = _typeSystem.resolveToBound(
enclosingExtension.extendedType,
);
if (extendedType is InterfaceType) {
enclosingType = extendedType;
} else if (extendedType is FunctionType) {
enclosingType = _typeProvider.functionType;
} else {
return null;
}
}
var result = _typePropertyResolver.resolve(
receiver: null,
receiverType: enclosingType,
name: identifier.name,
propertyErrorEntity: identifier,
nameErrorEntity: identifier,
);
element = result.getter ?? result.setter;
}
return element;
}
void _resolveSimpleIdentifierReference(
SimpleIdentifierImpl expression, {
required bool hasNewKeyword,
}) {
var element = _resolveSimpleIdentifier(expression);
if (element == null) {
return;
}
expression.staticElement = element;
if (hasNewKeyword) {
if (element is ClassElement) {
var constructor = element.unnamedConstructor;
if (constructor == null) {
// TODO(brianwilkerson) Report this error.
} else {
expression.staticElement = constructor;
}
} else {
// TODO(brianwilkerson) Report this error.
}
}
}
}