mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 10:22:24 +00:00
Handle functions that never return in RET503 (#2701)
This commit is contained in:
parent
ec63658250
commit
cda2ff0b18
3 changed files with 155 additions and 16 deletions
|
@ -1,3 +1,11 @@
|
|||
import os
|
||||
import posix
|
||||
from posix import abort
|
||||
import sys as std_sys
|
||||
|
||||
import pytest
|
||||
from pytest import xfail as py_xfail
|
||||
|
||||
###
|
||||
# Errors
|
||||
###
|
||||
|
@ -39,6 +47,20 @@ def x(y):
|
|||
print() # error
|
||||
|
||||
|
||||
# A nonexistent function
|
||||
def func_unknown(x):
|
||||
if x > 0:
|
||||
return False
|
||||
no_such_function() # error
|
||||
|
||||
|
||||
# A function that does return the control
|
||||
def func_no_noreturn(x):
|
||||
if x > 0:
|
||||
return False
|
||||
print("", end="") # error
|
||||
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
|
@ -123,3 +145,52 @@ def prompts(self, foo):
|
|||
for x in foo:
|
||||
yield x
|
||||
yield x + 1
|
||||
|
||||
|
||||
# Functions that never return
|
||||
def noreturn__exit(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
os._exit(0)
|
||||
|
||||
|
||||
def noreturn_abort(x):
|
||||
if x > 0:
|
||||
return 1
|
||||
os.abort()
|
||||
|
||||
|
||||
def noreturn_abort_2():
|
||||
if x > 0:
|
||||
return 1
|
||||
abort()
|
||||
|
||||
|
||||
def noreturn_exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
std_sys.exit(0)
|
||||
|
||||
|
||||
def noreturn_pytest_exit():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.exit("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_fail():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.fail("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_skip():
|
||||
if x > 0:
|
||||
return 1
|
||||
pytest.skip("oof")
|
||||
|
||||
|
||||
def noreturn_pytest_xfail():
|
||||
if x > 0:
|
||||
return 1
|
||||
py_xfail("oof")
|
||||
|
|
|
@ -165,6 +165,30 @@ fn implicit_return_value(checker: &mut Checker, stack: &Stack) {
|
|||
}
|
||||
}
|
||||
|
||||
const NORETURN_FUNCS: &[&[&str]] = &[
|
||||
// builtins
|
||||
&["", "exit"],
|
||||
// stdlib
|
||||
&["os", "_exit"],
|
||||
&["os", "abort"],
|
||||
&["posix", "abort"],
|
||||
&["sys", "exit"],
|
||||
// third-party modules
|
||||
&["pytest", "exit"],
|
||||
&["pytest", "fail"],
|
||||
&["pytest", "skip"],
|
||||
&["pytest", "xfail"],
|
||||
];
|
||||
|
||||
/// Return `true` if the `func` is a known function that never returns.
|
||||
fn is_noreturn_func(checker: &Checker, func: &Expr) -> bool {
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
NORETURN_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
})
|
||||
}
|
||||
|
||||
/// RET503
|
||||
fn implicit_return(checker: &mut Checker, last_stmt: &Stmt) {
|
||||
match &last_stmt.node {
|
||||
|
@ -208,6 +232,12 @@ fn implicit_return(checker: &mut Checker, last_stmt: &Stmt) {
|
|||
| StmtKind::While { .. }
|
||||
| StmtKind::Raise { .. }
|
||||
| StmtKind::Try { .. } => {}
|
||||
StmtKind::Expr { value, .. }
|
||||
if matches!(
|
||||
&value.node,
|
||||
ExprKind::Call { func, .. }
|
||||
if is_noreturn_func(checker, func)
|
||||
) => {}
|
||||
_ => {
|
||||
let mut diagnostic = Diagnostic::new(ImplicitReturn, Range::from_located(last_stmt));
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
|
|
|
@ -5,78 +5,116 @@ expression: diagnostics
|
|||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 7
|
||||
row: 15
|
||||
column: 4
|
||||
end_location:
|
||||
row: 8
|
||||
row: 16
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 14
|
||||
row: 22
|
||||
column: 8
|
||||
end_location:
|
||||
row: 14
|
||||
row: 22
|
||||
column: 15
|
||||
fix:
|
||||
content:
|
||||
- " return None"
|
||||
- ""
|
||||
location:
|
||||
row: 15
|
||||
row: 23
|
||||
column: 0
|
||||
end_location:
|
||||
row: 15
|
||||
row: 23
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 23
|
||||
row: 31
|
||||
column: 4
|
||||
end_location:
|
||||
row: 23
|
||||
row: 31
|
||||
column: 11
|
||||
fix:
|
||||
content:
|
||||
- " return None"
|
||||
- ""
|
||||
location:
|
||||
row: 24
|
||||
row: 32
|
||||
column: 0
|
||||
end_location:
|
||||
row: 24
|
||||
row: 32
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 29
|
||||
row: 37
|
||||
column: 8
|
||||
end_location:
|
||||
row: 30
|
||||
row: 38
|
||||
column: 20
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 39
|
||||
row: 47
|
||||
column: 8
|
||||
end_location:
|
||||
row: 39
|
||||
row: 47
|
||||
column: 15
|
||||
fix:
|
||||
content:
|
||||
- " return None"
|
||||
- ""
|
||||
location:
|
||||
row: 40
|
||||
row: 48
|
||||
column: 0
|
||||
end_location:
|
||||
row: 40
|
||||
row: 48
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 54
|
||||
column: 4
|
||||
end_location:
|
||||
row: 54
|
||||
column: 22
|
||||
fix:
|
||||
content:
|
||||
- " return None"
|
||||
- ""
|
||||
location:
|
||||
row: 55
|
||||
column: 0
|
||||
end_location:
|
||||
row: 55
|
||||
column: 0
|
||||
parent: ~
|
||||
- kind:
|
||||
ImplicitReturn: ~
|
||||
location:
|
||||
row: 61
|
||||
column: 4
|
||||
end_location:
|
||||
row: 61
|
||||
column: 21
|
||||
fix:
|
||||
content:
|
||||
- " return None"
|
||||
- ""
|
||||
location:
|
||||
row: 62
|
||||
column: 0
|
||||
end_location:
|
||||
row: 62
|
||||
column: 0
|
||||
parent: ~
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue