clippy_utils/ty/type_certainty/
mod.rs1use crate::paths::{PathNS, lookup_path};
15use rustc_ast::{LitFloatType, LitIntType, LitKind};
16use rustc_hir::def::{DefKind, Res};
17use rustc_hir::def_id::DefId;
18use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty};
19use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, Param, PathSegment, QPath, TyKind};
20use rustc_lint::LateContext;
21use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
22use rustc_span::Span;
23
24mod certainty;
25use certainty::{Certainty, Meet, join, meet};
26
27pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
28 expr_type_certainty(cx, expr, false).is_certain()
29}
30
31fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>, in_arg: bool) -> Certainty {
34 let certainty = match &expr.kind {
35 ExprKind::Unary(_, expr)
36 | ExprKind::Field(expr, _)
37 | ExprKind::Index(expr, _, _)
38 | ExprKind::AddrOf(_, _, expr) => expr_type_certainty(cx, expr, in_arg),
39
40 ExprKind::Array(exprs) => join(exprs.iter().map(|expr| expr_type_certainty(cx, expr, in_arg))),
41
42 ExprKind::Call(callee, args) => {
43 let lhs = expr_type_certainty(cx, callee, false);
44 let rhs = if type_is_inferable_from_arguments(cx, expr) {
45 meet(args.iter().map(|arg| expr_type_certainty(cx, arg, true)))
46 } else {
47 Certainty::Uncertain
48 };
49 lhs.join_clearing_def_ids(rhs)
50 },
51
52 ExprKind::MethodCall(method, receiver, args, _) => {
53 let mut receiver_type_certainty = expr_type_certainty(cx, receiver, false);
54 if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
58 && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id))
59 {
60 receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
61 }
62 let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
63 let rhs = if type_is_inferable_from_arguments(cx, expr) {
64 meet(
65 std::iter::once(receiver_type_certainty)
66 .chain(args.iter().map(|arg| expr_type_certainty(cx, arg, true))),
67 )
68 } else {
69 Certainty::Uncertain
70 };
71 lhs.join(rhs)
72 },
73
74 ExprKind::Tup(exprs) => meet(exprs.iter().map(|expr| expr_type_certainty(cx, expr, in_arg))),
75
76 ExprKind::Binary(_, lhs, rhs) => {
77 match (
80 expr_type_certainty(cx, lhs, in_arg),
81 expr_type_certainty(cx, rhs, in_arg),
82 ) {
83 (Certainty::Uncertain, Certainty::Certain(_)) | (Certainty::Certain(_), Certainty::Uncertain) => {
84 Certainty::Certain(None)
85 },
86 (l, r) => l.meet(r),
87 }
88 },
89
90 ExprKind::Lit(lit) => {
91 if !in_arg
92 && matches!(
93 lit.node,
94 LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)
95 )
96 {
97 Certainty::Uncertain
98 } else {
99 Certainty::Certain(None)
100 }
101 },
102
103 ExprKind::Cast(_, ty) => type_certainty(cx, ty),
104
105 ExprKind::If(_, if_expr, Some(else_expr)) => {
106 expr_type_certainty(cx, if_expr, in_arg).join(expr_type_certainty(cx, else_expr, in_arg))
107 },
108
109 ExprKind::Path(qpath) => qpath_certainty(cx, qpath, false),
110
111 ExprKind::Struct(qpath, _, _) => qpath_certainty(cx, qpath, true),
112
113 _ => Certainty::Uncertain,
114 };
115
116 let expr_ty = cx.typeck_results().expr_ty(expr);
117 if let Some(def_id) = adt_def_id(expr_ty) {
118 certainty.with_def_id(def_id)
119 } else {
120 certainty.clear_def_id()
121 }
122}
123
124struct CertaintyVisitor<'cx, 'tcx> {
125 cx: &'cx LateContext<'tcx>,
126 certainty: Certainty,
127}
128
129impl<'cx, 'tcx> CertaintyVisitor<'cx, 'tcx> {
130 fn new(cx: &'cx LateContext<'tcx>) -> Self {
131 Self {
132 cx,
133 certainty: Certainty::Certain(None),
134 }
135 }
136}
137
138impl<'cx> Visitor<'cx> for CertaintyVisitor<'cx, '_> {
139 fn visit_qpath(&mut self, qpath: &'cx QPath<'_>, hir_id: HirId, _: Span) {
140 self.certainty = self.certainty.meet(qpath_certainty(self.cx, qpath, true));
141 if self.certainty != Certainty::Uncertain {
142 walk_qpath(self, qpath, hir_id);
143 }
144 }
145
146 fn visit_ty(&mut self, ty: &'cx hir::Ty<'_, AmbigArg>) {
147 if self.certainty != Certainty::Uncertain {
148 walk_ty(self, ty);
149 }
150 }
151
152 fn visit_infer(&mut self, _inf_id: HirId, _inf_span: Span, _kind: InferKind<'cx>) -> Self::Result {
153 self.certainty = Certainty::Uncertain;
154 }
155}
156
157fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty {
158 if let TyKind::Path(qpath) = &ty.kind {
166 return qpath_certainty(cx, qpath, true);
167 }
168
169 let mut visitor = CertaintyVisitor::new(cx);
170 visitor.visit_ty_unambig(ty);
171 visitor.certainty
172}
173
174fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certainty {
175 let mut visitor = CertaintyVisitor::new(cx);
176 visitor.visit_generic_args(args);
177 visitor.certainty
178}
179
180fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bool) -> Certainty {
186 let certainty = match qpath {
187 QPath::Resolved(ty, path) => {
188 let len = path.segments.len();
189 path.segments.iter().enumerate().fold(
190 ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)),
191 |parent_certainty, (i, path_segment)| {
192 path_segment_certainty(cx, parent_certainty, path_segment, i != len - 1 || resolves_to_type)
193 },
194 )
195 },
196
197 QPath::TypeRelative(ty, path_segment) => {
198 path_segment_certainty(cx, type_certainty(cx, ty), path_segment, resolves_to_type)
199 },
200
201 QPath::LangItem(lang_item, ..) => cx
202 .tcx
203 .lang_items()
204 .get(*lang_item)
205 .map_or(Certainty::Uncertain, |def_id| {
206 let generics = cx.tcx.generics_of(def_id);
207 if generics.is_empty() {
208 Certainty::Certain(if resolves_to_type { Some(def_id) } else { None })
209 } else {
210 Certainty::Uncertain
211 }
212 }),
213 };
214 debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
215 certainty
216}
217
218fn param_certainty(cx: &LateContext<'_>, param: &Param<'_>) -> Certainty {
221 let owner_did = cx.tcx.hir_enclosing_body_owner(param.hir_id);
222 let Some(fn_decl) = cx.tcx.hir_fn_decl_by_hir_id(cx.tcx.local_def_id_to_hir_id(owner_did)) else {
223 return Certainty::Uncertain;
224 };
225 let inputs = fn_decl.inputs;
226 let body_params = cx.tcx.hir_body_owned_by(owner_did).params;
227 std::iter::zip(body_params, inputs)
228 .find(|(p, _)| p.hir_id == param.hir_id)
229 .map_or(Certainty::Uncertain, |(_, ty)| type_certainty(cx, ty).clear_def_id())
230}
231
232fn path_segment_certainty(
233 cx: &LateContext<'_>,
234 parent_certainty: Certainty,
235 path_segment: &PathSegment<'_>,
236 resolves_to_type: bool,
237) -> Certainty {
238 let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) {
239 Res::Def(_, def_id) => {
248 if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
250 let generics = cx.tcx.generics_of(def_id);
251
252 let own_count = generics.own_params.len();
253 let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 {
254 Certainty::Certain(None)
255 } else {
256 Certainty::Uncertain
257 };
258 let rhs = path_segment
259 .args
260 .map_or(Certainty::Uncertain, |args| generic_args_certainty(cx, args));
261 let certainty = lhs.join_clearing_def_ids(rhs);
263 if resolves_to_type {
264 if let DefKind::TyAlias = cx.tcx.def_kind(def_id) {
265 adt_def_id(cx.tcx.type_of(def_id).instantiate_identity())
266 .map_or(certainty, |def_id| certainty.with_def_id(def_id))
267 } else {
268 certainty.with_def_id(def_id)
269 }
270 } else {
271 certainty
272 }
273 } else {
274 Certainty::Certain(None)
275 }
276 },
277
278 Res::PrimTy(_) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::SelfCtor(_) => {
279 Certainty::Certain(None)
280 },
281
282 Res::Local(hir_id) => match cx.tcx.parent_hir_node(hir_id) {
284 Node::Param(param) => param_certainty(cx, param),
287 Node::LetStmt(local) => {
290 let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty));
291 let rhs = local
292 .init
293 .map_or(Certainty::Uncertain, |init| expr_type_certainty(cx, init, false));
294 let certainty = lhs.join(rhs);
295 if resolves_to_type {
296 certainty
297 } else {
298 certainty.clear_def_id()
299 }
300 },
301 _ => Certainty::Uncertain,
302 },
303
304 _ => Certainty::Uncertain,
305 };
306 debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
307 certainty
308}
309
310fn update_res(
313 cx: &LateContext<'_>,
314 parent_certainty: Certainty,
315 path_segment: &PathSegment<'_>,
316 resolves_to_type: bool,
317) -> Option<Res> {
318 if path_segment.res == Res::Err
319 && let Some(def_id) = parent_certainty.to_def_id()
320 {
321 let mut def_path = cx.get_def_path(def_id);
322 def_path.push(path_segment.ident.name);
323 let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value };
324 if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() {
325 return Some(Res::Def(cx.tcx.def_kind(id), id));
326 }
327 }
328
329 None
330}
331
332#[expect(clippy::cast_possible_truncation)]
333fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
334 let Some(callee_def_id) = (match expr.kind {
335 ExprKind::Call(callee, _) => {
336 let callee_ty = cx.typeck_results().expr_ty(callee);
337 if let ty::FnDef(callee_def_id, _) = callee_ty.kind() {
338 Some(*callee_def_id)
339 } else {
340 None
341 }
342 },
343 ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
344 _ => None,
345 }) else {
346 return false;
347 };
348
349 let generics = cx.tcx.generics_of(callee_def_id);
350 let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
351
352 (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| {
354 fn_sig
355 .inputs()
356 .iter()
357 .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
358 })
359}
360
361fn self_ty<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId) -> Ty<'tcx> {
362 cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()[0]
363}
364
365fn adt_def_id(ty: Ty<'_>) -> Option<DefId> {
366 ty.peel_refs().ty_adt_def().map(AdtDef::did)
367}
368
369fn contains_param(ty: Ty<'_>, index: u32) -> bool {
370 ty.walk()
371 .any(|arg| matches!(arg.kind(), GenericArgKind::Type(ty) if ty.is_param(index)))
372}