Skip to content

Commit 34b74d8

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[cfe] Handle static gets and tearoffs in dot shorthands
Resolve static getters and tear-offs in dot shorthands. Method and constructor invocations will come in a later CL. Also, looking to update the parser handling in a future CL, but we'll work incrementally. Bug: #59758 Change-Id: I15c9eb7e531975ea19d496a03ac4b666fa15a04e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/411940 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 46c6f9c commit 34b74d8

30 files changed

+442
-21
lines changed

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

+20-3
Original file line numberDiff line numberDiff line change
@@ -9976,23 +9976,29 @@ class BodyBuilder extends StackListenerImpl
99769976
}
99779977

99789978
@override
9979-
// Coverage-ignore(suite): Not run.
99809979
void handleDotShorthandContext(Token token) {
99819980
debugEvent("DotShorthandContext");
99829981
if (!libraryFeatures.dotShorthands.isEnabled) {
9982+
// Coverage-ignore-block(suite): Not run.
99839983
addProblem(
99849984
templateExperimentNotEnabledOffByDefault
99859985
.withArguments(ExperimentalFlag.dotShorthands.name),
99869986
token.offset,
99879987
token.length);
9988+
return;
99889989
}
9990+
9991+
// TODO(kallentu): Possibly could be ProblemBuilder? Testing needed.
9992+
assert(checkState(token, [ValueKinds.Expression]));
9993+
Expression value = pop() as Expression;
9994+
push(forest.createDotShorthandContext(token.charOffset, value));
99899995
}
99909996

99919997
@override
9992-
// Coverage-ignore(suite): Not run.
99939998
void handleDotShorthandHead(Token token) {
99949999
debugEvent("DotShorthandHead");
999510000
if (!libraryFeatures.dotShorthands.isEnabled) {
10001+
// Coverage-ignore-block(suite): Not run.
999610002
addProblem(
999710003
templateExperimentNotEnabledOffByDefault
999810004
.withArguments(ExperimentalFlag.dotShorthands.name),
@@ -10001,9 +10007,20 @@ class BodyBuilder extends StackListenerImpl
1000110007

1000210008
// Recovery, avoid crashing with an extra selector.
1000310009
pop();
10010+
return;
1000410011
}
1000510012

10006-
// TODO(kallentu): Handle dot shorthands.
10013+
Object? selector = pop();
10014+
if (libraryFeatures.dotShorthands.isEnabled && selector is Selector) {
10015+
// TODO(kallentu): Remove this once we have more of the dot shorthands
10016+
// implementation complete.
10017+
pop(); // ParserGeneratorError
10018+
10019+
// TODO(kallentu): Handle invocations.
10020+
10021+
push(forest.createDotShorthandPropertyGet(
10022+
offsetForToken(token), selector.name));
10023+
}
1000710024
}
1000810025
}
1000910026

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

-2
Original file line numberDiff line numberDiff line change
@@ -928,13 +928,11 @@ class Forest {
928928
..fileOffset = fileOffset;
929929
}
930930

931-
// Coverage-ignore(suite): Not run.
932931
DotShorthand createDotShorthandContext(
933932
int fileOffset, Expression innerExpression) {
934933
return new DotShorthand(innerExpression)..fileOffset = fileOffset;
935934
}
936935

937-
// Coverage-ignore(suite): Not run.
938936
DotShorthandPropertyGet createDotShorthandPropertyGet(
939937
int fileOffset, Name name) {
940938
return new DotShorthandPropertyGet(name)..fileOffset = fileOffset;

pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart

+4
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ class ClassMembersBuilder implements ClassHierarchyMembers {
202202
return getNodeFromClass(cls).getDispatchTarget(name, setter);
203203
}
204204

205+
Member? getStaticMember(Class cls, Name name, {bool setter = false}) {
206+
return getNodeFromClass(cls).getStaticMember(name, setter)?.getMember(this);
207+
}
208+
205209
static ClassMembersBuilder build(
206210
ClassHierarchyBuilder hierarchyBuilder,
207211
List<ClassBuilder> classes,

pkg/front_end/lib/src/kernel/hierarchy/members_node.dart

+10
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,16 @@ class ClassMembersNode {
11871187
}
11881188
return result;
11891189
}
1190+
1191+
ClassMember? getStaticMember(Name name, bool isSetter) {
1192+
ClassMember? result = classMemberMap[name];
1193+
if (result == null) {
1194+
return null;
1195+
} else if (result.isStatic) {
1196+
return result;
1197+
}
1198+
return null;
1199+
}
11901200
}
11911201

11921202
// Coverage-ignore(suite): Not run.

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -3222,7 +3222,6 @@ class ExtensionTypeRepresentationFieldInitializer extends InternalInitializer {
32223222
'ExtensionTypeRepresentationFieldInitializer(${toStringInternal()})';
32233223
}
32243224

3225-
// Coverage-ignore(suite): Not run.
32263225
/// Internal expression for a dot shorthand.
32273226
///
32283227
/// This node wraps around the [innerExpression] and indicates to the
@@ -3245,12 +3244,12 @@ class DotShorthand extends InternalExpression {
32453244
}
32463245

32473246
@override
3247+
// Coverage-ignore(suite): Not run.
32483248
void toTextInternal(AstPrinter printer) {
32493249
printer.writeExpression(innerExpression);
32503250
}
32513251
}
32523252

3253-
// Coverage-ignore(suite): Not run.
32543253
/// Internal expression for a dot shorthand head with no arguments.
32553254
/// (e.g. `.zero`).
32563255
///
@@ -3272,6 +3271,7 @@ class DotShorthandPropertyGet extends InternalExpression {
32723271
}
32733272

32743273
@override
3274+
// Coverage-ignore(suite): Not run.
32753275
void toTextInternal(AstPrinter printer) {
32763276
printer.write('.');
32773277
printer.writeName(name);

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

+26-4
Original file line numberDiff line numberDiff line change
@@ -12104,7 +12104,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1210412104
return _unhandledStatement(node);
1210512105
}
1210612106

12107-
// Coverage-ignore(suite): Not run.
1210812107
ExpressionInferenceResult visitDotShorthand(
1210912108
DotShorthand node, DartType typeContext) {
1211012109
DartType rewrittenType = analyzeDotShorthand(
@@ -12114,11 +12113,34 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1211412113
return new ExpressionInferenceResult(rewrittenType, rewrittenExpr);
1211512114
}
1211612115

12117-
// Coverage-ignore(suite): Not run.
1211812116
ExpressionInferenceResult visitDotShorthandPropertyGet(
1211912117
DotShorthandPropertyGet node, DartType typeContext) {
12120-
// TODO(kallentu): Implementation needed for dot shorthands.
12121-
return _unhandledExpression(node, typeContext);
12118+
// Use the previously cached context type to determine the declaration
12119+
// member that we're trying to find.
12120+
DartType cachedContext = getDotShorthandContext().unwrapTypeSchemaView();
12121+
Member? member = findInterfaceMember(
12122+
cachedContext, node.name, node.fileOffset,
12123+
includeExtensionMethods: false,
12124+
isSetter: false,
12125+
isDotShorthand: true)
12126+
.member;
12127+
12128+
ExpressionInferenceResult expressionInferenceResult;
12129+
if (member == null) {
12130+
// TODO(kallentu): This is temporary. Build a problem with an error
12131+
// specific to not being able to find a member named [node.name].
12132+
throw 'Error: Cannot find dot shorthand member.';
12133+
} else if (member is Procedure && !member.isGetter) {
12134+
// Tearoff like `Object.new`;
12135+
expressionInferenceResult =
12136+
inferExpression(new StaticTearOff(member), cachedContext);
12137+
} else {
12138+
expressionInferenceResult =
12139+
inferExpression(new StaticGet(member), cachedContext);
12140+
}
12141+
12142+
flowAnalysis.forwardExpression(expressionInferenceResult.expression, node);
12143+
return expressionInferenceResult;
1212212144
}
1212312145
}
1212412146

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

+14-3
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,8 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
11811181
DartType receiverType, Name name, int fileOffset,
11821182
{required bool isSetter,
11831183
bool instrumented = true,
1184-
bool includeExtensionMethods = false}) {
1184+
bool includeExtensionMethods = false,
1185+
bool isDotShorthand = false}) {
11851186
assert(isKnown(receiverType));
11861187

11871188
DartType receiverBound = receiverType.nonTypeParameterBound;
@@ -1200,6 +1201,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
12001201
classNode: classNode,
12011202
receiverBound: receiverBound,
12021203
hasNonObjectMemberAccess: hasNonObjectMemberAccess,
1204+
isDotShorthand: isDotShorthand,
12031205
isSetter: isSetter,
12041206
fileOffset: fileOffset);
12051207

@@ -3732,6 +3734,12 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
37323734
return TypeInferenceEngine.resolveInferenceNode(member, hierarchyBuilder);
37333735
}
37343736

3737+
Member? _getStaticMember(Class class_, Name name, bool setter) {
3738+
Member? member =
3739+
engine.membersBuilder.getStaticMember(class_, name, setter: setter);
3740+
return TypeInferenceEngine.resolveInferenceNode(member, hierarchyBuilder);
3741+
}
3742+
37353743
ClassMember? _getExtensionTypeMember(
37363744
ExtensionTypeDeclaration extensionTypeDeclaration,
37373745
Name name,
@@ -4617,6 +4625,7 @@ class _ObjectAccessDescriptor {
46174625
final DartType receiverBound;
46184626
final Class classNode;
46194627
final bool hasNonObjectMemberAccess;
4628+
final bool isDotShorthand;
46204629
final bool isSetter;
46214630
final int fileOffset;
46224631

@@ -4626,6 +4635,7 @@ class _ObjectAccessDescriptor {
46264635
required this.receiverBound,
46274636
required this.classNode,
46284637
required this.hasNonObjectMemberAccess,
4638+
required this.isDotShorthand,
46294639
required this.isSetter,
46304640
required this.fileOffset});
46314641

@@ -4717,8 +4727,9 @@ class _ObjectAccessDescriptor {
47174727
}
47184728

47194729
ObjectAccessTarget? target;
4720-
Member? interfaceMember =
4721-
visitor._getInterfaceMember(classNode, name, isSetter);
4730+
Member? interfaceMember = isDotShorthand
4731+
? visitor._getStaticMember(classNode, name, isSetter)
4732+
: visitor._getInterfaceMember(classNode, name, isSetter);
47224733
if (interfaceMember != null) {
47234734
target = new ObjectAccessTarget.interfaceMember(
47244735
receiverType, interfaceMember,

pkg/front_end/test/coverage_suite_expected.dart

+7-7
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
590590
),
591591
// 100.0%.
592592
"package:front_end/src/kernel/body_builder.dart": (
593-
hitCount: 7188,
593+
hitCount: 7216,
594594
missCount: 0,
595595
),
596596
// 100.0%.
@@ -660,7 +660,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
660660
),
661661
// 100.0%.
662662
"package:front_end/src/kernel/forest.dart": (
663-
hitCount: 396,
663+
hitCount: 402,
664664
missCount: 0,
665665
),
666666
// 100.0%.
@@ -695,12 +695,12 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
695695
),
696696
// 100.0%.
697697
"package:front_end/src/kernel/hierarchy/members_builder.dart": (
698-
hitCount: 129,
698+
hitCount: 133,
699699
missCount: 0,
700700
),
701701
// 100.0%.
702702
"package:front_end/src/kernel/hierarchy/members_node.dart": (
703-
hitCount: 1106,
703+
hitCount: 1110,
704704
missCount: 0,
705705
),
706706
// 100.0%.
@@ -720,7 +720,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
720720
),
721721
// 100.0%.
722722
"package:front_end/src/kernel/internal_ast.dart": (
723-
hitCount: 565,
723+
hitCount: 571,
724724
missCount: 0,
725725
),
726726
// 100.0%.
@@ -976,12 +976,12 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
976976
),
977977
// 100.0%.
978978
"package:front_end/src/type_inference/inference_visitor.dart": (
979-
hitCount: 8260,
979+
hitCount: 8283,
980980
missCount: 0,
981981
),
982982
// 100.0%.
983983
"package:front_end/src/type_inference/inference_visitor_base.dart": (
984-
hitCount: 2442,
984+
hitCount: 2451,
985985
missCount: 0,
986986
),
987987
// 100.0%.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--enable-experiment=dot-shorthands
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
class Color {
6+
final int x;
7+
static Color get red => Color(1);
8+
Color(this.x);
9+
}
10+
11+
void main() {
12+
Color c = .red;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
6+
// Try inserting an identifier before '.'.
7+
// Color c = .red;
8+
// ^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
class Color extends core::Object {
14+
final field core::int x;
15+
constructor •(core::int x) → self::Color
16+
: self::Color::x = x, super core::Object::•()
17+
;
18+
static get red() → self::Color
19+
return new self::Color::•(1);
20+
}
21+
static method main() → void {
22+
self::Color c = self::Color::red;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
6+
// Try inserting an identifier before '.'.
7+
// Color c = .red;
8+
// ^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
class Color extends core::Object {
14+
final field core::int x;
15+
constructor •(core::int x) → self::Color
16+
: self::Color::x = x, super core::Object::•()
17+
;
18+
static get red() → self::Color
19+
return new self::Color::•(1);
20+
}
21+
static method main() → void {
22+
self::Color c = self::Color::red;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class Color extends core::Object {
6+
final field core::int x;
7+
constructor •(core::int x) → self::Color
8+
;
9+
static get red() → self::Color
10+
;
11+
}
12+
static method main() → void
13+
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
6+
// Try inserting an identifier before '.'.
7+
// Color c = .red;
8+
// ^
9+
//
10+
import self as self;
11+
import "dart:core" as core;
12+
13+
class Color extends core::Object {
14+
final field core::int x;
15+
constructor •(core::int x) → self::Color
16+
: self::Color::x = x, super core::Object::•()
17+
;
18+
static get red() → self::Color
19+
return new self::Color::•(1);
20+
}
21+
static method main() → void {
22+
self::Color c = self::Color::red;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Color {
2+
final int x;
3+
static Color get red => Color(1);
4+
Color(this.x);
5+
}
6+
7+
void main() {}

0 commit comments

Comments
 (0)