diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index d79101a687df..e275e6a2b8e3 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -1,4 +1,4 @@ -use clippy_utils::ast_utils::is_useless_with_eq_exprs; +use clippy_utils::ast::is_useless_with_eq_exprs; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::{eq_expr_value, is_in_test_function, sym}; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 0d809c17989d..9ea487d5d6f7 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,4 +1,4 @@ -use clippy_utils::ast_utils::{IdentIter, eq_id, is_useless_with_eq_exprs}; +use clippy_utils::ast::{IdentIter, is_useless_with_eq_exprs}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; @@ -606,7 +606,7 @@ fn ident_difference_via_ident_iter_with_base_location> loop { match (left_iterator.next(), right_iterator.next()) { (Some(left_ident), Some(right_ident)) => { - if !eq_id(left_ident, right_ident) { + if left_ident.name != right_ident.name { difference += IdentDifference::Single(base); if difference.is_complete() { return (difference, base); @@ -636,7 +636,7 @@ fn suggestion_with_swapped_ident( applicability: &mut Applicability, ) -> Option { get_ident(expr, location).and_then(|current_ident| { - if eq_id(current_ident, new_ident) { + if current_ident.name == new_ident.name { // We never want to suggest a non-change return None; } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index f3410c98973f..562412803132 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,10 +1,9 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] use clippy_config::Conf; -use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; +use clippy_utils::ast::EqCtxt; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, MsrvStack}; -use clippy_utils::over; use rustc_ast::PatKind::*; use rustc_ast::mut_visit::*; use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind}; @@ -50,12 +49,14 @@ declare_clippy_lint! { pub struct UnnestedOrPatterns { msrv: MsrvStack, + eq_ctxt: EqCtxt, } impl UnnestedOrPatterns { pub fn new(conf: &'static Conf) -> Self { Self { msrv: MsrvStack::new(conf.msrv), + eq_ctxt: EqCtxt::default(), } } } @@ -65,7 +66,7 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { if self.msrv.meets(msrvs::OR_PATTERNS) { - lint_unnested_or_patterns(cx, &a.pat); + lint_unnested_or_patterns(cx, &a.pat, &mut self.eq_ctxt); } } @@ -73,26 +74,26 @@ impl EarlyLintPass for UnnestedOrPatterns { if self.msrv.meets(msrvs::OR_PATTERNS) && let ast::ExprKind::Let(pat, _, _, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); + lint_unnested_or_patterns(cx, pat, &mut self.eq_ctxt); } } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { if self.msrv.meets(msrvs::OR_PATTERNS) { - lint_unnested_or_patterns(cx, &p.pat); + lint_unnested_or_patterns(cx, &p.pat, &mut self.eq_ctxt); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { if self.msrv.meets(msrvs::OR_PATTERNS) { - lint_unnested_or_patterns(cx, &l.pat); + lint_unnested_or_patterns(cx, &l.pat, &mut self.eq_ctxt); } } extract_msrv_attr!(); } -fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat, eq_ctxt: &mut EqCtxt) { if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; @@ -104,7 +105,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { remove_all_parens(&mut pat); // Transform all unnested or-patterns into nested ones, and if there were none, quit. - if !unnest_or_patterns(&mut pat) { + if !unnest_or_patterns(&mut pat, eq_ctxt) { return; } @@ -163,11 +164,12 @@ fn insert_necessary_parens(pat: &mut Box) { /// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. /// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. -fn unnest_or_patterns(pat: &mut Box) -> bool { - struct Visitor { +fn unnest_or_patterns(pat: &mut Box, eq_ctxt: &mut EqCtxt) -> bool { + struct Visitor<'a> { changed: bool, + eq_ctxt: &'a mut EqCtxt, } - impl MutVisitor for Visitor { + impl MutVisitor for Visitor<'_> { fn visit_pat(&mut self, p: &mut Pat) { // This is a bottom up transformation, so recurse first. walk_pat(self, p); @@ -192,7 +194,7 @@ fn unnest_or_patterns(pat: &mut Box) -> bool { // Focus on `p_n` and then try to transform all `p_i` where `i > n`. let mut focus_idx = 0; while focus_idx < alternatives.len() { - this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx); + this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx, self.eq_ctxt); focus_idx += 1; } self.changed |= this_level_changed; @@ -204,7 +206,10 @@ fn unnest_or_patterns(pat: &mut Box) -> bool { } } - let mut visitor = Visitor { changed: false }; + let mut visitor = Visitor { + changed: false, + eq_ctxt, + }; visitor.visit_pat(pat); visitor.changed } @@ -223,7 +228,7 @@ macro_rules! always_pat { /// Focus on `focus_idx` in `alternatives`, /// attempting to extend it with elements of the same constructor `C` /// in `alternatives[focus_idx + 1..]`. -fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: usize) -> bool { +fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: usize, eq_cx: &mut EqCtxt) -> bool { // Extract the kind; we'll need to make some changes in it. let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, Wild); // We'll focus on `alternatives[focus_idx]`, @@ -263,19 +268,19 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: Ident(b1, i1, Some(target)) => extend_with_matching( target, start, alternatives, // Binding names must match. - |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), + |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && i1.name == i2.name), |k| always_pat!(k, Ident(_, _, Some(p)) => p), ), // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. Slice(ps1) => extend_with_matching_product( ps1, start, alternatives, - |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)), + |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx, eq_cx)), |k| always_pat!(k, Slice(ps) => ps), ), // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`. Tuple(ps1) => extend_with_matching_product( ps1, start, alternatives, - |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)), + |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx, eq_cx)), |k| always_pat!(k, Tuple(ps) => ps), ), // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`. @@ -284,14 +289,14 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: |k, ps1, idx| matches!( k, TupleStruct(qself2, path2, ps2) - if eq_maybe_qself(qself1.as_deref(), qself2.as_deref()) - && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + if eq_cx.eq(qself1, qself2) + && eq_cx.eq(path1, path2) && eq_pre_post(ps1, ps2, idx, eq_cx) ), |k| always_pat!(k, TupleStruct(_, _, ps) => ps), ), // Transform a record pattern `S { fp_0, ..., fp_n }`. Struct(qself1, path1, fps1, rest1) => { - extend_with_struct_pat(qself1.as_deref(), path1, fps1, *rest1, start, alternatives) + extend_with_struct_pat(qself1.as_deref(), path1, fps1, *rest1, start, alternatives, eq_cx) }, }; @@ -310,6 +315,7 @@ fn extend_with_struct_pat( rest1: ast::PatFieldsRest, start: usize, alternatives: &mut ThinVec>, + eq_cx: &mut EqCtxt, ) -> bool { (0..fps1.len()).any(|idx| { let pos_in_2 = Cell::new(None); // The element `k`. @@ -319,8 +325,8 @@ fn extend_with_struct_pat( |k| { matches!(k, Struct(qself2, path2, fps2, rest2) if rest1 == *rest2 // If one struct pattern has `..` so must the other. - && eq_maybe_qself(qself1, qself2.as_deref()) - && eq_path(path1, path2) + && eq_cx.eq(&qself1, &qself2.as_deref()) + && eq_cx.eq(path1, path2) && fps1.len() == fps2.len() && fps1.iter().enumerate().all(|(idx_1, fp1)| { if idx_1 == idx { @@ -329,12 +335,12 @@ fn extend_with_struct_pat( let pos = fps2.iter().position(|fp2| { // Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }` !(fp1.is_shorthand && fp2.is_shorthand) - && eq_id(fp1.ident, fp2.ident) + && fp1.ident.name == fp2.ident.name }); pos_in_2.set(pos); pos.is_some() } else { - fps2.iter().any(|fp2| eq_field_pat(fp1, fp2)) + fps2.iter().any(|fp2| eq_cx.eq(fp1, fp2)) } })) }, @@ -354,7 +360,7 @@ fn extend_with_matching_product( targets: &mut [Box], start: usize, alternatives: &mut ThinVec>, - predicate: impl Fn(&PatKind, &[Box], usize) -> bool, + mut predicate: impl FnMut(&PatKind, &[Box], usize) -> bool, extract: impl Fn(PatKind) -> ThinVec>, ) -> bool { (0..targets.len()).any(|idx| { @@ -409,7 +415,7 @@ fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec>) -> bool { fn drain_matching( start: usize, alternatives: &mut ThinVec>, - predicate: impl Fn(&PatKind) -> bool, + mut predicate: impl FnMut(&PatKind) -> bool, extract: impl Fn(PatKind) -> Box, ) -> ThinVec> { let mut tail_or = ThinVec::new(); @@ -451,9 +457,9 @@ fn extend_with_matching( } /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? -fn eq_pre_post(ps1: &[Box], ps2: &[Box], idx: usize) -> bool { +fn eq_pre_post(ps1: &[Box], ps2: &[Box], idx: usize, eq_cx: &mut EqCtxt) -> bool { ps1.len() == ps2.len() && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. - && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) - && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) + && eq_cx.eq(&ps1[..idx], &ps2[..idx]) + && eq_cx.eq(&ps1[idx + 1..], &ps2[idx + 1..]) } diff --git a/clippy_utils/src/ast_utils/ident_iter.rs b/clippy_utils/src/ast/ident_iter.rs similarity index 100% rename from clippy_utils/src/ast_utils/ident_iter.rs rename to clippy_utils/src/ast/ident_iter.rs diff --git a/clippy_utils/src/ast/mod.rs b/clippy_utils/src/ast/mod.rs new file mode 100644 index 000000000000..63509339765a --- /dev/null +++ b/clippy_utils/src/ast/mod.rs @@ -0,0 +1,31 @@ +//! Utilities for manipulating and extracting information from `rustc_ast::ast`. + +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] + +use rustc_ast::BinOpKind; + +pub mod ident_iter; +pub use ident_iter::IdentIter; + +mod spanless; +pub use self::spanless::EqCtxt; + +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { + use BinOpKind::*; + matches!( + kind, + Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr + ) +} + +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. +pub fn unordered_over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { + left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) +} + +/// Checks if two AST nodes are semantically equivalent. Small syntax differences, +/// spans and node IDs are ignored. +#[inline] +pub fn spanless_eq(l: &T, r: &T) -> bool { + EqCtxt::default().eq(l, r) +} diff --git a/clippy_utils/src/ast/spanless.rs b/clippy_utils/src/ast/spanless.rs new file mode 100644 index 000000000000..4e9ae656e25a --- /dev/null +++ b/clippy_utils/src/ast/spanless.rs @@ -0,0 +1,1791 @@ +use core::hash::{Hash, Hasher}; +use core::mem; +use rustc_ast::token::{self, CommentKind, Delimiter, Lit, Token, TokenKind}; +use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_ast::{ + AngleBracketedArg, AngleBracketedArgs, AnonConst, Arm, AsmMacro, AssignOpKind, AssocItemConstraint, + AssocItemConstraintKind, AssocItemKind, AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, Attribute, BinOpKind, + BindingMode, Block, BlockCheckMode, BorrowKind, BoundAsyncness, BoundConstness, BoundPolarity, ByRef, CaptureBy, + Closure, ClosureBinder, Const, ConstItem, CoroutineKind, Defaultness, Delegation, DelegationMac, DelimArgs, + EnumDef, Expr, ExprField, ExprKind, Extern, FieldDef, Fn, FnContract, FnDecl, FnHeader, FnPtrTy, FnRetTy, FnSig, + ForLoopKind, ForeignItemKind, ForeignMod, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, + FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, FormatDebugHex, FormatOptions, + FormatPlaceholder, FormatSign, FormatTrait, GenBlockKind, GenericArg, GenericArgs, GenericBound, GenericParam, + GenericParamKind, Generics, Impl, ImplPolarity, Inline, InlineAsm, InlineAsmOperand, InlineAsmOptions, + InlineAsmRegOrRegClass, InlineAsmSym, InlineAsmTemplatePiece, IsAuto, Item, ItemKind, Label, Lifetime, LitKind, + Local, LocalKind, MacCall, MacCallStmt, MacStmtStyle, MacroDef, MatchKind, MethodCall, ModKind, ModSpans, + Movability, MutTy, Mutability, NodeId, NormalAttr, Param, ParenthesizedArgs, Pat, PatField, PatFieldsRest, PatKind, + Path, PathSegment, PolyTraitRef, PreciseCapturingArg, QSelf, RangeEnd, RangeLimits, RangeSyntax, Recovered, Safety, + StaticItem, Stmt, StmtKind, StrLit, StrStyle, StructExpr, StructRest, Term, Trait, TraitBoundModifiers, + TraitImplHeader, TraitObjectSyntax, TraitRef, Ty, TyAlias, TyKind, TyPat, TyPatKind, UnOp, UnsafeBinderCastKind, + UnsafeBinderTy, UnsafeSource, UseTree, UseTreeKind, Variant, VariantData, Visibility, VisibilityKind, + WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate, WherePredicateKind, WhereRegionPredicate, + YieldKind, +}; +use rustc_data_structures::fx::FxHasher; +use rustc_span::source_map::Spanned; +use rustc_span::{ByteSymbol, ErrorGuaranteed, Ident, Span, Symbol}; +use std::borrow::Cow; +use thin_vec::ThinVec; + +/// Context structure for comparing/hashing AST nodes. +#[derive(Default)] +pub struct EqCtxt { + /// Stores a mapping of a node's hash to the index it's stored at. See + /// `UnorderedMatcher` for details. + unordered: Vec<(u64, usize)>, + hasher: FxHasher, +} + +impl EqCtxt { + /// Checks if two AST nodes are semantically equivalent. Small syntax differences, + /// spans and node IDs are ignored. + #[inline] + #[must_use] + pub fn eq(&mut self, l: &T, r: &T) -> bool { + T::eq(self, l, r) + } + + /// Hashes an AST node in a manner compatible with `eq`. + #[must_use] + pub fn hash(&mut self, item: &T) -> u64 { + // Swap out the current hasher state for a fresh one. We might be in the + // middle of hashing a node. + let hasher = mem::take(&mut self.hasher); + T::hash(self, item); + let hash = self.hasher.finish(); + self.hasher = hasher; + hash + } + + /// Optimized version of `unordered_eq` for `PatField`. Uses the field name + /// instead of a hash as it's almost certainly unique. + fn eq_pat_fields(&mut self, l: &[PatField], r: &[PatField]) -> bool { + fn hash_name(_: &mut EqCtxt, p: &PatField) -> u64 { + u64::from(p.ident.name.as_u32()) + } + + if l.len() != r.len() { + return false; + } + if l.is_empty() { + return true; + } + + let mut matcher = UnorderedMatcher::new(self, r, hash_name); + matcher.eq(l, r, hash_name) + } + + /// Compares two slices for equality ignoring the order of the elements. + fn unordered_eq(&mut self, l: &[T], r: &[T]) -> bool { + if l.len() != r.len() { + return false; + } + if l.is_empty() { + return true; + } + + let mut matcher = UnorderedMatcher::new(self, r, Self::hash); + matcher.eq(l, r, Self::hash) + } + + fn unordered_eq_by_key(&mut self, l: &[T], r: &[T], mut key: impl FnMut(&T) -> &U) -> bool { + if l.len() != r.len() { + return false; + } + if l.is_empty() { + return true; + } + + let mut matcher = UnorderedMatcher::new(self, r, |cx, item| cx.hash(key(item))); + matcher.eq(l, r, |cx, item| cx.hash(key(item))) + } + + fn write_unordered_hash(&mut self, items: &[T]) { + self.hasher.write_length_prefix(items.len()); + let matcher = UnorderedMatcher::new(self, items, Self::hash); + let items = &mut matcher.cx.unordered[matcher.start..]; + items.sort_unstable_by_key(|&(x, _)| x); + for &mut (x, _) in items { + matcher.cx.hasher.write_u64(x); + } + } +} + +/// Helper to implement unordered slice equality. Each instance of this represent +/// a single frame on the shared stack (`EqCtxt::unordered`). +/// +/// As the size of each frame isn't stored, only the top frame on the stack can be +/// safely used. Dropping this will pop the frame off the stack. +struct UnorderedMatcher<'a> { + cx: &'a mut EqCtxt, + start: usize, +} +impl<'a> UnorderedMatcher<'a> { + /// Allocates a frame for the list onto the top of the stack + fn new<'item, T>( + cx: &'a mut EqCtxt, + items: &'item [T], + mut hash: impl FnMut(&mut EqCtxt, &'item T) -> u64, + ) -> Self { + let start = cx.unordered.len(); + cx.unordered.resize_with(start + items.len(), || (0, 0)); + for (i, item) in items.iter().enumerate() { + let hash = hash(cx, item); + cx.unordered[start + i] = (hash, i); + } + Self { cx, start } + } + + /// Compares all items without considering order. + /// + /// `l` is the items that were pushed onto the stack. `r` is the list to + /// compare against. + fn eq<'item, T: 'item + AstNode>( + &mut self, + l: &'item [T], + r: &[T], + mut hash: impl FnMut(&mut EqCtxt, &'item T) -> u64, + ) -> bool { + // This has to be the top frame of the stack in order to work. + debug_assert_eq!(self.cx.unordered.len() - self.start, l.len()); + + 'outer: for l in l { + let l_hash = hash(self.cx, l); + let mut start = self.start; + while let Some(idx) = self.cx.unordered[start..].iter().position(|&(r, _)| l_hash == r) { + let r = &r[self.cx.unordered[idx].1]; + if self.cx.eq(l, r) { + // The order of elements in a frame doesn't matter so + // `swap_remove` is fine. + self.cx.unordered.swap_remove(idx); + continue 'outer; + } + start = idx + 1; + } + return false; + } + true + } +} +impl Drop for UnorderedMatcher<'_> { + fn drop(&mut self) { + self.cx.unordered.truncate(self.start); + } +} + +pub trait AstNode { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool; + fn hash(cx: &mut EqCtxt, item: &Self); +} + +impl AstNode for () { + #[inline] + fn eq(_: &mut EqCtxt, (): &Self, (): &Self) -> bool { + true + } + #[inline] + fn hash(_: &mut EqCtxt, (): &Self) {} +} + +impl AstNode for &T { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + T::eq(cx, l, r) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + T::hash(cx, item); + } +} +impl AstNode for Box { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + T::eq(cx, l, r) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + T::hash(cx, item); + } +} + +impl AstNode for (T, U) { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + T::eq(cx, &l.0, &r.0) && U::eq(cx, &l.1, &r.1) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + T::hash(cx, &item.0); + U::hash(cx, &item.1); + } +} +impl AstNode for (T, U, V) { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + T::eq(cx, &l.0, &r.0) && U::eq(cx, &l.1, &r.1) && V::eq(cx, &l.2, &r.2) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + T::hash(cx, &item.0); + U::hash(cx, &item.1); + V::hash(cx, &item.2); + } +} + +impl AstNode for [T] { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + l.len() == r.len() && l.iter().zip(r).all(|(l, r)| T::eq(cx, l, r)) + } + fn hash(cx: &mut EqCtxt, item: &Self) { + cx.hasher.write_length_prefix(item.len()); + for x in item { + T::hash(cx, x); + } + } +} +impl AstNode for Vec { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + <[T]>::eq(cx, l, r) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + <[T]>::hash(cx, item); + } +} +impl AstNode for ThinVec { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + <[T]>::eq(cx, l, r) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + <[T]>::hash(cx, item); + } +} + +impl AstNode for Cow<'_, T> { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + T::eq(cx, l.as_ref(), r.as_ref()) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + T::hash(cx, item.as_ref()); + } +} + +/// Implementations for types whose values should be ignored. +macro_rules! ignore_impl { + ($($ty:ty,)*) => {$( + impl AstNode for $ty { + #[inline] + fn eq(_: &mut EqCtxt, _: &Self, _: &Self) -> bool { + true + } + #[inline] + fn hash(_: &mut EqCtxt, _: &Self) {} + } + )*}; +} +ignore_impl!( + Span, + NodeId, + AttrId, + MatchKind, + TraitObjectSyntax, + RangeSyntax, + DelimSpacing, +); + +/// All parse errors are considered unequal. +impl AstNode for ErrorGuaranteed { + #[inline] + fn eq(_: &mut EqCtxt, _: &Self, _: &Self) -> bool { + false + } + #[inline] + fn hash(_: &mut EqCtxt, _: &Self) {} +} + +/// Implementations for types which can be forwarded to `PartialEq` and `Hash`. +macro_rules! forward_impl { + ($($ty:ty,)*) => {$( + impl AstNode for $ty { + #[inline] + fn eq(_: &mut EqCtxt, l: &Self, r: &Self) -> bool { + *l == *r + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + Hash::hash(item, &mut cx.hasher); + } + } + )*}; +} +forward_impl!( + bool, + u8, + u16, + char, + usize, + str, + Symbol, + Mutability, + ByteSymbol, + Movability, + InlineAsmOptions, + FormatTrait, +); + +/// Implementations for types which can be forwarded to `PartialEq`, but need +/// to hash their discriminant value. +macro_rules! forward_impl_with_hash_discr { + ($($ty:ty,)*) => {$( + impl AstNode for $ty { + #[inline] + fn eq(_: &mut EqCtxt, l: &Self, r: &Self) -> bool { + *l == *r + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + mem::discriminant(item).hash(&mut cx.hasher); + } + } + )*}; +} +forward_impl_with_hash_discr!( + Delimiter, + AsmMacro, + LitKind, + token::LitKind, + FormatArgPositionKind, + BinOpKind, + UnOp, + FormatAlignment, + FormatSign, + FormatDebugHex, + ForLoopKind, + GenBlockKind, + AssignOpKind, + RangeLimits, + BorrowKind, + UnsafeBinderCastKind, + CommentKind, + AttrStyle, + UnsafeSource, + MacStmtStyle, + IsAuto, + PatFieldsRest, +); + +macro_rules! eq_field { + (_ $_field:tt $_cx:ident $_rhs:ident) => { + true + }; + ($field:ident $cx:ident $rhs:ident) => { + AstNode::eq($cx, $field, &$rhs.$field) + }; + ($field:ident $tuple_idx:tt $cx:ident $rhs:ident) => { + AstNode::eq($cx, $field, &$rhs.$tuple_idx) + }; + (@unordered $field:ident $cx:ident $rhs:ident) => { + $cx.unordered_eq($field, &$rhs.$field) + }; +} +macro_rules! hash_field { + (_ $_field:ident $_cx:ident $_item:ident) => {}; + ($field:ident $cx:ident $item:ident) => { + AstNode::hash($cx, &$item.$field) + }; + ($_field:ident $tuple_idx:tt $cx:ident $item:ident) => { + AstNode::hash($cx, &$item.$tuple_idx) + }; + (@unordered $field:ident $cx:ident $item:ident) => { + $cx.write_unordered_hash(&$item.$field) + }; +} + +/// Implementations for structs which will be done by comparing/hashing each field. +/// +/// The basic form of the macro calls `eq` and `hash` for each field. e.g. +/// ```ignore +/// StructName { +/// field1, +/// field2, +/// } +/// ``` +/// +/// Fields can be ignored by using `: _` after the field. e.g. +/// ```ignore +/// StructName { +/// field1, +/// field2, +/// ignored: _, +/// } +/// ``` +/// +/// Tuple structs have to provide both an index and a name. The names are a syntax +/// limitation and must be unique, but are otherwise meaningless. e.g. +/// ```ignore +/// StructName { +/// 0: _0, +/// 1: _1, +/// } +/// ``` +/// +/// Fields which deref into a slice can be compared in an unordered manner by +/// adding `#[unordered]`. e.g. +/// ```ignore +/// StructName { +/// field1, +/// field2, +/// #[unordered] slice_field, +/// } +/// ``` +/// +/// Generics can be used by adding them after the struct name. Constraints must +/// be parenthesised. e.g. +/// ```ignore +/// StructName { +/// field1, +/// field2, +/// } +/// ``` +macro_rules! impl_struct { + ($($name:ident $(<$($gargs:tt $(: ($($gbounds:tt)*))?),*>)? { + $($(#[$unordered:tt])? $fname:tt $(: $capture:tt)?),* $(,)? + })*) => {$( + impl $(<$($gargs $(: $($gbounds)*)?),*>)? AstNode for $name $(<$($gargs),*>)? { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + let $name { $($fname $(: $capture)?),* } = l; + $(eq_field!($(@$unordered)? $($capture)? $fname cx r))&&* + } + fn hash(cx: &mut EqCtxt, item: &Self) { + $(hash_field!($(@$unordered)? $($capture)? $fname cx item);)* + } + } + )*} +} + +macro_rules! eq_enum_field { + ($cx:ident _ _) => { + true + }; + ($cx:ident $lname:ident $rname:ident) => { + AstNode::eq($cx, $lname, $rname) + }; + (@unordered $cx:ident $lname:ident $rname:ident) => { + $cx.unordered_eq($lname, $rname) + }; + (@unordered_pat_fields $cx:ident $lname:ident $rname:ident) => { + $cx.eq_pat_fields($lname, $rname) + }; +} +macro_rules! hash_enum_field { + ($cx:ident _) => {}; + ($cx:ident $name:ident) => { + AstNode::hash($cx, $name) + }; + (@unordered $cx:ident $name:ident) => { + $cx.write_unordered_hash($name) + }; + (@unordered_pat_fields $cx:ident $name:ident) => { + $cx.write_unordered_hash($name) + }; +} +macro_rules! on_unequal { + (@unequal $($val:tt)*) => { $($val)? }; +} + +/// Implementations for enums which will be done by matching/hashing the variant and +/// each of it's fields. +/// +/// The basic form of the macro matches/hashes the variant, and calls `eq` and `hash` +/// for each of it's fields. Each field must be given a pair of unique names. e.g. +/// ```ignore +/// SomeType { +/// UnitVariant {}, +/// StructVariant { +/// field1: (l0, r0), +/// field2: (l1, r1), +/// }, +/// TupleVariant { +/// 0: (l0, r0), +/// 1: (l1, r1), +/// }, +/// } +/// ``` +/// +/// A variant can always be compared as unequal by adding the `#[unequal]`. e.g. +/// ```ignore +/// SomeType { +/// UnitVariant {}, +/// #[unequal] Dummy {}, +/// } +/// ``` +/// +/// Fields can be ignored by using `_` for both names after the field. e.g. +/// ```ignore +/// SomeType { +/// StructVariant { +/// field1: (l0, r0), +/// ignored: (_, _), +/// }, +/// } +/// ``` +/// +/// Fields which deref into a slice can be compared in an unordered manner by +/// adding `#[unordered]`. e.g. +/// ```ignore +/// SomeType { +/// Var { +/// #[unordered] field: (l0, r0), +/// }, +/// } +/// ``` +/// +/// Generics can be used by adding them after the struct name. Constraints must +/// be parenthesised. e.g. +/// ```ignore +/// StructName { +/// Var { +/// field: (l0, r0), +/// }, +/// } +/// ``` +macro_rules! impl_enum { + ($($name:ident $(<$($gargs:ident $(: ($($gbounds:tt)*))?),*>)? {$( + $(#[$unequal:tt])? + $vname:ident { + $($(#[$unordered:tt])? .$fname:tt: ($lname:tt, $rname:tt),)* + }, + )*})*) => {$( + #[allow(non_snake_case, unreachable_code)] + impl $(<$($gargs $(: $($gbounds)*)?),*>)? AstNode for $name $(<$($gargs),*>)? { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + match (l, r) { + $( + ( + $name::$vname { $($fname: $lname),*}, + $name::$vname { $($fname: $rname),*} + ) => { + $(on_unequal!(@$unequal return false);)? + $(eq_enum_field!($(@$unordered)? cx $lname $rname) &&)* true + }, + )* + _ => false, + } + } + fn hash(cx: &mut EqCtxt, $name: &Self) { + mem::discriminant($name).hash(&mut cx.hasher); + match $name {$( + $name::$vname {$($fname: $lname),*} => { + $(on_unequal!(@$unequal return);)? + $(hash_enum_field!($(@$unordered)? cx $lname);)*} + ),*} + } + } + )*}; +} + +impl_struct! { + Ident { + name, + span, + } + Spanned { + node, + span, + } + Label { + ident, + } + PatField { + ident, + is_placeholder, + pat, + attrs, + id, + span, + is_shorthand: _, + } + QSelf { + ty, + position, + path_span, + } + Path { + segments, + span, + tokens: _, + } + PathSegment { + ident, + args, + id, + } + AngleBracketedArgs { + args, + span, + } + ParenthesizedArgs { + inputs, + output, + span, + inputs_span, + } + StructExpr { + qself, + path, + fields, + rest, + } + MethodCall { + seg, + receiver, + args, + span, + } + Closure { + binder, + capture_clause, + constness, + coroutine_kind, + movability, + fn_decl, + body, + fn_decl_span, + fn_arg_span, + } + ExprField { + ident, + is_placeholder, + expr, + attrs, + id, + span, + is_shorthand: _, + } + Arm { + is_placeholder, + pat, + body, + guard, + attrs, + id, + span, + } + Block { + rules, + stmts, + id, + span, + tokens: _, + } + MacCallStmt { + mac, + style, + attrs, + tokens: _, + } + Trait { + constness, + safety, + is_auto, + ident, + generics, + bounds, + items, + } + Impl { + generics, + of_trait, + self_ty, + items, + } + ForeignMod { + safety, + abi, + items, + extern_span, + } + TraitRef { + path, + ref_id, + } + TraitImplHeader { + defaultness, + safety, + constness, + polarity, + trait_ref, + } + StaticItem { + ident, + mutability, + safety, + ty, + expr, + define_opaque: _, + } + Fn { + ident, + defaultness, + sig, + generics, + contract, + body, + define_opaque: _, + } + TyAlias { + ident, + defaultness, + generics, + bounds, + ty, + where_clauses: _, + } + ConstItem { + ident, + defaultness, + generics, + ty, + expr, + define_opaque: _, + } + Variant { + ident, + vis, + is_placeholder, + data, + disr_expr, + attrs, + id, + span, + } + FieldDef { + ident, + vis, + safety, + is_placeholder, + ty, + default, + attrs, + id, + span, + } + FnSig { + header, + decl, + span, + } + FnHeader { + safety, + coroutine_kind, + constness, + ext, + } + FnContract { + requires, + ensures, + } + Generics { + params, + where_clause, + span, + } + WhereClause { + predicates, + span, + has_where_token: _, + } + WherePredicate { + kind, + is_placeholder, + attrs, + id, + span, + } + WhereBoundPredicate { + bounded_ty, + bound_generic_params, + bounds, + } + WhereRegionPredicate { + lifetime, + bounds, + } + WhereEqPredicate { + lhs_ty, + rhs_ty, + } + Lifetime { + ident, + id, + } + UseTree { + prefix, + kind, + span, + } + AnonConst { + value, + id, + } + FnDecl { + inputs, + output, + } + Param { + pat, + is_placeholder, + ty, + attrs, + id, + span, + } + MutTy { + ty, + mutbl, + } + FnPtrTy { + safety, + ext, + generic_params, + decl, + decl_span, + } + StrLit { + style, + symbol, + suffix, + span, + symbol_unescaped: _, + } + PolyTraitRef { + modifiers, + trait_ref, + bound_generic_params, + span, + parens: _, + } + TraitBoundModifiers { + constness, + asyncness, + polarity, + } + GenericParam { + ident, + is_placeholder, + bounds, + kind, + attrs, + id, + colon_span, + } + AssocItemConstraint { + ident, + gen_args, + kind, + id, + span, + } + MacCall { + path, + args, + } + Attribute { + style, + kind, + id, + span, + } + NormalAttr { + item, + tokens: _, + } + AttrItem { + path, + unsafety, + args, + tokens: _, + } + DelimArgs { + delim, + tokens, + dspan, + } + Lit { + kind, + symbol, + suffix, + } + Delegation { + ident, + qself, + path, + rename, + body, + from_glob, + id, + } + DelegationMac { + qself, + prefix, + suffixes, + body, + } + TyPat { + kind, + id, + span, + tokens: _, + } + Token { + kind, + span, + } + Visibility { + kind, + span, + tokens: _, + } + InlineAsm { + asm_macro, + template, + template_strs, + operands, + clobber_abis, + options, + line_spans: _, + } + ModSpans { + inner_span, + inject_use_span, + } + EnumDef { + variants, + } + DelimSpan { + open, + close, + } + MacroDef { + body, + macro_rules, + } + UnsafeBinderTy { + generic_params, + inner_ty, + } + Local { + id, + pat, + ty, + kind, + span, + attrs, + colon_sp: _, + super_: _, + tokens: _, + } + FormatArgs { + span, + template, + arguments, + is_source_literal, + uncooked_fmt_str: _, + } + FormatPlaceholder { + argument, + span, + format_trait, + format_options, + } + FormatArgPosition { + index, + kind, + span: _, + } + FormatArgument { + kind, + expr, + } + FormatOptions { + width, + precision, + alignment, + fill, + sign, + alternate, + zero_pad, + debug_hex, + } + InlineAsmSym { + id, + qself, + path, + } + Item { + id, + span, + vis, + kind, + attrs, + tokens: _, + } + BindingMode { + 0: f0, + 1: f1, + } +} + +impl_enum! { + Option { + Some { .0: (l0, r0), }, + None {}, + } + Result { + Ok { .0: (l0, r0), }, + Err { .0: (l0, r0), }, + } + Term { + Ty { .0: (l0, r0), }, + Const { .0: (l0, r0), }, + } + GenericArgs { + AngleBracketed { .0: (l0, r0), }, + Parenthesized { .0: (l0, r0), }, + ParenthesizedElided { .0: (l0, r0), }, + } + AngleBracketedArg { + Arg { .0: (l0, r0), }, + Constraint { .0: (l0, r0), }, + } + GenericArg { + Lifetime { .0: (l0, r0), }, + Type { .0: (l0, r0), }, + Const { .0: (l0, r0), }, + } + StructRest { + Base { .0: (l0, r0), }, + Rest { .0: (l0, r0), }, + None {}, + } + CaptureBy { + Ref {}, + Use { .use_kw: (l0, r0), }, + Value { .move_kw: (l0, r0), }, + } + CoroutineKind { + Async { + .span: (l0, r0), + .closure_id: (l1, r1), + .return_impl_trait_id: (l2, r2), + }, + Gen { + .span: (l0, r0), + .closure_id: (l1, r1), + .return_impl_trait_id: (l2, r2), + }, + AsyncGen { + .span: (l0, r0), + .closure_id: (l1, r1), + .return_impl_trait_id: (l2, r2), + }, + } + LocalKind { + Decl {}, + Init { .0: (l0, r0), }, + InitElse { + .0: (l0, r0), + .1: (l1, r1), + }, + } + ModKind { + Loaded { + .1: (l1, r1), + .0: (l0, r0), + .2: (l2, r2), + }, + Unloaded {}, + } + ItemKind { + ExternCrate { + .1: (l1, r1), + .0: (l0, r0), + }, + Use { .0: (l0, r0), }, + Static { .0: (l0, r0), }, + Const { .0: (l0, r0), }, + Fn { .0: (l0, r0), }, + Mod { + .1: (l1, r1), + .0: (l0, r0), + .2: (l2, r2), + }, + ForeignMod { .0: (l0, r0), }, + TyAlias { .0: (l0, r0), }, + Enum { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Struct { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Union { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Trait { .0: (l0, r0), }, + TraitAlias { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Impl { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + MacroDef { + .0: (l0, r0), + .1: (l1, r1), + }, + Delegation { .0: (l0, r0), }, + DelegationMac { .0: (l0, r0), }, + GlobalAsm { .0: (l0, r0), }, + } + Safety { + Default {}, + Safe { .0: (l0, r0), }, + Unsafe { .0: (l0, r0), }, + } + Const { + Yes { .0: (l0, r0), }, + No {}, + } + ImplPolarity { + Positive {}, + Negative { .0: (l0, r0), }, + } + ForeignItemKind { + Static { .0: (l0, r0), }, + Fn { .0: (l0, r0), }, + TyAlias { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + } + AssocItemKind { + Const { .0: (l0, r0), }, + Fn { .0: (l0, r0), }, + Type { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + Delegation { .0: (l0, r0), }, + DelegationMac { .0: (l0, r0), }, + } + VariantData { + Unit { .0: (l0, r0), }, + Struct { + .fields: (l0, r0), + .recovered: (l1, r1), + }, + Tuple { + .0: (l0, r0), + .1: (l1, r1), + }, + } + WherePredicateKind { + BoundPredicate { .0: (l0, r0), }, + RegionPredicate { .0: (l0, r0), }, + EqPredicate { .0: (l0, r0), }, + } + UseTreeKind { + Glob {}, + Simple { .0: (l0, r0), }, + Nested { + .items: (l0, r0), + .span: (l1, r1), + }, + } + Defaultness { + Final {}, + Default { .0: (l0, r0), }, + } + VisibilityKind { + Public {}, + Inherited {}, + Restricted { + .path: (l0, r0), + .id: (l1, r1), + .shorthand: (l2, r2), + }, + } + ClosureBinder { + NotPresent {}, + For { + .generic_params: (l0, r0), + .span: (l1, r1), + }, + } + FnRetTy { + Default { .0: (l0, r0), }, + Ty { .0: (l0, r0), }, + } + TyKind { + Never {}, + ImplicitSelf {}, + CVarArgs {}, + Paren { .0: (l0, r0), }, + Slice { .0: (l0, r0), }, + Ptr { .0: (l0, r0), }, + FnPtr { .0: (l0, r0), }, + Tup { .0: (l0, r0), }, + Typeof { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + UnsafeBinder { .0: (l0, r0), }, + Err { .0: (l0, r0), }, + Array { + .0: (l0, r0), + .1: (l1, r1), + }, + Ref { + .0: (l0, r0), + .1: (l1, r1), + }, + PinnedRef { + .0: (l0, r0), + .1: (l1, r1), + }, + Path { + .0: (l0, r0), + .1: (l1, r1), + }, + Pat { + .0: (l0, r0), + .1: (l1, r1), + }, + TraitObject { + .0: (l0, r0), + .1: (l1, r1), + }, + ImplTrait { + .1: (l1, r1), + .0: (l0, r0), + }, + #[unequal] Infer {}, + #[unequal] Dummy {}, + } + TyPatKind { + Range { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Or { #[unordered] .0: (l0, r0), }, + Err { .0: (l0, r0), }, + } + Extern { + None {}, + Implicit { .0: (l0, r0), }, + Explicit { + .0: (l0, r0), + .1: (l1, r1), + }, + } + BoundConstness { + Never {}, + Always { .0: (l0, r0), }, + Maybe { .0: (l0, r0), }, + } + BoundAsyncness { + Normal {}, + Async { .0: (l0, r0), }, + } + BoundPolarity { + Positive {}, + Negative { .0: (l0, r0), }, + Maybe { .0: (l0, r0), }, + } + GenericParamKind { + Lifetime {}, + Type { .default: (l0, r0), }, + Const { + .ty: (l0, r0), + .default: (l1, r1), + .span: (l2, r2), + }, + } + GenericBound { + Trait { .0: (l0, r0), }, + Outlives { .0: (l0, r0), }, + Use { + .0: (l0, r0), + .1: (l1, r1), + }, + } + PreciseCapturingArg { + Lifetime { .0: (l0, r0), }, + Arg { + .0: (l0, r0), + .1: (l1, r1), + }, + } + AssocItemConstraintKind { + Equality { .term: (l0, r0), }, + Bound { .bounds: (l0, r0), }, + } + AttrKind { + DocComment { + .1: (l1, r1), + .0: (l0, r0), + }, + Normal { .0: (l0, r0), }, + } + AttrArgs { + Empty {}, + Delimited { .0: (l0, r0), }, + Eq { + .expr: (l0, r0), + .eq_span: (l1, r1), + }, + } + TokenTree { + Token { + .1: (l1, r1), + .0: (l0, r0), + }, + Delimited { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + .3: (l3, r3), + }, + } + StmtKind { + Empty {}, + Let { .0: (l0, r0), }, + Item { .0: (l0, r0), }, + Expr { .0: (l0, r0), }, + Semi { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + } + ExprKind { + Underscore {}, + Err { .0: (l0, r0), }, + Array { .0: (l0, r0), }, + ConstBlock { .0: (l0, r0), }, + MethodCall { .0: (l0, r0), }, + Tup { .0: (l0, r0), }, + Lit { .0: (l0, r0), }, + Closure { .0: (l0, r0), }, + TryBlock { .0: (l0, r0), }, + Continue { .0: (l0, r0), }, + Ret { .0: (l0, r0), }, + InlineAsm { .0: (l0, r0), }, + MacCall { .0: (l0, r0), }, + Struct { .0: (l0, r0), }, + Paren { .0: (l0, r0), }, + Try { .0: (l0, r0), }, + Yield { .0: (l0, r0), }, + Yeet { .0: (l0, r0), }, + Become { .0: (l0, r0), }, + IncludedBytes { .0: (l0, r0), }, + FormatArgs { .0: (l0, r0), }, + Call { + .0: (l0, r0), + .1: (l1, r1), + }, + Binary { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Unary { + .0: (l0, r0), + .1: (l1, r1), + }, + Cast { + .0: (l0, r0), + .1: (l1, r1), + }, + Type { + .0: (l0, r0), + .1: (l1, r1), + }, + Let { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + .3: (l3, r3), + }, + If { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + While { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + ForLoop { + .pat: (l0, r0), + .iter: (l1, r1), + .label: (l2, r2), + .kind: (l3, r3), + .body: (l4, r4), + }, + Loop { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Match { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Block { + .0: (l0, r0), + .1: (l1, r1), + }, + Gen { + .0: (l0, r0), + .2: (l2, r2), + .1: (l1, r1), + .3: (l3, r3), + }, + Await { + .0: (l0, r0), + .1: (l1, r1), + }, + Use { + .0: (l0, r0), + .1: (l1, r1), + }, + Assign { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + AssignOp { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Field { + .0: (l0, r0), + .1: (l1, r1), + }, + Index { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Range { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Path { + .0: (l0, r0), + .1: (l1, r1), + }, + AddrOf { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Break { + .0: (l0, r0), + .1: (l1, r1), + }, + OffsetOf { + .0: (l0, r0), + .1: (l1, r1), + }, + Repeat { + .0: (l0, r0), + .1: (l1, r1), + }, + UnsafeBinderCast { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + #[unequal] Dummy {}, + } + InlineAsmTemplatePiece { + String { .0: (l0, r0), }, + Placeholder { + .operand_idx: (l0, r0), + .modifier: (l1, r1), + .span: (l2, r2), + }, + } + InlineAsmOperand { + In { + .reg: (l0, r0), + .expr: (l1, r1), + }, + Out { + .reg: (l0, r0), + .late: (l1, r1), + .expr: (l2, r2), + }, + InOut { + .reg: (l0, r0), + .late: (l1, r1), + .expr: (l2, r2), + }, + SplitInOut { + .reg: (l0, r0), + .late: (l1, r1), + .in_expr: (l2, r2), + .out_expr: (l3, r3), + }, + Const { .anon_const: (l0, r0), }, + Sym { .sym: (l0, r0), }, + Label { .block: (l0, r0), }, + } + InlineAsmRegOrRegClass { + Reg { .0: (l0, r0), }, + RegClass { .0: (l0, r0), }, + } + Inline { + Yes {}, + No { .had_parse_error: (l0, r0), }, + } + BlockCheckMode { + Default {}, + Unsafe { .0: (l0, r0), }, + } + StrStyle { + Cooked {}, + Raw { .0: (l0, r0), }, + } + Recovered { + No {}, + Yes { .0: (l0, r0), }, + } + RangeEnd { + Included { .0: (l0, r0), }, + Excluded {}, + } + FormatArgsPiece { + Literal { .0: (l0, r0), }, + Placeholder { .0: (l0, r0), }, + } + FormatArgumentKind { + Normal {}, + Named { .0: (l0, r0), }, + Captured { .0: (l0, r0), }, + } + FormatCount { + Literal { .0: (l0, r0), }, + Argument { .0: (l0, r0), }, + } + PatKind { + Paren { .0: (l0, r0), }, + Err { .0: (l0, r0), }, + Wild {}, + Rest {}, + Never {}, + Expr { .0: (l0, r0), }, + Ident { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Range { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Box { .0: (l0, r0), }, + Deref { .0: (l0, r0), }, + Ref { + .0: (l0, r0), + .1: (l1, r1), + }, + Tuple { .0: (l0, r0), }, + Slice { .0: (l0, r0), }, + Path { + .0: (l0, r0), + .1: (l1, r1), + }, + TupleStruct { + .0: (l0, r0), + .1: (l1, r1), + .2: (l2, r2), + }, + Struct { + .0: (l0, r0), + .1: (l1, r1), + .3: (l3, r3), + #[unordered_pat_fields] .2: (l2, r2), + }, + Or { + #[unordered] .0: (l0, r0), + }, + MacCall { .0: (l0, r0), }, + Guard { + .0: (l0, r0), + .1: (l1, r1), + }, + #[unequal] Missing {}, + } + ByRef { + Yes { .0: (l0, r0), }, + No {}, + } +} + +impl AstNode for Pat { + fn eq(cx: &mut EqCtxt, mut l: &Self, mut r: &Self) -> bool { + while let PatKind::Paren(x) = &l.kind { + l = &**x; + } + while let PatKind::Paren(x) = &r.kind { + r = &**x; + } + PatKind::eq(cx, &l.kind, &r.kind) + } + + fn hash(cx: &mut EqCtxt, mut item: &Self) { + while let PatKind::Paren(x) = &item.kind { + item = &**x; + } + PatKind::hash(cx, &item.kind); + } +} + +impl AstNode for Expr { + fn eq(cx: &mut EqCtxt, mut l: &Self, mut r: &Self) -> bool { + while l.attrs.is_empty() + && let ExprKind::Paren(x) = &l.kind + { + l = &**x; + } + while r.attrs.is_empty() + && let ExprKind::Paren(x) = &r.kind + { + r = &**x; + } + ExprKind::eq(cx, &l.kind, &r.kind) && AstNode::eq(cx, &l.attrs, &r.attrs) + } + + fn hash(cx: &mut EqCtxt, mut item: &Self) { + while item.attrs.is_empty() + && let ExprKind::Paren(x) = &item.kind + { + item = &**x; + } + ExprKind::hash(cx, &item.kind); + } +} + +impl AstNode for YieldKind { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + AstNode::eq(cx, &l.expr(), &r.expr()) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + AstNode::hash(cx, &item.expr()); + } +} + +impl AstNode for Stmt { + #[inline] + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + StmtKind::eq(cx, &l.kind, &r.kind) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + StmtKind::hash(cx, &item.kind); + } +} + +impl AstNode for Ty { + fn eq(cx: &mut EqCtxt, mut l: &Self, mut r: &Self) -> bool { + while let TyKind::Paren(x) = &l.kind { + l = &**x; + } + while let TyKind::Paren(x) = &r.kind { + r = &**x; + } + TyKind::eq(cx, &l.kind, &r.kind) + } + + fn hash(cx: &mut EqCtxt, mut item: &Self) { + while let TyKind::Paren(x) = &item.kind { + item = &**x; + } + TyKind::hash(cx, &item.kind); + } +} + +impl AstNode for TokenStream { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + l.len() == r.len() && l.iter().zip(r.iter()).all(|(l, r)| cx.eq(l, r)) + } + + fn hash(cx: &mut EqCtxt, item: &Self) { + cx.hasher.write_length_prefix(item.len()); + for item in item.iter() { + AstNode::hash(cx, item); + } + } +} + +impl AstNode for TokenKind { + #[inline] + fn eq(_: &mut EqCtxt, l: &Self, r: &Self) -> bool { + *l == *r + } + + fn hash(cx: &mut EqCtxt, item: &Self) { + use rustc_ast::token::TokenKind::*; + mem::discriminant(item).hash(&mut cx.hasher); + match item { + Ident(x, _) | Lifetime(x, _) | DocComment(_, _, x) => x.hash(&mut cx.hasher), + NtIdent(x, _) | NtLifetime(x, _) => AstNode::hash(cx, x), + Literal(x) => AstNode::hash(cx, x), + _ => {}, + } + } +} + +impl AstNode for Spacing { + #[inline] + fn eq(_: &mut EqCtxt, l: &Self, r: &Self) -> bool { + matches!(*l, Spacing::Joint) == matches!(*r, Spacing::Joint) + } + #[inline] + fn hash(cx: &mut EqCtxt, item: &Self) { + matches!(*item, Spacing::Joint).hash(&mut cx.hasher); + } +} + +impl AstNode for FormatArguments { + fn eq(cx: &mut EqCtxt, l: &Self, r: &Self) -> bool { + l.all_args().len() == r.all_args().len() + && AstNode::eq(cx, l.unnamed_args(), r.unnamed_args()) + && cx.unordered_eq_by_key(l.named_args(), r.named_args(), |x| &x.kind) + && cx.unordered_eq_by_key( + &l.all_args()[l.explicit_args().len()..], + &r.all_args()[r.explicit_args().len()..], + |x| &x.kind, + ) + } + + fn hash(cx: &mut EqCtxt, item: &Self) { + AstNode::hash(cx, item.unnamed_args()); + cx.write_unordered_hash(item.named_args()); + cx.write_unordered_hash(&item.all_args()[item.explicit_args().len()..]); + } +} diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs deleted file mode 100644 index ad69e6eb184e..000000000000 --- a/clippy_utils/src/ast_utils/mod.rs +++ /dev/null @@ -1,967 +0,0 @@ -//! Utilities for manipulating and extracting information from `rustc_ast::ast`. -//! -//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. - -#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] - -use crate::{both, over}; -use rustc_ast::{self as ast, *}; -use rustc_span::symbol::Ident; -use std::mem; - -pub mod ident_iter; -pub use ident_iter::IdentIter; - -pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { - use BinOpKind::*; - matches!( - kind, - Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr - ) -} - -/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. -pub fn unordered_over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { - left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) -} - -pub fn eq_id(l: Ident, r: Ident) -> bool { - l.name == r.name -} - -pub fn eq_pat(l: &Pat, r: &Pat) -> bool { - use PatKind::*; - match (&l.kind, &r.kind) { - (Missing, _) | (_, Missing) => unreachable!(), - (Paren(l), _) => eq_pat(l, r), - (_, Paren(r)) => eq_pat(l, r), - (Wild, Wild) | (Rest, Rest) => true, - (Expr(l), Expr(r)) => eq_expr(l, r), - (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { - b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) - }, - (Range(lf, lt, le), Range(rf, rt, re)) => { - eq_expr_opt(lf.as_deref(), rf.as_deref()) - && eq_expr_opt(lt.as_deref(), rt.as_deref()) - && eq_range_end(&le.node, &re.node) - }, - (Box(l), Box(r)) - | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) - | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), - (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), - (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp), - (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { - eq_maybe_qself(lqself.as_deref(), rqself.as_deref()) - && eq_path(lp, rp) - && over(lfs, rfs, |l, r| eq_pat(l, r)) - }, - (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { - lr == rr - && eq_maybe_qself(lqself.as_deref(), rqself.as_deref()) - && eq_path(lp, rp) - && unordered_over(lfs, rfs, eq_field_pat) - }, - (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - _ => false, - } -} - -pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { - match (l, r) { - (RangeEnd::Excluded, RangeEnd::Excluded) => true, - (RangeEnd::Included(l), RangeEnd::Included(r)) => { - matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) - }, - _ => false, - } -} - -pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { - l.is_placeholder == r.is_placeholder - && eq_id(l.ident, r.ident) - && eq_pat(&l.pat, &r.pat) - && over(&l.attrs, &r.attrs, eq_attr) -} - -pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { - l.position == r.position && eq_ty(&l.ty, &r.ty) -} - -pub fn eq_maybe_qself(l: Option<&QSelf>, r: Option<&QSelf>) -> bool { - match (l, r) { - (Some(l), Some(r)) => eq_qself(l, r), - (None, None) => true, - _ => false, - } -} - -pub fn eq_path(l: &Path, r: &Path) -> bool { - over(&l.segments, &r.segments, eq_path_seg) -} - -pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { - eq_id(l.ident, r.ident) && both(l.args.as_ref(), r.args.as_ref(), |l, r| eq_generic_args(l, r)) -} - -pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { - match (l, r) { - (AngleBracketed(l), AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), - (Parenthesized(l), Parenthesized(r)) => { - over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) - }, - _ => false, - } -} - -pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { - match (l, r) { - (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), - (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_item_constraint(l, r), - _ => false, - } -} - -pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { - match (l, r) { - (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), - (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), - (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), - _ => false, - } -} - -pub fn eq_expr_opt(l: Option<&Expr>, r: Option<&Expr>) -> bool { - both(l, r, eq_expr) -} - -pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { - match (l, r) { - (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), - (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, - _ => false, - } -} - -#[allow(clippy::too_many_lines)] // Just a big match statement -pub fn eq_expr(l: &Expr, r: &Expr) -> bool { - use ExprKind::*; - if !over(&l.attrs, &r.attrs, eq_attr) { - return false; - } - match (&l.kind, &r.kind) { - (Paren(l), _) => eq_expr(l, r), - (_, Paren(r)) => eq_expr(l, r), - (Err(_), Err(_)) => true, - (Dummy, _) | (_, Dummy) => unreachable!("comparing `ExprKind::Dummy`"), - (Try(l), Try(r)) | (Await(l, _), Await(r, _)) => eq_expr(l, r), - (Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)), - (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), - (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), - (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), - ( - MethodCall(box ast::MethodCall { - seg: ls, - receiver: lr, - args: la, - .. - }), - MethodCall(box ast::MethodCall { - seg: rs, - receiver: rr, - args: ra, - .. - }), - ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)), - (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), - (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), - (Lit(l), Lit(r)) => l == r, - (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), - (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), - (If(lc, lt, le), If(rc, rt, re)) => { - eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()) - }, - (While(lc, lt, ll), While(rc, rt, rl)) => { - eq_label(ll.as_ref(), rl.as_ref()) && eq_expr(lc, rc) && eq_block(lt, rt) - }, - ( - ForLoop { - pat: lp, - iter: li, - body: lt, - label: ll, - kind: lk, - }, - ForLoop { - pat: rp, - iter: ri, - body: rt, - label: rl, - kind: rk, - }, - ) => eq_label(ll.as_ref(), rl.as_ref()) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk, - (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt), - (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb), - (TryBlock(l), TryBlock(r)) => eq_block(l, r), - (Yield(l), Yield(r)) => eq_expr_opt(l.expr().map(Box::as_ref), r.expr().map(Box::as_ref)) && l.same_kind(r), - (Ret(l), Ret(r)) => eq_expr_opt(l.as_deref(), r.as_deref()), - (Break(ll, le), Break(rl, re)) => { - eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_deref(), re.as_deref()) - }, - (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()), - (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { - eq_expr(l1, r1) && eq_expr(l2, r2) - }, - (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), - (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), - (Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm), - ( - Closure(box ast::Closure { - binder: lb, - capture_clause: lc, - coroutine_kind: la, - movability: lm, - fn_decl: lf, - body: le, - .. - }), - Closure(box ast::Closure { - binder: rb, - capture_clause: rc, - coroutine_kind: ra, - movability: rm, - fn_decl: rf, - body: re, - .. - }), - ) => { - eq_closure_binder(lb, rb) - && lc == rc - && eq_coroutine_kind(*la, *ra) - && lm == rm - && eq_fn_decl(lf, rf) - && eq_expr(le, re) - }, - (Gen(lc, lb, lk, _), Gen(rc, rb, rk, _)) => lc == rc && eq_block(lb, rb) && lk == rk, - (Range(lf, lt, ll), Range(rf, rt, rl)) => { - ll == rl && eq_expr_opt(lf.as_deref(), rf.as_deref()) && eq_expr_opt(lt.as_deref(), rt.as_deref()) - }, - (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), - (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp), - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (Struct(lse), Struct(rse)) => { - eq_maybe_qself(lse.qself.as_deref(), rse.qself.as_deref()) - && eq_path(&lse.path, &rse.path) - && eq_struct_rest(&lse.rest, &rse.rest) - && unordered_over(&lse.fields, &rse.fields, eq_field) - }, - _ => false, - } -} - -fn eq_coroutine_kind(a: Option, b: Option) -> bool { - matches!( - (a, b), - (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) - | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) - | ( - Some(CoroutineKind::AsyncGen { .. }), - Some(CoroutineKind::AsyncGen { .. }) - ) - | (None, None) - ) -} - -pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { - l.is_placeholder == r.is_placeholder - && eq_id(l.ident, r.ident) - && eq_expr(&l.expr, &r.expr) - && over(&l.attrs, &r.attrs, eq_attr) -} - -pub fn eq_arm(l: &Arm, r: &Arm) -> bool { - l.is_placeholder == r.is_placeholder - && eq_pat(&l.pat, &r.pat) - && eq_expr_opt(l.body.as_deref(), r.body.as_deref()) - && eq_expr_opt(l.guard.as_deref(), r.guard.as_deref()) - && over(&l.attrs, &r.attrs, eq_attr) -} - -pub fn eq_label(l: Option<&Label>, r: Option<&Label>) -> bool { - both(l, r, |l, r| eq_id(l.ident, r.ident)) -} - -pub fn eq_block(l: &Block, r: &Block) -> bool { - l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt) -} - -pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool { - use StmtKind::*; - match (&l.kind, &r.kind) { - (Let(l), Let(r)) => { - eq_pat(&l.pat, &r.pat) - && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| eq_ty(l, r)) - && eq_local_kind(&l.kind, &r.kind) - && over(&l.attrs, &r.attrs, eq_attr) - }, - (Item(l), Item(r)) => eq_item(l, r, eq_item_kind), - (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r), - (Empty, Empty) => true, - (MacCall(l), MacCall(r)) => { - l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr) - }, - _ => false, - } -} - -pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool { - use LocalKind::*; - match (l, r) { - (Decl, Decl) => true, - (Init(l), Init(r)) => eq_expr(l, r), - (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re), - _ => false, - } -} - -pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool { - over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) -} - -#[expect(clippy::too_many_lines)] // Just a big match statement -pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { - use ItemKind::*; - match (l, r) { - (ExternCrate(ls, li), ExternCrate(rs, ri)) => ls == rs && eq_id(*li, *ri), - (Use(l), Use(r)) => eq_use_tree(l, r), - ( - Static(box StaticItem { - ident: li, - ty: lt, - mutability: lm, - expr: le, - safety: ls, - define_opaque: _, - }), - Static(box StaticItem { - ident: ri, - ty: rt, - mutability: rm, - expr: re, - safety: rs, - define_opaque: _, - }), - ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()), - ( - Const(box ConstItem { - defaultness: ld, - ident: li, - generics: lg, - ty: lt, - expr: le, - define_opaque: _, - }), - Const(box ConstItem { - defaultness: rd, - ident: ri, - generics: rg, - ty: rt, - expr: re, - define_opaque: _, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && eq_ty(lt, rt) - && eq_expr_opt(le.as_deref(), re.as_deref()) - }, - ( - Fn(box ast::Fn { - defaultness: ld, - sig: lf, - ident: li, - generics: lg, - contract: lc, - body: lb, - define_opaque: _, - }), - Fn(box ast::Fn { - defaultness: rd, - sig: rf, - ident: ri, - generics: rg, - contract: rc, - body: rb, - define_opaque: _, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && eq_opt_fn_contract(lc, rc) - && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) - }, - (Mod(ls, li, lmk), Mod(rs, ri, rmk)) => { - ls == rs - && eq_id(*li, *ri) - && match (lmk, rmk) { - (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => { - linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind)) - }, - (ModKind::Unloaded, ModKind::Unloaded) => true, - _ => false, - } - }, - (ForeignMod(l), ForeignMod(r)) => { - both(l.abi.as_ref(), r.abi.as_ref(), eq_str_lit) - && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind)) - }, - ( - TyAlias(box ast::TyAlias { - defaultness: ld, - generics: lg, - bounds: lb, - ty: lt, - .. - }), - TyAlias(box ast::TyAlias { - defaultness: rd, - generics: rg, - bounds: rb, - ty: rt, - .. - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_generics(lg, rg) - && over(lb, rb, eq_generic_bound) - && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) - }, - (Enum(li, lg, le), Enum(ri, rg, re)) => { - eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant) - }, - (Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => { - eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv) - }, - ( - Trait(box ast::Trait { - constness: lc, - is_auto: la, - safety: lu, - ident: li, - generics: lg, - bounds: lb, - items: lis, - }), - Trait(box ast::Trait { - constness: rc, - is_auto: ra, - safety: ru, - ident: ri, - generics: rg, - bounds: rb, - items: ris, - }), - ) => { - matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No) - && la == ra - && matches!(lu, Safety::Default) == matches!(ru, Safety::Default) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && over(lb, rb, eq_generic_bound) - && over(lis, ris, |l, r| eq_item(l, r, eq_assoc_item_kind)) - }, - (TraitAlias(li, lg, lb), TraitAlias(ri, rg, rb)) => { - eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound) - }, - ( - Impl(ast::Impl { - generics: lg, - of_trait: lot, - self_ty: lst, - items: li, - }), - Impl(ast::Impl { - generics: rg, - of_trait: rot, - self_ty: rst, - items: ri, - }), - ) => { - eq_generics(lg, rg) - && both(lot.as_deref(), rot.as_deref(), |l, r| { - matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default) - && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive) - && eq_defaultness(l.defaultness, r.defaultness) - && matches!(l.constness, ast::Const::No) == matches!(r.constness, ast::Const::No) - && eq_path(&l.trait_ref.path, &r.trait_ref.path) - }) - && eq_ty(lst, rst) - && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) - }, - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(li, ld), MacroDef(ri, rd)) => { - eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body) - }, - _ => false, - } -} - -pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { - use ForeignItemKind::*; - match (l, r) { - ( - Static(box StaticItem { - ident: li, - ty: lt, - mutability: lm, - expr: le, - safety: ls, - define_opaque: _, - }), - Static(box StaticItem { - ident: ri, - ty: rt, - mutability: rm, - expr: re, - safety: rs, - define_opaque: _, - }), - ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs, - ( - Fn(box ast::Fn { - defaultness: ld, - sig: lf, - ident: li, - generics: lg, - contract: lc, - body: lb, - define_opaque: _, - }), - Fn(box ast::Fn { - defaultness: rd, - sig: rf, - ident: ri, - generics: rg, - contract: rc, - body: rb, - define_opaque: _, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && eq_opt_fn_contract(lc, rc) - && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) - }, - ( - TyAlias(box ast::TyAlias { - defaultness: ld, - ident: li, - generics: lg, - where_clauses: _, - bounds: lb, - ty: lt, - }), - TyAlias(box ast::TyAlias { - defaultness: rd, - ident: ri, - generics: rg, - where_clauses: _, - bounds: rb, - ty: rt, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && over(lb, rb, eq_generic_bound) - && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) - }, - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - _ => false, - } -} - -pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { - use AssocItemKind::*; - match (l, r) { - ( - Const(box ConstItem { - defaultness: ld, - ident: li, - generics: lg, - ty: lt, - expr: le, - define_opaque: _, - }), - Const(box ConstItem { - defaultness: rd, - ident: ri, - generics: rg, - ty: rt, - expr: re, - define_opaque: _, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && eq_ty(lt, rt) - && eq_expr_opt(le.as_deref(), re.as_deref()) - }, - ( - Fn(box ast::Fn { - defaultness: ld, - sig: lf, - ident: li, - generics: lg, - contract: lc, - body: lb, - define_opaque: _, - }), - Fn(box ast::Fn { - defaultness: rd, - sig: rf, - ident: ri, - generics: rg, - contract: rc, - body: rb, - define_opaque: _, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_fn_sig(lf, rf) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && eq_opt_fn_contract(lc, rc) - && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) - }, - ( - Type(box TyAlias { - defaultness: ld, - ident: li, - generics: lg, - where_clauses: _, - bounds: lb, - ty: lt, - }), - Type(box TyAlias { - defaultness: rd, - ident: ri, - generics: rg, - where_clauses: _, - bounds: rb, - ty: rt, - }), - ) => { - eq_defaultness(*ld, *rd) - && eq_id(*li, *ri) - && eq_generics(lg, rg) - && over(lb, rb, eq_generic_bound) - && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) - }, - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - _ => false, - } -} - -pub fn eq_variant(l: &Variant, r: &Variant) -> bool { - l.is_placeholder == r.is_placeholder - && over(&l.attrs, &r.attrs, eq_attr) - && eq_vis(&l.vis, &r.vis) - && eq_id(l.ident, r.ident) - && eq_variant_data(&l.data, &r.data) - && both(l.disr_expr.as_ref(), r.disr_expr.as_ref(), |l, r| { - eq_expr(&l.value, &r.value) - }) -} - -pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool { - use VariantData::*; - match (l, r) { - (Unit(_), Unit(_)) => true, - (Struct { fields: l, .. }, Struct { fields: r, .. }) | (Tuple(l, _), Tuple(r, _)) => { - over(l, r, eq_struct_field) - }, - _ => false, - } -} - -pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { - l.is_placeholder == r.is_placeholder - && over(&l.attrs, &r.attrs, eq_attr) - && eq_vis(&l.vis, &r.vis) - && both(l.ident.as_ref(), r.ident.as_ref(), |l, r| eq_id(*l, *r)) - && eq_ty(&l.ty, &r.ty) -} - -pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool { - eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header) -} - -fn eq_opt_coroutine_kind(l: Option, r: Option) -> bool { - matches!( - (l, r), - (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) - | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) - | ( - Some(CoroutineKind::AsyncGen { .. }), - Some(CoroutineKind::AsyncGen { .. }) - ) - | (None, None) - ) -} - -pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { - matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default) - && eq_opt_coroutine_kind(l.coroutine_kind, r.coroutine_kind) - && matches!(l.constness, Const::No) == matches!(r.constness, Const::No) - && eq_ext(&l.ext, &r.ext) -} - -#[expect(clippy::ref_option, reason = "This is the type how it is stored in the AST")] -pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { - match (l, r) { - (Some(l), Some(r)) => { - eq_expr_opt(l.requires.as_deref(), r.requires.as_deref()) - && eq_expr_opt(l.ensures.as_deref(), r.ensures.as_deref()) - }, - (None, None) => true, - (Some(_), None) | (None, Some(_)) => false, - } -} - -pub fn eq_generics(l: &Generics, r: &Generics) -> bool { - over(&l.params, &r.params, eq_generic_param) - && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| { - eq_where_predicate(l, r) - }) -} - -pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { - use WherePredicateKind::*; - over(&l.attrs, &r.attrs, eq_attr) - && match (&l.kind, &r.kind) { - (BoundPredicate(l), BoundPredicate(r)) => { - over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { - eq_generic_param(l, r) - }) && eq_ty(&l.bounded_ty, &r.bounded_ty) - && over(&l.bounds, &r.bounds, eq_generic_bound) - }, - (RegionPredicate(l), RegionPredicate(r)) => { - eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound) - }, - (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty), - _ => false, - } -} - -pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool { - eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind) -} - -pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool { - eq_expr(&l.value, &r.value) -} - -pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { - use UseTreeKind::*; - match (l, r) { - (Glob, Glob) => true, - (Simple(l), Simple(r)) => both(l.as_ref(), r.as_ref(), |l, r| eq_id(*l, *r)), - (Nested { items: l, .. }, Nested { items: r, .. }) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), - _ => false, - } -} - -pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { - matches!( - (l, r), - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) - ) -} - -pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { - use VisibilityKind::*; - match (&l.kind, &r.kind) { - (Public, Public) | (Inherited, Inherited) => true, - (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r), - _ => false, - } -} - -pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool { - eq_fn_ret_ty(&l.output, &r.output) - && over(&l.inputs, &r.inputs, |l, r| { - l.is_placeholder == r.is_placeholder - && eq_pat(&l.pat, &r.pat) - && eq_ty(&l.ty, &r.ty) - && over(&l.attrs, &r.attrs, eq_attr) - }) -} - -pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool { - match (l, r) { - (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true, - (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => { - lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r)) - }, - _ => false, - } -} - -pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool { - match (l, r) { - (FnRetTy::Default(_), FnRetTy::Default(_)) => true, - (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r), - _ => false, - } -} - -pub fn eq_ty(l: &Ty, r: &Ty) -> bool { - use TyKind::*; - match (&l.kind, &r.kind) { - (Paren(l), _) => eq_ty(l, r), - (_, Paren(r)) => eq_ty(l, r), - (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err(_), Err(_)) | (CVarArgs, CVarArgs) => { - true - }, - (Slice(l), Slice(r)) => eq_ty(l, r), - (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value), - (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty), - (Ref(ll, l), Ref(rl, r)) => { - both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) - }, - (PinnedRef(ll, l), PinnedRef(rl, r)) => { - both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) - }, - (FnPtr(l), FnPtr(r)) => { - l.safety == r.safety - && eq_ext(&l.ext, &r.ext) - && over(&l.generic_params, &r.generic_params, eq_generic_param) - && eq_fn_decl(&l.decl, &r.decl) - }, - (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)), - (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp), - (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound), - (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound), - (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value), - (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - _ => false, - } -} - -pub fn eq_ext(l: &Extern, r: &Extern) -> bool { - use Extern::*; - match (l, r) { - (None, None) | (Implicit(_), Implicit(_)) => true, - (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r), - _ => false, - } -} - -pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool { - l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix -} - -pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool { - l.modifiers == r.modifiers - && eq_path(&l.trait_ref.path, &r.trait_ref.path) - && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { - eq_generic_param(l, r) - }) -} - -pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool { - use GenericParamKind::*; - l.is_placeholder == r.is_placeholder - && eq_id(l.ident, r.ident) - && over(&l.bounds, &r.bounds, eq_generic_bound) - && match (&l.kind, &r.kind) { - (Lifetime, Lifetime) => true, - (Type { default: l }, Type { default: r }) => both(l.as_ref(), r.as_ref(), |l, r| eq_ty(l, r)), - ( - Const { - ty: lt, - default: ld, - span: _, - }, - Const { - ty: rt, - default: rd, - span: _, - }, - ) => eq_ty(lt, rt) && both(ld.as_ref(), rd.as_ref(), eq_anon_const), - _ => false, - } - && over(&l.attrs, &r.attrs, eq_attr) -} - -pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool { - use GenericBound::*; - match (l, r) { - (Trait(ptr1), Trait(ptr2)) => eq_poly_ref_trait(ptr1, ptr2), - (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident), - _ => false, - } -} - -pub fn eq_precise_capture(l: &PreciseCapturingArg, r: &PreciseCapturingArg) -> bool { - match (l, r) { - (PreciseCapturingArg::Lifetime(l), PreciseCapturingArg::Lifetime(r)) => l.ident == r.ident, - (PreciseCapturingArg::Arg(l, _), PreciseCapturingArg::Arg(r, _)) => l.segments[0].ident == r.segments[0].ident, - _ => false, - } -} - -fn eq_term(l: &Term, r: &Term) -> bool { - match (l, r) { - (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r), - (Term::Const(l), Term::Const(r)) => eq_anon_const(l, r), - _ => false, - } -} - -pub fn eq_assoc_item_constraint(l: &AssocItemConstraint, r: &AssocItemConstraint) -> bool { - use AssocItemConstraintKind::*; - eq_id(l.ident, r.ident) - && match (&l.kind, &r.kind) { - (Equality { term: l }, Equality { term: r }) => eq_term(l, r), - (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound), - _ => false, - } -} - -pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool { - eq_path(&l.path, &r.path) && eq_delim_args(&l.args, &r.args) -} - -pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { - use AttrKind::*; - l.style == r.style - && match (&l.kind, &r.kind) { - (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, - (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args), - _ => false, - } -} - -pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { - use AttrArgs::*; - match (l, r) { - (Empty, Empty) => true, - (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra), - (Eq { eq_span: _, expr: le }, Eq { eq_span: _, expr: re }) => eq_expr(le, re), - _ => false, - } -} - -pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool { - l.delim == r.delim - && l.tokens.len() == r.tokens.len() - && l.tokens.iter().zip(r.tokens.iter()).all(|(a, b)| a.eq_unspanned(b)) -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0bb19dab1f7a..41696f34bdf4 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,4 +1,5 @@ #![feature(box_patterns)] +#![feature(hasher_prefixfree_extras)] #![feature(if_let_guard)] #![feature(macro_metavar_expr)] #![feature(never_type)] @@ -48,8 +49,9 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_trait_selection; extern crate smallvec; +extern crate thin_vec; -pub mod ast_utils; +pub mod ast; pub mod attrs; mod check_proc_macro; pub mod comparisons; @@ -88,7 +90,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; -use rustc_ast::ast::{self, LitKind, RangeLimits}; +use rustc_ast::ast::{LitKind, RangeLimits}; use rustc_ast::join_path_syms; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; @@ -126,7 +128,7 @@ use rustc_span::{InnerSpan, Span}; use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; -use crate::ast_utils::unordered_over; +use crate::ast::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; @@ -2330,7 +2332,7 @@ pub fn peel_hir_expr_while<'tcx>( pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { let mut remaining = count; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => { + ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, e) if remaining != 0 => { remaining -= 1; Some(e) }, @@ -2356,7 +2358,7 @@ pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { let mut count = 0; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => { + ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, e) => { count += 1; Some(e) }, @@ -2709,7 +2711,7 @@ pub enum ExprUseNode<'tcx> { /// Access of a field. FieldAccess(Ident), /// Borrow expression. - AddrOf(ast::BorrowKind, Mutability), + AddrOf(rustc_ast::BorrowKind, Mutability), Other, } impl<'tcx> ExprUseNode<'tcx> {