Skip to content

Commit 1d99738

Browse files
kallentuCommit Queue
authored and
Commit Queue
committed
[cfe] Add support for 'call()' methods in dot shorthands.
Any dot shorthand code in the form of `.id()` where `id` is a field or getter should resolve to the `call` method in the declaration of the type of `id`. Normal static invocations already do this, and this CL adds the extra bit of `call()` resolution to the dot shorthands feature. Bug: #59758 Change-Id: Ie8a22f251c3c80443e27d9fb307e7ef6b495315e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/418141 Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Johnni Winther <[email protected]>
1 parent c741d23 commit 1d99738

20 files changed

+356
-24
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12184,6 +12184,16 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1218412184
}
1218512185
}
1218612186

12187+
if (member != null &&
12188+
(member is Field || (member is Procedure && member.isGetter))) {
12189+
// Try to find a `.call()`.
12190+
DartType receiverType = member.getterType;
12191+
Expression receiver = new StaticGet(member)..fileOffset = node.fileOffset;
12192+
return inferMethodInvocation(this, node.fileOffset, receiver,
12193+
receiverType, callName, node.arguments as ArgumentsImpl, typeContext,
12194+
isExpressionInvocation: true, isImplicitCall: true);
12195+
}
12196+
1218712197
if (expr == null) {
1218812198
Expression replacement;
1218912199
if (isKnown(cachedContext)) {
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+
class C {
6+
const C();
7+
static C get id1 => const C();
8+
static C id2 = const C();
9+
10+
C call() => const C();
11+
}
12+
13+
void main() {
14+
C c1 = .id1();
15+
C c2 = .id2();
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object /*hasConstConstructor*/ {
6+
static field self::C id2 = #C1;
7+
const constructor •() → self::C
8+
: super core::Object::•()
9+
;
10+
static get id1() → self::C
11+
return #C1;
12+
method call() → self::C
13+
return #C1;
14+
}
15+
static method main() → void {
16+
self::C c1 = self::C::id1.{self::C::call}(){() → self::C};
17+
self::C c2 = self::C::id2.{self::C::call}(){() → self::C};
18+
}
19+
20+
constants {
21+
#C1 = self::C {}
22+
}
23+
24+
25+
Constructor coverage from constants:
26+
org-dartlang-testcase:///call.dart:
27+
- C. (from org-dartlang-testcase:///call.dart:6:9)
28+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object /*hasConstConstructor*/ {
6+
static field self::C id2 = #C1;
7+
const constructor •() → self::C
8+
: super core::Object::•()
9+
;
10+
static get id1() → self::C
11+
return #C1;
12+
method call() → self::C
13+
return #C1;
14+
}
15+
static method main() → void {
16+
self::C c1 = self::C::id1.{self::C::call}(){() → self::C};
17+
self::C c2 = self::C::id2.{self::C::call}(){() → self::C};
18+
}
19+
20+
constants {
21+
#C1 = self::C {}
22+
}
23+
24+
25+
Constructor coverage from constants:
26+
org-dartlang-testcase:///call.dart:
27+
- C. (from org-dartlang-testcase:///call.dart:6:9)
28+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object /*hasConstConstructor*/ {
6+
static field self::C id2;
7+
const constructor •() → self::C
8+
: super core::Object::•()
9+
;
10+
static get id1() → self::C
11+
;
12+
method call() → self::C
13+
;
14+
}
15+
static method main() → void
16+
;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object /*hasConstConstructor*/ {
6+
static field self::C id2 = #C1;
7+
const constructor •() → self::C
8+
: super core::Object::•()
9+
;
10+
static get id1() → self::C
11+
return #C1;
12+
method call() → self::C
13+
return #C1;
14+
}
15+
static method main() → void {
16+
self::C c1 = self::C::id1.{self::C::call}(){() → self::C};
17+
self::C c2 = self::C::id2.{self::C::call}(){() → self::C};
18+
}
19+
20+
constants {
21+
#C1 = self::C {}
22+
}
23+
24+
25+
Constructor coverage from constants:
26+
org-dartlang-testcase:///call.dart:
27+
- C. (from org-dartlang-testcase:///call.dart:6:9)
28+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class C {
2+
const C();
3+
static C get id1 => const C();
4+
static C id2 = const C();
5+
C call() => const C();
6+
}
7+
8+
void main() {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class C {
2+
C call() => const C();
3+
const C();
4+
static C get id1 => const C();
5+
static C id2 = const C();
6+
}
7+
8+
void main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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 C {
6+
const C();
7+
static C get id1 => const C();
8+
static C id2 = const C();
9+
}
10+
11+
void test() {
12+
// No `.call()` method.
13+
C c1 = .id1();
14+
C c2 = .id2();
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
6+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
7+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
8+
// C c1 = .id1();
9+
// ^
10+
//
11+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
12+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
13+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
14+
// C c2 = .id2();
15+
// ^
16+
//
17+
import self as self;
18+
import "dart:core" as core;
19+
20+
class C extends core::Object /*hasConstConstructor*/ {
21+
static field self::C id2 = #C1;
22+
const constructor •() → self::C
23+
: super core::Object::•()
24+
;
25+
static get id1() → self::C
26+
return #C1;
27+
}
28+
static method test() → void {
29+
self::C c1 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
30+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
31+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
32+
C c1 = .id1();
33+
^" in self::C::id1{<unresolved>}.call() as{TypeError,ForDynamic} self::C;
34+
self::C c2 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
35+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
36+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
37+
C c2 = .id2();
38+
^" in self::C::id2{<unresolved>}.call() as{TypeError,ForDynamic} self::C;
39+
}
40+
41+
constants {
42+
#C1 = self::C {}
43+
}
44+
45+
46+
Constructor coverage from constants:
47+
org-dartlang-testcase:///call_error.dart:
48+
- C. (from org-dartlang-testcase:///call_error.dart:6:9)
49+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
6+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
7+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
8+
// C c1 = .id1();
9+
// ^
10+
//
11+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
12+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
13+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
14+
// C c2 = .id2();
15+
// ^
16+
//
17+
import self as self;
18+
import "dart:core" as core;
19+
20+
class C extends core::Object /*hasConstConstructor*/ {
21+
static field self::C id2 = #C1;
22+
const constructor •() → self::C
23+
: super core::Object::•()
24+
;
25+
static get id1() → self::C
26+
return #C1;
27+
}
28+
static method test() → void {
29+
self::C c1 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
30+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
31+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
32+
C c1 = .id1();
33+
^" in self::C::id1{<unresolved>}.call() as{TypeError,ForDynamic} self::C;
34+
self::C c2 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
35+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
36+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
37+
C c2 = .id2();
38+
^" in self::C::id2{<unresolved>}.call() as{TypeError,ForDynamic} self::C;
39+
}
40+
41+
constants {
42+
#C1 = self::C {}
43+
}
44+
45+
46+
Constructor coverage from constants:
47+
org-dartlang-testcase:///call_error.dart:
48+
- C. (from org-dartlang-testcase:///call_error.dart:6:9)
49+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object /*hasConstConstructor*/ {
6+
static field self::C id2;
7+
const constructor •() → self::C
8+
: super core::Object::•()
9+
;
10+
static get id1() → self::C
11+
;
12+
}
13+
static method test() → void
14+
;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
6+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
7+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
8+
// C c1 = .id1();
9+
// ^
10+
//
11+
// pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
12+
// - 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
13+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
14+
// C c2 = .id2();
15+
// ^
16+
//
17+
import self as self;
18+
import "dart:core" as core;
19+
20+
class C extends core::Object /*hasConstConstructor*/ {
21+
static field self::C id2 = #C1;
22+
const constructor •() → self::C
23+
: super core::Object::•()
24+
;
25+
static get id1() → self::C
26+
return #C1;
27+
}
28+
static method test() → void {
29+
self::C c1 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:13:10: Error: The method 'call' isn't defined for the class 'C'.
30+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
31+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
32+
C c1 = .id1();
33+
^" in self::C::id1{<unresolved>}.call() as{TypeError,ForDynamic,Unchecked} self::C;
34+
self::C c2 = invalid-expression "pkg/front_end/testcases/dot_shorthands/call_error.dart:14:10: Error: The method 'call' isn't defined for the class 'C'.
35+
- 'C' is from 'pkg/front_end/testcases/dot_shorthands/call_error.dart'.
36+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
37+
C c2 = .id2();
38+
^" in self::C::id2{<unresolved>}.call() as{TypeError,ForDynamic,Unchecked} self::C;
39+
}
40+
41+
constants {
42+
#C1 = self::C {}
43+
}
44+
45+
46+
Constructor coverage from constants:
47+
org-dartlang-testcase:///call_error.dart:
48+
- C. (from org-dartlang-testcase:///call_error.dart:6:9)
49+
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C {
2+
const C();
3+
static C get id1 => const C();
4+
static C id2 = const C();
5+
}
6+
7+
void test() {}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C {
2+
const C();
3+
static C get id1 => const C();
4+
static C id2 = const C();
5+
}
6+
7+
void test() {}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ library;
22
//
33
// Problems in library:
44
//
5-
// pkg/front_end/testcases/dot_shorthands/static_method_error.dart:12:14: Error: The static method or constructor 'red' isn't defined for the type 'Color'.
5+
// pkg/front_end/testcases/dot_shorthands/static_method_error.dart:12:13: Error: The method 'call' isn't defined for the class 'Color'.
66
// - 'Color' is from 'pkg/front_end/testcases/dot_shorthands/static_method_error.dart'.
7-
// Try correcting the name to the name of an existing static method or constructor, or defining a static method or constructor named 'red'.
7+
// Try correcting the name to the name of an existing method, or defining a method named 'call'.
88
// Color c = .red();
9-
// ^^^
9+
// ^
1010
//
1111
// pkg/front_end/testcases/dot_shorthands/static_method_error.dart:13:15: Error: The static method or constructor 'blue' isn't defined for the type 'Color'.
1212
// - 'Color' is from 'pkg/front_end/testcases/dot_shorthands/static_method_error.dart'.
@@ -30,11 +30,11 @@ class Color extends core::Object {
3030
return new self::Color::•(1);
3131
}
3232
static method test() → void {
33-
self::Color c = invalid-expression "pkg/front_end/testcases/dot_shorthands/static_method_error.dart:12:14: Error: The static method or constructor 'red' isn't defined for the type 'Color'.
33+
self::Color c = invalid-expression "pkg/front_end/testcases/dot_shorthands/static_method_error.dart:12:13: Error: The method 'call' isn't defined for the class 'Color'.
3434
- 'Color' is from 'pkg/front_end/testcases/dot_shorthands/static_method_error.dart'.
35-
Try correcting the name to the name of an existing static method or constructor, or defining a static method or constructor named 'red'.
35+
Try correcting the name to the name of an existing method, or defining a method named 'call'.
3636
Color c = .red();
37-
^^^" as{TypeError,ForDynamic} self::Color;
37+
^" in self::Color::red{<unresolved>}.call() as{TypeError,ForDynamic} self::Color;
3838
self::Color cc = invalid-expression "pkg/front_end/testcases/dot_shorthands/static_method_error.dart:13:15: Error: The static method or constructor 'blue' isn't defined for the type 'Color'.
3939
- 'Color' is from 'pkg/front_end/testcases/dot_shorthands/static_method_error.dart'.
4040
Try correcting the name to the name of an existing static method or constructor, or defining a static method or constructor named 'blue'.

0 commit comments

Comments
 (0)