Skip to content

Commit 0f17fc4

Browse files
rchen152meta-codesync[bot]
authored andcommitted
Apply arity filter more aggressively
Summary: If only one overload has a parameter count that is compatible with a call's argument count, we should take that as the matching overload even if the call produces errors. This gives us more precise return types and better error messages. In some cases, this increases the number of errors because a single "No matching overload" error is replaced with multiple errors about specific parameters. IMO this is a good change. Fixes #2833. Reviewed By: stroxler Differential Revision: D97394258 fbshipit-source-id: 737258f4a3f2f56d287efd3240bec5c7052a8db3
1 parent ca37adf commit 0f17fc4

File tree

9 files changed

+39
-42
lines changed

9 files changed

+39
-42
lines changed

conformance/third_party/conformance.exp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9073,24 +9073,24 @@
90739073
},
90749074
{
90759075
"code": -2,
9076-
"column": 11,
9077-
"concise_description": "No matching overload found for function `example1_1` called with arguments: (Literal[1], Literal[1])",
9078-
"description": "No matching overload found for function `example1_1` called with arguments: (Literal[1], Literal[1])\n Possible overloads:\n (x: int, y: str) -> int [closest match]\n (x: str) -> str",
9076+
"column": 15,
9077+
"concise_description": "Argument `Literal[1]` is not assignable to parameter `y` with type `str` in function `example1_1`",
9078+
"description": "Argument `Literal[1]` is not assignable to parameter `y` with type `str` in function `example1_1`",
90799079
"line": 46,
9080-
"name": "no-matching-overload",
9080+
"name": "bad-argument-type",
90819081
"severity": "error",
9082-
"stop_column": 17,
9082+
"stop_column": 16,
90839083
"stop_line": 46
90849084
},
90859085
{
90869086
"code": -2,
9087-
"column": 11,
9088-
"concise_description": "No matching overload found for function `example1_1` called with arguments: (Literal[1])",
9089-
"description": "No matching overload found for function `example1_1` called with arguments: (Literal[1])\n Possible overloads:\n (x: int, y: str) -> int\n (x: str) -> str [closest match]",
9087+
"column": 12,
9088+
"concise_description": "Argument `Literal[1]` is not assignable to parameter `x` with type `str` in function `example1_1`",
9089+
"description": "Argument `Literal[1]` is not assignable to parameter `x` with type `str` in function `example1_1`",
90909090
"line": 51,
9091-
"name": "no-matching-overload",
9091+
"name": "bad-argument-type",
90929092
"severity": "error",
9093-
"stop_column": 14,
9093+
"stop_column": 13,
90949094
"stop_line": 51
90959095
},
90969096
{
@@ -11738,13 +11738,13 @@
1173811738
},
1173911739
{
1174011740
"code": -2,
11741-
"column": 11,
11742-
"concise_description": "No matching overload found for function `ExtraMovie.__init__` called with arguments: (name=Literal['No Country for Old Men'], language=Literal['English'])",
11743-
"description": "No matching overload found for function `ExtraMovie.__init__` called with arguments: (name=Literal['No Country for Old Men'], language=Literal['English'])\n Possible overloads:\n (*, name: str, **int) -> None [closest match]\n (__map: ExtraMovie, /, *, name: str = ..., **int) -> None",
11741+
"column": 52,
11742+
"concise_description": "Keyword argument `language` with type `Literal['English']` is not assignable to kwargs type `int` in function `ExtraMovie.__init__`",
11743+
"description": "Keyword argument `language` with type `Literal['English']` is not assignable to kwargs type `int` in function `ExtraMovie.__init__`",
1174411744
"line": 285,
11745-
"name": "no-matching-overload",
11745+
"name": "bad-argument-type",
1174611746
"severity": "error",
11747-
"stop_column": 62,
11747+
"stop_column": 61,
1174811748
"stop_line": 285
1174911749
},
1175011750
{

pyrefly/lib/alt/overload.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,11 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
319319
break;
320320
}
321321
}
322-
(closest_overload, matched)
322+
(
323+
closest_overload,
324+
// If there was only one overload with the right arity, it definitely matched.
325+
matched || arity_compatible_overloads.len() == 1,
326+
)
323327
}
324328
};
325329

@@ -364,6 +368,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
364368
msg,
365369
);
366370
}
371+
errors.extend(closest_overload.call_errors);
367372
(closest_overload.res, closest_overload.func.1.signature)
368373
} else {
369374
// Build a string showing the argument types for error messages

pyrefly/lib/test/dataclasses.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ testcase!(
660660
test_bad_keyword,
661661
r#"
662662
from dataclasses import dataclass
663-
@dataclass(flibbertigibbet=True) # E: No matching overload found
663+
@dataclass(flibbertigibbet=True) # E: Unexpected keyword argument `flibbertigibbet`
664664
class C:
665665
pass
666666
"#,

pyrefly/lib/test/dict.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,15 @@ def bar(yes: bool) -> None:
6868
);
6969

7070
testcase!(
71-
bug = "https://github.com/facebook/pyrefly/issues/2833",
7271
test_get_dict_value_even_with_error,
7372
r#"
7473
from typing import assert_type
7574
d: dict[str, int] = {}
7675
def f(k: str | None):
7776
# We should report the mismatch between `str` and `str | None` rather than "No matching overload".
78-
v = d.get(k) # E: No matching overload
77+
v = d.get(k) # E: Argument `str | None` is not assignable to parameter `key` with type `str`
7978
# Because only one overload of `dict.get` can match based on argument count, we should use its
8079
# return type of `int | None`.
81-
assert_type(v, int | None) # E: assert_type(Unknown, int | None)
80+
assert_type(v, int | None)
8281
"#,
8382
);

pyrefly/lib/test/generic_basic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def f():
358358
359359
def g():
360360
x: dict[int, int] = {}
361-
x.update(a=1) # E: No matching overload
361+
x.update(a=1) # E: `dict[int, int]` is not assignable to parameter `self` with type `SupportsGetItem[str, int]`
362362
"#,
363363
);
364364

pyrefly/lib/test/lsp/hover_type.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,7 @@ Hover Result: `(a: int, b: bool) -> str`
443443
444444
15 | overloaded_func(False)
445445
^
446-
Hover Result: `Overload[
447-
(a: str) -> bool
448-
(a: int, b: bool) -> str
449-
]`
446+
Hover Result: `(a: str) -> bool`
450447
"#
451448
.trim(),
452449
report.trim(),
@@ -488,10 +485,7 @@ Hover Result: `(self: Foo, a: int, b: bool) -> str`
488485
489486
17 | foo.overloaded_meth(False)
490487
^
491-
Hover Result: `Overload[
492-
(self: Foo, a: str) -> bool
493-
(self: Foo, a: int, b: bool) -> str
494-
]`
488+
Hover Result: `(self: Foo, a: str) -> bool`
495489
"#
496490
.trim(),
497491
report.trim(),

pyrefly/lib/test/overload.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,6 @@ def g(x):
16771677
);
16781678

16791679
testcase!(
1680-
bug = "https://github.com/facebook/pyrefly/issues/2833",
16811680
test_eliminate_overload_using_argument_count_even_with_error,
16821681
r#"
16831682
from typing import assert_type, overload
@@ -1691,9 +1690,9 @@ def f(x, y="") -> int | str: ...
16911690
def g(x: int | None):
16921691
# We should report the mismatch between `int` and `int | None` rather than "No matching overload",
16931692
# since we know only the first overload can match based on argument count.
1694-
e = f(x) # E: No matching overload
1693+
e = f(x) # E: `int | None` is not assignable to parameter `x` with type `int`
16951694
# Even though the call failed, we know that the second overload cannot match based on argument
16961695
# count, so we should use the return type from the first overload.
1697-
assert_type(e, int) # E: assert_type(Unknown, int)
1696+
assert_type(e, int)
16981697
"#,
16991698
);

pyrefly/lib/test/pydantic/extra.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ pydantic_testcase!(
143143
from pydantic import BaseModel, ConfigDict
144144
145145
class ModelForbid(BaseModel):
146-
model_config = ConfigDict(extra=False) # E: No matching overload found for function `pydantic.config.ConfigDict.__init__`
146+
model_config = ConfigDict(extra=False) # E: `Literal[False]` is not assignable to parameter `extra`
147147
x: int
148148
149149
ModelForbid(x=1, y=2)
@@ -156,7 +156,7 @@ pydantic_testcase!(
156156
from pydantic import BaseModel, ConfigDict
157157
158158
class ModelForbid(BaseModel):
159-
model_config = ConfigDict(extra="123") # E: No matching overload found for function `pydantic.config.ConfigDict.__init__`
159+
model_config = ConfigDict(extra="123") # E: `Literal['123']` is not assignable to parameter `extra`
160160
x: int
161161
162162
ModelForbid(x=1, y=2)

pyrefly/lib/test/typed_dict.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ v5 = td_o.pop("x", "fallback")
386386
assert_type(v5, int | str)
387387
388388
v6 = td_m.pop("a") # E:
389-
assert_type(v6, Any)
389+
assert_type(v6, int)
390390
391391
v7 = td_m.pop("x")
392392
assert_type(v7, int)
@@ -748,7 +748,7 @@ class A(TypedDict):
748748
class B(A):
749749
y: str
750750
B(x=0, y='1') # OK
751-
B(x=0, y=1) # E: No matching overload found for function `B.__init__`
751+
B(x=0, y=1) # E: `Literal[1]` is not assignable to parameter `y` with type `str`
752752
"#,
753753
);
754754

@@ -896,7 +896,7 @@ def f(c1: C, c2: C, c3: dict[str, int], d: D, e: E, f: F):
896896
c1.update([("x", 1), ("y", 2)])
897897
c1.update([("z", 3)]) # E: No matching overload found for function `C.update`
898898
c1.update(x=1, y=2)
899-
c1.update(z=1) # E: No matching overload found for function `C.update`
899+
c1.update(z=1) # E: Unexpected keyword argument `z`
900900
"#,
901901
);
902902

@@ -945,9 +945,9 @@ class C(TypedDict):
945945
x: int
946946
def f(c: C, s: str):
947947
assert_type(c.setdefault("x", 0), int)
948-
c.setdefault("x", 0.0) # E: No matching overload
948+
c.setdefault("x", 0.0) # E: `float` is not assignable to parameter `default` with type `int`
949949
c.setdefault("x") # E: No matching overload
950-
c.setdefault(s, 0) # E: No matching overload
950+
c.setdefault(s, 0) # E: `str` is not assignable to parameter `key` with type `Literal['x']`
951951
"#,
952952
);
953953

@@ -963,7 +963,7 @@ class D(TypedDict):
963963
def f(c: C, d: D):
964964
c.setdefault("x", 0) # E: `Literal['x']` is not assignable to parameter `k` with type `Never`
965965
d.setdefault("x", 0)
966-
d.setdefault("y", "oops") # E: No matching overload
966+
d.setdefault("y", "oops") # E: `Literal['y']` is not assignable to parameter `key` with type `Literal['x']` # E: `Literal['oops']` is not assignable to parameter `default` with type `int`
967967
"#,
968968
);
969969

@@ -986,7 +986,7 @@ testcase!(
986986
from typing import TypedDict
987987
class C(TypedDict):
988988
x: int
989-
C(0) # E: No matching overload found for function `C.__init__`
989+
C(0) # E: `Literal[0]` is not assignable to parameter `__map` with type `C`
990990
"#,
991991
);
992992

@@ -1258,7 +1258,7 @@ from typing import TypedDict
12581258
class Movie(TypedDict, extra_items=int):
12591259
name: str
12601260
good_movie = Movie(name='Toy Story', year=1995)
1261-
bad_movie = Movie(name='Toy Story', studio='Pixar') # E: No matching overload found for function `Movie.__init__`
1261+
bad_movie = Movie(name='Toy Story', studio='Pixar') # E: `Literal['Pixar']` is not assignable to kwargs type `int`
12621262
"#,
12631263
);
12641264

0 commit comments

Comments
 (0)