Skip to content

Commit ecdd5de

Browse files
committed
fixes #121331
1 parent 9fb91aa commit ecdd5de

17 files changed

+231
-34
lines changed

compiler/rustc_resolve/src/build_reduced_graph.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutio
1616

1717
use rustc_ast::visit::{self, AssocCtxt, Visitor};
1818
use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
19-
use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
19+
use rustc_ast::{AttrVec, Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
2020
use rustc_attr as attr;
2121
use rustc_data_structures::sync::Lrc;
2222
use rustc_errors::{codes::*, struct_span_code_err, Applicability};
@@ -30,6 +30,7 @@ use rustc_middle::{bug, ty};
3030
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
3131
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3232
use rustc_span::Span;
33+
use thin_vec::ThinVec;
3334

3435
use std::cell::Cell;
3536

@@ -360,6 +361,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
360361
use_span: item.span,
361362
use_span_with_attributes: item.span_with_attributes(),
362363
has_attributes: !item.attrs.is_empty(),
364+
has_allow_unused_imports_attribute: check_allow_unused_imports_attr(&item.attrs),
363365
root_span,
364366
root_id,
365367
vis: Cell::new(Some(vis)),
@@ -884,6 +886,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
884886
parent_scope: self.parent_scope,
885887
imported_module: Cell::new(module),
886888
has_attributes: !item.attrs.is_empty(),
889+
has_allow_unused_imports_attribute: check_allow_unused_imports_attr(&item.attrs),
887890
use_span_with_attributes: item.span_with_attributes(),
888891
use_span: item.span,
889892
root_span: item.span,
@@ -1091,6 +1094,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
10911094
imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
10921095
use_span_with_attributes: item.span_with_attributes(),
10931096
has_attributes: !item.attrs.is_empty(),
1097+
has_allow_unused_imports_attribute: check_allow_unused_imports_attr(&item.attrs),
10941098
use_span: item.span,
10951099
root_span: span,
10961100
span,
@@ -1262,6 +1266,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
12621266
parent_scope: self.parent_scope,
12631267
imported_module: Cell::new(None),
12641268
has_attributes: false,
1269+
has_allow_unused_imports_attribute: false,
12651270
use_span_with_attributes: span,
12661271
use_span: span,
12671272
root_span: span,
@@ -1534,3 +1539,16 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
15341539
}
15351540
}
15361541
}
1542+
1543+
fn check_allow_unused_imports_attr(attrs: &AttrVec) -> bool {
1544+
attrs.iter().any(|attr| {
1545+
if attr.has_name(sym::allow) {
1546+
for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
1547+
if item.has_name(sym::unused_imports) {
1548+
return true;
1549+
}
1550+
}
1551+
}
1552+
false
1553+
})
1554+
}

compiler/rustc_resolve/src/check_unused.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ use crate::imports::ImportKind;
2727
use crate::module_to_string;
2828
use crate::Resolver;
2929

30-
use crate::NameBindingKind;
30+
use crate::{LexicalScopeBinding, NameBindingKind};
3131
use rustc_ast as ast;
3232
use rustc_ast::visit::{self, Visitor};
3333
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
3434
use rustc_data_structures::unord::UnordSet;
3535
use rustc_errors::{pluralize, MultiSpan};
3636
use rustc_hir::def::{DefKind, Res};
37-
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS};
37+
use rustc_session::lint::builtin::{
38+
MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS, UNUSED_QUALIFICATIONS,
39+
};
3840
use rustc_session::lint::BuiltinLintDiag;
3941
use rustc_span::symbol::{kw, Ident};
4042
use rustc_span::{Span, DUMMY_SP};
@@ -494,6 +496,30 @@ impl Resolver<'_, '_> {
494496
}
495497

496498
let unused_imports = visitor.unused_imports;
499+
500+
// The lint fixes for unused_import and unnecessary_qualification may conflict.
501+
// Deleting both unused imports and unnecessary segments of an item may result
502+
// in the item not being found.
503+
for v in &self.potentially_unnecessary_qualification {
504+
if let LexicalScopeBinding::Item(name_binding) = v.binding
505+
&& let NameBindingKind::Import { import, .. } = name_binding.kind
506+
&& let Some(unused_import) = unused_imports.get(&import.root_id)
507+
&& let Some(id) = import.id()
508+
&& unused_import.unused.contains(&id)
509+
&& !import.has_allow_unused_imports_attribute
510+
{
511+
continue;
512+
}
513+
514+
self.lint_buffer.buffer_lint_with_diagnostic(
515+
UNUSED_QUALIFICATIONS,
516+
v.finalize.node_id,
517+
v.finalize.path_span,
518+
"unnecessary qualification",
519+
BuiltinLintDiag::UnusedQualifications { removal_span: v.span },
520+
);
521+
}
522+
497523
let mut check_redundant_imports = FxIndexSet::default();
498524
for module in self.arenas.local_modules().iter() {
499525
for (_key, resolution) in self.resolutions(*module).borrow().iter() {

compiler/rustc_resolve/src/imports.rs

+4
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ pub(crate) struct ImportData<'a> {
162162
/// Did the use statement have any attributes?
163163
pub has_attributes: bool,
164164

165+
/// Only for avoiding reporting unnecessary_qualification and unused_imports
166+
/// at same time.
167+
pub has_allow_unused_imports_attribute: bool,
168+
165169
/// Span of this use tree.
166170
pub span: Span,
167171

compiler/rustc_resolve/src/late.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,14 @@ impl MaybeExported<'_> {
580580
}
581581
}
582582

583+
/// Used for recording UnnecessaryQualification.
584+
#[derive(Debug)]
585+
pub(crate) struct UnnecessaryQualification<'a> {
586+
pub binding: LexicalScopeBinding<'a>,
587+
pub finalize: Finalize,
588+
pub span: Span,
589+
}
590+
583591
#[derive(Default)]
584592
struct DiagMetadata<'ast> {
585593
/// The current trait's associated items' ident, used for diagnostic suggestions.
@@ -4653,20 +4661,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
46534661
let ns = if i + 1 == path.len() { ns } else { TypeNS };
46544662
let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?;
46554663
let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?;
4656-
4657-
(res == binding.res()).then_some(seg)
4664+
(res == binding.clone().res()).then_some((seg, binding))
46584665
});
46594666

46604667
if let Some(unqualified) = unqualified {
4661-
self.r.lint_buffer.buffer_lint_with_diagnostic(
4662-
lint::builtin::UNUSED_QUALIFICATIONS,
4663-
finalize.node_id,
4664-
finalize.path_span,
4665-
"unnecessary qualification",
4666-
lint::BuiltinLintDiag::UnusedQualifications {
4667-
removal_span: path[0].ident.span.until(unqualified.ident.span),
4668-
},
4669-
);
4668+
self.r.potentially_unnecessary_qualification.push(UnnecessaryQualification {
4669+
binding: unqualified.1,
4670+
finalize: finalize,
4671+
span: path[0].ident.span.until(unqualified.0.ident.span),
4672+
});
46704673
}
46714674
}
46724675
}

compiler/rustc_resolve/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use std::fmt;
6868

6969
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
7070
use imports::{Import, ImportData, ImportKind, NameResolution};
71-
use late::{HasGenericParams, PathSource, PatternSource};
71+
use late::{HasGenericParams, PathSource, PatternSource, UnnecessaryQualification};
7272
use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
7373

7474
use crate::effective_visibilities::EffectiveVisibilitiesVisitor;
@@ -372,7 +372,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
372372
/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
373373
/// items are visible in their whole block, while `Res`es only from the place they are defined
374374
/// forward.
375-
#[derive(Debug)]
375+
#[derive(Debug, Clone)]
376376
enum LexicalScopeBinding<'a> {
377377
Item(NameBinding<'a>),
378378
Res(Res),
@@ -1105,6 +1105,8 @@ pub struct Resolver<'a, 'tcx> {
11051105

11061106
potentially_unused_imports: Vec<Import<'a>>,
11071107

1108+
potentially_unnecessary_qualification: Vec<UnnecessaryQualification<'a>>,
1109+
11081110
/// Table for mapping struct IDs into struct constructor IDs,
11091111
/// it's not used during normal resolution, only for better error reporting.
11101112
/// Also includes of list of each fields visibility
@@ -1464,6 +1466,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
14641466
local_macro_def_scopes: FxHashMap::default(),
14651467
name_already_seen: FxHashMap::default(),
14661468
potentially_unused_imports: Vec::new(),
1469+
potentially_unnecessary_qualification: Default::default(),
14671470
struct_constructors: Default::default(),
14681471
unused_macros: Default::default(),
14691472
unused_macro_rules: Default::default(),

tests/ui/lint/lint-qualification.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ fn main() {
2121
//~^ ERROR: unnecessary qualification
2222
//~| ERROR: unnecessary qualification
2323

24+
#[allow(unused_imports)]
2425
use std::fmt;
2526
let _: fmt::Result = Ok(()); //~ ERROR: unnecessary qualification
2627

tests/ui/lint/lint-qualification.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ fn main() {
2121
//~^ ERROR: unnecessary qualification
2222
//~| ERROR: unnecessary qualification
2323

24+
#[allow(unused_imports)]
2425
use std::fmt;
2526
let _: std::fmt::Result = Ok(()); //~ ERROR: unnecessary qualification
2627

tests/ui/lint/lint-qualification.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ LL + let _: std::vec::Vec<String> = Vec::<String>::new();
7676
|
7777

7878
error: unnecessary qualification
79-
--> $DIR/lint-qualification.rs:25:12
79+
--> $DIR/lint-qualification.rs:26:12
8080
|
8181
LL | let _: std::fmt::Result = Ok(());
8282
| ^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL + let _: fmt::Result = Ok(());
8888
|
8989

9090
error: unnecessary qualification
91-
--> $DIR/lint-qualification.rs:27:13
91+
--> $DIR/lint-qualification.rs:28:13
9292
|
9393
LL | let _ = <bool as ::std::default::Default>::default(); // issue #121999
9494
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//@ run-rustfix
2+
//@ edition:2021
3+
#![deny(unused_imports)]
4+
#![deny(unused_qualifications)]
5+
#![feature(coroutines, coroutine_trait)]
6+
7+
use std::ops::{
8+
Coroutine,
9+
CoroutineState::{self},
10+
//~^ ERROR unused import: `*`
11+
};
12+
use std::pin::Pin;
13+
14+
#[allow(dead_code)]
15+
fn finish<T>(mut amt: usize, mut t: T) -> T::Return
16+
where T: Coroutine<(), Yield = ()> + Unpin,
17+
{
18+
loop {
19+
match Pin::new(&mut t).resume(()) {
20+
CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(),
21+
CoroutineState::Complete(ret) => {
22+
assert_eq!(amt, 0);
23+
return ret
24+
}
25+
}
26+
}
27+
}
28+
29+
30+
mod foo {
31+
pub fn bar() {}
32+
}
33+
34+
mod baz {
35+
pub fn qux() {}
36+
}
37+
38+
pub fn main() {
39+
40+
41+
//~^ ERROR unused import: `foo::bar`
42+
foo::bar();
43+
44+
#[allow(unused_imports)]
45+
use baz::qux;
46+
qux();
47+
//~^ ERROR unnecessary qualification
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//@ run-rustfix
2+
//@ edition:2021
3+
#![deny(unused_imports)]
4+
#![deny(unused_qualifications)]
5+
#![feature(coroutines, coroutine_trait)]
6+
7+
use std::ops::{
8+
Coroutine,
9+
CoroutineState::{self, *},
10+
//~^ ERROR unused import: `*`
11+
};
12+
use std::pin::Pin;
13+
14+
#[allow(dead_code)]
15+
fn finish<T>(mut amt: usize, mut t: T) -> T::Return
16+
where T: Coroutine<(), Yield = ()> + Unpin,
17+
{
18+
loop {
19+
match Pin::new(&mut t).resume(()) {
20+
CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(),
21+
CoroutineState::Complete(ret) => {
22+
assert_eq!(amt, 0);
23+
return ret
24+
}
25+
}
26+
}
27+
}
28+
29+
30+
mod foo {
31+
pub fn bar() {}
32+
}
33+
34+
mod baz {
35+
pub fn qux() {}
36+
}
37+
38+
pub fn main() {
39+
40+
use foo::bar;
41+
//~^ ERROR unused import: `foo::bar`
42+
foo::bar();
43+
44+
#[allow(unused_imports)]
45+
use baz::qux;
46+
baz::qux();
47+
//~^ ERROR unnecessary qualification
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: unused import: `*`
2+
--> $DIR/lint-unnecessary-qualification-issue-121331.rs:9:28
3+
|
4+
LL | CoroutineState::{self, *},
5+
| ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-unnecessary-qualification-issue-121331.rs:3:9
9+
|
10+
LL | #![deny(unused_imports)]
11+
| ^^^^^^^^^^^^^^
12+
13+
error: unused import: `foo::bar`
14+
--> $DIR/lint-unnecessary-qualification-issue-121331.rs:40:9
15+
|
16+
LL | use foo::bar;
17+
| ^^^^^^^^
18+
19+
error: unnecessary qualification
20+
--> $DIR/lint-unnecessary-qualification-issue-121331.rs:46:5
21+
|
22+
LL | baz::qux();
23+
| ^^^^^^^^
24+
|
25+
note: the lint level is defined here
26+
--> $DIR/lint-unnecessary-qualification-issue-121331.rs:4:9
27+
|
28+
LL | #![deny(unused_qualifications)]
29+
| ^^^^^^^^^^^^^^^^^^^^^
30+
help: remove the unnecessary path segments
31+
|
32+
LL - baz::qux();
33+
LL + qux();
34+
|
35+
36+
error: aborting due to 3 previous errors
37+

tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#[allow(unused_imports)]
88
use std::ops;
9+
#[allow(unused_imports)]
910
use std::ops::Index;
1011

1112
pub struct A;

tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#[allow(unused_imports)]
88
use std::ops;
9+
#[allow(unused_imports)]
910
use std::ops::Index;
1011

1112
pub struct A;

0 commit comments

Comments
 (0)