Skip to content

Commit

Permalink
[flake8-return] Recognize functions returning Never as non-return…
Browse files Browse the repository at this point in the history
…ing (`RET503`)
  • Loading branch information
viccie30 committed Jan 6, 2025
1 parent bafe871 commit bcd639c
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 160 deletions.
51 changes: 44 additions & 7 deletions crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,17 +331,19 @@ def end_of_file():


# function return type annotation NoReturn
def bar() -> NoReturn:
abort()

def foo(x: int) -> int:
def bar() -> NoReturn:
abort()
if x == 5:
return 5
bar()


def raises(value: str) -> NoReturn:
raise RuntimeError("something went wrong")

def foo(string: str) -> str:
def raises(value: str) -> NoReturn:
raise RuntimeError("something went wrong")

match string:
case "a":
Expand All @@ -353,17 +355,52 @@ def raises(value: str) -> NoReturn:
case _:
raises(string)

def bar() -> NoReturn:
a = 1 + 2
raise AssertionError("Very bad")

def foo() -> int:
def baz() -> int:
return 1

if baz() > 3:
return 1
bar()



def bar() -> NoReturn:
a = 1 + 2
raise AssertionError("Very bad")
# function return type annotation typing_extensions.Never
def bar() -> typing_extensions.Never:
abort()

def foo(x: int) -> int:
if x == 5:
return 5
bar()


def raises(value: str) -> typing_extensions.Never:
raise RuntimeError("something went wrong")

def foo(string: str) -> str:

match string:
case "a":
return "first"
case "b":
return "second"
case "c":
return "third"
case _:
raises(string)

def bar() -> typing_extensions.Never:
a = 1 + 2
raise AssertionError("Very bad")

def foo() -> int:
def baz() -> int:
return 1

if baz() > 3:
return 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from posix import abort
from typing import NoReturn

import typing_extensions



# function return type annotation NoReturn
def foo(x: int) -> int:
def bar() -> NoReturn:
abort()
if x == 5:
return 5
bar()


def foo(string: str) -> str:
def raises(value: str) -> NoReturn:
raise RuntimeError("something went wrong")

match string:
case "a":
return "first"
case "b":
return "second"
case "c":
return "third"
case _:
raises(string)


def foo() -> int:
def baz() -> int:
return 1


def bar() -> NoReturn:
a = 1 + 2
raise AssertionError("Very bad")



if baz() > 3:
return 1
bar()



# function return type annotation typing_extensions.Never
def foo(x: int) -> int:
def bar() -> typing_extensions.Never:
abort()
if x == 5:
return 5
bar()


def foo(string: str) -> str:
def raises(value: str) -> typing_extensions.Never:
raise RuntimeError("something went wrong")

match string:
case "a":
return "first"
case "b":
return "second"
case "c":
return "third"
case _:
raises(string)


def foo() -> int:
def baz() -> int:
return 1


def bar() -> typing_extensions.Never:
a = 1 + 2
raise AssertionError("Very bad")



if baz() > 3:
return 1
bar()
11 changes: 11 additions & 0 deletions crates/ruff_linter/src/rules/flake8_return/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,15 @@ mod tests {
assert_messages!(snapshot, diagnostics);
Ok(())
}

#[test]
#[ignore = "Non-returning nested functions are not handled correctly"]
fn noreturn_nested_functions() -> Result<()> {
let diagnostics = test_path(
Path::new("flake8_return/RET503_nested_functions.py"),
&LinterSettings::for_rule(Rule::ImplicitReturn),
)?;
assert!(diagnostics.is_empty());
Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool {
};

semantic.match_typing_qualified_name(&qualified_name, "NoReturn")
|| semantic.match_typing_qualified_name(&qualified_name, "Never")
}

fn add_return_none(checker: &mut Checker, stmt: &Stmt, range: TextRange) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,71 +403,17 @@ RET503.py:329:5: RET503 [*] Missing explicit `return` at the end of function abl
332 333 |
333 334 | # function return type annotation NoReturn

RET503.py:339:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
RET503.py:415:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
337 | if x == 5:
338 | return 5
339 | bar()
| ^^^^^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
337 337 | if x == 5:
338 338 | return 5
339 339 | bar()
340 |+ return None
340 341 |
341 342 |
342 343 | def foo(string: str) -> str:

RET503.py:354:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
352 | return "third"
353 | case _:
354 | raises(string)
| ^^^^^^^^^^^^^^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
352 352 | return "third"
353 353 | case _:
354 354 | raises(string)
355 |+ return None
355 356 |
356 357 |
357 358 | def foo() -> int:

RET503.py:370:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
368 | if baz() > 3:
369 | return 1
370 | bar()
| ^^^^^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
368 368 | if baz() > 3:
369 369 | return 1
370 370 | bar()
371 |+ return None
371 372 |
372 373 |
373 374 | def f():

RET503.py:378:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
376 | else:
377 | with c:
378 | d
413 | else:
414 | with c:
415 | d
| ^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
376 376 | else:
377 377 | with c:
378 378 | d
379 |+ return None
413 413 | else:
414 414 | with c:
415 415 | d
416 |+ return None
Original file line number Diff line number Diff line change
Expand Up @@ -411,99 +411,20 @@ RET503.py:326:1: RET503 [*] Missing explicit `return` at the end of function abl
332 333 |
333 334 | # function return type annotation NoReturn

RET503.py:334:1: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
333 | # function return type annotation NoReturn
334 | / def foo(x: int) -> int:
335 | | def bar() -> NoReturn:
336 | | abort()
337 | | if x == 5:
338 | | return 5
339 | | bar()
| |_________^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
337 337 | if x == 5:
338 338 | return 5
339 339 | bar()
340 |+ return None
340 341 |
341 342 |
342 343 | def foo(string: str) -> str:

RET503.py:342:1: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
342 | / def foo(string: str) -> str:
343 | | def raises(value: str) -> NoReturn:
344 | | raise RuntimeError("something went wrong")
345 | |
346 | | match string:
347 | | case "a":
348 | | return "first"
349 | | case "b":
350 | | return "second"
351 | | case "c":
352 | | return "third"
353 | | case _:
354 | | raises(string)
| |__________________________^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
352 352 | return "third"
353 353 | case _:
354 354 | raises(string)
355 |+ return None
355 356 |
356 357 |
357 358 | def foo() -> int:

RET503.py:357:1: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
357 | / def foo() -> int:
358 | | def baz() -> int:
359 | | return 1
360 | |
361 | |
362 | | def bar() -> NoReturn:
363 | | a = 1 + 2
364 | | raise AssertionError("Very bad")
365 | |
366 | |
367 | |
368 | | if baz() > 3:
369 | | return 1
370 | | bar()
| |_________^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
368 368 | if baz() > 3:
369 369 | return 1
370 370 | bar()
371 |+ return None
371 372 |
372 373 |
373 374 | def f():

RET503.py:373:1: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
373 | / def f():
374 | | if a:
375 | | return b
376 | | else:
377 | | with c:
378 | | d
RET503.py:410:1: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value
|
410 | / def f():
411 | | if a:
412 | | return b
413 | | else:
414 | | with c:
415 | | d
| |_____________^ RET503
|
= help: Add explicit `return` statement

ℹ Unsafe fix
376 376 | else:
377 377 | with c:
378 378 | d
379 |+ return None
413 413 | else:
414 414 | with c:
415 415 | d
416 |+ return None

0 comments on commit bcd639c

Please sign in to comment.