class RuboCop::Cop::Sorbet::EnforceSignatures

  • ‘Style`: signature style to enforce - ’sig’ for sig blocks, ‘rbs’ for RBS comments, ‘both’ to allow either (default: ‘sig’)
    * ‘ReturnTypePlaceholder`: placeholders used for return types (default: ’T.untyped’)
    * ‘ParameterTypePlaceholder`: placeholders used for parameter types (default: ’T.untyped’)
    You can configure the placeholders used by changing the following options:
    “‘
    def foo(a, b, c); end
    sig { params(a: T.untyped, b: T.untyped, c: T.untyped).returns(T.untyped)
    “`
    Will be corrected as:
    “`
    def foo(a, b, c); end
    “`
    It also suggest an autocorrect with placeholders so the following code:
    Checks that every method definition and attribute accessor has a Sorbet signature.

def add_accessor_parameter_if_needed(suggest, symbol, method)

def add_accessor_parameter_if_needed(suggest, symbol, method)
  return unless symbol && writer_or_accessor?(method)
  suggest.params << symbol.value
end

def allow_rbs?

def allow_rbs?
  cop_config["AllowRBS"] == true
end

def autocorrect_with_signature_type(corrector, node, type)

def autocorrect_with_signature_type(corrector, node, type)
  suggest = create_signature_suggestion(node, type)
  populate_signature_suggestion(suggest, node)
  corrector.insert_before(node, suggest.to_autocorrect)
end

def check_node(node)

def check_node(node)
  scope = self.scope(node)
  sig_node = sig_checker.signature_node(scope)
  rbs_node = rbs_checker.signature_node(node)
  case signature_style
  when "rbs"
    # RBS style - only RBS signatures allowed
    if sig_node
      add_offense(sig_node, message: "Use RBS signature comments rather than sig blocks.")
      return
    end
    unless rbs_node
      add_offense(node, message: "Each method is required to have an RBS signature.") do |corrector|
        autocorrect_with_signature_type(corrector, node, "rbs")
      end
    end
  when "both"
    # Both styles allowed - require at least one
    unless sig_node || rbs_node
      add_offense(node, message: "Each method is required to have a signature.") do |corrector|
        autocorrect_with_signature_type(corrector, node, "sig")
      end
    end
  else # "sig" (default)
    # Sig style - only sig signatures allowed
    unless sig_node
      add_offense(node, message: "Each method is required to have a sig block signature.") do |corrector|
        autocorrect_with_signature_type(corrector, node, "sig")
      end
    end
  end
ensure
  sig_checker.clear_signature(scope)
end

def create_signature_suggestion(node, type)

def create_signature_suggestion(node, type)
  case type
  when "rbs"
    RBSSuggestion.new(node.loc.column)
  else # "sig"
    SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)
  end
end

def on_def(node)

def on_def(node)
  check_node(node)
end

def on_defs(node)

def on_defs(node)
  check_node(node)
end

def on_new_investigation

def on_new_investigation
  super
  @sig_checker = nil
  @rbs_checker = nil
end

def on_send(node)

def on_send(node)
  check_node(node) if accessor?(node)
end

def on_signature(node)

def on_signature(node)
  sig_checker.on_signature(node, scope(node))
end

def param_type_placeholder

def param_type_placeholder
  cop_config["ParameterTypePlaceholder"] || "T.untyped"
end

def populate_accessor_suggestion(suggest, node)

def populate_accessor_suggestion(suggest, node)
  method = node.children[1]
  symbol = node.children[2]
  add_accessor_parameter_if_needed(suggest, symbol, method)
  set_void_return_for_writer(suggest, method)
end

def populate_method_definition_suggestion(suggest, node)

def populate_method_definition_suggestion(suggest, node)
  node.arguments.each do |arg|
    if arg.blockarg_type? && suggest.respond_to?(:has_block=)
      suggest.has_block = true
    else
      suggest.params << arg.children.first
    end
  end
end

def populate_signature_suggestion(suggest, node)

def populate_signature_suggestion(suggest, node)
  if node.any_def_type?
    populate_method_definition_suggestion(suggest, node)
  elsif accessor?(node)
    populate_accessor_suggestion(suggest, node)
  end
end

def rbs_checker

def rbs_checker
  @rbs_checker ||= RBSSignatureChecker.new(processed_source)
end

def return_type_placeholder

def return_type_placeholder
  cop_config["ReturnTypePlaceholder"] || "T.untyped"
end

def scope(node)

def scope(node)
  return unless node.parent
  return node.parent if [:begin, :block, :class, :module].include?(node.parent.type)
  scope(node.parent)
end

def set_void_return_for_writer(suggest, method)

def set_void_return_for_writer(suggest, method)
  suggest.returns = "void" if method == :attr_writer
end

def sig_checker

def sig_checker
  @sig_checker ||= SigSignatureChecker.new(processed_source)
end

def signature_style

def signature_style
  config_value = cop_config["Style"]
  if config_value
    unless VALID_STYLES.include?(config_value)
      raise ArgumentError, "Invalid Style option: '#{config_value}'. Valid options are: #{VALID_STYLES.join(", ")}"
    end
    return config_value
  end
  return "both" if allow_rbs?
  "sig"
end

def writer_or_accessor?(method)

def writer_or_accessor?(method)
  method == :attr_writer || method == :attr_accessor
end