Skip to content

Commit

Permalink
Port dead_code lints to be translatable.
Browse files Browse the repository at this point in the history
  • Loading branch information
crlf0710 committed Oct 24, 2022
1 parent 56f1325 commit 113e8df
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 99 deletions.
34 changes: 34 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/passes.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,37 @@ passes_missing_const_err =
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
32 changes: 32 additions & 0 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rustc_target::abi::TargetDataLayoutErrors;
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};

Expand Down Expand Up @@ -170,6 +171,37 @@ impl IntoDiagnosticArg for Level {
}
}

#[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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ pub use diagnostic::{
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.
Expand Down
175 changes: 84 additions & 91 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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};
Expand All @@ -18,7 +18,10 @@ use rustc_session::lint;
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
Expand Down Expand Up @@ -698,99 +701,89 @@ impl<'tcx> DeadVisitor<'tcx> {
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(
Expand Down
80 changes: 79 additions & 1 deletion compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ use std::{
};

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)]
Expand Down Expand Up @@ -1449,3 +1453,77 @@ pub struct MissingConstErr {
#[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,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#[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: (),
Expand Down
Loading

0 comments on commit 113e8df

Please sign in to comment.