Skip to content

Commit d95745e

Browse files
committed
Auto merge of #85427 - ehuss:fix-use-placement, r=jackh726
Fix use placement for suggestions near main. This fixes an edge case for the suggestion to add a `use`. When running with `--test`, the `main` function will be annotated with an `#[allow(dead_code)]` attribute. The `UsePlacementFinder` would end up using the dummy span of that synthetic attribute. If there are top-level inner attributes, this would place the `use` in the wrong position. The solution here is to ignore attributes with dummy spans. In the process of working on this, I discovered that the `use_suggestion_placement` test was broken. `UsePlacementFinder` is unaware of active attributes. Attributes like `#[derive]` don't exist in the AST since they are removed. Fixing that is difficult, since the AST does not retain enough information. I considered trying to place the `use` towards the top of the module after any `extern crate` items, but I couldn't find a way to get a span for the start of a module block (the `mod` span starts at the `mod` keyword, and it seems tricky to find the spot just after the opening bracket and past inner attributes). For now, I just put some comments about the issue. This appears to have been a known issue in #44215 where the test for it was introduced, and the fix seemed to be deferred to later.
2 parents 456a032 + 1400cb0 commit d95745e

11 files changed

+167
-20
lines changed

compiler/rustc_resolve/src/lib.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,15 @@ impl UsePlacementFinder {
335335
if self.span.map_or(true, |span| item.span < span)
336336
&& !item.span.from_expansion()
337337
{
338+
self.span = Some(item.span.shrink_to_lo());
338339
// don't insert between attributes and an item
339-
if item.attrs.is_empty() {
340-
self.span = Some(item.span.shrink_to_lo());
341-
} else {
342-
// find the first attribute on the item
343-
for attr in &item.attrs {
344-
if self.span.map_or(true, |span| attr.span < span) {
345-
self.span = Some(attr.span.shrink_to_lo());
346-
}
340+
// find the first attribute on the item
341+
// FIXME: This is broken for active attributes.
342+
for attr in &item.attrs {
343+
if !attr.span.is_dummy()
344+
&& self.span.map_or(true, |span| attr.span < span)
345+
{
346+
self.span = Some(attr.span.shrink_to_lo());
347347
}
348348
}
349349
}

compiler/rustc_typeck/src/check/method/suggest.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -1650,16 +1650,16 @@ impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> {
16501650
_ => {
16511651
if self.span.map_or(true, |span| item.span < span) {
16521652
if !item.span.from_expansion() {
1653+
self.span = Some(item.span.shrink_to_lo());
16531654
// Don't insert between attributes and an item.
16541655
let attrs = self.tcx.hir().attrs(item.hir_id());
1655-
if attrs.is_empty() {
1656-
self.span = Some(item.span.shrink_to_lo());
1657-
} else {
1658-
// Find the first attribute on the item.
1659-
for attr in attrs {
1660-
if self.span.map_or(true, |span| attr.span < span) {
1661-
self.span = Some(attr.span.shrink_to_lo());
1662-
}
1656+
// Find the first attribute on the item.
1657+
// FIXME: This is broken for active attributes.
1658+
for attr in attrs {
1659+
if !attr.span.is_dummy()
1660+
&& self.span.map_or(true, |span| attr.span < span)
1661+
{
1662+
self.span = Some(attr.span.shrink_to_lo());
16631663
}
16641664
}
16651665
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
4+
use m::A;
5+
6+
use std::collections::HashMap;
7+
8+
macro_rules! y {
9+
() => {}
10+
}
11+
12+
mod m {
13+
pub const A: i32 = 0;
14+
}
15+
16+
mod foo {
17+
// FIXME: UsePlacementFinder is broken because active attributes are
18+
// removed, and thus the `derive` attribute here is not in the AST.
19+
// An inert attribute should work, though.
20+
// #[derive(Debug)]
21+
use std::path::Path;
22+
23+
#[allow(warnings)]
24+
pub struct Foo;
25+
26+
// test whether the use suggestion isn't
27+
// placed into the expansion of `#[derive(Debug)]
28+
type Bar = Path; //~ ERROR cannot find
29+
}
30+
31+
fn main() {
32+
y!();
33+
let _ = A; //~ ERROR cannot find
34+
foo();
35+
}
36+
37+
fn foo() {
38+
type Dict<K, V> = HashMap<K, V>; //~ ERROR cannot find
39+
}

src/test/ui/resolve/use_suggestion_placement.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
14
macro_rules! y {
25
() => {}
36
}
@@ -7,7 +10,11 @@ mod m {
710
}
811

912
mod foo {
10-
#[derive(Debug)]
13+
// FIXME: UsePlacementFinder is broken because active attributes are
14+
// removed, and thus the `derive` attribute here is not in the AST.
15+
// An inert attribute should work, though.
16+
// #[derive(Debug)]
17+
#[allow(warnings)]
1118
pub struct Foo;
1219

1320
// test whether the use suggestion isn't

src/test/ui/resolve/use_suggestion_placement.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0412]: cannot find type `Path` in this scope
2-
--> $DIR/use_suggestion_placement.rs:15:16
2+
--> $DIR/use_suggestion_placement.rs:22:16
33
|
44
LL | type Bar = Path;
55
| ^^^^ not found in this scope
@@ -10,7 +10,7 @@ LL | use std::path::Path;
1010
|
1111

1212
error[E0425]: cannot find value `A` in this scope
13-
--> $DIR/use_suggestion_placement.rs:20:13
13+
--> $DIR/use_suggestion_placement.rs:27:13
1414
|
1515
LL | let _ = A;
1616
| ^ not found in this scope
@@ -21,7 +21,7 @@ LL | use m::A;
2121
|
2222

2323
error[E0412]: cannot find type `HashMap` in this scope
24-
--> $DIR/use_suggestion_placement.rs:25:23
24+
--> $DIR/use_suggestion_placement.rs:32:23
2525
|
2626
LL | type Dict<K, V> = HashMap<K, V>;
2727
| ^^^^^^^ not found in this scope
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: --test
2+
// run-rustfix
3+
// Checks that the `use` suggestion appears *below* this inner attribute.
4+
// There was an issue where the test synthetic #[allow(dead)] attribute on
5+
// main which has a dummy span caused the suggestion to be placed at the top
6+
// of the file.
7+
#![allow(unused)]
8+
9+
use std::fmt::Debug;
10+
11+
fn main() {}
12+
13+
fn foobar<T: Debug>(x: T) {} //~ ERROR expected trait, found derive macro
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// compile-flags: --test
2+
// run-rustfix
3+
// Checks that the `use` suggestion appears *below* this inner attribute.
4+
// There was an issue where the test synthetic #[allow(dead)] attribute on
5+
// main which has a dummy span caused the suggestion to be placed at the top
6+
// of the file.
7+
#![allow(unused)]
8+
9+
fn main() {}
10+
11+
fn foobar<T: Debug>(x: T) {} //~ ERROR expected trait, found derive macro
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0404]: expected trait, found derive macro `Debug`
2+
--> $DIR/use-placement-resolve.rs:11:14
3+
|
4+
LL | fn foobar<T: Debug>(x: T) {}
5+
| ^^^^^ not a trait
6+
|
7+
help: consider importing this trait instead
8+
|
9+
LL | use std::fmt::Debug;
10+
|
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0404`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// compile-flags: --test
2+
// run-rustfix
3+
// Checks that the `use` suggestion appears *below* this inner attribute.
4+
// There was an issue where the test synthetic #[allow(dead)] attribute on
5+
// main which has a dummy span caused the suggestion to be placed at the top
6+
// of the file.
7+
#![allow(unused)]
8+
9+
use m::Foo;
10+
11+
fn main() {
12+
let s = m::S;
13+
s.abc(); //~ ERROR no method named `abc`
14+
}
15+
16+
mod m {
17+
pub trait Foo {
18+
fn abc(&self) {}
19+
}
20+
pub struct S;
21+
impl Foo for S{}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: --test
2+
// run-rustfix
3+
// Checks that the `use` suggestion appears *below* this inner attribute.
4+
// There was an issue where the test synthetic #[allow(dead)] attribute on
5+
// main which has a dummy span caused the suggestion to be placed at the top
6+
// of the file.
7+
#![allow(unused)]
8+
9+
fn main() {
10+
let s = m::S;
11+
s.abc(); //~ ERROR no method named `abc`
12+
}
13+
14+
mod m {
15+
pub trait Foo {
16+
fn abc(&self) {}
17+
}
18+
pub struct S;
19+
impl Foo for S{}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0599]: no method named `abc` found for struct `S` in the current scope
2+
--> $DIR/use-placement-typeck.rs:11:7
3+
|
4+
LL | s.abc();
5+
| ^^^ method not found in `S`
6+
...
7+
LL | fn abc(&self) {}
8+
| --- the method is available for `S` here
9+
LL | }
10+
LL | pub struct S;
11+
| ------------- method `abc` not found for this
12+
|
13+
= help: items from traits can only be used if the trait is in scope
14+
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
15+
|
16+
LL | use m::Foo;
17+
|
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)