mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 22:31:47 +00:00
Detect implicit returns in auto-return-types (#8952)
## Summary Adds detection for branches without a `return` or `raise`, so that we can properly `Optional` the return types. I'd like to remove this and replace it with our code graph analysis from the `unreachable.rs` rule, but it at least fixes the worst offenders. Closes #8942.
This commit is contained in:
parent
d66063bb33
commit
e5db72459e
8 changed files with 860 additions and 20 deletions
|
@ -63,3 +63,87 @@ def func(x: int):
|
|||
return "str"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x:
|
||||
return 1
|
||||
|
||||
|
||||
def func():
|
||||
x = 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
match x:
|
||||
case [1, 2, 3]:
|
||||
return 1
|
||||
case 4 as y:
|
||||
return "foo"
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
break
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
finally:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
return 1
|
||||
except:
|
||||
return 2
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def func(x: int):
|
||||
while x > 0:
|
||||
break
|
||||
return 1
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use itertools::Itertools;
|
||||
use ruff_diagnostics::Edit;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::helpers::{
|
||||
pep_604_union, typing_optional, typing_union, ReturnStatementVisitor,
|
||||
implicit_return, pep_604_union, typing_optional, typing_union, ReturnStatementVisitor,
|
||||
};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext};
|
||||
|
@ -13,6 +12,7 @@ use ruff_python_semantic::analyze::visibility;
|
|||
use ruff_python_semantic::{Definition, SemanticModel};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// Return the name of the function, if it's overloaded.
|
||||
|
@ -48,14 +48,19 @@ pub(crate) fn auto_return_type(function: &ast::StmtFunctionDef) -> Option<AutoPy
|
|||
let returns = {
|
||||
let mut visitor = ReturnStatementVisitor::default();
|
||||
visitor.visit_body(&function.body);
|
||||
|
||||
// Ignore generators.
|
||||
if visitor.is_generator {
|
||||
return None;
|
||||
}
|
||||
|
||||
visitor.returns
|
||||
};
|
||||
|
||||
// Determine the return type of the first `return` statement.
|
||||
let (return_statement, returns) = returns.split_first()?;
|
||||
let Some((return_statement, returns)) = returns.split_first() else {
|
||||
return Some(AutoPythonType::Atom(PythonType::None));
|
||||
};
|
||||
let mut return_type = return_statement.value.as_deref().map_or(
|
||||
ResolvedPythonType::Atom(PythonType::None),
|
||||
ResolvedPythonType::from,
|
||||
|
@ -69,6 +74,16 @@ pub(crate) fn auto_return_type(function: &ast::StmtFunctionDef) -> Option<AutoPy
|
|||
));
|
||||
}
|
||||
|
||||
// If the function has an implicit return, union with `None`, as in:
|
||||
// ```python
|
||||
// def func(x: int):
|
||||
// if x > 0:
|
||||
// return 1
|
||||
// ```
|
||||
if implicit_return(function) {
|
||||
return_type = return_type.union(ResolvedPythonType::Atom(PythonType::None));
|
||||
}
|
||||
|
||||
match return_type {
|
||||
ResolvedPythonType::Atom(python_type) => Some(AutoPythonType::Atom(python_type)),
|
||||
ResolvedPythonType::Union(python_types) => Some(AutoPythonType::Union(python_types)),
|
||||
|
|
|
@ -200,4 +200,231 @@ auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public f
|
|||
61 61 | return 1
|
||||
62 62 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
65 65 | return None
|
||||
66 66 |
|
||||
67 67 |
|
||||
68 |-def func(x: int):
|
||||
68 |+def func(x: int) -> int | None:
|
||||
69 69 | if x:
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
77 |-def func(x: int):
|
||||
77 |+def func(x: int) -> int | None:
|
||||
78 78 | if x > 0:
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `str | int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
81 81 |
|
||||
82 |-def func(x: int):
|
||||
82 |+def func(x: int) -> str | int:
|
||||
83 83 | match x:
|
||||
84 84 | case [1, 2, 3]:
|
||||
85 85 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
87 87 | return "foo"
|
||||
88 88 |
|
||||
89 89 |
|
||||
90 |-def func(x: int):
|
||||
90 |+def func(x: int) -> int | None:
|
||||
91 91 | for i in range(5):
|
||||
92 92 | if i > 0:
|
||||
93 93 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
101 101 | return 4
|
||||
102 102 |
|
||||
103 103 |
|
||||
104 |-def func(x: int):
|
||||
104 |+def func(x: int) -> int | None:
|
||||
105 105 | for i in range(5):
|
||||
106 106 | if i > 0:
|
||||
107 107 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
109 109 | return 4
|
||||
110 110 |
|
||||
111 111 |
|
||||
112 |-def func(x: int):
|
||||
112 |+def func(x: int) -> int | None:
|
||||
113 113 | try:
|
||||
114 114 | pass
|
||||
115 115 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
143 143 | pass
|
||||
144 144 |
|
||||
145 145 |
|
||||
146 |-def func(x: int):
|
||||
146 |+def func(x: int) -> int | None:
|
||||
147 147 | while x > 0:
|
||||
148 148 | break
|
||||
149 149 | return 1
|
||||
|
||||
|
||||
|
|
|
@ -220,4 +220,266 @@ auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public f
|
|||
61 62 | return 1
|
||||
62 63 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
65 66 | return None
|
||||
66 67 |
|
||||
67 68 |
|
||||
68 |-def func(x: int):
|
||||
69 |+def func(x: int) -> Optional[int]:
|
||||
69 70 | if x:
|
||||
70 71 | return 1
|
||||
71 72 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
74 75 | x = 1
|
||||
75 76 |
|
||||
76 77 |
|
||||
77 |-def func(x: int):
|
||||
78 |+def func(x: int) -> Optional[int]:
|
||||
78 79 | if x > 0:
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
81 82 |
|
||||
82 |-def func(x: int):
|
||||
83 |+def func(x: int) -> Union[str | int]:
|
||||
83 84 | match x:
|
||||
84 85 | case [1, 2, 3]:
|
||||
85 86 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
87 88 | return "foo"
|
||||
88 89 |
|
||||
89 90 |
|
||||
90 |-def func(x: int):
|
||||
91 |+def func(x: int) -> Optional[int]:
|
||||
91 92 | for i in range(5):
|
||||
92 93 | if i > 0:
|
||||
93 94 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
101 102 | return 4
|
||||
102 103 |
|
||||
103 104 |
|
||||
104 |-def func(x: int):
|
||||
105 |+def func(x: int) -> Optional[int]:
|
||||
105 106 | for i in range(5):
|
||||
106 107 | if i > 0:
|
||||
107 108 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
109 110 | return 4
|
||||
110 111 |
|
||||
111 112 |
|
||||
112 |-def func(x: int):
|
||||
113 |+def func(x: int) -> Optional[int]:
|
||||
113 114 | try:
|
||||
114 115 | pass
|
||||
115 116 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
143 144 | pass
|
||||
144 145 |
|
||||
145 146 |
|
||||
146 |-def func(x: int):
|
||||
147 |+def func(x: int) -> Optional[int]:
|
||||
147 148 | while x > 0:
|
||||
148 149 | break
|
||||
149 150 | return 1
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
annotation_presence.py:5:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:5:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
4 | # Error
|
||||
5 | def foo(a, b):
|
||||
| ^^^ ANN201
|
||||
6 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | from typing_extensions import override
|
||||
3 3 |
|
||||
4 4 | # Error
|
||||
5 |-def foo(a, b):
|
||||
5 |+def foo(a, b) -> None:
|
||||
6 6 | pass
|
||||
7 7 |
|
||||
8 8 |
|
||||
|
||||
annotation_presence.py:5:9: ANN001 Missing type annotation for function argument `a`
|
||||
|
|
||||
|
@ -26,14 +36,24 @@ annotation_presence.py:5:12: ANN001 Missing type annotation for function argumen
|
|||
6 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:10:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:10:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
9 | # Error
|
||||
10 | def foo(a: int, b):
|
||||
| ^^^ ANN201
|
||||
11 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 |
|
||||
8 8 |
|
||||
9 9 | # Error
|
||||
10 |-def foo(a: int, b):
|
||||
10 |+def foo(a: int, b) -> None:
|
||||
11 11 | pass
|
||||
12 12 |
|
||||
13 13 |
|
||||
|
||||
annotation_presence.py:10:17: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
|
@ -51,23 +71,43 @@ annotation_presence.py:15:17: ANN001 Missing type annotation for function argume
|
|||
16 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:20:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:20:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
19 | # Error
|
||||
20 | def foo(a: int, b: int):
|
||||
| ^^^ ANN201
|
||||
21 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
annotation_presence.py:25:5: ANN201 Missing return type annotation for public function `foo`
|
||||
ℹ Unsafe fix
|
||||
17 17 |
|
||||
18 18 |
|
||||
19 19 | # Error
|
||||
20 |-def foo(a: int, b: int):
|
||||
20 |+def foo(a: int, b: int) -> None:
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
|
||||
annotation_presence.py:25:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
24 | # Error
|
||||
25 | def foo():
|
||||
| ^^^ ANN201
|
||||
26 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 24 | # Error
|
||||
25 |-def foo():
|
||||
25 |+def foo() -> None:
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
|
||||
annotation_presence.py:45:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
ignore_fully_untyped.py:24:5: ANN201 Missing return type annotation for public function `error_partially_typed_1`
|
||||
ignore_fully_untyped.py:24:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_1`
|
||||
|
|
||||
24 | def error_partially_typed_1(a: int, b):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
25 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 |-def error_partially_typed_1(a: int, b):
|
||||
24 |+def error_partially_typed_1(a: int, b) -> None:
|
||||
25 25 | pass
|
||||
26 26 |
|
||||
27 27 |
|
||||
|
||||
ignore_fully_untyped.py:24:37: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
|
@ -23,15 +33,25 @@ ignore_fully_untyped.py:28:37: ANN001 Missing type annotation for function argum
|
|||
29 | pass
|
||||
|
|
||||
|
||||
ignore_fully_untyped.py:32:5: ANN201 Missing return type annotation for public function `error_partially_typed_3`
|
||||
ignore_fully_untyped.py:32:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_3`
|
||||
|
|
||||
32 | def error_partially_typed_3(a: int, b: int):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
33 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public function `error_typed_self`
|
||||
ℹ Unsafe fix
|
||||
29 29 | pass
|
||||
30 30 |
|
||||
31 31 |
|
||||
32 |-def error_partially_typed_3(a: int, b: int):
|
||||
32 |+def error_partially_typed_3(a: int, b: int) -> None:
|
||||
33 33 | pass
|
||||
34 34 |
|
||||
35 35 |
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 [*] Missing return type annotation for public function `error_typed_self`
|
||||
|
|
||||
41 | pass
|
||||
42 |
|
||||
|
@ -39,6 +59,14 @@ ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public f
|
|||
| ^^^^^^^^^^^^^^^^ ANN201
|
||||
44 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
40 40 | def ok_untyped_method(self):
|
||||
41 41 | pass
|
||||
42 42 |
|
||||
43 |- def error_typed_self(self: X):
|
||||
43 |+ def error_typed_self(self: X) -> None:
|
||||
44 44 | pass
|
||||
|
||||
|
||||
|
|
|
@ -41,14 +41,24 @@ mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special
|
|||
13 13 |
|
||||
14 14 |
|
||||
|
||||
mypy_init_return.py:40:5: ANN202 Missing return type annotation for private function `__init__`
|
||||
mypy_init_return.py:40:5: ANN202 [*] Missing return type annotation for private function `__init__`
|
||||
|
|
||||
39 | # Error
|
||||
40 | def __init__(self, foo: int):
|
||||
| ^^^^^^^^ ANN202
|
||||
41 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | # Error
|
||||
40 |-def __init__(self, foo: int):
|
||||
40 |+def __init__(self, foo: int) -> None:
|
||||
41 41 | ...
|
||||
42 42 |
|
||||
43 43 |
|
||||
|
||||
mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
||||
|
|
||||
|
|
|
@ -911,6 +911,180 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the function has an implicit return.
|
||||
pub fn implicit_return(function: &ast::StmtFunctionDef) -> bool {
|
||||
/// Returns `true` if the body may break via a `break` statement.
|
||||
fn sometimes_breaks(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
||||
if returns(body) {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if returns(body) {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if std::iter::once(body)
|
||||
.chain(elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
.any(|body| sometimes_breaks(body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
if cases.iter().any(|case| sometimes_breaks(&case.body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
if sometimes_breaks(body)
|
||||
|| handlers.iter().any(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
}) = handler;
|
||||
sometimes_breaks(body)
|
||||
})
|
||||
|| sometimes_breaks(orelse)
|
||||
|| sometimes_breaks(finalbody)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
if sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Break(_) => return true,
|
||||
Stmt::Return(_) => return false,
|
||||
Stmt::Raise(_) => return false,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the body may break via a `break` statement.
|
||||
fn always_breaks(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::Break(_) => return true,
|
||||
Stmt::Return(_) => return false,
|
||||
Stmt::Raise(_) => return false,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the body contains a branch that ends without an explicit `return` or
|
||||
/// `raise` statement.
|
||||
fn returns(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts.iter().rev() {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
||||
if always_breaks(body) {
|
||||
return false;
|
||||
}
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
if returns(orelse) && !sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if always_breaks(body) {
|
||||
return false;
|
||||
}
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
if returns(orelse) && !sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if elif_else_clauses.iter().any(|clause| clause.test.is_none())
|
||||
&& std::iter::once(body)
|
||||
.chain(elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
.all(|body| returns(body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
// Note: we assume the `match` is exhaustive.
|
||||
if cases.iter().all(|case| returns(&case.body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
// If the `finally` block returns, the `try` block must also return.
|
||||
if returns(finalbody) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the `body` or the `else` block returns, the `try` block must also return.
|
||||
if (returns(body) || returns(orelse))
|
||||
&& handlers.iter().all(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
}) = handler;
|
||||
returns(body)
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Return(_) => return true,
|
||||
Stmt::Raise(_) => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
!returns(&function.body)
|
||||
}
|
||||
|
||||
/// A [`StatementVisitor`] that collects all `raise` statements in a function or method.
|
||||
#[derive(Default)]
|
||||
pub struct RaiseStatementVisitor<'a> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue