Skip to content

Commit 1456bec

Browse files
committed
Suggest unit struct and constants.
1 parent 83c6a45 commit 1456bec

File tree

6 files changed

+257
-27
lines changed

6 files changed

+257
-27
lines changed

compiler/rustc_mir_transform/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,5 @@ mir_transform_unused_variable = unused variable: `{$name}`
104104
mir_transform_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable
105105
106106
mir_transform_unused_variable_try_ignore = try ignoring the field
107+
108+
mir_transform_unused_variable_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}`

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ pub(crate) struct UnusedCaptureMaybeCaptureRef {
170170
#[note]
171171
pub(crate) struct UnusedVarAssignedOnly {
172172
pub name: Symbol,
173+
#[subdiagnostic]
174+
pub typo: Option<PatternTypo>,
173175
}
174176

175177
#[derive(LintDiagnostic)]
@@ -235,6 +237,8 @@ pub(crate) enum UnusedVariableSugg {
235237
#[suggestion_part(code = "_{name}")]
236238
spans: Vec<Span>,
237239
name: Symbol,
240+
#[subdiagnostic]
241+
typo: Option<PatternTypo>,
238242
},
239243

240244
#[help(mir_transform_unused_variable_args_in_macro)]
@@ -266,6 +270,20 @@ impl Subdiagnostic for UnusedVariableStringInterp {
266270
}
267271
}
268272

273+
#[derive(Subdiagnostic)]
274+
#[multipart_suggestion(
275+
mir_transform_unused_variable_typo,
276+
style = "verbose",
277+
applicability = "machine-applicable"
278+
)]
279+
pub(crate) struct PatternTypo {
280+
#[suggestion_part(code = "{code}")]
281+
pub span: Span,
282+
pub code: String,
283+
pub item_name: Symbol,
284+
pub kind: &'static str,
285+
}
286+
269287
pub(crate) struct MustNotSupend<'a, 'tcx> {
270288
pub tcx: TyCtxt<'tcx>,
271289
pub yield_sp: Span,

compiler/rustc_mir_transform/src/liveness.rs

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use rustc_abi::FieldIdx;
22
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry};
33
use rustc_hir::attrs::AttributeKind;
4-
use rustc_hir::def::DefKind;
5-
use rustc_hir::def_id::LocalDefId;
4+
use rustc_hir::def::{CtorKind, DefKind};
5+
use rustc_hir::def_id::{DefId, LocalDefId};
66
use rustc_hir::find_attr;
77
use rustc_index::IndexVec;
88
use rustc_index::bit_set::DenseBitSet;
@@ -11,11 +11,13 @@ use rustc_middle::mir::visit::{
1111
MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor,
1212
};
1313
use rustc_middle::mir::*;
14+
use rustc_middle::ty::print::with_no_trimmed_paths;
1415
use rustc_middle::ty::{self, Ty, TyCtxt};
1516
use rustc_mir_dataflow::fmt::DebugWithContext;
1617
use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor};
1718
use rustc_session::lint;
1819
use rustc_span::Span;
20+
use rustc_span::edit_distance::find_best_match_for_name;
1921
use rustc_span::symbol::{Symbol, kw, sym};
2022

2123
use crate::errors;
@@ -201,6 +203,62 @@ fn maybe_suggest_literal_matching_name(
201203
finder.found
202204
}
203205

206+
/// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct.
207+
fn maybe_suggest_unit_pattern_typo<'tcx>(
208+
tcx: TyCtxt<'tcx>,
209+
body_def_id: DefId,
210+
name: Symbol,
211+
span: Span,
212+
ty: Ty<'tcx>,
213+
) -> Option<errors::PatternTypo> {
214+
if let ty::Adt(adt_def, _) = ty.peel_refs().kind() {
215+
let variant_names: Vec<_> = adt_def
216+
.variants()
217+
.iter()
218+
.filter(|v| matches!(v.ctor, Some((CtorKind::Const, _))))
219+
.map(|v| v.name)
220+
.collect();
221+
if let Some(name) = find_best_match_for_name(&variant_names, name, None)
222+
&& let Some(variant) = adt_def
223+
.variants()
224+
.iter()
225+
.find(|v| v.name == name && matches!(v.ctor, Some((CtorKind::Const, _))))
226+
{
227+
return Some(errors::PatternTypo {
228+
span,
229+
code: with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)),
230+
kind: tcx.def_descr(variant.def_id),
231+
item_name: variant.name,
232+
});
233+
}
234+
}
235+
236+
// Look for consts of the same type with similar names as well,
237+
// not just unit structs and variants.
238+
let constants = tcx
239+
.hir_body_owners()
240+
.filter(|&def_id| {
241+
matches!(tcx.def_kind(def_id), DefKind::Const)
242+
&& tcx.type_of(def_id).instantiate_identity() == ty
243+
&& tcx.visibility(def_id).is_accessible_from(body_def_id, tcx)
244+
})
245+
.collect::<Vec<_>>();
246+
let names = constants.iter().map(|&def_id| tcx.item_name(def_id)).collect::<Vec<_>>();
247+
if let Some(item_name) = find_best_match_for_name(&names, name, None)
248+
&& let Some(position) = names.iter().position(|&n| n == item_name)
249+
&& let Some(&def_id) = constants.get(position)
250+
{
251+
return Some(errors::PatternTypo {
252+
span,
253+
code: with_no_trimmed_paths!(tcx.def_path_str(def_id)),
254+
kind: "constant",
255+
item_name,
256+
});
257+
}
258+
259+
None
260+
}
261+
204262
/// Return whether we should consider the current place as a drop guard and skip reporting.
205263
fn maybe_drop_guard<'tcx>(
206264
tcx: TyCtxt<'tcx>,
@@ -860,7 +918,14 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
860918
let sugg = if from_macro {
861919
errors::UnusedVariableSugg::NoSugg { span: def_span, name }
862920
} else {
863-
errors::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name }
921+
let typo = maybe_suggest_unit_pattern_typo(
922+
tcx,
923+
self.body.source.def_id(),
924+
name,
925+
def_span,
926+
decl.ty,
927+
);
928+
errors::UnusedVariableSugg::TryPrefix { spans: vec![def_span], name, typo }
864929
};
865930
tcx.emit_node_span_lint(
866931
lint::builtin::UNUSED_VARIABLES,
@@ -909,11 +974,18 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
909974
continue;
910975
}
911976

977+
let typo = maybe_suggest_unit_pattern_typo(
978+
tcx,
979+
self.body.source.def_id(),
980+
name,
981+
def_span,
982+
decl.ty,
983+
);
912984
tcx.emit_node_span_lint(
913985
lint::builtin::UNUSED_VARIABLES,
914986
hir_id,
915987
def_span,
916-
errors::UnusedVarAssignedOnly { name },
988+
errors::UnusedVarAssignedOnly { name, typo },
917989
);
918990
continue;
919991
}
@@ -944,9 +1016,23 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
9441016
} else if from_macro {
9451017
errors::UnusedVariableSugg::NoSugg { span: def_span, name }
9461018
} else if !introductions.is_empty() {
947-
errors::UnusedVariableSugg::TryPrefix { name, spans: spans.clone() }
1019+
let typo = maybe_suggest_unit_pattern_typo(
1020+
tcx,
1021+
self.body.source.def_id(),
1022+
name,
1023+
def_span,
1024+
decl.ty,
1025+
);
1026+
errors::UnusedVariableSugg::TryPrefix { name, typo, spans: spans.clone() }
9481027
} else {
949-
errors::UnusedVariableSugg::TryPrefix { name, spans: vec![def_span] }
1028+
let typo = maybe_suggest_unit_pattern_typo(
1029+
tcx,
1030+
self.body.source.def_id(),
1031+
name,
1032+
def_span,
1033+
decl.ty,
1034+
);
1035+
errors::UnusedVariableSugg::TryPrefix { name, typo, spans: vec![def_span] }
9501036
};
9511037

9521038
tcx.emit_node_span_lint(

tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,37 @@ warning: unused variable: `Foo`
1616
--> $DIR/lint-uppercase-variables.rs:22:9
1717
|
1818
LL | Foo => {}
19-
| ^^^ help: if this is intentional, prefix it with an underscore: `_Foo`
19+
| ^^^
2020
|
2121
note: the lint level is defined here
2222
--> $DIR/lint-uppercase-variables.rs:1:9
2323
|
2424
LL | #![warn(unused)]
2525
| ^^^^^^
2626
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
27+
help: you might have meant to pattern match on the similarly named variant `Foo`
28+
|
29+
LL | foo::Foo::Foo => {}
30+
| ++++++++++
31+
help: if this is intentional, prefix it with an underscore
32+
|
33+
LL | _Foo => {}
34+
| +
2735

2836
warning: unused variable: `Foo`
2937
--> $DIR/lint-uppercase-variables.rs:28:9
3038
|
3139
LL | let Foo = foo::Foo::Foo;
32-
| ^^^ help: if this is intentional, prefix it with an underscore: `_Foo`
40+
| ^^^
41+
|
42+
help: you might have meant to pattern match on the similarly named variant `Foo`
43+
|
44+
LL | let foo::Foo::Foo = foo::Foo::Foo;
45+
| ++++++++++
46+
help: if this is intentional, prefix it with an underscore
47+
|
48+
LL | let _Foo = foo::Foo::Foo;
49+
| +
3350

3451
error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
3552
--> $DIR/lint-uppercase-variables.rs:33:17
@@ -41,7 +58,16 @@ warning: unused variable: `Foo`
4158
--> $DIR/lint-uppercase-variables.rs:33:17
4259
|
4360
LL | fn in_param(Foo: foo::Foo) {}
44-
| ^^^ help: if this is intentional, prefix it with an underscore: `_Foo`
61+
| ^^^
62+
|
63+
help: you might have meant to pattern match on the similarly named variant `Foo`
64+
|
65+
LL | fn in_param(foo::Foo::Foo: foo::Foo) {}
66+
| ++++++++++
67+
help: if this is intentional, prefix it with an underscore
68+
|
69+
LL | fn in_param(_Foo: foo::Foo) {}
70+
| +
4571

4672
error: structure field `X` should have a snake case name
4773
--> $DIR/lint-uppercase-variables.rs:10:5

tests/ui/or-patterns/binding-typo-2.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ fn foo(x: (Lol, Lol)) {
1616
//~| HELP: you might have meant to use the similarly named previously used binding `Bar`
1717
//~| NOTE: pattern doesn't bind `Ban`
1818
//~| NOTE: variable not in all patterns
19-
//~| ERROR: variable `Ban` is assigned to, but never used
20-
//~| NOTE: consider using `_Ban` instead
19+
//~| ERROR: unused variable: `Ban`
20+
//~| HELP: if this is intentional, prefix it with an underscore
2121
//~| HELP: you might have meant to pattern match on the similarly named
2222
_ => {}
2323
}
@@ -27,8 +27,8 @@ fn foo(x: (Lol, Lol)) {
2727
//~| HELP: you might have meant to use the similarly named unit variant `Bar`
2828
//~| NOTE: pattern doesn't bind `Ban`
2929
//~| NOTE: variable not in all patterns
30-
//~| ERROR: variable `Ban` is assigned to, but never used
31-
//~| NOTE: consider using `_Ban` instead
30+
//~| ERROR: unused variable: `Ban`
31+
//~| HELP: if this is intentional, prefix it with an underscore
3232
//~| HELP: you might have meant to pattern match on the similarly named
3333
_ => {}
3434
}
@@ -73,8 +73,8 @@ fn bar(x: (Lol, Lol)) {
7373
//~| HELP: you might have meant to use the similarly named constant `Bat`
7474
//~| NOTE: pattern doesn't bind `Ban`
7575
//~| NOTE: variable not in all patterns
76-
//~| ERROR: variable `Ban` is assigned to, but never used
77-
//~| NOTE: consider using `_Ban` instead
76+
//~| ERROR: unused variable: `Ban`
77+
//~| HELP: if this is intentional, prefix it with an underscore
7878
//~| HELP: you might have meant to pattern match on the similarly named
7979
_ => {}
8080
}
@@ -89,8 +89,8 @@ fn baz(x: (Lol, Lol)) {
8989
//~| HELP: you might have meant to use the similarly named constant `Bat`
9090
//~| NOTE: pattern doesn't bind `Ban`
9191
//~| NOTE: variable not in all patterns
92-
//~| ERROR: variable `Ban` is assigned to, but never used
93-
//~| NOTE: consider using `_Ban` instead
92+
//~| ERROR: unused variable: `Ban`
93+
//~| HELP: if this is intentional, prefix it with an underscore
9494
//~| HELP: you might have meant to pattern match on the similarly named
9595
_ => {}
9696
}

0 commit comments

Comments
 (0)