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