@@ -33,6 +33,7 @@ impl Analyzer<'_> {
3333 self . check_signature_explicitness ( ) ;
3434 self . check_resource_fields ( ) ;
3535 self . check_resource_pool_type_arguments ( ) ;
36+ self . check_resource_generic_arguments ( ) ;
3637 checks:: mode:: check ( self ) ;
3738 checks:: calls:: check ( self ) ;
3839 checks:: body:: check ( self ) ;
@@ -191,6 +192,28 @@ impl Analyzer<'_> {
191192 }
192193 }
193194
195+ fn check_resource_generic_arguments ( & mut self ) {
196+ let items = self . syntax_program . items . clone ( ) ;
197+ for item in & items {
198+ match item {
199+ Item :: Type ( decl) => {
200+ for field in & decl. fields {
201+ self . check_resource_generic_type_ref ( & field. ty ) ;
202+ }
203+ }
204+ Item :: Function ( function) => {
205+ for param in & function. params {
206+ self . check_resource_generic_type_ref ( & param. ty ) ;
207+ }
208+ if let Some ( return_ty) = & function. return_ty {
209+ self . check_resource_generic_type_ref ( return_ty) ;
210+ }
211+ self . check_resource_generic_calls_in_block ( & function. body ) ;
212+ }
213+ }
214+ }
215+ }
216+
194217 fn check_resource_pool_type_ref ( & mut self , ty : & TypeRef ) {
195218 if ty. name == "ResourcePool" {
196219 match ty. args . first ( ) {
@@ -212,6 +235,19 @@ impl Analyzer<'_> {
212235 }
213236 }
214237
238+ fn check_resource_generic_type_ref ( & mut self , ty : & TypeRef ) {
239+ if ty. name != "ResourcePool" {
240+ for arg in & ty. args {
241+ if self . hir . type_kind ( & arg. name ) == Some ( HirTypeKind :: Resource ) {
242+ self . resource_generic_argument_diagnostic ( & ty. name , & arg. name , & arg. span ) ;
243+ }
244+ }
245+ }
246+ for arg in & ty. args {
247+ self . check_resource_generic_type_ref ( arg) ;
248+ }
249+ }
250+
215251 fn check_resource_pool_calls_in_stmt ( & mut self , statement : & Stmt ) {
216252 match statement {
217253 Stmt :: Let ( stmt) => {
@@ -279,6 +315,76 @@ impl Analyzer<'_> {
279315 }
280316 }
281317
318+ fn check_resource_generic_calls_in_block ( & mut self , block : & crate :: syntax:: ast:: Block ) {
319+ for statement in & block. statements {
320+ self . check_resource_generic_calls_in_stmt ( statement) ;
321+ }
322+ }
323+
324+ fn check_resource_generic_calls_in_stmt ( & mut self , statement : & Stmt ) {
325+ match statement {
326+ Stmt :: Let ( stmt) => {
327+ if let Some ( value) = & stmt. value {
328+ self . check_resource_generic_calls_in_expr ( value) ;
329+ }
330+ }
331+ Stmt :: Return ( stmt) => {
332+ if let Some ( value) = & stmt. value {
333+ self . check_resource_generic_calls_in_expr ( value) ;
334+ }
335+ }
336+ Stmt :: Expr ( value) => self . check_resource_generic_calls_in_expr ( value) ,
337+ Stmt :: With ( stmt) => {
338+ self . check_resource_generic_calls_in_expr ( & stmt. resource ) ;
339+ self . check_resource_generic_calls_in_block ( & stmt. body ) ;
340+ }
341+ Stmt :: If ( stmt) => {
342+ self . check_resource_generic_calls_in_expr ( & stmt. condition ) ;
343+ self . check_resource_generic_calls_in_block ( & stmt. then_body ) ;
344+ if let Some ( else_body) = & stmt. else_body {
345+ self . check_resource_generic_calls_in_block ( else_body) ;
346+ }
347+ }
348+ Stmt :: Loop ( stmt) => {
349+ if let Some ( condition) = & stmt. condition {
350+ self . check_resource_generic_calls_in_expr ( condition) ;
351+ }
352+ self . check_resource_generic_calls_in_block ( & stmt. body ) ;
353+ }
354+ Stmt :: Break ( _) | Stmt :: Continue ( _) | Stmt :: Unknown ( _) => { }
355+ }
356+ }
357+
358+ fn check_resource_generic_calls_in_expr ( & mut self , expr : & Expr ) {
359+ match expr {
360+ Expr :: Call { callee, args, span } => {
361+ if let Callee :: Qualified { namespace, .. } = callee
362+ && let Some ( ( root, args) ) = generic_namespace_args ( namespace)
363+ && root != "ResourcePool"
364+ {
365+ for arg in args {
366+ if self . hir . type_kind ( arg) == Some ( HirTypeKind :: Resource ) {
367+ self . resource_generic_argument_diagnostic ( root, arg, span) ;
368+ }
369+ }
370+ }
371+ for arg in args {
372+ self . check_resource_generic_calls_in_expr ( & arg. value ) ;
373+ }
374+ }
375+ Expr :: Binary { left, right, .. } => {
376+ self . check_resource_generic_calls_in_expr ( left) ;
377+ self . check_resource_generic_calls_in_expr ( right) ;
378+ }
379+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } => {
380+ self . check_resource_generic_calls_in_expr ( value) ;
381+ }
382+ Expr :: Field { base, .. } => self . check_resource_generic_calls_in_expr ( base) ,
383+ Expr :: Closure { body, .. } => self . check_resource_generic_calls_in_block ( body) ,
384+ Expr :: Ident ( _, _) | Expr :: Number ( _, _) | Expr :: String ( _, _) | Expr :: Unknown ( _) => { }
385+ }
386+ }
387+
282388 fn check_resource_pool_arg ( & mut self , type_name : & str , span : & crate :: diagnostic:: Span ) {
283389 match self . hir . type_kind ( type_name) {
284390 Some ( HirTypeKind :: Resource ) | None => { }
@@ -313,6 +419,30 @@ impl Analyzer<'_> {
313419 ) ,
314420 ) ;
315421 }
422+
423+ fn resource_generic_argument_diagnostic (
424+ & mut self ,
425+ generic_name : & str ,
426+ resource_name : & str ,
427+ span : & crate :: diagnostic:: Span ,
428+ ) {
429+ self . diagnostics . push (
430+ Diagnostic :: error (
431+ code:: RESOURCE_GENERIC_ARGUMENT ,
432+ format ! (
433+ "generic type `{generic_name}` cannot be instantiated with resource `{resource_name}`."
434+ ) ,
435+ span. clone ( ) ,
436+ "resource generic argument" ,
437+ )
438+ . with_cause ( "Only explicit resource APIs such as `ResourcePool<T: Resource>` may hold resources." )
439+ . with_fix (
440+ "use_resource_api" ,
441+ "Use `with`, `ResourcePool<T: Resource>`, or a non-resource value type." ,
442+ "manual" ,
443+ ) ,
444+ ) ;
445+ }
316446}
317447
318448fn resource_pool_namespace_arg ( namespace : & str ) -> Option < & str > {
@@ -321,6 +451,31 @@ fn resource_pool_namespace_arg(namespace: &str) -> Option<&str> {
321451 . and_then ( |rest| rest. strip_suffix ( '>' ) )
322452}
323453
454+ fn generic_namespace_args ( namespace : & str ) -> Option < ( & str , Vec < & str > ) > {
455+ let ( root, rest) = namespace. split_once ( '<' ) ?;
456+ let args = rest. strip_suffix ( '>' ) ?;
457+ Some ( ( root, split_top_level_type_args ( args) ) )
458+ }
459+
460+ fn split_top_level_type_args ( args : & str ) -> Vec < & str > {
461+ let mut result = Vec :: new ( ) ;
462+ let mut depth = 0usize ;
463+ let mut start = 0usize ;
464+ for ( index, ch) in args. char_indices ( ) {
465+ match ch {
466+ '<' => depth += 1 ,
467+ '>' => depth = depth. saturating_sub ( 1 ) ,
468+ ',' if depth == 0 => {
469+ result. push ( args[ start..index] . trim ( ) ) ;
470+ start = index + 1 ;
471+ }
472+ _ => { }
473+ }
474+ }
475+ result. push ( args[ start..] . trim ( ) ) ;
476+ result
477+ }
478+
324479fn effect_name ( effect : & EffectDecl ) -> & str {
325480 match effect {
326481 EffectDecl :: Name ( name) | EffectDecl :: Retains ( name) => name,
0 commit comments