Skip to content

Commit 612bb78

Browse files
authored
Rollup merge of #103397 - crlf0710:port_dead_code_lint, r=davidtwco
Port `dead_code` lints to be translatable. This adds an additional comma to lists with three or more items, to be consistent with list formatters like `icu4x`. r? `@davidtwco`
2 parents dabb6c6 + a777c46 commit 612bb78

11 files changed

+215
-99
lines changed

compiler/rustc_error_messages/locales/en-US/passes.ftl

+33
Original file line numberDiff line numberDiff line change
@@ -665,3 +665,36 @@ passes_missing_const_err =
665665
attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
666666
.help = make the function or method const
667667
.label = attribute specified here
668+
669+
passes_dead_codes =
670+
{ $multiple ->
671+
*[true] multiple {$descr}s are
672+
[false] { $num ->
673+
[one] {$descr} {$name_list} is
674+
*[other] {$descr}s {$name_list} are
675+
}
676+
} never {$participle}
677+
678+
passes_change_fields_to_be_of_unit_type =
679+
consider changing the { $num ->
680+
[one] field
681+
*[other] fields
682+
} to be of unit type to suppress this warning while preserving the field numbering, or remove the { $num ->
683+
[one] field
684+
*[other] fields
685+
}
686+
687+
passes_parent_info =
688+
{$num ->
689+
[one] {$descr}
690+
*[other] {$descr}s
691+
} in this {$parent_descr}
692+
693+
passes_ignored_derived_impls =
694+
`{$name}` has {$trait_list_len ->
695+
[one] a derived impl
696+
*[other] derived impls
697+
} for the {$trait_list_len ->
698+
[one] trait {$trait_list}, but this is
699+
*[other] traits {$trait_list}, but these are
700+
} intentionally ignored during dead code analysis

compiler/rustc_errors/src/diagnostic_impls.rs

+32
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_target::abi::TargetDataLayoutErrors;
1111
use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
1212
use std::borrow::Cow;
1313
use std::fmt;
14+
use std::fmt::Write;
1415
use std::num::ParseIntError;
1516
use std::path::{Path, PathBuf};
1617

@@ -170,6 +171,37 @@ impl IntoDiagnosticArg for Level {
170171
}
171172
}
172173

174+
#[derive(Clone)]
175+
pub struct DiagnosticSymbolList(Vec<Symbol>);
176+
177+
impl From<Vec<Symbol>> for DiagnosticSymbolList {
178+
fn from(v: Vec<Symbol>) -> Self {
179+
DiagnosticSymbolList(v)
180+
}
181+
}
182+
183+
impl IntoDiagnosticArg for DiagnosticSymbolList {
184+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
185+
// FIXME: replace the logic here with a real list formatter
186+
let symbols = match &self.0[..] {
187+
[symbol] => format!("`{symbol}`"),
188+
[symbol, last] => {
189+
format!("`{symbol}` and `{last}`",)
190+
}
191+
[symbols @ .., last] => {
192+
let mut result = String::new();
193+
for symbol in symbols {
194+
write!(result, "`{symbol}`, ").unwrap();
195+
}
196+
write!(result, "and `{last}`").unwrap();
197+
result
198+
}
199+
[] => unreachable!(),
200+
};
201+
DiagnosticArgValue::Str(Cow::Owned(symbols))
202+
}
203+
}
204+
173205
impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
174206
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
175207
let mut diag;

compiler/rustc_errors/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ pub use diagnostic::{
376376
DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
377377
};
378378
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
379-
pub use diagnostic_impls::DiagnosticArgFromDisplay;
379+
pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList};
380380
use std::backtrace::Backtrace;
381381

382382
/// A handler deals with errors and other compiler output.

compiler/rustc_passes/src/dead.rs

+84-91
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use itertools::Itertools;
66
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7-
use rustc_errors::{pluralize, Applicability, MultiSpan};
7+
use rustc_errors::MultiSpan;
88
use rustc_hir as hir;
99
use rustc_hir::def::{CtorOf, DefKind, Res};
1010
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -18,7 +18,10 @@ use rustc_session::lint;
1818
use rustc_span::symbol::{sym, Symbol};
1919
use std::mem;
2020

21-
use crate::errors::UselessAssignment;
21+
use crate::errors::{
22+
ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo,
23+
UselessAssignment,
24+
};
2225

2326
// Any local node that may call something in its body block should be
2427
// explored. For example, if it's a live Node::Item that is a
@@ -693,99 +696,89 @@ impl<'tcx> DeadVisitor<'tcx> {
693696
parent_item: Option<LocalDefId>,
694697
is_positional: bool,
695698
) {
696-
if let Some(&first_id) = dead_codes.first() {
697-
let tcx = self.tcx;
698-
let names: Vec<_> = dead_codes
699-
.iter()
700-
.map(|&def_id| tcx.item_name(def_id.to_def_id()).to_string())
701-
.collect();
702-
let spans: Vec<_> = dead_codes
703-
.iter()
704-
.map(|&def_id| match tcx.def_ident_span(def_id) {
705-
Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()),
706-
None => tcx.def_span(def_id),
699+
let Some(&first_id) = dead_codes.first() else {
700+
return;
701+
};
702+
let tcx = self.tcx;
703+
let names: Vec<_> =
704+
dead_codes.iter().map(|&def_id| tcx.item_name(def_id.to_def_id())).collect();
705+
let spans: Vec<_> = dead_codes
706+
.iter()
707+
.map(|&def_id| match tcx.def_ident_span(def_id) {
708+
Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()),
709+
None => tcx.def_span(def_id),
710+
})
711+
.collect();
712+
713+
let descr = tcx.def_kind(first_id).descr(first_id.to_def_id());
714+
let num = dead_codes.len();
715+
let multiple = num > 6;
716+
let name_list = names.into();
717+
718+
let lint = if is_positional {
719+
lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS
720+
} else {
721+
lint::builtin::DEAD_CODE
722+
};
723+
724+
let parent_info = if let Some(parent_item) = parent_item {
725+
let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id());
726+
Some(ParentInfo {
727+
num,
728+
descr,
729+
parent_descr,
730+
span: tcx.def_ident_span(parent_item).unwrap(),
731+
})
732+
} else {
733+
None
734+
};
735+
736+
let encl_def_id = parent_item.unwrap_or(first_id);
737+
let ignored_derived_impls =
738+
if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
739+
let trait_list = ign_traits
740+
.iter()
741+
.map(|(trait_id, _)| self.tcx.item_name(*trait_id))
742+
.collect::<Vec<_>>();
743+
let trait_list_len = trait_list.len();
744+
Some(IgnoredDerivedImpls {
745+
name: self.tcx.item_name(encl_def_id.to_def_id()),
746+
trait_list: trait_list.into(),
747+
trait_list_len,
707748
})
708-
.collect();
709-
710-
let descr = tcx.def_kind(first_id).descr(first_id.to_def_id());
711-
let span_len = dead_codes.len();
712-
let names = match &names[..] {
713-
_ if span_len > 6 => String::new(),
714-
[name] => format!("`{name}` "),
715-
[names @ .., last] => {
716-
format!(
717-
"{} and `{last}` ",
718-
names.iter().map(|name| format!("`{name}`")).join(", ")
719-
)
720-
}
721-
[] => unreachable!(),
749+
} else {
750+
None
722751
};
723-
let msg = format!(
724-
"{these}{descr}{s} {names}{are} never {participle}",
725-
these = if span_len > 6 { "multiple " } else { "" },
726-
s = pluralize!(span_len),
727-
are = pluralize!("is", span_len),
728-
);
729-
730-
tcx.struct_span_lint_hir(
731-
if is_positional {
732-
lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS
733-
} else {
734-
lint::builtin::DEAD_CODE
735-
},
736-
tcx.hir().local_def_id_to_hir_id(first_id),
737-
MultiSpan::from_spans(spans.clone()),
738-
msg,
739-
|err| {
740-
if is_positional {
741-
err.multipart_suggestion(
742-
&format!(
743-
"consider changing the field{s} to be of unit type to \
744-
suppress this warning while preserving the field \
745-
numbering, or remove the field{s}",
746-
s = pluralize!(span_len)
747-
),
748-
spans.iter().map(|sp| (*sp, "()".to_string())).collect(),
749-
// "HasPlaceholders" because applying this fix by itself isn't
750-
// enough: All constructor calls have to be adjusted as well
751-
Applicability::HasPlaceholders,
752-
);
753-
}
754752

755-
if let Some(parent_item) = parent_item {
756-
let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id());
757-
err.span_label(
758-
tcx.def_ident_span(parent_item).unwrap(),
759-
format!("{descr}{s} in this {parent_descr}", s = pluralize!(span_len)),
760-
);
761-
}
753+
let diag = if is_positional {
754+
MultipleDeadCodes::UnusedTupleStructFields {
755+
multiple,
756+
num,
757+
descr,
758+
participle,
759+
name_list,
760+
change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() },
761+
parent_info,
762+
ignored_derived_impls,
763+
}
764+
} else {
765+
MultipleDeadCodes::DeadCodes {
766+
multiple,
767+
num,
768+
descr,
769+
participle,
770+
name_list,
771+
parent_info,
772+
ignored_derived_impls,
773+
}
774+
};
762775

763-
let encl_def_id = parent_item.unwrap_or(first_id);
764-
if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
765-
let traits_str = ign_traits
766-
.iter()
767-
.map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id)))
768-
.collect::<Vec<_>>()
769-
.join(" and ");
770-
let plural_s = pluralize!(ign_traits.len());
771-
let article = if ign_traits.len() > 1 { "" } else { "a " };
772-
let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" };
773-
let msg = format!(
774-
"`{}` has {}derived impl{} for the trait{} {}, but {} \
775-
intentionally ignored during dead code analysis",
776-
self.tcx.item_name(encl_def_id.to_def_id()),
777-
article,
778-
plural_s,
779-
plural_s,
780-
traits_str,
781-
is_are
782-
);
783-
err.note(&msg);
784-
}
785-
err
786-
},
787-
);
788-
}
776+
self.tcx.emit_spanned_lint(
777+
lint,
778+
tcx.hir().local_def_id_to_hir_id(first_id),
779+
MultiSpan::from_spans(spans.clone()),
780+
diag,
781+
);
789782
}
790783

791784
fn warn_dead_fields_and_variants(

compiler/rustc_passes/src/errors.rs

+59-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use std::{
44
};
55

66
use rustc_ast::Label;
7-
use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
7+
use rustc_errors::{
8+
error_code, Applicability, DiagnosticSymbolList, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
9+
};
810
use rustc_hir::{self as hir, ExprKind, Target};
911
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
1012
use rustc_middle::ty::{MainDefinition, Ty};
@@ -1446,3 +1448,59 @@ pub struct MissingConstErr {
14461448
#[label]
14471449
pub const_span: Span,
14481450
}
1451+
1452+
#[derive(LintDiagnostic)]
1453+
pub enum MultipleDeadCodes<'tcx> {
1454+
#[diag(passes_dead_codes)]
1455+
DeadCodes {
1456+
multiple: bool,
1457+
num: usize,
1458+
descr: &'tcx str,
1459+
participle: &'tcx str,
1460+
name_list: DiagnosticSymbolList,
1461+
#[subdiagnostic]
1462+
parent_info: Option<ParentInfo<'tcx>>,
1463+
#[subdiagnostic]
1464+
ignored_derived_impls: Option<IgnoredDerivedImpls>,
1465+
},
1466+
#[diag(passes_dead_codes)]
1467+
UnusedTupleStructFields {
1468+
multiple: bool,
1469+
num: usize,
1470+
descr: &'tcx str,
1471+
participle: &'tcx str,
1472+
name_list: DiagnosticSymbolList,
1473+
#[subdiagnostic]
1474+
change_fields_suggestion: ChangeFieldsToBeOfUnitType,
1475+
#[subdiagnostic]
1476+
parent_info: Option<ParentInfo<'tcx>>,
1477+
#[subdiagnostic]
1478+
ignored_derived_impls: Option<IgnoredDerivedImpls>,
1479+
},
1480+
}
1481+
1482+
#[derive(Subdiagnostic)]
1483+
#[label(passes_parent_info)]
1484+
pub struct ParentInfo<'tcx> {
1485+
pub num: usize,
1486+
pub descr: &'tcx str,
1487+
pub parent_descr: &'tcx str,
1488+
#[primary_span]
1489+
pub span: Span,
1490+
}
1491+
1492+
#[derive(Subdiagnostic)]
1493+
#[note(passes_ignored_derived_impls)]
1494+
pub struct IgnoredDerivedImpls {
1495+
pub name: Symbol,
1496+
pub trait_list: DiagnosticSymbolList,
1497+
pub trait_list_len: usize,
1498+
}
1499+
1500+
#[derive(Subdiagnostic)]
1501+
#[multipart_suggestion(passes_change_fields_to_be_of_unit_type, applicability = "has-placeholders")]
1502+
pub struct ChangeFieldsToBeOfUnitType {
1503+
pub num: usize,
1504+
#[suggestion_part(code = "()")]
1505+
pub spans: Vec<Span>,
1506+
}

src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#[derive(Debug)]
44
pub struct Whatever {
55
pub field0: (),
6-
field1: (), //~ ERROR fields `field1`, `field2`, `field3` and `field4` are never read
6+
field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
77
field2: (),
88
field3: (),
99
field4: (),

src/test/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: fields `field1`, `field2`, `field3` and `field4` are never read
1+
error: fields `field1`, `field2`, `field3`, and `field4` are never read
22
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
33
|
44
LL | pub struct Whatever {

src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct Bar {
77
b: usize, //~ ERROR field `b` is never read
88
#[deny(dead_code)]
99
c: usize, //~ ERROR fields `c` and `e` are never read
10-
d: usize, //~ WARN fields `d`, `f` and `g` are never read
10+
d: usize, //~ WARN fields `d`, `f`, and `g` are never read
1111
#[deny(dead_code)]
1212
e: usize,
1313
f: usize,

src/test/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
warning: fields `d`, `f` and `g` are never read
1+
warning: fields `d`, `f`, and `g` are never read
22
--> $DIR/multiple-dead-codes-in-the-same-struct.rs:10:5
33
|
44
LL | struct Bar {

0 commit comments

Comments
 (0)