rustc_hir_analysis/errors/
remove_or_use_generic.rs1use std::ops::ControlFlow;
2
3use rustc_errors::{Applicability, Diag};
4use rustc_hir::def::DefKind;
5use rustc_hir::def_id::{DefId, LocalDefId};
6use rustc_hir::intravisit::{self, Visitor, walk_lifetime};
7use rustc_hir::{GenericArg, HirId, LifetimeKind, Path, QPath, TyKind};
8use rustc_middle::hir::nested_filter::All;
9use rustc_middle::ty::{GenericParamDef, GenericParamDefKind, TyCtxt};
10
11use crate::hir::def::Res;
12
13struct ParamUsageVisitor<'tcx> {
15 tcx: TyCtxt<'tcx>,
16 param_def_id: DefId,
18 found: bool,
19}
20
21impl<'tcx> Visitor<'tcx> for ParamUsageVisitor<'tcx> {
22 type NestedFilter = All;
23
24 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
25 self.tcx
26 }
27
28 type Result = ControlFlow<()>;
29
30 fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result {
31 if let Some(res_def_id) = path.res.opt_def_id() {
32 if res_def_id == self.param_def_id {
33 self.found = true;
34 return ControlFlow::Break(());
35 }
36 }
37 intravisit::walk_path(self, path)
38 }
39
40 fn visit_lifetime(&mut self, lifetime: &'tcx rustc_hir::Lifetime) -> Self::Result {
41 if let LifetimeKind::Param(id) = lifetime.kind {
42 if let Some(local_def_id) = self.param_def_id.as_local() {
43 if id == local_def_id {
44 self.found = true;
45 return ControlFlow::Break(());
46 }
47 }
48 }
49 walk_lifetime(self, lifetime)
50 }
51}
52
53pub(crate) fn suggest_to_remove_or_use_generic(
61 tcx: TyCtxt<'_>,
62 diag: &mut Diag<'_>,
63 impl_def_id: LocalDefId,
64 param: &GenericParamDef,
65 is_lifetime: bool,
66) {
67 let node = tcx.hir_node_by_def_id(impl_def_id);
68 let hir_impl = node.expect_item().expect_impl();
69
70 let Some((index, _)) = hir_impl
71 .generics
72 .params
73 .iter()
74 .enumerate()
75 .find(|(_, par)| par.def_id.to_def_id() == param.def_id)
76 else {
77 return;
78 };
79
80 let struct_def_id = if let TyKind::Path(QPath::Resolved(_, path)) = hir_impl.self_ty.kind
82 && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, def_id) = path.res
83 {
84 def_id
85 } else {
86 return;
87 };
88
89 let generics = tcx.generics_of(struct_def_id);
91 let total_params = generics
92 .own_params
93 .iter()
94 .filter(|p| {
95 if is_lifetime {
96 #[allow(non_exhaustive_omitted_patterns)] match p.kind {
GenericParamDefKind::Lifetime => true,
_ => false,
}matches!(p.kind, GenericParamDefKind::Lifetime)
97 } else {
98 #[allow(non_exhaustive_omitted_patterns)] match p.kind {
GenericParamDefKind::Type { .. } => true,
_ => false,
}matches!(p.kind, GenericParamDefKind::Type { .. })
99 }
100 })
101 .count();
102
103 let mut provided_params = 0;
105 let mut last_segment_args = None;
106
107 if let TyKind::Path(QPath::Resolved(_, path)) = hir_impl.self_ty.kind
108 && let Some(seg) = path.segments.last()
109 && let Some(args) = seg.args
110 {
111 last_segment_args = Some(args);
112 provided_params = args
113 .args
114 .iter()
115 .filter(|arg| match arg {
116 GenericArg::Lifetime(_) => is_lifetime,
117 GenericArg::Type(_) => !is_lifetime,
118 _ => false,
119 })
120 .count();
121 }
122
123 let mut visitor = ParamUsageVisitor { tcx, param_def_id: param.def_id, found: false };
124 for item_ref in hir_impl.items {
125 let _ = visitor.visit_impl_item_ref(item_ref);
126 if visitor.found {
127 break;
128 }
129 }
130 let is_param_used = visitor.found;
131
132 let mut suggestions = ::alloc::vec::Vec::new()vec![];
133
134 if !is_param_used {
136 suggestions.push((hir_impl.generics.span_for_param_removal(index), String::new()));
137 }
138
139 if provided_params < total_params || is_param_used {
142 if let Some(args) = last_segment_args {
143 suggestions.push((args.span().unwrap().shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", {0}", param.name))
})format!(", {}", param.name)));
145 } else if let TyKind::Path(QPath::Resolved(_, path)) = hir_impl.self_ty.kind {
146 let seg = path.segments.last().unwrap();
148 suggestions.push((seg.ident.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}>", param.name))
})format!("<{}>", param.name)));
149 }
150 if is_param_used {
151 let struct_span = tcx.def_span(struct_def_id);
153 let last_param_span = if let Some(local_def_id) = struct_def_id.as_local() {
154 let hir_struct = tcx.hir_node_by_def_id(local_def_id).expect_item().expect_struct();
155 hir_struct.1.params.last().map(|param| param.span)
156 } else {
157 let generics = tcx.generics_of(struct_def_id);
158 generics.own_params.last().map(|param| tcx.def_span(param.def_id))
159 };
160
161 if let Some(last_param_span) = last_param_span {
162 suggestions.push((last_param_span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", {0}", param.name))
})format!(", {}", param.name)));
163 } else {
164 suggestions.push((struct_span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}>", param.name))
})format!("<{}>", param.name)));
165 }
166 }
167 }
168
169 if suggestions.is_empty() {
170 return;
171 }
172
173 let parameter_type = if is_lifetime { "lifetime" } else { "type" };
174 if is_param_used {
175 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use the {0} parameter `{1}` in the `{2}` type and use it in the type definition",
parameter_type, param.name, tcx.def_path_str(struct_def_id)))
})format!(
176 "use the {} parameter `{}` in the `{}` type and use it in the type definition",
177 parameter_type,
178 param.name,
179 tcx.def_path_str(struct_def_id)
180 );
181 diag.multipart_suggestion(
182 msg,
183 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(suggestions[0].0, suggestions[0].1.clone()),
(suggestions[1].0, suggestions[1].1.clone())]))vec![
184 (suggestions[0].0, suggestions[0].1.clone()),
185 (suggestions[1].0, suggestions[1].1.clone()),
186 ],
187 Applicability::MaybeIncorrect,
188 );
189 } else {
190 let msg = if suggestions.len() == 2 {
191 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("either remove the unused {0} parameter `{1}`",
parameter_type, param.name))
})format!("either remove the unused {} parameter `{}`", parameter_type, param.name)
192 } else {
193 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("remove the unused {0} parameter `{1}`",
parameter_type, param.name))
})format!("remove the unused {} parameter `{}`", parameter_type, param.name)
194 };
195 diag.span_suggestion(
196 suggestions[0].0,
197 msg,
198 suggestions[0].1.clone(),
199 Applicability::MaybeIncorrect,
200 );
201 if suggestions.len() == 2 {
202 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("or use it"))
})format!("or use it");
203 diag.span_suggestion(
204 suggestions[1].0,
205 msg,
206 suggestions[1].1.clone(),
207 Applicability::MaybeIncorrect,
208 );
209 }
210 };
211}