Skip to content

Commit 59acc56

Browse files
kallentuCommit Queue
authored andcommitted
[cfe] Dot shorthands: Static invocations are inferred.
Similar to constructor invocations, we infer the type parameters for the dot shorthand static member that we find. Added language + cfe tests for this case. This CL is a follow up to: https://dart-review.googlesource.com/c/sdk/+/417960/comment/90f32aed_8d1f6c01/ Bug: #59758 Change-Id: I96666ba9faa6ffaf42bd46e4d39175b88f7a353d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420283 Reviewed-by: Chloe Stefantsova <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent 5b7fb2e commit 59acc56

9 files changed

+114
-34
lines changed

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

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12134,8 +12134,22 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1213412134

1213512135
Expression? expr;
1213612136
if (member is Procedure && member.kind == ProcedureKind.Method) {
12137+
// The shorthand expression is inferred in the empty context and then type
12138+
// inference infers the type arguments.
12139+
FunctionType functionType =
12140+
member.function.computeThisFunctionType(Nullability.nonNullable);
12141+
InvocationInferenceResult result = inferInvocation(
12142+
this,
12143+
typeContext,
12144+
node.fileOffset,
12145+
new InvocationTargetFunctionType(functionType),
12146+
node.arguments as ArgumentsImpl,
12147+
isConst: node.isConst,
12148+
staticTarget: member);
1213712149
expr = new StaticInvocation(member, node.arguments)
1213812150
..fileOffset = node.fileOffset;
12151+
return new ExpressionInferenceResult(
12152+
result.inferredType, result.applyResult(expr));
1213912153
} else if (member == null && cachedContext is TypeDeclarationType) {
1214012154
// Couldn't find a static method in the declaration so we'll try and find
1214112155
// a constructor of that name instead.
@@ -12161,8 +12175,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1216112175
node.name.text.length));
1216212176
}
1216312177

12164-
// With constructor invocations, the shorthand expression is preceded by
12165-
// the raw type and then type inference infers the type arguments.
12178+
// The shorthand expression is inferred in the empty context and then
12179+
// type inference infers the type arguments.
1216612180
FunctionType functionType = constructor.function
1216712181
.computeThisFunctionType(Nullability.nonNullable);
1216812182
InvocationInferenceResult result = inferInvocation(
@@ -12189,8 +12203,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1218912203
node.name.text.length));
1219012204
}
1219112205

12192-
// With constructor invocations, the shorthand expression is preceded by
12193-
// the raw type and then type inference infers the type arguments.
12206+
// The shorthand expression is inferred in the empty context and then
12207+
// type inference infers the type arguments.
1219412208
FunctionType functionType = constructor.function
1219512209
.computeThisFunctionType(Nullability.nonNullable);
1219612210
InvocationInferenceResult result = inferInvocation(
@@ -12224,33 +12238,28 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1222412238
isExpressionInvocation: true, isImplicitCall: true);
1222512239
}
1222612240

12227-
if (expr == null) {
12228-
Expression replacement;
12229-
if (isKnown(cachedContext)) {
12230-
// Error when we can't find the static member or constructor named
12231-
// [node.name] in the declaration of [cachedContext].
12232-
replacement = helper.buildProblem(
12233-
templateDotShorthandsUndefinedInvocation.withArguments(
12234-
node.name.text, cachedContext),
12235-
node.nameOffset,
12236-
node.name.text.length);
12237-
} else {
12238-
// Error when no context type or an invalid context type is given to
12239-
// resolve the dot shorthand.
12240-
//
12241-
// e.g. `var x = .one;`
12242-
replacement = helper.buildProblem(
12243-
templateDotShorthandsInvalidContext.withArguments(node.name.text),
12244-
node.nameOffset,
12245-
node.name.text.length);
12246-
}
12247-
return new ExpressionInferenceResult(const DynamicType(), replacement);
12241+
// Error handling. At this point, we've exhausted all possible valid
12242+
// invocations.
12243+
Expression replacement;
12244+
if (isKnown(cachedContext)) {
12245+
// Error when we can't find the static member or constructor named
12246+
// [node.name] in the declaration of [cachedContext].
12247+
replacement = helper.buildProblem(
12248+
templateDotShorthandsUndefinedInvocation.withArguments(
12249+
node.name.text, cachedContext),
12250+
node.nameOffset,
12251+
node.name.text.length);
12252+
} else {
12253+
// Error when no context type or an invalid context type is given to
12254+
// resolve the dot shorthand.
12255+
//
12256+
// e.g. `var x = .one;`
12257+
replacement = helper.buildProblem(
12258+
templateDotShorthandsInvalidContext.withArguments(node.name.text),
12259+
node.nameOffset,
12260+
node.name.text.length);
1224812261
}
12249-
12250-
ExpressionInferenceResult expressionInferenceResult =
12251-
inferExpression(expr, cachedContext);
12252-
flowAnalysis.forwardExpression(expressionInferenceResult.expression, node);
12253-
return expressionInferenceResult;
12262+
return new ExpressionInferenceResult(const DynamicType(), replacement);
1225412263
}
1225512264

1225612265
ExpressionInferenceResult visitDotShorthandPropertyGet(

pkg/front_end/testcases/dot_shorthands/static_method.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ class Color {
88
Color(this.x);
99
}
1010

11+
class C<T> {
12+
static C<X> foo<X>(X x) => new C<X>();
13+
14+
C<U> cast<U>() => new C<U>();
15+
}
16+
1117
void main() {
12-
Color c = .red();
18+
Color color = .red();
19+
C<bool> c = .foo("String").cast();
1320
}

pkg/front_end/testcases/dot_shorthands/static_method.dart.strong.expect

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ class Color extends core::Object {
1010
static method red() → self::Color
1111
return new self::Color::•(1);
1212
}
13+
class C<T extends core::Object? = dynamic> extends core::Object {
14+
synthetic constructor •() → self::C<self::C::T%>
15+
: super core::Object::•()
16+
;
17+
static method foo<X extends core::Object? = dynamic>(self::C::foo::X% x) → self::C<self::C::foo::X%>
18+
return new self::C::•<self::C::foo::X%>();
19+
method cast<U extends core::Object? = dynamic>() → self::C<self::C::cast::U%>
20+
return new self::C::•<self::C::cast::U%>();
21+
}
1322
static method main() → void {
14-
self::Color c = self::Color::red();
23+
self::Color color = self::Color::red();
24+
self::C<core::bool> c = self::C::foo<core::String>("String").{self::C::cast}<core::bool>(){() → self::C<core::bool>};
1525
}

pkg/front_end/testcases/dot_shorthands/static_method.dart.strong.modular.expect

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ class Color extends core::Object {
1010
static method red() → self::Color
1111
return new self::Color::•(1);
1212
}
13+
class C<T extends core::Object? = dynamic> extends core::Object {
14+
synthetic constructor •() → self::C<self::C::T%>
15+
: super core::Object::•()
16+
;
17+
static method foo<X extends core::Object? = dynamic>(self::C::foo::X% x) → self::C<self::C::foo::X%>
18+
return new self::C::•<self::C::foo::X%>();
19+
method cast<U extends core::Object? = dynamic>() → self::C<self::C::cast::U%>
20+
return new self::C::•<self::C::cast::U%>();
21+
}
1322
static method main() → void {
14-
self::Color c = self::Color::red();
23+
self::Color color = self::Color::red();
24+
self::C<core::bool> c = self::C::foo<core::String>("String").{self::C::cast}<core::bool>(){() → self::C<core::bool>};
1525
}

pkg/front_end/testcases/dot_shorthands/static_method.dart.strong.outline.expect

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,13 @@ class Color extends core::Object {
99
static method red() → self::Color
1010
;
1111
}
12+
class C<T extends core::Object? = dynamic> extends core::Object {
13+
synthetic constructor •() → self::C<self::C::T%>
14+
;
15+
static method foo<X extends core::Object? = dynamic>(self::C::foo::X% x) → self::C<self::C::foo::X%>
16+
;
17+
method cast<U extends core::Object? = dynamic>() → self::C<self::C::cast::U%>
18+
;
19+
}
1220
static method main() → void
1321
;

pkg/front_end/testcases/dot_shorthands/static_method.dart.strong.transformed.expect

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ class Color extends core::Object {
1010
static method red() → self::Color
1111
return new self::Color::•(1);
1212
}
13+
class C<T extends core::Object? = dynamic> extends core::Object {
14+
synthetic constructor •() → self::C<self::C::T%>
15+
: super core::Object::•()
16+
;
17+
static method foo<X extends core::Object? = dynamic>(self::C::foo::X% x) → self::C<self::C::foo::X%>
18+
return new self::C::•<self::C::foo::X%>();
19+
method cast<U extends core::Object? = dynamic>() → self::C<self::C::cast::U%>
20+
return new self::C::•<self::C::cast::U%>();
21+
}
1322
static method main() → void {
14-
self::Color c = self::Color::red();
23+
self::Color color = self::Color::red();
24+
self::C<core::bool> c = self::C::foo<core::String>("String").{self::C::cast}<core::bool>(){() → self::C<core::bool>};
1525
}

pkg/front_end/testcases/dot_shorthands/static_method.dart.textual_outline.expect

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@ class Color {
44
Color(this.x);
55
}
66

7+
class C<T> {
8+
static C<X> foo<X>(X x) => new C<X>();
9+
C<U> cast<U>() => new C<U>();
10+
}
11+
712
void main() {}

pkg/front_end/testcases/dot_shorthands/static_method.dart.textual_outline_modelled.expect

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
class C<T> {
2+
C<U> cast<U>() => new C<U>();
3+
static C<X> foo<X>(X x) => new C<X>();
4+
}
5+
16
class Color {
27
Color(this.x);
38
final int x;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
// Type parameters are inferred in dot shorthand static invocations.
6+
7+
// SharedOptions=--enable-experiment=dot-shorthands
8+
9+
class C<T> {
10+
static C<X> foo<X>(X x) => new C<X>();
11+
C<U> cast<U>() => new C<U>();
12+
}
13+
14+
void main() {
15+
C<bool> c = .foo("String").cast();
16+
}

0 commit comments

Comments
 (0)