From fa09fb9bcfda02c8b78f6f756e06a6773ab29f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikko=20Lepp=C3=A4nen?= Date: Wed, 24 Jan 2024 18:13:07 +0200 Subject: [PATCH 1/2] fix(RET503): take NoReturn function annotation into account when analyzing implicit returns --- .../test/fixtures/flake8_return/RET503.py | 52 +- .../src/rules/flake8_return/rules/function.rs | 26 +- ...lake8_return__tests__RET503_RET503.py.snap | 513 ++++++++++-------- 3 files changed, 357 insertions(+), 234 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py index 9ec90617d31c4..acda343839726 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py @@ -1,14 +1,15 @@ +import _thread import builtins import os import posix -from posix import abort import sys as std_sys import typing -import typing_extensions -import _thread -import _winapi +from posix import abort +from typing import NoReturn +import _winapi import pytest +import typing_extensions from pytest import xfail as py_xfail ### @@ -326,3 +327,46 @@ def end_of_file(): if False: return 1 x = 2 \ + + + +# 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() \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs index 396a157e9cde1..ecee19604f276 100644 --- a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs +++ b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs @@ -368,6 +368,30 @@ fn implicit_return_value(checker: &mut Checker, stack: &Stack) { } } +fn is_func_def_annotated_with_noreturn(func: &Expr, semantic: &SemanticModel) -> bool { + let Some(func_binding) = semantic.lookup_attribute(func) else { + return false; + }; + let Some(node_id) = semantic.binding(func_binding).source else { + return false; + }; + + let Stmt::FunctionDef(ast::StmtFunctionDef { returns, .. }) = semantic.statement(node_id) + else { + return false; + }; + + let Some(return_expr) = returns.as_ref() else { + return false; + }; + + let Some(call_path) = semantic.resolve_call_path(return_expr) else { + return false; + }; + + semantic.match_typing_call_path(&call_path, "NoReturn") +} + /// Return `true` if the `func` is a known function that never returns. fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { semantic.resolve_call_path(func).is_some_and(|call_path| { @@ -455,7 +479,7 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) { if matches!( value.as_ref(), Expr::Call(ast::ExprCall { func, .. }) - if is_noreturn_func(func, checker.semantic()) + if is_noreturn_func(func, checker.semantic()) || is_func_def_annotated_with_noreturn(func, checker.semantic()) ) => {} _ => { let mut diagnostic = Diagnostic::new(ImplicitReturn, stmt.range()); diff --git a/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap b/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap index f3e16f59191df..9d72b3feea14d 100644 --- a/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap +++ b/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap @@ -1,403 +1,458 @@ --- source: crates/ruff_linter/src/rules/flake8_return/mod.rs --- -RET503.py:20:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:21:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -18 | # if/elif/else -19 | def x(y): -20 | if not y: +19 | # if/elif/else +20 | def x(y): +21 | if not y: | _____^ -21 | | return 1 +22 | | return 1 | |________________^ RET503 -22 | # error +23 | # error | = help: Add explicit `return` statement ℹ Unsafe fix -19 19 | def x(y): -20 20 | if not y: -21 21 | return 1 - 22 |+ return None -22 23 | # error -23 24 | +20 20 | def x(y): +21 21 | if not y: +22 22 | return 1 + 23 |+ return None +23 24 | # error 24 25 | +25 26 | -RET503.py:27:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:28:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -25 | def x(y): -26 | if not y: -27 | print() # error +26 | def x(y): +27 | if not y: +28 | print() # error | ^^^^^^^ RET503 -28 | else: -29 | return 2 +29 | else: +30 | return 2 | = help: Add explicit `return` statement ℹ Unsafe fix -25 25 | def x(y): -26 26 | if not y: -27 27 | print() # error - 28 |+ return None -28 29 | else: -29 30 | return 2 -30 31 | - -RET503.py:36:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +26 26 | def x(y): +27 27 | if not y: +28 28 | print() # error + 29 |+ return None +29 30 | else: +30 31 | return 2 +31 32 | + +RET503.py:37:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -34 | return 1 -35 | -36 | print() # error +35 | return 1 +36 | +37 | print() # error | ^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -34 34 | return 1 -35 35 | -36 36 | print() # error - 37 |+ return None -37 38 | +35 35 | return 1 +36 36 | +37 37 | print() # error + 38 |+ return None 38 39 | -39 40 | # for +39 40 | +40 41 | # for -RET503.py:41:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:42:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -39 | # for -40 | def x(y): -41 | for i in range(10): +40 | # for +41 | def x(y): +42 | for i in range(10): | _____^ -42 | | if i > 10: -43 | | return i +43 | | if i > 10: +44 | | return i | |____________________^ RET503 -44 | # error +45 | # error | = help: Add explicit `return` statement ℹ Unsafe fix -41 41 | for i in range(10): -42 42 | if i > 10: -43 43 | return i - 44 |+ return None -44 45 | # error -45 46 | +42 42 | for i in range(10): +43 43 | if i > 10: +44 44 | return i + 45 |+ return None +45 46 | # error 46 47 | +47 48 | -RET503.py:52:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:53:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -50 | return i -51 | else: -52 | print() # error +51 | return i +52 | else: +53 | print() # error | ^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -50 50 | return i -51 51 | else: -52 52 | print() # error - 53 |+ return None -53 54 | +51 51 | return i +52 52 | else: +53 53 | print() # error + 54 |+ return None 54 55 | -55 56 | # A nonexistent function +55 56 | +56 57 | # A nonexistent function -RET503.py:59:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:60:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -57 | if x > 0: -58 | return False -59 | no_such_function() # error +58 | if x > 0: +59 | return False +60 | no_such_function() # error | ^^^^^^^^^^^^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -57 57 | if x > 0: -58 58 | return False -59 59 | no_such_function() # error - 60 |+ return None -60 61 | +58 58 | if x > 0: +59 59 | return False +60 60 | no_such_function() # error + 61 |+ return None 61 62 | -62 63 | # A function that does return the control +62 63 | +63 64 | # A function that does return the control -RET503.py:66:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:67:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -64 | if x > 0: -65 | return False -66 | print("", end="") # error +65 | if x > 0: +66 | return False +67 | print("", end="") # error | ^^^^^^^^^^^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -64 64 | if x > 0: -65 65 | return False -66 66 | print("", end="") # error - 67 |+ return None -67 68 | +65 65 | if x > 0: +66 66 | return False +67 67 | print("", end="") # error + 68 |+ return None 68 69 | -69 70 | ### +69 70 | +70 71 | ### -RET503.py:82:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:83:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -80 | # last line in while loop -81 | def x(y): -82 | while i > 0: +81 | # last line in while loop +82 | def x(y): +83 | while i > 0: | _____^ -83 | | if y > 0: -84 | | return 1 -85 | | y += 1 +84 | | if y > 0: +85 | | return 1 +86 | | y += 1 | |______________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -83 83 | if y > 0: -84 84 | return 1 -85 85 | y += 1 - 86 |+ return None -86 87 | +84 84 | if y > 0: +85 85 | return 1 +86 86 | y += 1 + 87 |+ return None 87 88 | -88 89 | # exclude empty functions +88 89 | +89 90 | # exclude empty functions -RET503.py:113:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:114:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -111 | # return value within loop -112 | def bar1(x, y, z): -113 | for i in x: +112 | # return value within loop +113 | def bar1(x, y, z): +114 | for i in x: | _____^ -114 | | if i > y: -115 | | break -116 | | return z +115 | | if i > y: +116 | | break +117 | | return z | |________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -114 114 | if i > y: -115 115 | break -116 116 | return z - 117 |+ return None -117 118 | +115 115 | if i > y: +116 116 | break +117 117 | return z + 118 |+ return None 118 119 | -119 120 | def bar3(x, y, z): +119 120 | +120 121 | def bar3(x, y, z): -RET503.py:120:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:121:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -119 | def bar3(x, y, z): -120 | for i in x: +120 | def bar3(x, y, z): +121 | for i in x: | _____^ -121 | | if i > y: -122 | | if z: -123 | | break -124 | | else: -125 | | return z -126 | | return None +122 | | if i > y: +123 | | if z: +124 | | break +125 | | else: +126 | | return z +127 | | return None | |___________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -124 124 | else: -125 125 | return z -126 126 | return None - 127 |+ return None -127 128 | +125 125 | else: +126 126 | return z +127 127 | return None + 128 |+ return None 128 129 | -129 130 | def bar1(x, y, z): +129 130 | +130 131 | def bar1(x, y, z): -RET503.py:130:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:131:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -129 | def bar1(x, y, z): -130 | for i in x: +130 | def bar1(x, y, z): +131 | for i in x: | _____^ -131 | | if i < y: -132 | | continue -133 | | return z +132 | | if i < y: +133 | | continue +134 | | return z | |________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -131 131 | if i < y: -132 132 | continue -133 133 | return z - 134 |+ return None -134 135 | +132 132 | if i < y: +133 133 | continue +134 134 | return z + 135 |+ return None 135 136 | -136 137 | def bar3(x, y, z): +136 137 | +137 138 | def bar3(x, y, z): -RET503.py:137:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:138:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -136 | def bar3(x, y, z): -137 | for i in x: +137 | def bar3(x, y, z): +138 | for i in x: | _____^ -138 | | if i < y: -139 | | if z: -140 | | continue -141 | | else: -142 | | return z -143 | | return None +139 | | if i < y: +140 | | if z: +141 | | continue +142 | | else: +143 | | return z +144 | | return None | |___________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -141 141 | else: -142 142 | return z -143 143 | return None - 144 |+ return None -144 145 | +142 142 | else: +143 143 | return z +144 144 | return None + 145 |+ return None 145 146 | -146 147 | def prompts(self, foo): +146 147 | +147 148 | def prompts(self, foo): -RET503.py:274:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:275:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -272 | return False -273 | -274 | for value in values: +273 | return False +274 | +275 | for value in values: | _____^ -275 | | print(value) +276 | | print(value) | |____________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -273 273 | -274 274 | for value in values: -275 275 | print(value) - 276 |+ return None -276 277 | +274 274 | +275 275 | for value in values: +276 276 | print(value) + 277 |+ return None 277 278 | -278 279 | def while_true(): +278 279 | +279 280 | def while_true(): -RET503.py:291:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:292:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -289 | return 1 -290 | case 1: -291 | print() # error +290 | return 1 +291 | case 1: +292 | print() # error | ^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -289 289 | return 1 -290 290 | case 1: -291 291 | print() # error - 292 |+ return None -292 293 | +290 290 | return 1 +291 291 | case 1: +292 292 | print() # error + 293 |+ return None 293 294 | -294 295 | def foo(baz: str) -> str: +294 295 | +295 296 | def foo(baz: str) -> str: -RET503.py:300:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:301:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -298 | def end_of_statement(): -299 | def example(): -300 | if True: +299 | def end_of_statement(): +300 | def example(): +301 | if True: | _________^ -301 | | return "" +302 | | return "" | |_____________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -299 299 | def example(): -300 300 | if True: -301 301 | return "" - 302 |+ return None -302 303 | +300 300 | def example(): +301 301 | if True: +302 302 | return "" + 303 |+ return None 303 304 | -304 305 | def example(): +304 305 | +305 306 | def example(): -RET503.py:305:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:306:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -304 | def example(): -305 | if True: +305 | def example(): +306 | if True: | _________^ -306 | | return "" +307 | | return "" | |_____________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -304 304 | def example(): -305 305 | if True: -306 306 | return "" - 307 |+ return None -307 308 | +305 305 | def example(): +306 306 | if True: +307 307 | return "" + 308 |+ return None 308 309 | -309 310 | def example(): +309 310 | +310 311 | def example(): -RET503.py:310:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:311:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -309 | def example(): -310 | if True: +310 | def example(): +311 | if True: | _________^ -311 | | return "" # type: ignore +312 | | return "" # type: ignore | |_____________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -309 309 | def example(): -310 310 | if True: -311 311 | return "" # type: ignore - 312 |+ return None -312 313 | +310 310 | def example(): +311 311 | if True: +312 312 | return "" # type: ignore + 313 |+ return None 313 314 | -314 315 | def example(): +314 315 | +315 316 | def example(): -RET503.py:315:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:316:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -314 | def example(): -315 | if True: +315 | def example(): +316 | if True: | _________^ -316 | | return "" ; +317 | | return "" ; | |_____________________^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -314 314 | def example(): -315 315 | if True: -316 316 | return "" ; - 317 |+ return None -317 318 | +315 315 | def example(): +316 316 | if True: +317 317 | return "" ; + 318 |+ return None 318 319 | -319 320 | def example(): +319 320 | +320 321 | def example(): -RET503.py:320:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:321:9: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -319 | def example(): -320 | if True: +320 | def example(): +321 | if True: | _________^ -321 | | return "" \ +322 | | return "" \ | |_____________________^ RET503 -322 | ; # type: ignore +323 | ; # type: ignore | = help: Add explicit `return` statement ℹ Unsafe fix -320 320 | if True: -321 321 | return "" \ -322 322 | ; # type: ignore - 323 |+ return None -323 324 | +321 321 | if True: +322 322 | return "" \ +323 323 | ; # type: ignore + 324 |+ return None 324 325 | -325 326 | def end_of_file(): +325 326 | +326 327 | def end_of_file(): -RET503.py:328:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:329:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -326 | if False: -327 | return 1 -328 | x = 2 \ +327 | if False: +328 | return 1 +329 | x = 2 \ | ^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -326 326 | if False: -327 327 | return 1 -328 328 | x = 2 \ - 329 |+ - 330 |+ return None +328 328 | return 1 +329 329 | x = 2 \ +330 330 | + 331 |+ return None +331 332 | +332 333 | +333 334 | # function return type annotation NoReturn + +RET503.py:341:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value + | +339 | if x == 5: +340 | return 5 +341 | bar() + | ^^^^^ RET503 + | + = help: Add explicit `return` statement + +ℹ Unsafe fix +339 339 | if x == 5: +340 340 | return 5 +341 341 | bar() + 342 |+ return None +342 343 | +343 344 | +344 345 | def foo(string: str) -> str: + +RET503.py:356:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value + | +354 | return "third" +355 | case _: +356 | raises(string) + | ^^^^^^^^^^^^^^ RET503 + | + = help: Add explicit `return` statement + +ℹ Unsafe fix +354 354 | return "third" +355 355 | case _: +356 356 | raises(string) + 357 |+ return None +357 358 | +358 359 | +359 360 | def foo() -> int: + +RET503.py:372:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value + | +370 | if baz() > 3: +371 | return 1 +372 | bar() + | ^^^^^ RET503 + | + = help: Add explicit `return` statement + +ℹ Unsafe fix +369 369 | +370 370 | if baz() > 3: +371 371 | return 1 +372 |- bar() + 372 |+ bar() + 373 |+ return None From 8d9780d10c6b29fd8b8f2d97b4eeb6ab7f6b4426 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 24 Jan 2024 12:14:27 -0500 Subject: [PATCH 2/2] Use a single func --- .../test/fixtures/flake8_return/RET503.py | 4 +- .../src/rules/flake8_return/rules/function.rs | 39 ++++++----- ...lake8_return__tests__RET503_RET503.py.snap | 66 +++++++++---------- 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py index acda343839726..60091c7eea102 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET503.py @@ -331,8 +331,6 @@ def end_of_file(): # function return type annotation NoReturn - - def foo(x: int) -> int: def bar() -> NoReturn: abort() @@ -369,4 +367,4 @@ def bar() -> NoReturn: if baz() > 3: return 1 - bar() \ No newline at end of file + bar() diff --git a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs index ecee19604f276..d7bbd7b107421 100644 --- a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs +++ b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs @@ -368,7 +368,24 @@ fn implicit_return_value(checker: &mut Checker, stack: &Stack) { } } -fn is_func_def_annotated_with_noreturn(func: &Expr, semantic: &SemanticModel) -> bool { +/// Return `true` if the `func` appears to be non-returning. +fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { + // First, look for known functions that never return from the standard library and popular + // libraries. + if semantic.resolve_call_path(func).is_some_and(|call_path| { + matches!( + call_path.as_slice(), + ["" | "builtins" | "sys" | "_thread" | "pytest", "exit"] + | ["" | "builtins", "quit"] + | ["os" | "posix", "_exit" | "abort"] + | ["_winapi", "ExitProcess"] + | ["pytest", "fail" | "skip" | "xfail"] + ) || semantic.match_typing_call_path(&call_path, "assert_never") + }) { + return true; + } + + // Second, look for `NoReturn` annotations on the return type. let Some(func_binding) = semantic.lookup_attribute(func) else { return false; }; @@ -381,31 +398,17 @@ fn is_func_def_annotated_with_noreturn(func: &Expr, semantic: &SemanticModel) -> return false; }; - let Some(return_expr) = returns.as_ref() else { + let Some(returns) = returns.as_ref() else { return false; }; - let Some(call_path) = semantic.resolve_call_path(return_expr) else { + let Some(call_path) = semantic.resolve_call_path(returns) else { return false; }; semantic.match_typing_call_path(&call_path, "NoReturn") } -/// Return `true` if the `func` is a known function that never returns. -fn is_noreturn_func(func: &Expr, semantic: &SemanticModel) -> bool { - semantic.resolve_call_path(func).is_some_and(|call_path| { - matches!( - call_path.as_slice(), - ["" | "builtins" | "sys" | "_thread" | "pytest", "exit"] - | ["" | "builtins", "quit"] - | ["os" | "posix", "_exit" | "abort"] - | ["_winapi", "ExitProcess"] - | ["pytest", "fail" | "skip" | "xfail"] - ) || semantic.match_typing_call_path(&call_path, "assert_never") - }) -} - /// RET503 fn implicit_return(checker: &mut Checker, stmt: &Stmt) { match stmt { @@ -479,7 +482,7 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) { if matches!( value.as_ref(), Expr::Call(ast::ExprCall { func, .. }) - if is_noreturn_func(func, checker.semantic()) || is_func_def_annotated_with_noreturn(func, checker.semantic()) + if is_noreturn_func(func, checker.semantic()) ) => {} _ => { let mut diagnostic = Diagnostic::new(ImplicitReturn, stmt.range()); diff --git a/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap b/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap index 9d72b3feea14d..27e3258087d4b 100644 --- a/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap +++ b/crates/ruff_linter/src/rules/flake8_return/snapshots/ruff_linter__rules__flake8_return__tests__RET503_RET503.py.snap @@ -402,57 +402,55 @@ 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:341:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +RET503.py:339:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value | -339 | if x == 5: -340 | return 5 -341 | bar() +337 | if x == 5: +338 | return 5 +339 | bar() | ^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -339 339 | if x == 5: -340 340 | return 5 -341 341 | bar() - 342 |+ return None -342 343 | -343 344 | -344 345 | def foo(string: str) -> str: - -RET503.py:356:13: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +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 | -354 | return "third" -355 | case _: -356 | raises(string) +352 | return "third" +353 | case _: +354 | raises(string) | ^^^^^^^^^^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -354 354 | return "third" -355 355 | case _: -356 356 | raises(string) - 357 |+ return None -357 358 | -358 359 | -359 360 | def foo() -> int: - -RET503.py:372:5: RET503 [*] Missing explicit `return` at the end of function able to return non-`None` value +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 | -370 | if baz() > 3: -371 | return 1 -372 | bar() +368 | if baz() > 3: +369 | return 1 +370 | bar() | ^^^^^ RET503 | = help: Add explicit `return` statement ℹ Unsafe fix -369 369 | -370 370 | if baz() > 3: -371 371 | return 1 -372 |- bar() - 372 |+ bar() - 373 |+ return None +368 368 | if baz() > 3: +369 369 | return 1 +370 370 | bar() + 371 |+ return None