Class: Steep::TypeInference::SendArgs

Inherits:
Object
  • Object
show all
Defined in:
lib/steep/type_inference/send_args.rb

Defined Under Namespace

Classes: BlockPassArg, ForwardedArgs, KeywordArgs, PositionalArgs

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(node:, arguments:, type:) ⇒ SendArgs

Returns a new instance of SendArgs.



502
503
504
505
506
507
# File 'lib/steep/type_inference/send_args.rb', line 502

def initialize(node:, arguments:, type:)
  raise "Untyped function is not supported" unless type.type.params
  @node = node
  @arguments = arguments
  @type = type
end

Instance Attribute Details

#argumentsObject (readonly)

Returns the value of attribute arguments.



499
500
501
# File 'lib/steep/type_inference/send_args.rb', line 499

def arguments
  @arguments
end

#nodeObject (readonly)

Returns the value of attribute node.



498
499
500
# File 'lib/steep/type_inference/send_args.rb', line 498

def node
  @node
end

#typeObject (readonly)

Returns the value of attribute type.



500
501
502
# File 'lib/steep/type_inference/send_args.rb', line 500

def type
  @type
end

Instance Method Details

#blockObject



520
521
522
523
524
525
526
527
# File 'lib/steep/type_inference/send_args.rb', line 520

def block
  case type
  when Interface::MethodType
    type.block
  when AST::Types::Proc
    type.block
  end
end

#block_pass_argObject



567
568
569
570
571
# File 'lib/steep/type_inference/send_args.rb', line 567

def block_pass_arg
  node = arguments.find {|node| node.type == :block_pass }

  BlockPassArg.new(node: node, block: block)
end

#eachObject



573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
# File 'lib/steep/type_inference/send_args.rb', line 573

def each
  if block_given?
    errors = [] #: Array[PositionalArgs::error_arg | KeywordArgs::error_arg]

    last_positional_args = positional_arg

    positional_arg.tap do |args|
      while (value, args = args.next())
        yield value

        case value
        when PositionalArgs::SplatArg
          type = value.type

          case type
          when nil
            raise
          when AST::Types::Tuple
            ts, args = args.consume(type.types.size, node: value.node)

            case ts
            when Array
              ty = AST::Types::Tuple.new(types: ts.map(&:type))
              yield PositionalArgs::NodeTypePair.new(node: value.node, type: ty)
            when PositionalArgs::UnexpectedArg
              errors << ts
              yield ts
            end
          else
            if t = args.uniform_type
              args.following_args.each do |node|
                yield PositionalArgs::NodeTypePair.new(node: node, type: t)
              end
            else
              args.following_args.each do |node|
                arg = PositionalArgs::UnexpectedArg.new(node: node)
                yield arg
                errors << arg
              end
            end

            break
          end
        when PositionalArgs::UnexpectedArg, PositionalArgs::MissingArg
          errors << value
        end

        last_positional_args = args
      end
    end

    if fag = forwarded_args_node
      forward_params = Interface::Function::Params.new(
        positional_params: last_positional_args.positional_params,
        keyword_params: keyword_params
      )

      forwarded_args = ForwardedArgs.new(node: fag, params: forward_params)
    else
      keyword_args.tap do |args|
        while (a, args = args.next)
          case a
          when KeywordArgs::MissingKeyword
            errors << a
          when KeywordArgs::UnexpectedKeyword
            errors << a
          end

          yield a

          case a
          when KeywordArgs::SplatArg
            case type = a.type
            when nil
              raise
            when AST::Types::Record
              # @type var keys: Array[Symbol]
              keys = _ = type.elements.keys
              ts, args = args.consume_keys(keys, node: a.node)

              case ts
              when KeywordArgs::UnexpectedKeyword
                yield ts
                errors << ts
              when Array
                pairs = keys.zip(ts) #: Array[[Symbol, AST::Types::t]]
                record = AST::Types::Record.new(elements: Hash[pairs], required_keys: Set.new(keys))
                yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, record]])
              end
            else
              args = args.update(index: args.index + 1)

              if args.rest_type
                type = AST::Builtin::Hash.instance_type(AST::Builtin::Symbol.instance_type, args.possible_value_type)
                yield KeywordArgs::ArgTypePairs.new(pairs: [[a.node, type]])
              else
                yield KeywordArgs::UnexpectedKeyword.new(keyword: nil, node: a.node)
              end
            end
          end
        end
      end
    end

    diagnostics = [] #: Array[Diagnostic::Ruby::Base]

    missing_keywords = [] #: Array[Symbol]
    errors.each do |error|
      case error
      when KeywordArgs::UnexpectedKeyword
        diagnostics << Diagnostic::Ruby::UnexpectedKeywordArgument.new(node: error.node, params: params)
      when KeywordArgs::MissingKeyword
        missing_keywords.push(*error.keywords.to_a)
      when PositionalArgs::UnexpectedArg
        if error.node.type == :kwargs
          error.node.children.each do |kwarg|
            if kwarg.type == :pair
              diagnostics << Diagnostic::Ruby::UnexpectedKeywordArgument.new(node: kwarg, params: params)
            end
          end
        else
          diagnostics << Diagnostic::Ruby::UnexpectedPositionalArgument.new(node: error.node, params: params)
        end
      when PositionalArgs::MissingArg
        diagnostics << Diagnostic::Ruby::InsufficientPositionalArguments.new(node: node, params: params)
      end
    end

    unless missing_keywords.empty?
      diagnostics << Diagnostic::Ruby::InsufficientKeywordArguments.new(node: node, params: params, missing_keywords: missing_keywords)
    end

    [forwarded_args, diagnostics]
  else
    enum_for :each
  end
end

#forwarded_args_nodeObject



556
557
558
# File 'lib/steep/type_inference/send_args.rb', line 556

def forwarded_args_node
  arguments.find {|node| node.type == :forwarded_args }
end

#keyword_argsObject



560
561
562
563
564
565
# File 'lib/steep/type_inference/send_args.rb', line 560

def keyword_args
  KeywordArgs.new(
    kwarg_nodes: kwargs_node&.children || [],
    keyword_params: keyword_params
  )
end

#keyword_paramsObject



534
535
536
537
# File 'lib/steep/type_inference/send_args.rb', line 534

def keyword_params
  params or raise
  params.keyword_params
end

#kwargs_nodeObject



539
540
541
542
543
# File 'lib/steep/type_inference/send_args.rb', line 539

def kwargs_node
  unless keyword_params.empty?
    arguments.find {|node| node.type == :kwargs }
  end
end

#paramsObject



509
510
511
512
513
514
515
516
517
518
# File 'lib/steep/type_inference/send_args.rb', line 509

def params
  case type
  when Interface::MethodType
    type.type.params or raise
  when AST::Types::Proc
    type.type.params or raise
  else
    raise
  end
end

#positional_argObject



545
546
547
548
549
550
551
552
553
554
# File 'lib/steep/type_inference/send_args.rb', line 545

def positional_arg
  args =
    if keyword_params.empty?
      arguments.take_while {|node| node.type != :block_pass }
    else
      arguments.take_while {|node| node.type != :kwargs && node.type != :block_pass }
    end

  PositionalArgs.new(args: args, index: 0, positional_params: positional_params)
end

#positional_paramsObject



529
530
531
532
# File 'lib/steep/type_inference/send_args.rb', line 529

def positional_params
  params or raise
  params.positional_params
end