blob: 97097a173b78baf08bb5adf4212d9ea38d13aac3 [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/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/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
/// A resolver for [ConstructorReference] nodes.
class ConstructorReferenceResolver {
/// The resolver driving this participant.
final ResolverVisitor _resolver;
ConstructorReferenceResolver(this._resolver);
void resolve(ConstructorReferenceImpl node, {required DartType contextType}) {
if (!_resolver.isConstructorTearoffsEnabled &&
node.constructorName.type.typeArguments == null) {
// Only report this if [node] has no explicit type arguments; otherwise
// the parser has already reported an error.
_resolver.errorReporter.atNode(
node,
WarningCode.SDK_VERSION_CONSTRUCTOR_TEAROFFS,
);
}
node.constructorName.accept(_resolver);
var element = node.constructorName.element;
if (element != null && !element.isFactory) {
var enclosingElement = element.enclosingElement2;
if (enclosingElement is ClassElementImpl2 &&
enclosingElement.isAbstract) {
_resolver.errorReporter.atNode(
node,
CompileTimeErrorCode
.TEAROFF_OF_GENERATIVE_CONSTRUCTOR_OF_ABSTRACT_CLASS,
);
}
}
var name = node.constructorName.name;
if (element == null &&
name != null &&
_resolver.isConstructorTearoffsEnabled) {
// The illegal construction, which looks like a type-instantiated
// constructor tearoff, may be an attempt to reference a member on
// [enclosingElement]. Try to provide a helpful error, and fall back to
// "unknown constructor."
//
// Only report errors when the constructor tearoff feature is enabled,
// to avoid reporting redundant errors.
var enclosingElement = node.constructorName.type.element2;
if (enclosingElement is TypeAliasElement) {
var aliasedType = enclosingElement.aliasedType;
enclosingElement =
aliasedType is InterfaceType ? aliasedType.element3 : null;
}
// TODO(srawlins): Handle `enclosingElement` being a function typedef:
// typedef F<T> = void Function(); var a = F<int>.extensionOnType;`.
// This is illegal.
if (enclosingElement is InterfaceElement) {
var method =
enclosingElement.getMethod2(name.name) ??
enclosingElement.getGetter2(name.name) ??
enclosingElement.getSetter2(name.name);
if (method != null) {
var error =
method.isStatic
? CompileTimeErrorCode
.CLASS_INSTANTIATION_ACCESS_TO_STATIC_MEMBER
: CompileTimeErrorCode
.CLASS_INSTANTIATION_ACCESS_TO_INSTANCE_MEMBER;
_resolver.errorReporter.atNode(node, error, arguments: [name.name]);
} else if (!name.isSynthetic) {
_resolver.errorReporter.atNode(
node,
CompileTimeErrorCode.CLASS_INSTANTIATION_ACCESS_TO_UNKNOWN_MEMBER,
arguments: [enclosingElement.name3!, name.name],
);
}
}
}
_inferArgumentTypes(node, contextType: contextType);
}
void _inferArgumentTypes(
ConstructorReferenceImpl node, {
required DartType contextType,
}) {
var constructorName = node.constructorName;
var elementToInfer = _resolver.inferenceHelper.constructorElementToInfer(
typeElement: constructorName.type.element2,
constructorName: constructorName.name,
definingLibrary: _resolver.definingLibrary,
);
// If the constructor is generic, we'll have a ConstructorMember that
// substitutes in type arguments (possibly `dynamic`) from earlier in
// resolution.
//
// Otherwise we'll have a ConstructorElement, and we can skip inference
// because there's nothing to infer in a non-generic type.
if (elementToInfer != null &&
elementToInfer.typeParameters2.isNotEmpty &&
constructorName.type.typeArguments == null) {
// TODO(leafp): Currently, we may re-infer types here, since we
// sometimes resolve multiple times. We should really check that we
// have not already inferred something. However, the obvious ways to
// check this don't work, since we may have been instantiated
// to bounds in an earlier phase, and we *do* want to do inference
// in that case.
// Get back to the uninstantiated generic constructor.
// TODO(jmesserly): should we store this earlier in resolution?
// Or look it up, instead of jumping backwards through the Member?
var rawElement = elementToInfer.element2.baseElement;
var constructorType = elementToInfer.asType;
var inferred =
_resolver.inferenceHelper.inferTearOff(
node,
constructorName.name!,
constructorType,
contextType: contextType,
)
as FunctionType?;
if (inferred != null) {
var inferredReturnType = inferred.returnType as InterfaceType;
// Update the static element as well. This is used in some cases, such
// as computing constant values. It is stored in two places.
var constructorElement = ConstructorMember.from2(
rawElement,
inferredReturnType,
);
constructorName.element = constructorElement.baseElement;
constructorName.name?.element = constructorElement.baseElement;
node.recordStaticType(inferred, resolver: _resolver);
// The NamedType child of `constructorName` doesn't have a static type.
constructorName.type.type = null;
}
} else {
var constructorElement = constructorName.element;
node.recordStaticType(
constructorElement == null
? InvalidTypeImpl.instance
: constructorElement.type,
resolver: _resolver,
);
// The NamedType child of `constructorName` doesn't have a static type.
constructorName.type.type = null;
}
}
}