blob: a08ba51a711fe3eef870da461c750be090188929 [file]
// 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/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/resolver/invocation_inferrer.dart';
import 'package:analyzer/src/dart/resolver/type_analyzer.dart';
import 'package:analyzer/src/dart/type_instantiation_target.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/error/listener.dart';
import 'package:analyzer/src/error/lookup_failure_reporter.dart';
/// A resolver for [ConstructorInvocation] and
/// [DotShorthandConstructorInvocation2] nodes.
class ConstructorInvocationResolver {
/// The type analyzer driving this participant.
final TypeAnalyzer _typeAnalyzer;
ConstructorInvocationResolver(this._typeAnalyzer);
void resolve(
ConstructorInvocationImpl node, {
required TypeImpl contextType,
}) {
_resolveConstructorInvocation(node, contextType: contextType);
}
/// Resolves a [DotShorthandConstructorInvocation2] node.
void resolveDotShorthand(
DotShorthandConstructorInvocation2Impl node, {
required TypeImpl contextType,
required DotShorthandContextResolutionImpl shorthandContext,
}) {
var dotShorthandContextType = switch (shorthandContext) {
MissingDotShorthandContextResolutionImpl() => InvalidTypeImpl.instance,
InvalidDotShorthandContextResolutionImpl() => InvalidTypeImpl.instance,
ValidDotShorthandContextResolutionImpl(:var lookupType) => lookupType,
};
if (shorthandContext case ValidDotShorthandContextResolutionImpl(
lookupType: InterfaceTypeImpl(element: var contextElement),
)) {
// This branch will be true if we're resolving an explicitly marked
// const constructor invocation. Other shorthand constructor invocations
// already have an element selected from the parsed invocation head.
if (node.element == null) {
if (contextElement.getNamedConstructor(node.name.lexeme)
case ConstructorElementImpl element?
when element.isAccessibleIn(_typeAnalyzer.definingLibrary)) {
node.element = element;
} else {
_typeAnalyzer.diagnosticReporter.report(
diag.constWithUndefinedConstructor
.withArguments(
className: contextElement.displayName,
constructorName: node.name.lexeme,
)
.at(node.name),
);
}
}
var typeArguments = node.typeArguments;
var constructorElement = node.element;
if (contextElement is ClassElementImpl &&
contextElement.isAbstract &&
constructorElement != null &&
!constructorElement.isFactory) {
_typeAnalyzer.diagnosticReporter.report(
diag.instantiateAbstractClass.at(node),
);
} else if (typeArguments != null) {
_typeAnalyzer.diagnosticReporter.report(
diag.wrongNumberOfTypeArgumentsDotShorthandConstructor
.withArguments(
className: contextElement.displayName,
constructorName: node.name.lexeme,
)
.at(typeArguments),
);
}
} else {
_typeAnalyzer.diagnosticReporter.report(
diag.dotShorthandMissingContext.at(node),
);
}
_resolveDotShorthandConstructorInvocation(
node,
contextType: contextType,
dotShorthandContextType: dotShorthandContextType,
);
}
void _resolveConstructorInvocation(
ConstructorInvocationImpl node, {
required TypeImpl contextType,
}) {
var whyNotPromotedArguments = <WhyNotPromotedGetter>[];
var constructorReference = node.constructorReference;
var typeReference = constructorReference.typeReference;
if (constructorReference.selector case var selector?
when typeReference.type is FunctionTypeImpl) {
_typeAnalyzer.lookupFailureReporter.reportReadFailure(
domain: FunctionTypeAliasLookupDomain(
importPrefix: typeReference.importPrefix,
name: typeReference.name,
),
name: selector.name2,
foundInstead: null,
);
}
var elementToInfer = _typeAnalyzer.inferenceHelper
.constructorElementToInfer(
typeElement: constructorReference.typeReference.element,
constructorName: constructorReference.selector?.name2,
definingLibrary: _typeAnalyzer.definingLibrary,
);
constructorReference.element = elementToInfer?.element;
_typeAnalyzer.elementResolver.visitConstructorInvocation(node);
var target = elementToInfer == null
? null
: InvocationTargetConstructorElement(
elementToInfer.element,
// TODO(paulberry): eliminate this cast by changing the type of
// `ConstructorElementToInfer.asType`.
elementToInfer.asType as FunctionTypeImpl,
);
ConstructorInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
contextType: contextType,
whyNotPromotedArguments: whyNotPromotedArguments,
target: target,
).resolveInvocation();
node.recordStaticType(
typeReference.type is FunctionTypeImpl
? InvalidTypeImpl.instance
: typeReference.type!,
typeAnalyzer: _typeAnalyzer,
);
_typeAnalyzer.checkForArgumentTypesNotAssignableInList(
node.argumentList,
whyNotPromotedArguments,
);
}
void _resolveDotShorthandConstructorInvocation(
DotShorthandConstructorInvocation2Impl node, {
required TypeImpl contextType,
required TypeImpl dotShorthandContextType,
}) {
var whyNotPromotedArguments = <WhyNotPromotedGetter>[];
_typeAnalyzer.elementResolver.visitDotShorthandConstructorInvocation2(node);
var elementToInfer = _typeAnalyzer.inferenceHelper
.constructorElementToInfer(
typeElement: dotShorthandContextType.element,
constructorName: node.name,
definingLibrary: _typeAnalyzer.definingLibrary,
);
var target = elementToInfer == null
? null
: InvocationTargetConstructorElement(
elementToInfer.element,
// TODO(paulberry): eliminate this cast by changing the type of
// `ConstructorElementToInfer.asType`.
elementToInfer.asType as FunctionTypeImpl,
);
var returnType = DotShorthandConstructorInvocationInferrer(
typeAnalyzer: _typeAnalyzer,
node: node,
argumentList: node.argumentList,
contextType: contextType,
whyNotPromotedArguments: whyNotPromotedArguments,
target: target,
).resolveInvocation();
node.recordStaticType(returnType, typeAnalyzer: _typeAnalyzer);
_typeAnalyzer.checkForArgumentTypesNotAssignableInList(
node.argumentList,
whyNotPromotedArguments,
);
}
}