mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 06:42:02 +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"
|
return "str"
|
||||||
else:
|
else:
|
||||||
return None
|
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 itertools::Itertools;
|
||||||
use ruff_diagnostics::Edit;
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::importer::{ImportRequest, Importer};
|
use ruff_diagnostics::Edit;
|
||||||
use ruff_python_ast::helpers::{
|
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::visitor::Visitor;
|
||||||
use ruff_python_ast::{self as ast, Expr, ExprContext};
|
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_python_semantic::{Definition, SemanticModel};
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
|
use crate::importer::{ImportRequest, Importer};
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
|
|
||||||
/// Return the name of the function, if it's overloaded.
|
/// 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 returns = {
|
||||||
let mut visitor = ReturnStatementVisitor::default();
|
let mut visitor = ReturnStatementVisitor::default();
|
||||||
visitor.visit_body(&function.body);
|
visitor.visit_body(&function.body);
|
||||||
|
|
||||||
|
// Ignore generators.
|
||||||
if visitor.is_generator {
|
if visitor.is_generator {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.returns
|
visitor.returns
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the return type of the first `return` statement.
|
// 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(
|
let mut return_type = return_statement.value.as_deref().map_or(
|
||||||
ResolvedPythonType::Atom(PythonType::None),
|
ResolvedPythonType::Atom(PythonType::None),
|
||||||
ResolvedPythonType::from,
|
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 {
|
match return_type {
|
||||||
ResolvedPythonType::Atom(python_type) => Some(AutoPythonType::Atom(python_type)),
|
ResolvedPythonType::Atom(python_type) => Some(AutoPythonType::Atom(python_type)),
|
||||||
ResolvedPythonType::Union(python_types) => Some(AutoPythonType::Union(python_types)),
|
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
|
61 61 | return 1
|
||||||
62 62 | elif x > 5:
|
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
|
61 62 | return 1
|
||||||
62 63 | elif x > 5:
|
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
|
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
|
4 | # Error
|
||||||
5 | def foo(a, b):
|
5 | def foo(a, b):
|
||||||
| ^^^ ANN201
|
| ^^^ ANN201
|
||||||
6 | pass
|
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`
|
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
|
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
|
9 | # Error
|
||||||
10 | def foo(a: int, b):
|
10 | def foo(a: int, b):
|
||||||
| ^^^ ANN201
|
| ^^^ ANN201
|
||||||
11 | pass
|
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`
|
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
|
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
|
19 | # Error
|
||||||
20 | def foo(a: int, b: int):
|
20 | def foo(a: int, b: int):
|
||||||
| ^^^ ANN201
|
| ^^^ ANN201
|
||||||
21 | pass
|
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
|
24 | # Error
|
||||||
25 | def foo():
|
25 | def foo():
|
||||||
| ^^^ ANN201
|
| ^^^ ANN201
|
||||||
26 | pass
|
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`
|
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
|
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):
|
24 | def error_partially_typed_1(a: int, b):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||||
25 | pass
|
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`
|
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
|
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):
|
32 | def error_partially_typed_3(a: int, b: int):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||||
33 | pass
|
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
|
41 | pass
|
||||||
42 |
|
42 |
|
||||||
|
@ -39,6 +59,14 @@ ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public f
|
||||||
| ^^^^^^^^^^^^^^^^ ANN201
|
| ^^^^^^^^^^^^^^^^ ANN201
|
||||||
44 | pass
|
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 |
|
13 13 |
|
||||||
14 14 |
|
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
|
39 | # Error
|
||||||
40 | def __init__(self, foo: int):
|
40 | def __init__(self, foo: int):
|
||||||
| ^^^^^^^^ ANN202
|
| ^^^^^^^^ ANN202
|
||||||
41 | ...
|
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__`
|
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.
|
/// A [`StatementVisitor`] that collects all `raise` statements in a function or method.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RaiseStatementVisitor<'a> {
|
pub struct RaiseStatementVisitor<'a> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue