11use nu_protocol:: {
22 ParseError , Span , Type ,
33 ast:: { Assignment , Block , Comparison , Expr , Expression , Math , Operator , Pipeline , Range } ,
4+ combined_type_string,
45 engine:: StateWorkingSet ,
56} ;
67
@@ -757,65 +758,71 @@ pub fn math_result_type(
757758 }
758759}
759760
761+ /// Determine the possible output types of a pipeline.
762+ ///
763+ /// Output is union of types in the `Vec`.
760764pub fn check_pipeline_type (
761765 working_set : & StateWorkingSet ,
762766 pipeline : & Pipeline ,
763767 input_type : Type ,
764- ) -> ( Type , Option < Vec < ParseError > > ) {
765- let mut current_type = input_type;
768+ ) -> ( Vec < Type > , Option < Vec < ParseError > > ) {
769+ let mut current_types: Vec < Type > ;
770+ let mut new_types: Vec < Type > = vec ! [ input_type] ;
766771
767772 let mut output_errors: Option < Vec < ParseError > > = None ;
768773
769- ' elem: for elem in & pipeline. elements {
774+ for elem in & pipeline. elements {
775+ current_types = std:: mem:: take ( & mut new_types) ;
776+
770777 if elem. redirection . is_some ( ) {
771- current_type = Type :: Any ;
772- } else if let Expr :: Call ( call) = & elem. expr . expr {
778+ new_types = vec ! [ Type :: Any ] ;
779+ continue ;
780+ }
781+ if let Expr :: Call ( call) = & elem. expr . expr {
773782 let decl = working_set. get_decl ( call. decl_id ) ;
774-
775- if current_type == Type :: Any {
776- let mut new_current_type = None ;
777- for ( _, call_output) in decl. signature ( ) . input_output_types {
778- if let Some ( inner_current_type) = & new_current_type {
779- if inner_current_type == & Type :: Any {
780- break ;
781- } else if inner_current_type != & call_output {
782- // Union unequal types to Any for now
783- new_current_type = Some ( Type :: Any )
784- }
785- } else {
786- new_current_type = Some ( call_output. clone ( ) )
787- }
788- }
789-
790- if let Some ( new_current_type) = new_current_type {
791- current_type = new_current_type
792- } else {
793- current_type = Type :: Any ;
794- }
795- continue ' elem;
783+ let io_types = decl. signature ( ) . input_output_types ;
784+ if new_types. contains ( & Type :: Any ) {
785+ // if input type is any, then output type could be any of the valid output types
786+ new_types = io_types. into_iter ( ) . map ( |( _, out_type) | out_type) . collect ( ) ;
796787 } else {
797- for ( call_input, call_output) in decl. signature ( ) . input_output_types {
798- if type_compatible ( & call_input, & current_type) {
799- current_type = call_output. clone ( ) ;
800- continue ' elem;
801- }
802- }
788+ // any current type which matches an input type is a possible output type
789+ new_types = io_types
790+ . into_iter ( )
791+ . filter ( |( in_type, _) | {
792+ current_types. iter ( ) . any ( |ty| type_compatible ( in_type, ty) )
793+ } )
794+ . map ( |( _, out_type) | out_type)
795+ . collect ( ) ;
803796 }
804797
805- if !decl. signature ( ) . input_output_types . is_empty ( ) {
806- if let Some ( output_errors) = & mut output_errors {
807- output_errors. push ( ParseError :: InputMismatch ( current_type, call. head ) )
808- } else {
809- output_errors = Some ( vec ! [ ParseError :: InputMismatch ( current_type, call. head) ] ) ;
810- }
798+ if !new_types. is_empty ( ) {
799+ continue ;
811800 }
812- current_type = Type :: Any ;
801+
802+ if decl. signature ( ) . input_output_types . is_empty ( ) {
803+ new_types = vec ! [ Type :: Any ] ;
804+ continue ;
805+ }
806+
807+ let Some ( types_string) = combined_type_string ( & current_types, "or" ) else {
808+ output_errors
809+ . get_or_insert_default ( )
810+ . push ( ParseError :: InternalError (
811+ "Pipeline has no type at this point" . to_string ( ) ,
812+ elem. expr . span ,
813+ ) ) ;
814+ continue ;
815+ } ;
816+
817+ output_errors
818+ . get_or_insert_default ( )
819+ . push ( ParseError :: InputMismatch ( types_string, call. head ) ) ;
813820 } else {
814- current_type = elem. expr . ty . clone ( ) ;
821+ new_types = vec ! [ elem. expr. ty. clone( ) ] ;
815822 }
816823 }
817824
818- ( current_type , output_errors)
825+ ( new_types , output_errors)
819826}
820827
821828pub fn check_block_input_output ( working_set : & StateWorkingSet , block : & Block ) -> Vec < ParseError > {
@@ -824,21 +831,29 @@ pub fn check_block_input_output(working_set: &StateWorkingSet, block: &Block) ->
824831
825832 for ( input_type, output_type) in & block. signature . input_output_types {
826833 let mut current_type = input_type. clone ( ) ;
827- let mut current_output_type = Type :: Nothing ;
834+ let mut current_output_types = vec ! [ ] ;
828835
829836 for pipeline in & block. pipelines {
830- let ( checked_output_type , err) =
837+ let ( checked_output_types , err) =
831838 check_pipeline_type ( working_set, pipeline, current_type) ;
832- current_output_type = checked_output_type ;
839+ current_output_types = checked_output_types ;
833840 current_type = Type :: Nothing ;
834841 if let Some ( err) = err {
835842 output_errors. extend_from_slice ( & err) ;
836843 }
837844 }
838845
839- if !type_compatible ( output_type, & current_output_type)
840- && output_type != & Type :: Any
841- && current_output_type != Type :: Any
846+ if block. pipelines . is_empty ( ) {
847+ current_output_types = vec ! [ Type :: Nothing ] ;
848+ }
849+
850+ if output_type == & Type :: Any || current_output_types. contains ( & Type :: Any ) {
851+ continue ;
852+ }
853+
854+ if !current_output_types
855+ . iter ( )
856+ . any ( |ty| type_compatible ( output_type, ty) )
842857 {
843858 let span = if block. pipelines . is_empty ( ) {
844859 if let Some ( span) = block. span {
@@ -858,9 +873,17 @@ pub fn check_block_input_output(working_set: &StateWorkingSet, block: &Block) ->
858873 . span
859874 } ;
860875
876+ let Some ( current_ty_string) = combined_type_string ( & current_output_types, "or" ) else {
877+ output_errors. push ( ParseError :: InternalError (
878+ "Block has no type at this point" . to_string ( ) ,
879+ span,
880+ ) ) ;
881+ continue ;
882+ } ;
883+
861884 output_errors. push ( ParseError :: OutputMismatch (
862885 output_type. clone ( ) ,
863- current_output_type . clone ( ) ,
886+ current_ty_string ,
864887 span,
865888 ) )
866889 }
0 commit comments