Skip to content

Commit a8f7ccf

Browse files
authored
[ty] Improve diagnostics when NotImplemented is called (#21523)
## Summary Fixes astral-sh/ty#1571. I realised I was overcomplicating things when I described what we should do in that issue description. The simplest thing to do here is just to special-case call expressions and short-circuit the call-binding machinery entirely if we see it's `NotImplemented` being called. It doesn't really matter if the subdiagnostic doesn't fire when a union is called and one element of the union is `NotImplemented` -- the subdiagnostic doesn't need to be exhaustive; it's just to help people in some common cases. ## Test Plan Added snapshots
1 parent ce06094 commit a8f7ccf

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

crates/ty_python_semantic/resources/mdtest/call/builtins.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ isinstance("", t.Any) # error: [invalid-argument-type]
200200

201201
## The builtin `NotImplemented` constant is not callable
202202

203+
<!-- snapshot-diagnostics -->
204+
203205
```py
204-
NotImplemented() # error: [call-non-callable]
206+
raise NotImplemented() # error: [call-non-callable]
207+
raise NotImplemented("this module is not implemented yet!!!") # error: [call-non-callable]
205208
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
source: crates/ty_test/src/lib.rs
3+
expression: snapshot
4+
---
5+
---
6+
mdtest name: builtins.md - Calling builtins - The builtin `NotImplemented` constant is not callable
7+
mdtest path: crates/ty_python_semantic/resources/mdtest/call/builtins.md
8+
---
9+
10+
# Python source files
11+
12+
## mdtest_snippet.py
13+
14+
```
15+
1 | raise NotImplemented() # error: [call-non-callable]
16+
2 | raise NotImplemented("this module is not implemented yet!!!") # error: [call-non-callable]
17+
```
18+
19+
# Diagnostics
20+
21+
```
22+
error[call-non-callable]: `NotImplemented` is not callable
23+
--> src/mdtest_snippet.py:1:7
24+
|
25+
1 | raise NotImplemented() # error: [call-non-callable]
26+
| --------------^^
27+
| |
28+
| Did you mean `NotImplementedError`?
29+
2 | raise NotImplemented("this module is not implemented yet!!!") # error: [call-non-callable]
30+
|
31+
info: rule `call-non-callable` is enabled by default
32+
33+
```
34+
35+
```
36+
error[call-non-callable]: `NotImplemented` is not callable
37+
--> src/mdtest_snippet.py:2:7
38+
|
39+
1 | raise NotImplemented() # error: [call-non-callable]
40+
2 | raise NotImplemented("this module is not implemented yet!!!") # error: [call-non-callable]
41+
| --------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42+
| |
43+
| Did you mean `NotImplementedError`?
44+
|
45+
info: rule `call-non-callable` is enabled by default
46+
47+
```

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7912,6 +7912,24 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
79127912
ty
79137913
});
79147914

7915+
if callable_type.is_notimplemented(self.db()) {
7916+
if let Some(builder) = self
7917+
.context
7918+
.report_lint(&CALL_NON_CALLABLE, call_expression)
7919+
{
7920+
let mut diagnostic = builder.into_diagnostic("`NotImplemented` is not callable");
7921+
diagnostic.annotate(
7922+
self.context
7923+
.secondary(&**func)
7924+
.message("Did you mean `NotImplementedError`?"),
7925+
);
7926+
diagnostic.set_concise_message(
7927+
"`NotImplemented` is not callable - did you mean `NotImplementedError`?",
7928+
);
7929+
}
7930+
return Type::unknown();
7931+
}
7932+
79157933
// Special handling for `TypedDict` method calls
79167934
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
79177935
let value_type = self.expression_type(value);

0 commit comments

Comments
 (0)