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,8 +241,8 @@ 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 ,
@@ -198,7 +269,7 @@ fn resolve_maybe_upvar<'db>(
198269#[ cfg( test) ]
199270mod tests {
200271 use expect_test:: { Expect , expect} ;
201- use hir_def:: { ModuleDefId , expr_store:: Body , nameres:: crate_def_map} ;
272+ use hir_def:: { DefWithBodyId , ModuleDefId , expr_store:: Body , nameres:: crate_def_map} ;
202273 use itertools:: Itertools ;
203274 use span:: Edition ;
204275 use test_fixture:: WithFixture ;
@@ -220,7 +291,7 @@ mod tests {
220291 . exactly_one ( )
221292 . unwrap_or_else ( |_| panic ! ( "expected one function" ) ) ;
222293 let ( body, source_map) = Body :: with_source_map ( & db, func. into ( ) ) ;
223- let Some ( upvars) = upvars_mentioned ( & db, func. into ( ) ) else {
294+ let Some ( upvars) = upvars_mentioned ( & db, DefWithBodyId :: from ( func) . into ( ) ) else {
224295 expectation. assert_eq ( "" ) ;
225296 return ;
226297 } ;
0 commit comments