Skip to content

Commit 9ec112a

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
analyzer: Support declaring and invoking unnamed constructor via 'new'
This adds support for both declaring an unnamed constructor with the explicit name, "new", and support for invoking an unnamed constructor as a named constructor named "new". The parser will report EXPERIMENT_NOT_ENABLED if the experiment is not enabled, as the parser takes care around the keyword, "new". Tearoff support will be separate. Bug: #46020 Change-Id: Iaf3af333dd22337b560aa7f4e5811a4cb38b2a7f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208760 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent cf3387e commit 9ec112a

File tree

8 files changed

+186
-21
lines changed

8 files changed

+186
-21
lines changed

pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ C c() => C.new(C.new());
440440
Future<void> test_rename_removed() async {
441441
setPackageContent('''
442442
class C {
443-
C.new([C c]);
443+
C.updated([C c]);
444444
}
445445
''');
446446
addPackageDataFile('''
@@ -454,15 +454,15 @@ transforms:
454454
inClass: 'C'
455455
changes:
456456
- kind: 'rename'
457-
newName: 'new'
457+
newName: 'updated'
458458
''');
459459
await resolveTestCode('''
460460
import '$importUri';
461461
C c() => C(C());
462462
''');
463463
await assertHasFix('''
464464
import '$importUri';
465-
C c() => C.new(C.new());
465+
C c() => C.updated(C.updated());
466466
''');
467467
}
468468
}

pkg/analyzer/lib/src/dart/element/element.dart

+4
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,10 @@ class ClassElementImpl extends AbstractClassElementImpl
948948

949949
static ConstructorElement? getNamedConstructorFromList(
950950
String name, List<ConstructorElement> constructors) {
951+
if (name == 'new') {
952+
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
953+
name = '';
954+
}
951955
for (ConstructorElement element in constructors) {
952956
if (element.name == name) {
953957
return element;

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

+20-14
Original file line numberDiff line numberDiff line change
@@ -282,19 +282,6 @@ class MethodInvocationResolver {
282282
);
283283
}
284284

285-
void _reportUndefinedMethod(
286-
MethodInvocationImpl node,
287-
String name,
288-
ClassElement typeReference,
289-
List<WhyNotPromotedGetter> whyNotPromotedList) {
290-
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
291-
_resolver.errorReporter.reportErrorForNode(
292-
CompileTimeErrorCode.UNDEFINED_METHOD,
293-
node.methodName,
294-
[name, typeReference.displayName],
295-
);
296-
}
297-
298285
void _reportUseOfVoidType(MethodInvocationImpl node, AstNode errorNode,
299286
List<WhyNotPromotedGetter> whyNotPromotedList) {
300287
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
@@ -779,7 +766,26 @@ class MethodInvocationResolver {
779766
return;
780767
}
781768

782-
_reportUndefinedMethod(node, name, receiver, whyNotPromotedList);
769+
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
770+
if (nameNode.name == 'new') {
771+
// Attempting to invoke the unnamed constructor via `C.new(`.
772+
if (_resolver.isConstructorTearoffsEnabled) {
773+
_resolver.errorReporter.reportErrorForNode(
774+
CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
775+
nameNode,
776+
[receiver.displayName],
777+
);
778+
} else {
779+
// [ParserErrorCode.EXPERIMENT_NOT_ENABLED] is reported by the parser.
780+
// Do not report extra errors.
781+
}
782+
} else {
783+
_resolver.errorReporter.reportErrorForNode(
784+
CompileTimeErrorCode.UNDEFINED_METHOD,
785+
node.methodName,
786+
[name, receiver.displayName],
787+
);
788+
}
783789
}
784790

785791
/// If the given [type] is a type parameter, replace with its bound.

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

+3
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
376376
InstanceCreationExpressionResolver(this);
377377
}
378378

379+
bool get isConstructorTearoffsEnabled =>
380+
_featureSet.isEnabled(Feature.constructor_tearoffs);
381+
379382
/// Return the object providing promoted or declared types of variables.
380383
LocalVariableTypeProvider get localVariableTypeProvider {
381384
if (flowAnalysis != null) {

pkg/analyzer/lib/src/generated/testing/element_factory.dart

+5-3
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,11 @@ class ElementFactory {
106106
static ConstructorElementImpl constructorElement(
107107
ClassElement definingClass, String? name, bool isConst,
108108
[List<DartType> argumentTypes = const []]) {
109-
ConstructorElementImpl constructor = name == null
110-
? ConstructorElementImpl("", -1)
111-
: ConstructorElementImpl(name, 0);
109+
var offset = name == null ? -1 : 0;
110+
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
111+
var constructor = name == null || name == 'new'
112+
? ConstructorElementImpl('', offset)
113+
: ConstructorElementImpl(name, offset);
112114
if (name != null) {
113115
if (name.isEmpty) {
114116
constructor.nameEnd = definingClass.name.length;

pkg/analyzer/lib/src/summary2/element_builder.dart

+4
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
172172
) {
173173
var nameNode = node.name ?? node.returnType;
174174
var name = node.name?.name ?? '';
175+
if (name == 'new') {
176+
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
177+
name = '';
178+
}
175179
var nameOffset = nameNode.offset;
176180

177181
var element = ConstructorElementImpl(name, nameOffset);

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

+73
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
56
import 'package:analyzer/src/error/codes.dart';
67
import 'package:test_reflective_loader/test_reflective_loader.dart';
78

@@ -10,6 +11,7 @@ import 'context_collection_resolution.dart';
1011
main() {
1112
defineReflectiveSuite(() {
1213
defineReflectiveTests(InstanceCreationTest);
14+
defineReflectiveTests(InstanceCreationWithoutConstructorTearoffsTest);
1315
});
1416
}
1517

@@ -426,4 +428,75 @@ void f() {
426428
expectedSubstitution: {'T': 'String'},
427429
);
428430
}
431+
432+
test_unnamed_declaredNew() async {
433+
await assertNoErrorsInCode('''
434+
class A {
435+
A.new(int a);
436+
}
437+
438+
void f() {
439+
A(0);
440+
}
441+
442+
''');
443+
444+
var creation = findNode.instanceCreation('A(0)');
445+
assertInstanceCreation(creation, findElement.class_('A'), 'A');
446+
}
447+
448+
test_unnamedViaNew_declaredNew() async {
449+
await assertNoErrorsInCode('''
450+
class A {
451+
A.new(int a);
452+
}
453+
454+
void f() {
455+
A.new(0);
456+
}
457+
458+
''');
459+
460+
var creation = findNode.instanceCreation('A.new(0)');
461+
assertInstanceCreation(creation, findElement.class_('A'), 'A');
462+
}
463+
464+
test_unnamedViaNew_declaredUnnamed() async {
465+
await assertNoErrorsInCode('''
466+
class A {
467+
A(int a);
468+
}
469+
470+
void f() {
471+
A.new(0);
472+
}
473+
474+
''');
475+
476+
var creation = findNode.instanceCreation('A.new(0)');
477+
assertInstanceCreation(creation, findElement.class_('A'), 'A');
478+
}
479+
}
480+
481+
@reflectiveTest
482+
class InstanceCreationWithoutConstructorTearoffsTest
483+
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
484+
test_unnamedViaNew() async {
485+
await assertErrorsInCode('''
486+
class A {
487+
A(int a);
488+
}
489+
490+
void f() {
491+
A.new(0);
492+
}
493+
494+
''', [
495+
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 40, 3),
496+
]);
497+
498+
// Resolution should continue even though the experiment is not enabled.
499+
var creation = findNode.instanceCreation('A.new(0)');
500+
assertInstanceCreation(creation, findElement.class_('A'), 'A');
501+
}
429502
}

pkg/analyzer/test/src/diagnostics/new_with_undefined_constructor_test.dart

+74-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
56
import 'package:analyzer/src/error/codes.dart';
67
import 'package:test_reflective_loader/test_reflective_loader.dart';
78

@@ -10,11 +11,16 @@ import '../dart/resolution/context_collection_resolution.dart';
1011
main() {
1112
defineReflectiveSuite(() {
1213
defineReflectiveTests(NewWithUndefinedConstructorTest);
14+
defineReflectiveTests(
15+
NewWithUndefinedConstructorWithoutConstructorTearoffsTest);
1316
});
1417
}
1518

1619
@reflectiveTest
17-
class NewWithUndefinedConstructorTest extends PubPackageResolutionTest {
20+
class NewWithUndefinedConstructorTest extends PubPackageResolutionTest
21+
with NewWithUndefinedConstructorTestCases {}
22+
23+
mixin NewWithUndefinedConstructorTestCases on PubPackageResolutionTest {
1824
test_default() async {
1925
await assertErrorsInCode('''
2026
class A {
@@ -28,6 +34,43 @@ f() {
2834
]);
2935
}
3036

37+
test_default_noKeyword() async {
38+
await assertErrorsInCode('''
39+
class A {
40+
A.name() {}
41+
}
42+
f() {
43+
A();
44+
}
45+
''', [
46+
error(CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, 34, 1),
47+
]);
48+
}
49+
50+
test_default_unnamedViaNew() async {
51+
await assertErrorsInCode('''
52+
class A {
53+
A.name() {}
54+
}
55+
f() {
56+
A.new();
57+
}
58+
''', [
59+
error(CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, 36, 3),
60+
]);
61+
}
62+
63+
test_defaultViaNew() async {
64+
await assertNoErrorsInCode('''
65+
class A {
66+
A.new() {}
67+
}
68+
f() {
69+
A();
70+
}
71+
''');
72+
}
73+
3174
test_defined_named() async {
3275
await assertNoErrorsInCode(r'''
3376
class A {
@@ -111,3 +154,33 @@ void f() {
111154
]);
112155
}
113156
}
157+
158+
@reflectiveTest
159+
class NewWithUndefinedConstructorWithoutConstructorTearoffsTest
160+
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
161+
test_defaultViaNew() async {
162+
await assertErrorsInCode('''
163+
class A {
164+
A.new() {}
165+
}
166+
f() {
167+
A();
168+
}
169+
''', [
170+
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 14, 3),
171+
]);
172+
}
173+
174+
test_unnamedViaNew() async {
175+
await assertErrorsInCode('''
176+
class A {
177+
A.named() {}
178+
}
179+
f() {
180+
A.new();
181+
}
182+
''', [
183+
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 37, 3),
184+
]);
185+
}
186+
}

0 commit comments

Comments
 (0)