attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const`
.help = make the function or method const
.label = attribute specified here
+
+passes_dead_codes =
+ { $multiple ->
+ *[true] multiple {$descr}s are
+ [false] { $num ->
+ [one] {$descr} {$name_list} is
+ *[other] {$descr}s {$name_list} are
+ }
+ } never {$participle}
+
+passes_change_fields_to_be_of_unit_type =
+ consider changing the { $num ->
+ [one] field
+ *[other] fields
+ } to be of unit type to suppress this warning
+ while preserving the field numbering, or remove the { $num ->
+ [one] field
+ *[other] fields
+ }
+
+passes_parent_info =
+ {$num ->
+ [one] {$descr}
+ *[other] {$descr}s
+ } in this {$parent_descr}
+
+passes_ignored_derived_impls =
+ `{$name}` has {$trait_list_len ->
+ [one] a derived impl
+ *[other] derived impls
+ } for the {$trait_list_len ->
+ [one] trait {$trait_list}, but this is
+ *[other] traits {$trait_list}, but these are
+ } intentionally ignored during dead code analysis
use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
use std::borrow::Cow;
use std::fmt;
+use std::fmt::Write;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
}
}
+#[derive(Clone)]
+pub struct DiagnosticSymbolList(Vec<Symbol>);
+
+impl From<Vec<Symbol>> for DiagnosticSymbolList {
+ fn from(v: Vec<Symbol>) -> Self {
+ DiagnosticSymbolList(v)
+ }
+}
+
+impl IntoDiagnosticArg for DiagnosticSymbolList {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ // FIXME: replace the logic here with a real list formatter
+ let symbols = match &self.0[..] {
+ [symbol] => format!("`{symbol}`"),
+ [symbol, last] => {
+ format!("`{symbol}` and `{last}`",)
+ }
+ [symbols @ .., last] => {
+ let mut result = String::new();
+ for symbol in symbols {
+ write!(result, "`{symbol}`, ").unwrap();
+ }
+ write!(result, "and `{last}`").unwrap();
+ result
+ }
+ [] => unreachable!(),
+ };
+ DiagnosticArgValue::Str(Cow::Owned(symbols))
+ }
+}
+
impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
let mut diag;
DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
};
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
-pub use diagnostic_impls::DiagnosticArgFromDisplay;
+pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList};
use std::backtrace::Backtrace;
/// A handler deals with errors and other compiler output.
use itertools::Itertools;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, Applicability, MultiSpan};
+use rustc_errors::MultiSpan;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_span::symbol::{sym, Symbol};
use std::mem;
-use crate::errors::UselessAssignment;
+use crate::errors::{
+ ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo,
+ UselessAssignment,
+};
// 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
parent_item: Option<LocalDefId>,
is_positional: bool,
) {
- if let Some(&first_id) = dead_codes.first() {
- let tcx = self.tcx;
- let names: Vec<_> = dead_codes
- .iter()
- .map(|&def_id| tcx.item_name(def_id.to_def_id()).to_string())
- .collect();
- let spans: Vec<_> = dead_codes
- .iter()
- .map(|&def_id| match tcx.def_ident_span(def_id) {
- Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()),
- None => tcx.def_span(def_id),
+ let Some(&first_id) = dead_codes.first() else {
+ return;
+ };
+ let tcx = self.tcx;
+ let names: Vec<_> =
+ dead_codes.iter().map(|&def_id| tcx.item_name(def_id.to_def_id())).collect();
+ let spans: Vec<_> = dead_codes
+ .iter()
+ .map(|&def_id| match tcx.def_ident_span(def_id) {
+ Some(s) => s.with_ctxt(tcx.def_span(def_id).ctxt()),
+ None => tcx.def_span(def_id),
+ })
+ .collect();
+
+ let descr = tcx.def_kind(first_id).descr(first_id.to_def_id());
+ let num = dead_codes.len();
+ let multiple = num > 6;
+ let name_list = names.into();
+
+ let lint = if is_positional {
+ lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS
+ } else {
+ lint::builtin::DEAD_CODE
+ };
+
+ let parent_info = if let Some(parent_item) = parent_item {
+ let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id());
+ Some(ParentInfo {
+ num,
+ descr,
+ parent_descr,
+ span: tcx.def_ident_span(parent_item).unwrap(),
+ })
+ } else {
+ None
+ };
+
+ let encl_def_id = parent_item.unwrap_or(first_id);
+ let ignored_derived_impls =
+ if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
+ let trait_list = ign_traits
+ .iter()
+ .map(|(trait_id, _)| self.tcx.item_name(*trait_id))
+ .collect::<Vec<_>>();
+ let trait_list_len = trait_list.len();
+ Some(IgnoredDerivedImpls {
+ name: self.tcx.item_name(encl_def_id.to_def_id()),
+ trait_list: trait_list.into(),
+ trait_list_len,
})
- .collect();
-
- let descr = tcx.def_kind(first_id).descr(first_id.to_def_id());
- let span_len = dead_codes.len();
- let names = match &names[..] {
- _ if span_len > 6 => String::new(),
- [name] => format!("`{name}` "),
- [names @ .., last] => {
- format!(
- "{} and `{last}` ",
- names.iter().map(|name| format!("`{name}`")).join(", ")
- )
- }
- [] => unreachable!(),
+ } else {
+ None
};
- let msg = format!(
- "{these}{descr}{s} {names}{are} never {participle}",
- these = if span_len > 6 { "multiple " } else { "" },
- s = pluralize!(span_len),
- are = pluralize!("is", span_len),
- );
-
- tcx.struct_span_lint_hir(
- if is_positional {
- lint::builtin::UNUSED_TUPLE_STRUCT_FIELDS
- } else {
- lint::builtin::DEAD_CODE
- },
- tcx.hir().local_def_id_to_hir_id(first_id),
- MultiSpan::from_spans(spans.clone()),
- msg,
- |err| {
- if is_positional {
- err.multipart_suggestion(
- &format!(
- "consider changing the field{s} to be of unit type to \
- suppress this warning while preserving the field \
- numbering, or remove the field{s}",
- s = pluralize!(span_len)
- ),
- spans.iter().map(|sp| (*sp, "()".to_string())).collect(),
- // "HasPlaceholders" because applying this fix by itself isn't
- // enough: All constructor calls have to be adjusted as well
- Applicability::HasPlaceholders,
- );
- }
- if let Some(parent_item) = parent_item {
- let parent_descr = tcx.def_kind(parent_item).descr(parent_item.to_def_id());
- err.span_label(
- tcx.def_ident_span(parent_item).unwrap(),
- format!("{descr}{s} in this {parent_descr}", s = pluralize!(span_len)),
- );
- }
+ let diag = if is_positional {
+ MultipleDeadCodes::UnusedTupleStructFields {
+ multiple,
+ num,
+ descr,
+ participle,
+ name_list,
+ change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() },
+ parent_info,
+ ignored_derived_impls,
+ }
+ } else {
+ MultipleDeadCodes::DeadCodes {
+ multiple,
+ num,
+ descr,
+ participle,
+ name_list,
+ parent_info,
+ ignored_derived_impls,
+ }
+ };
- let encl_def_id = parent_item.unwrap_or(first_id);
- if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
- let traits_str = ign_traits
- .iter()
- .map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id)))
- .collect::<Vec<_>>()
- .join(" and ");
- let plural_s = pluralize!(ign_traits.len());
- let article = if ign_traits.len() > 1 { "" } else { "a " };
- let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" };
- let msg = format!(
- "`{}` has {}derived impl{} for the trait{} {}, but {} \
- intentionally ignored during dead code analysis",
- self.tcx.item_name(encl_def_id.to_def_id()),
- article,
- plural_s,
- plural_s,
- traits_str,
- is_are
- );
- err.note(&msg);
- }
- err
- },
- );
- }
+ self.tcx.emit_spanned_lint(
+ lint,
+ tcx.hir().local_def_id_to_hir_id(first_id),
+ MultiSpan::from_spans(spans.clone()),
+ diag,
+ );
}
fn warn_dead_fields_and_variants(
};
use rustc_ast::Label;
-use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan};
+use rustc_errors::{
+ error_code, Applicability, DiagnosticSymbolList, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
+};
use rustc_hir::{self as hir, ExprKind, Target};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_errors::{pluralize, AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
+
use crate::lang_items::Duplicate;
#[derive(LintDiagnostic)]
#[label]
pub const_span: Span,
}
+
+#[derive(LintDiagnostic)]
+pub enum MultipleDeadCodes<'tcx> {
+ #[diag(passes_dead_codes)]
+ DeadCodes {
+ multiple: bool,
+ num: usize,
+ descr: &'tcx str,
+ participle: &'tcx str,
+ name_list: DiagnosticSymbolList,
+ #[subdiagnostic]
+ parent_info: Option<ParentInfo<'tcx>>,
+ #[subdiagnostic]
+ ignored_derived_impls: Option<IgnoredDerivedImpls>,
+ },
+ #[diag(passes_dead_codes)]
+ UnusedTupleStructFields {
+ multiple: bool,
+ num: usize,
+ descr: &'tcx str,
+ participle: &'tcx str,
+ name_list: DiagnosticSymbolList,
+ #[subdiagnostic]
+ change_fields_suggestion: ChangeFieldsToBeOfUnitType,
+ #[subdiagnostic]
+ parent_info: Option<ParentInfo<'tcx>>,
+ #[subdiagnostic]
+ ignored_derived_impls: Option<IgnoredDerivedImpls>,
+ },
+}
+
+#[derive(Subdiagnostic)]
+#[label(passes_parent_info)]
+pub struct ParentInfo<'tcx> {
+ pub num: usize,
+ pub descr: &'tcx str,
+ pub parent_descr: &'tcx str,
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[note(passes_ignored_derived_impls)]
+pub struct IgnoredDerivedImpls {
+ pub name: Symbol,
+ pub trait_list: DiagnosticSymbolList,
+ pub trait_list_len: usize,
+}
+
+pub struct ChangeFieldsToBeOfUnitType {
+ pub num: usize,
+ pub spans: Vec<Span>,
+}
+
+// FIXME: Replace this impl with a derive.
+impl AddToDiagnostic for ChangeFieldsToBeOfUnitType {
+ fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+ where
+ F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+ {
+ diag.multipart_suggestion(
+ &format!(
+ "consider changing the field{s} to be of unit type to \
+ suppress this warning while preserving the field \
+ numbering, or remove the field{s}",
+ s = pluralize!(self.num)
+ ),
+ self.spans.iter().map(|sp| (*sp, "()".to_string())).collect(),
+ // "HasPlaceholders" because applying this fix by itself isn't
+ // enough: All constructor calls have to be adjusted as well
+ Applicability::HasPlaceholders,
+ );
+ }
+}
#[derive(Debug)]
pub struct Whatever {
pub field0: (),
- field1: (), //~ ERROR fields `field1`, `field2`, `field3` and `field4` are never read
+ field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
field2: (),
field3: (),
field4: (),
-error: fields `field1`, `field2`, `field3` and `field4` are never read
+error: fields `field1`, `field2`, `field3`, and `field4` are never read
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
|
LL | pub struct Whatever {
b: usize, //~ ERROR field `b` is never read
#[deny(dead_code)]
c: usize, //~ ERROR fields `c` and `e` are never read
- d: usize, //~ WARN fields `d`, `f` and `g` are never read
+ d: usize, //~ WARN fields `d`, `f`, and `g` are never read
#[deny(dead_code)]
e: usize,
f: usize,
-warning: fields `d`, `f` and `g` are never read
+warning: fields `d`, `f`, and `g` are never read
--> $DIR/multiple-dead-codes-in-the-same-struct.rs:10:5
|
LL | struct Bar {
//~| HELP: consider changing the field to be of unit type
struct MultipleUnused(i32, f32, String, u8);
-//~^ ERROR: fields `0`, `1`, `2` and `3` are never read
+//~^ ERROR: fields `0`, `1`, `2`, and `3` are never read
//~| NOTE: fields in this struct
//~| HELP: consider changing the fields to be of unit type
LL | struct SingleUnused(i32, (), String);
| ~~
-error: fields `0`, `1`, `2` and `3` are never read
+error: fields `0`, `1`, `2`, and `3` are never read
--> $DIR/tuple-struct-field.rs:13:23
|
LL | struct MultipleUnused(i32, f32, String, u8);