1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

analyzer: parse prefixed class constructor tearoff with no type args

Change-Id: I5f19dd9592f83820100e0ac498522245cac16dc4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212700
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2021-09-09 07:04:34 +00:00 committed by commit-bot@chromium.org
parent 2785fe9996
commit e995cb5f7c
2 changed files with 138 additions and 14 deletions

View File

@ -266,21 +266,31 @@ class AstRewriter {
return node;
}
var receiver = node.target!;
if (receiver is! FunctionReference) {
return node;
}
var propertyName = node.propertyName;
if (propertyName.isSynthetic) {
// This isn't a constructor reference.
return node;
}
// A [ConstructorReference] with explicit type arguments is initially parsed
// as a [PropertyAccess] with a [FunctionReference] target; for example:
// `List<int>.filled` or `core.List<int>.filled`.
var receiverIdentifier = receiver.function;
if (receiverIdentifier is! Identifier) {
// If [receiverIdentifier] is not an Identifier then [node] is not a
// ConstructorReference.
Identifier receiverIdentifier;
TypeArgumentList? typeArguments;
if (receiver is PrefixedIdentifier) {
receiverIdentifier = receiver;
} else if (receiver is FunctionReference) {
// A [ConstructorReference] with explicit type arguments is initially
// parsed as a [PropertyAccess] with a [FunctionReference] target; for
// example: `List<int>.filled` or `core.List<int>.filled`.
var function = receiver.function;
if (function is! Identifier) {
// If [receiverIdentifier] is not an Identifier then [node] is not a
// ConstructorReference.
return node;
}
receiverIdentifier = function;
typeArguments = receiver.typeArguments;
} else {
// If the receiver is not (initially) a prefixed identifier or a function
// reference, then [node] is not a constructor reference.
return node;
}
@ -310,7 +320,7 @@ class AstRewriter {
return _toConstructorReference_propertyAccess(
node: node,
receiver: receiverIdentifier,
typeArguments: receiver.typeArguments!,
typeArguments: typeArguments,
classElement: element,
);
} else if (element is TypeAliasElement) {
@ -323,7 +333,7 @@ class AstRewriter {
return _toConstructorReference_propertyAccess(
node: node,
receiver: receiverIdentifier,
typeArguments: receiver.typeArguments!,
typeArguments: typeArguments,
classElement: aliasedType.element,
);
}
@ -393,12 +403,24 @@ class AstRewriter {
return constructorReference;
}
ConstructorReference _toConstructorReference_propertyAccess({
AstNode _toConstructorReference_propertyAccess({
required PropertyAccess node,
required Identifier receiver,
required TypeArgumentList typeArguments,
required TypeArgumentList? typeArguments,
required ClassElement classElement,
}) {
var name = node.propertyName.name;
var constructorElement = name == 'new'
? classElement.unnamedConstructor
: classElement.getNamedConstructor(name);
if (constructorElement == null && typeArguments == null) {
// If there is no constructor by this name, and no type arguments,
// do not rewrite the node. If there _are_ type arguments (like
// `prefix.C<int>.name`, then it looks more like a constructor tearoff
// than anything else, so continue with the rewrite.
return node;
}
var operator = node.operator;
var typeName = astFactory.typeName(receiver, typeArguments);

View File

@ -696,6 +696,108 @@ bar() {
);
}
test_prefixedAlias_nonGeneric_named() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
A.foo();
}
typedef TA = A;
''');
await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
a.TA.foo;
}
''');
var classElement =
findElement.importFind('package:test/a.dart').class_('A');
assertConstructorReference(
findNode.constructorReference('a.TA.foo;'),
classElement.getNamedConstructor('foo'),
classElement,
'A Function()',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
expectedTypeNameElement:
findElement.importFind('package:test/a.dart').typeAlias('TA'),
);
}
test_prefixedAlias_nonGeneric_unnamed() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
A();
}
typedef TA = A;
''');
await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
a.TA.new;
}
''');
var classElement =
findElement.importFind('package:test/a.dart').class_('A');
assertConstructorReference(
findNode.constructorReference('a.TA.new;'),
classElement.unnamedConstructor,
classElement,
'A Function()',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
expectedTypeNameElement:
findElement.importFind('package:test/a.dart').typeAlias('TA'),
);
}
test_prefixedClass_nonGeneric_named() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
A.foo();
}
''');
await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
a.A.foo;
}
''');
var classElement =
findElement.importFind('package:test/a.dart').class_('A');
assertConstructorReference(
findNode.constructorReference('a.A.foo;'),
classElement.getNamedConstructor('foo'),
classElement,
'A Function()',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
);
}
test_prefixedClass_nonGeneric_unnamed() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
A();
}
''');
await assertNoErrorsInCode('''
import 'a.dart' as a;
bar() {
a.A.new;
}
''');
var classElement =
findElement.importFind('package:test/a.dart').class_('A');
assertConstructorReference(
findNode.constructorReference('a.A.new;'),
classElement.unnamedConstructor,
classElement,
'A Function()',
expectedPrefix: findElement.import('package:test/a.dart').prefix,
);
}
test_typeAlias_generic_const() async {
await assertNoErrorsInCode('''
class A<T> {