mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:39:48 +00:00
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: https://github.com/dart-lang/sdk/issues/46020 Change-Id: Iaf3af333dd22337b560aa7f4e5811a4cb38b2a7f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208760 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
cf3387efd3
commit
9ec112a509
|
@ -440,7 +440,7 @@ C c() => C.new(C.new());
|
|||
Future<void> test_rename_removed() async {
|
||||
setPackageContent('''
|
||||
class C {
|
||||
C.new([C c]);
|
||||
C.updated([C c]);
|
||||
}
|
||||
''');
|
||||
addPackageDataFile('''
|
||||
|
@ -454,7 +454,7 @@ transforms:
|
|||
inClass: 'C'
|
||||
changes:
|
||||
- kind: 'rename'
|
||||
newName: 'new'
|
||||
newName: 'updated'
|
||||
''');
|
||||
await resolveTestCode('''
|
||||
import '$importUri';
|
||||
|
@ -462,7 +462,7 @@ C c() => C(C());
|
|||
''');
|
||||
await assertHasFix('''
|
||||
import '$importUri';
|
||||
C c() => C.new(C.new());
|
||||
C c() => C.updated(C.updated());
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -948,6 +948,10 @@ class ClassElementImpl extends AbstractClassElementImpl
|
|||
|
||||
static ConstructorElement? getNamedConstructorFromList(
|
||||
String name, List<ConstructorElement> constructors) {
|
||||
if (name == 'new') {
|
||||
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
|
||||
name = '';
|
||||
}
|
||||
for (ConstructorElement element in constructors) {
|
||||
if (element.name == name) {
|
||||
return element;
|
||||
|
|
|
@ -282,19 +282,6 @@ class MethodInvocationResolver {
|
|||
);
|
||||
}
|
||||
|
||||
void _reportUndefinedMethod(
|
||||
MethodInvocationImpl node,
|
||||
String name,
|
||||
ClassElement typeReference,
|
||||
List<WhyNotPromotedGetter> whyNotPromotedList) {
|
||||
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_METHOD,
|
||||
node.methodName,
|
||||
[name, typeReference.displayName],
|
||||
);
|
||||
}
|
||||
|
||||
void _reportUseOfVoidType(MethodInvocationImpl node, AstNode errorNode,
|
||||
List<WhyNotPromotedGetter> whyNotPromotedList) {
|
||||
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
|
||||
|
@ -779,7 +766,26 @@ class MethodInvocationResolver {
|
|||
return;
|
||||
}
|
||||
|
||||
_reportUndefinedMethod(node, name, receiver, whyNotPromotedList);
|
||||
_setDynamicResolution(node, whyNotPromotedList: whyNotPromotedList);
|
||||
if (nameNode.name == 'new') {
|
||||
// Attempting to invoke the unnamed constructor via `C.new(`.
|
||||
if (_resolver.isConstructorTearoffsEnabled) {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
|
||||
nameNode,
|
||||
[receiver.displayName],
|
||||
);
|
||||
} else {
|
||||
// [ParserErrorCode.EXPERIMENT_NOT_ENABLED] is reported by the parser.
|
||||
// Do not report extra errors.
|
||||
}
|
||||
} else {
|
||||
_resolver.errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.UNDEFINED_METHOD,
|
||||
node.methodName,
|
||||
[name, receiver.displayName],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// If the given [type] is a type parameter, replace with its bound.
|
||||
|
|
|
@ -376,6 +376,9 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
InstanceCreationExpressionResolver(this);
|
||||
}
|
||||
|
||||
bool get isConstructorTearoffsEnabled =>
|
||||
_featureSet.isEnabled(Feature.constructor_tearoffs);
|
||||
|
||||
/// Return the object providing promoted or declared types of variables.
|
||||
LocalVariableTypeProvider get localVariableTypeProvider {
|
||||
if (flowAnalysis != null) {
|
||||
|
|
|
@ -106,9 +106,11 @@ class ElementFactory {
|
|||
static ConstructorElementImpl constructorElement(
|
||||
ClassElement definingClass, String? name, bool isConst,
|
||||
[List<DartType> argumentTypes = const []]) {
|
||||
ConstructorElementImpl constructor = name == null
|
||||
? ConstructorElementImpl("", -1)
|
||||
: ConstructorElementImpl(name, 0);
|
||||
var offset = name == null ? -1 : 0;
|
||||
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
|
||||
var constructor = name == null || name == 'new'
|
||||
? ConstructorElementImpl('', offset)
|
||||
: ConstructorElementImpl(name, offset);
|
||||
if (name != null) {
|
||||
if (name.isEmpty) {
|
||||
constructor.nameEnd = definingClass.name.length;
|
||||
|
|
|
@ -172,6 +172,10 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
|
|||
) {
|
||||
var nameNode = node.name ?? node.returnType;
|
||||
var name = node.name?.name ?? '';
|
||||
if (name == 'new') {
|
||||
// An unnamed constructor declared with `C.new(` is modeled as unnamed.
|
||||
name = '';
|
||||
}
|
||||
var nameOffset = nameNode.offset;
|
||||
|
||||
var element = ConstructorElementImpl(name, nameOffset);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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/error/syntactic_errors.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
|
@ -10,6 +11,7 @@ import 'context_collection_resolution.dart';
|
|||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(InstanceCreationTest);
|
||||
defineReflectiveTests(InstanceCreationWithoutConstructorTearoffsTest);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -426,4 +428,75 @@ void f() {
|
|||
expectedSubstitution: {'T': 'String'},
|
||||
);
|
||||
}
|
||||
|
||||
test_unnamed_declaredNew() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A.new(int a);
|
||||
}
|
||||
|
||||
void f() {
|
||||
A(0);
|
||||
}
|
||||
|
||||
''');
|
||||
|
||||
var creation = findNode.instanceCreation('A(0)');
|
||||
assertInstanceCreation(creation, findElement.class_('A'), 'A');
|
||||
}
|
||||
|
||||
test_unnamedViaNew_declaredNew() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A.new(int a);
|
||||
}
|
||||
|
||||
void f() {
|
||||
A.new(0);
|
||||
}
|
||||
|
||||
''');
|
||||
|
||||
var creation = findNode.instanceCreation('A.new(0)');
|
||||
assertInstanceCreation(creation, findElement.class_('A'), 'A');
|
||||
}
|
||||
|
||||
test_unnamedViaNew_declaredUnnamed() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A(int a);
|
||||
}
|
||||
|
||||
void f() {
|
||||
A.new(0);
|
||||
}
|
||||
|
||||
''');
|
||||
|
||||
var creation = findNode.instanceCreation('A.new(0)');
|
||||
assertInstanceCreation(creation, findElement.class_('A'), 'A');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class InstanceCreationWithoutConstructorTearoffsTest
|
||||
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
|
||||
test_unnamedViaNew() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A(int a);
|
||||
}
|
||||
|
||||
void f() {
|
||||
A.new(0);
|
||||
}
|
||||
|
||||
''', [
|
||||
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 40, 3),
|
||||
]);
|
||||
|
||||
// Resolution should continue even though the experiment is not enabled.
|
||||
var creation = findNode.instanceCreation('A.new(0)');
|
||||
assertInstanceCreation(creation, findElement.class_('A'), 'A');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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/error/syntactic_errors.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
|
@ -10,11 +11,16 @@ import '../dart/resolution/context_collection_resolution.dart';
|
|||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(NewWithUndefinedConstructorTest);
|
||||
defineReflectiveTests(
|
||||
NewWithUndefinedConstructorWithoutConstructorTearoffsTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class NewWithUndefinedConstructorTest extends PubPackageResolutionTest {
|
||||
class NewWithUndefinedConstructorTest extends PubPackageResolutionTest
|
||||
with NewWithUndefinedConstructorTestCases {}
|
||||
|
||||
mixin NewWithUndefinedConstructorTestCases on PubPackageResolutionTest {
|
||||
test_default() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
|
@ -28,6 +34,43 @@ f() {
|
|||
]);
|
||||
}
|
||||
|
||||
test_default_noKeyword() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A.name() {}
|
||||
}
|
||||
f() {
|
||||
A();
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, 34, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_default_unnamedViaNew() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A.name() {}
|
||||
}
|
||||
f() {
|
||||
A.new();
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT, 36, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_defaultViaNew() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
A.new() {}
|
||||
}
|
||||
f() {
|
||||
A();
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_defined_named() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {
|
||||
|
@ -111,3 +154,33 @@ void f() {
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class NewWithUndefinedConstructorWithoutConstructorTearoffsTest
|
||||
extends PubPackageResolutionTest with WithoutConstructorTearoffsMixin {
|
||||
test_defaultViaNew() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A.new() {}
|
||||
}
|
||||
f() {
|
||||
A();
|
||||
}
|
||||
''', [
|
||||
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 14, 3),
|
||||
]);
|
||||
}
|
||||
|
||||
test_unnamedViaNew() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
A.named() {}
|
||||
}
|
||||
f() {
|
||||
A.new();
|
||||
}
|
||||
''', [
|
||||
error(ParserErrorCode.EXPERIMENT_NOT_ENABLED, 37, 3),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue