Skip to content

Commit

Permalink
[cfe] Handle static gets and tearoffs in dot shorthands
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
kallentu authored and Commit Queue committed Feb 26, 2025
1 parent 46c6f9c commit 34b74d8
Show file tree
Hide file tree
Showing 30 changed files with 442 additions and 21 deletions.
23 changes: 20 additions & 3 deletions pkg/front_end/lib/src/kernel/body_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9976,23 +9976,29 @@ class BodyBuilder extends StackListenerImpl
}

@override
// Coverage-ignore(suite): Not run.
void handleDotShorthandContext(Token token) {
debugEvent("DotShorthandContext");
if (!libraryFeatures.dotShorthands.isEnabled) {
// Coverage-ignore-block(suite): Not run.
addProblem(
templateExperimentNotEnabledOffByDefault
.withArguments(ExperimentalFlag.dotShorthands.name),
token.offset,
token.length);
return;
}

// TODO(kallentu): Possibly could be ProblemBuilder? Testing needed.
assert(checkState(token, [ValueKinds.Expression]));
Expression value = pop() as Expression;
push(forest.createDotShorthandContext(token.charOffset, value));
}

@override
// Coverage-ignore(suite): Not run.
void handleDotShorthandHead(Token token) {
debugEvent("DotShorthandHead");
if (!libraryFeatures.dotShorthands.isEnabled) {
// Coverage-ignore-block(suite): Not run.
addProblem(
templateExperimentNotEnabledOffByDefault
.withArguments(ExperimentalFlag.dotShorthands.name),
Expand All @@ -10001,9 +10007,20 @@ class BodyBuilder extends StackListenerImpl

// Recovery, avoid crashing with an extra selector.
pop();
return;
}

// TODO(kallentu): Handle dot shorthands.
Object? selector = pop();
if (libraryFeatures.dotShorthands.isEnabled && selector is Selector) {
// TODO(kallentu): Remove this once we have more of the dot shorthands
// implementation complete.
pop(); // ParserGeneratorError

// TODO(kallentu): Handle invocations.

push(forest.createDotShorthandPropertyGet(
offsetForToken(token), selector.name));
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions pkg/front_end/lib/src/kernel/forest.dart
Original file line number Diff line number Diff line change
Expand Up @@ -928,13 +928,11 @@ class Forest {
..fileOffset = fileOffset;
}

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

// Coverage-ignore(suite): Not run.
DotShorthandPropertyGet createDotShorthandPropertyGet(
int fileOffset, Name name) {
return new DotShorthandPropertyGet(name)..fileOffset = fileOffset;
Expand Down
4 changes: 4 additions & 0 deletions pkg/front_end/lib/src/kernel/hierarchy/members_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ class ClassMembersBuilder implements ClassHierarchyMembers {
return getNodeFromClass(cls).getDispatchTarget(name, setter);
}

Member? getStaticMember(Class cls, Name name, {bool setter = false}) {
return getNodeFromClass(cls).getStaticMember(name, setter)?.getMember(this);
}

static ClassMembersBuilder build(
ClassHierarchyBuilder hierarchyBuilder,
List<ClassBuilder> classes,
Expand Down
10 changes: 10 additions & 0 deletions pkg/front_end/lib/src/kernel/hierarchy/members_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,16 @@ class ClassMembersNode {
}
return result;
}

ClassMember? getStaticMember(Name name, bool isSetter) {
ClassMember? result = classMemberMap[name];
if (result == null) {
return null;
} else if (result.isStatic) {
return result;
}
return null;
}
}

// Coverage-ignore(suite): Not run.
Expand Down
4 changes: 2 additions & 2 deletions pkg/front_end/lib/src/kernel/internal_ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3222,7 +3222,6 @@ class ExtensionTypeRepresentationFieldInitializer extends InternalInitializer {
'ExtensionTypeRepresentationFieldInitializer(${toStringInternal()})';
}

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

@override
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
printer.writeExpression(innerExpression);
}
}

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

@override
// Coverage-ignore(suite): Not run.
void toTextInternal(AstPrinter printer) {
printer.write('.');
printer.writeName(name);
Expand Down
30 changes: 26 additions & 4 deletions pkg/front_end/lib/src/type_inference/inference_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12104,7 +12104,6 @@ class InferenceVisitorImpl extends InferenceVisitorBase
return _unhandledStatement(node);
}

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

// Coverage-ignore(suite): Not run.
ExpressionInferenceResult visitDotShorthandPropertyGet(
DotShorthandPropertyGet node, DartType typeContext) {
// TODO(kallentu): Implementation needed for dot shorthands.
return _unhandledExpression(node, typeContext);
// Use the previously cached context type to determine the declaration
// member that we're trying to find.
DartType cachedContext = getDotShorthandContext().unwrapTypeSchemaView();
Member? member = findInterfaceMember(
cachedContext, node.name, node.fileOffset,
includeExtensionMethods: false,
isSetter: false,
isDotShorthand: true)
.member;

ExpressionInferenceResult expressionInferenceResult;
if (member == null) {
// TODO(kallentu): This is temporary. Build a problem with an error
// specific to not being able to find a member named [node.name].
throw 'Error: Cannot find dot shorthand member.';
} else if (member is Procedure && !member.isGetter) {
// Tearoff like `Object.new`;
expressionInferenceResult =
inferExpression(new StaticTearOff(member), cachedContext);
} else {
expressionInferenceResult =
inferExpression(new StaticGet(member), cachedContext);
}

flowAnalysis.forwardExpression(expressionInferenceResult.expression, node);
return expressionInferenceResult;
}
}

Expand Down
17 changes: 14 additions & 3 deletions pkg/front_end/lib/src/type_inference/inference_visitor_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,8 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
DartType receiverType, Name name, int fileOffset,
{required bool isSetter,
bool instrumented = true,
bool includeExtensionMethods = false}) {
bool includeExtensionMethods = false,
bool isDotShorthand = false}) {
assert(isKnown(receiverType));

DartType receiverBound = receiverType.nonTypeParameterBound;
Expand All @@ -1200,6 +1201,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
classNode: classNode,
receiverBound: receiverBound,
hasNonObjectMemberAccess: hasNonObjectMemberAccess,
isDotShorthand: isDotShorthand,
isSetter: isSetter,
fileOffset: fileOffset);

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

Member? _getStaticMember(Class class_, Name name, bool setter) {
Member? member =
engine.membersBuilder.getStaticMember(class_, name, setter: setter);
return TypeInferenceEngine.resolveInferenceNode(member, hierarchyBuilder);
}

ClassMember? _getExtensionTypeMember(
ExtensionTypeDeclaration extensionTypeDeclaration,
Name name,
Expand Down Expand Up @@ -4617,6 +4625,7 @@ class _ObjectAccessDescriptor {
final DartType receiverBound;
final Class classNode;
final bool hasNonObjectMemberAccess;
final bool isDotShorthand;
final bool isSetter;
final int fileOffset;

Expand All @@ -4626,6 +4635,7 @@ class _ObjectAccessDescriptor {
required this.receiverBound,
required this.classNode,
required this.hasNonObjectMemberAccess,
required this.isDotShorthand,
required this.isSetter,
required this.fileOffset});

Expand Down Expand Up @@ -4717,8 +4727,9 @@ class _ObjectAccessDescriptor {
}

ObjectAccessTarget? target;
Member? interfaceMember =
visitor._getInterfaceMember(classNode, name, isSetter);
Member? interfaceMember = isDotShorthand
? visitor._getStaticMember(classNode, name, isSetter)
: visitor._getInterfaceMember(classNode, name, isSetter);
if (interfaceMember != null) {
target = new ObjectAccessTarget.interfaceMember(
receiverType, interfaceMember,
Expand Down
14 changes: 7 additions & 7 deletions pkg/front_end/test/coverage_suite_expected.dart
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/kernel/body_builder.dart": (
hitCount: 7188,
hitCount: 7216,
missCount: 0,
),
// 100.0%.
Expand Down Expand Up @@ -660,7 +660,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/kernel/forest.dart": (
hitCount: 396,
hitCount: 402,
missCount: 0,
),
// 100.0%.
Expand Down Expand Up @@ -695,12 +695,12 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/kernel/hierarchy/members_builder.dart": (
hitCount: 129,
hitCount: 133,
missCount: 0,
),
// 100.0%.
"package:front_end/src/kernel/hierarchy/members_node.dart": (
hitCount: 1106,
hitCount: 1110,
missCount: 0,
),
// 100.0%.
Expand All @@ -720,7 +720,7 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/kernel/internal_ast.dart": (
hitCount: 565,
hitCount: 571,
missCount: 0,
),
// 100.0%.
Expand Down Expand Up @@ -976,12 +976,12 @@ const Map<String, ({int hitCount, int missCount})> _expect = {
),
// 100.0%.
"package:front_end/src/type_inference/inference_visitor.dart": (
hitCount: 8260,
hitCount: 8283,
missCount: 0,
),
// 100.0%.
"package:front_end/src/type_inference/inference_visitor_base.dart": (
hitCount: 2442,
hitCount: 2451,
missCount: 0,
),
// 100.0%.
Expand Down
1 change: 1 addition & 0 deletions pkg/front_end/testcases/dot_shorthands/folder.options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--enable-experiment=dot-shorthands
13 changes: 13 additions & 0 deletions pkg/front_end/testcases/dot_shorthands/simple_class.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// 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.

class Color {
final int x;
static Color get red => Color(1);
Color(this.x);
}

void main() {
Color c = .red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
// Try inserting an identifier before '.'.
// Color c = .red;
// ^
//
import self as self;
import "dart:core" as core;

class Color extends core::Object {
final field core::int x;
constructor •(core::int x) → self::Color
: self::Color::x = x, super core::Object::•()
;
static get red() → self::Color
return new self::Color::•(1);
}
static method main() → void {
self::Color c = self::Color::red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
// Try inserting an identifier before '.'.
// Color c = .red;
// ^
//
import self as self;
import "dart:core" as core;

class Color extends core::Object {
final field core::int x;
constructor •(core::int x) → self::Color
: self::Color::x = x, super core::Object::•()
;
static get red() → self::Color
return new self::Color::•(1);
}
static method main() → void {
self::Color c = self::Color::red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
library;
import self as self;
import "dart:core" as core;

class Color extends core::Object {
final field core::int x;
constructor •(core::int x) → self::Color
;
static get red() → self::Color
;
}
static method main() → void
;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/dot_shorthands/simple_class.dart:12:13: Error: Expected an identifier, but got '.'.
// Try inserting an identifier before '.'.
// Color c = .red;
// ^
//
import self as self;
import "dart:core" as core;

class Color extends core::Object {
final field core::int x;
constructor •(core::int x) → self::Color
: self::Color::x = x, super core::Object::•()
;
static get red() → self::Color
return new self::Color::•(1);
}
static method main() → void {
self::Color c = self::Color::red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Color {
final int x;
static Color get red => Color(1);
Color(this.x);
}

void main() {}
Loading

0 comments on commit 34b74d8

Please sign in to comment.