blob: 1176e7cc2d6604f24e0acf189481e3497a24daf5 [file] [log] [blame]
// Copyright (c) 2020, 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/element/element.dart';
import 'package:analyzer/dart/element/type.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_provider.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/inference_log.dart';
import 'package:analyzer/src/generated/resolver.dart';
class PrefixedIdentifierResolver {
final ResolverVisitor _resolver;
PrefixedIdentifierResolver(this._resolver);
InvocationInferenceHelper get _inferenceHelper => _resolver.inferenceHelper;
TypeProviderImpl get _typeProvider => _resolver.typeProvider;
PropertyAccessImpl? resolve(
PrefixedIdentifierImpl node, {
required DartType contextType,
}) {
node.prefix.accept(_resolver);
var prefixElement = node.prefix.staticElement;
if (prefixElement is! PrefixElement) {
var prefixType = node.prefix.staticType;
// TODO(scheglov): It would be nice to rewrite all such cases.
if (prefixType != null) {
var prefixTypeResolved =
_resolver.typeSystem.resolveToBound(prefixType);
if (prefixTypeResolved is RecordType) {
var propertyAccess = PropertyAccessImpl(
target: node.prefix,
operator: node.period,
propertyName: node.identifier,
);
_resolver.replaceExpression(node, propertyAccess);
return propertyAccess;
}
}
}
var resolver = PropertyElementResolver(_resolver);
var result = resolver.resolvePrefixedIdentifier(
node: node,
hasRead: true,
hasWrite: false,
);
var element = result.readElement;
var identifier = node.identifier;
identifier.staticElement = element;
if (element is ExtensionElement) {
_setExtensionIdentifierType(node);
return null;
}
if (identical(node.prefix.staticType, NeverTypeImpl.instance)) {
identifier.setPseudoExpressionStaticType(NeverTypeImpl.instance);
node.recordStaticType(NeverTypeImpl.instance, resolver: _resolver);
return null;
}
DartType type = InvalidTypeImpl.instance;
if (result.readElementRequested == null &&
result.readElementRecovery != null) {
// Since the element came from error recovery logic, its type isn't
// trustworthy; leave it as `dynamic`.
} else if (element is InterfaceElement) {
if (_isExpressionIdentifier(node)) {
var type = _typeProvider.typeType;
node.recordStaticType(type, resolver: _resolver);
identifier.setPseudoExpressionStaticType(type);
} else {
inferenceLogWriter?.recordExpressionWithNoType(node);
}
return null;
} else if (element is DynamicElementImpl) {
var type = _typeProvider.typeType;
node.recordStaticType(type, resolver: _resolver);
identifier.setPseudoExpressionStaticType(type);
return null;
} else if (element is TypeAliasElement) {
if (node.parent is NamedType) {
// no type
} else {
var type = _typeProvider.typeType;
node.recordStaticType(type, resolver: _resolver);
identifier.setPseudoExpressionStaticType(type);
}
return null;
} else if (element is MethodElement) {
type = element.type;
} else if (element is PropertyAccessorElement) {
type = result.getType!;
} else if (element is ExecutableElement) {
type = element.type;
} else if (element is VariableElement) {
type = element.type;
} else if (result.functionTypeCallType != null) {
type = result.functionTypeCallType!;
} else if (result.atDynamicTarget) {
type = DynamicTypeImpl.instance;
}
if (!_resolver.isConstructorTearoffsEnabled) {
// Only perform a generic function instantiation on a [PrefixedIdentifier]
// in pre-constructor-tearoffs code. In constructor-tearoffs-enabled code,
// generic function instantiation is performed at assignability check
// sites.
// TODO(srawlins): Switch all resolution to use the latter method, in a
// breaking change release.
type = _inferenceHelper.inferTearOff(node, identifier, type,
contextType: contextType);
}
identifier.setPseudoExpressionStaticType(type);
node.recordStaticType(type, resolver: _resolver);
return null;
}
/// Return `true` if the given [node] is not a type literal.
///
// TODO(scheglov): this is duplicate
bool _isExpressionIdentifier(Identifier node) {
var parent = node.parent;
if (node is SimpleIdentifier && node.inDeclarationContext()) {
return false;
}
if (parent is ConstructorDeclaration) {
if (parent.returnType == node) {
return false;
}
}
if (parent is ConstructorName ||
parent is MethodInvocation ||
parent is PrefixedIdentifier && parent.prefix == node ||
parent is PropertyAccess ||
parent is NamedType) {
return false;
}
return true;
}
// TODO(scheglov): this is duplicate
void _setExtensionIdentifierType(IdentifierImpl node) {
if (node is SimpleIdentifierImpl && node.inDeclarationContext()) {
return;
}
var parent = node.parent;
if (parent is PrefixedIdentifierImpl && parent.identifier == node) {
node = parent;
parent = node.parent;
}
if (parent is CommentReference ||
parent is MethodInvocation && parent.target == node ||
parent is PrefixedIdentifierImpl && parent.prefix == node ||
parent is PropertyAccess && parent.target == node) {
inferenceLogWriter?.recordExpressionWithNoType(node);
return;
}
_resolver.errorReporter.atNode(
node,
CompileTimeErrorCode.EXTENSION_AS_EXPRESSION,
arguments: [node.name],
);
if (node is PrefixedIdentifierImpl) {
node.identifier.setPseudoExpressionStaticType(DynamicTypeImpl.instance);
node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver);
} else if (node is SimpleIdentifier) {
node.recordStaticType(DynamicTypeImpl.instance, resolver: _resolver);
}
}
}