@@ -301,3 +301,50 @@ impl<S: Stage> NoArgsAttributeParser<S> for DefaultLibAllocatorParser {
301301 const ALLOWED_TARGETS : AllowedTargets = AllowedTargets :: AllowList ( & [ Allow ( Target :: Crate ) ] ) ;
302302 const CREATE : fn ( Span ) -> AttributeKind = |_| AttributeKind :: DefaultLibAllocator ;
303303}
304+
305+ pub ( crate ) struct RegisterToolParser ;
306+
307+ impl < S : Stage > CombineAttributeParser < S > for RegisterToolParser {
308+ const PATH : & [ Symbol ] = & [ sym:: register_tool] ;
309+ type Item = Ident ;
310+ const CONVERT : ConvertFn < Self :: Item > = AttributeKind :: RegisterTool ;
311+ const ALLOWED_TARGETS : AllowedTargets = AllowedTargets :: AllowList ( ALL_TARGETS ) ;
312+ const TEMPLATE : AttributeTemplate = template ! ( List : & [ "tool1, tool2, ..." ] ) ;
313+
314+ fn extend (
315+ cx : & mut AcceptContext < ' _ , ' _ , S > ,
316+ args : & ArgParser ,
317+ ) -> impl IntoIterator < Item = Self :: Item > {
318+ let ArgParser :: List ( list) = args else {
319+ cx. expected_list ( cx. attr_span , args) ;
320+ return Vec :: new ( ) ;
321+ } ;
322+
323+ if list. is_empty ( ) {
324+ cx. warn_empty_attribute ( cx. attr_span ) ;
325+ }
326+
327+ let mut res = Vec :: new ( ) ;
328+
329+ for elem in list. mixed ( ) {
330+ let Some ( elem) = elem. meta_item ( ) else {
331+ cx. expected_identifier ( elem. span ( ) ) ;
332+ continue ;
333+ } ;
334+ if let Err ( arg_span) = elem. args ( ) . no_args ( ) {
335+ cx. expected_no_args ( arg_span) ;
336+ continue ;
337+ }
338+
339+ let path = elem. path ( ) ;
340+ let Some ( ident) = path. word ( ) else {
341+ cx. expected_identifier ( path. span ( ) ) ;
342+ continue ;
343+ } ;
344+
345+ res. push ( ident) ;
346+ }
347+
348+ res
349+ }
350+ }
0 commit comments