@@ -2,7 +2,7 @@ use crate::checks;
22use crate :: diagnostic:: { Diagnostic , code} ;
33use crate :: hir:: { DuplicateSymbolKind , Hir , HirTypeKind } ;
44use crate :: lexer:: { Token , lex} ;
5- use crate :: syntax:: ast:: { EffectDecl , Item } ;
5+ use crate :: syntax:: ast:: { Callee , EffectDecl , Expr , Item , Stmt , TypeRef } ;
66use crate :: syntax:: parse_source;
77
88pub fn analyze_source ( file : & str , source : & str ) -> Vec < Diagnostic > {
@@ -32,6 +32,7 @@ impl Analyzer<'_> {
3232 self . check_duplicate_declarations ( ) ;
3333 self . check_signature_explicitness ( ) ;
3434 self . check_resource_fields ( ) ;
35+ self . check_resource_pool_type_arguments ( ) ;
3536 checks:: mode:: check ( self ) ;
3637 checks:: calls:: check ( self ) ;
3738 checks:: body:: check ( self ) ;
@@ -167,6 +168,157 @@ impl Analyzer<'_> {
167168 }
168169 }
169170 }
171+
172+ fn check_resource_pool_type_arguments ( & mut self ) {
173+ let items = self . syntax_program . items . clone ( ) ;
174+ for item in & items {
175+ match item {
176+ Item :: Type ( decl) => {
177+ for field in & decl. fields {
178+ self . check_resource_pool_type_ref ( & field. ty ) ;
179+ }
180+ }
181+ Item :: Function ( function) => {
182+ for param in & function. params {
183+ self . check_resource_pool_type_ref ( & param. ty ) ;
184+ }
185+ if let Some ( return_ty) = & function. return_ty {
186+ self . check_resource_pool_type_ref ( return_ty) ;
187+ }
188+ self . check_resource_pool_calls_in_block ( & function. body ) ;
189+ }
190+ }
191+ }
192+ }
193+
194+ fn check_resource_pool_type_ref ( & mut self , ty : & TypeRef ) {
195+ if ty. name == "ResourcePool" {
196+ match ty. args . first ( ) {
197+ Some ( arg) => self . check_resource_pool_arg ( & arg. name , & arg. span ) ,
198+ None => self . invalid_resource_pool_type_diagnostic (
199+ "ResourcePool must declare a resource type argument." ,
200+ ty. span . clone ( ) ,
201+ ) ,
202+ }
203+ }
204+ for arg in & ty. args {
205+ self . check_resource_pool_type_ref ( arg) ;
206+ }
207+ }
208+
209+ fn check_resource_pool_calls_in_block ( & mut self , block : & crate :: syntax:: ast:: Block ) {
210+ for statement in & block. statements {
211+ self . check_resource_pool_calls_in_stmt ( statement) ;
212+ }
213+ }
214+
215+ fn check_resource_pool_calls_in_stmt ( & mut self , statement : & Stmt ) {
216+ match statement {
217+ Stmt :: Let ( stmt) => {
218+ if let Some ( value) = & stmt. value {
219+ self . check_resource_pool_calls_in_expr ( value) ;
220+ }
221+ }
222+ Stmt :: Return ( stmt) => {
223+ if let Some ( value) = & stmt. value {
224+ self . check_resource_pool_calls_in_expr ( value) ;
225+ }
226+ }
227+ Stmt :: Expr ( value) => self . check_resource_pool_calls_in_expr ( value) ,
228+ Stmt :: With ( stmt) => {
229+ self . check_resource_pool_calls_in_expr ( & stmt. resource ) ;
230+ self . check_resource_pool_calls_in_block ( & stmt. body ) ;
231+ }
232+ Stmt :: If ( stmt) => {
233+ self . check_resource_pool_calls_in_expr ( & stmt. condition ) ;
234+ self . check_resource_pool_calls_in_block ( & stmt. then_body ) ;
235+ if let Some ( else_body) = & stmt. else_body {
236+ self . check_resource_pool_calls_in_block ( else_body) ;
237+ }
238+ }
239+ Stmt :: Loop ( stmt) => {
240+ if let Some ( condition) = & stmt. condition {
241+ self . check_resource_pool_calls_in_expr ( condition) ;
242+ }
243+ self . check_resource_pool_calls_in_block ( & stmt. body ) ;
244+ }
245+ Stmt :: Break ( _) | Stmt :: Continue ( _) | Stmt :: Unknown ( _) => { }
246+ }
247+ }
248+
249+ fn check_resource_pool_calls_in_expr ( & mut self , expr : & Expr ) {
250+ match expr {
251+ Expr :: Call { callee, args, span } => {
252+ if let Callee :: Qualified { namespace, name } = callee
253+ && namespace == "ResourcePool"
254+ && name == "new"
255+ {
256+ self . invalid_resource_pool_type_diagnostic (
257+ "ResourcePool.new must be called as ResourcePool<T>.new with resource T." ,
258+ span. clone ( ) ,
259+ ) ;
260+ } else if let Callee :: Qualified { namespace, .. } = callee
261+ && let Some ( arg) = resource_pool_namespace_arg ( namespace)
262+ {
263+ self . check_resource_pool_arg ( arg, span) ;
264+ }
265+ for arg in args {
266+ self . check_resource_pool_calls_in_expr ( & arg. value ) ;
267+ }
268+ }
269+ Expr :: Binary { left, right, .. } => {
270+ self . check_resource_pool_calls_in_expr ( left) ;
271+ self . check_resource_pool_calls_in_expr ( right) ;
272+ }
273+ Expr :: Effect { value, .. } | Expr :: Manage { value, .. } => {
274+ self . check_resource_pool_calls_in_expr ( value) ;
275+ }
276+ Expr :: Field { base, .. } => self . check_resource_pool_calls_in_expr ( base) ,
277+ Expr :: Closure { body, .. } => self . check_resource_pool_calls_in_block ( body) ,
278+ Expr :: Ident ( _, _) | Expr :: Number ( _, _) | Expr :: String ( _, _) | Expr :: Unknown ( _) => { }
279+ }
280+ }
281+
282+ fn check_resource_pool_arg ( & mut self , type_name : & str , span : & crate :: diagnostic:: Span ) {
283+ match self . hir . type_kind ( type_name) {
284+ Some ( HirTypeKind :: Resource ) | None => { }
285+ Some ( HirTypeKind :: Class ) | Some ( HirTypeKind :: Struct ) => {
286+ self . invalid_resource_pool_type_diagnostic (
287+ format ! (
288+ "ResourcePool can only hold resources, but `{type_name}` is not a resource."
289+ ) ,
290+ span. clone ( ) ,
291+ ) ;
292+ }
293+ }
294+ }
295+
296+ fn invalid_resource_pool_type_diagnostic (
297+ & mut self ,
298+ summary : impl Into < String > ,
299+ span : crate :: diagnostic:: Span ,
300+ ) {
301+ self . diagnostics . push (
302+ Diagnostic :: error (
303+ code:: INVALID_RESOURCE_POOL_TYPE ,
304+ summary,
305+ span,
306+ "invalid ResourcePool type" ,
307+ )
308+ . with_cause ( "`ResourcePool<T>` is the privileged container for long-lived resource values, so `T` must be a resource." )
309+ . with_fix (
310+ "use_resource_type" ,
311+ "Use a resource type argument or a non-resource container for ordinary values." ,
312+ "manual" ,
313+ ) ,
314+ ) ;
315+ }
316+ }
317+
318+ fn resource_pool_namespace_arg ( namespace : & str ) -> Option < & str > {
319+ namespace
320+ . strip_prefix ( "ResourcePool<" )
321+ . and_then ( |rest| rest. strip_suffix ( '>' ) )
170322}
171323
172324fn effect_name ( effect : & EffectDecl ) -> & str {
0 commit comments