Skip to content

Commit ecc6b93

Browse files
kallentuCommit Queue
authored andcommitted
[cfe/analyzer] Dot shorthands: Abstract constructors can't be instantiated or torn off.
Adding a few errors on when we instantiate or tear off abstract constructors. Should be parallel to how we currently check for these errors in the CFE and analyzer. Follow up to https://dart-review.googlesource.com/c/sdk/+/426682 Language test added, co19 test passing, and unit tests added. Bug: #59758, #59835 Change-Id: I9b34e5f960856ce50aa969ca27e56907ae3a2a8b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428201 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 77d673c commit ecc6b93

File tree

11 files changed

+206
-0
lines changed

11 files changed

+206
-0
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ class PropertyElementResolver with ScopeHelpers {
6464
_definingLibrary,
6565
);
6666
if (element != null) {
67+
if (!element.isFactory) {
68+
var enclosingElement = element.enclosingElement2;
69+
if (enclosingElement is ClassElementImpl2 &&
70+
enclosingElement.isAbstract) {
71+
_resolver.errorReporter.atNode(
72+
node,
73+
CompileTimeErrorCode
74+
.TEAROFF_OF_GENERATIVE_CONSTRUCTOR_OF_ABSTRACT_CLASS,
75+
);
76+
}
77+
}
78+
6779
return PropertyElementResolverResult(
6880
readElementRequested2: element,
6981
getType: element.returnType,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,24 @@ DotShorthandPropertyAccess
591591
''');
592592
}
593593

594+
test_tearOff_constructor_abstract() async {
595+
await assertErrorsInCode(
596+
r'''
597+
Function fn() {
598+
return .new;
599+
}
600+
''',
601+
[
602+
error(
603+
CompileTimeErrorCode
604+
.TEAROFF_OF_GENERATIVE_CONSTRUCTOR_OF_ABSTRACT_CLASS,
605+
25,
606+
4,
607+
),
608+
],
609+
);
610+
}
611+
594612
test_tearOff_constructor_new() async {
595613
await assertNoErrorsInCode('''
596614
void main() {

pkg/front_end/lib/src/type_inference/inference_visitor.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12172,6 +12172,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1217212172
node.name.text.length));
1217312173
}
1217412174

12175+
TypeDeclaration typeDeclaration = cachedContext.typeDeclaration;
12176+
if (typeDeclaration is Class && typeDeclaration.isAbstract) {
12177+
return new ExpressionInferenceResult(
12178+
const DynamicType(),
12179+
helper.buildProblem(
12180+
templateAbstractClassInstantiation
12181+
.withArguments(typeDeclaration.name),
12182+
node.nameOffset,
12183+
node.name.text.length));
12184+
}
12185+
1217512186
// The shorthand expression is inferred in the empty context and then
1217612187
// type inference infers the type arguments.
1217712188
FunctionType functionType = constructor.function
@@ -12310,6 +12321,14 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1231012321
node.name.text.length));
1231112322
}
1231212323
if (constructor is Constructor) {
12324+
TypeDeclaration typeDeclaration = cachedContext.typeDeclaration;
12325+
if (typeDeclaration is Class && typeDeclaration.isAbstract) {
12326+
return new ExpressionInferenceResult(
12327+
const DynamicType(),
12328+
helper.buildProblem(messageAbstractClassConstructorTearOff,
12329+
node.nameOffset, node.name.text.length));
12330+
}
12331+
1231312332
DartType type = constructor.function
1231412333
.computeFunctionType(Nullability.nonNullable);
1231512334
Expression tearOff = new ConstructorTearOff(constructor)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
Function instantiation() {
6+
return .new(); // Error
7+
}
8+
9+
Function tearOff() {
10+
return .new; // Error
11+
}
12+
13+
void main() async {
14+
var iter = [1, 2];
15+
await for (var x in .fromIterable(iter)) { // No error.
16+
print(x);
17+
}
18+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
6+
// return .new(); // Error
7+
// ^^^
8+
//
9+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
10+
// return .new; // Error
11+
// ^^^
12+
//
13+
import self as self;
14+
import "dart:core" as core;
15+
import "dart:async" as asy;
16+
17+
static method instantiation() → core::Function {
18+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
19+
return .new(); // Error
20+
^^^" as{TypeError,ForDynamic} core::Function;
21+
}
22+
static method tearOff() → core::Function {
23+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
24+
return .new; // Error
25+
^^^" as{TypeError,ForDynamic} core::Function;
26+
}
27+
static method main() → void async /* emittedValueType= void */ {
28+
core::List<core::int> iter = <core::int>[1, 2];
29+
await for (core::int x in asy::Stream::fromIterable<core::int>(iter)) {
30+
core::print(x);
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
6+
// return .new(); // Error
7+
// ^^^
8+
//
9+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
10+
// return .new; // Error
11+
// ^^^
12+
//
13+
import self as self;
14+
import "dart:core" as core;
15+
import "dart:async" as asy;
16+
17+
static method instantiation() → core::Function {
18+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
19+
return .new(); // Error
20+
^^^" as{TypeError,ForDynamic} core::Function;
21+
}
22+
static method tearOff() → core::Function {
23+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
24+
return .new; // Error
25+
^^^" as{TypeError,ForDynamic} core::Function;
26+
}
27+
static method main() → void async /* emittedValueType= void */ {
28+
core::List<core::int> iter = <core::int>[1, 2];
29+
await for (core::int x in asy::Stream::fromIterable<core::int>(iter)) {
30+
core::print(x);
31+
}
32+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
static method instantiation() → core::Function
6+
;
7+
static method tearOff() → core::Function
8+
;
9+
static method main() → void async
10+
;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
6+
// return .new(); // Error
7+
// ^^^
8+
//
9+
// pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
10+
// return .new; // Error
11+
// ^^^
12+
//
13+
import self as self;
14+
import "dart:core" as core;
15+
import "dart:async" as asy;
16+
17+
static method instantiation() → core::Function {
18+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:6:11: Error: The class 'Function' is abstract and can't be instantiated.
19+
return .new(); // Error
20+
^^^" as{TypeError,ForDynamic} core::Function;
21+
}
22+
static method tearOff() → core::Function {
23+
return invalid-expression "pkg/front_end/testcases/dot_shorthands/abstract_class.dart:10:11: Error: Constructors on abstract classes can't be torn off.
24+
return .new; // Error
25+
^^^" as{TypeError,ForDynamic} core::Function;
26+
}
27+
static method main() → void async /* emittedValueType= void */ {
28+
core::List<core::int> iter = core::_GrowableList::_literal2<core::int>(1, 2);
29+
{
30+
synthesized asy::Stream<core::int> :stream = asy::Stream::fromIterable<core::int>(iter);
31+
synthesized asy::_StreamIterator<core::int>? :for-iterator = new asy::_StreamIterator::•<core::int>(:stream);
32+
try
33+
while (let dynamic #t1 = asy::_asyncStarMoveNextHelper(:stream) in await :for-iterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>}) {
34+
core::int x = :for-iterator.{asy::_StreamIterator::current}{core::int};
35+
{
36+
core::print(x);
37+
}
38+
}
39+
finally
40+
if(!(:for-iterator.{asy::_StreamIterator::_subscription}{asy::StreamSubscription<core::int>?} == null))
41+
await :for-iterator.{asy::_StreamIterator::cancel}(){() → asy::Future<dynamic>};
42+
}
43+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Function instantiation() {}
2+
3+
Function tearOff() {}
4+
5+
void main() async {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Function instantiation() {}
2+
3+
Function tearOff() {}
4+
5+
void main() async {}

0 commit comments

Comments
 (0)