@@ -75,6 +75,7 @@ impl Analyzer<'_> {
7575 self . check_generic_constraints ( ) ;
7676 self . check_pure_bodies ( ) ;
7777 self . check_no_block_bodies ( ) ;
78+ self . check_no_panic_bodies ( ) ;
7879 self . check_noalloc_allocations ( ) ;
7980 self . check_try_operator_result_returns ( ) ;
8081 self . check_resource_fields ( ) ;
@@ -927,6 +928,95 @@ impl Analyzer<'_> {
927928 }
928929 }
929930
931+ fn check_no_panic_bodies ( & mut self ) {
932+ let items = self . syntax_program . items . clone ( ) ;
933+ for item in & items {
934+ let Item :: Function ( function) = item else {
935+ continue ;
936+ } ;
937+ if !function_has_effect ( function, "no_panic" ) {
938+ continue ;
939+ }
940+ self . check_no_panic_block ( & function. name , & function. body ) ;
941+ }
942+ }
943+
944+ fn check_no_panic_block ( & mut self , function_name : & str , block : & Block ) {
945+ for statement in & block. statements {
946+ self . check_no_panic_stmt ( function_name, statement) ;
947+ }
948+ }
949+
950+ fn check_no_panic_stmt ( & mut self , function_name : & str , statement : & Stmt ) {
951+ match statement {
952+ Stmt :: Let ( stmt) => {
953+ if let Some ( value) = & stmt. value {
954+ self . check_no_panic_expr ( function_name, value) ;
955+ }
956+ }
957+ Stmt :: Return ( stmt) => {
958+ if let Some ( value) = & stmt. value {
959+ self . check_no_panic_expr ( function_name, value) ;
960+ }
961+ }
962+ Stmt :: Expr ( value) => self . check_no_panic_expr ( function_name, value) ,
963+ Stmt :: With ( stmt) => {
964+ self . check_no_panic_expr ( function_name, & stmt. resource ) ;
965+ self . check_no_panic_block ( function_name, & stmt. body ) ;
966+ }
967+ Stmt :: If ( stmt) => {
968+ self . check_no_panic_expr ( function_name, & stmt. condition ) ;
969+ self . check_no_panic_block ( function_name, & stmt. then_body ) ;
970+ if let Some ( else_body) = & stmt. else_body {
971+ self . check_no_panic_block ( function_name, else_body) ;
972+ }
973+ }
974+ Stmt :: Loop ( stmt) => {
975+ if let Some ( condition) = & stmt. condition {
976+ self . check_no_panic_expr ( function_name, condition) ;
977+ }
978+ self . check_no_panic_block ( function_name, & stmt. body ) ;
979+ }
980+ Stmt :: Break ( _) | Stmt :: Continue ( _) | Stmt :: Unknown ( _) => { }
981+ }
982+ }
983+
984+ fn check_no_panic_expr ( & mut self , function_name : & str , expr : & Expr ) {
985+ match expr {
986+ Expr :: Call {
987+ callee, args, span, ..
988+ } => {
989+ match self . hir . resolve_call ( callee) {
990+ CallResolution :: Resolved { signature, kind } => {
991+ if !matches ! ( kind, ResolvedCalleeKind :: Constructor { .. } )
992+ && !signature. effects . iter ( ) . any ( |effect| effect == "no_panic" )
993+ {
994+ self . panic_call_diagnostic ( function_name, callee, span) ;
995+ }
996+ }
997+ CallResolution :: EnumVariant | CallResolution :: Unknown => { }
998+ }
999+ for arg in args {
1000+ self . check_no_panic_expr ( function_name, & arg. value ) ;
1001+ }
1002+ }
1003+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } | Expr :: Try { value, .. } => {
1004+ self . check_no_panic_expr ( function_name, value) ;
1005+ }
1006+ Expr :: Binary { left, right, .. } => {
1007+ self . check_no_panic_expr ( function_name, left) ;
1008+ self . check_no_panic_expr ( function_name, right) ;
1009+ }
1010+ Expr :: Field { base, .. } => self . check_no_panic_expr ( function_name, base) ,
1011+ Expr :: Index { base, index, .. } => {
1012+ self . check_no_panic_expr ( function_name, base) ;
1013+ self . check_no_panic_expr ( function_name, index) ;
1014+ }
1015+ Expr :: Closure { body, .. } => self . check_no_panic_block ( function_name, body) ,
1016+ Expr :: Ident ( _, _) | Expr :: Number ( _, _) | Expr :: String ( _, _) | Expr :: Unknown ( _) => { }
1017+ }
1018+ }
1019+
9301020 fn check_duplicate_declarations ( & mut self ) {
9311021 for duplicate in self . hir . duplicate_symbols ( ) {
9321022 self . diagnostics . push (
@@ -1360,6 +1450,33 @@ impl Analyzer<'_> {
13601450 ) ,
13611451 ) ;
13621452 }
1453+
1454+ fn panic_call_diagnostic (
1455+ & mut self ,
1456+ function_name : & str ,
1457+ callee : & Callee ,
1458+ span : & crate :: diagnostic:: Span ,
1459+ ) {
1460+ self . diagnostics . push (
1461+ Diagnostic :: error (
1462+ code:: INVALID_NO_PANIC_CALL ,
1463+ format ! (
1464+ "`{function_name}` is declared no_panic but calls possibly panicking function `{}`." ,
1465+ callee_display( callee)
1466+ ) ,
1467+ span. clone ( ) ,
1468+ "possibly panicking call in no_panic function" ,
1469+ )
1470+ . with_cause (
1471+ "A `no_panic` function may only call constructors, enum variants, or functions also declared `effects(no_panic)`." ,
1472+ )
1473+ . with_fix (
1474+ "remove_no_panic_or_call_no_panic" ,
1475+ "Remove `no_panic`, or call only APIs whose signatures are declared `effects(no_panic)`." ,
1476+ "manual" ,
1477+ ) ,
1478+ ) ;
1479+ }
13631480}
13641481
13651482fn resource_pool_namespace_arg ( namespace : & str ) -> Option < & str > {
0 commit comments