Skip to content

Commit df4d22a

Browse files
authored
Rollup merge of rust-lang#83004 - sunjay:field-never-read-issue-81658, r=pnkfelix
Improve diagnostic for when field is never read Related to (but does not close) rust-lang#81658 This completes the first step of `@pnkfelix's` [mentoring instructions](rust-lang#81658 (comment)) but does not actually improve the diagnostics (yet!). The two tests are heavily reduced versions of code from the original bug report. I've confirmed that the reduced `field-used-in-ffi` test [fails on nightly](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=f0862c89ddca028c55c20a5ed05e679a) but [passes on stable](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f0862c89ddca028c55c20a5ed05e679a). This confirms that the regression is reproduced correctly. The `drop-only-field` test is a case that `@pnkfelix` mentioned in his mentoring instructions. It is not a regression, but will come in handy when we make the diagnostic smarter by looking at whether the field type implements `Drop`. Per the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/tests/adding.html), each test includes a comment summarizing what it is about.
2 parents c20c921 + 0ba2c6a commit df4d22a

31 files changed

+429
-81
lines changed

compiler/rustc_passes/src/dead.rs

+52-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// from live codes are live, and everything else is dead.
44

55
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
6+
use rustc_errors::Applicability;
67
use rustc_hir as hir;
78
use rustc_hir::def::{CtorOf, DefKind, Res};
89
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -15,7 +16,7 @@ use rustc_middle::middle::privacy;
1516
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
1617
use rustc_session::lint;
1718

18-
use rustc_span::symbol::{sym, Symbol};
19+
use rustc_span::symbol::{sym, Ident, Symbol};
1920

2021
// Any local node that may call something in its body block should be
2122
// explored. For example, if it's a live Node::Item that is a
@@ -507,6 +508,13 @@ fn find_live<'tcx>(
507508
symbol_visitor.live_symbols
508509
}
509510

511+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
512+
enum ExtraNote {
513+
/// Use this to provide some examples in the diagnostic of potential other purposes for a value
514+
/// or field that is dead code
515+
OtherPurposeExamples,
516+
}
517+
510518
struct DeadVisitor<'tcx> {
511519
tcx: TyCtxt<'tcx>,
512520
live_symbols: FxHashSet<hir::HirId>,
@@ -572,14 +580,42 @@ impl DeadVisitor<'tcx> {
572580
&mut self,
573581
id: hir::HirId,
574582
span: rustc_span::Span,
575-
name: Symbol,
583+
name: Ident,
576584
participle: &str,
585+
extra_note: Option<ExtraNote>,
577586
) {
578587
if !name.as_str().starts_with('_') {
579588
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
580589
let def_id = self.tcx.hir().local_def_id(id);
581590
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
582-
lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit()
591+
592+
let prefixed = vec![(name.span, format!("_{}", name))];
593+
594+
let mut diag =
595+
lint.build(&format!("{} is never {}: `{}`", descr, participle, name));
596+
597+
diag.multipart_suggestion(
598+
"if this is intentional, prefix it with an underscore",
599+
prefixed,
600+
Applicability::MachineApplicable,
601+
);
602+
603+
let mut note = format!(
604+
"the leading underscore signals that this {} serves some other \
605+
purpose even if it isn't used in a way that we can detect.",
606+
descr,
607+
);
608+
if matches!(extra_note, Some(ExtraNote::OtherPurposeExamples)) {
609+
note += " (e.g. for its effect when dropped or in foreign code)";
610+
}
611+
612+
diag.note(&note);
613+
614+
// Force the note we added to the front, before any other subdiagnostics
615+
// added in lint.build(...)
616+
diag.children.rotate_right(1);
617+
618+
diag.emit()
583619
});
584620
}
585621
}
@@ -625,7 +661,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
625661
hir::ItemKind::Struct(..) => "constructed", // Issue #52325
626662
_ => "used",
627663
};
628-
self.warn_dead_code(item.hir_id(), span, item.ident.name, participle);
664+
self.warn_dead_code(item.hir_id(), span, item.ident, participle, None);
629665
} else {
630666
// Only continue if we didn't warn
631667
intravisit::walk_item(self, item);
@@ -639,22 +675,28 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
639675
id: hir::HirId,
640676
) {
641677
if self.should_warn_about_variant(&variant) {
642-
self.warn_dead_code(variant.id, variant.span, variant.ident.name, "constructed");
678+
self.warn_dead_code(variant.id, variant.span, variant.ident, "constructed", None);
643679
} else {
644680
intravisit::walk_variant(self, variant, g, id);
645681
}
646682
}
647683

648684
fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) {
649685
if self.should_warn_about_foreign_item(fi) {
650-
self.warn_dead_code(fi.hir_id(), fi.span, fi.ident.name, "used");
686+
self.warn_dead_code(fi.hir_id(), fi.span, fi.ident, "used", None);
651687
}
652688
intravisit::walk_foreign_item(self, fi);
653689
}
654690

655691
fn visit_field_def(&mut self, field: &'tcx hir::FieldDef<'tcx>) {
656692
if self.should_warn_about_field(&field) {
657-
self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read");
693+
self.warn_dead_code(
694+
field.hir_id,
695+
field.span,
696+
field.ident,
697+
"read",
698+
Some(ExtraNote::OtherPurposeExamples),
699+
);
658700
}
659701
intravisit::walk_field_def(self, field);
660702
}
@@ -666,8 +708,9 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
666708
self.warn_dead_code(
667709
impl_item.hir_id(),
668710
impl_item.span,
669-
impl_item.ident.name,
711+
impl_item.ident,
670712
"used",
713+
None,
671714
);
672715
}
673716
self.visit_nested_body(body_id)
@@ -685,7 +728,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
685728
} else {
686729
impl_item.ident.span
687730
};
688-
self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident.name, "used");
731+
self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident, "used", None);
689732
}
690733
self.visit_nested_body(body_id)
691734
}

src/test/ui/associated-consts/associated-const-dead-code.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ error: associated constant is never used: `BAR`
22
--> $DIR/associated-const-dead-code.rs:6:5
33
|
44
LL | const BAR: u32 = 1;
5-
| ^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^---^^^^^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_BAR`
68
|
9+
= note: the leading underscore signals that this associated constant serves some other purpose even if it isn't used in a way that we can detect.
710
note: the lint level is defined here
811
--> $DIR/associated-const-dead-code.rs:1:9
912
|

src/test/ui/derive-uninhabited-enum-38885.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ warning: variant is never constructed: `Void`
22
--> $DIR/derive-uninhabited-enum-38885.rs:13:5
33
|
44
LL | Void(Void),
5-
| ^^^^^^^^^^
5+
| ----^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_Void`
68
|
9+
= note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect.
710
= note: `-W dead-code` implied by `-W unused`
811

912
warning: 1 warning emitted

src/test/ui/issues/issue-37515.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ warning: type alias is never used: `Z`
22
--> $DIR/issue-37515.rs:5:1
33
|
44
LL | type Z = dyn for<'x> Send;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^-^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_Z`
68
|
9+
= note: the leading underscore signals that this type alias serves some other purpose even if it isn't used in a way that we can detect.
710
note: the lint level is defined here
811
--> $DIR/issue-37515.rs:3:9
912
|

src/test/ui/lint/dead-code/basic.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error: function is never used: `foo`
22
--> $DIR/basic.rs:4:4
33
|
44
LL | fn foo() {
5-
| ^^^
5+
| ^^^ help: if this is intentional, prefix it with an underscore: `_foo`
66
|
7+
= note: the leading underscore signals that this function serves some other purpose even if it isn't used in a way that we can detect.
78
note: the lint level is defined here
89
--> $DIR/basic.rs:1:9
910
|

src/test/ui/lint/dead-code/const-and-self.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ warning: variant is never constructed: `B`
22
--> $DIR/const-and-self.rs:33:5
33
|
44
LL | B,
5-
| ^
5+
| ^ help: if this is intentional, prefix it with an underscore: `_B`
66
|
7+
= note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect.
78
note: the lint level is defined here
89
--> $DIR/const-and-self.rs:3:9
910
|
@@ -14,7 +15,9 @@ warning: variant is never constructed: `C`
1415
--> $DIR/const-and-self.rs:34:5
1516
|
1617
LL | C,
17-
| ^
18+
| ^ help: if this is intentional, prefix it with an underscore: `_C`
19+
|
20+
= note: the leading underscore signals that this variant serves some other purpose even if it isn't used in a way that we can detect.
1821

1922
warning: 2 warnings emitted
2023

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//! The field `guard` is never used directly, but it is still useful for its side effect when
2+
//! dropped. Since rustc doesn't consider a `Drop` impl as a use, we want to make sure we at least
3+
//! produce a helpful diagnostic that points the user to what they can do if they indeed intended to
4+
//! have a field that is only used for its `Drop` side effect.
5+
//!
6+
//! Issue: https://github.com/rust-lang/rust/issues/81658
7+
8+
#![deny(dead_code)]
9+
10+
use std::sync::{Mutex, MutexGuard};
11+
12+
/// Holds a locked value until it is dropped
13+
pub struct Locked<'a, T> {
14+
// Field is kept for its affect when dropped, but otherwise unused
15+
guard: MutexGuard<'a, T>, //~ ERROR field is never read
16+
}
17+
18+
impl<'a, T> Locked<'a, T> {
19+
pub fn new(value: &'a Mutex<T>) -> Self {
20+
Self {
21+
guard: value.lock().unwrap(),
22+
}
23+
}
24+
}
25+
26+
fn main() {
27+
let items = Mutex::new(vec![1, 2, 3]);
28+
29+
// Hold a lock on items while doing something else
30+
let result = {
31+
// The lock will be released at the end of this scope
32+
let _lock = Locked::new(&items);
33+
34+
do_something_else()
35+
};
36+
37+
println!("{}", result);
38+
}
39+
40+
fn do_something_else() -> i32 {
41+
1 + 1
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: field is never read: `guard`
2+
--> $DIR/drop-only-field-issue-81658.rs:15:5
3+
|
4+
LL | guard: MutexGuard<'a, T>,
5+
| -----^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_guard`
8+
|
9+
= note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code)
10+
note: the lint level is defined here
11+
--> $DIR/drop-only-field-issue-81658.rs:8:9
12+
|
13+
LL | #![deny(dead_code)]
14+
| ^^^^^^^^^
15+
16+
error: aborting due to previous error
17+

src/test/ui/lint/dead-code/empty-unused-enum.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error: enum is never used: `E`
22
--> $DIR/empty-unused-enum.rs:3:6
33
|
44
LL | enum E {}
5-
| ^
5+
| ^ help: if this is intentional, prefix it with an underscore: `_E`
66
|
7+
= note: the leading underscore signals that this enum serves some other purpose even if it isn't used in a way that we can detect.
78
note: the lint level is defined here
89
--> $DIR/empty-unused-enum.rs:1:9
910
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! The field `items` is being "used" by FFI (implicitly through pointers). However, since rustc
2+
//! doesn't know how to detect that, we produce a message that says the field is unused. This can
3+
//! cause some confusion and we want to make sure our diagnostics help as much as they can.
4+
//!
5+
//! Issue: https://github.com/rust-lang/rust/issues/81658
6+
7+
#![deny(dead_code)]
8+
9+
/// A struct for holding on to data while it is being used in our FFI code
10+
pub struct FFIData<T> {
11+
/// These values cannot be dropped while the pointers to each item
12+
/// are still in use
13+
items: Option<Vec<T>>, //~ ERROR field is never read
14+
}
15+
16+
impl<T> FFIData<T> {
17+
pub fn new() -> Self {
18+
Self {items: None}
19+
}
20+
21+
/// Load items into this type and return pointers to each item that can
22+
/// be passed to FFI
23+
pub fn load(&mut self, items: Vec<T>) -> Vec<*const T> {
24+
let ptrs = items.iter().map(|item| item as *const _).collect();
25+
26+
self.items = Some(items);
27+
28+
ptrs
29+
}
30+
}
31+
32+
extern {
33+
/// The FFI code that uses items
34+
fn process_item(item: *const i32);
35+
}
36+
37+
fn main() {
38+
// Data cannot be dropped until the end of this scope or else the items
39+
// will be dropped before they are processed
40+
let mut data = FFIData::new();
41+
42+
let ptrs = data.load(vec![1, 2, 3, 4, 5]);
43+
44+
for ptr in ptrs {
45+
// Safety: This pointer is valid as long as the arena is in scope
46+
unsafe { process_item(ptr); }
47+
}
48+
49+
// Items will be safely freed at the end of this scope
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: field is never read: `items`
2+
--> $DIR/field-used-in-ffi-issue-81658.rs:13:5
3+
|
4+
LL | items: Option<Vec<T>>,
5+
| -----^^^^^^^^^^^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_items`
8+
|
9+
= note: the leading underscore signals that this field serves some other purpose even if it isn't used in a way that we can detect. (e.g. for its effect when dropped or in foreign code)
10+
note: the lint level is defined here
11+
--> $DIR/field-used-in-ffi-issue-81658.rs:7:9
12+
|
13+
LL | #![deny(dead_code)]
14+
| ^^^^^^^^^
15+
16+
error: aborting due to previous error
17+

src/test/ui/lint/dead-code/impl-trait.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ error: type alias is never used: `Unused`
22
--> $DIR/impl-trait.rs:12:1
33
|
44
LL | type Unused = ();
5-
| ^^^^^^^^^^^^^^^^^
5+
| ^^^^^------^^^^^^
6+
| |
7+
| help: if this is intentional, prefix it with an underscore: `_Unused`
68
|
9+
= note: the leading underscore signals that this type alias serves some other purpose even if it isn't used in a way that we can detect.
710
note: the lint level is defined here
811
--> $DIR/impl-trait.rs:1:9
912
|

0 commit comments

Comments
 (0)