Skip to content

Commit 9b116d0

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[cfe] Dot shorthands - Const constructors
Parse and build dot shorthand invocations that are constant. Added a new listener to handle and store the const-ness. It didn't feel right re-using any of the other `beginConstPattern` methods. Added an error message if invoking a non-const constructor where we expected a const constructor. Bug: #59758 Change-Id: I8551e3b8f71e89a69d090510bb64694d5e09247d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414660 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent cf9f45f commit 9b116d0

File tree

38 files changed

+604
-33
lines changed

38 files changed

+604
-33
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

+10
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,16 @@ class ForwardingListener implements Listener {
22032203
void handleDotShorthandContext(Token token) {
22042204
listener?.handleDotShorthandContext(token);
22052205
}
2206+
2207+
@override
2208+
void beginConstDotShorthand(Token token) {
2209+
listener?.beginConstDotShorthand(token);
2210+
}
2211+
2212+
@override
2213+
void endConstDotShorthand(Token token) {
2214+
listener?.beginConstDotShorthand(token);
2215+
}
22062216
}
22072217

22082218
class NullListener extends ForwardingListener {

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

+6
Original file line numberDiff line numberDiff line change
@@ -2400,4 +2400,10 @@ class Listener implements UnescapeErrorListener {
24002400
void handleDotShorthandHead(Token token) {
24012401
logEvent('DotShorthandHead');
24022402
}
2403+
2404+
void beginConstDotShorthand(Token token) {}
2405+
2406+
void endConstDotShorthand(Token token) {
2407+
logEvent('ConstDotShorthand');
2408+
}
24032409
}

pkg/_fe_analyzer_shared/lib/src/parser/parser_impl.dart

+26-2
Original file line numberDiff line numberDiff line change
@@ -5826,13 +5826,24 @@ class Parser {
58265826
return token;
58275827
}
58285828

5829+
/// Returns `true` if [period] is a `.` and the next token after is an
5830+
/// identifier or the `new` keyword.
5831+
///
5832+
/// This indicates the parsing of a dot shorthand e.g. `.parse(42)`.
5833+
bool _isDotShorthand(Token period) {
5834+
if (period.isA(TokenType.PERIOD) &&
5835+
(period.next!.isIdentifier || period.next!.isA(Keyword.NEW))) {
5836+
return true;
5837+
}
5838+
return false;
5839+
}
5840+
58295841
Token parsePrecedenceExpression(Token token, int precedence,
58305842
bool allowCascades, ConstantPatternContext constantPatternContext) {
58315843
assert(precedence >= 1);
58325844
assert(precedence <= SELECTOR_PRECEDENCE);
58335845

5834-
bool isDotShorthand = token.next!.isA(TokenType.PERIOD) &&
5835-
(token.next!.next!.isIdentifier || token.next!.next!.isA(Keyword.NEW));
5846+
bool isDotShorthand = _isDotShorthand(token.next!);
58365847
if (isDotShorthand) {
58375848
// TODO(kallentu): Once the analyzer implementation is done, we can avoid
58385849
// adding a synthetic identifier completely, but currently, the parser
@@ -7463,6 +7474,19 @@ class Parser {
74637474
assert(false, "Expected either [, [] or < but found neither.");
74647475
}
74657476
}
7477+
7478+
bool isDotShorthand = _isDotShorthand(token.next!);
7479+
if (isDotShorthand) {
7480+
Token dot = token.next!;
7481+
listener.beginConstDotShorthand(constKeyword);
7482+
token = parsePrimary(dot, IdentifierContext.expressionContinuation,
7483+
ConstantPatternContext.explicit);
7484+
listener.handleDotShorthandHead(dot);
7485+
listener.handleDotShorthandContext(dot);
7486+
listener.endConstDotShorthand(constKeyword);
7487+
return token;
7488+
}
7489+
74667490
listener.beginConstExpression(constKeyword);
74677491
token = parseConstructorReference(token, ConstructorReferenceContext.Const,
74687492
/* typeArg = */ potentialTypeArg);

pkg/analyzer/lib/src/fasta/ast_builder.dart

+13
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,19 @@ class AstBuilder extends StackListener {
14931493
constKeyword: constKeyword, expression: pop() as ExpressionImpl));
14941494
}
14951495

1496+
@override
1497+
void endConstDotShorthand(Token token) {
1498+
debugEvent("endConstDotShorthand");
1499+
if (!enabledDotShorthands) {
1500+
_reportFeatureNotEnabled(
1501+
feature: ExperimentalFeatures.dot_shorthands,
1502+
startToken: token,
1503+
);
1504+
}
1505+
1506+
// TODO(kallentu): Handle dot shorthands.
1507+
}
1508+
14961509
@override
14971510
void endConstExpression(Token constKeyword) {
14981511
assert(optional('const', constKeyword));

pkg/front_end/lib/src/kernel/body_builder.dart

+19-3
Original file line numberDiff line numberDiff line change
@@ -6413,6 +6413,7 @@ class BodyBuilder extends StackListenerImpl
64136413
push(
64146414
new ParserErrorGenerator(this, nameToken, cfe.messageSyntheticToken));
64156415
} else if (type is InvalidExpression) {
6416+
// Coverage-ignore-block(suite): Not run.
64166417
push(type);
64176418
} else if (type is Expression) {
64186419
push(createInstantiationAndInvocation(
@@ -9982,7 +9983,6 @@ class BodyBuilder extends StackListenerImpl
99829983
void handleDotShorthandContext(Token token) {
99839984
debugEvent("DotShorthandContext");
99849985
if (!libraryFeatures.dotShorthands.isEnabled) {
9985-
// Coverage-ignore-block(suite): Not run.
99869986
addProblem(
99879987
templateExperimentNotEnabledOffByDefault
99889988
.withArguments(ExperimentalFlag.dotShorthands.name),
@@ -10005,7 +10005,6 @@ class BodyBuilder extends StackListenerImpl
1000510005
void handleDotShorthandHead(Token token) {
1000610006
debugEvent("DotShorthandHead");
1000710007
if (!libraryFeatures.dotShorthands.isEnabled) {
10008-
// Coverage-ignore-block(suite): Not run.
1000910008
addProblem(
1001010009
templateExperimentNotEnabledOffByDefault
1001110010
.withArguments(ExperimentalFlag.dotShorthands.name),
@@ -10024,14 +10023,31 @@ class BodyBuilder extends StackListenerImpl
1002410023
if (selector is InvocationSelector) {
1002510024
// e.g. `.parse(2)`
1002610025
push(forest.createDotShorthandInvocation(
10027-
offsetForToken(token), selector.name, selector.arguments));
10026+
offsetForToken(token), selector.name, selector.arguments,
10027+
nameOffset: offsetForToken(token.next),
10028+
isConst: constantContext == ConstantContext.inferred));
1002810029
} else if (selector is PropertySelector) {
1002910030
// e.g. `.zero`
1003010031
push(forest.createDotShorthandPropertyGet(
1003110032
offsetForToken(token), selector.name));
1003210033
}
1003310034
}
1003410035
}
10036+
10037+
@override
10038+
void beginConstDotShorthand(Token token) {
10039+
debugEvent("beginConstDotShorthand");
10040+
super.push(constantContext);
10041+
constantContext = ConstantContext.inferred;
10042+
}
10043+
10044+
@override
10045+
void endConstDotShorthand(Token token) {
10046+
debugEvent("endConstDotShorthand");
10047+
Object? dotShorthand = pop();
10048+
constantContext = pop() as ConstantContext;
10049+
push(dotShorthand);
10050+
}
1003510051
}
1003610052

1003710053
class Operator {

pkg/front_end/lib/src/kernel/expression_generator.dart

+1
Original file line numberDiff line numberDiff line change
@@ -4440,6 +4440,7 @@ class ParserErrorGenerator extends Generator {
44404440
}
44414441

44424442
@override
4443+
// Coverage-ignore(suite): Not run.
44434444
Expression qualifiedLookup(Token name) {
44444445
return buildProblem();
44454446
}

pkg/front_end/lib/src/kernel/forest.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -934,8 +934,11 @@ class Forest {
934934
}
935935

936936
DotShorthandInvocation createDotShorthandInvocation(
937-
int fileOffset, Name name, Arguments arguments) {
938-
return new DotShorthandInvocation(name, arguments)..fileOffset = fileOffset;
937+
int fileOffset, Name name, Arguments arguments,
938+
{required int nameOffset, required bool isConst}) {
939+
return new DotShorthandInvocation(name, arguments, nameOffset,
940+
isConst: isConst)
941+
..fileOffset = fileOffset;
939942
}
940943

941944
DotShorthandPropertyGet createDotShorthandPropertyGet(

pkg/front_end/lib/src/kernel/internal_ast.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -3254,11 +3254,13 @@ class DotShorthand extends InternalExpression {
32543254
/// This node could represent a shorthand of a static method or a named
32553255
/// constructor.
32563256
class DotShorthandInvocation extends InternalExpression {
3257-
Name name;
3258-
3259-
Arguments arguments;
3257+
final Name name;
3258+
final int nameOffset;
3259+
final Arguments arguments;
3260+
final bool isConst;
32603261

3261-
DotShorthandInvocation(this.name, this.arguments);
3262+
DotShorthandInvocation(this.name, this.arguments, this.nameOffset,
3263+
{required this.isConst});
32623264

32633265
@override
32643266
ExpressionInferenceResult acceptInference(
@@ -3274,6 +3276,9 @@ class DotShorthandInvocation extends InternalExpression {
32743276
@override
32753277
// Coverage-ignore(suite): Not run.
32763278
void toTextInternal(AstPrinter printer) {
3279+
if (isConst) {
3280+
printer.write('const ');
3281+
}
32773282
printer.write('.');
32783283
printer.writeName(name);
32793284
printer.writeArguments(arguments);

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

+30-5
Original file line numberDiff line numberDiff line change
@@ -12151,15 +12151,40 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1215112151
Member? constructor =
1215212152
findConstructor(cachedContext, node.name, node.fileOffset);
1215312153
if (constructor is Constructor) {
12154-
// TODO(kallentu): Const constructors.
12154+
if (!constructor.isConst && node.isConst) {
12155+
Expression replacement = helper.buildProblem(
12156+
messageNonConstConstructor,
12157+
node.nameOffset,
12158+
node.name.text.length);
12159+
return new ExpressionInferenceResult(
12160+
const DynamicType(), replacement);
12161+
}
12162+
1215512163
expr = new ConstructorInvocation(constructor, node.arguments,
12156-
isConst: false)
12164+
isConst: node.isConst)
1215712165
..fileOffset = node.fileOffset;
1215812166
} else if (constructor is Procedure) {
1215912167
// [constructor] can be a [Procedure] if we have an extension type
12160-
// constructor.
12161-
expr = new StaticInvocation(constructor, node.arguments)
12162-
..fileOffset = node.fileOffset;
12168+
// constructor or a redirecting factory constructor.
12169+
if (!constructor.isConst && node.isConst) {
12170+
// Coverage-ignore-block(suite): Not run.
12171+
Expression replacement = helper.buildProblem(
12172+
messageNonConstConstructor,
12173+
node.nameOffset,
12174+
node.name.text.length);
12175+
return new ExpressionInferenceResult(
12176+
const DynamicType(), replacement);
12177+
}
12178+
12179+
if (constructor.isRedirectingFactory) {
12180+
expr = new FactoryConstructorInvocation(constructor, node.arguments,
12181+
isConst: node.isConst)
12182+
..fileOffset = node.fileOffset;
12183+
} else {
12184+
expr = new StaticInvocation(constructor, node.arguments,
12185+
isConst: node.isConst)
12186+
..fileOffset = node.fileOffset;
12187+
}
1216312188
} else {
1216412189
// Coverage-ignore-block(suite): Not run.
1216512190
// TODO(kallentu): This is temporary. Build a problem with an error

pkg/front_end/lib/src/util/parser_ast_helper.dart

+62
Original file line numberDiff line numberDiff line change
@@ -3216,6 +3216,20 @@ abstract class AbstractParserAstListener implements Listener {
32163216
new DotShorthandHeadHandle(ParserAstType.HANDLE, token: token);
32173217
seen(data);
32183218
}
3219+
3220+
@override
3221+
void beginConstDotShorthand(Token token) {
3222+
ConstDotShorthandBegin data =
3223+
new ConstDotShorthandBegin(ParserAstType.BEGIN, token: token);
3224+
seen(data);
3225+
}
3226+
3227+
@override
3228+
void endConstDotShorthand(Token token) {
3229+
ConstDotShorthandEnd data =
3230+
new ConstDotShorthandEnd(ParserAstType.END, token: token);
3231+
seen(data);
3232+
}
32193233
}
32203234

32213235
class ArgumentsBegin extends ParserAstNode {
@@ -10156,6 +10170,36 @@ class DotShorthandHeadHandle extends ParserAstNode {
1015610170
R accept<R>(ParserAstVisitor<R> v) => v.visitDotShorthandHeadHandle(this);
1015710171
}
1015810172

10173+
class ConstDotShorthandBegin extends ParserAstNode {
10174+
final Token token;
10175+
10176+
ConstDotShorthandBegin(ParserAstType type, {required this.token})
10177+
: super("ConstDotShorthand", type);
10178+
10179+
@override
10180+
Map<String, Object?> get deprecatedArguments => {
10181+
"token": token,
10182+
};
10183+
10184+
@override
10185+
R accept<R>(ParserAstVisitor<R> v) => v.visitConstDotShorthandBegin(this);
10186+
}
10187+
10188+
class ConstDotShorthandEnd extends ParserAstNode {
10189+
final Token token;
10190+
10191+
ConstDotShorthandEnd(ParserAstType type, {required this.token})
10192+
: super("ConstDotShorthand", type);
10193+
10194+
@override
10195+
Map<String, Object?> get deprecatedArguments => {
10196+
"token": token,
10197+
};
10198+
10199+
@override
10200+
R accept<R>(ParserAstVisitor<R> v) => v.visitConstDotShorthandEnd(this);
10201+
}
10202+
1015910203
abstract class ParserAstVisitor<R> {
1016010204
R visitArgumentsBegin(ArgumentsBegin node);
1016110205
R visitArgumentsEnd(ArgumentsEnd node);
@@ -10552,6 +10596,8 @@ abstract class ParserAstVisitor<R> {
1055210596
R visitPatternAssignmentHandle(PatternAssignmentHandle node);
1055310597
R visitDotShorthandContextHandle(DotShorthandContextHandle node);
1055410598
R visitDotShorthandHeadHandle(DotShorthandHeadHandle node);
10599+
R visitConstDotShorthandBegin(ConstDotShorthandBegin node);
10600+
R visitConstDotShorthandEnd(ConstDotShorthandEnd node);
1055510601
}
1055610602

1055710603
class RecursiveParserAstVisitor implements ParserAstVisitor<void> {
@@ -11970,6 +12016,14 @@ class RecursiveParserAstVisitor implements ParserAstVisitor<void> {
1197012016
@override
1197112017
void visitDotShorthandHeadHandle(DotShorthandHeadHandle node) =>
1197212018
node.visitChildren(this);
12019+
12020+
@override
12021+
void visitConstDotShorthandBegin(ConstDotShorthandBegin node) =>
12022+
node.visitChildren(this);
12023+
12024+
@override
12025+
void visitConstDotShorthandEnd(ConstDotShorthandEnd node) =>
12026+
node.visitChildren(this);
1197312027
}
1197412028

1197512029
class RecursiveParserAstVisitorWithDefaultNodeAsync
@@ -13466,4 +13520,12 @@ class RecursiveParserAstVisitorWithDefaultNodeAsync
1346613520
@override
1346713521
Future<void> visitDotShorthandHeadHandle(DotShorthandHeadHandle node) =>
1346813522
defaultNode(node);
13523+
13524+
@override
13525+
Future<void> visitConstDotShorthandBegin(ConstDotShorthandBegin node) =>
13526+
defaultNode(node);
13527+
13528+
@override
13529+
Future<void> visitConstDotShorthandEnd(ConstDotShorthandEnd node) =>
13530+
defaultNode(node);
1346913531
}

pkg/front_end/test/coverage_suite_expected.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
625625
),
626626
// 100.0%.
627627
"package:front_end/src/kernel/body_builder.dart": (
628-
hitCount: 7217,
628+
hitCount: 7246,
629629
missCount: 0,
630630
),
631631
// 100.0%.
@@ -685,7 +685,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
685685
),
686686
// 100.0%.
687687
"package:front_end/src/kernel/expression_generator.dart": (
688-
hitCount: 2490,
688+
hitCount: 2488,
689689
missCount: 0,
690690
),
691691
// 100.0%.
@@ -1011,7 +1011,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
10111011
),
10121012
// 100.0%.
10131013
"package:front_end/src/type_inference/inference_visitor.dart": (
1014-
hitCount: 8221,
1014+
hitCount: 8236,
10151015
missCount: 0,
10161016
),
10171017
// 100.0%.

pkg/front_end/test/parser_test_listener.dart

+14
Original file line numberDiff line numberDiff line change
@@ -3343,4 +3343,18 @@ class ParserTestListener implements Listener {
33433343
seen(token);
33443344
doPrint('handleDotShorthandHead(' '$token)');
33453345
}
3346+
3347+
@override
3348+
void beginConstDotShorthand(Token token) {
3349+
seen(token);
3350+
doPrint('beginConstDotShorthand(' '$token)');
3351+
indent++;
3352+
}
3353+
3354+
@override
3355+
void endConstDotShorthand(Token token) {
3356+
indent--;
3357+
seen(token);
3358+
doPrint('endConstDotShorthand(' '$token)');
3359+
}
33463360
}

pkg/front_end/testcases/constructor_tearoffs/issue46133.dart.strong.expect

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ library;
22
//
33
// Problems in library:
44
//
5-
// pkg/front_end/testcases/constructor_tearoffs/issue46133.dart:7:18: Error: Expected an identifier, but got '.'.
6-
// Try inserting an identifier before '.'.
5+
// pkg/front_end/testcases/constructor_tearoffs/issue46133.dart:7:18: Error: This requires the experimental 'dot-shorthands' language feature to be enabled.
6+
// Try passing the '--enable-experiment=dot-shorthands' command line option.
77
// test() => A.const.toString();
88
// ^
99
//

0 commit comments

Comments
 (0)