Skip to content

Commit a303b7a

Browse files
committed
Merge remote-tracking branch 'origin/main' into dcreager/callable-return
* origin/main: new module for parsing ranged suppressions (#21441) [ty] `type[T]` is assignable to an inferable typevar (#21766) Fix syntax error false positives for `await` outside functions (#21763) [ty] Improve diagnostics for unsupported comparison operations (#21737)
2 parents 3045258 + abaa49f commit a303b7a

33 files changed

+2639
-261
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff_linter/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ anyhow = { workspace = true }
3535
bitflags = { workspace = true }
3636
clap = { workspace = true, features = ["derive", "string"], optional = true }
3737
colored = { workspace = true }
38+
compact_str = { workspace = true }
3839
fern = { workspace = true }
3940
glob = { workspace = true }
4041
globset = { workspace = true }

crates/ruff_linter/resources/test/fixtures/pyflakes/F704.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,24 @@ def _():
1717

1818
# Valid yield scope
1919
yield 3
20+
21+
22+
# await is valid in any generator, sync or async
23+
(await cor async for cor in f()) # ok
24+
(await cor for cor in f()) # ok
25+
26+
# but not in comprehensions
27+
[await cor async for cor in f()] # F704
28+
{await cor async for cor in f()} # F704
29+
{await cor: 1 async for cor in f()} # F704
30+
[await cor for cor in f()] # F704
31+
{await cor for cor in f()} # F704
32+
{await cor: 1 for cor in f()} # F704
33+
34+
# or in the iterator of an async generator, which is evaluated in the parent
35+
# scope
36+
(cor async for cor in await f()) # F704
37+
(await cor async for cor in [await c for c in f()]) # F704
38+
39+
# this is also okay because the comprehension is within the generator scope
40+
([await c for c in cor] async for cor in f()) # ok

crates/ruff_linter/resources/test/fixtures/syntax_errors/await_outside_async_function.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ def func():
33

44
# Top-level await
55
await 1
6+
7+
([await c for c in cor] async for cor in func()) # ok

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,10 @@ impl SemanticSyntaxContext for Checker<'_> {
780780
match scope.kind {
781781
ScopeKind::Class(_) => return false,
782782
ScopeKind::Function(_) | ScopeKind::Lambda(_) => return true,
783+
ScopeKind::Generator {
784+
kind: GeneratorKind::Generator,
785+
..
786+
} => return true,
783787
ScopeKind::Generator { .. }
784788
| ScopeKind::Module
785789
| ScopeKind::Type
@@ -829,14 +833,19 @@ impl SemanticSyntaxContext for Checker<'_> {
829833
self.source_type.is_ipynb()
830834
}
831835

832-
fn in_generator_scope(&self) -> bool {
833-
matches!(
834-
&self.semantic.current_scope().kind,
835-
ScopeKind::Generator {
836-
kind: GeneratorKind::Generator,
837-
..
836+
fn in_generator_context(&self) -> bool {
837+
for scope in self.semantic.current_scopes() {
838+
if matches!(
839+
scope.kind,
840+
ScopeKind::Generator {
841+
kind: GeneratorKind::Generator,
842+
..
843+
}
844+
) {
845+
return true;
838846
}
839-
)
847+
}
848+
false
840849
}
841850

842851
fn in_loop_context(&self) -> bool {

crates/ruff_linter/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub mod rule_selector;
4646
pub mod rules;
4747
pub mod settings;
4848
pub mod source_kind;
49+
pub mod suppression;
4950
mod text_helpers;
5051
pub mod upstream_categories;
5152
mod violation;

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F704_F704.py.snap

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,88 @@ F704 `await` statement outside of a function
3737
12 |
3838
13 | def _():
3939
|
40+
41+
F704 `await` statement outside of a function
42+
--> F704.py:27:2
43+
|
44+
26 | # but not in comprehensions
45+
27 | [await cor async for cor in f()] # F704
46+
| ^^^^^^^^^
47+
28 | {await cor async for cor in f()} # F704
48+
29 | {await cor: 1 async for cor in f()} # F704
49+
|
50+
51+
F704 `await` statement outside of a function
52+
--> F704.py:28:2
53+
|
54+
26 | # but not in comprehensions
55+
27 | [await cor async for cor in f()] # F704
56+
28 | {await cor async for cor in f()} # F704
57+
| ^^^^^^^^^
58+
29 | {await cor: 1 async for cor in f()} # F704
59+
30 | [await cor for cor in f()] # F704
60+
|
61+
62+
F704 `await` statement outside of a function
63+
--> F704.py:29:2
64+
|
65+
27 | [await cor async for cor in f()] # F704
66+
28 | {await cor async for cor in f()} # F704
67+
29 | {await cor: 1 async for cor in f()} # F704
68+
| ^^^^^^^^^
69+
30 | [await cor for cor in f()] # F704
70+
31 | {await cor for cor in f()} # F704
71+
|
72+
73+
F704 `await` statement outside of a function
74+
--> F704.py:30:2
75+
|
76+
28 | {await cor async for cor in f()} # F704
77+
29 | {await cor: 1 async for cor in f()} # F704
78+
30 | [await cor for cor in f()] # F704
79+
| ^^^^^^^^^
80+
31 | {await cor for cor in f()} # F704
81+
32 | {await cor: 1 for cor in f()} # F704
82+
|
83+
84+
F704 `await` statement outside of a function
85+
--> F704.py:31:2
86+
|
87+
29 | {await cor: 1 async for cor in f()} # F704
88+
30 | [await cor for cor in f()] # F704
89+
31 | {await cor for cor in f()} # F704
90+
| ^^^^^^^^^
91+
32 | {await cor: 1 for cor in f()} # F704
92+
|
93+
94+
F704 `await` statement outside of a function
95+
--> F704.py:32:2
96+
|
97+
30 | [await cor for cor in f()] # F704
98+
31 | {await cor for cor in f()} # F704
99+
32 | {await cor: 1 for cor in f()} # F704
100+
| ^^^^^^^^^
101+
33 |
102+
34 | # or in the iterator of an async generator, which is evaluated in the parent
103+
|
104+
105+
F704 `await` statement outside of a function
106+
--> F704.py:36:23
107+
|
108+
34 | # or in the iterator of an async generator, which is evaluated in the parent
109+
35 | # scope
110+
36 | (cor async for cor in await f()) # F704
111+
| ^^^^^^^^^
112+
37 | (await cor async for cor in [await c for c in f()]) # F704
113+
|
114+
115+
F704 `await` statement outside of a function
116+
--> F704.py:37:30
117+
|
118+
35 | # scope
119+
36 | (cor async for cor in await f()) # F704
120+
37 | (await cor async for cor in [await c for c in f()]) # F704
121+
| ^^^^^^^
122+
38 |
123+
39 | # this is also okay because the comprehension is within the generator scope
124+
|

crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__await_outside_async_function.py.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ PLE1142 `await` should be used within an async function
1717
4 | # Top-level await
1818
5 | await 1
1919
| ^^^^^^^
20+
6 |
21+
7 | ([await c for c in cor] async for cor in func()) # ok
2022
|

0 commit comments

Comments
 (0)