Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions crates/ruff/tests/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5588,15 +5588,15 @@ fn cookiecutter_globbing() -> Result<()> {
.args(STDIN_BASE_OPTIONS)
.arg("--select=F811")
.current_dir(tempdir.path()), @r"
success: false
exit_code: 1
----- stdout -----
{{cookiecutter.repo_name}}/tests/maintest.py:3:8: F811 [*] Redefinition of unused `foo` from line 1
Found 1 error.
[*] 1 fixable with the `--fix` option.
success: false
exit_code: 1
----- stdout -----
{{cookiecutter.repo_name}}/tests/maintest.py:3:8: F811 [*] Redefinition of unused `foo` from line 1: `foo` redefined here
Found 1 error.
[*] 1 fixable with the `--fix` option.

----- stderr -----
");
----- stderr -----
");
});

Ok(())
Expand Down
15 changes: 15 additions & 0 deletions crates/ruff_db/src/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ impl Diagnostic {
.find(|ann| ann.is_primary)
}

/// Returns a mutable borrow of all annotations of this diagnostic.
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation> {
Arc::make_mut(&mut self.inner).annotations.iter_mut()
}

/// Returns the "primary" span of this diagnostic if one exists.
///
/// When there are multiple primary spans, then the first one that was
Expand Down Expand Up @@ -310,6 +315,11 @@ impl Diagnostic {
&self.inner.subs
}

/// Returns a mutable borrow of the sub-diagnostics of this diagnostic.
pub fn sub_diagnostics_mut(&mut self) -> impl Iterator<Item = &mut SubDiagnostic> {
Arc::make_mut(&mut self.inner).subs.iter_mut()
}

/// Returns the fix for this diagnostic if it exists.
pub fn fix(&self) -> Option<&Fix> {
self.inner.fix.as_ref()
Expand Down Expand Up @@ -621,6 +631,11 @@ impl SubDiagnostic {
&self.inner.annotations
}

/// Returns a mutable borrow of the annotations of this sub-diagnostic.
pub fn annotations_mut(&mut self) -> impl Iterator<Item = &mut Annotation> {
self.inner.annotations.iter_mut()
}

/// Returns a shared borrow of the "primary" annotation of this diagnostic
/// if one exists.
///
Expand Down
7 changes: 6 additions & 1 deletion crates/ruff_db/src/diagnostic/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,12 @@ impl<'a> ResolvedDiagnostic<'a> {
.annotations
.iter()
.filter_map(|ann| {
let path = ann.span.file.path(resolver);
let path = ann
.span
.file
.relative_path(resolver)
.to_str()
.unwrap_or_else(|| ann.span.file.path(resolver));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what is the motivation for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to match the path in from_diagnostic above. This isn't used in this PR now that we're using a secondary annotation, so I can revert it!

I'll double-check if it's actually needed for full diagnostics too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using path instead of relative_path changes some of our CLI snapshots for the main diagnostic, so I think we'll need this for sub-diagnostics too, once we use any sub-diagnostics with paths in Ruff.

    ────────────────────────────────────────────────────────────────────────────────
    -old snapshot
    +new results
    ────────────┬───────────────────────────────────────────────────────────────────
        0     0 │ success: false
        1     1 │ exit_code: 1
        2     2 │ ----- stdout -----
        3     3 │ F401 [*] `bar` imported but unused
        4       │- --> bar.py:2:8
              4 │+ --> /tmp/.tmpJqw1Qi/bar.py:2:8
        5     5 │   |
        6     6 │ 2 | import bar   # unused import
        7     7 │   |        ^^^
        8     8 │   |
        9     9 │ help: Remove unused import: `bar`
       10    10 │ 
       11    11 │ F401 [*] `foo` imported but unused
       12       │- --> foo.py:2:8
             12 │+ --> /tmp/.tmpJqw1Qi/foo.py:2:8
       13    13 │   |
       14    14 │ 2 | import foo   # unused import
       15    15 │   |        ^^^
       16    16 │   |
    ────────────┴───────────────────────────────────────────────────────────────────

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I think I'll go ahead and leave this instead of reverting, since it seems like it will be useful in the future)

let diagnostic_source = ann.span.file.diagnostic_source(resolver);
ResolvedAnnotation::new(path, &diagnostic_source, ann, resolver)
})
Expand Down
13 changes: 12 additions & 1 deletion crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use itertools::Itertools;
use log::debug;
use rustc_hash::{FxHashMap, FxHashSet};

use ruff_db::diagnostic::Diagnostic;
use ruff_db::diagnostic::{Annotation, Diagnostic, IntoDiagnosticMessage, Span};
use ruff_diagnostics::{Applicability, Fix, IsolationLevel};
use ruff_notebook::{CellOffsets, NotebookIndex};
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
Expand Down Expand Up @@ -3305,6 +3305,17 @@ impl DiagnosticGuard<'_, '_> {
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
}
}

/// Add a secondary annotation with the given message and range.
pub(crate) fn secondary_annotation<'a>(
&mut self,
message: impl IntoDiagnosticMessage + 'a,
range: impl Ranged,
) {
let span = Span::from(self.context.source_file.clone()).with_range(range.range());
let ann = Annotation::secondary(span).message(message);
self.diagnostic.as_mut().unwrap().annotate(ann);
}
}

impl std::ops::Deref for DiagnosticGuard<'_, '_> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,24 @@ pub(crate) fn redefined_while_unused(checker: &Checker, scope_id: ScopeId, scope
// Create diagnostics for each statement.
for (source, entries) in &redefinitions {
for (shadowed, binding) in entries {
let name = binding.name(checker.source());
let mut diagnostic = checker.report_diagnostic(
RedefinedWhileUnused {
name: binding.name(checker.source()).to_string(),
name: name.to_string(),
row: checker.compute_source_row(shadowed.start()),
},
binding.range(),
);

diagnostic.secondary_annotation(
format_args!("previous definition of `{name}` here"),
shadowed,
);

if let Some(ann) = diagnostic.primary_annotation_mut() {
ann.set_message(format_args!("`{name}` redefined here"));
}

if let Some(range) = binding.parent_range(checker.semantic()) {
diagnostic.set_parent(range.start());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ F811 Redefinition of unused `bar` from line 6
--> F811_0.py:10:5
|
10 | def bar():
| ^^^
| ^^^ `bar` redefined here
11 | pass
|
::: F811_0.py:6:5
|
5 | @foo
6 | def bar():
| --- previous definition of `bar` here
7 | pass
|
help: Remove definition: `bar`
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `FU` from line 1
--> F811_1.py:1:25
--> F811_1.py:1:14
|
1 | import fu as FU, bar as FU
| ^^
| -- ^^ `FU` redefined here
| |
| previous definition of `FU` here
|
help: Remove definition: `FU`
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `mixer` from line 2
--> F811_12.py:6:20
--> F811_12.py:2:20
|
1 | try:
2 | from aa import mixer
| ----- previous definition of `mixer` here
3 | except ImportError:
4 | pass
5 | else:
6 | from bb import mixer
| ^^^^^
| ^^^^^ `mixer` redefined here
7 | mixer(123)
|
help: Remove definition: `mixer`
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ F811 Redefinition of unused `fu` from line 1
--> F811_15.py:4:5
|
4 | def fu():
| ^^
| ^^ `fu` redefined here
5 | pass
|
::: F811_15.py:1:8
|
1 | import fu
| -- previous definition of `fu` here
|
help: Remove definition: `fu`
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ F811 Redefinition of unused `fu` from line 3
6 | def bar():
7 | def baz():
8 | def fu():
| ^^
| ^^ `fu` redefined here
9 | pass
|
::: F811_16.py:3:8
|
1 | """Test that shadowing a global with a nested function generates a warning."""
2 |
3 | import fu
| -- previous definition of `fu` here
|
help: Remove definition: `fu`
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ F811 [*] Redefinition of unused `fu` from line 2
|
5 | def bar():
6 | import fu
| ^^
| ^^ `fu` redefined here
7 |
8 | def baz():
|
::: F811_17.py:2:8
|
1 | """Test that shadowing a global name with a nested function generates a warning."""
2 | import fu
| -- previous definition of `fu` here
|
help: Remove definition: `fu`

ℹ Safe fix
Expand All @@ -22,11 +28,15 @@ help: Remove definition: `fu`
9 8 | def fu():

F811 Redefinition of unused `fu` from line 6
--> F811_17.py:9:13
--> F811_17.py:6:12
|
5 | def bar():
6 | import fu
| -- previous definition of `fu` here
7 |
8 | def baz():
9 | def fu():
| ^^
| ^^ `fu` redefined here
10 | pass
|
help: Remove definition: `fu`
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `FU` from line 1
--> F811_2.py:1:34
--> F811_2.py:1:23
|
1 | from moo import fu as FU, bar as FU
| ^^
| -- ^^ `FU` redefined here
| |
| previous definition of `FU` here
|
help: Remove definition: `FU`
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ F811 [*] Redefinition of unused `Sequence` from line 26
30 | from typing import (
31 | List, # noqa: F811
32 | Sequence,
| ^^^^^^^^
| ^^^^^^^^ `Sequence` redefined here
33 | )
|
::: F811_21.py:26:5
|
24 | from typing import (
25 | List, # noqa
26 | Sequence, # noqa
| -------- previous definition of `Sequence` here
27 | )
|
help: Remove definition: `Sequence`

ℹ Safe fix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `foo` from line 3
--> F811_23.py:4:15
--> F811_23.py:3:15
|
1 | """Test that shadowing an explicit re-export produces a warning."""
2 |
3 | import foo as foo
| --- previous definition of `foo` here
4 | import bar as foo
| ^^^
| ^^^ `foo` redefined here
|
help: Remove definition: `foo`
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `func` from line 2
--> F811_26.py:5:9
--> F811_26.py:2:9
|
1 | class Class:
2 | def func(self):
| ---- previous definition of `func` here
3 | pass
4 |
5 | def func(self):
| ^^^^
| ^^^^ `func` redefined here
6 | pass
|
help: Remove definition: `func`
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `datetime` from line 3
--> F811_28.py:4:22
--> F811_28.py:3:8
|
1 | """Regression test for: https://github.com/astral-sh/ruff/issues/10384"""
2 |
3 | import datetime
| -------- previous definition of `datetime` here
4 | from datetime import datetime
| ^^^^^^^^
| ^^^^^^^^ `datetime` redefined here
5 |
6 | datetime(1, 2, 3)
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `Bar` from line 3
--> F811_29.pyi:8:1
--> F811_29.pyi:3:24
|
1 | """Regression test for: https://github.com/astral-sh/ruff/issues/10509"""
2 |
3 | from foo import Bar as Bar
| --- previous definition of `Bar` here
4 |
5 | class Eggs:
6 | Bar: int # OK
7 |
8 | Bar = 1 # F811
| ^^^
| ^^^ `Bar` redefined here
|
help: Remove definition: `Bar`
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F811 Redefinition of unused `fu` from line 1
--> F811_3.py:1:12
--> F811_3.py:1:8
|
1 | import fu; fu = 3
| ^^
| -- ^^ `fu` redefined here
| |
| previous definition of `fu` here
|
help: Remove definition: `fu`
Loading
Loading