11//! A simple query to collect tall locals (upvars) a closure use.
22
33use hir_def:: {
4- DefWithBodyId ,
5- expr_store:: { Body , path:: Path } ,
4+ DefWithBodyId , ExpressionStoreOwnerId , GenericDefId , VariantId ,
5+ expr_store:: { ExpressionStore , path:: Path } ,
66 hir:: { BindingId , Expr , ExprId , ExprOrPatId , Pat } ,
77 resolver:: { HasResolver , Resolver , ValueNs } ,
88} ;
@@ -36,18 +36,89 @@ impl Upvars {
3636 pub fn is_empty ( & self ) -> bool {
3737 self . 0 . is_empty ( )
3838 }
39+
40+ #[ inline]
41+ pub fn as_ref ( & self ) -> UpvarsRef < ' _ > {
42+ UpvarsRef ( & self . 0 )
43+ }
44+ }
45+
46+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash , Default ) ]
47+ // Kept sorted.
48+ pub struct UpvarsRef < ' db > ( & ' db [ BindingId ] ) ;
49+
50+ impl UpvarsRef < ' _ > {
51+ #[ inline]
52+ pub fn contains ( self , local : BindingId ) -> bool {
53+ self . 0 . binary_search ( & local) . is_ok ( )
54+ }
55+
56+ #[ inline]
57+ pub fn iter ( self ) -> impl ExactSizeIterator < Item = BindingId > {
58+ self . 0 . iter ( ) . copied ( )
59+ }
60+
61+ #[ inline]
62+ pub fn is_empty ( self ) -> bool {
63+ self . 0 . is_empty ( )
64+ }
65+
66+ #[ inline]
67+ pub const fn empty ( ) -> Self {
68+ UpvarsRef ( & [ ] )
69+ }
3970}
4071
4172/// Returns a map from `Expr::Closure` to its upvars.
42- #[ salsa:: tracked( returns( as_deref) ) ]
4373pub fn upvars_mentioned (
4474 db : & dyn HirDatabase ,
45- owner : DefWithBodyId ,
75+ owner : ExpressionStoreOwnerId ,
76+ ) -> Option < & FxHashMap < ExprId , Upvars > > {
77+ return match owner {
78+ ExpressionStoreOwnerId :: Signature ( owner) => signature_upvars_mentioned ( db, owner) ,
79+ ExpressionStoreOwnerId :: Body ( owner) => body_upvars_mentioned ( db, owner) ,
80+ ExpressionStoreOwnerId :: VariantFields ( owner) => variant_fields_upvars_mentioned ( db, owner) ,
81+ } ;
82+
83+ #[ salsa:: tracked( returns( as_deref) ) ]
84+ pub fn signature_upvars_mentioned (
85+ db : & dyn HirDatabase ,
86+ owner : GenericDefId ,
87+ ) -> Option < Box < FxHashMap < ExprId , Upvars > > > {
88+ upvars_mentioned_impl ( db, owner. into ( ) )
89+ }
90+
91+ #[ salsa:: tracked( returns( as_deref) ) ]
92+ pub fn body_upvars_mentioned (
93+ db : & dyn HirDatabase ,
94+ owner : DefWithBodyId ,
95+ ) -> Option < Box < FxHashMap < ExprId , Upvars > > > {
96+ upvars_mentioned_impl ( db, owner. into ( ) )
97+ }
98+
99+ #[ salsa:: tracked( returns( as_deref) ) ]
100+ pub fn variant_fields_upvars_mentioned (
101+ db : & dyn HirDatabase ,
102+ owner : VariantId ,
103+ ) -> Option < Box < FxHashMap < ExprId , Upvars > > > {
104+ upvars_mentioned_impl ( db, owner. into ( ) )
105+ }
106+ }
107+
108+ pub fn upvars_mentioned_impl (
109+ db : & dyn HirDatabase ,
110+ owner : ExpressionStoreOwnerId ,
46111) -> Option < Box < FxHashMap < ExprId , Upvars > > > {
47- let body = Body :: of ( db, owner) ;
112+ let store = ExpressionStore :: of ( db, owner) ;
113+ if store. const_expr_origins ( ) . is_empty ( ) {
114+ // Save constructing a Resolver.
115+ return None ;
116+ }
48117 let mut resolver = owner. resolver ( db) ;
49118 let mut result = FxHashMap :: default ( ) ;
50- handle_expr_outside_closure ( db, & mut resolver, owner, body, body. root_expr ( ) , & mut result) ;
119+ for root_expr in store. expr_roots ( ) {
120+ handle_expr_outside_closure ( db, & mut resolver, owner, store, root_expr, & mut result) ;
121+ }
51122 return if result. is_empty ( ) {
52123 None
53124 } else {
@@ -58,8 +129,8 @@ pub fn upvars_mentioned(
58129 fn handle_expr_outside_closure < ' db > (
59130 db : & ' db dyn HirDatabase ,
60131 resolver : & mut Resolver < ' db > ,
61- owner : DefWithBodyId ,
62- body : & Body ,
132+ owner : ExpressionStoreOwnerId ,
133+ body : & ExpressionStore ,
63134 expr : ExprId ,
64135 closures_map : & mut FxHashMap < ExprId , Upvars > ,
65136 ) {
@@ -89,8 +160,8 @@ pub fn upvars_mentioned(
89160 fn handle_expr_inside_closure < ' db > (
90161 db : & ' db dyn HirDatabase ,
91162 resolver : & mut Resolver < ' db > ,
92- owner : DefWithBodyId ,
93- body : & Body ,
163+ owner : ExpressionStoreOwnerId ,
164+ body : & ExpressionStore ,
94165 current_closure : ExprId ,
95166 expr : ExprId ,
96167 upvars : & mut FxHashSet < BindingId > ,
@@ -170,17 +241,18 @@ pub fn upvars_mentioned(
170241fn resolve_maybe_upvar < ' db > (
171242 db : & ' db dyn HirDatabase ,
172243 resolver : & mut Resolver < ' db > ,
173- owner : DefWithBodyId ,
174- body : & Body ,
244+ owner : ExpressionStoreOwnerId ,
245+ body : & ExpressionStore ,
175246 current_closure : ExprId ,
176247 expr : ExprId ,
177248 id : ExprOrPatId ,
178249 upvars : & mut FxHashSet < BindingId > ,
179250 path : & Path ,
180251) {
181252 if let Path :: BarePath ( mod_path) = path
182- && matches ! ( mod_path. kind, PathKind :: Plain )
183- && mod_path. segments ( ) . len ( ) == 1
253+ && matches ! ( mod_path. kind, PathKind :: Plain | PathKind :: SELF )
254+ // `self` is length zero.
255+ && mod_path. segments ( ) . len ( ) <= 1
184256 {
185257 // Could be a variable.
186258 let guard = resolver. update_to_inner_scope ( db, owner, expr) ;
@@ -198,7 +270,9 @@ fn resolve_maybe_upvar<'db>(
198270#[ cfg( test) ]
199271mod tests {
200272 use expect_test:: { Expect , expect} ;
201- use hir_def:: { ModuleDefId , expr_store:: Body , nameres:: crate_def_map} ;
273+ use hir_def:: {
274+ AssocItemId , DefWithBodyId , ModuleDefId , expr_store:: Body , nameres:: crate_def_map,
275+ } ;
202276 use itertools:: Itertools ;
203277 use span:: Edition ;
204278 use test_fixture:: WithFixture ;
@@ -217,10 +291,18 @@ mod tests {
217291 ModuleDefId :: FunctionId ( func) => Some ( func) ,
218292 _ => None ,
219293 } )
294+ . chain ( def_map. modules ( ) . flat_map ( |( _, module) | {
295+ module. scope . impls ( ) . flat_map ( |impl_| & * impl_. impl_items ( & db) . items ) . filter_map (
296+ |& ( _, item) | match item {
297+ AssocItemId :: FunctionId ( it) => Some ( it) ,
298+ _ => None ,
299+ } ,
300+ )
301+ } ) )
220302 . exactly_one ( )
221303 . unwrap_or_else ( |_| panic ! ( "expected one function" ) ) ;
222304 let ( body, source_map) = Body :: with_source_map ( & db, func. into ( ) ) ;
223- let Some ( upvars) = upvars_mentioned ( & db, func. into ( ) ) else {
305+ let Some ( upvars) = upvars_mentioned ( & db, DefWithBodyId :: from ( func) . into ( ) ) else {
224306 expectation. assert_eq ( "" ) ;
225307 return ;
226308 } ;
@@ -316,4 +398,19 @@ fn foo() {
316398 49..110: a, b"# ] ] ,
317399 ) ;
318400 }
401+
402+ #[ test]
403+ fn self_upvar ( ) {
404+ check (
405+ r#"
406+ struct Foo(i32);
407+ impl Foo {
408+ fn foo(&self) {
409+ || self.0;
410+ }
411+ }
412+ "# ,
413+ expect ! [ "56..65: self" ] ,
414+ ) ;
415+ }
319416}
0 commit comments