diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:03:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:03:36 +0000 |
commit | 17d40c6057c88f4c432b0d7bac88e1b84cb7e67f (patch) | |
tree | 3f66c4a5918660bb8a758ab6cda5ff8ee4f6cdcd /compiler/rustc_passes/src/liveness.rs | |
parent | Adding upstream version 1.64.0+dfsg1. (diff) | |
download | rustc-upstream/1.65.0+dfsg1.tar.xz rustc-upstream/1.65.0+dfsg1.zip |
Adding upstream version 1.65.0+dfsg1.upstream/1.65.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_passes/src/liveness.rs')
-rw-r--r-- | compiler/rustc_passes/src/liveness.rs | 220 |
1 files changed, 151 insertions, 69 deletions
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 461dd52b9..6a4cd79cd 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -89,16 +89,15 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::*; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::vec::IndexVec; -use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; +use rustc_span::{BytePos, Span}; use std::collections::VecDeque; use std::io; @@ -139,12 +138,54 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { } } -fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx)); +fn check_liveness(tcx: TyCtxt<'_>, def_id: DefId) { + let local_def_id = match def_id.as_local() { + None => return, + Some(def_id) => def_id, + }; + + // Don't run unused pass for #[derive()] + let parent = tcx.local_parent(local_def_id); + if let DefKind::Impl = tcx.def_kind(parent) + && tcx.has_attr(parent.to_def_id(), sym::automatically_derived) + { + return; + } + + // Don't run unused pass for #[naked] + if tcx.has_attr(def_id, sym::naked) { + return; + } + + let mut maps = IrMaps::new(tcx); + let body_id = tcx.hir().body_owned_by(local_def_id); + let hir_id = tcx.hir().body_owner(body_id); + let body = tcx.hir().body(body_id); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for &var_hir_id in upvars.keys() { + let var_name = tcx.hir().name(var_hir_id); + maps.add_variable(Upvar(var_hir_id, var_name)); + } + } + + // gather up the various local variables, significant expressions, + // and so forth: + maps.visit_body(body); + + // compute liveness + let mut lsets = Liveness::new(&mut maps, local_def_id); + let entry_ln = lsets.compute(&body, hir_id); + lsets.log_liveness(entry_ln, body_id.hir_id); + + // check for various error conditions + lsets.visit_body(body); + lsets.warn_about_unused_upvars(entry_ln); + lsets.warn_about_unused_args(body, entry_ln); } pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_liveness, ..*providers }; + *providers = Providers { check_liveness, ..*providers }; } // ______________________________________________________________________ @@ -188,6 +229,19 @@ enum VarKind { Upvar(HirId, Symbol), } +struct CollectLitsVisitor<'tcx> { + lit_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Lit(_) = expr.kind { + self.lit_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + struct IrMaps<'tcx> { tcx: TyCtxt<'tcx>, live_node_map: HirIdMap<LiveNode>, @@ -316,56 +370,6 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - debug!("visit_body {:?}", body.id()); - - // swap in a new set of IR maps for this body - let mut maps = IrMaps::new(self.tcx); - let hir_id = maps.tcx.hir().body_owner(body.id()); - let local_def_id = maps.tcx.hir().local_def_id(hir_id); - let def_id = local_def_id.to_def_id(); - - // Don't run unused pass for #[derive()] - let parent = self.tcx.local_parent(local_def_id); - if let DefKind::Impl = self.tcx.def_kind(parent) - && self.tcx.has_attr(parent.to_def_id(), sym::automatically_derived) - { - return; - } - - // Don't run unused pass for #[naked] - if self.tcx.has_attr(def_id, sym::naked) { - return; - } - - if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { - for &var_hir_id in upvars.keys() { - let var_name = maps.tcx.hir().name(var_hir_id); - maps.add_variable(Upvar(var_hir_id, var_name)); - } - } - - // gather up the various local variables, significant expressions, - // and so forth: - intravisit::walk_body(&mut maps, body); - - // compute liveness - let mut lsets = Liveness::new(&mut maps, local_def_id); - let entry_ln = lsets.compute(&body, hir_id); - lsets.log_liveness(entry_ln, body.id().hir_id); - - // check for various error conditions - lsets.visit_body(body); - lsets.warn_about_unused_upvars(entry_ln); - lsets.warn_about_unused_args(body, entry_ln); - } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { self.add_from_pat(&local.pat); if local.els.is_some() { @@ -1035,9 +1039,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&f, succ) } - hir::ExprKind::MethodCall(.., ref args, _) => { + hir::ExprKind::MethodCall(.., receiver, ref args, _) => { let succ = self.check_is_ty_uninhabited(expr, succ); - self.propagate_through_exprs(args, succ) + let succ = self.propagate_through_exprs(args, succ); + self.propagate_through_expr(receiver, succ) } hir::ExprKind::Tup(ref exprs) => self.propagate_through_exprs(exprs, succ), @@ -1342,7 +1347,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { - self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| { + self.check_unused_vars_in_pat(&local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); } @@ -1357,7 +1362,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { } fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { - self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {}); + self.check_unused_vars_in_pat(&arm.pat, None, None, |_, _, _, _| {}); intravisit::walk_arm(self, arm); } } @@ -1396,7 +1401,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { } hir::ExprKind::Let(let_expr) => { - this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {}); + this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {}); } // no correctness conditions related to liveness @@ -1517,13 +1522,18 @@ impl<'tcx> Liveness<'_, 'tcx> { fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { for p in body.params { - self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| { - if !self.live_on_entry(ln, var) { - self.report_unused_assign(hir_id, spans, var, |name| { - format!("value passed to `{}` is never read", name) - }); - } - }); + self.check_unused_vars_in_pat( + &p.pat, + Some(entry_ln), + Some(body), + |spans, hir_id, ln, var| { + if !self.live_on_entry(ln, var) { + self.report_unused_assign(hir_id, spans, var, |name| { + format!("value passed to `{}` is never read", name) + }); + } + }, + ); } } @@ -1531,6 +1541,7 @@ impl<'tcx> Liveness<'_, 'tcx> { &self, pat: &hir::Pat<'_>, entry_ln: Option<LiveNode>, + opt_body: Option<&hir::Body<'_>>, on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable), ) { // In an or-pattern, only consider the variable; any later patterns must have the same @@ -1549,6 +1560,8 @@ impl<'tcx> Liveness<'_, 'tcx> { .or_insert_with(|| (ln, var, vec![id_and_sp])); }); + let can_remove = matches!(&pat.kind, hir::PatKind::Struct(_, _, true)); + for (_, (ln, var, hir_ids_and_spans)) in vars { if self.used_on_entry(ln, var) { let id = hir_ids_and_spans[0].0; @@ -1556,16 +1569,20 @@ impl<'tcx> Liveness<'_, 'tcx> { hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect(); on_used_on_entry(spans, id, ln, var); } else { - self.report_unused(hir_ids_and_spans, ln, var); + self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body); } } } + #[instrument(skip(self), level = "INFO")] fn report_unused( &self, hir_ids_and_spans: Vec<(HirId, Span, Span)>, ln: LiveNode, var: Variable, + can_remove: bool, + pat: &hir::Pat<'_>, + opt_body: Option<&hir::Body<'_>>, ) { let first_hir_id = hir_ids_and_spans[0].0; @@ -1590,6 +1607,32 @@ impl<'tcx> Liveness<'_, 'tcx> { .emit(); }, ) + } else if can_remove { + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_VARIABLES, + first_hir_id, + hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>(), + |lint| { + let mut err = lint.build(&format!("unused variable: `{}`", name)); + err.multipart_suggestion( + "try removing the field", + hir_ids_and_spans + .iter() + .map(|(_, pat_span, _)| { + let span = self + .ir + .tcx + .sess + .source_map() + .span_extend_to_next_char(*pat_span, ',', true); + (span.with_hi(BytePos(span.hi().0 + 1)), String::new()) + }) + .collect(), + Applicability::MachineApplicable, + ); + err.emit(); + }, + ); } else { let (shorthands, non_shorthands): (Vec<_>, Vec<_>) = hir_ids_and_spans.iter().copied().partition(|(hir_id, _, ident_span)| { @@ -1643,6 +1686,9 @@ impl<'tcx> Liveness<'_, 'tcx> { .collect::<Vec<_>>(), |lint| { let mut err = lint.build(&format!("unused variable: `{}`", name)); + if self.has_added_lit_match_name_span(&name, opt_body, &mut err) { + err.span_label(pat.span, "unused variable"); + } err.multipart_suggestion( "if this is intentional, prefix it with an underscore", non_shorthands, @@ -1656,6 +1702,42 @@ impl<'tcx> Liveness<'_, 'tcx> { } } + fn has_added_lit_match_name_span( + &self, + name: &str, + opt_body: Option<&hir::Body<'_>>, + err: &mut rustc_errors::DiagnosticBuilder<'_, ()>, + ) -> bool { + let mut has_litstring = false; + let Some(opt_body) = opt_body else {return false;}; + let mut visitor = CollectLitsVisitor { lit_exprs: vec![] }; + intravisit::walk_body(&mut visitor, opt_body); + for lit_expr in visitor.lit_exprs { + let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue }; + let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; }; + let name_str: &str = syb.as_str(); + let mut name_pa = String::from("{"); + name_pa.push_str(&name); + name_pa.push('}'); + if name_str.contains(&name_pa) { + err.span_label( + lit_expr.span, + "you might have meant to use string interpolation in this string literal", + ); + err.multipart_suggestion( + "string interpolation only works in `format!` invocations", + vec![ + (lit_expr.span.shrink_to_lo(), "format!(".to_string()), + (lit_expr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + has_litstring = true; + } + } + has_litstring + } + fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) { if !self.live_on_exit(ln, var) { self.report_unused_assign(hir_id, spans, var, |name| { |