Skip to content

Commit 1ce6bab

Browse files
committed
Merge branch 'main' into dcreager/legacy-class
* main: [red-knot] Refactor: no mutability in call APIs (#17788) [red-knot] Fix panic for `tuple[x[y]]` string annotation (#17787) [red-knot] Implicit instance attributes in generic methods (#17769) doc: Add link to `check-typed-exception` from `S110` and `S112` (#17786) Fix module name in ASYNC110, 115, and 116 fixes (#17774) [red-knot] More informative hover-types for assignments (#17762) [syntax-errors] Use consistent message for bad starred expression usage. (#17772) red_knot_server: add auto-completion MVP Allow passing a virtual environment to `ruff analyze graph` (#17743) Bump 0.11.8 (#17766) [`flake8-use-pathlib`] Fix `PTH104`false positive when `rename` is passed a file descriptor (#17712)
2 parents 2c6693a + ea3f4ac commit 1ce6bab

File tree

52 files changed

+847
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+847
-323
lines changed

CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
# Changelog
22

3+
## 0.11.8
4+
5+
### Preview features
6+
7+
- \[`airflow`\] Apply auto fixes to cases where the names have changed in Airflow 3 (`AIR302`, `AIR311`) ([#17553](https://github.com/astral-sh/ruff/pull/17553), [#17570](https://github.com/astral-sh/ruff/pull/17570), [#17571](https://github.com/astral-sh/ruff/pull/17571))
8+
- \[`airflow`\] Extend `AIR301` rule ([#17598](https://github.com/astral-sh/ruff/pull/17598))
9+
- \[`airflow`\] Update existing `AIR302` rules with better suggestions ([#17542](https://github.com/astral-sh/ruff/pull/17542))
10+
- \[`refurb`\] Mark fix as safe for `readlines-in-for` (`FURB129`) ([#17644](https://github.com/astral-sh/ruff/pull/17644))
11+
- [syntax-errors] `nonlocal` declaration at module level ([#17559](https://github.com/astral-sh/ruff/pull/17559))
12+
- [syntax-errors] Detect single starred expression assignment `x = *y` ([#17624](https://github.com/astral-sh/ruff/pull/17624))
13+
14+
### Bug fixes
15+
16+
- \[`flake8-pyi`\] Ensure `Literal[None,] | Literal[None,]` is not autofixed to `None | None` (`PYI061`) ([#17659](https://github.com/astral-sh/ruff/pull/17659))
17+
- \[`flake8-use-pathlib`\] Avoid suggesting `Path.iterdir()` for `os.listdir` with file descriptor (`PTH208`) ([#17715](https://github.com/astral-sh/ruff/pull/17715))
18+
- \[`flake8-use-pathlib`\] Fix `PTH104` false positive when `rename` is passed a file descriptor ([#17712](https://github.com/astral-sh/ruff/pull/17712))
19+
- \[`flake8-use-pathlib`\] Fix `PTH116` false positive when `stat` is passed a file descriptor ([#17709](https://github.com/astral-sh/ruff/pull/17709))
20+
- \[`flake8-use-pathlib`\] Fix `PTH123` false positive when `open` is passed a file descriptor from a function call ([#17705](https://github.com/astral-sh/ruff/pull/17705))
21+
- \[`pycodestyle`\] Fix duplicated diagnostic in `E712` ([#17651](https://github.com/astral-sh/ruff/pull/17651))
22+
- \[`pylint`\] Detect `global` declarations in module scope (`PLE0118`) ([#17411](https://github.com/astral-sh/ruff/pull/17411))
23+
- [syntax-errors] Make `async-comprehension-in-sync-comprehension` more specific ([#17460](https://github.com/astral-sh/ruff/pull/17460))
24+
25+
### Configuration
26+
27+
- Add option to disable `typing_extensions` imports ([#17611](https://github.com/astral-sh/ruff/pull/17611))
28+
29+
### Documentation
30+
31+
- Fix example syntax for the `lint.pydocstyle.ignore-var-parameters` option ([#17740](https://github.com/astral-sh/ruff/pull/17740))
32+
- Add fix safety sections (`ASYNC116`, `FLY002`, `D200`, `RUF005`, `RUF017`, `RUF027`, `RUF028`, `RUF057`) ([#17497](https://github.com/astral-sh/ruff/pull/17497), [#17496](https://github.com/astral-sh/ruff/pull/17496), [#17502](https://github.com/astral-sh/ruff/pull/17502), [#17484](https://github.com/astral-sh/ruff/pull/17484), [#17480](https://github.com/astral-sh/ruff/pull/17480), [#17485](https://github.com/astral-sh/ruff/pull/17485), [#17722](https://github.com/astral-sh/ruff/pull/17722), [#17483](https://github.com/astral-sh/ruff/pull/17483))
33+
34+
### Other changes
35+
36+
- Add Python 3.14 to configuration options ([#17647](https://github.com/astral-sh/ruff/pull/17647))
37+
- Make syntax error for unparenthesized except tuples version specific to before 3.14 ([#17660](https://github.com/astral-sh/ruff/pull/17660))
38+
339
## 0.11.7
440

541
### Preview features

Cargo.lock

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

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
149149
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
150150

151151
# For a specific version.
152-
curl -LsSf https://astral.sh/ruff/0.11.7/install.sh | sh
153-
powershell -c "irm https://astral.sh/ruff/0.11.7/install.ps1 | iex"
152+
curl -LsSf https://astral.sh/ruff/0.11.8/install.sh | sh
153+
powershell -c "irm https://astral.sh/ruff/0.11.8/install.ps1 | iex"
154154
```
155155

156156
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -183,7 +183,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
183183
```yaml
184184
- repo: https://github.com/astral-sh/ruff-pre-commit
185185
# Ruff version.
186-
rev: v0.11.7
186+
rev: v0.11.8
187187
hooks:
188188
# Run the linter.
189189
- id: ruff

crates/red_knot_ide/src/completion.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use ruff_db::files::File;
2+
use ruff_db::parsed::parsed_module;
3+
use ruff_python_ast::visitor::source_order::SourceOrderVisitor;
4+
use ruff_python_ast::{AnyNodeRef, Identifier};
5+
use ruff_text_size::TextSize;
6+
7+
use crate::Db;
8+
9+
pub struct Completion {
10+
pub label: String,
11+
}
12+
13+
pub fn completion(db: &dyn Db, file: File, _offset: TextSize) -> Vec<Completion> {
14+
let parsed = parsed_module(db.upcast(), file);
15+
identifiers(parsed.syntax().into())
16+
.into_iter()
17+
.map(|label| Completion { label })
18+
.collect()
19+
}
20+
21+
fn identifiers(node: AnyNodeRef) -> Vec<String> {
22+
struct Visitor {
23+
identifiers: Vec<String>,
24+
}
25+
26+
impl<'a> SourceOrderVisitor<'a> for Visitor {
27+
fn visit_identifier(&mut self, id: &'a Identifier) {
28+
self.identifiers.push(id.id.as_str().to_string());
29+
}
30+
}
31+
32+
let mut visitor = Visitor {
33+
identifiers: vec![],
34+
};
35+
node.visit_source_order(&mut visitor);
36+
visitor.identifiers.sort();
37+
visitor.identifiers.dedup();
38+
visitor.identifiers
39+
}

crates/red_knot_ide/src/hover.rs

Lines changed: 183 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,20 +444,141 @@ mod tests {
444444
}
445445

446446
#[test]
447-
fn hover_class_member_declaration() {
447+
fn hover_variable_assignment() {
448+
let test = cursor_test(
449+
r#"
450+
value<CURSOR> = 1
451+
"#,
452+
);
453+
454+
assert_snapshot!(test.hover(), @r"
455+
Literal[1]
456+
---------------------------------------------
457+
```text
458+
Literal[1]
459+
```
460+
---------------------------------------------
461+
info: lint:hover: Hovered content is
462+
--> main.py:2:13
463+
|
464+
2 | value = 1
465+
| ^^^^^- Cursor offset
466+
| |
467+
| source
468+
|
469+
");
470+
}
471+
472+
#[test]
473+
fn hover_augmented_assignment() {
474+
let test = cursor_test(
475+
r#"
476+
value = 1
477+
value<CURSOR> += 2
478+
"#,
479+
);
480+
481+
// We currently show the *previous* value of the variable (1), not the new one (3).
482+
// Showing the new value might be more intuitive for some users, but the actual 'use'
483+
// of the `value` symbol here in read-context is `1`. This comment mainly exists to
484+
// signal that it might be okay to revisit this in the future and reveal 3 instead.
485+
assert_snapshot!(test.hover(), @r"
486+
Literal[1]
487+
---------------------------------------------
488+
```text
489+
Literal[1]
490+
```
491+
---------------------------------------------
492+
info: lint:hover: Hovered content is
493+
--> main.py:3:13
494+
|
495+
2 | value = 1
496+
3 | value += 2
497+
| ^^^^^- Cursor offset
498+
| |
499+
| source
500+
|
501+
");
502+
}
503+
504+
#[test]
505+
fn hover_attribute_assignment() {
506+
let test = cursor_test(
507+
r#"
508+
class C:
509+
attr: int = 1
510+
511+
C.attr<CURSOR> = 2
512+
"#,
513+
);
514+
515+
assert_snapshot!(test.hover(), @r"
516+
Literal[2]
517+
---------------------------------------------
518+
```text
519+
Literal[2]
520+
```
521+
---------------------------------------------
522+
info: lint:hover: Hovered content is
523+
--> main.py:5:13
524+
|
525+
3 | attr: int = 1
526+
4 |
527+
5 | C.attr = 2
528+
| ^^^^^^- Cursor offset
529+
| |
530+
| source
531+
|
532+
");
533+
}
534+
535+
#[test]
536+
fn hover_augmented_attribute_assignment() {
537+
let test = cursor_test(
538+
r#"
539+
class C:
540+
attr = 1
541+
542+
C.attr<CURSOR> += 2
543+
"#,
544+
);
545+
546+
// See the comment in the `hover_augmented_assignment` test above. The same
547+
// reasoning applies here.
548+
assert_snapshot!(test.hover(), @r"
549+
Unknown | Literal[1]
550+
---------------------------------------------
551+
```text
552+
Unknown | Literal[1]
553+
```
554+
---------------------------------------------
555+
info: lint:hover: Hovered content is
556+
--> main.py:5:13
557+
|
558+
3 | attr = 1
559+
4 |
560+
5 | C.attr += 2
561+
| ^^^^^^- Cursor offset
562+
| |
563+
| source
564+
|
565+
");
566+
}
567+
568+
#[test]
569+
fn hover_annotated_assignment() {
448570
let test = cursor_test(
449571
r#"
450572
class Foo:
451573
a<CURSOR>: int
452574
"#,
453575
);
454576

455-
// TODO: This should be int and not `Never`, https://github.com/astral-sh/ruff/issues/17122
456577
assert_snapshot!(test.hover(), @r"
457-
Never
578+
int
458579
---------------------------------------------
459580
```text
460-
Never
581+
int
461582
```
462583
---------------------------------------------
463584
info: lint:hover: Hovered content is
@@ -472,6 +593,64 @@ mod tests {
472593
");
473594
}
474595

596+
#[test]
597+
fn hover_annotated_assignment_with_rhs() {
598+
let test = cursor_test(
599+
r#"
600+
class Foo:
601+
a<CURSOR>: int = 1
602+
"#,
603+
);
604+
605+
assert_snapshot!(test.hover(), @r"
606+
Literal[1]
607+
---------------------------------------------
608+
```text
609+
Literal[1]
610+
```
611+
---------------------------------------------
612+
info: lint:hover: Hovered content is
613+
--> main.py:3:13
614+
|
615+
2 | class Foo:
616+
3 | a: int = 1
617+
| ^- Cursor offset
618+
| |
619+
| source
620+
|
621+
");
622+
}
623+
624+
#[test]
625+
fn hover_annotated_attribute_assignment() {
626+
let test = cursor_test(
627+
r#"
628+
class Foo:
629+
def __init__(self, a: int):
630+
self.a<CURSOR>: int = a
631+
"#,
632+
);
633+
634+
assert_snapshot!(test.hover(), @r"
635+
int
636+
---------------------------------------------
637+
```text
638+
int
639+
```
640+
---------------------------------------------
641+
info: lint:hover: Hovered content is
642+
--> main.py:4:17
643+
|
644+
2 | class Foo:
645+
3 | def __init__(self, a: int):
646+
4 | self.a: int = a
647+
| ^^^^^^- Cursor offset
648+
| |
649+
| source
650+
|
651+
");
652+
}
653+
475654
#[test]
476655
fn hover_type_narrowing() {
477656
let test = cursor_test(

crates/red_knot_ide/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
mod completion;
12
mod db;
23
mod find_node;
34
mod goto;
45
mod hover;
56
mod inlay_hints;
67
mod markup;
78

9+
pub use completion::completion;
810
pub use db::Db;
911
pub use goto::goto_type_definition;
1012
pub use hover::hover;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Regression test for an issue that came up while working
2+
# on https://github.com/astral-sh/ruff/pull/17769
3+
4+
class C:
5+
def method[T](self, x: T) -> T:
6+
def inner():
7+
self.attr = 1
8+
9+
C().attr
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
t: "tuple[list[int]]"

crates/red_knot_python_semantic/resources/mdtest/attributes.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,27 @@ reveal_type(C.a_type) # revealed: type
19501950
reveal_type(C.a_none) # revealed: None
19511951
```
19521952

1953+
### Generic methods
1954+
1955+
We also detect implicit instance attributes on methods that are themselves generic. We have an extra
1956+
test for this because generic functions have an extra type-params scope in between the function body
1957+
scope and the outer scope, so we need to make sure that our implementation can still recognize `f`
1958+
as a method of `C` here:
1959+
1960+
```toml
1961+
[environment]
1962+
python-version = "3.12"
1963+
```
1964+
1965+
```py
1966+
class C:
1967+
def f[T](self, t: T) -> T:
1968+
self.x: int = 1
1969+
return t
1970+
1971+
reveal_type(C().x) # revealed: int
1972+
```
1973+
19531974
## Enum classes
19541975

19551976
Enums are not supported yet; attribute access on an enum class is inferred as `Todo`.

0 commit comments

Comments
 (0)