clippy_utils/ty/type_certainty/
mod.rs1use crate::paths::{PathNS, lookup_path};
15use rustc_hir::def::{DefKind, Res};
16use rustc_hir::def_id::DefId;
17use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty};
18use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
19use rustc_lint::LateContext;
20use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
21use rustc_span::Span;
22
23mod certainty;
24use certainty::{Certainty, Meet, join, meet};
25
26pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
27 expr_type_certainty(cx, expr).is_certain()
28}
29
30fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
31 let certainty = match &expr.kind {
32 ExprKind::Unary(_, expr)
33 | ExprKind::Field(expr, _)
34 | ExprKind::Index(expr, _, _)
35 | ExprKind::AddrOf(_, _, expr) => expr_type_certainty(cx, expr),
36
37 ExprKind::Array(exprs) => join(exprs.iter().map(|expr| expr_type_certainty(cx, expr))),
38
39 ExprKind::Call(callee, args) => {
40 let lhs = expr_type_certainty(cx, callee);
41 let rhs = if type_is_inferable_from_arguments(cx, expr) {
42 meet(args.iter().map(|arg| expr_type_certainty(cx, arg)))
43 } else {
44 Certainty::Uncertain
45 };
46 lhs.join_clearing_def_ids(rhs)
47 },
48
49 ExprKind::MethodCall(method, receiver, args, _) => {
50 let mut receiver_type_certainty = expr_type_certainty(cx, receiver);
51 if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
55 && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id))
56 {
57 receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
58 }
59 let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
60 let rhs = if type_is_inferable_from_arguments(cx, expr) {
61 meet(
62 std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))),
63 )
64 } else {
65 Certainty::Uncertain
66 };
67 lhs.join(rhs)
68 },
69
70 ExprKind::Tup(exprs) => meet(exprs.iter().map(|expr| expr_type_certainty(cx, expr))),
71
72 ExprKind::Binary(_, lhs, rhs) => expr_type_certainty(cx, lhs).meet(expr_type_certainty(cx, rhs)),
73
74 ExprKind::Lit(_) => Certainty::Certain(None),
75
76 ExprKind::Cast(_, ty) => type_certainty(cx, ty),
77
78 ExprKind::If(_, if_expr, Some(else_expr)) => {
79 expr_type_certainty(cx, if_expr).join(expr_type_certainty(cx, else_expr))
80 },
81
82 ExprKind::Path(qpath) => qpath_certainty(cx, qpath, false),
83
84 ExprKind::Struct(qpath, _, _) => qpath_certainty(cx, qpath, true),
85
86 _ => Certainty::Uncertain,
87 };
88
89 let expr_ty = cx.typeck_results().expr_ty(expr);
90 if let Some(def_id) = adt_def_id(expr_ty) {
91 certainty.with_def_id(def_id)
92 } else {
93 certainty.clear_def_id()
94 }
95}
96
97struct CertaintyVisitor<'cx, 'tcx> {
98 cx: &'cx LateContext<'tcx>,
99 certainty: Certainty,
100}
101
102impl<'cx, 'tcx> CertaintyVisitor<'cx, 'tcx> {
103 fn new(cx: &'cx LateContext<'tcx>) -> Self {
104 Self {
105 cx,
106 certainty: Certainty::Certain(None),
107 }
108 }
109}
110
111impl<'cx> Visitor<'cx> for CertaintyVisitor<'cx, '_> {
112 fn visit_qpath(&mut self, qpath: &'cx QPath<'_>, hir_id: HirId, _: Span) {
113 self.certainty = self.certainty.meet(qpath_certainty(self.cx, qpath, true));
114 if self.certainty != Certainty::Uncertain {
115 walk_qpath(self, qpath, hir_id);
116 }
117 }
118
119 fn visit_ty(&mut self, ty: &'cx hir::Ty<'_, AmbigArg>) {
120 if self.certainty != Certainty::Uncertain {
121 walk_ty(self, ty);
122 }
123 }
124
125 fn visit_infer(&mut self, _inf_id: HirId, _inf_span: Span, _kind: InferKind<'cx>) -> Self::Result {
126 self.certainty = Certainty::Uncertain;
127 }
128}
129
130fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty {
131 if let TyKind::Path(qpath) = &ty.kind {
139 return qpath_certainty(cx, qpath, true);
140 }
141
142 let mut visitor = CertaintyVisitor::new(cx);
143 visitor.visit_ty_unambig(ty);
144 visitor.certainty
145}
146
147fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certainty {
148 let mut visitor = CertaintyVisitor::new(cx);
149 visitor.visit_generic_args(args);
150 visitor.certainty
151}
152
153fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bool) -> Certainty {
159 let certainty = match qpath {
160 QPath::Resolved(ty, path) => {
161 let len = path.segments.len();
162 path.segments.iter().enumerate().fold(
163 ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)),
164 |parent_certainty, (i, path_segment)| {
165 path_segment_certainty(cx, parent_certainty, path_segment, i != len - 1 || resolves_to_type)
166 },
167 )
168 },
169
170 QPath::TypeRelative(ty, path_segment) => {
171 path_segment_certainty(cx, type_certainty(cx, ty), path_segment, resolves_to_type)
172 },
173
174 QPath::LangItem(lang_item, ..) => cx
175 .tcx
176 .lang_items()
177 .get(*lang_item)
178 .map_or(Certainty::Uncertain, |def_id| {
179 let generics = cx.tcx.generics_of(def_id);
180 if generics.is_empty() {
181 Certainty::Certain(if resolves_to_type { Some(def_id) } else { None })
182 } else {
183 Certainty::Uncertain
184 }
185 }),
186 };
187 debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
188 certainty
189}
190
191fn path_segment_certainty(
192 cx: &LateContext<'_>,
193 parent_certainty: Certainty,
194 path_segment: &PathSegment<'_>,
195 resolves_to_type: bool,
196) -> Certainty {
197 let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) {
198 Res::Def(_, def_id) => {
207 if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
209 let generics = cx.tcx.generics_of(def_id);
210
211 let own_count = generics.own_params.len();
212 let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && own_count == 0 {
213 Certainty::Certain(None)
214 } else {
215 Certainty::Uncertain
216 };
217 let rhs = path_segment
218 .args
219 .map_or(Certainty::Uncertain, |args| generic_args_certainty(cx, args));
220 let certainty = lhs.join_clearing_def_ids(rhs);
222 if resolves_to_type {
223 if let DefKind::TyAlias = cx.tcx.def_kind(def_id) {
224 adt_def_id(cx.tcx.type_of(def_id).instantiate_identity())
225 .map_or(certainty, |def_id| certainty.with_def_id(def_id))
226 } else {
227 certainty.with_def_id(def_id)
228 }
229 } else {
230 certainty
231 }
232 } else {
233 Certainty::Certain(None)
234 }
235 },
236
237 Res::PrimTy(_) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::SelfCtor(_) => {
238 Certainty::Certain(None)
239 },
240
241 Res::Local(hir_id) => match cx.tcx.parent_hir_node(hir_id) {
243 Node::Param(..) => Certainty::Certain(None),
245 Node::LetStmt(local) => {
248 let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty));
249 let rhs = local
250 .init
251 .map_or(Certainty::Uncertain, |init| expr_type_certainty(cx, init));
252 let certainty = lhs.join(rhs);
253 if resolves_to_type {
254 certainty
255 } else {
256 certainty.clear_def_id()
257 }
258 },
259 _ => Certainty::Uncertain,
260 },
261
262 _ => Certainty::Uncertain,
263 };
264 debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
265 certainty
266}
267
268fn update_res(
271 cx: &LateContext<'_>,
272 parent_certainty: Certainty,
273 path_segment: &PathSegment<'_>,
274 resolves_to_type: bool,
275) -> Option<Res> {
276 if path_segment.res == Res::Err
277 && let Some(def_id) = parent_certainty.to_def_id()
278 {
279 let mut def_path = cx.get_def_path(def_id);
280 def_path.push(path_segment.ident.name);
281 let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value };
282 if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() {
283 return Some(Res::Def(cx.tcx.def_kind(id), id));
284 }
285 }
286
287 None
288}
289
290#[allow(clippy::cast_possible_truncation)]
291fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
292 let Some(callee_def_id) = (match expr.kind {
293 ExprKind::Call(callee, _) => {
294 let callee_ty = cx.typeck_results().expr_ty(callee);
295 if let ty::FnDef(callee_def_id, _) = callee_ty.kind() {
296 Some(*callee_def_id)
297 } else {
298 None
299 }
300 },
301 ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
302 _ => None,
303 }) else {
304 return false;
305 };
306
307 let generics = cx.tcx.generics_of(callee_def_id);
308 let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
309
310 (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| {
312 fn_sig
313 .inputs()
314 .iter()
315 .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
316 })
317}
318
319fn self_ty<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId) -> Ty<'tcx> {
320 cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()[0]
321}
322
323fn adt_def_id(ty: Ty<'_>) -> Option<DefId> {
324 ty.peel_refs().ty_adt_def().map(AdtDef::did)
325}
326
327fn contains_param(ty: Ty<'_>, index: u32) -> bool {
328 ty.walk()
329 .any(|arg| matches!(arg.kind(), GenericArgKind::Type(ty) if ty.is_param(index)))
330}