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