Skip to content

Commit

Permalink
Rollup merge of rust-lang#83004 - sunjay:field-never-read-issue-81658…
Browse files Browse the repository at this point in the history
…, 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.
  • Loading branch information
RalfJung authored May 5, 2021
2 parents 2d11e25 + 0ba2c6a commit c0130af
Show file tree
Hide file tree
Showing 31 changed files with 429 additions and 81 deletions.
61 changes: 52 additions & 9 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// from live codes are live, and everything else is dead.

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
Expand All @@ -15,7 +16,7 @@ use rustc_middle::middle::privacy;
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::lint;

use rustc_span::symbol::{sym, Symbol};
use rustc_span::symbol::{sym, Ident, Symbol};

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

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ExtraNote {
/// Use this to provide some examples in the diagnostic of potential other purposes for a value
/// or field that is dead code
OtherPurposeExamples,
}

struct DeadVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
live_symbols: FxHashSet<hir::HirId>,
Expand Down Expand Up @@ -572,14 +580,42 @@ impl DeadVisitor<'tcx> {
&mut self,
id: hir::HirId,
span: rustc_span::Span,
name: Symbol,
name: Ident,
participle: &str,
extra_note: Option<ExtraNote>,
) {
if !name.as_str().starts_with('_') {
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
let def_id = self.tcx.hir().local_def_id(id);
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit()

let prefixed = vec![(name.span, format!("_{}", name))];

let mut diag =
lint.build(&format!("{} is never {}: `{}`", descr, participle, name));

diag.multipart_suggestion(
"if this is intentional, prefix it with an underscore",
prefixed,
Applicability::MachineApplicable,
);

let mut note = format!(
"the leading underscore signals that this {} serves some other \
purpose even if it isn't used in a way that we can detect.",
descr,
);
if matches!(extra_note, Some(ExtraNote::OtherPurposeExamples)) {
note += " (e.g. for its effect when dropped or in foreign code)";
}

diag.note(&note);

// Force the note we added to the front, before any other subdiagnostics
// added in lint.build(...)
diag.children.rotate_right(1);

diag.emit()
});
}
}
Expand Down Expand Up @@ -625,7 +661,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
hir::ItemKind::Struct(..) => "constructed", // Issue #52325
_ => "used",
};
self.warn_dead_code(item.hir_id(), span, item.ident.name, participle);
self.warn_dead_code(item.hir_id(), span, item.ident, participle, None);
} else {
// Only continue if we didn't warn
intravisit::walk_item(self, item);
Expand All @@ -639,22 +675,28 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
id: hir::HirId,
) {
if self.should_warn_about_variant(&variant) {
self.warn_dead_code(variant.id, variant.span, variant.ident.name, "constructed");
self.warn_dead_code(variant.id, variant.span, variant.ident, "constructed", None);
} else {
intravisit::walk_variant(self, variant, g, id);
}
}

fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) {
if self.should_warn_about_foreign_item(fi) {
self.warn_dead_code(fi.hir_id(), fi.span, fi.ident.name, "used");
self.warn_dead_code(fi.hir_id(), fi.span, fi.ident, "used", None);
}
intravisit::walk_foreign_item(self, fi);
}

fn visit_field_def(&mut self, field: &'tcx hir::FieldDef<'tcx>) {
if self.should_warn_about_field(&field) {
self.warn_dead_code(field.hir_id, field.span, field.ident.name, "read");
self.warn_dead_code(
field.hir_id,
field.span,
field.ident,
"read",
Some(ExtraNote::OtherPurposeExamples),
);
}
intravisit::walk_field_def(self, field);
}
Expand All @@ -666,8 +708,9 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
self.warn_dead_code(
impl_item.hir_id(),
impl_item.span,
impl_item.ident.name,
impl_item.ident,
"used",
None,
);
}
self.visit_nested_body(body_id)
Expand All @@ -685,7 +728,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
} else {
impl_item.ident.span
};
self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident.name, "used");
self.warn_dead_code(impl_item.hir_id(), span, impl_item.ident, "used", None);
}
self.visit_nested_body(body_id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ error: associated constant is never used: `BAR`
--> $DIR/associated-const-dead-code.rs:6:5
|
LL | const BAR: u32 = 1;
| ^^^^^^^^^^^^^^^^^^^
| ^^^^^^---^^^^^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_BAR`
|
= 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.
note: the lint level is defined here
--> $DIR/associated-const-dead-code.rs:1:9
|
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/derive-uninhabited-enum-38885.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ warning: variant is never constructed: `Void`
--> $DIR/derive-uninhabited-enum-38885.rs:13:5
|
LL | Void(Void),
| ^^^^^^^^^^
| ----^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_Void`
|
= 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.
= note: `-W dead-code` implied by `-W unused`

warning: 1 warning emitted
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/issues/issue-37515.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ warning: type alias is never used: `Z`
--> $DIR/issue-37515.rs:5:1
|
LL | type Z = dyn for<'x> Send;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^-^^^^^^^^^^^^^^^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_Z`
|
= 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.
note: the lint level is defined here
--> $DIR/issue-37515.rs:3:9
|
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/lint/dead-code/basic.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ error: function is never used: `foo`
--> $DIR/basic.rs:4:4
|
LL | fn foo() {
| ^^^
| ^^^ help: if this is intentional, prefix it with an underscore: `_foo`
|
= 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.
note: the lint level is defined here
--> $DIR/basic.rs:1:9
|
Expand Down
7 changes: 5 additions & 2 deletions src/test/ui/lint/dead-code/const-and-self.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ warning: variant is never constructed: `B`
--> $DIR/const-and-self.rs:33:5
|
LL | B,
| ^
| ^ help: if this is intentional, prefix it with an underscore: `_B`
|
= 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.
note: the lint level is defined here
--> $DIR/const-and-self.rs:3:9
|
Expand All @@ -14,7 +15,9 @@ warning: variant is never constructed: `C`
--> $DIR/const-and-self.rs:34:5
|
LL | C,
| ^
| ^ help: if this is intentional, prefix it with an underscore: `_C`
|
= 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.

warning: 2 warnings emitted

42 changes: 42 additions & 0 deletions src/test/ui/lint/dead-code/drop-only-field-issue-81658.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! The field `guard` is never used directly, but it is still useful for its side effect when
//! dropped. Since rustc doesn't consider a `Drop` impl as a use, we want to make sure we at least
//! produce a helpful diagnostic that points the user to what they can do if they indeed intended to
//! have a field that is only used for its `Drop` side effect.
//!
//! Issue: https://github.com/rust-lang/rust/issues/81658

#![deny(dead_code)]

use std::sync::{Mutex, MutexGuard};

/// Holds a locked value until it is dropped
pub struct Locked<'a, T> {
// Field is kept for its affect when dropped, but otherwise unused
guard: MutexGuard<'a, T>, //~ ERROR field is never read
}

impl<'a, T> Locked<'a, T> {
pub fn new(value: &'a Mutex<T>) -> Self {
Self {
guard: value.lock().unwrap(),
}
}
}

fn main() {
let items = Mutex::new(vec![1, 2, 3]);

// Hold a lock on items while doing something else
let result = {
// The lock will be released at the end of this scope
let _lock = Locked::new(&items);

do_something_else()
};

println!("{}", result);
}

fn do_something_else() -> i32 {
1 + 1
}
17 changes: 17 additions & 0 deletions src/test/ui/lint/dead-code/drop-only-field-issue-81658.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: field is never read: `guard`
--> $DIR/drop-only-field-issue-81658.rs:15:5
|
LL | guard: MutexGuard<'a, T>,
| -----^^^^^^^^^^^^^^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_guard`
|
= 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)
note: the lint level is defined here
--> $DIR/drop-only-field-issue-81658.rs:8:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^

error: aborting due to previous error

3 changes: 2 additions & 1 deletion src/test/ui/lint/dead-code/empty-unused-enum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ error: enum is never used: `E`
--> $DIR/empty-unused-enum.rs:3:6
|
LL | enum E {}
| ^
| ^ help: if this is intentional, prefix it with an underscore: `_E`
|
= 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.
note: the lint level is defined here
--> $DIR/empty-unused-enum.rs:1:9
|
Expand Down
50 changes: 50 additions & 0 deletions src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! The field `items` is being "used" by FFI (implicitly through pointers). However, since rustc
//! doesn't know how to detect that, we produce a message that says the field is unused. This can
//! cause some confusion and we want to make sure our diagnostics help as much as they can.
//!
//! Issue: https://github.com/rust-lang/rust/issues/81658

#![deny(dead_code)]

/// A struct for holding on to data while it is being used in our FFI code
pub struct FFIData<T> {
/// These values cannot be dropped while the pointers to each item
/// are still in use
items: Option<Vec<T>>, //~ ERROR field is never read
}

impl<T> FFIData<T> {
pub fn new() -> Self {
Self {items: None}
}

/// Load items into this type and return pointers to each item that can
/// be passed to FFI
pub fn load(&mut self, items: Vec<T>) -> Vec<*const T> {
let ptrs = items.iter().map(|item| item as *const _).collect();

self.items = Some(items);

ptrs
}
}

extern {
/// The FFI code that uses items
fn process_item(item: *const i32);
}

fn main() {
// Data cannot be dropped until the end of this scope or else the items
// will be dropped before they are processed
let mut data = FFIData::new();

let ptrs = data.load(vec![1, 2, 3, 4, 5]);

for ptr in ptrs {
// Safety: This pointer is valid as long as the arena is in scope
unsafe { process_item(ptr); }
}

// Items will be safely freed at the end of this scope
}
17 changes: 17 additions & 0 deletions src/test/ui/lint/dead-code/field-used-in-ffi-issue-81658.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: field is never read: `items`
--> $DIR/field-used-in-ffi-issue-81658.rs:13:5
|
LL | items: Option<Vec<T>>,
| -----^^^^^^^^^^^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_items`
|
= 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)
note: the lint level is defined here
--> $DIR/field-used-in-ffi-issue-81658.rs:7:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^

error: aborting due to previous error

5 changes: 4 additions & 1 deletion src/test/ui/lint/dead-code/impl-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ error: type alias is never used: `Unused`
--> $DIR/impl-trait.rs:12:1
|
LL | type Unused = ();
| ^^^^^^^^^^^^^^^^^
| ^^^^^------^^^^^^
| |
| help: if this is intentional, prefix it with an underscore: `_Unused`
|
= 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.
note: the lint level is defined here
--> $DIR/impl-trait.rs:1:9
|
Expand Down
Loading

0 comments on commit c0130af

Please sign in to comment.