1use rustc_hir::def::{DefKind, Res};
2use rustc_hir::def_id::DefId;
3use rustc_hir::{
4 self as hir, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, TyKind,
5};
6use rustc_lint::LateContext;
7use rustc_middle::ty::layout::HasTyCtxt;
8use rustc_middle::ty::{AdtDef, AdtKind, Binder, EarlyBinder, Ty, TypeckResults};
9use rustc_span::{Ident, Symbol};
10
11pub trait HasHirId: Copy {
13 fn hir_id(self) -> HirId;
14}
15impl HasHirId for HirId {
16 #[inline]
17 fn hir_id(self) -> HirId {
18 self
19 }
20}
21impl HasHirId for &Expr<'_> {
22 #[inline]
23 fn hir_id(self) -> HirId {
24 self.hir_id
25 }
26}
27
28type DefRes = (DefKind, DefId);
29
30pub trait MaybeTypeckRes<'tcx> {
31 fn typeck_res(&self) -> Option<&TypeckResults<'tcx>>;
37
38 #[inline]
44 #[cfg_attr(debug_assertions, track_caller)]
45 fn ty_based_def(&self, node: impl HasHirId) -> Option<DefRes> {
46 #[inline]
47 #[cfg_attr(debug_assertions, track_caller)]
48 fn f(typeck: &TypeckResults<'_>, id: HirId) -> Option<DefRes> {
49 if typeck.hir_owner == id.owner {
50 let def = typeck.type_dependent_def(id);
51 debug_assert!(
52 def.is_some(),
53 "attempted type-dependent lookup for a node with no definition\
54 \n node `{id:?}`",
55 );
56 def
57 } else {
58 debug_assert!(
59 false,
60 "attempted type-dependent lookup for a node in the wrong body\
61 \n in body `{:?}`\
62 \n expected body `{:?}`",
63 typeck.hir_owner, id.owner,
64 );
65 None
66 }
67 }
68 self.typeck_res().and_then(|typeck| f(typeck, node.hir_id()))
69 }
70}
71impl<'tcx> MaybeTypeckRes<'tcx> for LateContext<'tcx> {
72 #[inline]
73 #[cfg_attr(debug_assertions, track_caller)]
74 fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> {
75 if let Some(typeck) = self.maybe_typeck_results() {
76 Some(typeck)
77 } else {
78 debug_assert!(false, "attempted type-dependent lookup in a non-body context");
82 None
83 }
84 }
85}
86impl<'tcx> MaybeTypeckRes<'tcx> for TypeckResults<'tcx> {
87 #[inline]
88 fn typeck_res(&self) -> Option<&TypeckResults<'tcx>> {
89 Some(self)
90 }
91}
92
93type QPathId<'tcx> = (&'tcx QPath<'tcx>, HirId);
95
96pub trait MaybeQPath<'a>: Copy {
98 fn opt_qpath(self) -> Option<QPathId<'a>>;
101
102 #[inline]
104 fn opt_lang_path(self) -> Option<LangItem> {
105 match self.opt_qpath() {
106 Some((&QPath::LangItem(item, _), _)) => Some(item),
107 _ => None,
108 }
109 }
110
111 #[inline]
113 #[cfg_attr(debug_assertions, track_caller)]
114 fn res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res {
115 #[cfg_attr(debug_assertions, track_caller)]
116 fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res {
117 match *qpath {
118 QPath::Resolved(_, p) => p.res,
119 QPath::TypeRelative(..) | QPath::LangItem(..) if let Some((kind, id)) = typeck.ty_based_def(id) => {
120 Res::Def(kind, id)
121 },
122 QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err,
123 }
124 }
125 match self.opt_qpath() {
126 Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck),
127 _ => Res::Err,
128 }
129 }
130
131 #[inline]
134 #[cfg_attr(debug_assertions, track_caller)]
135 fn res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res {
136 #[cfg_attr(debug_assertions, track_caller)]
137 fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res {
138 match *qpath {
139 QPath::Resolved(_, p)
140 if let [.., seg] = p.segments
141 && seg.ident.name == name =>
142 {
143 p.res
144 },
145 QPath::TypeRelative(_, seg)
146 if seg.ident.name == name
147 && let Some((kind, id)) = typeck.ty_based_def(id) =>
148 {
149 Res::Def(kind, id)
150 },
151 QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err,
152 }
153 }
154 match self.opt_qpath() {
155 Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name),
156 _ => Res::Err,
157 }
158 }
159
160 #[inline]
162 #[cfg_attr(debug_assertions, track_caller)]
163 fn res_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> (Res, Option<&'a PathSegment<'a>>) {
164 #[cfg_attr(debug_assertions, track_caller)]
165 fn f<'a>(qpath: &QPath<'a>, id: HirId, typeck: &TypeckResults<'_>) -> (Res, Option<&'a PathSegment<'a>>) {
166 match *qpath {
167 QPath::Resolved(_, p) if let [.., seg] = p.segments => (p.res, Some(seg)),
168 QPath::TypeRelative(_, seg) if let Some((kind, id)) = typeck.ty_based_def(id) => {
169 (Res::Def(kind, id), Some(seg))
170 },
171 QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => (Res::Err, None),
172 }
173 }
174 match self.opt_qpath() {
175 Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck),
176 _ => (Res::Err, None),
177 }
178 }
179
180 #[inline]
185 #[cfg_attr(debug_assertions, track_caller)]
186 fn typeless_res<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Res {
187 #[cfg_attr(debug_assertions, track_caller)]
188 fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>) -> Res {
189 match *qpath {
190 QPath::Resolved(
191 None
192 | Some(&hir::Ty {
193 kind: TyKind::Infer(()),
194 ..
195 }),
196 p,
197 ) => p.res,
198 QPath::TypeRelative(
199 &hir::Ty {
200 kind: TyKind::Infer(()),
201 ..
202 },
203 _,
204 ) if let Some((kind, id)) = typeck.ty_based_def(id) => Res::Def(kind, id),
205 QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err,
206 }
207 }
208 match self.opt_qpath() {
209 Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck),
210 _ => Res::Err,
211 }
212 }
213
214 #[inline]
219 #[cfg_attr(debug_assertions, track_caller)]
220 fn typeless_res_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Res {
221 #[cfg_attr(debug_assertions, track_caller)]
222 fn f(qpath: &QPath<'_>, id: HirId, typeck: &TypeckResults<'_>, name: Symbol) -> Res {
223 match *qpath {
224 QPath::Resolved(
225 None
226 | Some(&hir::Ty {
227 kind: TyKind::Infer(()),
228 ..
229 }),
230 p,
231 ) if let [.., seg] = p.segments
232 && seg.ident.name == name =>
233 {
234 p.res
235 },
236 QPath::TypeRelative(
237 &hir::Ty {
238 kind: TyKind::Infer(()),
239 ..
240 },
241 seg,
242 ) if seg.ident.name == name
243 && let Some((kind, id)) = typeck.ty_based_def(id) =>
244 {
245 Res::Def(kind, id)
246 },
247 QPath::Resolved(..) | QPath::TypeRelative(..) | QPath::LangItem(..) => Res::Err,
248 }
249 }
250 match self.opt_qpath() {
251 Some((qpath, id)) if let Some(typeck) = typeck.typeck_res() => f(qpath, id, typeck, name),
252 _ => Res::Err,
253 }
254 }
255
256 #[inline]
260 #[cfg_attr(debug_assertions, track_caller)]
261 fn ty_rel_def<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<DefRes> {
262 match self.opt_qpath() {
263 Some((QPath::TypeRelative(..), id)) => typeck.ty_based_def(id),
264 _ => None,
265 }
266 }
267
268 #[inline]
273 #[cfg_attr(debug_assertions, track_caller)]
274 fn ty_rel_def_if_named<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>, name: Symbol) -> Option<DefRes> {
275 match self.opt_qpath() {
276 Some((&QPath::TypeRelative(_, seg), id)) if seg.ident.name == name => typeck.ty_based_def(id),
277 _ => None,
278 }
279 }
280
281 #[inline]
286 #[cfg_attr(debug_assertions, track_caller)]
287 fn ty_rel_def_with_seg<'tcx>(self, typeck: &impl MaybeTypeckRes<'tcx>) -> Option<(DefRes, &'a PathSegment<'a>)> {
288 match self.opt_qpath() {
289 Some((QPath::TypeRelative(_, seg), id)) if let Some(def) = typeck.ty_based_def(id) => Some((def, seg)),
290 _ => None,
291 }
292 }
293}
294
295impl<'tcx> MaybeQPath<'tcx> for QPathId<'tcx> {
296 #[inline]
297 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
298 Some((self.0, self.1))
299 }
300}
301impl<'tcx> MaybeQPath<'tcx> for &'tcx Expr<'_> {
302 #[inline]
303 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
304 match &self.kind {
305 ExprKind::Path(qpath) => Some((qpath, self.hir_id)),
306 _ => None,
307 }
308 }
309}
310impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> {
311 #[inline]
312 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
313 match &self.kind {
314 PatExprKind::Path(qpath) => Some((qpath, self.hir_id)),
315 _ => None,
316 }
317 }
318}
319impl<'tcx, AmbigArg> MaybeQPath<'tcx> for &'tcx hir::Ty<'_, AmbigArg> {
320 #[inline]
321 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
322 match &self.kind {
323 TyKind::Path(qpath) => Some((qpath, self.hir_id)),
324 _ => None,
325 }
326 }
327}
328impl<'tcx> MaybeQPath<'tcx> for &'_ Pat<'tcx> {
329 #[inline]
330 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
331 match self.kind {
332 PatKind::Expr(e) => e.opt_qpath(),
333 _ => None,
334 }
335 }
336}
337impl<'tcx, T: MaybeQPath<'tcx>> MaybeQPath<'tcx> for Option<T> {
338 #[inline]
339 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
340 self.and_then(T::opt_qpath)
341 }
342}
343impl<'tcx, T: Copy + MaybeQPath<'tcx>> MaybeQPath<'tcx> for &Option<T> {
344 #[inline]
345 fn opt_qpath(self) -> Option<QPathId<'tcx>> {
346 self.and_then(T::opt_qpath)
347 }
348}
349
350type OptResPath<'tcx> = (Option<&'tcx hir::Ty<'tcx>>, Option<&'tcx Path<'tcx>>);
352
353pub trait MaybeResPath<'a>: Copy {
364 fn opt_res_path(self) -> OptResPath<'a>;
367
368 #[inline]
371 fn basic_res(self) -> &'a Res {
372 self.opt_res_path().1.map_or(&Res::Err, |p| &p.res)
373 }
374
375 #[inline]
377 fn res_local_id(self) -> Option<HirId> {
378 if let (_, Some(p)) = self.opt_res_path()
379 && let Res::Local(id) = p.res
380 {
381 Some(id)
382 } else {
383 None
384 }
385 }
386
387 fn res_local_id_and_ident(self) -> Option<(HirId, &'a Ident)> {
389 if let (_, Some(p)) = self.opt_res_path()
390 && let Res::Local(id) = p.res
391 && let [seg] = p.segments
392 {
393 Some((id, &seg.ident))
394 } else {
395 None
396 }
397 }
398}
399impl<'a> MaybeResPath<'a> for &'a Path<'a> {
400 #[inline]
401 fn opt_res_path(self) -> OptResPath<'a> {
402 (None, Some(self))
403 }
404
405 #[inline]
406 fn basic_res(self) -> &'a Res {
407 &self.res
408 }
409}
410impl<'a> MaybeResPath<'a> for &QPath<'a> {
411 #[inline]
412 fn opt_res_path(self) -> OptResPath<'a> {
413 match *self {
414 QPath::Resolved(ty, path) => (ty, Some(path)),
415 _ => (None, None),
416 }
417 }
418}
419impl<'a> MaybeResPath<'a> for &Expr<'a> {
420 #[inline]
421 fn opt_res_path(self) -> OptResPath<'a> {
422 match &self.kind {
423 ExprKind::Path(qpath) => qpath.opt_res_path(),
424 _ => (None, None),
425 }
426 }
427}
428impl<'a> MaybeResPath<'a> for &PatExpr<'a> {
429 #[inline]
430 fn opt_res_path(self) -> OptResPath<'a> {
431 match &self.kind {
432 PatExprKind::Path(qpath) => qpath.opt_res_path(),
433 _ => (None, None),
434 }
435 }
436}
437impl<'a, AmbigArg> MaybeResPath<'a> for &hir::Ty<'a, AmbigArg> {
438 #[inline]
439 fn opt_res_path(self) -> OptResPath<'a> {
440 match &self.kind {
441 TyKind::Path(qpath) => qpath.opt_res_path(),
442 _ => (None, None),
443 }
444 }
445}
446impl<'a> MaybeResPath<'a> for &Pat<'a> {
447 #[inline]
448 fn opt_res_path(self) -> OptResPath<'a> {
449 match self.kind {
450 PatKind::Expr(e) => e.opt_res_path(),
451 _ => (None, None),
452 }
453 }
454}
455impl<'a, T: MaybeResPath<'a>> MaybeResPath<'a> for Option<T> {
456 #[inline]
457 fn opt_res_path(self) -> OptResPath<'a> {
458 match self {
459 Some(x) => T::opt_res_path(x),
460 None => (None, None),
461 }
462 }
463
464 #[inline]
465 fn basic_res(self) -> &'a Res {
466 self.map_or(&Res::Err, T::basic_res)
467 }
468}
469
470pub trait MaybeDef: Copy {
472 fn opt_def_id(self) -> Option<DefId>;
473
474 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)>;
477
478 #[inline]
480 fn opt_diag_name<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<Symbol> {
481 self.opt_def_id().and_then(|id| tcx.tcx().get_diagnostic_name(id))
482 }
483
484 #[inline]
486 fn is_diag_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, name: Symbol) -> bool {
487 self.opt_def_id()
488 .is_some_and(|id| tcx.tcx().is_diagnostic_item(name, id))
489 }
490
491 #[inline]
493 fn is_lang_item<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>, item: LangItem) -> bool {
494 self.opt_def_id()
495 .is_some_and(|id| tcx.tcx().lang_items().get(item) == Some(id))
496 }
497
498 #[inline]
500 fn opt_impl_ty<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<EarlyBinder<'tcx, Ty<'tcx>>> {
501 match self.opt_def(tcx) {
502 Some((DefKind::Impl { .. }, id)) => Some(tcx.tcx().type_of(id)),
503 _ => None,
504 }
505 }
506
507 #[inline]
509 fn opt_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<DefId> {
510 self.opt_def_id().and_then(|id| tcx.tcx().opt_parent(id))
511 }
512
513 #[inline]
515 fn is_impl<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> bool {
516 matches!(self.opt_def(tcx), Some((DefKind::Impl { .. }, _)))
517 }
518
519 #[inline]
521 fn ctor_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<DefId> {
522 match self.opt_def(tcx) {
523 Some((DefKind::Ctor(..), id)) => tcx.tcx().opt_parent(id),
524 _ => None,
525 }
526 }
527
528 #[inline]
531 fn assoc_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<DefId> {
532 match self.opt_def(tcx) {
533 Some((DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, id)) => tcx.tcx().opt_parent(id),
534 _ => None,
535 }
536 }
537
538 #[inline]
541 fn assoc_fn_parent<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<DefId> {
542 match self.opt_def(tcx) {
543 Some((DefKind::AssocFn, id)) => tcx.tcx().opt_parent(id),
544 _ => None,
545 }
546 }
547}
548impl MaybeDef for DefId {
549 #[inline]
550 fn opt_def_id(self) -> Option<DefId> {
551 Some(self)
552 }
553
554 #[inline]
555 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
556 self.opt_def_id().map(|id| (tcx.tcx().def_kind(id), id))
557 }
558}
559impl MaybeDef for (DefKind, DefId) {
560 #[inline]
561 fn opt_def_id(self) -> Option<DefId> {
562 Some(self.1)
563 }
564
565 #[inline]
566 fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
567 Some(self)
568 }
569}
570impl MaybeDef for AdtDef<'_> {
571 #[inline]
572 fn opt_def_id(self) -> Option<DefId> {
573 Some(self.did())
574 }
575
576 #[inline]
577 fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
578 let did = self.did();
579 match self.adt_kind() {
580 AdtKind::Enum => Some((DefKind::Enum, did)),
581 AdtKind::Struct => Some((DefKind::Struct, did)),
582 AdtKind::Union => Some((DefKind::Union, did)),
583 }
584 }
585}
586impl MaybeDef for Ty<'_> {
587 #[inline]
588 fn opt_def_id(self) -> Option<DefId> {
589 self.ty_adt_def().opt_def_id()
590 }
591
592 #[inline]
593 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
594 self.ty_adt_def().opt_def(tcx)
595 }
596}
597impl MaybeDef for Res {
598 #[inline]
599 fn opt_def_id(self) -> Option<DefId> {
600 Res::opt_def_id(&self)
601 }
602
603 #[inline]
604 fn opt_def<'tcx>(self, _: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
605 match self {
606 Res::Def(kind, id) => Some((kind, id)),
607 _ => None,
608 }
609 }
610}
611impl<T: MaybeDef> MaybeDef for Option<T> {
612 #[inline]
613 fn opt_def_id(self) -> Option<DefId> {
614 self.and_then(T::opt_def_id)
615 }
616
617 #[inline]
618 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
619 self.and_then(|x| T::opt_def(x, tcx))
620 }
621}
622impl<T: MaybeDef> MaybeDef for EarlyBinder<'_, T> {
623 #[inline]
624 fn opt_def_id(self) -> Option<DefId> {
625 self.skip_binder().opt_def_id()
626 }
627
628 #[inline]
629 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
630 self.skip_binder().opt_def(tcx)
631 }
632}
633impl<T: MaybeDef> MaybeDef for Binder<'_, T> {
634 #[inline]
635 fn opt_def_id(self) -> Option<DefId> {
636 self.skip_binder().opt_def_id()
637 }
638
639 #[inline]
640 fn opt_def<'tcx>(self, tcx: &impl HasTyCtxt<'tcx>) -> Option<(DefKind, DefId)> {
641 self.skip_binder().opt_def(tcx)
642 }
643}