11use std:: collections:: { HashMap , HashSet } ;
22
33use crate :: diagnostic:: Span ;
4- use crate :: hir:: { HirBinding , HirBindingKind , HirEffectEvent , HirEffectEventKind , HirFunctionBody } ;
4+ use crate :: hir:: {
5+ HirBinding , HirBindingKind , HirBlock , HirEffectEvent , HirEffectEventKind , HirExpr ,
6+ HirFunctionBody , HirStmt ,
7+ } ;
58
69use super :: body:: Flow ;
710
@@ -16,12 +19,26 @@ pub(crate) struct BodyState {
1619
1720pub ( crate ) struct LocalAnalysis {
1821 body : Option < HirFunctionBody > ,
22+ events_by_span : HashMap < Span , Vec < HirEffectEvent > > ,
23+ binding_types_by_span : HashMap < Span , String > ,
1924}
2025
2126impl LocalAnalysis {
2227 pub ( crate ) fn new ( body : Option < & HirFunctionBody > ) -> Self {
28+ let body = body. cloned ( ) ;
29+ let events_by_span = body
30+ . as_ref ( )
31+ . and_then ( |body| body. block . as_ref ( ) )
32+ . map_or_else ( HashMap :: new, index_events_from_block) ;
33+ let binding_types_by_span = body
34+ . as_ref ( )
35+ . and_then ( |body| body. block . as_ref ( ) )
36+ . map_or_else ( HashMap :: new, index_binding_types_from_block) ;
37+
2338 Self {
24- body : body. cloned ( ) ,
39+ body,
40+ events_by_span,
41+ binding_types_by_span,
2542 }
2643 }
2744
@@ -41,21 +58,161 @@ impl LocalAnalysis {
4158 state. apply_retention_events ( self . effect_events ( span) ) ;
4259 }
4360
61+ pub ( crate ) fn binding_type ( & self , span : & Span ) -> Option < & str > {
62+ self . binding_types_by_span . get ( span) . map ( String :: as_str)
63+ }
64+
4465 fn effect_events ( & self , span : & Span ) -> & [ HirEffectEvent ] {
45- self . body
46- . as_ref ( )
47- . and_then ( |body| events_at_span ( & body. effect_events , span) )
48- . unwrap_or ( & [ ] )
66+ self . events_by_span . get ( span) . map_or ( & [ ] , Vec :: as_slice)
4967 }
5068}
5169
52- fn events_at_span < ' a > ( events : & ' a [ HirEffectEvent ] , span : & Span ) -> Option < & ' a [ HirEffectEvent ] > {
53- let start = events. iter ( ) . position ( |event| event. span == * span) ?;
54- let end = events[ start..]
55- . iter ( )
56- . position ( |event| event. span != * span)
57- . map_or ( events. len ( ) , |offset| start + offset) ;
58- Some ( & events[ start..end] )
70+ fn index_events_from_block ( block : & HirBlock ) -> HashMap < Span , Vec < HirEffectEvent > > {
71+ let mut events = HashMap :: new ( ) ;
72+ collect_block_events ( block, & mut events) ;
73+ events
74+ }
75+
76+ fn collect_block_events ( block : & HirBlock , events : & mut HashMap < Span , Vec < HirEffectEvent > > ) {
77+ for statement in & block. statements {
78+ collect_stmt_events ( statement, events) ;
79+ }
80+ }
81+
82+ fn collect_stmt_events ( statement : & HirStmt , events : & mut HashMap < Span , Vec < HirEffectEvent > > ) {
83+ match statement {
84+ HirStmt :: Let {
85+ value : Some ( value) , ..
86+ }
87+ | HirStmt :: Return {
88+ value : Some ( value) , ..
89+ }
90+ | HirStmt :: Expr ( value) => collect_expr_events ( value, events) ,
91+ HirStmt :: With { resource, body, .. } => {
92+ collect_expr_events ( resource, events) ;
93+ collect_block_events ( body, events) ;
94+ }
95+ HirStmt :: If {
96+ condition,
97+ then_body,
98+ else_body,
99+ ..
100+ } => {
101+ collect_expr_events ( condition, events) ;
102+ collect_block_events ( then_body, events) ;
103+ if let Some ( else_body) = else_body {
104+ collect_block_events ( else_body, events) ;
105+ }
106+ }
107+ HirStmt :: Loop {
108+ condition, body, ..
109+ } => {
110+ if let Some ( condition) = condition {
111+ collect_expr_events ( condition, events) ;
112+ }
113+ collect_block_events ( body, events) ;
114+ }
115+ HirStmt :: Let { value : None , .. }
116+ | HirStmt :: Return { value : None , .. }
117+ | HirStmt :: Break ( _)
118+ | HirStmt :: Continue ( _)
119+ | HirStmt :: Unknown ( _) => { }
120+ }
121+ }
122+
123+ fn collect_expr_events ( expr : & HirExpr , events : & mut HashMap < Span , Vec < HirEffectEvent > > ) {
124+ match expr {
125+ HirExpr :: Call {
126+ args,
127+ events : expr_events,
128+ span,
129+ ..
130+ } => {
131+ record_expr_events ( events, span, expr_events) ;
132+ for arg in args {
133+ collect_expr_events ( & arg. value , events) ;
134+ }
135+ }
136+ HirExpr :: Effect {
137+ value,
138+ events : expr_events,
139+ span,
140+ ..
141+ }
142+ | HirExpr :: Manage {
143+ value,
144+ events : expr_events,
145+ span,
146+ ..
147+ } => {
148+ record_expr_events ( events, span, expr_events) ;
149+ collect_expr_events ( value, events) ;
150+ }
151+ HirExpr :: Field { base, .. } => collect_expr_events ( base, events) ,
152+ HirExpr :: Closure { body, .. } => collect_block_events ( body, events) ,
153+ HirExpr :: Ident { .. }
154+ | HirExpr :: Number { .. }
155+ | HirExpr :: String { .. }
156+ | HirExpr :: Unknown ( _) => { }
157+ }
158+ }
159+
160+ fn record_expr_events (
161+ events : & mut HashMap < Span , Vec < HirEffectEvent > > ,
162+ span : & Span ,
163+ expr_events : & [ HirEffectEvent ] ,
164+ ) {
165+ if expr_events. is_empty ( ) {
166+ return ;
167+ }
168+ events
169+ . entry ( span. clone ( ) )
170+ . or_default ( )
171+ . extend ( expr_events. iter ( ) . cloned ( ) ) ;
172+ }
173+
174+ fn index_binding_types_from_block ( block : & HirBlock ) -> HashMap < Span , String > {
175+ let mut types = HashMap :: new ( ) ;
176+ collect_block_binding_types ( block, & mut types) ;
177+ types
178+ }
179+
180+ fn collect_block_binding_types ( block : & HirBlock , types : & mut HashMap < Span , String > ) {
181+ for statement in & block. statements {
182+ collect_stmt_binding_types ( statement, types) ;
183+ }
184+ }
185+
186+ fn collect_stmt_binding_types ( statement : & HirStmt , types : & mut HashMap < Span , String > ) {
187+ match statement {
188+ HirStmt :: Let {
189+ type_name : Some ( type_name) ,
190+ span,
191+ ..
192+ } => {
193+ types. insert ( span. clone ( ) , type_name. clone ( ) ) ;
194+ }
195+ HirStmt :: With { body, .. } => collect_block_binding_types ( body, types) ,
196+ HirStmt :: If {
197+ then_body,
198+ else_body,
199+ ..
200+ } => {
201+ collect_block_binding_types ( then_body, types) ;
202+ if let Some ( else_body) = else_body {
203+ collect_block_binding_types ( else_body, types) ;
204+ }
205+ }
206+ HirStmt :: Loop { body, .. } => collect_block_binding_types ( body, types) ,
207+ HirStmt :: Let {
208+ type_name : None , ..
209+ }
210+ | HirStmt :: Return { .. }
211+ | HirStmt :: Break ( _)
212+ | HirStmt :: Continue ( _)
213+ | HirStmt :: Expr ( _)
214+ | HirStmt :: Unknown ( _) => { }
215+ }
59216}
60217
61218impl BodyState {
@@ -263,6 +420,8 @@ fn merge_fallthrough_states(base: &BodyState, left: &BodyState, right: &BodyStat
263420#[ cfg( test) ]
264421mod tests {
265422 use super :: * ;
423+ use crate :: hir:: { CallResolution , HirBlock , HirExpr , HirStmt } ;
424+ use crate :: syntax:: ast:: Callee ;
266425
267426 fn span ( line : usize ) -> Span {
268427 Span {
@@ -304,6 +463,23 @@ mod tests {
304463
305464 #[ test]
306465 fn local_analysis_seeds_params_and_applies_events_by_span ( ) {
466+ let retain_event = HirEffectEvent {
467+ function_name : "run" . to_string ( ) ,
468+ kind : HirEffectEventKind :: Retain {
469+ callee : "Cache.store" . to_string ( ) ,
470+ param : "value" . to_string ( ) ,
471+ } ,
472+ binding_name : "cached" . to_string ( ) ,
473+ span : span ( 20 ) ,
474+ value_span : span ( 20 ) ,
475+ } ;
476+ let manage_event = HirEffectEvent {
477+ function_name : "run" . to_string ( ) ,
478+ kind : HirEffectEventKind :: Manage ,
479+ binding_name : "image" . to_string ( ) ,
480+ span : span ( 21 ) ,
481+ value_span : span ( 21 ) ,
482+ } ;
307483 let body = HirFunctionBody {
308484 function_name : "run" . to_string ( ) ,
309485 bindings : vec ! [ HirBinding {
@@ -313,25 +489,36 @@ mod tests {
313489 span: span( 1 ) ,
314490 type_name: Some ( "ResourcePool<File>" . to_string( ) ) ,
315491 } ] ,
316- effect_events : vec ! [
317- HirEffectEvent {
318- function_name: "run" . to_string( ) ,
319- kind: HirEffectEventKind :: Retain {
320- callee: "Cache.store" . to_string( ) ,
321- param: "value" . to_string( ) ,
492+ block : Some ( HirBlock {
493+ statements : vec ! [
494+ HirStmt :: Let {
495+ kind: HirBindingKind :: LocalLet ,
496+ name: "cached" . to_string( ) ,
497+ value: None ,
498+ type_name: Some ( "Image" . to_string( ) ) ,
499+ span: span( 2 ) ,
322500 } ,
323- binding_name: "cached" . to_string( ) ,
324- span: span( 20 ) ,
325- value_span: span( 20 ) ,
326- } ,
327- HirEffectEvent {
328- function_name: "run" . to_string( ) ,
329- kind: HirEffectEventKind :: Manage ,
330- binding_name: "image" . to_string( ) ,
331- span: span( 21 ) ,
332- value_span: span( 21 ) ,
333- } ,
334- ] ,
501+ HirStmt :: Expr ( HirExpr :: Call {
502+ callee: Callee :: Name ( "store" . to_string( ) ) ,
503+ args: Vec :: new( ) ,
504+ resolution: CallResolution :: Unknown ,
505+ events: vec![ retain_event] ,
506+ type_name: None ,
507+ span: span( 20 ) ,
508+ } ) ,
509+ HirStmt :: Expr ( HirExpr :: Manage {
510+ value: Box :: new( HirExpr :: Ident {
511+ name: "image" . to_string( ) ,
512+ type_name: Some ( "Image" . to_string( ) ) ,
513+ span: span( 21 ) ,
514+ } ) ,
515+ events: vec![ manage_event] ,
516+ type_name: Some ( "Image" . to_string( ) ) ,
517+ span: span( 21 ) ,
518+ } ) ,
519+ ] ,
520+ span : span ( 1 ) ,
521+ } ) ,
335522 ..HirFunctionBody :: default ( )
336523 } ;
337524 let local_analysis = LocalAnalysis :: new ( Some ( & body) ) ;
@@ -340,6 +527,7 @@ mod tests {
340527 state. bind_local ( "image" ) ;
341528
342529 assert_eq ! ( state. value_type( "pool" ) , Some ( "ResourcePool<File>" ) ) ;
530+ assert_eq ! ( local_analysis. binding_type( & span( 2 ) ) , Some ( "Image" ) ) ;
343531
344532 local_analysis. apply_retention_events ( & span ( 20 ) , & mut state) ;
345533 local_analysis. apply_move_events ( & span ( 21 ) , & mut state) ;
0 commit comments