@@ -74,6 +74,7 @@ impl Analyzer<'_> {
7474 self . check_signature_explicitness ( ) ;
7575 self . check_generic_constraints ( ) ;
7676 self . check_pure_bodies ( ) ;
77+ self . check_no_block_bodies ( ) ;
7778 self . check_noalloc_allocations ( ) ;
7879 self . check_try_operator_result_returns ( ) ;
7980 self . check_resource_fields ( ) ;
@@ -837,6 +838,95 @@ impl Analyzer<'_> {
837838 }
838839 }
839840
841+ fn check_no_block_bodies ( & mut self ) {
842+ let items = self . syntax_program . items . clone ( ) ;
843+ for item in & items {
844+ let Item :: Function ( function) = item else {
845+ continue ;
846+ } ;
847+ if !function_has_effect ( function, "no_block" ) {
848+ continue ;
849+ }
850+ self . check_no_block_block ( & function. name , & function. body ) ;
851+ }
852+ }
853+
854+ fn check_no_block_block ( & mut self , function_name : & str , block : & Block ) {
855+ for statement in & block. statements {
856+ self . check_no_block_stmt ( function_name, statement) ;
857+ }
858+ }
859+
860+ fn check_no_block_stmt ( & mut self , function_name : & str , statement : & Stmt ) {
861+ match statement {
862+ Stmt :: Let ( stmt) => {
863+ if let Some ( value) = & stmt. value {
864+ self . check_no_block_expr ( function_name, value) ;
865+ }
866+ }
867+ Stmt :: Return ( stmt) => {
868+ if let Some ( value) = & stmt. value {
869+ self . check_no_block_expr ( function_name, value) ;
870+ }
871+ }
872+ Stmt :: Expr ( value) => self . check_no_block_expr ( function_name, value) ,
873+ Stmt :: With ( stmt) => {
874+ self . check_no_block_expr ( function_name, & stmt. resource ) ;
875+ self . check_no_block_block ( function_name, & stmt. body ) ;
876+ }
877+ Stmt :: If ( stmt) => {
878+ self . check_no_block_expr ( function_name, & stmt. condition ) ;
879+ self . check_no_block_block ( function_name, & stmt. then_body ) ;
880+ if let Some ( else_body) = & stmt. else_body {
881+ self . check_no_block_block ( function_name, else_body) ;
882+ }
883+ }
884+ Stmt :: Loop ( stmt) => {
885+ if let Some ( condition) = & stmt. condition {
886+ self . check_no_block_expr ( function_name, condition) ;
887+ }
888+ self . check_no_block_block ( function_name, & stmt. body ) ;
889+ }
890+ Stmt :: Break ( _) | Stmt :: Continue ( _) | Stmt :: Unknown ( _) => { }
891+ }
892+ }
893+
894+ fn check_no_block_expr ( & mut self , function_name : & str , expr : & Expr ) {
895+ match expr {
896+ Expr :: Call {
897+ callee, args, span, ..
898+ } => {
899+ match self . hir . resolve_call ( callee) {
900+ CallResolution :: Resolved { signature, kind } => {
901+ if !matches ! ( kind, ResolvedCalleeKind :: Constructor { .. } )
902+ && !signature. effects . iter ( ) . any ( |effect| effect == "no_block" )
903+ {
904+ self . blocking_call_diagnostic ( function_name, callee, span) ;
905+ }
906+ }
907+ CallResolution :: EnumVariant | CallResolution :: Unknown => { }
908+ }
909+ for arg in args {
910+ self . check_no_block_expr ( function_name, & arg. value ) ;
911+ }
912+ }
913+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } | Expr :: Try { value, .. } => {
914+ self . check_no_block_expr ( function_name, value) ;
915+ }
916+ Expr :: Binary { left, right, .. } => {
917+ self . check_no_block_expr ( function_name, left) ;
918+ self . check_no_block_expr ( function_name, right) ;
919+ }
920+ Expr :: Field { base, .. } => self . check_no_block_expr ( function_name, base) ,
921+ Expr :: Index { base, index, .. } => {
922+ self . check_no_block_expr ( function_name, base) ;
923+ self . check_no_block_expr ( function_name, index) ;
924+ }
925+ Expr :: Closure { body, .. } => self . check_no_block_block ( function_name, body) ,
926+ Expr :: Ident ( _, _) | Expr :: Number ( _, _) | Expr :: String ( _, _) | Expr :: Unknown ( _) => { }
927+ }
928+ }
929+
840930 fn check_duplicate_declarations ( & mut self ) {
841931 for duplicate in self . hir . duplicate_symbols ( ) {
842932 self . diagnostics . push (
@@ -1243,6 +1333,33 @@ impl Analyzer<'_> {
12431333 ) ,
12441334 ) ;
12451335 }
1336+
1337+ fn blocking_call_diagnostic (
1338+ & mut self ,
1339+ function_name : & str ,
1340+ callee : & Callee ,
1341+ span : & crate :: diagnostic:: Span ,
1342+ ) {
1343+ self . diagnostics . push (
1344+ Diagnostic :: error (
1345+ code:: INVALID_NO_BLOCK_CALL ,
1346+ format ! (
1347+ "`{function_name}` is declared no_block but calls possibly blocking function `{}`." ,
1348+ callee_display( callee)
1349+ ) ,
1350+ span. clone ( ) ,
1351+ "possibly blocking call in no_block function" ,
1352+ )
1353+ . with_cause (
1354+ "A `no_block` function may only call constructors, enum variants, or functions also declared `effects(no_block)`." ,
1355+ )
1356+ . with_fix (
1357+ "remove_no_block_or_call_no_block" ,
1358+ "Remove `no_block`, or call only APIs whose signatures are declared `effects(no_block)`." ,
1359+ "manual" ,
1360+ ) ,
1361+ ) ;
1362+ }
12461363}
12471364
12481365fn resource_pool_namespace_arg ( namespace : & str ) -> Option < & str > {
0 commit comments