mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
analyzer: add constructor references from PrefixedIdentifier
Bug: https://github.com/dart-lang/sdk/issues/46020 Change-Id: Ifafea6edb6c9ce7fdf1c1f092ff278738c273bb0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209300 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
7c772a653b
commit
9b586a3da6
|
@ -141,6 +141,7 @@ const List<ErrorCode> errorCodeValues = [
|
|||
CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS,
|
||||
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR,
|
||||
CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
|
||||
CompileTimeErrorCode.CONSTRUCTOR_TEAROFFS_NOT_ENABLED,
|
||||
CompileTimeErrorCode.CONTINUE_LABEL_ON_SWITCH,
|
||||
CompileTimeErrorCode.COULD_NOT_INFER,
|
||||
CompileTimeErrorCode.DEFAULT_LIST_CONSTRUCTOR,
|
||||
|
|
|
@ -19,6 +19,8 @@ class AstRewriter {
|
|||
|
||||
AstRewriter(this._errorReporter);
|
||||
|
||||
/// Possibly rewrites [node] as an [ExtensionOverride] or as an
|
||||
/// [InstanceCreationExpression].
|
||||
AstNode methodInvocation(Scope nameScope, MethodInvocation node) {
|
||||
SimpleIdentifier methodName = node.methodName;
|
||||
if (methodName.isSynthetic) {
|
||||
|
@ -140,6 +142,44 @@ class AstRewriter {
|
|||
return node;
|
||||
}
|
||||
|
||||
/// Possibly rewrites [node] as a [ConstructorReference].
|
||||
AstNode prefixedIdentifier(Scope nameScope, PrefixedIdentifier node) {
|
||||
if (node.parent is Annotation) {
|
||||
// An annotations which is a const constructor invocation can initially be
|
||||
// represented with a [PrefixedIdentifier]. Do not rewrite such nodes.
|
||||
return node;
|
||||
}
|
||||
if (node.parent is CommentReference) {
|
||||
// TODO(srawlins): This probably should be rewritten to a
|
||||
// [ConstructorReference] at some point.
|
||||
return node;
|
||||
}
|
||||
var identifier = node.identifier;
|
||||
if (identifier.isSynthetic) {
|
||||
// This isn't a constructor reference.
|
||||
return node;
|
||||
}
|
||||
var prefix = node.prefix;
|
||||
var element = nameScope.lookup(prefix.name).getter;
|
||||
if (element is ClassElement) {
|
||||
// Example:
|
||||
// class C { C.named(); }
|
||||
// C.named
|
||||
return _toConstructorReference(node: node, classElement: element);
|
||||
} else if (element is TypeAliasElement) {
|
||||
var aliasedType = element.aliasedType;
|
||||
if (aliasedType is InterfaceType) {
|
||||
// Example:
|
||||
// class C { C.named(); }
|
||||
// typedef X = C;
|
||||
// X.named
|
||||
return _toConstructorReference(
|
||||
node: node, classElement: aliasedType.element);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
AstNode _instanceCreation_prefix_type_name({
|
||||
required MethodInvocation node,
|
||||
required PrefixedIdentifier typeNameIdentifier,
|
||||
|
@ -170,6 +210,25 @@ class AstRewriter {
|
|||
return instanceCreationExpression;
|
||||
}
|
||||
|
||||
AstNode _toConstructorReference(
|
||||
{required PrefixedIdentifier node, required ClassElement classElement}) {
|
||||
var name = node.identifier.name;
|
||||
var constructorElement = name == 'new'
|
||||
? classElement.unnamedConstructor
|
||||
: classElement.getNamedConstructor(name);
|
||||
if (constructorElement == null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
var typeName = astFactory.typeName(node.prefix, null);
|
||||
var constructorName =
|
||||
astFactory.constructorName(typeName, node.period, node.identifier);
|
||||
var constructorReference =
|
||||
astFactory.constructorReference(constructorName: constructorName);
|
||||
NodeReplacer.replace(node, constructorReference);
|
||||
return constructorReference;
|
||||
}
|
||||
|
||||
InstanceCreationExpression _toInstanceCreation_prefix_type({
|
||||
required MethodInvocation node,
|
||||
required SimpleIdentifier prefixIdentifier,
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// 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/type.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/element/member.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) {
|
||||
if (!_resolver.isConstructorTearoffsEnabled) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.CONSTRUCTOR_TEAROFFS_NOT_ENABLED, node, []);
|
||||
}
|
||||
node.constructorName.accept(_resolver);
|
||||
_inferArgumentTypes(node);
|
||||
}
|
||||
|
||||
void _inferArgumentTypes(ConstructorReferenceImpl node) {
|
||||
var constructorName = node.constructorName;
|
||||
var typeName = constructorName.type;
|
||||
var elementToInfer = _resolver.inferenceHelper.constructorElementToInfer(
|
||||
constructorName: constructorName,
|
||||
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) {
|
||||
// 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.element;
|
||||
var constructorType = elementToInfer.asType;
|
||||
|
||||
var inferred = _resolver.inferenceHelper.inferTearOff(
|
||||
node, constructorName.name!, constructorType) as FunctionType?;
|
||||
|
||||
if (inferred != null) {
|
||||
typeName.type = inferred.returnType;
|
||||
|
||||
// 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.from(
|
||||
rawElement,
|
||||
inferred.returnType as InterfaceType,
|
||||
);
|
||||
constructorName.staticElement = constructorElement;
|
||||
constructorName.name?.staticElement = constructorElement;
|
||||
node.staticType = inferred;
|
||||
}
|
||||
} else {
|
||||
node.staticType = node.constructorName.staticElement!.type;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -128,6 +128,9 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
|
|||
return thenExpression.accept(this)! && elseExpression.accept(this)!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool visitConstructorReference(ConstructorReference node) => false;
|
||||
|
||||
@override
|
||||
bool visitContinueStatement(ContinueStatement node) {
|
||||
_enclosingBlockContainsContinue = true;
|
||||
|
|
|
@ -50,7 +50,7 @@ class ConstructorElementToInfer {
|
|||
typeFormals: typeParameters,
|
||||
parameters: element.parameters,
|
||||
returnType: element.returnType,
|
||||
nullabilitySuffix: NullabilitySuffix.star,
|
||||
nullabilitySuffix: NullabilitySuffix.none,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -859,6 +859,16 @@ class ResolutionVisitor extends RecursiveAstVisitor<void> {
|
|||
super.visitPartOfDirective(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitPrefixedIdentifier(PrefixedIdentifier node) {
|
||||
var newNode = _astRewriter.prefixedIdentifier(_nameScope, node);
|
||||
if (newNode != node) {
|
||||
return newNode.accept(this);
|
||||
}
|
||||
|
||||
super.visitPrefixedIdentifier(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSimpleFormalParameter(covariant SimpleFormalParameterImpl node) {
|
||||
ParameterElementImpl element;
|
||||
|
|
|
@ -2262,6 +2262,23 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
|
|||
"Evaluation of this constant expression throws an "
|
||||
"IntegerDivisionByZeroException.");
|
||||
|
||||
/**
|
||||
* A constructor cannot be torn off without the 'constructor-tearoffs'
|
||||
* language feature.
|
||||
*
|
||||
* There is also a [ParserError.EXPERIMENT_NOT_ENABLED] code which catches
|
||||
* some cases of constructor tearoff features (like `List<int>.filled;`).
|
||||
* Other constructor tearoff cases are not realized until resolution
|
||||
* (like `List.filled;`).
|
||||
*/
|
||||
static const CompileTimeErrorCode CONSTRUCTOR_TEAROFFS_NOT_ENABLED =
|
||||
CompileTimeErrorCode(
|
||||
'CONSTRUCTOR_TEAROFFS_NOT_ENABLED',
|
||||
"Tearing off a constructor requires the 'constructor-tearoffs' "
|
||||
"language feature.",
|
||||
correction: "Try updating your pubspec.yaml to set the minimum SDK "
|
||||
"constraint to 2.14 or higher, and running 'pub get'.");
|
||||
|
||||
/**
|
||||
* 16.12.2 Const: An expression of one of the forms !e, e1 && e2 or e1 || e2,
|
||||
* where e, e1 and e2 are constant expressions that evaluate to a boolean
|
||||
|
|
|
@ -31,6 +31,7 @@ import 'package:analyzer/src/dart/resolver/annotation_resolver.dart';
|
|||
import 'package:analyzer/src/dart/resolver/assignment_expression_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/binary_expression_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/body_inference_context.dart';
|
||||
import 'package:analyzer/src/dart/resolver/constructor_reference_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/extension_member_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
|
||||
import 'package:analyzer/src/dart/resolver/for_resolver.dart';
|
||||
|
@ -186,6 +187,8 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
|
||||
late final AssignmentExpressionResolver _assignmentExpressionResolver;
|
||||
late final BinaryExpressionResolver _binaryExpressionResolver;
|
||||
late final ConstructorReferenceResolver _constructorReferenceResolver =
|
||||
ConstructorReferenceResolver(this);
|
||||
late final FunctionExpressionInvocationResolver
|
||||
_functionExpressionInvocationResolver;
|
||||
late final FunctionExpressionResolver _functionExpressionResolver;
|
||||
|
@ -250,7 +253,8 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
late final FunctionReferenceResolver _functionReferenceResolver;
|
||||
|
||||
late final InstanceCreationExpressionResolver
|
||||
_instanceCreationExpressionResolver;
|
||||
_instanceCreationExpressionResolver =
|
||||
InstanceCreationExpressionResolver(this);
|
||||
|
||||
/// Initialize a newly created visitor to resolve the nodes in an AST node.
|
||||
///
|
||||
|
@ -372,8 +376,6 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
typeAnalyzer = StaticTypeAnalyzer(this, migrationResolutionHooks);
|
||||
_functionReferenceResolver =
|
||||
FunctionReferenceResolver(this, _isNonNullableByDefault);
|
||||
_instanceCreationExpressionResolver =
|
||||
InstanceCreationExpressionResolver(this);
|
||||
}
|
||||
|
||||
bool get isConstructorTearoffsEnabled =>
|
||||
|
@ -1226,6 +1228,11 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
node.accept(typeAnalyzer);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorReference(covariant ConstructorReferenceImpl node) {
|
||||
_constructorReferenceResolver.resolve(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitContinueStatement(ContinueStatement node) {
|
||||
//
|
||||
|
|
|
@ -92,6 +92,10 @@ class FindNode {
|
|||
return _node(search, (n) => n is ConstructorName);
|
||||
}
|
||||
|
||||
ConstructorReference constructorReference(String search) {
|
||||
return _node(search, (n) => n is ConstructorReference);
|
||||
}
|
||||
|
||||
ContinueStatement continueStatement(String search) {
|
||||
return _node(search, (n) => n is ContinueStatement);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
// 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/error/codes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'context_collection_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(ConstructorReferenceResolutionTest);
|
||||
defineReflectiveTests(
|
||||
ConstructorReferenceResolutionWithoutConstructorTearoffsTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ConstructorReferenceResolutionTest extends PubPackageResolutionTest {
|
||||
test_class_generic_inferFromContext_badTypeArgument() async {
|
||||
await assertErrorsInCode('''
|
||||
class A<T extends num> {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
A<String> Function() bar() {
|
||||
return A.foo;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 41, 6),
|
||||
]);
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
elementMatcher(constructorElement, substitution: {'T': 'Never'}),
|
||||
classElement,
|
||||
'A<Never> Function()',
|
||||
expectedTypeNameType: 'A<Never>',
|
||||
);
|
||||
}
|
||||
|
||||
test_class_generic_named_uninstantiated() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T> {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
void bar() {
|
||||
A.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
elementMatcher(constructorElement, substitution: {'T': 'T'}),
|
||||
classElement,
|
||||
'A<T> Function<T>()',
|
||||
expectedTypeNameType: 'A<T>',
|
||||
);
|
||||
}
|
||||
|
||||
test_class_generic_named_uninstantiated_bound() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T extends num> {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
void bar() {
|
||||
A.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
elementMatcher(constructorElement, substitution: {'T': 'T'}),
|
||||
classElement,
|
||||
'A<T> Function<T extends num>()',
|
||||
expectedTypeNameType: 'A<T>',
|
||||
);
|
||||
}
|
||||
|
||||
test_class_nonGeneric_named() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
void bar() {
|
||||
A.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
classElement.getNamedConstructor('foo')!,
|
||||
classElement,
|
||||
'A Function()',
|
||||
expectedTypeNameType: 'A',
|
||||
);
|
||||
}
|
||||
|
||||
test_class_nonGeneric_unnamed() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A();
|
||||
}
|
||||
|
||||
bar() {
|
||||
A.new;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.new;'),
|
||||
classElement.unnamedConstructor,
|
||||
classElement,
|
||||
'A Function()',
|
||||
expectedTypeNameType: 'A',
|
||||
);
|
||||
}
|
||||
|
||||
test_classs_generic_named_inferTypeFromContext() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T> {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
A<int> Function() bar() {
|
||||
return A.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
elementMatcher(constructorElement, substitution: {'T': 'int'}),
|
||||
classElement,
|
||||
'A<int> Function()',
|
||||
expectedTypeNameType: 'A<int>',
|
||||
);
|
||||
}
|
||||
|
||||
test_typeAlias_generic_named_uninstantiated() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T, U> {
|
||||
A.foo();
|
||||
}
|
||||
typedef TA<U> = A<String, U>;
|
||||
|
||||
bar() {
|
||||
TA.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('TA.foo;'),
|
||||
elementMatcher(constructorElement,
|
||||
substitution: {'T': 'String', 'U': 'U'}),
|
||||
findElement.class_('A'),
|
||||
'A<String, U> Function<U>()',
|
||||
expectedTypeNameType: 'A<String, U>',
|
||||
expectedTypeNameElement: findElement.typeAlias('TA'),
|
||||
);
|
||||
}
|
||||
|
||||
test_typeAlias_instantiated_named() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T> {
|
||||
A.foo();
|
||||
}
|
||||
typedef TA = A<int>;
|
||||
|
||||
bar() {
|
||||
TA.foo;
|
||||
}
|
||||
''');
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
var constructorElement = classElement.getNamedConstructor('foo')!;
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('TA.foo;'),
|
||||
elementMatcher(constructorElement, substitution: {'T': 'int'}),
|
||||
classElement,
|
||||
'A<int> Function()',
|
||||
expectedTypeNameType: 'A<int>',
|
||||
expectedTypeNameElement: findElement.typeAlias('TA'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ConstructorReferenceResolutionWithoutConstructorTearoffsTest
|
||||
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
|
||||
test_constructorTearoff() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A.foo();
|
||||
}
|
||||
|
||||
void bar() {
|
||||
A.foo;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.CONSTRUCTOR_TEAROFFS_NOT_ENABLED, 39, 5),
|
||||
]);
|
||||
|
||||
var classElement = findElement.class_('A');
|
||||
assertConstructorReference(
|
||||
findNode.constructorReference('A.foo;'),
|
||||
classElement.getNamedConstructor('foo')!,
|
||||
classElement,
|
||||
'A Function()',
|
||||
expectedTypeNameType: 'A',
|
||||
);
|
||||
}
|
||||
}
|
|
@ -161,6 +161,33 @@ mixin ResolutionTest implements ResourceProviderMixin {
|
|||
}
|
||||
}
|
||||
|
||||
void assertConstructorReference(
|
||||
ConstructorReference node,
|
||||
Object? expectedConstructorElement,
|
||||
ClassElement expectedClassElement,
|
||||
String expectedType, {
|
||||
required String expectedTypeNameType,
|
||||
PrefixElement? expectedPrefix,
|
||||
Element? expectedTypeNameElement,
|
||||
}) {
|
||||
var actualConstructorElement = getNodeElement(node) as ConstructorElement?;
|
||||
var actualConstructorName = node.constructorName.name;
|
||||
if (actualConstructorName != null) {
|
||||
assertConstructorElement(
|
||||
actualConstructorName.staticElement as ConstructorElement?,
|
||||
actualConstructorElement,
|
||||
);
|
||||
}
|
||||
|
||||
assertElement(node, expectedConstructorElement);
|
||||
assertType(node, expectedType);
|
||||
|
||||
var typeName = node.constructorName.type;
|
||||
expectedTypeNameElement ??= expectedClassElement;
|
||||
assertTypeName(typeName, expectedTypeNameElement, expectedTypeNameType,
|
||||
expectedPrefix: expectedPrefix);
|
||||
}
|
||||
|
||||
void assertConstructors(ClassElement class_, List<String> expected) {
|
||||
expect(
|
||||
class_.constructors.map((c) {
|
||||
|
@ -396,6 +423,9 @@ mixin ResolutionTest implements ResourceProviderMixin {
|
|||
}
|
||||
}
|
||||
|
||||
/// TODO(srawlins): Refactor to accept an `Object? expectedConstructor` which
|
||||
/// can accept `elementMatcher` for generics, and simplify, similar to
|
||||
/// [assertConstructorReference].
|
||||
void assertInstanceCreation(
|
||||
InstanceCreationExpression creation,
|
||||
ClassElement expectedClassElement,
|
||||
|
@ -406,22 +436,8 @@ mixin ResolutionTest implements ResourceProviderMixin {
|
|||
PrefixElement? expectedPrefix,
|
||||
Element? expectedTypeNameElement,
|
||||
}) {
|
||||
String expectedClassName = expectedClassElement.name;
|
||||
|
||||
ConstructorElement? expectedConstructorElement;
|
||||
if (constructorName != null) {
|
||||
expectedConstructorElement =
|
||||
expectedClassElement.getNamedConstructor(constructorName);
|
||||
if (expectedConstructorElement == null) {
|
||||
fail("No constructor '$constructorName' in class"
|
||||
" '$expectedClassName'.");
|
||||
}
|
||||
} else {
|
||||
expectedConstructorElement = expectedClassElement.unnamedConstructor;
|
||||
if (expectedConstructorElement == null) {
|
||||
fail("No unnamed constructor in class '$expectedClassName'.");
|
||||
}
|
||||
}
|
||||
var expectedConstructorElement =
|
||||
_getConstructorElement(expectedClassElement, constructorName);
|
||||
|
||||
var actualConstructorElement =
|
||||
getNodeElement(creation) as ConstructorElement?;
|
||||
|
@ -866,6 +882,8 @@ mixin ResolutionTest implements ResourceProviderMixin {
|
|||
return node.staticElement;
|
||||
} else if (node is BinaryExpression) {
|
||||
return node.staticElement;
|
||||
} else if (node is ConstructorReference) {
|
||||
return node.constructorName.staticElement;
|
||||
} else if (node is Declaration) {
|
||||
return node.declaredElement;
|
||||
} else if (node is ExtensionOverride) {
|
||||
|
@ -978,6 +996,16 @@ mixin ResolutionTest implements ResourceProviderMixin {
|
|||
fail('Expected SimpleIdentifier: (${node.runtimeType}) $node');
|
||||
}
|
||||
|
||||
ConstructorElement _getConstructorElement(
|
||||
ClassElement classElement, String? constructorName) {
|
||||
var constructorElement = constructorName == null
|
||||
? classElement.unnamedConstructor
|
||||
: classElement.getNamedConstructor(constructorName);
|
||||
return constructorElement ??
|
||||
fail("No constructor '${constructorName ?? '<unnamed>'}' in class "
|
||||
"'${classElement.name}'.");
|
||||
}
|
||||
|
||||
static String _extractReturnType(String invokeType) {
|
||||
int functionIndex = invokeType.indexOf(' Function');
|
||||
expect(functionIndex, isNonNegative);
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'class_alias_test.dart' as class_alias;
|
|||
import 'class_test.dart' as class_resolution;
|
||||
import 'comment_test.dart' as comment;
|
||||
import 'constant_test.dart' as constant;
|
||||
import 'constructor_reference_test.dart' as constructor_reference;
|
||||
import 'constructor_test.dart' as constructor;
|
||||
import 'enum_test.dart' as enum_resolution;
|
||||
import 'export_test.dart' as export_;
|
||||
|
@ -76,6 +77,7 @@ main() {
|
|||
comment.main();
|
||||
constant.main();
|
||||
constructor.main();
|
||||
constructor_reference.main();
|
||||
enum_resolution.main();
|
||||
export_.main();
|
||||
extension_method.main();
|
||||
|
|
|
@ -87,8 +87,11 @@ main() {
|
|||
Foo.bar();
|
||||
Foo.bar.baz();
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_GETTER
|
||||
// [cfe] Getter not found: 'bar'.
|
||||
//^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.CONSTRUCTOR_TEAROFFS_NOT_ENABLED
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
Foo<int>();
|
||||
Foo<int>.bar();
|
||||
Foo<int>.bar.baz();
|
||||
|
@ -111,6 +114,9 @@ main() {
|
|||
// [cfe] Method not found: 'Foo.bar.baz'.
|
||||
Foo.bar.baz<int>();
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_GETTER
|
||||
// [cfe] Getter not found: 'bar'.
|
||||
//^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.CONSTRUCTOR_TEAROFFS_NOT_ENABLED
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
}
|
||||
|
|
|
@ -89,8 +89,11 @@ main() {
|
|||
Foo.bar();
|
||||
Foo.bar.baz();
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_GETTER
|
||||
// [cfe] Getter not found: 'bar'.
|
||||
//^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.CONSTRUCTOR_TEAROFFS_NOT_ENABLED
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
Foo<int>();
|
||||
Foo<int>.bar();
|
||||
Foo<int>.bar.baz();
|
||||
|
@ -113,6 +116,9 @@ main() {
|
|||
// [cfe] Method not found: 'Foo.bar.baz'.
|
||||
Foo.bar.baz<int>();
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_GETTER
|
||||
// [cfe] Getter not found: 'bar'.
|
||||
//^^^^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.CONSTRUCTOR_TEAROFFS_NOT_ENABLED
|
||||
// ^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_METHOD
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue