@@ -2,8 +2,8 @@ use std::collections::{HashMap, HashSet};
22
33use crate :: diagnostic:: Span ;
44use crate :: syntax:: ast:: {
5- DataEffect , EffectDecl , FieldDecl , FunctionDecl , Item , Param , Program as SyntaxProgram ,
6- TypeDecl , TypeKind ,
5+ Block , Callee , DataEffect , EffectDecl , Expr , FieldDecl , FunctionDecl , Item , Param ,
6+ Program as SyntaxProgram , Stmt , TypeDecl , TypeKind ,
77} ;
88
99#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -78,12 +78,39 @@ pub struct DuplicateSymbol {
7878 pub duplicate_span : Span ,
7979}
8080
81+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
82+ pub enum ResolvedCalleeKind {
83+ UserFunction ,
84+ BuiltinFunction ,
85+ Constructor { type_kind : HirTypeKind } ,
86+ }
87+
88+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
89+ pub enum CallResolution {
90+ Resolved {
91+ signature : FunctionSig ,
92+ kind : ResolvedCalleeKind ,
93+ } ,
94+ EnumVariant ,
95+ Unknown ,
96+ }
97+
98+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
99+ pub struct HirCallSite {
100+ pub function_name : String ,
101+ pub callee : Callee ,
102+ pub span : Span ,
103+ pub resolution : CallResolution ,
104+ }
105+
81106#[ derive( Debug , Default ) ]
82107pub struct Hir {
83108 signatures : HashMap < String , FunctionSig > ,
84109 types : HashMap < String , TypeInfo > ,
85110 fields_by_name : HashMap < String , Vec < FieldInfo > > ,
86111 duplicate_symbols : Vec < DuplicateSymbol > ,
112+ call_sites : Vec < HirCallSite > ,
113+ call_resolutions_by_span : HashMap < Span , CallResolution > ,
87114}
88115
89116impl Hir {
@@ -124,6 +151,7 @@ impl Hir {
124151 }
125152 }
126153 }
154+ hir. collect_call_sites ( program) ;
127155 hir
128156 }
129157
@@ -159,6 +187,37 @@ impl Hir {
159187 & self . duplicate_symbols
160188 }
161189
190+ pub fn call_resolution ( & self , span : & Span ) -> Option < & CallResolution > {
191+ self . call_resolutions_by_span . get ( span)
192+ }
193+
194+ pub fn resolve_call ( & self , callee : & Callee ) -> CallResolution {
195+ let call_name = callee_name ( callee) ;
196+ if is_enum_variant_call ( call_name) {
197+ return CallResolution :: EnumVariant ;
198+ }
199+
200+ let signature = match callee {
201+ Callee :: Name ( name) => self . resolve_function ( None , name) ,
202+ Callee :: Qualified { namespace, name } => self . resolve_function ( Some ( namespace) , name) ,
203+ } ;
204+ let Some ( signature) = signature else {
205+ return CallResolution :: Unknown ;
206+ } ;
207+ let kind = match callee {
208+ Callee :: Name ( name) => self . type_kind ( name) . map_or_else (
209+ || function_kind ( signature) ,
210+ |type_kind| ResolvedCalleeKind :: Constructor { type_kind } ,
211+ ) ,
212+ Callee :: Qualified { .. } => function_kind ( signature) ,
213+ } ;
214+
215+ CallResolution :: Resolved {
216+ signature : signature. clone ( ) ,
217+ kind,
218+ }
219+ }
220+
162221 fn insert_function ( & mut self , signature : FunctionSig ) {
163222 let key = match & signature. namespace {
164223 Some ( namespace) => qualified_key ( namespace, & signature. name ) ,
@@ -184,6 +243,117 @@ impl Hir {
184243 self . insert_function ( signature) ;
185244 }
186245 }
246+
247+ fn collect_call_sites ( & mut self , program : & SyntaxProgram ) {
248+ let mut sites = Vec :: new ( ) ;
249+ for item in & program. items {
250+ let Item :: Function ( function) = item else {
251+ continue ;
252+ } ;
253+ collect_call_sites_in_block ( self , & function. name , & function. body , & mut sites) ;
254+ }
255+
256+ self . call_resolutions_by_span = sites
257+ . iter ( )
258+ . map ( |site| ( site. span . clone ( ) , site. resolution . clone ( ) ) )
259+ . collect ( ) ;
260+ self . call_sites = sites;
261+ }
262+ }
263+
264+ fn collect_call_sites_in_block (
265+ hir : & Hir ,
266+ function_name : & str ,
267+ block : & Block ,
268+ sites : & mut Vec < HirCallSite > ,
269+ ) {
270+ for statement in & block. statements {
271+ collect_call_sites_in_stmt ( hir, function_name, statement, sites) ;
272+ }
273+ }
274+
275+ fn collect_call_sites_in_stmt (
276+ hir : & Hir ,
277+ function_name : & str ,
278+ statement : & Stmt ,
279+ sites : & mut Vec < HirCallSite > ,
280+ ) {
281+ match statement {
282+ Stmt :: Let ( stmt) => {
283+ if let Some ( value) = & stmt. value {
284+ collect_call_sites_in_expr ( hir, function_name, value, sites) ;
285+ }
286+ }
287+ Stmt :: Return ( stmt) => {
288+ if let Some ( value) = & stmt. value {
289+ collect_call_sites_in_expr ( hir, function_name, value, sites) ;
290+ }
291+ }
292+ Stmt :: With ( stmt) => {
293+ collect_call_sites_in_expr ( hir, function_name, & stmt. resource , sites) ;
294+ collect_call_sites_in_block ( hir, function_name, & stmt. body , sites) ;
295+ }
296+ Stmt :: If ( stmt) => {
297+ collect_call_sites_in_expr ( hir, function_name, & stmt. condition , sites) ;
298+ collect_call_sites_in_block ( hir, function_name, & stmt. then_body , sites) ;
299+ if let Some ( else_body) = & stmt. else_body {
300+ collect_call_sites_in_block ( hir, function_name, else_body, sites) ;
301+ }
302+ }
303+ Stmt :: Loop ( stmt) => {
304+ if let Some ( condition) = & stmt. condition {
305+ collect_call_sites_in_expr ( hir, function_name, condition, sites) ;
306+ }
307+ collect_call_sites_in_block ( hir, function_name, & stmt. body , sites) ;
308+ }
309+ Stmt :: Expr ( expr) => collect_call_sites_in_expr ( hir, function_name, expr, sites) ,
310+ Stmt :: Break ( _) | Stmt :: Continue ( _) | Stmt :: Unknown ( _) => { }
311+ }
312+ }
313+
314+ fn collect_call_sites_in_expr (
315+ hir : & Hir ,
316+ function_name : & str ,
317+ expr : & Expr ,
318+ sites : & mut Vec < HirCallSite > ,
319+ ) {
320+ match expr {
321+ Expr :: Call { callee, args, span } => {
322+ sites. push ( HirCallSite {
323+ function_name : function_name. to_string ( ) ,
324+ callee : callee. clone ( ) ,
325+ span : span. clone ( ) ,
326+ resolution : hir. resolve_call ( callee) ,
327+ } ) ;
328+ for arg in args {
329+ collect_call_sites_in_expr ( hir, function_name, & arg. value , sites) ;
330+ }
331+ }
332+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } => {
333+ collect_call_sites_in_expr ( hir, function_name, value, sites) ;
334+ }
335+ Expr :: Field { base, .. } => collect_call_sites_in_expr ( hir, function_name, base, sites) ,
336+ Expr :: Closure { body, .. } => collect_call_sites_in_block ( hir, function_name, body, sites) ,
337+ Expr :: Ident ( _, _) | Expr :: Number ( _, _) | Expr :: String ( _, _) | Expr :: Unknown ( _) => { }
338+ }
339+ }
340+
341+ fn function_kind ( signature : & FunctionSig ) -> ResolvedCalleeKind {
342+ if signature. is_builtin {
343+ ResolvedCalleeKind :: BuiltinFunction
344+ } else {
345+ ResolvedCalleeKind :: UserFunction
346+ }
347+ }
348+
349+ fn is_enum_variant_call ( name : & str ) -> bool {
350+ matches ! ( name, "Ok" | "Err" | "Some" | "None" | "Result" | "Option" )
351+ }
352+
353+ fn callee_name ( callee : & Callee ) -> & str {
354+ match callee {
355+ Callee :: Name ( name) | Callee :: Qualified { name, .. } => name,
356+ }
187357}
188358
189359fn function_sig_from_decl ( function : & FunctionDecl ) -> FunctionSig {
@@ -826,4 +996,45 @@ struct Response {
826996 assert_eq ! ( duplicate. first_span. line, 5 ) ;
827997 assert_eq ! ( duplicate. duplicate_span. line, 6 ) ;
828998 }
999+
1000+ #[ test]
1001+ fn resolves_body_call_sites ( ) {
1002+ let source = r#"
1003+ mode: managed
1004+
1005+ struct Response {
1006+ status: Int
1007+ body: String
1008+ }
1009+
1010+ fn render(body: read String) -> Result<fresh Response, HttpError> {
1011+ Log.write(message: read body)
1012+ Missing.call(value: read body)
1013+ return Response(status: 200, body: read body)
1014+ }
1015+ "# ;
1016+
1017+ let program = parse_source ( "test.rss" , source) ;
1018+ let hir = Hir :: from_syntax ( & program) ;
1019+ let sites = & hir. call_sites ;
1020+
1021+ assert_eq ! ( sites. len( ) , 3 ) ;
1022+ assert ! ( matches!(
1023+ sites[ 0 ] . resolution,
1024+ CallResolution :: Resolved {
1025+ kind: ResolvedCalleeKind :: BuiltinFunction ,
1026+ ..
1027+ }
1028+ ) ) ;
1029+ assert ! ( matches!( sites[ 1 ] . resolution, CallResolution :: Unknown ) ) ;
1030+ assert ! ( matches!(
1031+ sites[ 2 ] . resolution,
1032+ CallResolution :: Resolved {
1033+ kind: ResolvedCalleeKind :: Constructor {
1034+ type_kind: HirTypeKind :: Struct
1035+ } ,
1036+ ..
1037+ }
1038+ ) ) ;
1039+ }
8291040}
0 commit comments