]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/lib.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / lib.rs
CommitLineData
f20569fa 1#![feature(box_patterns)]
a2a8927a 2#![feature(control_flow_enum)]
a2a8927a 3#![feature(let_else)]
5e7ed085 4#![feature(let_chains)]
923072b8 5#![feature(lint_reasons)]
a2a8927a 6#![feature(once_cell)]
f20569fa
XL
7#![feature(rustc_private)]
8#![recursion_limit = "512"]
17df50a5 9#![cfg_attr(feature = "deny-warnings", deny(warnings))]
f20569fa 10#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
17df50a5
XL
11// warn on the same lints as `clippy_lints`
12#![warn(trivial_casts, trivial_numeric_casts)]
13// warn on lints, that are included in `rust-lang/rust`s bootstrap
14#![warn(rust_2018_idioms, unused_lifetimes)]
15// warn on rustc internal lints
16#![warn(rustc::internal)]
f20569fa
XL
17
18// FIXME: switch to something more ergonomic here, once available.
19// (Currently there is no way to opt into sysroot crates without `extern crate`.)
20extern crate rustc_ast;
21extern crate rustc_ast_pretty;
cdc7bbd5 22extern crate rustc_attr;
f20569fa
XL
23extern crate rustc_data_structures;
24extern crate rustc_errors;
25extern crate rustc_hir;
26extern crate rustc_infer;
27extern crate rustc_lexer;
28extern crate rustc_lint;
29extern crate rustc_middle;
f20569fa
XL
30extern crate rustc_session;
31extern crate rustc_span;
32extern crate rustc_target;
33extern crate rustc_trait_selection;
34extern crate rustc_typeck;
35
36#[macro_use]
37pub mod sym_helper;
38
f20569fa
XL
39pub mod ast_utils;
40pub mod attrs;
f20569fa
XL
41pub mod comparisons;
42pub mod consts;
cdc7bbd5 43pub mod diagnostics;
f20569fa
XL
44pub mod eager_or_lazy;
45pub mod higher;
46mod hir_utils;
5099ac24 47pub mod macros;
cdc7bbd5 48pub mod msrvs;
f20569fa
XL
49pub mod numeric_literal;
50pub mod paths;
51pub mod ptr;
52pub mod qualify_min_const_fn;
cdc7bbd5 53pub mod source;
3c0e092e 54pub mod str_utils;
f20569fa 55pub mod sugg;
cdc7bbd5 56pub mod ty;
f20569fa
XL
57pub mod usage;
58pub mod visitors;
59
60pub use self::attrs::*;
923072b8
FG
61pub use self::hir_utils::{
62 both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
63};
f20569fa 64
f20569fa
XL
65use std::collections::hash_map::Entry;
66use std::hash::BuildHasherDefault;
923072b8 67use std::sync::OnceLock;
a2a8927a 68use std::sync::{Mutex, MutexGuard};
f20569fa
XL
69
70use if_chain::if_chain;
04454e1e 71use rustc_ast::ast::{self, LitKind};
923072b8 72use rustc_ast::Attribute;
a2a8927a 73use rustc_data_structures::fx::FxHashMap;
17df50a5 74use rustc_data_structures::unhash::UnhashMap;
f20569fa 75use rustc_hir as hir;
cdc7bbd5 76use rustc_hir::def::{DefKind, Res};
5099ac24 77use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
c295e0f8 78use rustc_hir::hir_id::{HirIdMap, HirIdSet};
5099ac24 79use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
c295e0f8 80use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
f20569fa 81use rustc_hir::{
04454e1e 82 def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
923072b8
FG
83 HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
84 Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
85 UnOp,
f20569fa 86};
f20569fa 87use rustc_lint::{LateContext, Level, Lint, LintContext};
c295e0f8 88use rustc_middle::hir::place::PlaceBase;
cdc7bbd5 89use rustc_middle::ty as rustc_ty;
c295e0f8
XL
90use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
91use rustc_middle::ty::binding::BindingMode;
04454e1e
FG
92use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
93 ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
94 PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
95};
c295e0f8 96use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
04454e1e 97use rustc_middle::ty::{FloatTy, IntTy, UintTy};
f20569fa
XL
98use rustc_semver::RustcVersion;
99use rustc_session::Session;
cdc7bbd5 100use rustc_span::hygiene::{ExpnKind, MacroKind};
f20569fa
XL
101use rustc_span::source_map::original_sp;
102use rustc_span::sym;
103use rustc_span::symbol::{kw, Symbol};
cdc7bbd5 104use rustc_span::{Span, DUMMY_SP};
f20569fa 105use rustc_target::abi::Integer;
f20569fa
XL
106
107use crate::consts::{constant, Constant};
c295e0f8 108use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
a2a8927a 109use crate::visitors::expr_visitor_no_bodies;
f20569fa
XL
110
111pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
112 if let Ok(version) = RustcVersion::parse(msrv) {
113 return Some(version);
114 } else if let Some(sess) = sess {
115 if let Some(span) = span {
116 sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
117 }
118 }
119 None
120}
121
923072b8
FG
122pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
123 msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
f20569fa
XL
124}
125
126#[macro_export]
127macro_rules! extract_msrv_attr {
5099ac24
FG
128 ($context:ident) => {
129 fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
130 let sess = rustc_lint::LintContext::sess(cx);
131 match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
f20569fa
XL
132 Some(msrv_attr) => {
133 if let Some(msrv) = msrv_attr.value_str() {
5099ac24 134 self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
f20569fa 135 } else {
5099ac24 136 sess.span_err(msrv_attr.span, "bad clippy attribute");
f20569fa
XL
137 }
138 },
139 _ => (),
140 }
141 }
142 };
143}
144
cdc7bbd5
XL
145/// If the given expression is a local binding, find the initializer expression.
146/// If that initializer expression is another local binding, find its initializer again.
147/// This process repeats as long as possible (but usually no more than once). Initializer
148/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
149/// instead.
150///
151/// Examples:
a2a8927a 152/// ```
cdc7bbd5
XL
153/// let abc = 1;
154/// // ^ output
155/// let def = abc;
a2a8927a 156/// dbg!(def);
cdc7bbd5
XL
157/// // ^^^ input
158///
159/// // or...
160/// let abc = 1;
161/// let def = abc + 2;
162/// // ^^^^^^^ output
a2a8927a 163/// dbg!(def);
cdc7bbd5
XL
164/// // ^^^ input
165/// ```
166pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
167 while let Some(init) = path_to_local(expr)
168 .and_then(|id| find_binding_init(cx, id))
169 .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
170 {
171 expr = init;
172 }
173 expr
174}
175
176/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
177/// By only considering immutable bindings, we guarantee that the returned expression represents the
178/// value of the binding wherever it is referenced.
179///
180/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
181/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
182/// canonical binding `HirId`.
183pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
184 let hir = cx.tcx.hir();
185 if_chain! {
186 if let Some(Node::Binding(pat)) = hir.find(hir_id);
187 if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
188 let parent = hir.get_parent_node(hir_id);
189 if let Some(Node::Local(local)) = hir.find(parent);
190 then {
191 return local.init;
192 }
193 }
194 None
195}
196
f20569fa
XL
197/// Returns `true` if the given `NodeId` is inside a constant context
198///
199/// # Example
200///
201/// ```rust,ignore
202/// if in_constant(cx, expr.hir_id) {
203/// // Do something
204/// }
205/// ```
206pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
207 let parent_id = cx.tcx.hir().get_parent_item(id);
5099ac24 208 match cx.tcx.hir().get_by_def_id(parent_id) {
f20569fa
XL
209 Node::Item(&Item {
210 kind: ItemKind::Const(..) | ItemKind::Static(..),
211 ..
212 })
213 | Node::TraitItem(&TraitItem {
214 kind: TraitItemKind::Const(..),
215 ..
216 })
217 | Node::ImplItem(&ImplItem {
218 kind: ImplItemKind::Const(..),
219 ..
220 })
221 | Node::AnonConst(_) => true,
222 Node::Item(&Item {
223 kind: ItemKind::Fn(ref sig, ..),
224 ..
225 })
226 | Node::ImplItem(&ImplItem {
227 kind: ImplItemKind::Fn(ref sig, _),
228 ..
229 }) => sig.header.constness == Constness::Const,
230 _ => false,
231 }
232}
233
cdc7bbd5
XL
234/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
235/// For example, use this to check whether a function call or a pattern is `Some(..)`.
236pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
237 if let QPath::Resolved(_, path) = qpath {
238 if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
239 if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
04454e1e 240 return cx.tcx.parent(ctor_id) == item_id;
cdc7bbd5
XL
241 }
242 }
243 }
244 false
245}
246
94222f64 247pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
c295e0f8
XL
248 matches!(
249 expr.kind,
250 ExprKind::Block(
251 Block {
252 stmts: [],
253 expr: None,
254 ..
255 },
256 _
257 ) | ExprKind::Tup([])
258 )
94222f64
XL
259}
260
f20569fa 261/// Checks if given pattern is a wildcard (`_`)
136023e0 262pub fn is_wild(pat: &Pat<'_>) -> bool {
f20569fa
XL
263 matches!(pat.kind, PatKind::Wild)
264}
265
f20569fa 266/// Checks if the method call given in `expr` belongs to the given trait.
cdc7bbd5 267/// This is a deprecated function, consider using [`is_trait_method`].
f20569fa
XL
268pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
269 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
270 let trt_id = cx.tcx.trait_of_item(def_id);
271 trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
272}
273
cdc7bbd5
XL
274/// Checks if a method is defined in an impl of a diagnostic item
275pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
276 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
277 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
5e7ed085 278 return cx.tcx.is_diagnostic_item(diag_item, adt.did());
cdc7bbd5
XL
279 }
280 }
281 false
282}
283
284/// Checks if a method is in a diagnostic item trait
285pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
286 if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
287 return cx.tcx.is_diagnostic_item(diag_item, trait_did);
288 }
289 false
290}
291
292/// Checks if the method call given in `expr` belongs to the given trait.
293pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
294 cx.typeck_results()
295 .type_dependent_def_id(expr.hir_id)
296 .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
f20569fa
XL
297}
298
94222f64
XL
299/// Checks if the given expression is a path referring an item on the trait
300/// that is marked with the given diagnostic item.
301///
302/// For checking method call expressions instead of path expressions, use
303/// [`is_trait_method`].
304///
305/// For example, this can be used to find if an expression like `u64::default`
306/// refers to an item of the trait `Default`, which is associated with the
307/// `diag_item` of `sym::Default`.
308pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
309 if let hir::ExprKind::Path(ref qpath) = expr.kind {
310 cx.qpath_res(qpath, expr.hir_id)
311 .opt_def_id()
312 .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
313 } else {
314 false
315 }
316}
317
f20569fa
XL
318pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
319 match *path {
17df50a5
XL
320 QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
321 QPath::TypeRelative(_, seg) => seg,
f20569fa
XL
322 QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
323 }
324}
325
5099ac24
FG
326pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
327 last_path_segment(qpath)
328 .args
329 .map_or(&[][..], |a| a.args)
f20569fa 330 .iter()
5099ac24
FG
331 .filter_map(|a| match a {
332 hir::GenericArg::Type(ty) => Some(ty),
333 _ => None,
f20569fa
XL
334 })
335}
336
cdc7bbd5
XL
337/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
338/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
339/// `QPath::Resolved.1.res.opt_def_id()`.
340///
f20569fa
XL
341/// Matches a `QPath` against a slice of segment string literals.
342///
343/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
344/// `rustc_hir::QPath`.
345///
346/// # Examples
347/// ```rust,ignore
348/// match_qpath(path, &["std", "rt", "begin_unwind"])
349/// ```
350pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
351 match *path {
17df50a5
XL
352 QPath::Resolved(_, path) => match_path(path, segments),
353 QPath::TypeRelative(ty, segment) => match ty.kind {
f20569fa
XL
354 TyKind::Path(ref inner_path) => {
355 if let [prefix @ .., end] = segments {
356 if match_qpath(inner_path, prefix) {
357 return segment.ident.name.as_str() == *end;
358 }
359 }
360 false
361 },
362 _ => false,
363 },
364 QPath::LangItem(..) => false,
365 }
366}
367
cdc7bbd5 368/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
94222f64
XL
369///
370/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
cdc7bbd5 371pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
5099ac24 372 path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
cdc7bbd5
XL
373}
374
94222f64
XL
375/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
376/// diagnostic item.
377pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
5099ac24 378 path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
94222f64
XL
379}
380
cdc7bbd5
XL
381/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
382/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
383/// `QPath::Resolved.1.res.opt_def_id()`.
384///
f20569fa
XL
385/// Matches a `Path` against a slice of segment string literals.
386///
387/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
388/// `rustc_hir::Path`.
389///
390/// # Examples
391///
392/// ```rust,ignore
393/// if match_path(&trait_ref.path, &paths::HASH) {
394/// // This is the `std::hash::Hash` trait.
395/// }
396///
397/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
398/// // This is a `rustc_middle::lint::Lint`.
399/// }
400/// ```
401pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
402 path.segments
403 .iter()
404 .rev()
405 .zip(segments.iter().rev())
406 .all(|(a, b)| a.ident.name.as_str() == *b)
407}
408
f20569fa
XL
409/// If the expression is a path to a local, returns the canonical `HirId` of the local.
410pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
17df50a5 411 if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
f20569fa
XL
412 if let Res::Local(id) = path.res {
413 return Some(id);
414 }
415 }
416 None
417}
418
419/// Returns true if the expression is a path to a local with the specified `HirId`.
420/// Use this function to see if an expression matches a function argument or a match binding.
421pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
422 path_to_local(expr) == Some(id)
423}
424
5099ac24
FG
425pub trait MaybePath<'hir> {
426 fn hir_id(&self) -> HirId;
427 fn qpath_opt(&self) -> Option<&QPath<'hir>>;
428}
429
430macro_rules! maybe_path {
431 ($ty:ident, $kind:ident) => {
432 impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
433 fn hir_id(&self) -> HirId {
434 self.hir_id
435 }
436 fn qpath_opt(&self) -> Option<&QPath<'hir>> {
437 match &self.kind {
438 hir::$kind::Path(qpath) => Some(qpath),
439 _ => None,
440 }
441 }
442 }
443 };
444}
445maybe_path!(Expr, ExprKind);
446maybe_path!(Pat, PatKind);
447maybe_path!(Ty, TyKind);
448
449/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
450pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
451 match maybe_path.qpath_opt() {
452 None => Res::Err,
453 Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
454 }
455}
456
457/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
458pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
459 path_res(cx, maybe_path).opt_def_id()
460}
461
462/// Resolves a def path like `std::vec::Vec`.
463/// This function is expensive and should be used sparingly.
464pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
5099ac24
FG
465 fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
466 match tcx.def_kind(def_id) {
467 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
468 .module_children(def_id)
469 .iter()
470 .find(|item| item.ident.name.as_str() == name)
471 .map(|child| child.res.expect_non_local()),
472 DefKind::Impl => tcx
473 .associated_item_def_ids(def_id)
474 .iter()
475 .copied()
476 .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
477 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
478 _ => None,
479 }
480 }
5e7ed085
FG
481 fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
482 let single = |ty| tcx.incoherent_impls(ty).iter().copied();
483 let empty = || [].iter().copied();
484 match name {
485 "bool" => single(BoolSimplifiedType),
486 "char" => single(CharSimplifiedType),
487 "str" => single(StrSimplifiedType),
488 "array" => single(ArraySimplifiedType),
489 "slice" => single(SliceSimplifiedType),
490 // FIXME: rustdoc documents these two using just `pointer`.
491 //
492 // Maybe this is something we should do here too.
493 "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
494 "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
495 "isize" => single(IntSimplifiedType(IntTy::Isize)),
496 "i8" => single(IntSimplifiedType(IntTy::I8)),
497 "i16" => single(IntSimplifiedType(IntTy::I16)),
498 "i32" => single(IntSimplifiedType(IntTy::I32)),
499 "i64" => single(IntSimplifiedType(IntTy::I64)),
500 "i128" => single(IntSimplifiedType(IntTy::I128)),
501 "usize" => single(UintSimplifiedType(UintTy::Usize)),
502 "u8" => single(UintSimplifiedType(UintTy::U8)),
503 "u16" => single(UintSimplifiedType(UintTy::U16)),
504 "u32" => single(UintSimplifiedType(UintTy::U32)),
505 "u64" => single(UintSimplifiedType(UintTy::U64)),
506 "u128" => single(UintSimplifiedType(UintTy::U128)),
507 "f32" => single(FloatSimplifiedType(FloatTy::F32)),
508 "f64" => single(FloatSimplifiedType(FloatTy::F64)),
509 _ => empty(),
5099ac24
FG
510 }
511 }
512 fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
513 tcx.crates(())
f20569fa 514 .iter()
5e7ed085
FG
515 .copied()
516 .find(|&num| tcx.crate_name(num).as_str() == name)
5099ac24 517 .map(CrateNum::as_def_id)
f20569fa
XL
518 }
519
5099ac24
FG
520 let (base, first, path) = match *path {
521 [base, first, ref path @ ..] => (base, first, path),
94222f64
XL
522 [primitive] => {
523 return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
524 },
f20569fa
XL
525 _ => return Res::Err,
526 };
527 let tcx = cx.tcx;
5e7ed085
FG
528 let starts = find_primitive(tcx, base)
529 .chain(find_crate(tcx, base))
04454e1e 530 .filter_map(|id| item_child_by_name(tcx, id, first));
5099ac24 531
5e7ed085
FG
532 for first in starts {
533 let last = path
534 .iter()
535 .copied()
536 // for each segment, find the child item
537 .try_fold(first, |res, segment| {
538 let def_id = res.def_id();
539 if let Some(item) = item_child_by_name(tcx, def_id, segment) {
540 Some(item)
541 } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
542 // it is not a child item so check inherent impl items
543 tcx.inherent_impls(def_id)
544 .iter()
545 .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
546 } else {
547 None
548 }
549 });
550
551 if let Some(last) = last {
552 return last;
553 }
554 }
555
556 Res::Err
f20569fa
XL
557}
558
559/// Convenience function to get the `DefId` of a trait by path.
560/// It could be a trait or trait alias.
561pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
5099ac24 562 match def_path_res(cx, path) {
f20569fa
XL
563 Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
564 _ => None,
565 }
566}
567
f20569fa
XL
568/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
569///
570/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
571///
572/// ```rust
573/// struct Point(isize, isize);
574///
575/// impl std::ops::Add for Point {
576/// type Output = Self;
577///
578/// fn add(self, other: Self) -> Self {
579/// Point(0, 0)
580/// }
581/// }
582/// ```
5099ac24 583pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
f20569fa 584 // Get the implemented trait for the current function
5099ac24 585 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
f20569fa
XL
586 let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
587 if_chain! {
5099ac24
FG
588 if parent_impl != CRATE_DEF_ID;
589 if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
f20569fa 590 if let hir::ItemKind::Impl(impl_) = &item.kind;
5099ac24
FG
591 then {
592 return impl_.of_trait.as_ref();
593 }
f20569fa
XL
594 }
595 None
596}
597
94222f64
XL
598/// This method will return tuple of projection stack and root of the expression,
599/// used in `can_mut_borrow_both`.
600///
601/// For example, if `e` represents the `v[0].a.b[x]`
602/// this method will return a tuple, composed of a `Vec`
603/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
604/// and an `Expr` for root of them, `v`
605fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
606 let mut result = vec![];
607 let root = loop {
608 match e.kind {
609 ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
610 result.push(e);
611 e = ep;
612 },
613 _ => break e,
614 };
615 };
616 result.reverse();
617 (result, root)
618}
619
5099ac24
FG
620/// Gets the mutability of the custom deref adjustment, if any.
621pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
622 cx.typeck_results()
623 .expr_adjustments(e)
624 .iter()
625 .find_map(|a| match a.kind {
626 Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
627 Adjust::Deref(None) => None,
628 _ => Some(None),
629 })
630 .and_then(|x| x)
631}
632
94222f64
XL
633/// Checks if two expressions can be mutably borrowed simultaneously
634/// and they aren't dependent on borrowing same thing twice
635pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
636 let (s1, r1) = projection_stack(e1);
637 let (s2, r2) = projection_stack(e2);
638 if !eq_expr_value(cx, r1, r2) {
639 return true;
640 }
5099ac24
FG
641 if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
642 return false;
643 }
644
94222f64 645 for (x1, x2) in s1.iter().zip(s2.iter()) {
5099ac24
FG
646 if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
647 return false;
648 }
649
94222f64
XL
650 match (&x1.kind, &x2.kind) {
651 (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
652 if i1 != i2 {
653 return true;
654 }
655 },
656 (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
657 if !eq_expr_value(cx, i1, i2) {
658 return false;
659 }
660 },
661 _ => return false,
662 }
663 }
664 false
665}
666
c295e0f8
XL
667/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
668/// constructor from the std library
669fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
670 let std_types_symbols = &[
671 sym::String,
672 sym::Vec,
673 sym::VecDeque,
674 sym::LinkedList,
675 sym::HashMap,
676 sym::BTreeMap,
677 sym::HashSet,
678 sym::BTreeSet,
679 sym::BinaryHeap,
680 ];
681
682 if let QPath::TypeRelative(_, method) = path {
683 if method.ident.name == sym::new {
684 if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
685 if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
686 return std_types_symbols
687 .iter()
5e7ed085 688 .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
c295e0f8
XL
689 }
690 }
691 }
692 }
693 false
694}
695
a2a8927a
XL
696/// Return true if the expr is equal to `Default::default` when evaluated.
697pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
698 if_chain! {
699 if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
700 if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
701 if is_diag_trait_item(cx, repl_def_id, sym::Default)
702 || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
5099ac24 703 then { true } else { false }
a2a8927a
XL
704 }
705}
706
c295e0f8
XL
707/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
708/// It doesn't cover all cases, for example indirect function calls (some of std
709/// functions are supported) but it is the best we have.
710pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
711 match &e.kind {
712 ExprKind::Lit(lit) => match lit.node {
713 LitKind::Bool(false) | LitKind::Int(0, _) => true,
714 LitKind::Str(s, _) => s.is_empty(),
715 _ => false,
716 },
717 ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
5099ac24 718 ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
a2a8927a 719 if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
c295e0f8
XL
720 if let LitKind::Int(v, _) = const_lit.node;
721 if v <= 32 && is_default_equivalent(cx, x);
722 then {
723 true
724 }
725 else {
726 false
727 }
728 },
a2a8927a 729 ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
c295e0f8
XL
730 ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
731 ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
732 _ => false,
733 }
734}
735
cdc7bbd5 736/// Checks if the top level expression can be moved into a closure as is.
c295e0f8
XL
737/// Currently checks for:
738/// * Break/Continue outside the given loop HIR ids.
3c0e092e 739/// * Yield/Return statements.
c295e0f8
XL
740/// * Inline assembly.
741/// * Usages of a field of a local where the type of the local can be partially moved.
742///
743/// For example, given the following function:
744///
745/// ```
746/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
747/// for item in iter {
748/// let s = item.1;
749/// if item.0 > 10 {
750/// continue;
751/// } else {
752/// s.clear();
753/// }
754/// }
755/// }
756/// ```
757///
758/// When called on the expression `item.0` this will return false unless the local `item` is in the
759/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
760/// isn't always safe to move into a closure when only a single field is needed.
761///
762/// When called on the `continue` expression this will return false unless the outer loop expression
763/// is in the `loop_ids` set.
764///
765/// Note that this check is not recursive, so passing the `if` expression will always return true
766/// even though sub-expressions might return false.
5099ac24 767pub fn can_move_expr_to_closure_no_visit<'tcx>(
c295e0f8
XL
768 cx: &LateContext<'tcx>,
769 expr: &'tcx Expr<'_>,
770 loop_ids: &[HirId],
771 ignore_locals: &HirIdSet,
772) -> bool {
cdc7bbd5
XL
773 match expr.kind {
774 ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
775 | ExprKind::Continue(Destination { target_id: Ok(id), .. })
c295e0f8 776 if loop_ids.contains(&id) =>
cdc7bbd5
XL
777 {
778 true
779 },
780 ExprKind::Break(..)
781 | ExprKind::Continue(_)
782 | ExprKind::Ret(_)
783 | ExprKind::Yield(..)
5099ac24 784 | ExprKind::InlineAsm(_) => false,
cdc7bbd5
XL
785 // Accessing a field of a local value can only be done if the type isn't
786 // partially moved.
c295e0f8
XL
787 ExprKind::Field(
788 &Expr {
789 hir_id,
790 kind:
791 ExprKind::Path(QPath::Resolved(
792 _,
793 Path {
794 res: Res::Local(local_id),
795 ..
796 },
797 )),
798 ..
799 },
800 _,
801 ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
cdc7bbd5
XL
802 // TODO: check if the local has been partially moved. Assume it has for now.
803 false
c295e0f8 804 },
cdc7bbd5 805 _ => true,
f20569fa
XL
806 }
807}
808
c295e0f8
XL
809/// How a local is captured by a closure
810#[derive(Debug, Clone, Copy, PartialEq, Eq)]
811pub enum CaptureKind {
812 Value,
813 Ref(Mutability),
814}
815impl CaptureKind {
816 pub fn is_imm_ref(self) -> bool {
817 self == Self::Ref(Mutability::Not)
818 }
819}
820impl std::ops::BitOr for CaptureKind {
821 type Output = Self;
822 fn bitor(self, rhs: Self) -> Self::Output {
823 match (self, rhs) {
824 (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
825 (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
826 | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
827 (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
828 }
829 }
830}
831impl std::ops::BitOrAssign for CaptureKind {
832 fn bitor_assign(&mut self, rhs: Self) {
833 *self = *self | rhs;
834 }
835}
836
837/// Given an expression referencing a local, determines how it would be captured in a closure.
838/// Note as this will walk up to parent expressions until the capture can be determined it should
839/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
840/// function argument (other than a receiver).
5099ac24 841pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
c295e0f8
XL
842 fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
843 let mut capture = CaptureKind::Ref(Mutability::Not);
844 pat.each_binding_or_first(&mut |_, id, span, _| match cx
845 .typeck_results()
846 .extract_binding_mode(cx.sess(), id, span)
847 .unwrap()
848 {
849 BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
850 capture = CaptureKind::Value;
851 },
852 BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
853 capture = CaptureKind::Ref(Mutability::Mut);
854 },
855 _ => (),
856 });
857 capture
858 }
859
860 debug_assert!(matches!(
861 e.kind,
862 ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
863 ));
864
865 let mut child_id = e.hir_id;
866 let mut capture = CaptureKind::Value;
867 let mut capture_expr_ty = e;
868
869 for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
870 if let [
871 Adjustment {
872 kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
873 target,
874 },
875 ref adjust @ ..,
876 ] = *cx
877 .typeck_results()
878 .adjustments()
879 .get(child_id)
880 .map_or(&[][..], |x| &**x)
881 {
882 if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
883 *adjust.last().map_or(target, |a| a.target).kind()
884 {
885 return CaptureKind::Ref(mutability);
886 }
887 }
888
889 match parent {
890 Node::Expr(e) => match e.kind {
891 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
892 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
893 ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
894 return CaptureKind::Ref(Mutability::Mut);
895 },
896 ExprKind::Field(..) => {
897 if capture == CaptureKind::Value {
898 capture_expr_ty = e;
899 }
900 },
a2a8927a
XL
901 ExprKind::Let(let_expr) => {
902 let mutability = match pat_capture_kind(cx, let_expr.pat) {
c295e0f8
XL
903 CaptureKind::Value => Mutability::Not,
904 CaptureKind::Ref(m) => m,
905 };
906 return CaptureKind::Ref(mutability);
907 },
908 ExprKind::Match(_, arms, _) => {
909 let mut mutability = Mutability::Not;
910 for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
911 match capture {
912 CaptureKind::Value => break,
913 CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
914 CaptureKind::Ref(Mutability::Not) => (),
915 }
916 }
917 return CaptureKind::Ref(mutability);
918 },
919 _ => break,
920 },
921 Node::Local(l) => match pat_capture_kind(cx, l.pat) {
922 CaptureKind::Value => break,
923 capture @ CaptureKind::Ref(_) => return capture,
924 },
925 _ => break,
926 }
927
928 child_id = parent_id;
929 }
930
931 if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
932 // Copy types are never automatically captured by value.
933 CaptureKind::Ref(Mutability::Not)
934 } else {
935 capture
936 }
937}
938
939/// Checks if the expression can be moved into a closure as is. This will return a list of captures
940/// if so, otherwise, `None`.
5099ac24 941pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
cdc7bbd5
XL
942 struct V<'cx, 'tcx> {
943 cx: &'cx LateContext<'tcx>,
c295e0f8 944 // Stack of potential break targets contained in the expression.
cdc7bbd5 945 loops: Vec<HirId>,
c295e0f8
XL
946 /// Local variables created in the expression. These don't need to be captured.
947 locals: HirIdSet,
948 /// Whether this expression can be turned into a closure.
cdc7bbd5 949 allow_closure: bool,
c295e0f8
XL
950 /// Locals which need to be captured, and whether they need to be by value, reference, or
951 /// mutable reference.
952 captures: HirIdMap<CaptureKind>,
f20569fa 953 }
5099ac24 954 impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
cdc7bbd5
XL
955 fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
956 if !self.allow_closure {
957 return;
958 }
c295e0f8
XL
959
960 match e.kind {
961 ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
962 if !self.locals.contains(&l) {
963 let cap = capture_local_usage(self.cx, e);
964 self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
965 }
966 },
923072b8 967 ExprKind::Closure { .. } => {
c295e0f8
XL
968 let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id();
969 for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
970 let local_id = match capture.place.base {
971 PlaceBase::Local(id) => id,
972 PlaceBase::Upvar(var) => var.var_path.hir_id,
973 _ => continue,
974 };
975 if !self.locals.contains(&local_id) {
976 let capture = match capture.info.capture_kind {
5099ac24
FG
977 UpvarCapture::ByValue => CaptureKind::Value,
978 UpvarCapture::ByRef(kind) => match kind {
c295e0f8
XL
979 BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
980 BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
981 CaptureKind::Ref(Mutability::Mut)
982 },
983 },
984 };
985 self.captures
986 .entry(local_id)
987 .and_modify(|e| *e |= capture)
988 .or_insert(capture);
989 }
990 }
991 },
992 ExprKind::Loop(b, ..) => {
993 self.loops.push(e.hir_id);
994 self.visit_block(b);
995 self.loops.pop();
996 },
997 _ => {
998 self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
999 walk_expr(self, e);
1000 },
cdc7bbd5
XL
1001 }
1002 }
c295e0f8
XL
1003
1004 fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
1005 p.each_binding_or_first(&mut |_, id, _, _| {
1006 self.locals.insert(id);
1007 });
1008 }
f20569fa 1009 }
cdc7bbd5
XL
1010
1011 let mut v = V {
1012 cx,
1013 allow_closure: true,
1014 loops: Vec::new(),
c295e0f8
XL
1015 locals: HirIdSet::default(),
1016 captures: HirIdMap::default(),
cdc7bbd5
XL
1017 };
1018 v.visit_expr(expr);
c295e0f8 1019 v.allow_closure.then(|| v.captures)
f20569fa
XL
1020}
1021
1022/// Returns the method names and argument list of nested method call expressions that make up
1023/// `expr`. method/span lists are sorted with the most recent call first.
1024pub fn method_calls<'tcx>(
1025 expr: &'tcx Expr<'tcx>,
1026 max_depth: usize,
1027) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
1028 let mut method_names = Vec::with_capacity(max_depth);
1029 let mut arg_lists = Vec::with_capacity(max_depth);
1030 let mut spans = Vec::with_capacity(max_depth);
1031
1032 let mut current = expr;
1033 for _ in 0..max_depth {
5099ac24 1034 if let ExprKind::MethodCall(path, args, _) = &current.kind {
f20569fa
XL
1035 if args.iter().any(|e| e.span.from_expansion()) {
1036 break;
1037 }
1038 method_names.push(path.ident.name);
1039 arg_lists.push(&**args);
5099ac24 1040 spans.push(path.ident.span);
f20569fa
XL
1041 current = &args[0];
1042 } else {
1043 break;
1044 }
1045 }
1046
1047 (method_names, arg_lists, spans)
1048}
1049
1050/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
1051///
1052/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
1053/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
1054/// containing the `Expr`s for
1055/// `.bar()` and `.baz()`
1056pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
1057 let mut current = expr;
1058 let mut matched = Vec::with_capacity(methods.len());
1059 for method_name in methods.iter().rev() {
1060 // method chains are stored last -> first
5099ac24 1061 if let ExprKind::MethodCall(path, args, _) = current.kind {
f20569fa
XL
1062 if path.ident.name.as_str() == *method_name {
1063 if args.iter().any(|e| e.span.from_expansion()) {
1064 return None;
1065 }
17df50a5
XL
1066 matched.push(args); // build up `matched` backwards
1067 current = &args[0]; // go to parent expression
f20569fa
XL
1068 } else {
1069 return None;
1070 }
1071 } else {
1072 return None;
1073 }
1074 }
1075 // Reverse `matched` so that it is in the same order as `methods`.
1076 matched.reverse();
1077 Some(matched)
1078}
1079
1080/// Returns `true` if the provided `def_id` is an entrypoint to a program.
1081pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
1082 cx.tcx
17df50a5 1083 .entry_fn(())
cdc7bbd5 1084 .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
f20569fa
XL
1085}
1086
1087/// Returns `true` if the expression is in the program's `#[panic_handler]`.
1088pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1089 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
5099ac24 1090 Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
f20569fa
XL
1091}
1092
1093/// Gets the name of the item the expression is in, if available.
1094pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
1095 let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
5099ac24 1096 match cx.tcx.hir().find_by_def_id(parent_id) {
f20569fa
XL
1097 Some(
1098 Node::Item(Item { ident, .. })
1099 | Node::TraitItem(TraitItem { ident, .. })
1100 | Node::ImplItem(ImplItem { ident, .. }),
1101 ) => Some(ident.name),
1102 _ => None,
1103 }
1104}
1105
cdc7bbd5
XL
1106pub struct ContainsName {
1107 pub name: Symbol,
1108 pub result: bool,
f20569fa
XL
1109}
1110
1111impl<'tcx> Visitor<'tcx> for ContainsName {
f20569fa
XL
1112 fn visit_name(&mut self, _: Span, name: Symbol) {
1113 if self.name == name {
1114 self.result = true;
1115 }
1116 }
f20569fa
XL
1117}
1118
1119/// Checks if an `Expr` contains a certain name.
1120pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
1121 let mut cn = ContainsName { name, result: false };
1122 cn.visit_expr(expr);
1123 cn.result
1124}
1125
1126/// Returns `true` if `expr` contains a return expression
1127pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
a2a8927a
XL
1128 let mut found = false;
1129 expr_visitor_no_bodies(|expr| {
1130 if !found {
f20569fa 1131 if let hir::ExprKind::Ret(..) = &expr.kind {
a2a8927a 1132 found = true;
f20569fa
XL
1133 }
1134 }
a2a8927a
XL
1135 !found
1136 })
1137 .visit_expr(expr);
1138 found
f20569fa
XL
1139}
1140
f20569fa
XL
1141/// Extends the span to the beginning of the spans line, incl. whitespaces.
1142///
a2a8927a 1143/// ```rust
f20569fa
XL
1144/// let x = ();
1145/// // ^^
1146/// // will be converted to
1147/// let x = ();
1148/// // ^^^^^^^^^^^^^^
1149/// ```
1150fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
1151 let span = original_sp(span, DUMMY_SP);
1152 let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
1153 let line_no = source_map_and_line.line;
923072b8 1154 let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
94222f64 1155 span.with_lo(line_start)
f20569fa
XL
1156}
1157
cdc7bbd5
XL
1158/// Gets the parent node, if any.
1159pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
1160 tcx.hir().parent_iter(id).next().map(|(_, node)| node)
f20569fa
XL
1161}
1162
1163/// Gets the parent expression, if any –- this is useful to constrain a lint.
1164pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
17df50a5
XL
1165 get_parent_expr_for_hir(cx, e.hir_id)
1166}
1167
1168/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
1169/// constraint lints
1170pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
1171 match get_parent_node(cx.tcx, hir_id) {
cdc7bbd5
XL
1172 Some(Node::Expr(parent)) => Some(parent),
1173 _ => None,
1174 }
f20569fa
XL
1175}
1176
1177pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
1178 let map = &cx.tcx.hir();
1179 let enclosing_node = map
1180 .get_enclosing_scope(hir_id)
1181 .and_then(|enclosing_id| map.find(enclosing_id));
1182 enclosing_node.and_then(|node| match node {
1183 Node::Block(block) => Some(block),
1184 Node::Item(&Item {
1185 kind: ItemKind::Fn(_, _, eid),
1186 ..
1187 })
1188 | Node::ImplItem(&ImplItem {
1189 kind: ImplItemKind::Fn(_, eid),
1190 ..
1191 }) => match cx.tcx.hir().body(eid).value.kind {
17df50a5 1192 ExprKind::Block(block, _) => Some(block),
f20569fa
XL
1193 _ => None,
1194 },
1195 _ => None,
1196 })
1197}
1198
136023e0 1199/// Gets the loop or closure enclosing the given expression, if any.
5099ac24 1200pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
c295e0f8 1201 for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
17df50a5
XL
1202 match node {
1203 Node::Expr(
c295e0f8 1204 e @ Expr {
923072b8 1205 kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
17df50a5
XL
1206 ..
1207 },
1208 ) => return Some(e),
1209 Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
1210 _ => break,
1211 }
1212 }
1213 None
1214}
1215
f20569fa
XL
1216/// Gets the parent node if it's an impl block.
1217pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
c295e0f8 1218 match tcx.hir().parent_iter(id).next() {
f20569fa
XL
1219 Some((
1220 _,
1221 Node::Item(Item {
1222 kind: ItemKind::Impl(imp),
1223 ..
1224 }),
1225 )) => Some(imp),
1226 _ => None,
1227 }
1228}
1229
a2a8927a
XL
1230/// Removes blocks around an expression, only if the block contains just one expression
1231/// and no statements. Unsafe blocks are not removed.
1232///
1233/// Examples:
1234/// * `{}` -> `{}`
1235/// * `{ x }` -> `x`
1236/// * `{{ x }}` -> `x`
1237/// * `{ x; }` -> `{ x; }`
1238/// * `{ x; y }` -> `{ x; y }`
1239/// * `{ unsafe { x } }` -> `unsafe { x }`
1240pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1241 while let ExprKind::Block(
1242 Block {
1243 stmts: [],
1244 expr: Some(inner),
1245 rules: BlockCheckMode::DefaultBlock,
1246 ..
1247 },
1248 _,
1249 ) = expr.kind
1250 {
1251 expr = inner;
1252 }
1253 expr
1254}
1255
1256/// Removes blocks around an expression, only if the block contains just one expression
1257/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
1258///
1259/// Examples:
1260/// * `{}` -> `{}`
1261/// * `{ x }` -> `x`
1262/// * `{ x; }` -> `x`
1263/// * `{{ x; }}` -> `x`
1264/// * `{ x; y }` -> `{ x; y }`
1265/// * `{ unsafe { x } }` -> `unsafe { x }`
1266pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
1267 while let ExprKind::Block(
1268 Block {
1269 stmts: [],
1270 expr: Some(inner),
1271 rules: BlockCheckMode::DefaultBlock,
1272 ..
1273 }
1274 | Block {
1275 stmts:
1276 [
1277 Stmt {
1278 kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
1279 ..
1280 },
1281 ],
1282 expr: None,
1283 rules: BlockCheckMode::DefaultBlock,
1284 ..
1285 },
1286 _,
1287 ) = expr.kind
1288 {
1289 expr = inner;
1290 }
1291 expr
1292}
1293
cdc7bbd5
XL
1294/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
1295pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
c295e0f8 1296 let mut iter = tcx.hir().parent_iter(expr.hir_id);
cdc7bbd5 1297 match iter.next() {
cdc7bbd5
XL
1298 Some((
1299 _,
1300 Node::Expr(Expr {
1301 kind: ExprKind::If(_, _, Some(else_expr)),
1302 ..
1303 }),
1304 )) => else_expr.hir_id == expr.hir_id,
1305 _ => false,
f20569fa 1306 }
f20569fa
XL
1307}
1308
1309/// Checks whether the given expression is a constant integer of the given value.
1310/// unlike `is_integer_literal`, this version does const folding
1311pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
1312 if is_integer_literal(e, value) {
1313 return true;
1314 }
136023e0
XL
1315 let enclosing_body = cx.tcx.hir().local_def_id(cx.tcx.hir().enclosing_body_owner(e.hir_id));
1316 if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
3c0e092e 1317 return value == v;
f20569fa 1318 }
3c0e092e 1319 false
f20569fa
XL
1320}
1321
1322/// Checks whether the given expression is a constant literal of the given value.
1323pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
1324 // FIXME: use constant folding
1325 if let ExprKind::Lit(ref spanned) = expr.kind {
1326 if let LitKind::Int(v, _) = spanned.node {
1327 return v == value;
1328 }
1329 }
1330 false
1331}
1332
1333/// Returns `true` if the given `Expr` has been coerced before.
1334///
1335/// Examples of coercions can be found in the Nomicon at
1336/// <https://doc.rust-lang.org/nomicon/coercions.html>.
1337///
1338/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
1339/// information on adjustments and coercions.
1340pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
1341 cx.typeck_results().adjustments().get(e.hir_id).is_some()
1342}
1343
a2a8927a 1344/// Returns the pre-expansion span if this comes from an expansion of the
f20569fa 1345/// macro `name`.
3c0e092e 1346/// See also [`is_direct_expn_of`].
f20569fa
XL
1347#[must_use]
1348pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
1349 loop {
1350 if span.from_expansion() {
1351 let data = span.ctxt().outer_expn_data();
1352 let new_span = data.call_site;
1353
136023e0 1354 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
f20569fa
XL
1355 if mac_name.as_str() == name {
1356 return Some(new_span);
1357 }
1358 }
1359
1360 span = new_span;
1361 } else {
1362 return None;
1363 }
1364 }
1365}
1366
1367/// Returns the pre-expansion span if the span directly comes from an expansion
1368/// of the macro `name`.
3c0e092e
XL
1369/// The difference with [`is_expn_of`] is that in
1370/// ```rust
a2a8927a
XL
1371/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
1372/// # macro_rules! bar { ($e:expr) => { $e } }
f20569fa
XL
1373/// foo!(bar!(42));
1374/// ```
1375/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
3c0e092e 1376/// from `bar!` by `is_direct_expn_of`.
f20569fa
XL
1377#[must_use]
1378pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
1379 if span.from_expansion() {
1380 let data = span.ctxt().outer_expn_data();
1381 let new_span = data.call_site;
1382
136023e0 1383 if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
f20569fa
XL
1384 if mac_name.as_str() == name {
1385 return Some(new_span);
1386 }
1387 }
1388 }
1389
1390 None
1391}
1392
1393/// Convenience function to get the return type of a function.
1394pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
1395 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1396 let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
1397 cx.tcx.erase_late_bound_regions(ret_ty)
1398}
1399
a2a8927a
XL
1400/// Convenience function to get the nth argument type of a function.
1401pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
1402 let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
1403 let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
1404 cx.tcx.erase_late_bound_regions(arg)
1405}
1406
f20569fa
XL
1407/// Checks if an expression is constructing a tuple-like enum variant or struct
1408pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
17df50a5 1409 if let ExprKind::Call(fun, _) = expr.kind {
f20569fa
XL
1410 if let ExprKind::Path(ref qp) = fun.kind {
1411 let res = cx.qpath_res(qp, fun.hir_id);
1412 return match res {
1413 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
1414 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
1415 _ => false,
1416 };
1417 }
1418 }
1419 false
1420}
1421
1422/// Returns `true` if a pattern is refutable.
1423// TODO: should be implemented using rustc/mir_build/thir machinery
1424pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
1425 fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
1426 matches!(
1427 cx.qpath_res(qpath, id),
1428 def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
1429 )
1430 }
1431
136023e0
XL
1432 fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
1433 i.into_iter().any(|pat| is_refutable(cx, pat))
f20569fa
XL
1434 }
1435
1436 match pat.kind {
1437 PatKind::Wild => false,
1438 PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
17df50a5 1439 PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
f20569fa
XL
1440 PatKind::Lit(..) | PatKind::Range(..) => true,
1441 PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
17df50a5 1442 PatKind::Or(pats) => {
f20569fa 1443 // TODO: should be the honest check, that pats is exhaustive set
136023e0 1444 are_refutable(cx, pats)
f20569fa 1445 },
136023e0 1446 PatKind::Tuple(pats, _) => are_refutable(cx, pats),
17df50a5 1447 PatKind::Struct(ref qpath, fields, _) => {
923072b8 1448 is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
f20569fa 1449 },
136023e0
XL
1450 PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
1451 PatKind::Slice(head, middle, tail) => {
f20569fa 1452 match &cx.typeck_results().node_type(pat.hir_id).kind() {
cdc7bbd5 1453 rustc_ty::Slice(..) => {
f20569fa
XL
1454 // [..] is the only irrefutable slice pattern.
1455 !head.is_empty() || middle.is_none() || !tail.is_empty()
1456 },
136023e0 1457 rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
f20569fa
XL
1458 _ => {
1459 // unreachable!()
1460 true
1461 },
1462 }
1463 },
1464 }
1465}
1466
cdc7bbd5
XL
1467/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
1468/// the function once on the given pattern.
1469pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
1470 if let PatKind::Or(pats) = pat.kind {
136023e0 1471 pats.iter().for_each(f);
cdc7bbd5 1472 } else {
17df50a5 1473 f(pat);
cdc7bbd5
XL
1474 }
1475}
1476
f20569fa
XL
1477pub fn is_self(slf: &Param<'_>) -> bool {
1478 if let PatKind::Binding(.., name, _) = slf.pat.kind {
1479 name.name == kw::SelfLower
1480 } else {
1481 false
1482 }
1483}
1484
1485pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
3c0e092e 1486 if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
5099ac24 1487 if let Res::SelfTy { .. } = path.res {
3c0e092e 1488 return true;
f20569fa
XL
1489 }
1490 }
1491 false
1492}
1493
1494pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
1495 (0..decl.inputs.len()).map(move |i| &body.params[i])
1496}
1497
1498/// Checks if a given expression is a match expression expanded from the `?`
1499/// operator or the `try` macro.
cdc7bbd5
XL
1500pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
1501 fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
f20569fa 1502 if_chain! {
17df50a5 1503 if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
cdc7bbd5 1504 if is_lang_ctor(cx, path, ResultOk);
f20569fa
XL
1505 if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
1506 if path_to_local_id(arm.body, hir_id);
1507 then {
1508 return true;
1509 }
1510 }
1511 false
1512 }
1513
cdc7bbd5 1514 fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
f20569fa 1515 if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
cdc7bbd5 1516 is_lang_ctor(cx, path, ResultErr)
f20569fa
XL
1517 } else {
1518 false
1519 }
1520 }
1521
17df50a5 1522 if let ExprKind::Match(_, arms, ref source) = expr.kind {
f20569fa 1523 // desugared from a `?` operator
c295e0f8 1524 if *source == MatchSource::TryDesugar {
f20569fa
XL
1525 return Some(expr);
1526 }
1527
1528 if_chain! {
1529 if arms.len() == 2;
1530 if arms[0].guard.is_none();
1531 if arms[1].guard.is_none();
5099ac24 1532 if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
f20569fa
XL
1533 then {
1534 return Some(expr);
1535 }
1536 }
1537 }
1538
1539 None
1540}
1541
1542/// Returns `true` if the lint is allowed in the current context
1543///
1544/// Useful for skipping long running code when it's unnecessary
136023e0 1545pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
f20569fa
XL
1546 cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
1547}
1548
1549pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
1550 while let PatKind::Ref(subpat, _) = pat.kind {
1551 pat = subpat;
1552 }
1553 pat
1554}
1555
cdc7bbd5 1556pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
f20569fa
XL
1557 Integer::from_int_ty(&tcx, ity).size().bits()
1558}
1559
923072b8 1560#[expect(clippy::cast_possible_wrap)]
f20569fa 1561/// Turn a constant int byte representation into an i128
cdc7bbd5 1562pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
f20569fa
XL
1563 let amt = 128 - int_bits(tcx, ity);
1564 ((u as i128) << amt) >> amt
1565}
1566
923072b8 1567#[expect(clippy::cast_sign_loss)]
f20569fa 1568/// clip unused bytes
cdc7bbd5 1569pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
f20569fa
XL
1570 let amt = 128 - int_bits(tcx, ity);
1571 ((u as u128) << amt) >> amt
1572}
1573
1574/// clip unused bytes
cdc7bbd5 1575pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
f20569fa
XL
1576 let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
1577 let amt = 128 - bits;
1578 (u << amt) >> amt
1579}
1580
a2a8927a
XL
1581pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
1582 attrs.iter().any(|attr| attr.has_name(symbol))
1583}
1584
1585pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
f20569fa
XL
1586 let map = &tcx.hir();
1587 let mut prev_enclosing_node = None;
1588 let mut enclosing_node = node;
1589 while Some(enclosing_node) != prev_enclosing_node {
a2a8927a 1590 if has_attr(map.attrs(enclosing_node), symbol) {
f20569fa
XL
1591 return true;
1592 }
1593 prev_enclosing_node = Some(enclosing_node);
5099ac24 1594 enclosing_node = map.local_def_id_to_hir_id(map.get_parent_item(enclosing_node));
f20569fa 1595 }
a2a8927a 1596
f20569fa
XL
1597 false
1598}
1599
a2a8927a
XL
1600pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
1601 any_parent_has_attr(tcx, node, sym::automatically_derived)
1602}
1603
f20569fa
XL
1604/// Matches a function call with the given path and returns the arguments.
1605///
1606/// Usage:
1607///
1608/// ```rust,ignore
1609/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
1610/// ```
1611pub fn match_function_call<'tcx>(
1612 cx: &LateContext<'tcx>,
1613 expr: &'tcx Expr<'_>,
1614 path: &[&str],
1615) -> Option<&'tcx [Expr<'tcx>]> {
1616 if_chain! {
17df50a5 1617 if let ExprKind::Call(fun, args) = expr.kind;
f20569fa
XL
1618 if let ExprKind::Path(ref qpath) = fun.kind;
1619 if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
1620 if match_def_path(cx, fun_def_id, path);
1621 then {
5099ac24 1622 return Some(args);
f20569fa
XL
1623 }
1624 };
1625 None
1626}
1627
cdc7bbd5
XL
1628/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
1629/// any.
94222f64 1630///
5099ac24 1631/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
cdc7bbd5
XL
1632pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
1633 let search_path = cx.get_def_path(did);
1634 paths
1635 .iter()
1636 .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
f20569fa
XL
1637}
1638
cdc7bbd5 1639/// Checks if the given `DefId` matches the path.
f20569fa 1640pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
cdc7bbd5
XL
1641 // We should probably move to Symbols in Clippy as well rather than interning every time.
1642 let path = cx.get_def_path(did);
1643 syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
f20569fa
XL
1644}
1645
a2a8927a
XL
1646/// Checks if the given `DefId` matches the `libc` item.
1647pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
1648 let path = cx.get_def_path(did);
1649 // libc is meant to be used as a flat list of names, but they're all actually defined in different
1650 // modules based on the target platform. Ignore everything but crate name and the item name.
1651 path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
1652}
1653
f20569fa
XL
1654/// Returns the list of condition expressions and the list of blocks in a
1655/// sequence of `if/else`.
1656/// E.g., this returns `([a, b], [c, d, e])` for the expression
1657/// `if a { c } else if b { d } else { e }`.
cdc7bbd5
XL
1658pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
1659 let mut conds = Vec::new();
1660 let mut blocks: Vec<&Block<'_>> = Vec::new();
f20569fa 1661
94222f64 1662 while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
923072b8 1663 conds.push(cond);
c295e0f8 1664 if let ExprKind::Block(block, _) = then.kind {
f20569fa
XL
1665 blocks.push(block);
1666 } else {
1667 panic!("ExprKind::If node is not an ExprKind::Block");
1668 }
1669
c295e0f8 1670 if let Some(else_expr) = r#else {
f20569fa
XL
1671 expr = else_expr;
1672 } else {
1673 break;
1674 }
1675 }
1676
1677 // final `else {..}`
1678 if !blocks.is_empty() {
17df50a5
XL
1679 if let ExprKind::Block(block, _) = expr.kind {
1680 blocks.push(block);
f20569fa
XL
1681 }
1682 }
1683
1684 (conds, blocks)
1685}
1686
17df50a5
XL
1687/// Checks if the given function kind is an async function.
1688pub fn is_async_fn(kind: FnKind<'_>) -> bool {
04454e1e 1689 matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
17df50a5
XL
1690}
1691
1692/// Peels away all the compiler generated code surrounding the body of an async function,
5099ac24 1693pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
17df50a5
XL
1694 if let ExprKind::Call(
1695 _,
c295e0f8
XL
1696 &[
1697 Expr {
923072b8 1698 kind: ExprKind::Closure { body, .. },
c295e0f8
XL
1699 ..
1700 },
1701 ],
17df50a5
XL
1702 ) = body.value.kind
1703 {
1704 if let ExprKind::Block(
1705 Block {
1706 stmts: [],
1707 expr:
1708 Some(Expr {
1709 kind: ExprKind::DropTemps(expr),
1710 ..
1711 }),
1712 ..
1713 },
1714 _,
1715 ) = tcx.hir().body(body).value.kind
1716 {
1717 return Some(expr);
1718 }
1719 };
1720 None
1721}
1722
f20569fa
XL
1723// check if expr is calling method or function with #[must_use] attribute
1724pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1725 let did = match expr.kind {
17df50a5 1726 ExprKind::Call(path, _) => if_chain! {
f20569fa
XL
1727 if let ExprKind::Path(ref qpath) = path.kind;
1728 if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
1729 then {
1730 Some(did)
1731 } else {
1732 None
1733 }
1734 },
5099ac24 1735 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
f20569fa
XL
1736 _ => None,
1737 };
1738
04454e1e 1739 did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
f20569fa
XL
1740}
1741
136023e0
XL
1742/// Checks if an expression represents the identity function
1743/// Only examines closures and `std::convert::identity`
1744pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1745 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
1746 /// * `|x| x`
1747 /// * `|x| return x`
1748 /// * `|x| { return x }`
1749 /// * `|x| { return x; }`
1750 fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
1751 let id = if_chain! {
1752 if let [param] = func.params;
1753 if let PatKind::Binding(_, id, _, _) = param.pat.kind;
1754 then {
1755 id
1756 } else {
1757 return false;
1758 }
1759 };
1760
1761 let mut expr = &func.value;
1762 loop {
1763 match expr.kind {
1764 #[rustfmt::skip]
1765 ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
1766 | ExprKind::Ret(Some(e)) => expr = e,
1767 #[rustfmt::skip]
1768 ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
1769 if_chain! {
1770 if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
1771 if let ExprKind::Ret(Some(ret_val)) = e.kind;
1772 then {
1773 expr = ret_val;
1774 } else {
1775 return false;
1776 }
1777 }
1778 },
1779 _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
1780 }
1781 }
1782 }
1783
1784 match expr.kind {
923072b8 1785 ExprKind::Closure { body, .. } => is_body_identity_function(cx, cx.tcx.hir().body(body)),
5099ac24 1786 _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
136023e0
XL
1787 }
1788}
1789
cdc7bbd5 1790/// Gets the node where an expression is either used, or it's type is unified with another branch.
5099ac24
FG
1791/// Returns both the node and the `HirId` of the closest child node.
1792pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
cdc7bbd5 1793 let mut child_id = expr.hir_id;
c295e0f8 1794 let mut iter = tcx.hir().parent_iter(child_id);
cdc7bbd5
XL
1795 loop {
1796 match iter.next() {
1797 None => break None,
1798 Some((id, Node::Block(_))) => child_id = id,
1799 Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
1800 Some((_, Node::Expr(expr))) => match expr.kind {
1801 ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
1802 ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
1803 ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
5099ac24 1804 _ => break Some((Node::Expr(expr), child_id)),
cdc7bbd5 1805 },
5099ac24 1806 Some((_, node)) => break Some((node, child_id)),
cdc7bbd5
XL
1807 }
1808 }
1809}
1810
1811/// Checks if the result of an expression is used, or it's type is unified with another branch.
1812pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1813 !matches!(
1814 get_expr_use_or_unification_node(tcx, expr),
5099ac24
FG
1815 None | Some((
1816 Node::Stmt(Stmt {
1817 kind: StmtKind::Expr(_)
1818 | StmtKind::Semi(_)
1819 | StmtKind::Local(Local {
1820 pat: Pat {
1821 kind: PatKind::Wild,
1822 ..
1823 },
cdc7bbd5 1824 ..
5099ac24
FG
1825 }),
1826 ..
1827 }),
1828 _
1829 ))
cdc7bbd5
XL
1830 )
1831}
1832
1833/// Checks if the expression is the final expression returned from a block.
1834pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
1835 matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
1836}
1837
a2a8927a
XL
1838pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
1839 if !is_no_std_crate(cx) {
1840 Some("std")
1841 } else if !is_no_core_crate(cx) {
1842 Some("core")
1843 } else {
1844 None
1845 }
1846}
1847
f20569fa
XL
1848pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
1849 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
1850 if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
1851 attr.path == sym::no_std
1852 } else {
1853 false
1854 }
1855 })
1856}
1857
a2a8927a
XL
1858pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
1859 cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
1860 if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
1861 attr.path == sym::no_core
1862 } else {
1863 false
1864 }
1865 })
1866}
1867
f20569fa
XL
1868/// Check if parent of a hir node is a trait implementation block.
1869/// For example, `f` in
a2a8927a
XL
1870/// ```rust
1871/// # struct S;
1872/// # trait Trait { fn f(); }
f20569fa
XL
1873/// impl Trait for S {
1874/// fn f() {}
1875/// }
1876/// ```
1877pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
1878 if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
1879 matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
1880 } else {
1881 false
1882 }
1883}
1884
1885/// Check if it's even possible to satisfy the `where` clause for the item.
1886///
1887/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
1888///
1889/// ```ignore
1890/// fn foo() where i32: Iterator {
1891/// for _ in 2i32 {}
1892/// }
1893/// ```
1894pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
1895 use rustc_trait_selection::traits;
1896 let predicates = cx
1897 .tcx
1898 .predicates_of(did)
1899 .predicates
1900 .iter()
5099ac24 1901 .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
f20569fa
XL
1902 traits::impossible_predicates(
1903 cx.tcx,
1904 traits::elaborate_predicates(cx.tcx, predicates)
1905 .map(|o| o.predicate)
1906 .collect::<Vec<_>>(),
1907 )
1908}
1909
1910/// Returns the `DefId` of the callee if the given expression is a function or method call.
1911pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
1912 match &expr.kind {
1913 ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
1914 ExprKind::Call(
1915 Expr {
1916 kind: ExprKind::Path(qpath),
1917 hir_id: path_hir_id,
1918 ..
1919 },
1920 ..,
923072b8
FG
1921 ) => {
1922 // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
1923 // deref to fn pointers, dyn Fn, impl Fn - #8850
1924 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
1925 cx.typeck_results().qpath_res(qpath, *path_hir_id)
1926 {
1927 Some(id)
1928 } else {
1929 None
1930 }
1931 },
f20569fa
XL
1932 _ => None,
1933 }
1934}
1935
f20569fa
XL
1936/// Returns Option<String> where String is a textual representation of the type encapsulated in the
1937/// slice iff the given expression is a slice of primitives (as defined in the
1938/// `is_recursively_primitive_type` function) and None otherwise.
1939pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
1940 let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
1941 let expr_kind = expr_type.kind();
1942 let is_primitive = match expr_kind {
5099ac24 1943 rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
cdc7bbd5
XL
1944 rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
1945 if let rustc_ty::Slice(element_type) = inner_ty.kind() {
5099ac24 1946 is_recursively_primitive_type(*element_type)
f20569fa
XL
1947 } else {
1948 unreachable!()
1949 }
1950 },
1951 _ => false,
1952 };
1953
1954 if is_primitive {
1955 // if we have wrappers like Array, Slice or Tuple, print these
1956 // and get the type enclosed in the slice ref
5099ac24 1957 match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
cdc7bbd5
XL
1958 rustc_ty::Slice(..) => return Some("slice".into()),
1959 rustc_ty::Array(..) => return Some("array".into()),
1960 rustc_ty::Tuple(..) => return Some("tuple".into()),
f20569fa
XL
1961 _ => {
1962 // is_recursively_primitive_type() should have taken care
1963 // of the rest and we can rely on the type that is found
1964 let refs_peeled = expr_type.peel_refs();
5099ac24 1965 return Some(refs_peeled.walk().last().unwrap().to_string());
f20569fa
XL
1966 },
1967 }
1968 }
1969 None
1970}
1971
1972/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
1973/// `hash` must be comformed with `eq`
1974pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
1975where
1976 Hash: Fn(&T) -> u64,
1977 Eq: Fn(&T, &T) -> bool,
1978{
17df50a5
XL
1979 match exprs {
1980 [a, b] if eq(a, b) => return vec![(a, b)],
1981 _ if exprs.len() <= 2 => return vec![],
1982 _ => {},
f20569fa
XL
1983 }
1984
1985 let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
1986
17df50a5
XL
1987 let mut map: UnhashMap<u64, Vec<&_>> =
1988 UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
f20569fa
XL
1989
1990 for expr in exprs {
1991 match map.entry(hash(expr)) {
1992 Entry::Occupied(mut o) => {
1993 for o in o.get() {
1994 if eq(o, expr) {
1995 match_expr_list.push((o, expr));
1996 }
1997 }
1998 o.get_mut().push(expr);
1999 },
2000 Entry::Vacant(v) => {
2001 v.insert(vec![expr]);
2002 },
2003 }
2004 }
2005
2006 match_expr_list
2007}
2008
2009/// Peels off all references on the pattern. Returns the underlying pattern and the number of
2010/// references removed.
5099ac24
FG
2011pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
2012 fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
f20569fa
XL
2013 if let PatKind::Ref(pat, _) = pat.kind {
2014 peel(pat, count + 1)
2015 } else {
2016 (pat, count)
2017 }
2018 }
2019 peel(pat, 0)
2020}
2021
cdc7bbd5
XL
2022/// Peels of expressions while the given closure returns `Some`.
2023pub fn peel_hir_expr_while<'tcx>(
2024 mut expr: &'tcx Expr<'tcx>,
2025 mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
2026) -> &'tcx Expr<'tcx> {
2027 while let Some(e) = f(expr) {
2028 expr = e;
2029 }
2030 expr
2031}
2032
f20569fa
XL
2033/// Peels off up to the given number of references on the expression. Returns the underlying
2034/// expression and the number of references removed.
5099ac24 2035pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
cdc7bbd5
XL
2036 let mut remaining = count;
2037 let e = peel_hir_expr_while(expr, |e| match e.kind {
c295e0f8 2038 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
cdc7bbd5
XL
2039 remaining -= 1;
2040 Some(e)
2041 },
2042 _ => None,
2043 });
2044 (e, count - remaining)
f20569fa
XL
2045}
2046
2047/// Peels off all references on the expression. Returns the underlying expression and the number of
2048/// references removed.
5099ac24 2049pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
cdc7bbd5
XL
2050 let mut count = 0;
2051 let e = peel_hir_expr_while(expr, |e| match e.kind {
c295e0f8 2052 ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
cdc7bbd5
XL
2053 count += 1;
2054 Some(e)
2055 },
2056 _ => None,
2057 });
2058 (e, count)
f20569fa
XL
2059}
2060
136023e0
XL
2061/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
2062/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
2063pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
2064 loop {
2065 match expr.kind {
2066 ExprKind::AddrOf(_, _, e) => expr = e,
2067 ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
2068 _ => break,
2069 }
2070 }
2071 expr
2072}
2073
f20569fa 2074pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
3c0e092e
XL
2075 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2076 if let Res::Def(_, def_id) = path.res {
2077 return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
f20569fa
XL
2078 }
2079 }
3c0e092e 2080 false
f20569fa 2081}
136023e0 2082
923072b8 2083static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
a2a8927a 2084
5099ac24 2085fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
a2a8927a
XL
2086 let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
2087 let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
923072b8
FG
2088 let value = map.entry(module);
2089 match value {
a2a8927a
XL
2090 Entry::Occupied(entry) => f(entry.get()),
2091 Entry::Vacant(entry) => {
923072b8
FG
2092 let mut names = Vec::new();
2093 for id in tcx.hir().module_items(module) {
2094 if matches!(tcx.def_kind(id.def_id), DefKind::Const)
2095 && let item = tcx.hir().item(id)
2096 && let ItemKind::Const(ty, _body) = item.kind {
2097 if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
2098 // We could also check for the type name `test::TestDescAndFn`
2099 if let Res::Def(DefKind::Struct, _) = path.res {
2100 let has_test_marker = tcx
2101 .hir()
2102 .attrs(item.hir_id())
2103 .iter()
2104 .any(|a| a.has_name(sym::rustc_test_marker));
2105 if has_test_marker {
2106 names.push(item.ident.name);
2107 }
2108 }
2109 }
2110 }
2111 }
2112 names.sort_unstable();
2113 f(&*entry.insert(names))
a2a8927a
XL
2114 },
2115 }
2116}
2117
3c0e092e
XL
2118/// Checks if the function containing the given `HirId` is a `#[test]` function
2119///
5099ac24 2120/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
3c0e092e 2121pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
a2a8927a
XL
2122 with_test_item_names(tcx, tcx.parent_module(id), |names| {
2123 tcx.hir()
2124 .parent_iter(id)
2125 // Since you can nest functions we need to collect all until we leave
2126 // function scope
2127 .any(|(_id, node)| {
2128 if let Node::Item(item) = node {
2129 if let ItemKind::Fn(_, _, _) = item.kind {
2130 // Note that we have sorted the item names in the visitor,
2131 // so the binary_search gets the same as `contains`, but faster.
2132 return names.binary_search(&item.ident.name).is_ok();
2133 }
3c0e092e 2134 }
a2a8927a
XL
2135 false
2136 })
2137 })
3c0e092e
XL
2138}
2139
923072b8
FG
2140/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
2141///
2142/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
2143pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
2144 fn is_cfg_test(attr: &Attribute) -> bool {
2145 if attr.has_name(sym::cfg)
2146 && let Some(items) = attr.meta_item_list()
2147 && let [item] = &*items
2148 && item.has_name(sym::test)
2149 {
2150 true
2151 } else {
2152 false
2153 }
2154 }
2155 tcx.hir()
2156 .parent_iter(id)
2157 .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
2158 .any(is_cfg_test)
2159}
2160
3c0e092e
XL
2161/// Checks whether item either has `test` attribute applied, or
2162/// is a module with `test` in its name.
2163///
5099ac24 2164/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
3c0e092e
XL
2165pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2166 is_in_test_function(tcx, item.hir_id())
2167 || matches!(item.kind, ItemKind::Mod(..))
2168 && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
136023e0
XL
2169}
2170
2171macro_rules! op_utils {
2172 ($($name:ident $assign:ident)*) => {
2173 /// Binary operation traits like `LangItem::Add`
2174 pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
2175
2176 /// Operator-Assign traits like `LangItem::AddAssign`
2177 pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
2178
2179 /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
2180 pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
2181 match kind {
2182 $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
2183 _ => None,
2184 }
2185 }
2186 };
2187}
2188
2189op_utils! {
2190 Add AddAssign
2191 Sub SubAssign
2192 Mul MulAssign
2193 Div DivAssign
2194 Rem RemAssign
2195 BitXor BitXorAssign
2196 BitAnd BitAndAssign
2197 BitOr BitOrAssign
2198 Shl ShlAssign
2199 Shr ShrAssign
2200}