Skip to content

Commit e995cb5

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
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 <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 2785fe9 commit e995cb5

File tree

2 files changed

+138
-14
lines changed

2 files changed

+138
-14
lines changed

pkg/analyzer/lib/src/dart/resolver/ast_rewrite.dart

+36-14
Original file line numberDiff line numberDiff line change
@@ -266,21 +266,31 @@ class AstRewriter {
266266
return node;
267267
}
268268
var receiver = node.target!;
269-
if (receiver is! FunctionReference) {
270-
return node;
271-
}
272269
var propertyName = node.propertyName;
273270
if (propertyName.isSynthetic) {
274271
// This isn't a constructor reference.
275272
return node;
276273
}
277-
// A [ConstructorReference] with explicit type arguments is initially parsed
278-
// as a [PropertyAccess] with a [FunctionReference] target; for example:
279-
// `List<int>.filled` or `core.List<int>.filled`.
280-
var receiverIdentifier = receiver.function;
281-
if (receiverIdentifier is! Identifier) {
282-
// If [receiverIdentifier] is not an Identifier then [node] is not a
283-
// ConstructorReference.
274+
275+
Identifier receiverIdentifier;
276+
TypeArgumentList? typeArguments;
277+
if (receiver is PrefixedIdentifier) {
278+
receiverIdentifier = receiver;
279+
} else if (receiver is FunctionReference) {
280+
// A [ConstructorReference] with explicit type arguments is initially
281+
// parsed as a [PropertyAccess] with a [FunctionReference] target; for
282+
// example: `List<int>.filled` or `core.List<int>.filled`.
283+
var function = receiver.function;
284+
if (function is! Identifier) {
285+
// If [receiverIdentifier] is not an Identifier then [node] is not a
286+
// ConstructorReference.
287+
return node;
288+
}
289+
receiverIdentifier = function;
290+
typeArguments = receiver.typeArguments;
291+
} else {
292+
// If the receiver is not (initially) a prefixed identifier or a function
293+
// reference, then [node] is not a constructor reference.
284294
return node;
285295
}
286296

@@ -310,7 +320,7 @@ class AstRewriter {
310320
return _toConstructorReference_propertyAccess(
311321
node: node,
312322
receiver: receiverIdentifier,
313-
typeArguments: receiver.typeArguments!,
323+
typeArguments: typeArguments,
314324
classElement: element,
315325
);
316326
} else if (element is TypeAliasElement) {
@@ -323,7 +333,7 @@ class AstRewriter {
323333
return _toConstructorReference_propertyAccess(
324334
node: node,
325335
receiver: receiverIdentifier,
326-
typeArguments: receiver.typeArguments!,
336+
typeArguments: typeArguments,
327337
classElement: aliasedType.element,
328338
);
329339
}
@@ -393,12 +403,24 @@ class AstRewriter {
393403
return constructorReference;
394404
}
395405

396-
ConstructorReference _toConstructorReference_propertyAccess({
406+
AstNode _toConstructorReference_propertyAccess({
397407
required PropertyAccess node,
398408
required Identifier receiver,
399-
required TypeArgumentList typeArguments,
409+
required TypeArgumentList? typeArguments,
400410
required ClassElement classElement,
401411
}) {
412+
var name = node.propertyName.name;
413+
var constructorElement = name == 'new'
414+
? classElement.unnamedConstructor
415+
: classElement.getNamedConstructor(name);
416+
if (constructorElement == null && typeArguments == null) {
417+
// If there is no constructor by this name, and no type arguments,
418+
// do not rewrite the node. If there _are_ type arguments (like
419+
// `prefix.C<int>.name`, then it looks more like a constructor tearoff
420+
// than anything else, so continue with the rewrite.
421+
return node;
422+
}
423+
402424
var operator = node.operator;
403425

404426
var typeName = astFactory.typeName(receiver, typeArguments);

pkg/analyzer/test/src/dart/resolution/constructor_reference_test.dart

+102
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,108 @@ bar() {
696696
);
697697
}
698698

699+
test_prefixedAlias_nonGeneric_named() async {
700+
newFile('$testPackageLibPath/a.dart', content: '''
701+
class A {
702+
A.foo();
703+
}
704+
typedef TA = A;
705+
''');
706+
await assertNoErrorsInCode('''
707+
import 'a.dart' as a;
708+
bar() {
709+
a.TA.foo;
710+
}
711+
''');
712+
713+
var classElement =
714+
findElement.importFind('package:test/a.dart').class_('A');
715+
assertConstructorReference(
716+
findNode.constructorReference('a.TA.foo;'),
717+
classElement.getNamedConstructor('foo'),
718+
classElement,
719+
'A Function()',
720+
expectedPrefix: findElement.import('package:test/a.dart').prefix,
721+
expectedTypeNameElement:
722+
findElement.importFind('package:test/a.dart').typeAlias('TA'),
723+
);
724+
}
725+
726+
test_prefixedAlias_nonGeneric_unnamed() async {
727+
newFile('$testPackageLibPath/a.dart', content: '''
728+
class A {
729+
A();
730+
}
731+
typedef TA = A;
732+
''');
733+
await assertNoErrorsInCode('''
734+
import 'a.dart' as a;
735+
bar() {
736+
a.TA.new;
737+
}
738+
''');
739+
740+
var classElement =
741+
findElement.importFind('package:test/a.dart').class_('A');
742+
assertConstructorReference(
743+
findNode.constructorReference('a.TA.new;'),
744+
classElement.unnamedConstructor,
745+
classElement,
746+
'A Function()',
747+
expectedPrefix: findElement.import('package:test/a.dart').prefix,
748+
expectedTypeNameElement:
749+
findElement.importFind('package:test/a.dart').typeAlias('TA'),
750+
);
751+
}
752+
753+
test_prefixedClass_nonGeneric_named() async {
754+
newFile('$testPackageLibPath/a.dart', content: '''
755+
class A {
756+
A.foo();
757+
}
758+
''');
759+
await assertNoErrorsInCode('''
760+
import 'a.dart' as a;
761+
bar() {
762+
a.A.foo;
763+
}
764+
''');
765+
766+
var classElement =
767+
findElement.importFind('package:test/a.dart').class_('A');
768+
assertConstructorReference(
769+
findNode.constructorReference('a.A.foo;'),
770+
classElement.getNamedConstructor('foo'),
771+
classElement,
772+
'A Function()',
773+
expectedPrefix: findElement.import('package:test/a.dart').prefix,
774+
);
775+
}
776+
777+
test_prefixedClass_nonGeneric_unnamed() async {
778+
newFile('$testPackageLibPath/a.dart', content: '''
779+
class A {
780+
A();
781+
}
782+
''');
783+
await assertNoErrorsInCode('''
784+
import 'a.dart' as a;
785+
bar() {
786+
a.A.new;
787+
}
788+
''');
789+
790+
var classElement =
791+
findElement.importFind('package:test/a.dart').class_('A');
792+
assertConstructorReference(
793+
findNode.constructorReference('a.A.new;'),
794+
classElement.unnamedConstructor,
795+
classElement,
796+
'A Function()',
797+
expectedPrefix: findElement.import('package:test/a.dart').prefix,
798+
);
799+
}
800+
699801
test_typeAlias_generic_const() async {
700802
await assertNoErrorsInCode('''
701803
class A<T> {

0 commit comments

Comments
 (0)