@@ -350,7 +350,8 @@ impl<'a> AstValidator<'a> {
350350
351351 fn check_fn_decl ( & self , fn_decl : & FnDecl , self_semantic : SelfSemantic ) {
352352 self . check_decl_num_args ( fn_decl) ;
353- self . check_decl_cvariadic_pos ( fn_decl) ;
353+ let c_variadic_span = self . check_decl_cvariadic_pos ( fn_decl) ;
354+ self . check_decl_splatting ( fn_decl, c_variadic_span) ;
354355 self . check_decl_attrs ( fn_decl) ;
355356 self . check_decl_self_param ( fn_decl, self_semantic) ;
356357 }
@@ -368,17 +369,68 @@ impl<'a> AstValidator<'a> {
368369 /// Emits an error if a function declaration has a variadic parameter in the
369370 /// beginning or middle of parameter list.
370371 /// Example: `fn foo(..., x: i32)` will emit an error.
371- fn check_decl_cvariadic_pos ( & self , fn_decl : & FnDecl ) {
372+ /// Returns true if a C-variadic parameter is found.
373+ fn check_decl_cvariadic_pos ( & self , fn_decl : & FnDecl ) -> Option < Span > {
374+ let mut c_variadic_span = None ;
375+
372376 match & * fn_decl. inputs {
373377 [ ps @ .., _] => {
374378 for Param { ty, span, .. } in ps {
375379 if let TyKind :: CVarArgs = ty. kind {
380+ c_variadic_span = Some ( * span) ;
376381 self . dcx ( ) . emit_err ( errors:: FnParamCVarArgsNotLast { span : * span } ) ;
377382 }
378383 }
379384 }
380385 _ => { }
381386 }
387+
388+ if let Some ( Param { ty, span, .. } ) = & fn_decl. inputs . last ( ) {
389+ if let TyKind :: CVarArgs = ty. kind {
390+ c_variadic_span = Some ( * span) ;
391+ }
392+ }
393+
394+ c_variadic_span
395+ }
396+
397+ /// Emits an error if a function declaration has more than one splatted argument, with a
398+ /// C-variadic parameter, or a splat at an unsupported index (for performance).
399+ /// Example: `fn foo(#[splat] x: (), #[splat] y: ())` will emit an error.
400+ fn check_decl_splatting ( & self , fn_decl : & FnDecl , c_variadic_span : Option < Span > ) {
401+ let ( splatted_arg_indexes, mut splatted_spans) : ( Vec < u16 > , Vec < Span > ) = fn_decl
402+ . inputs
403+ . iter ( )
404+ . enumerate ( )
405+ . filter_map ( |( index, arg) | {
406+ arg. attrs
407+ . iter ( )
408+ . any ( |attr| attr. has_name ( sym:: splat) )
409+ . then_some ( ( u16:: try_from ( index) . unwrap ( ) , arg. span ) )
410+ } )
411+ . unzip ( ) ;
412+
413+ // A splatted argument at the "no splatted" marker index is not supported (this is an
414+ // unlikely edge case).
415+ if let ( Some ( & splatted_arg_index) , Some ( & splatted_span) ) =
416+ ( splatted_arg_indexes. last ( ) , splatted_spans. last ( ) )
417+ && splatted_arg_index == FnDecl :: NO_SPLATTED_ARG_INDEX
418+ {
419+ self . dcx ( )
420+ . emit_err ( errors:: InvalidSplattedArg { splatted_arg_index, span : splatted_span } ) ;
421+ }
422+
423+ // Multiple splatted arguments are invalid: we can't know which arguments go in each splat.
424+ if splatted_arg_indexes. len ( ) > 1 {
425+ self . dcx ( ) . emit_err ( errors:: DuplicateSplattedArgs { spans : splatted_spans. clone ( ) } ) ;
426+ }
427+
428+ if let Some ( c_variadic_span) = c_variadic_span
429+ && !splatted_spans. is_empty ( )
430+ {
431+ splatted_spans. push ( c_variadic_span) ;
432+ self . dcx ( ) . emit_err ( errors:: CVarArgsAndSplat { spans : splatted_spans } ) ;
433+ }
382434 }
383435
384436 fn check_decl_attrs ( & self , fn_decl : & FnDecl ) {
@@ -394,6 +446,7 @@ impl<'a> AstValidator<'a> {
394446 sym:: deny,
395447 sym:: expect,
396448 sym:: forbid,
449+ sym:: splat,
397450 sym:: warn,
398451 ] ;
399452 !attr. has_any_name ( & arr) && rustc_attr_parsing:: is_builtin_attr ( * attr)
0 commit comments