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