Skip to content

Commit c31cdb0

Browse files
committed
[syntax-errors] return outside function
Summary -- This PR reimplements [return-outside-function (F706)](https://docs.astral.sh/ruff/rules/return-outside-function/) as a semantic syntax error. These changes are very similar to those in #17298, which this is currently stacked on. Test Plan -- Simple new inline tests, as well as updates to existing tests in the second commit.
1 parent 5e9ec10 commit c31cdb0

File tree

8 files changed

+116
-21
lines changed

8 files changed

+116
-21
lines changed

crates/ruff_linter/src/checkers/ast/analyze/statement.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
380380
}
381381
}
382382
Stmt::Return(_) => {
383-
if checker.enabled(Rule::ReturnOutsideFunction) {
384-
pyflakes::rules::return_outside_function(checker, stmt);
385-
}
386383
if checker.enabled(Rule::ReturnInInit) {
387384
pylint::rules::return_in_init(checker, stmt);
388385
}

crates/ruff_linter/src/checkers/ast/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ use crate::importer::Importer;
6767
use crate::noqa::NoqaMapping;
6868
use crate::package::PackageRoot;
6969
use crate::registry::Rule;
70-
use crate::rules::pyflakes::rules::{DeferralKeyword, LateFutureImport, YieldOutsideFunction};
70+
use crate::rules::pyflakes::rules::{
71+
DeferralKeyword, LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
72+
};
7173
use crate::rules::pylint::rules::LoadBeforeGlobalDeclaration;
7274
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
7375
use crate::settings::{flags, LinterSettings};
@@ -586,6 +588,11 @@ impl SemanticSyntaxContext for Checker<'_> {
586588
));
587589
}
588590
}
591+
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
592+
if self.settings.rules.enabled(Rule::ReturnOutsideFunction) {
593+
self.report_diagnostic(Diagnostic::new(ReturnOutsideFunction, error.range));
594+
}
595+
}
589596
SemanticSyntaxErrorKind::ReboundComprehensionVariable
590597
| SemanticSyntaxErrorKind::DuplicateTypeParameter
591598
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)

crates/ruff_linter/src/rules/pyflakes/rules/return_outside_function.rs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
use ruff_python_ast::Stmt;
2-
3-
use ruff_diagnostics::{Diagnostic, Violation};
1+
use ruff_diagnostics::Violation;
42
use ruff_macros::{derive_message_formats, ViolationMetadata};
5-
use ruff_python_semantic::ScopeKind;
6-
use ruff_text_size::Ranged;
7-
8-
use crate::checkers::ast::Checker;
93

104
/// ## What it does
115
/// Checks for `return` statements outside of functions.
@@ -31,12 +25,3 @@ impl Violation for ReturnOutsideFunction {
3125
"`return` statement outside of a function/method".to_string()
3226
}
3327
}
34-
35-
pub(crate) fn return_outside_function(checker: &Checker, stmt: &Stmt) {
36-
if matches!(
37-
checker.semantic().current_scope().kind,
38-
ScopeKind::Class(_) | ScopeKind::Module
39-
) {
40-
checker.report_diagnostic(Diagnostic::new(ReturnOutsideFunction, stmt.range()));
41-
}
42-
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
return 1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def f(): return 1

crates/ruff_python_parser/src/semantic_errors.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,20 @@ impl SemanticSyntaxChecker {
119119
}
120120
}
121121
Stmt::Return(ast::StmtReturn {
122-
value: Some(value), ..
122+
value: Some(value),
123+
range,
123124
}) => {
124125
// test_err single_star_return
125126
// def f(): return *x
126127
Self::invalid_star_expression(value, ctx);
128+
if !self.in_function_scope {
129+
// test_ok return_inside_function
130+
// def f(): return 1
131+
132+
// test_err return_outside_function
133+
// return 1
134+
Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range);
135+
}
127136
}
128137
Stmt::For(ast::StmtFor { target, iter, .. }) => {
129138
// test_err single_star_for
@@ -877,6 +886,9 @@ impl Display for SemanticSyntaxError {
877886
SemanticSyntaxErrorKind::YieldOutsideFunction(kind) => {
878887
write!(f, "`{kind}` statement outside of a function")
879888
}
889+
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
890+
f.write_str("`return` statement outside of a function")
891+
}
880892
}
881893
}
882894
}
@@ -1098,6 +1110,9 @@ pub enum SemanticSyntaxErrorKind {
10981110

10991111
/// Represents the use of `yield`, `yield from`, or `await` outside of a function scope.
11001112
YieldOutsideFunction(YieldOutsideFunctionKind),
1113+
1114+
/// Represents the use of `return` outside of a function scope.
1115+
ReturnOutsideFunction,
11011116
}
11021117

11031118
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/err/return_outside_function.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
range: 0..9,
11+
body: [
12+
Return(
13+
StmtReturn {
14+
range: 0..8,
15+
value: Some(
16+
NumberLiteral(
17+
ExprNumberLiteral {
18+
range: 7..8,
19+
value: Int(
20+
1,
21+
),
22+
},
23+
),
24+
),
25+
},
26+
),
27+
],
28+
},
29+
)
30+
```
31+
## Semantic Syntax Errors
32+
33+
|
34+
1 | return 1
35+
| ^^^^^^^^ Syntax Error: `return` statement outside of a function
36+
|
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/ok/return_inside_function.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
range: 0..18,
11+
body: [
12+
FunctionDef(
13+
StmtFunctionDef {
14+
range: 0..17,
15+
is_async: false,
16+
decorator_list: [],
17+
name: Identifier {
18+
id: Name("f"),
19+
range: 4..5,
20+
},
21+
type_params: None,
22+
parameters: Parameters {
23+
range: 5..7,
24+
posonlyargs: [],
25+
args: [],
26+
vararg: None,
27+
kwonlyargs: [],
28+
kwarg: None,
29+
},
30+
returns: None,
31+
body: [
32+
Return(
33+
StmtReturn {
34+
range: 9..17,
35+
value: Some(
36+
NumberLiteral(
37+
ExprNumberLiteral {
38+
range: 16..17,
39+
value: Int(
40+
1,
41+
),
42+
},
43+
),
44+
),
45+
},
46+
),
47+
],
48+
},
49+
),
50+
],
51+
},
52+
)
53+
```

0 commit comments

Comments
 (0)