@@ -145,6 +145,22 @@ pub struct HirEffectEvent {
145145 pub value_span : Span ,
146146}
147147
148+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
149+ pub enum HirReturnProof {
150+ NoValue ,
151+ Ident { name : String } ,
152+ StructConstructor ,
153+ FreshCall ,
154+ Unknown ,
155+ }
156+
157+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
158+ pub struct HirReturn {
159+ pub function_name : String ,
160+ pub span : Span ,
161+ pub proof : HirReturnProof ,
162+ }
163+
148164#[ derive( Debug , Default ) ]
149165pub struct Hir {
150166 signatures : HashMap < String , FunctionSig > ,
@@ -160,6 +176,8 @@ pub struct Hir {
160176 field_accesses_by_span : HashMap < Span , HirFieldAccess > ,
161177 effect_events : Vec < HirEffectEvent > ,
162178 effect_events_by_span : HashMap < Span , Vec < HirEffectEvent > > ,
179+ returns : Vec < HirReturn > ,
180+ returns_by_span : HashMap < Span , HirReturn > ,
163181}
164182
165183impl Hir {
@@ -260,6 +278,10 @@ impl Hir {
260278 . map_or ( & [ ] , Vec :: as_slice)
261279 }
262280
281+ pub fn return_fact ( & self , span : & Span ) -> Option < & HirReturn > {
282+ self . returns_by_span . get ( span)
283+ }
284+
263285 pub fn resolve_call ( & self , callee : & Callee ) -> CallResolution {
264286 let call_name = callee_name ( callee) ;
265287 if is_enum_variant_call ( call_name) {
@@ -357,10 +379,16 @@ impl Hir {
357379 by_span
358380 } ,
359381 ) ;
382+ self . returns_by_span = facts
383+ . returns
384+ . iter ( )
385+ . map ( |return_fact| ( return_fact. span . clone ( ) , return_fact. clone ( ) ) )
386+ . collect ( ) ;
360387 self . call_sites = facts. call_sites ;
361388 self . bindings = facts. bindings ;
362389 self . field_accesses = facts. field_accesses ;
363390 self . effect_events = facts. effect_events ;
391+ self . returns = facts. returns ;
364392 }
365393}
366394
@@ -370,6 +398,7 @@ struct BodyFacts {
370398 bindings : Vec < HirBinding > ,
371399 field_accesses : Vec < HirFieldAccess > ,
372400 effect_events : Vec < HirEffectEvent > ,
401+ returns : Vec < HirReturn > ,
373402}
374403
375404fn collect_function_body_facts ( hir : & Hir , function : & FunctionDecl , facts : & mut BodyFacts ) {
@@ -428,7 +457,18 @@ fn collect_body_facts_in_stmt(
428457 }
429458 Stmt :: Return ( stmt) => {
430459 if let Some ( value) = & stmt. value {
460+ facts. returns . push ( HirReturn {
461+ function_name : function_name. to_string ( ) ,
462+ span : value. span ( ) . clone ( ) ,
463+ proof : classify_return_expr ( hir, value) ,
464+ } ) ;
431465 collect_body_facts_in_expr ( hir, function_name, value, value_types, facts) ;
466+ } else {
467+ facts. returns . push ( HirReturn {
468+ function_name : function_name. to_string ( ) ,
469+ span : stmt. span . clone ( ) ,
470+ proof : HirReturnProof :: NoValue ,
471+ } ) ;
432472 }
433473 }
434474 Stmt :: With ( stmt) => {
@@ -617,6 +657,40 @@ fn infer_hir_expr_type(
617657 }
618658}
619659
660+ fn classify_return_expr ( hir : & Hir , expr : & Expr ) -> HirReturnProof {
661+ match expr {
662+ Expr :: Ident ( name, _) => HirReturnProof :: Ident { name : name. clone ( ) } ,
663+ Expr :: Call { callee, .. } => match hir. resolve_call ( callee) {
664+ CallResolution :: Resolved {
665+ signature,
666+ kind :
667+ ResolvedCalleeKind :: Constructor {
668+ type_kind : HirTypeKind :: Struct ,
669+ } ,
670+ } if signature. returns_fresh => HirReturnProof :: StructConstructor ,
671+ CallResolution :: Resolved { signature, .. } if signature. returns_fresh => {
672+ HirReturnProof :: FreshCall
673+ }
674+ CallResolution :: Resolved {
675+ kind :
676+ ResolvedCalleeKind :: Constructor {
677+ type_kind : HirTypeKind :: Struct ,
678+ } ,
679+ ..
680+ } => HirReturnProof :: StructConstructor ,
681+ CallResolution :: Resolved { .. }
682+ | CallResolution :: EnumVariant
683+ | CallResolution :: Unknown => HirReturnProof :: Unknown ,
684+ } ,
685+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } => classify_return_expr ( hir, value) ,
686+ Expr :: Field { .. }
687+ | Expr :: Closure { .. }
688+ | Expr :: Number ( _, _)
689+ | Expr :: String ( _, _)
690+ | Expr :: Unknown ( _) => HirReturnProof :: Unknown ,
691+ }
692+ }
693+
620694fn function_kind ( signature : & FunctionSig ) -> ResolvedCalleeKind {
621695 if signature. is_builtin {
622696 ResolvedCalleeKind :: BuiltinFunction
@@ -1338,6 +1412,20 @@ fn render(body: read String) -> Result<fresh Response, HttpError> {
13381412 . kind,
13391413 HirBindingKind :: ManagedLet
13401414 ) ) ;
1415+
1416+ let returns = & hir. returns ;
1417+ assert_eq ! ( returns. len( ) , 1 ) ;
1418+ assert_eq ! ( returns[ 0 ] . function_name, "render" ) ;
1419+ assert ! ( matches!(
1420+ returns[ 0 ] . proof,
1421+ HirReturnProof :: Ident { ref name } if name == "response"
1422+ ) ) ;
1423+ assert ! ( matches!(
1424+ hir. return_fact( & returns[ 0 ] . span)
1425+ . expect( "return lookup by span works" )
1426+ . proof,
1427+ HirReturnProof :: Ident { .. }
1428+ ) ) ;
13411429 }
13421430
13431431 #[ test]
@@ -1436,4 +1524,29 @@ fn publish(cache: mut ImageCache, path: read Path) -> Unit {
14361524 assert_eq ! ( hir. effect_events[ 2 ] . binding_name, "image" ) ;
14371525 assert_eq ! ( hir. effect_events( & hir. effect_events[ 0 ] . span) . len( ) , 1 ) ;
14381526 }
1527+
1528+ #[ test]
1529+ fn classifies_fresh_return_facts ( ) {
1530+ let source = r#"
1531+ mode: managed
1532+
1533+ struct Response {
1534+ status: Int
1535+ }
1536+
1537+ fn make_response() -> fresh Response {
1538+ return Response(status: 200)
1539+ }
1540+ "# ;
1541+
1542+ let program = parse_source ( "test.rss" , source) ;
1543+ let hir = Hir :: from_syntax ( & program) ;
1544+ let return_fact = hir. returns . first ( ) . expect ( "return fact exists" ) ;
1545+
1546+ assert_eq ! ( return_fact. function_name, "make_response" ) ;
1547+ assert ! ( matches!(
1548+ return_fact. proof,
1549+ HirReturnProof :: StructConstructor
1550+ ) ) ;
1551+ }
14391552}
0 commit comments