Skip to content

Commit 50f8558

Browse files
committed
Suppress unused_mut if unused_variables is reported
1 parent bc7b17c commit 50f8558

12 files changed

+176
-133
lines changed

compiler/rustc_borrowck/src/consumers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ pub fn get_body_with_borrowck_facts<'tcx>(
3434
tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| {
3535
let input_body: &Body<'_> = &input_body.borrow();
3636
let promoted: &IndexVec<_, _> = &promoted.borrow();
37-
*super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
37+
*super::do_mir_borrowck(&infcx, input_body, promoted, &None, true).1.unwrap()
3838
})
3939
}

compiler/rustc_borrowck/src/lib.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extern crate rustc_middle;
1717
#[macro_use]
1818
extern crate tracing;
1919

20-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
20+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
2121
use rustc_data_structures::graph::dominators::Dominators;
2222
use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
2323
use rustc_hir as hir;
@@ -129,16 +129,22 @@ fn mir_borrowck<'tcx>(
129129
def: ty::WithOptConstParam<LocalDefId>,
130130
) -> &'tcx BorrowCheckResult<'tcx> {
131131
let (input_body, promoted) = tcx.mir_promoted(def);
132-
debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
132+
133+
let def_id = def.did.to_def_id();
134+
debug!("run query mir_borrowck: {}", tcx.def_path_str(def_id));
135+
133136
let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
134137

138+
let is_fn = matches!(tcx.hir().body_owner_kind(def.did), hir::BodyOwnerKind::Fn);
139+
let unused_variables_spans = if is_fn { tcx.check_liveness(def_id) } else { &None };
140+
135141
let opt_closure_req = tcx
136142
.infer_ctxt()
137143
.with_opaque_type_inference(DefiningAnchor::Bind(hir_owner))
138144
.enter(|infcx| {
139145
let input_body: &Body<'_> = &input_body.borrow();
140146
let promoted: &IndexVec<_, _> = &promoted.borrow();
141-
do_mir_borrowck(&infcx, input_body, promoted, false).0
147+
do_mir_borrowck(&infcx, input_body, promoted, unused_variables_spans, false).0
142148
});
143149
debug!("mir_borrowck done");
144150

@@ -155,6 +161,7 @@ fn do_mir_borrowck<'a, 'tcx>(
155161
infcx: &InferCtxt<'a, 'tcx>,
156162
input_body: &Body<'tcx>,
157163
input_promoted: &IndexVec<Promoted, Body<'tcx>>,
164+
unused_variables_spans: &Option<FxIndexSet<Span>>,
158165
return_body_with_facts: bool,
159166
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
160167
let def = input_body.source.with_opt_param().as_local().unwrap();
@@ -428,6 +435,14 @@ fn do_mir_borrowck<'a, 'tcx>(
428435
}
429436

430437
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
438+
let ident_span = span.with_lo(mut_span.hi());
439+
440+
// Suppress lints if we already reported unused variables
441+
if let Some(unused_variables_spans) = unused_variables_spans {
442+
if unused_variables_spans.contains(&ident_span) {
443+
continue;
444+
}
445+
}
431446

432447
tcx.emit_spanned_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
433448
}

compiler/rustc_middle/src/query/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,8 @@ rustc_queries! {
830830
desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) }
831831
}
832832

833-
query check_liveness(key: DefId) {
833+
query check_liveness(key: DefId) -> Option<FxIndexSet<Span>> {
834+
arena_cache
834835
desc { |tcx| "checking liveness of variables in {}", tcx.def_path_str(key) }
835836
}
836837

compiler/rustc_passes/src/liveness.rs

+34-18
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ use self::LiveNodeKind::*;
8585
use self::VarKind::*;
8686

8787
use rustc_ast::InlineAsmOptions;
88-
use rustc_data_structures::fx::FxIndexMap;
88+
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
8989
use rustc_errors::Applicability;
9090
use rustc_hir as hir;
9191
use rustc_hir::def::*;
@@ -99,6 +99,7 @@ use rustc_session::lint;
9999
use rustc_span::symbol::{kw, sym, Symbol};
100100
use rustc_span::{BytePos, Span};
101101

102+
use std::cell::RefCell;
102103
use std::collections::VecDeque;
103104
use std::io;
104105
use std::io::prelude::*;
@@ -138,9 +139,9 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
138139
}
139140
}
140141

141-
fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) {
142+
fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxIndexSet<Span>> {
142143
let local_def_id = match def_id.as_local() {
143-
None => return,
144+
None => return None,
144145
Some(def_id) => def_id,
145146
};
146147

@@ -149,12 +150,12 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) {
149150
if let DefKind::Impl = tcx.def_kind(parent)
150151
&& tcx.has_attr(parent.to_def_id(), sym::automatically_derived)
151152
{
152-
return;
153+
return None;
153154
}
154155

155156
// Don't run unused pass for #[naked]
156157
if tcx.has_attr(def_id, sym::naked) {
157-
return;
158+
return None;
158159
}
159160

160161
let mut maps = IrMaps::new(tcx);
@@ -182,6 +183,8 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) {
182183
lsets.visit_body(body);
183184
lsets.warn_about_unused_upvars(entry_ln);
184185
lsets.warn_about_unused_args(body, entry_ln);
186+
187+
Some(lsets.unused_variables_spans.into_inner())
185188
}
186189

187190
pub fn provide(providers: &mut Providers) {
@@ -517,6 +520,8 @@ struct Liveness<'a, 'tcx> {
517520
// it probably doesn't now)
518521
break_ln: HirIdMap<LiveNode>,
519522
cont_ln: HirIdMap<LiveNode>,
523+
524+
unused_variables_spans: RefCell<FxIndexSet<Span>>,
520525
}
521526

522527
impl<'a, 'tcx> Liveness<'a, 'tcx> {
@@ -541,6 +546,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
541546
exit_ln,
542547
break_ln: Default::default(),
543548
cont_ln: Default::default(),
549+
unused_variables_spans: Default::default(),
544550
}
545551
}
546552

@@ -1504,6 +1510,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
15041510
}
15051511
} else {
15061512
if let Some(name) = self.should_warn(var) {
1513+
self.unused_variables_spans.borrow_mut().insert(span);
15071514
self.ir.tcx.struct_span_lint_hir(
15081515
lint::builtin::UNUSED_VARIABLES,
15091516
var_hir_id,
@@ -1594,24 +1601,29 @@ impl<'tcx> Liveness<'_, 'tcx> {
15941601
if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) };
15951602

15961603
if is_assigned {
1604+
let spans = hir_ids_and_spans
1605+
.into_iter()
1606+
.map(|(_, _, ident_span)| ident_span)
1607+
.collect::<Vec<_>>();
1608+
self.unused_variables_spans.borrow_mut().extend(&spans);
15971609
self.ir.tcx.struct_span_lint_hir(
15981610
lint::builtin::UNUSED_VARIABLES,
15991611
first_hir_id,
1600-
hir_ids_and_spans
1601-
.into_iter()
1602-
.map(|(_, _, ident_span)| ident_span)
1603-
.collect::<Vec<_>>(),
1612+
spans,
16041613
|lint| {
16051614
lint.build(&format!("variable `{}` is assigned to, but never used", name))
16061615
.note(&format!("consider using `_{}` instead", name))
16071616
.emit();
16081617
},
16091618
)
16101619
} else if can_remove {
1620+
let spans =
1621+
hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>();
1622+
self.unused_variables_spans.borrow_mut().extend(&spans);
16111623
self.ir.tcx.struct_span_lint_hir(
16121624
lint::builtin::UNUSED_VARIABLES,
16131625
first_hir_id,
1614-
hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>(),
1626+
spans,
16151627
|lint| {
16161628
let mut err = lint.build(&format!("unused variable: `{}`", name));
16171629
err.multipart_suggestion(
@@ -1654,13 +1666,15 @@ impl<'tcx> Liveness<'_, 'tcx> {
16541666
)
16551667
.collect::<Vec<_>>();
16561668

1669+
let spans = hir_ids_and_spans
1670+
.iter()
1671+
.map(|(_, pat_span, _)| *pat_span)
1672+
.collect::<Vec<_>>();
1673+
self.unused_variables_spans.borrow_mut().extend(&spans);
16571674
self.ir.tcx.struct_span_lint_hir(
16581675
lint::builtin::UNUSED_VARIABLES,
16591676
first_hir_id,
1660-
hir_ids_and_spans
1661-
.iter()
1662-
.map(|(_, pat_span, _)| *pat_span)
1663-
.collect::<Vec<_>>(),
1677+
spans,
16641678
|lint| {
16651679
let mut err = lint.build(&format!("unused variable: `{}`", name));
16661680
err.multipart_suggestion(
@@ -1677,13 +1691,15 @@ impl<'tcx> Liveness<'_, 'tcx> {
16771691
.map(|(_, _, ident_span)| (ident_span, format!("_{}", name)))
16781692
.collect::<Vec<_>>();
16791693

1694+
let spans = hir_ids_and_spans
1695+
.iter()
1696+
.map(|(_, _, ident_span)| *ident_span)
1697+
.collect::<Vec<_>>();
1698+
self.unused_variables_spans.borrow_mut().extend(&spans);
16801699
self.ir.tcx.struct_span_lint_hir(
16811700
lint::builtin::UNUSED_VARIABLES,
16821701
first_hir_id,
1683-
hir_ids_and_spans
1684-
.iter()
1685-
.map(|(_, _, ident_span)| *ident_span)
1686-
.collect::<Vec<_>>(),
1702+
spans,
16871703
|lint| {
16881704
let mut err = lint.build(&format!("unused variable: `{}`", name));
16891705
if self.has_added_lit_match_name_span(&name, opt_body, &mut err) {

src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,22 @@ mod foo {
2121
}
2222

2323
#[expect(
24-
unused_mut,
24+
unused_variables,
2525
//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
26-
//~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered
27-
reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered"
26+
//~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_variables` lint is triggered
27+
reason = "this `expect` is overridden by a `warn` attribute before the `unused_variables` lint is triggered"
2828
)]
2929
mod oof {
3030
#[warn(
31-
unused_mut,
31+
unused_variables,
3232
//~^ NOTE the lint level is defined here
33-
reason = "this overrides the previous `expect` lint level and warns about the `unused_mut` lint here"
33+
reason = "this overrides the previous `expect` lint level and warns about the `unused_variables` lint here"
3434
)]
3535
fn bar() {
3636
let mut v = 0;
37-
//~^ WARNING variable does not need to be mutable [unused_mut]
38-
//~| NOTE this overrides the previous `expect` lint level and warns about the `unused_mut` lint here
39-
//~| HELP remove this `mut`
37+
//~^ WARNING unused variable: `v` [unused_variables]
38+
//~| NOTE this overrides the previous `expect` lint level and warns about the `unused_variables` lint here
39+
//~| HELP if this is intentional, prefix it with an underscore
4040
}
4141
}
4242

src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr

+16-18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
warning: unused variable: `v`
2+
--> $DIR/expect_nested_lint_levels.rs:36:17
3+
|
4+
LL | let mut v = 0;
5+
| ^ help: if this is intentional, prefix it with an underscore: `_v`
6+
|
7+
= note: this overrides the previous `expect` lint level and warns about the `unused_variables` lint here
8+
note: the lint level is defined here
9+
--> $DIR/expect_nested_lint_levels.rs:31:9
10+
|
11+
LL | unused_variables,
12+
| ^^^^^^^^^^^^^^^^
13+
114
error: unused variable: `this_is_my_function`
215
--> $DIR/expect_nested_lint_levels.rs:48:9
316
|
@@ -10,21 +23,6 @@ note: the lint level is defined here
1023
LL | #[forbid(unused_variables)]
1124
| ^^^^^^^^^^^^^^^^
1225

13-
warning: variable does not need to be mutable
14-
--> $DIR/expect_nested_lint_levels.rs:36:13
15-
|
16-
LL | let mut v = 0;
17-
| ----^
18-
| |
19-
| help: remove this `mut`
20-
|
21-
= note: this overrides the previous `expect` lint level and warns about the `unused_mut` lint here
22-
note: the lint level is defined here
23-
--> $DIR/expect_nested_lint_levels.rs:31:9
24-
|
25-
LL | unused_mut,
26-
| ^^^^^^^^^^
27-
2826
warning: this lint expectation is unfulfilled
2927
--> $DIR/expect_nested_lint_levels.rs:7:5
3028
|
@@ -37,10 +35,10 @@ LL | unused_mut,
3735
warning: this lint expectation is unfulfilled
3836
--> $DIR/expect_nested_lint_levels.rs:24:5
3937
|
40-
LL | unused_mut,
41-
| ^^^^^^^^^^
38+
LL | unused_variables,
39+
| ^^^^^^^^^^^^^^^^
4240
|
43-
= note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered
41+
= note: this `expect` is overridden by a `warn` attribute before the `unused_variables` lint is triggered
4442

4543
warning: this lint expectation is unfulfilled
4644
--> $DIR/expect_nested_lint_levels.rs:43:10

src/test/ui/lint/unused/issue-47390-unused-variable-in-struct-pattern.rs

-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ fn main() {
3232

3333
let mut mut_unused_var = 1;
3434
//~^ WARNING unused variable: `mut_unused_var`
35-
//~| WARNING variable does not need to be mutable
3635

3736
let (mut var, unused_var) = (1, 2);
3837
//~^ WARNING unused variable: `var`
3938
//~| WARNING unused variable: `unused_var`
40-
//~| WARNING variable does not need to be mutable
4139
// NOTE: `var` comes after `unused_var` lexicographically yet the warning
4240
// for `var` will be emitted before the one for `unused_var`. We use an
4341
// `IndexMap` to ensure this is the case instead of a `BTreeMap`.

0 commit comments

Comments
 (0)