Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store.rs')
| -rw-r--r-- | crates/hir-def/src/expr_store.rs | 677 |
1 files changed, 401 insertions, 276 deletions
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 497ed7d37f..4dc7267231 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -9,7 +9,10 @@ pub mod scope; #[cfg(test)] mod tests; -use std::ops::{Deref, Index}; +use std::{ + borrow::Borrow, + ops::{Deref, Index}, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -25,14 +28,18 @@ use tt::TextRange; use crate::{ AdtId, BlockId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, db::DefDatabase, - expr_store::path::Path, + expr_store::path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path}, hir::{ - Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, RecordSpread, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, InlineAsm, Label, + LabelId, MatchArm, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, }, nameres::{DefMap, block_def_map}, signatures::VariantFields, - type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId}, + type_ref::{ + ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, PathId, RefType, TypeBound, + TypeRef, TypeRefId, UseArgRef, + }, }; pub use self::body::{Body, BodySourceMap}; @@ -91,21 +98,15 @@ pub type TypeSource = InFile<TypePtr>; pub type LifetimePtr = AstPtr<ast::Lifetime>; pub type LifetimeSource = InFile<LifetimePtr>; -/// Describes where a const expression originated from. -/// -/// Used by signature/body inference to determine the expected type for each -/// const expression root. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum RootExprOrigin { - /// Array length expression: `[T; <expr>]` — expected type is `usize`. - ArrayLength, - /// Const parameter default value: `const N: usize = <expr>`. - ConstParam(crate::hir::generics::LocalTypeOrConstParamId), - /// Const generic argument in a path: `SomeType::<{ <expr> }>` or `some_fn::<{ <expr> }>()`. - /// Determining the expected type requires path resolution, so it is deferred. - GenericArgsPath, - /// The root expression of a body. - BodyRoot, +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ExprRoot { + root: ExprId, + // We store, for each root, the range of exprs (and pats and bindings) it holds. + // We store only the end (exclusive), since the start can be inferred from the previous + // roots or is zero. + exprs_end: ExprId, + pats_end: PatId, + bindings_end: BindingId, } // We split the store into types-only and expressions, because most stores (e.g. generics) @@ -129,7 +130,34 @@ struct ExpressionOnlyStore { ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, /// Maps expression roots to their origin. - expr_roots: SmallVec<[(ExprId, RootExprOrigin); 1]>, + /// + /// Note: while every root expr is an inference root (aka. an `AnonConst`), there could be other roots that do not appear here. + /// This can happen when anon consts are nested, for example: + /// + /// ``` + /// [ + /// (); + /// { + /// // this repeat expr is anon const #1, and *only it* appears in this list. + /// [ + /// (); + /// { + /// // this repeat expr is anon const #2. + /// 0 + /// } + /// ]; + /// 0 + /// } + /// ] + /// ``` + /// We do this because this allows us to search this list using a binary search, + /// and it does not bother us because we use this list for two things: constructing `ExprScopes`, which + /// works fine with nested exprs, and retrieving inference results, and we copy the inner const's inference + /// into the outer const. + // FIXME: Array repeat is not problematic indeed, but this could still break with exprs in types, + // which we do not visit for `ExprScopes` (they're fine for inference though). We either need to visit them, + // or use a more complicated search. + expr_roots: SmallVec<[ExprRoot; 1]>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -243,7 +271,7 @@ pub struct ExpressionStoreBuilder { pub types: Arena<TypeRef>, block_scopes: Vec<BlockId>, ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, - pub inference_roots: Option<SmallVec<[(ExprId, RootExprOrigin); 1]>>, + inference_roots: Option<SmallVec<[ExprRoot; 1]>>, // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). @@ -303,6 +331,7 @@ pub enum ExpressionStoreDiagnostics { UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String }, UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, + PatternArgInExternFn { node: InFile<AstPtr<ast::Pat>> }, } impl ExpressionStoreBuilder { @@ -375,8 +404,8 @@ impl ExpressionStoreBuilder { let store = { let expr_only = if has_exprs { - if let Some(const_expr_origins) = &mut expr_roots { - const_expr_origins.shrink_to_fit(); + if let Some(expr_roots) = &mut expr_roots { + expr_roots.shrink_to_fit(); } Some(Box::new(ExpressionOnlyStore { exprs, @@ -386,7 +415,8 @@ impl ExpressionStoreBuilder { binding_owners, block_scopes: block_scopes.into_boxed_slice(), ident_hygiene, - expr_roots: expr_roots.unwrap_or_default(), + expr_roots: expr_roots + .expect("should always finish with a `Some(_)` expr_roots"), })) } else { None @@ -427,6 +457,14 @@ impl ExpressionStoreBuilder { } impl ExpressionStore { + const EMPTY: &ExpressionStore = + &ExpressionStore { expr_only: None, types: Arena::new(), lifetimes: Arena::new() }; + + #[inline] + pub fn empty() -> &'static ExpressionStore { + ExpressionStore::EMPTY + } + pub fn of(db: &dyn DefDatabase, def: ExpressionStoreOwnerId) -> &ExpressionStore { match def { ExpressionStoreOwnerId::Signature(def) => { @@ -516,19 +554,35 @@ impl ExpressionStore { } /// Returns all expression root `ExprId`s found in this store. - pub fn expr_roots(&self) -> impl Iterator<Item = ExprId> { - self.const_expr_origins().iter().map(|&(id, _)| id) + pub fn expr_roots(&self) -> impl DoubleEndedIterator<Item = ExprId> { + self.expr_only + .as_ref() + .map_or(&[][..], |expr_only| &expr_only.expr_roots) + .iter() + .map(|root| root.root) } - /// Like [`Self::expr_roots`], but also returns the origin - /// of each expression. - pub fn expr_roots_with_origins(&self) -> impl Iterator<Item = (ExprId, RootExprOrigin)> { - self.const_expr_origins().iter().map(|&(id, origin)| (id, origin)) + fn find_root_for( + &self, + mut get: impl FnMut(&ExprRoot) -> la_arena::RawIdx, + find: la_arena::RawIdx, + ) -> ExprId { + let expr_only = self.assert_expr_only(); + let find = find.into_u32(); + let entry = expr_only.expr_roots.partition_point(|root| get(root).into_u32() <= find); + expr_only.expr_roots[entry].root } - /// Returns the map of const expression roots to their origins. - pub fn const_expr_origins(&self) -> &[(ExprId, RootExprOrigin)] { - self.expr_only.as_ref().map_or(&[], |it| &it.expr_roots) + pub fn find_root_for_expr(&self, expr: ExprId) -> ExprId { + self.find_root_for(|root| root.exprs_end.into_raw(), expr.into_raw()) + } + + pub fn find_root_for_pat(&self, pat: PatId) -> ExprId { + self.find_root_for(|root| root.pats_end.into_raw(), pat.into_raw()) + } + + pub fn find_root_for_binding(&self, binding: BindingId) -> ExprId { + self.find_root_for(|root| root.bindings_end.into_raw(), binding.into_raw()) } /// Returns an iterator over all block expressions in this store that define inner items. @@ -552,33 +606,46 @@ impl ExpressionStore { }); } - pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) { + pub fn visit_pat_children(&self, pat_id: PatId, mut visitor: impl StoreVisitor) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). let pat = &self[pat_id]; match pat { - Pat::Range { .. } - | Pat::Lit(..) - | Pat::Path(..) - | Pat::ConstBlock(..) - | Pat::Wild - | Pat::Missing - | Pat::Expr(_) => {} - &Pat::Bind { subpat, .. } => { - if let Some(subpat) = subpat { - f(subpat); - } + Pat::Range { start, end, range_type: _ } => { + visitor.on_expr_opt(*start); + visitor.on_expr_opt(*end); } - Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { - args.iter().copied().for_each(f); + Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), + Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), + Pat::TupleStruct { args, ellipsis: _, path } => { + visitor.on_pats(args); + visitor.on_path(path); } - Pat::Ref { pat, .. } => f(*pat), + Pat::Ref { pat, mutability: _ } => visitor.on_pat(*pat), Pat::Slice { prefix, slice, suffix } => { - let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); - total_iter.copied().for_each(f); + visitor.on_pats(prefix); + visitor.on_pat_opt(*slice); + visitor.on_pats(suffix); } - Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat)); + Pat::Record { args, ellipsis: _, path } => { + args.iter().for_each(|RecordFieldPat { pat, name: _ }| visitor.on_pat(*pat)); + visitor.on_path(path); + } + Pat::Box { inner } | Pat::Deref { inner } => visitor.on_pat(*inner), + } + } + + pub fn walk_pats_shallow(&self, pat_id: PatId, f: impl FnMut(PatId)) { + return self.visit_pat_children(pat_id, Visitor(f)); + + struct Visitor<F>(F); + + impl<F: FnMut(PatId)> StoreVisitor for Visitor<F> { + fn on_pat(&mut self, pat: PatId) { + (self.0)(pat); } - Pat::Box { inner } => f(*inner), } } @@ -604,276 +671,212 @@ impl ExpressionStore { self.expr_only.as_ref()?.binding_owners.get(&id).copied() } - /// Walks the immediate children expressions and calls `f` for each child expression. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) - | Expr::Missing - | Expr::Path(_) - | Expr::OffsetOf(_) - | Expr::Literal(_) - | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); + pub fn visit_expr_children(&self, expr_id: ExprId, mut visitor: impl StoreVisitor) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). + match &self[expr_id] { + Expr::OffsetOf(OffsetOf { container, fields: _ }) => visitor.on_type(*container), + Expr::Path(path) => visitor.on_path(path), + Expr::Continue { label: _ } | Expr::Missing | Expr::Literal(_) | Expr::Underscore => {} + Expr::InlineAsm(InlineAsm { operands, options: _, kind: _ }) => { + operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, reg: _ } + | AsmOperand::Out { expr: Some(expr), late: _, reg: _ } + | AsmOperand::InOut { expr, late: _, reg: _ } + | AsmOperand::Const(expr) + | AsmOperand::Label(expr) => visitor.on_expr(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, late: _, reg: _ } => { + visitor.on_expr(*in_expr); + visitor.on_expr_opt(*out_expr); } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), + AsmOperand::Out { expr: None, late: _, reg: _ } | AsmOperand::Sym(_) => (), + }) + } Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } + visitor.on_expr(*condition); + visitor.on_expr(*then_branch); + visitor.on_expr_opt(*else_branch); } Expr::Let { expr, pat } => { - self.walk_exprs_in_pat(*pat, &mut f); - f(*expr); + visitor.on_pat(*pat); + visitor.on_expr(*expr); } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { + Expr::Block { statements, tail, id: _, label: _ } + | Expr::Unsafe { statements, tail, id: _ } => { + for stmt in statements { match stmt { - Statement::Let { initializer, else_branch, pat, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - self.walk_exprs_in_pat(*pat, &mut f); + Statement::Let { initializer, else_branch, pat, type_ref } => { + visitor.on_expr_opt(*initializer); + visitor.on_expr_opt(*else_branch); + visitor.on_pat(*pat); + visitor.on_type_opt(*type_ref); + } + Statement::Expr { expr: expression, has_semi: _ } => { + visitor.on_expr(*expression) } - Statement::Expr { expr: expression, .. } => f(*expression), Statement::Item(_) => (), } } - if let &Some(expr) = tail { - f(expr); - } + visitor.on_expr_opt(*tail); } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); + Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Call { callee, args } => { + visitor.on_expr(*callee); + visitor.on_exprs(args); } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); + Expr::MethodCall { receiver, args, generic_args, method_name: _ } => { + visitor.on_expr(*receiver); + visitor.on_exprs(args); + visitor.on_generic_args_opt(generic_args); } Expr::Match { expr, arms } => { - f(*expr); - arms.iter().for_each(|arm| { - f(arm.expr); - if let Some(guard) = arm.guard { - f(guard); - } - self.walk_exprs_in_pat(arm.pat, &mut f); + visitor.on_expr(*expr); + arms.iter().for_each(|MatchArm { pat, guard, expr }| { + visitor.on_expr(*expr); + visitor.on_expr_opt(*guard); + visitor.on_pat(*pat); }); } - Expr::Break { expr, .. } + Expr::Break { expr, label: _ } | Expr::Return { expr } | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); + | Expr::Yeet { expr } => visitor.on_expr_opt(*expr), + Expr::Become { expr } => visitor.on_expr(*expr), + Expr::RecordLit { fields, spread, path } => { + for RecordLitField { name: _, expr } in fields.iter() { + visitor.on_expr(*expr); } - } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); + match spread { + RecordSpread::Expr(expr) => visitor.on_expr(*expr), + RecordSpread::None | RecordSpread::FieldDefaults => {} } + visitor.on_path(path); } - Expr::Closure { body, .. } => { - f(*body); + Expr::Closure { body, args, arg_types, ret_type, capture_by: _, closure_kind: _ } => { + visitor.on_expr(*body); + visitor.on_pats(args); + arg_types.iter().for_each(|arg_type| visitor.on_type_opt(*arg_type)); + visitor.on_type_opt(*ret_type); } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); + Expr::BinaryOp { lhs, rhs, op: _ } => { + visitor.on_expr(*lhs); + visitor.on_expr(*rhs); } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + Expr::Range { lhs, rhs, range_type: _ } => { + visitor.on_expr_opt(*lhs); + visitor.on_expr_opt(*rhs); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + Expr::Index { base, index } => { + visitor.on_expr(*base); + visitor.on_expr(*index); } - Expr::Field { expr, .. } + Expr::Cast { expr, type_ref } => { + visitor.on_expr(*expr); + visitor.on_type(*type_ref); + } + Expr::Field { expr, name: _ } | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + | Expr::Ref { expr, mutability: _, rawness: _ } + | Expr::UnaryOp { expr, op: _ } + | Expr::Box { expr } + | Expr::Const(expr) => { + visitor.on_expr(*expr); } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), + Expr::Tuple { exprs } => visitor.on_exprs(exprs), Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), + Array::ElementList { elements } => visitor.on_exprs(elements), Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) + visitor.on_expr(*initializer); + visitor.on_anon_const_expr(*repeat) } }, &Expr::Assignment { target, value } => { - self.walk_exprs_in_pat(target, &mut f); - f(value); + visitor.on_pat(target); + visitor.on_expr(value); } } } - /// Walks the immediate children expressions and calls `f` for each child expression but does - /// not walk expressions within patterns. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) - | Expr::Missing - | Expr::Path(_) - | Expr::OffsetOf(_) - | Expr::Literal(_) - | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); - } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), - Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } - } - Expr::Let { expr, .. } => { - f(*expr); - } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { - match stmt { - Statement::Let { initializer, else_branch, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - } - Statement::Expr { expr: expression, .. } => f(*expression), - Statement::Item(_) => (), - } - } - if let &Some(expr) = tail { - f(expr); - } - } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); - } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); + /// Walks the immediate children expressions and calls `f` for each child expression. + pub fn walk_child_exprs(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.visit_expr_children(expr_id, Visitor { callback, store: self }); + + struct Visitor<'a, F> { + callback: F, + store: &'a ExpressionStore, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Match { expr, arms } => { - f(*expr); - arms.iter().map(|arm| arm.expr).for_each(f); + + fn on_pat(&mut self, pat: PatId) { + self.store.walk_exprs_in_pat(pat, &mut self.callback); } - Expr::Break { expr, .. } - | Expr::Return { expr } - | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); - } + } + } + + /// Walks the immediate children expressions and calls `f` for each child expression but does + /// not walk expressions within patterns. + pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.visit_expr_children(expr_id, Visitor { callback }); + + struct Visitor<F> { + callback: F, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); - } + } + } + + pub fn walk_exprs_in_pat(&self, pat_id: PatId, callback: impl FnMut(ExprId)) { + return Visitor { callback, store: self }.on_pat(pat_id); + + struct Visitor<'a, F> { + callback: F, + store: &'a ExpressionStore, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Closure { body, .. } => { - f(*body); + + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, self); } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); + } + } + + pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { + match &self[type_ref] { + TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + TypeRef::Tuple(types) => visitor.on_types(types), + TypeRef::Path(path) => visitor.on_path(path), + TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), + TypeRef::Reference(ref_type) => { + let RefType { ty, lifetime, mutability: _ } = &**ref_type; + visitor.on_type(*ty); + visitor.on_lifetime_opt(*lifetime); } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + TypeRef::Array(ArrayType { ty, len: ConstRef { expr: len } }) => { + visitor.on_type(*ty); + visitor.on_anon_const_expr(*len); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + TypeRef::Fn(fn_type) => { + let FnType { params, is_varargs: _, is_unsafe: _, abi: _ } = &**fn_type; + params.iter().for_each(|(_, param_ty)| visitor.on_type(*param_ty)); } - Expr::Field { expr, .. } - | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { + visitor.on_type_bounds(bounds) } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), - Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), - Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) - } - }, - &Expr::Assignment { target: _, value } => f(value), } } - pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) { - self.walk_pats(pat_id, &mut |pat| { - if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] { - f(expr); - } - }); - } - #[inline] #[track_caller] fn assert_expr_only(&self) -> &ExpressionOnlyStore { @@ -938,6 +941,128 @@ impl ExpressionStore { } } +pub trait StoreVisitor { + fn on_expr(&mut self, expr: ExprId) { + let _ = expr; + } + fn on_anon_const_expr(&mut self, expr: ExprId) { + self.on_expr(expr); + } + fn on_pat(&mut self, pat: PatId) { + let _ = pat; + } + fn on_type(&mut self, ty: TypeRefId) { + let _ = ty; + } + fn on_lifetime(&mut self, lifetime: LifetimeRefId) { + let _ = lifetime; + } +} + +impl<V: StoreVisitor> StoreVisitor for &mut V { + fn on_expr(&mut self, expr: ExprId) { + V::on_expr(self, expr); + } + fn on_anon_const_expr(&mut self, expr: ExprId) { + V::on_anon_const_expr(self, expr); + } + fn on_pat(&mut self, pat: PatId) { + V::on_pat(self, pat); + } + fn on_type(&mut self, ty: TypeRefId) { + V::on_type(self, ty); + } + fn on_lifetime(&mut self, lifetime: LifetimeRefId) { + V::on_lifetime(self, lifetime); + } +} + +trait StoreVisitorExt: StoreVisitor { + fn on_generic_args(&mut self, args: &GenericArgs) { + let GenericArgs { args, bindings, parenthesized: _, has_self_type: _ } = args; + for arg in args { + match arg { + GenericArg::Type(arg) => self.on_type(*arg), + GenericArg::Const(ConstRef { expr }) => self.on_anon_const_expr(*expr), + GenericArg::Lifetime(arg) => self.on_lifetime(*arg), + } + } + for AssociatedTypeBinding { name: _, args, type_ref, bounds } in bindings { + self.on_generic_args_opt(args); + self.on_type_opt(*type_ref); + self.on_type_bounds(bounds); + } + } + + fn on_type_bound(&mut self, bound: &TypeBound) { + match bound { + TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()), + TypeBound::ForLifetime(_, path_id) => self.on_type(path_id.type_ref()), + TypeBound::Lifetime(lifetime) => self.on_lifetime(*lifetime), + TypeBound::Use(args) => { + for arg in args { + match arg { + UseArgRef::Lifetime(lifetime) => self.on_lifetime(*lifetime), + UseArgRef::Name(_) => {} + } + } + } + TypeBound::Error => {} + } + } + + fn on_path(&mut self, path: &Path) { + match path { + Path::Normal(path) => { + let NormalPath { generic_args, type_anchor, mod_path: _ } = &**path; + generic_args.iter().for_each(|generic_arg| self.on_generic_args_opt(generic_arg)); + self.on_type_opt(*type_anchor); + } + Path::BarePath(_) | Path::LangItem(..) => {} + } + } + + fn on_expr_opt(&mut self, expr: Option<ExprId>) { + if let Some(expr) = expr { + self.on_expr(expr); + } + } + fn on_pat_opt(&mut self, pat: Option<PatId>) { + if let Some(pat) = pat { + self.on_pat(pat); + } + } + fn on_type_opt(&mut self, ty: Option<TypeRefId>) { + if let Some(ty) = ty { + self.on_type(ty); + } + } + fn on_lifetime_opt(&mut self, lifetime: Option<LifetimeRefId>) { + if let Some(lifetime) = lifetime { + self.on_lifetime(lifetime); + } + } + fn on_generic_args_opt(&mut self, args: &Option<impl Borrow<GenericArgs>>) { + if let Some(args) = args { + self.on_generic_args(args.borrow()); + } + } + + fn on_exprs(&mut self, exprs: impl IntoIterator<Item: Borrow<ExprId>>) { + exprs.into_iter().for_each(|expr| self.on_expr(*expr.borrow())); + } + fn on_pats(&mut self, pats: impl IntoIterator<Item: Borrow<PatId>>) { + pats.into_iter().for_each(|pat| self.on_pat(*pat.borrow())); + } + fn on_types(&mut self, types: impl IntoIterator<Item: Borrow<TypeRefId>>) { + types.into_iter().for_each(|ty| self.on_type(*ty.borrow())); + } + fn on_type_bounds(&mut self, bounds: impl IntoIterator<Item: Borrow<TypeBound>>) { + bounds.into_iter().for_each(|bound| self.on_type_bound(bound.borrow())); + } +} +impl<V: StoreVisitor> StoreVisitorExt for V {} + impl Index<ExprId> for ExpressionStore { type Output = Expr; |