mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 14:23:48 +04:00
Enable strict typing in Library/Homebrew/rubocops/
This commit is contained in:
Vendored
+2
-1
@@ -66,5 +66,6 @@
|
||||
}
|
||||
},
|
||||
"simplecov-vscode.path": "Library/Homebrew/test/coverage/.resultset.json",
|
||||
"simplecov-vscode.enabled": false
|
||||
"simplecov-vscode.enabled": false,
|
||||
"specstory.cloudSync.enabled": "never"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ module RuboCop
|
||||
|
||||
sha256_nodes = find_method_calls_by_name(bottle_node.body, :sha256)
|
||||
cellar_node = find_node_method_by_name(bottle_node.body, :cellar)
|
||||
cellar_source = cellar_node&.first_argument&.source
|
||||
cellar_source = T.cast(cellar_node, T.nilable(RuboCop::AST::SendNode))&.first_argument&.source
|
||||
|
||||
if sha256_nodes.present? && cellar_node.present?
|
||||
offending_node(cellar_node)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
@@ -35,6 +35,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(source: T::Array[String]).returns(T::Array[String]) }
|
||||
def sort_array(source)
|
||||
# Combine each comment with the line(s) below so that they remain in the same relative location
|
||||
combined_source = source.each_with_index.filter_map do |line, index|
|
||||
@@ -50,17 +51,22 @@ module RuboCop
|
||||
|
||||
# Sort the lines that should be sorted
|
||||
to_sort.sort! do |a, b|
|
||||
a_non_comment = a.split("\n").reject { |line| line.strip.start_with?("#") }.first
|
||||
b_non_comment = b.split("\n").reject { |line| line.strip.start_with?("#") }.first
|
||||
a_non_comment.downcase <=> b_non_comment.downcase
|
||||
a_non_comment = a.split("\n").reject { |line| line.strip.start_with?("#") }.fetch(0)
|
||||
b_non_comment = b.split("\n").reject { |line| line.strip.start_with?("#") }.fetch(0)
|
||||
a_non_comment.downcase <=> b_non_comment.downcase || raise("Expected non-comment lines to be present")
|
||||
end
|
||||
|
||||
# Merge the sorted lines and the unsorted lines, preserving the original positions of the unsorted lines
|
||||
combined_source.map { |line| to_keep.include?(line) ? line : to_sort.shift }
|
||||
combined_source.map do |line|
|
||||
next line if to_keep.include?(line)
|
||||
|
||||
to_sort.shift || raise("Expected to_sort to be present")
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(source: T::Array[String], index: Integer, line: String).returns(String) }
|
||||
def recursively_find_comments(source, index, line)
|
||||
if source[index - 1].strip.start_with?("#")
|
||||
if source.fetch(index - 1).strip.start_with?("#")
|
||||
return recursively_find_comments(source, index - 1, "#{source[index - 1]}\n#{line}")
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
@@ -7,18 +7,22 @@ module RuboCop
|
||||
# This class wraps the AST method node that represents the cask header. It
|
||||
# includes various helper methods to aid cops in their analysis.
|
||||
class CaskHeader
|
||||
sig { params(method_node: T.all(RuboCop::AST::Node, RuboCop::AST::ParameterizedNode::RestArguments)).void }
|
||||
def initialize(method_node)
|
||||
@method_node = method_node
|
||||
end
|
||||
|
||||
sig { returns(T.all(RuboCop::AST::Node, RuboCop::AST::ParameterizedNode::RestArguments)) }
|
||||
attr_reader :method_node
|
||||
|
||||
sig { returns(String) }
|
||||
def header_str
|
||||
@header_str ||= source_range.source
|
||||
@header_str ||= T.let(source_range.source, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(Parser::Source::Range) }
|
||||
def source_range
|
||||
@source_range ||= method_node.loc.expression
|
||||
@source_range ||= T.let(method_node.loc.expression, T.nilable(Parser::Source::Range))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
@@ -26,16 +30,19 @@ module RuboCop
|
||||
"cask '#{cask_token}'"
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def cask_token
|
||||
@cask_token ||= method_node.first_argument.str_content
|
||||
@cask_token ||= T.let(method_node.first_argument.str_content, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(T.all(RuboCop::AST::Node, RuboCop::AST::ParameterizedNode::RestArguments)) }
|
||||
def hash_node
|
||||
@hash_node ||= method_node.each_child_node(:hash).first
|
||||
@hash_node ||= T.let(method_node.each_child_node(:hash).first, T.nilable(RuboCop::AST::Node))
|
||||
end
|
||||
|
||||
sig { returns(T.all(RuboCop::AST::Node, RuboCop::AST::ParameterizedNode::RestArguments)) }
|
||||
def pair_node
|
||||
@pair_node ||= hash_node.each_child_node(:pair).first
|
||||
@pair_node ||= T.let(hash_node.each_child_node(:pair).first, T.nilable(RuboCop::AST::Node))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@ module RuboCop
|
||||
|
||||
sig {
|
||||
params(
|
||||
method_node: RuboCop::AST::Node,
|
||||
method_node: T.any(RuboCop::AST::AsgnNode, RuboCop::AST::BlockNode, RuboCop::AST::SendNode),
|
||||
all_comments: T::Array[T.any(String, Parser::Source::Comment)],
|
||||
).void
|
||||
}
|
||||
@@ -23,7 +23,7 @@ module RuboCop
|
||||
@all_comments = all_comments
|
||||
end
|
||||
|
||||
sig { returns(RuboCop::AST::Node) }
|
||||
sig { returns(T.any(RuboCop::AST::AsgnNode, RuboCop::AST::BlockNode, RuboCop::AST::SendNode)) }
|
||||
attr_reader :method_node
|
||||
alias stanza_node method_node
|
||||
|
||||
|
||||
@@ -19,3 +19,8 @@ class RuboCop::AST::Node
|
||||
sig { returns(T::Boolean) }
|
||||
def begin_block?; end
|
||||
end
|
||||
|
||||
class RuboCop::AST::BlockNode < RuboCop::AST::Node
|
||||
sig { returns(RuboCop::AST::SendNode) }
|
||||
def method_node; end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
@@ -17,9 +17,10 @@ module RuboCop
|
||||
MISSING_LINE_MSG = "stanza groups should be separated by a single empty line"
|
||||
EXTRA_LINE_MSG = "stanzas within the same group should have no lines between them"
|
||||
|
||||
sig { override.params(cask_block: RuboCop::Cask::AST::CaskBlock).void }
|
||||
def on_cask(cask_block)
|
||||
@cask_block = cask_block
|
||||
@line_ops = {}
|
||||
@cask_block = T.let(cask_block, T.nilable(RuboCop::Cask::AST::CaskBlock))
|
||||
@line_ops = T.let({}, T.nilable(T::Hash[Integer, Symbol]))
|
||||
cask_stanzas = cask_block.toplevel_stanzas
|
||||
add_offenses(cask_stanzas)
|
||||
|
||||
@@ -33,13 +34,15 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
attr_reader :cask_block, :line_ops
|
||||
sig { returns(T.nilable(RuboCop::Cask::AST::CaskBlock)) }
|
||||
attr_reader :cask_block
|
||||
|
||||
def_delegators :cask_block, :cask_node, :toplevel_stanzas
|
||||
|
||||
sig { params(stanzas: T::Array[RuboCop::Cask::AST::Stanza]).void }
|
||||
def add_offenses(stanzas)
|
||||
stanzas.each_cons(2) do |stanza, next_stanza|
|
||||
next unless next_stanza
|
||||
next if !stanza || !next_stanza
|
||||
|
||||
if missing_line_after?(stanza, next_stanza)
|
||||
add_offense_missing_line(stanza)
|
||||
@@ -49,28 +52,39 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Integer, Symbol]) }
|
||||
def line_ops
|
||||
@line_ops || raise("Call to line_ops before it has been initialized")
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza, next_stanza: RuboCop::Cask::AST::Stanza).returns(T::Boolean) }
|
||||
def missing_line_after?(stanza, next_stanza)
|
||||
!(stanza.same_group?(next_stanza) ||
|
||||
empty_line_after?(stanza))
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza, next_stanza: RuboCop::Cask::AST::Stanza).returns(T::Boolean) }
|
||||
def extra_line_after?(stanza, next_stanza)
|
||||
stanza.same_group?(next_stanza) &&
|
||||
empty_line_after?(stanza)
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza).returns(T::Boolean) }
|
||||
def empty_line_after?(stanza)
|
||||
source_line_after(stanza).empty?
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza).returns(String) }
|
||||
def source_line_after(stanza)
|
||||
processed_source[index_of_line_after(stanza)]
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza).returns(Integer) }
|
||||
def index_of_line_after(stanza)
|
||||
stanza.source_range.last_line
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza).void }
|
||||
def add_offense_missing_line(stanza)
|
||||
line_index = index_of_line_after(stanza)
|
||||
line_ops[line_index] = :insert
|
||||
@@ -79,6 +93,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(stanza: RuboCop::Cask::AST::Stanza).void }
|
||||
def add_offense_extra_line(stanza)
|
||||
line_index = index_of_line_after(stanza)
|
||||
line_ops[line_index] = :remove
|
||||
@@ -87,11 +102,14 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
def add_offense(line_index, message:)
|
||||
sig { params(line_index: Integer, message: String, block: T.proc.params(corrector: RuboCop::Cop::Corrector).void).void }
|
||||
def add_offense(line_index, message:, &block)
|
||||
line_length = [processed_source[line_index].size, 1].max
|
||||
@range = source_range(processed_source.buffer, line_index + 1, 0,
|
||||
line_length)
|
||||
super(@range, message:)
|
||||
@range = T.let(
|
||||
source_range(processed_source.buffer, line_index + 1, 0, line_length),
|
||||
T.nilable(Parser::Source::Range),
|
||||
)
|
||||
super(@range, message:, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
@@ -15,6 +15,7 @@ module RuboCop
|
||||
|
||||
MESSAGE = "`%<stanza>s` stanza out of order"
|
||||
|
||||
sig { override.params(stanza_block: RuboCop::Cask::AST::StanzaBlock).void }
|
||||
def on_cask_stanza_block(stanza_block)
|
||||
stanzas = stanza_block.stanzas
|
||||
ordered_stanzas = sort_stanzas(stanzas)
|
||||
@@ -29,6 +30,7 @@ module RuboCop
|
||||
message: format(MESSAGE, stanza: stanza_before.stanza_name),
|
||||
) do |corrector|
|
||||
next if part_of_ignored_node?(stanza_before.method_node)
|
||||
raise "unexpected nil value for stanza_after" unless stanza_after
|
||||
|
||||
corrector.replace(
|
||||
stanza_before.source_range_with_comments,
|
||||
@@ -41,6 +43,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def on_new_investigation
|
||||
super
|
||||
|
||||
@@ -49,6 +52,7 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
sig { params(stanzas: T::Array[RuboCop::Cask::AST::Stanza]).returns(T::Array[RuboCop::Cask::AST::Stanza]) }
|
||||
def sort_stanzas(stanzas)
|
||||
stanzas.sort do |stanza1, stanza2|
|
||||
i1 = stanza1.stanza_index
|
||||
@@ -58,15 +62,12 @@ module RuboCop
|
||||
i1 = stanzas.index(stanza1)
|
||||
i2 = stanzas.index(stanza2)
|
||||
end
|
||||
raise "unexpected nil value for i1" unless i1
|
||||
raise "unexpected nil value for i2" unless i2
|
||||
|
||||
i1 - i2
|
||||
end
|
||||
end
|
||||
|
||||
def stanza_order_index(stanza)
|
||||
stanza_name = stanza.respond_to?(:method_name) ? stanza.method_name : stanza.stanza_name
|
||||
RuboCop::Cask::Constants::STANZA_ORDER.index(stanza_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ module RuboCop
|
||||
url_stanza = stanza_node.first_argument
|
||||
hash_node = stanza_node.last_argument
|
||||
|
||||
audit_url(:cask, [stanza.stanza_node], [], livecheck_urls: [])
|
||||
audit_url(:cask, [stanza_node], [], livecheck_urls: [])
|
||||
|
||||
# Check for http:// URLs in homebrew-cask (skip deprecated/disabled casks)
|
||||
# TODO: Remove the deprecated/disabled check after Homebrew/cask has no more
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "ast_constants"
|
||||
@@ -14,13 +14,20 @@ module RuboCop
|
||||
class ComponentsOrder < FormulaCop
|
||||
extend AutoCorrector
|
||||
|
||||
sig { params(config: T.nilable(RuboCop::Config), options: T.nilable(T::Hash[Symbol, T.anything])).void }
|
||||
def initialize(config = nil, options = nil)
|
||||
super
|
||||
@present_components = T.let(nil, T.nilable(T::Array[T::Array[RuboCop::AST::Node]]))
|
||||
@offensive_nodes = T.let(nil, T.nilable(T::Array[RuboCop::AST::Node]))
|
||||
end
|
||||
|
||||
sig { override.params(formula_nodes: FormulaNodes).void }
|
||||
def audit_formula(formula_nodes)
|
||||
return if (body_node = formula_nodes.body_node).nil?
|
||||
|
||||
@present_components, @offensive_nodes = check_order(FORMULA_COMPONENT_PRECEDENCE_LIST, body_node)
|
||||
|
||||
component_problem @offensive_nodes[0], @offensive_nodes[1] if @offensive_nodes
|
||||
component_problem @offensive_nodes.fetch(0), @offensive_nodes.fetch(1) if @offensive_nodes
|
||||
|
||||
component_precedence_list = [
|
||||
[{ name: :depends_on, type: :method_call }],
|
||||
@@ -42,7 +49,7 @@ module RuboCop
|
||||
problem "There can only be one `#{on_method}` block in a formula."
|
||||
end
|
||||
|
||||
check_on_system_block_content(component_precedence_list, on_method_blocks.first)
|
||||
check_on_system_block_content(component_precedence_list, on_method_blocks.fetch(0))
|
||||
end
|
||||
|
||||
resource_blocks = find_blocks(body_node, :resource)
|
||||
@@ -119,11 +126,23 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
component_precedence_list: T::Array[T::Array[{ name: Symbol, type: Symbol }]],
|
||||
block: RuboCop::AST::BlockNode,
|
||||
).void
|
||||
}
|
||||
def check_block_component_order(component_precedence_list, block)
|
||||
@present_components, offensive_node = check_order(component_precedence_list, block.body)
|
||||
component_problem(*offensive_node) if offensive_node
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
component_precedence_list: T::Array[T::Array[{ name: Symbol, type: Symbol }]],
|
||||
on_system_block: RuboCop::AST::BlockNode,
|
||||
).void
|
||||
}
|
||||
def check_on_system_block_content(component_precedence_list, on_system_block)
|
||||
if on_system_block.body.block_type? && !on_system_methods.include?(on_system_block.body.method_name) &&
|
||||
on_system_block.body.method_name != :fails_with
|
||||
@@ -171,6 +190,7 @@ module RuboCop
|
||||
# Reorder two nodes in the source, using the corrector instance in autocorrect method.
|
||||
# Components of same type are grouped together when rewriting the source.
|
||||
# Linebreaks are introduced if components are of two different methods/blocks/multilines.
|
||||
sig { params(corrector: RuboCop::Cop::Corrector, node1: RuboCop::AST::Node, node2: RuboCop::AST::Node).void }
|
||||
def reorder_components(corrector, node1, node2)
|
||||
# order_idx : node1's index in component_precedence_list
|
||||
# curr_p_idx: node1's index in preceding_comp_arr
|
||||
@@ -179,7 +199,7 @@ module RuboCop
|
||||
|
||||
# curr_p_idx.positive? means node1 needs to be grouped with its own kind
|
||||
if curr_p_idx.positive?
|
||||
node2 = preceding_comp_arr[curr_p_idx - 1]
|
||||
node2 = preceding_comp_arr.fetch(curr_p_idx - 1)
|
||||
indentation = " " * (start_column(node2) - line_start_column(node2))
|
||||
line_breaks = node2.multiline? ? "\n\n" : "\n"
|
||||
corrector.insert_after(node2.source_range, line_breaks + indentation + node1.source)
|
||||
@@ -193,12 +213,20 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns precedence index and component's index to properly reorder and group during autocorrect.
|
||||
sig { params(node1: RuboCop::AST::Node).returns([Integer, Integer, T::Array[RuboCop::AST::Node]]) }
|
||||
def get_state(node1)
|
||||
@present_components.each_with_index do |comp, idx|
|
||||
return [idx, comp.index(node1), comp] if comp.member?(node1)
|
||||
T.must(@present_components).each_with_index do |comp, idx|
|
||||
return [idx, T.must(comp.index(node1)), comp] if comp.member?(node1)
|
||||
end
|
||||
raise "Could not find node1 in present_components"
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
component_precedence_list: T::Array[T::Array[{ name: Symbol, type: Symbol }]],
|
||||
body_node: RuboCop::AST::Node,
|
||||
).returns(T.nilable([T::Array[T::Array[RuboCop::AST::Node]], T::Array[RuboCop::AST::Node]]))
|
||||
}
|
||||
def check_order(component_precedence_list, body_node)
|
||||
present_components = component_precedence_list.map do |components|
|
||||
components.flat_map do |component|
|
||||
@@ -229,6 +257,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Method to report and correct component precedence violations.
|
||||
sig { params(component1: RuboCop::AST::Node, component2: RuboCop::AST::Node).void }
|
||||
def component_problem(component1, component2)
|
||||
return if tap_style_exception? :components_order_exceptions
|
||||
|
||||
|
||||
@@ -20,16 +20,16 @@ module RuboCop
|
||||
find_method_calls_by_name(body_node, :conflicts_with).each do |conflicts_with_call|
|
||||
next unless parameters(conflicts_with_call).last.respond_to? :values
|
||||
|
||||
reason = parameters(conflicts_with_call).last.values.first
|
||||
reason = T.cast(parameters(conflicts_with_call).fetch(-1), RuboCop::AST::HashNode).values.first
|
||||
offending_node(reason)
|
||||
name = Regexp.new(T.must(@formula_name), Regexp::IGNORECASE)
|
||||
reason_text = string_content(reason).sub(name, "")
|
||||
first_word = reason_text.split.first
|
||||
first_word = reason_text.split.fetch(0)
|
||||
|
||||
if reason_text.match?(/\A[A-Z]/)
|
||||
problem "'#{first_word}' from the `conflicts_with` reason " \
|
||||
"should be '#{first_word.downcase}'." do |corrector|
|
||||
reason_text[0] = reason_text[0].downcase
|
||||
reason_text[0] = T.must(reason_text[0]).downcase
|
||||
corrector.replace(reason.source_range, "\"#{reason_text}\"")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubocops/extend/formula_cop"
|
||||
@@ -28,6 +28,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(parent_node: T.nilable(RuboCop::AST::Node)).void }
|
||||
def check_uses_from_macos_nodes_order(parent_node)
|
||||
return if parent_node.nil?
|
||||
|
||||
@@ -35,6 +36,7 @@ module RuboCop
|
||||
ensure_dependency_order(dependency_nodes)
|
||||
end
|
||||
|
||||
sig { params(parent_node: T.nilable(RuboCop::AST::Node)).void }
|
||||
def check_dependency_nodes_order(parent_node)
|
||||
return if parent_node.nil?
|
||||
|
||||
@@ -42,9 +44,14 @@ module RuboCop
|
||||
ensure_dependency_order(dependency_nodes)
|
||||
end
|
||||
|
||||
sig { params(nodes: T::Array[RuboCop::AST::Node]).void }
|
||||
def ensure_dependency_order(nodes)
|
||||
name_node_pairs = nodes.map { |node| [dependency_name(node), node] }
|
||||
name_node_pairs.select! { |name, _| name } # skip nodes with invalid dependency name
|
||||
name_node_pairs = nodes.filter_map do |node|
|
||||
name = dependency_name(node)
|
||||
next unless name
|
||||
|
||||
[name, node]
|
||||
end
|
||||
name_node_pairs.sort_by! { |name, _| name.downcase }
|
||||
ordered = sort_dependencies_by_type(name_node_pairs.map { |_, node| node })
|
||||
sort_conditional_dependencies!(ordered)
|
||||
@@ -53,6 +60,7 @@ module RuboCop
|
||||
|
||||
# Separate dependencies according to precedence order:
|
||||
# build-time > test > normal > recommended > optional
|
||||
sig { params(dependency_nodes: T::Array[RuboCop::AST::Node]).returns(T::Array[RuboCop::AST::Node]) }
|
||||
def sort_dependencies_by_type(dependency_nodes)
|
||||
unsorted_deps = dependency_nodes.to_a
|
||||
ordered = []
|
||||
@@ -90,13 +98,14 @@ module RuboCop
|
||||
end
|
||||
break if idx2
|
||||
end
|
||||
insert_after!(ordered, idx1, idx2 + T.must(idx1)) if idx2
|
||||
insert_after!(ordered, idx1, idx2 + idx1) if idx1 &&idx2
|
||||
end
|
||||
ordered
|
||||
end
|
||||
|
||||
# Verify actual order of sorted `depends_on` nodes in source code;
|
||||
# raise RuboCop problem otherwise.
|
||||
sig { params(ordered: T::Array[RuboCop::AST::Node]).void }
|
||||
def verify_order_in_source(ordered)
|
||||
ordered.each_with_index do |node_1, idx|
|
||||
l1 = line_number(node_1)
|
||||
@@ -154,16 +163,22 @@ module RuboCop
|
||||
(send (send nil? :build) :with? $({str sym} _))
|
||||
EOS
|
||||
|
||||
sig { params(arr: T::Array[RuboCop::AST::Node], idx1: Integer, idx2: Integer).void }
|
||||
def insert_after!(arr, idx1, idx2)
|
||||
arr.insert(idx2+1, arr.delete_at(idx1))
|
||||
arr.insert(
|
||||
idx2+1,
|
||||
arr.delete_at(idx1) || raise("unexpected nil value for arr.delete_at(idx1)"),
|
||||
)
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::Node).returns(T.nilable(T::Array[String])) }
|
||||
def build_with_dependency_name(node)
|
||||
match_nodes = build_with_dependency_node(node)
|
||||
match_nodes = match_nodes.to_a.compact
|
||||
match_nodes.map { |n| string_content(n) } unless match_nodes.empty?
|
||||
end
|
||||
|
||||
sig { params(dependency_node: RuboCop::AST::Node).returns(T.nilable(String)) }
|
||||
def dependency_name(dependency_node)
|
||||
match_node = dependency_name_node(dependency_node).to_a.first
|
||||
string_content(match_node) if match_node
|
||||
|
||||
@@ -23,7 +23,7 @@ module RuboCop
|
||||
Date.iso8601(string_content(date_node))
|
||||
rescue ArgumentError
|
||||
fixed_date_string = Date.parse(string_content(date_node)).iso8601
|
||||
offending_node(date_node)
|
||||
@offensive_node = date_node
|
||||
problem "Use `#{fixed_date_string}` to comply with ISO 8601" do |corrector|
|
||||
corrector.replace(date_node.source_range, "\"#{fixed_date_string}\"")
|
||||
end
|
||||
@@ -56,18 +56,18 @@ module RuboCop
|
||||
reason_found = true
|
||||
next if reason_node.sym_type?
|
||||
|
||||
offending_node(reason_node)
|
||||
@offensive_node = reason_node
|
||||
reason_string = string_content(reason_node)
|
||||
|
||||
if reason_string.start_with?("it ")
|
||||
problem "Do not start the reason with `it`" do |corrector|
|
||||
corrector.replace(T.must(@offensive_node).source_range, "\"#{reason_string[3..]}\"")
|
||||
corrector.replace(@offensive_node.source_range, "\"#{reason_string[3..]}\"")
|
||||
end
|
||||
end
|
||||
|
||||
if PUNCTUATION_MARKS.include?(reason_string[-1])
|
||||
problem "Do not end the reason with a punctuation mark" do |corrector|
|
||||
corrector.replace(T.must(@offensive_node).source_range, "\"#{reason_string.chop}\"")
|
||||
corrector.replace(@offensive_node.source_range, "\"#{reason_string.chop}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,12 +51,12 @@ module RuboCop
|
||||
sig {
|
||||
params(
|
||||
urls: T::Array[RuboCop::AST::Node], regex: Regexp,
|
||||
_block: T.proc.params(arg0: T::Array[RuboCop::AST::Node], arg1: String, arg2: Integer).void
|
||||
_block: T.proc.params(arg0: MatchData, arg1: String, arg2: Integer).void
|
||||
).void
|
||||
}
|
||||
def audit_urls(urls, regex, &_block)
|
||||
urls.each_with_index do |url_node, index|
|
||||
url_string_node = parameters(url_node).first
|
||||
url_string_node = parameters(url_node).fetch(0)
|
||||
url_string = string_content(url_string_node)
|
||||
match_object = regex_match_group(url_string_node, regex)
|
||||
next unless match_object
|
||||
@@ -145,20 +145,21 @@ module RuboCop
|
||||
def get_checksum_node(call)
|
||||
return if parameters(call).empty? || parameters(call).nil?
|
||||
|
||||
if parameters(call).first.str_type?
|
||||
if parameters(call).fetch(0).str_type?
|
||||
parameters(call).first
|
||||
# sha256 is passed as a key-value pair in bottle blocks
|
||||
elsif parameters(call).first.hash_type?
|
||||
if parameters(call).first.keys.first.value == :cellar
|
||||
elsif parameters(call).fetch(0).hash_type?
|
||||
hash_node = T.cast(parameters(call).fetch(0), RuboCop::AST::HashNode)
|
||||
if hash_node.keys.first.value == :cellar
|
||||
# sha256 :cellar :any, :tag "hexdigest"
|
||||
parameters(call).first.values.last
|
||||
elsif parameters(call).first.keys.first.is_a?(RuboCop::AST::SymbolNode)
|
||||
hash_node.values.last
|
||||
elsif hash_node.keys.first.is_a?(RuboCop::AST::SymbolNode)
|
||||
# sha256 :tag "hexdigest"
|
||||
parameters(call).first.values.first
|
||||
hash_node.values.first
|
||||
else
|
||||
# Legacy bottle block syntax
|
||||
# sha256 "hexdigest" => :tag
|
||||
parameters(call).first.keys.first
|
||||
hash_node.keys.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,7 @@ module RuboCop
|
||||
return
|
||||
end
|
||||
|
||||
homepage_parameter_node = parameters(homepage_node).first
|
||||
homepage_parameter_node = parameters(homepage_node).fetch(0)
|
||||
offending_node(homepage_parameter_node)
|
||||
content = string_content(homepage_parameter_node)
|
||||
|
||||
|
||||
@@ -27,23 +27,23 @@ module RuboCop
|
||||
Firefox
|
||||
].freeze
|
||||
|
||||
reason = parameters(keg_only_node).first
|
||||
offending_node(reason)
|
||||
reason = parameters(keg_only_node).fetch(0)
|
||||
@offensive_node = reason
|
||||
name = Regexp.new(T.must(@formula_name), Regexp::IGNORECASE)
|
||||
reason = string_content(reason).sub(name, "")
|
||||
first_word = reason.split.first
|
||||
first_word = reason.split.fetch(0)
|
||||
|
||||
if /\A[A-Z]/.match?(reason) && !reason.start_with?(*allowlist)
|
||||
problem "'#{first_word}' from the `keg_only` reason should be '#{first_word.downcase}'." do |corrector|
|
||||
reason[0] = reason[0].downcase
|
||||
corrector.replace(T.must(@offensive_node).source_range, "\"#{reason}\"")
|
||||
reason[0] = T.must(reason[0]).downcase # reason[0] must exist because of the regexp match
|
||||
corrector.replace(@offensive_node.source_range, "\"#{reason}\"")
|
||||
end
|
||||
end
|
||||
|
||||
return unless reason.end_with?(".")
|
||||
|
||||
problem "`keg_only` reason should not end with a period." do |corrector|
|
||||
corrector.replace(T.must(@offensive_node).source_range, "\"#{reason.chop}\"")
|
||||
corrector.replace(@offensive_node.source_range, "\"#{reason.chop}\"")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,7 +51,9 @@ module RuboCop
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
reason = string_content(node)
|
||||
reason[0] = reason[0].downcase
|
||||
raise "unexpected empty reason" unless reason[0]
|
||||
|
||||
reason[0] = T.must(reason[0]).downcase # reason[0] must exist because of the previous line
|
||||
reason = reason.delete_suffix(".")
|
||||
corrector.replace(node.source_range, "\"#{reason}\"")
|
||||
end
|
||||
|
||||
@@ -39,8 +39,10 @@ module RuboCop
|
||||
end_pos = end_column(formula_nodes.class_node)
|
||||
return if begin_pos-end_pos == 3
|
||||
|
||||
raise "unexpected nil value for @formula_name" unless @formula_name
|
||||
|
||||
problem "Use a space in class inheritance: " \
|
||||
"class #{T.must(@formula_name).capitalize} < #{class_name(parent_class_node)}"
|
||||
"class #{@formula_name.capitalize} < #{class_name(parent_class_node)}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -112,25 +114,25 @@ module RuboCop
|
||||
|
||||
find_every_method_call_by_name(body_node, :assert_predicate).each do |method|
|
||||
args = parameters(method)
|
||||
next if args[1].source != ":exist?"
|
||||
next if args.fetch(1).source != ":exist?"
|
||||
|
||||
offending_node(method)
|
||||
@offensive_node = method
|
||||
problem "Use `assert_path_exists <path_to_file>` instead of `#{method.source}`" do |corrector|
|
||||
correct = "assert_path_exists #{args.first.source}"
|
||||
correct += ", #{args[2].source}" if args.length == 3
|
||||
corrector.replace(T.must(@offensive_node).source_range, correct)
|
||||
correct = "assert_path_exists #{args.fetch(0).source}"
|
||||
correct += ", #{args.fetch(2).source}" if args.length == 3
|
||||
corrector.replace(@offensive_node.source_range, correct)
|
||||
end
|
||||
end
|
||||
|
||||
find_every_method_call_by_name(body_node, :refute_predicate).each do |method|
|
||||
args = parameters(method)
|
||||
next if args[1].source != ":exist?"
|
||||
next if args.fetch(1).source != ":exist?"
|
||||
|
||||
offending_node(method)
|
||||
@offensive_node = method
|
||||
problem "Use `refute_path_exists <path_to_file>` instead of `#{method.source}`" do |corrector|
|
||||
correct = "refute_path_exists #{args.first.source}"
|
||||
correct += ", #{args[2].source}" if args.length == 3
|
||||
corrector.replace(T.must(@offensive_node).source_range, correct)
|
||||
correct = "refute_path_exists #{args.fetch(0).source}"
|
||||
correct += ", #{args.fetch(2).source}" if args.length == 3
|
||||
corrector.replace(@offensive_node.source_range, correct)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -188,7 +190,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
find_instance_method_call(body_node, :build, :without?) do |method|
|
||||
arg = parameters(method).first
|
||||
arg = parameters(method).fetch(0)
|
||||
next unless (match = regex_match_group(arg, /^-?-?without-(.*)/))
|
||||
|
||||
problem "Instead of duplicating `without`, " \
|
||||
@@ -196,7 +198,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
find_instance_method_call(body_node, :build, :with?) do |method|
|
||||
arg = parameters(method).first
|
||||
arg = parameters(method).fetch(0)
|
||||
next unless (match = regex_match_group(arg, /^-?-?with-(.*)/))
|
||||
|
||||
problem "Instead of duplicating `with`, " \
|
||||
@@ -262,7 +264,7 @@ module RuboCop
|
||||
find_method_with_args(body_node, :std_npm_install_args) do |method|
|
||||
problem "Use `std_npm_args` instead of `#{T.cast(@offensive_node,
|
||||
RuboCop::AST::SendNode).method_name}`." do |corrector|
|
||||
if (param = parameters(method).first.source) == "libexec"
|
||||
if (param = parameters(method).fetch(0).source) == "libexec"
|
||||
corrector.replace(T.must(@offensive_node).source_range, "std_npm_args")
|
||||
else
|
||||
corrector.replace(T.must(@offensive_node).source_range, "std_npm_args(prefix: #{param})")
|
||||
@@ -342,7 +344,7 @@ module RuboCop
|
||||
next if dependency.empty?
|
||||
next unless dependency.end_with?("-full")
|
||||
|
||||
offending_node(node)
|
||||
@offensive_node = node
|
||||
problem "Formulae in homebrew/core should not depend on `#{dependency}`."
|
||||
break
|
||||
end
|
||||
@@ -399,7 +401,7 @@ module RuboCop
|
||||
|
||||
popen_commands.each do |command|
|
||||
find_instance_method_call(body_node, "Utils", command) do |method|
|
||||
next unless (match = regex_match_group(parameters(method).first, /^([^"' ]+)=([^"' ]+)(?: (.*))?$/))
|
||||
next unless (match = regex_match_group(parameters(method).fetch(0), /^([^"' ]+)=([^"' ]+)(?: (.*))?$/))
|
||||
|
||||
good_args = "Utils.#{command}({ \"#{match[1]}\" => \"#{match[2]}\" }, \"#{match[3]}\")"
|
||||
|
||||
@@ -422,11 +424,12 @@ module RuboCop
|
||||
license_node = find_node_method_by_name(body_node, :license)
|
||||
return unless license_node
|
||||
|
||||
license = parameters(license_node).first
|
||||
license = parameters(license_node).fetch(0)
|
||||
return unless license.array_type?
|
||||
|
||||
problem "Use `license any_of: #{license.source}` instead of `license #{license.source}`" do |corrector|
|
||||
corrector.replace(license_node.source_range, "license any_of: #{parameters(license_node).first.source}")
|
||||
corrector.replace(license_node.source_range,
|
||||
"license any_of: #{parameters(license_node).fetch(0).source}")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -441,7 +444,7 @@ module RuboCop
|
||||
return unless license_node
|
||||
return if license_node.source.include?("\n")
|
||||
|
||||
parameters(license_node).first.each_descendant(:hash).each do |license_hash|
|
||||
parameters(license_node).fetch(0).each_descendant(:hash).each do |license_hash|
|
||||
next if license_exception? license_hash
|
||||
|
||||
problem "Split nested license declarations onto multiple lines"
|
||||
@@ -462,19 +465,20 @@ module RuboCop
|
||||
return if (body_node = formula_nodes.body_node).nil?
|
||||
|
||||
python_formula_node = find_every_method_call_by_name(body_node, :depends_on).find do |dep|
|
||||
string_content(parameters(dep).first).start_with? "python@"
|
||||
string_content(parameters(dep).fetch(0)).start_with? "python@"
|
||||
end
|
||||
|
||||
python_version = if python_formula_node.blank?
|
||||
other_python_nodes = find_every_method_call_by_name(body_node, :depends_on).select do |dep|
|
||||
parameters(dep).first.instance_of?(RuboCop::AST::HashNode) &&
|
||||
string_content(parameters(dep).first.keys.first).start_with?("python@")
|
||||
first_param = parameters(dep).first
|
||||
first_param.instance_of?(RuboCop::AST::HashNode) &&
|
||||
string_content(first_param.keys.first).start_with?("python@")
|
||||
end
|
||||
return if other_python_nodes.size != 1
|
||||
|
||||
string_content(parameters(other_python_nodes.first).first.keys.first).split("@").last
|
||||
string_content(T.cast(parameters(other_python_nodes.fetch(0)).fetch(0), RuboCop::AST::HashNode).keys.first).split("@").last
|
||||
else
|
||||
string_content(parameters(python_formula_node).first).split("@").last
|
||||
string_content(parameters(python_formula_node).fetch(0)).split("@").last
|
||||
end
|
||||
|
||||
find_strings(body_node).each do |str|
|
||||
@@ -596,11 +600,11 @@ module RuboCop
|
||||
replacement_args << "shell_parameter_format: #{shell_parameter_format.inspect}"
|
||||
end
|
||||
|
||||
offending_node(node)
|
||||
@offensive_node = node
|
||||
replacement = "generate_completions_from_executable(#{replacement_args.join(", ")})"
|
||||
|
||||
problem "Use `#{replacement}` instead of `#{T.must(@offensive_node).source}`." do |corrector|
|
||||
corrector.replace(T.must(@offensive_node).source_range, replacement)
|
||||
problem "Use `#{replacement}` instead of `#{@offensive_node.source}`." do |corrector|
|
||||
corrector.replace(@offensive_node.source_range, replacement)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -674,30 +678,30 @@ module RuboCop
|
||||
next
|
||||
end
|
||||
|
||||
offending_node(node)
|
||||
@offensive_node = node
|
||||
problem "Use a single `generate_completions_from_executable` " \
|
||||
"call combining all specified shells." do |corrector|
|
||||
# adjust range by -4 and +1 to also include & remove leading spaces and trailing \n
|
||||
corrector.replace(T.must(@offensive_node).source_range.adjust(begin_pos: -4, end_pos: 1), "")
|
||||
corrector.replace(@offensive_node.source_range.adjust(begin_pos: -4, end_pos: 1), "")
|
||||
end
|
||||
end
|
||||
|
||||
return if shells.length <= 1 # no shells to combine left
|
||||
|
||||
offending_node(offenses.last)
|
||||
@offensive_node = offenses.fetch(-1)
|
||||
replacement = if (%w[:bash :zsh :fish] - shells).empty?
|
||||
T.must(@offensive_node).source
|
||||
.sub(/shells: \[(:bash|:zsh|:fish)\]/, "")
|
||||
.sub(", )", ")") # clean up dangling trailing comma
|
||||
.sub("(, ", "(") # clean up dangling leading comma
|
||||
.sub(", , ", ", ") # clean up dangling enclosed comma
|
||||
@offensive_node.source
|
||||
.sub(/shells: \[(:bash|:zsh|:fish)\]/, "")
|
||||
.sub(", )", ")") # clean up dangling trailing comma
|
||||
.sub("(, ", "(") # clean up dangling leading comma
|
||||
.sub(", , ", ", ") # clean up dangling enclosed comma
|
||||
else
|
||||
T.must(@offensive_node).source.sub(/shells: \[(:bash|:zsh|:fish)\]/,
|
||||
"shells: [#{shells.join(", ")}]")
|
||||
@offensive_node.source.sub(/shells: \[(:bash|:zsh|:fish)\]/,
|
||||
"shells: [#{shells.join(", ")}]")
|
||||
end
|
||||
|
||||
problem "Use `#{replacement}` instead of `#{T.must(@offensive_node).source}`." do |corrector|
|
||||
corrector.replace(T.must(@offensive_node).source_range, replacement)
|
||||
problem "Use `#{replacement}` instead of `#{@offensive_node.source}`." do |corrector|
|
||||
corrector.replace(@offensive_node.source_range, replacement)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -733,14 +737,14 @@ module RuboCop
|
||||
end
|
||||
|
||||
find_instance_method_call(body_node, :man, :+) do |method|
|
||||
next unless (match = regex_match_group(parameters(method).first, /^man[1-8]$/))
|
||||
next unless (match = regex_match_group(parameters(method).fetch(0), /^man[1-8]$/))
|
||||
|
||||
problem "`#{method.source}` should be `#{match[0]}`"
|
||||
end
|
||||
|
||||
# Avoid hard-coding compilers
|
||||
find_every_method_call_by_name(body_node, :system).each do |method|
|
||||
param = parameters(method).first
|
||||
param = parameters(method).fetch(0)
|
||||
if (match = regex_match_group(param, %r{^(/usr/bin/)?(gcc|clang|cc|c[89]9)(\s|$)}))
|
||||
problem "Use `\#{ENV.cc}` instead of hard-coding `#{match[2]}`"
|
||||
elsif (match = regex_match_group(param, %r{^(/usr/bin/)?((g|clang|c)\+\+)(\s|$)}))
|
||||
@@ -749,7 +753,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
find_instance_method_call(body_node, "ENV", :[]=) do |method|
|
||||
param = parameters(method)[1]
|
||||
param = parameters(method).fetch(1)
|
||||
if (match = regex_match_group(param, %r{^(/usr/bin/)?(gcc|clang|cc|c[89]9)(\s|$)}))
|
||||
problem "Use `\#{ENV.cc}` instead of hard-coding `#{match[2]}`"
|
||||
elsif (match = regex_match_group(param, %r{^(/usr/bin/)?((g|clang|c)\+\+)(\s|$)}))
|
||||
@@ -772,12 +776,13 @@ module RuboCop
|
||||
problem ["`#", "{prefix}", match[1], '` should be `#{', match[3], "}`"].join
|
||||
end
|
||||
if (match = regex_match_group(p, %r{^(/(bin|include|libexec|lib|sbin|share|Frameworks))}i))
|
||||
problem ["`#", "{prefix}", match[1], '` should be `#{', match[2].downcase, "}`"].join
|
||||
# match[2] must exist because of the previous line
|
||||
problem ["`#", "{prefix}", match[1], '` should be `#{', T.must(match[2]).downcase, "}`"].join
|
||||
end
|
||||
end
|
||||
|
||||
find_every_method_call_by_name(body_node, :depends_on).each do |method|
|
||||
key, value = destructure_hash(parameters(method).first)
|
||||
key, value = destructure_hash(parameters(method).fetch(0))
|
||||
next if key.nil? || value.nil?
|
||||
next unless (match = regex_match_group(value, /^(lua|perl|python|ruby)(\d*)/))
|
||||
|
||||
@@ -785,13 +790,13 @@ module RuboCop
|
||||
end
|
||||
|
||||
find_every_method_call_by_name(body_node, :system).each do |method|
|
||||
next unless (match = regex_match_group(parameters(method).first, /^(env|export)(\s+)?/))
|
||||
next unless (match = regex_match_group(parameters(method).fetch(0), /^(env|export)(\s+)?/))
|
||||
|
||||
problem "Use `ENV` instead of invoking `#{match[1]}` to modify the environment"
|
||||
end
|
||||
|
||||
find_every_method_call_by_name(body_node, :depends_on).each do |method|
|
||||
param = parameters(method).first
|
||||
param = parameters(method).fetch(0)
|
||||
dep, option_child_nodes = hash_dep(param)
|
||||
next if dep.nil? || option_child_nodes.empty?
|
||||
|
||||
@@ -861,6 +866,8 @@ module RuboCop
|
||||
end
|
||||
|
||||
if find_method_def(processed_source.ast)
|
||||
raise "unexpected nil value for @offensive_node" unless @offensive_node
|
||||
|
||||
problem "Define method `#{method_name(@offensive_node)}` in the class body, not at the top-level"
|
||||
end
|
||||
|
||||
@@ -889,7 +896,7 @@ module RuboCop
|
||||
find_instance_method_call(body_node, "Dir", :[]) do |method|
|
||||
next if parameters(method).size != 1
|
||||
|
||||
path = parameters(method).first
|
||||
path = parameters(method).fetch(0)
|
||||
next unless path.str_type?
|
||||
next unless (match = regex_match_group(path, /^[^*{},]+$/))
|
||||
|
||||
@@ -902,7 +909,7 @@ module RuboCop
|
||||
.join("|"),
|
||||
)
|
||||
find_every_method_call_by_name(body_node, :system).each do |method|
|
||||
param = parameters(method).first
|
||||
param = parameters(method).fetch(0)
|
||||
next unless (match = regex_match_group(param, fileutils_methods))
|
||||
|
||||
problem "Use the `#{match}` Ruby method instead of `#{method.source}`"
|
||||
@@ -954,10 +961,10 @@ module RuboCop
|
||||
params = parameters(method)
|
||||
next unless node_equals?(params[0], "make")
|
||||
|
||||
params[1..].each do |arg|
|
||||
params[1..]&.each do |arg|
|
||||
next unless regex_match_group(arg, /^(checks?|tests?)$/)
|
||||
|
||||
offending_node(method)
|
||||
@offensive_node = method
|
||||
problem "Formulae in homebrew/core (except e.g. cryptography, libraries) " \
|
||||
"should not run build-time checks"
|
||||
end
|
||||
|
||||
@@ -16,15 +16,16 @@ module RuboCop
|
||||
livecheck_node = find_block(formula_nodes.body_node, :livecheck)
|
||||
return if livecheck_node.blank?
|
||||
|
||||
skip = find_every_method_call_by_name(livecheck_node, :skip).first
|
||||
skip = T.let(find_every_method_call_by_name(livecheck_node, :skip).first,
|
||||
T.nilable(T.any(RuboCop::AST::Node, String)))
|
||||
return if skip.blank?
|
||||
|
||||
return if find_every_method_call_by_name(livecheck_node).length < 3
|
||||
|
||||
offending_node(livecheck_node)
|
||||
problem "Skipped formulae must not contain other livecheck information." do |corrector|
|
||||
skip = find_every_method_call_by_name(livecheck_node, :skip).first
|
||||
skip = find_strings(skip).first
|
||||
skip = find_every_method_call_by_name(livecheck_node, :skip).fetch(0)
|
||||
skip = find_strings(skip).fetch(0)
|
||||
skip = string_content(skip) if skip.present?
|
||||
corrector.replace(
|
||||
livecheck_node.source_range,
|
||||
@@ -74,6 +75,8 @@ module RuboCop
|
||||
return if skip.present?
|
||||
|
||||
livecheck_url_node = find_every_method_call_by_name(livecheck_node, :url).first
|
||||
return if livecheck_url_node.blank?
|
||||
|
||||
livecheck_url = find_strings(livecheck_url_node).first
|
||||
return if livecheck_url.blank?
|
||||
|
||||
@@ -101,7 +104,7 @@ module RuboCop
|
||||
stable_url = string_content(stable_url) if stable_url.present?
|
||||
|
||||
homepage = find_every_method_call_by_name(body_node, :homepage).first
|
||||
homepage_url = string_content(find_strings(homepage).first) if homepage.present?
|
||||
homepage_url = string_content(find_strings(homepage).fetch(0)) if homepage.present?
|
||||
|
||||
formula_urls = { head: head_url, stable: stable_url, homepage: homepage_url }.compact
|
||||
|
||||
@@ -161,13 +164,13 @@ module RuboCop
|
||||
return if livecheck_regex_node.blank?
|
||||
|
||||
regex_node = livecheck_regex_node.descendants.first
|
||||
pattern = string_content(find_strings(regex_node).first)
|
||||
pattern = string_content(find_strings(regex_node).fetch(0))
|
||||
match = pattern.match(TAR_PATTERN)
|
||||
return if match.blank?
|
||||
|
||||
offending_node(regex_node)
|
||||
problem "Use `\\.t` instead of `#{match}`" do |corrector|
|
||||
node = find_strings(regex_node).first
|
||||
node = find_strings(regex_node).fetch(0)
|
||||
correct = node.source.gsub(TAR_PATTERN, "\\.t")
|
||||
corrector.replace(node.source_range, correct)
|
||||
end
|
||||
|
||||
@@ -17,7 +17,7 @@ module RuboCop
|
||||
|
||||
option_call_nodes = find_every_method_call_by_name(body_node, :option)
|
||||
option_call_nodes.each do |option_call|
|
||||
option = parameters(option_call).first
|
||||
option = parameters(option_call).fetch(0)
|
||||
offending_node(option_call)
|
||||
option = string_content(option)
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ module RuboCop
|
||||
|
||||
external_patches = find_all_blocks(body_node, :patch)
|
||||
external_patches.each do |patch_block|
|
||||
url_node = find_every_method_call_by_name(patch_block, :url).first
|
||||
url_string = parameters(url_node).first
|
||||
url_node = find_every_method_call_by_name(patch_block, :url).fetch(0)
|
||||
url_string = parameters(url_node).fetch(0)
|
||||
sha256_node = find_every_method_call_by_name(patch_block, :sha256).first
|
||||
sha256_string = parameters(sha256_node).first if sha256_node
|
||||
patch_problems(url_string, sha256_string)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
@@ -97,26 +97,38 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
params(node: RuboCop::AST::IfNode, receiver: RuboCop::AST::Node, other: T.nilable(RuboCop::AST::Node)).void
|
||||
}
|
||||
def register_offense(node, receiver, other)
|
||||
add_offense(node, message: message(node, receiver, other)) do |corrector|
|
||||
corrector.replace(node, replacement(receiver, other, node.left_sibling))
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::IfNode).returns(T::Boolean) }
|
||||
def ignore_if_node?(node)
|
||||
node.elsif?
|
||||
end
|
||||
|
||||
sig { params(node: T.nilable(RuboCop::AST::Node)).returns(T::Boolean) }
|
||||
def ignore_other_node?(node)
|
||||
node && (node.if_type? || node.rescue_type? || node.while_type?)
|
||||
return false unless node
|
||||
|
||||
node.if_type? || node.rescue_type? || node.while_type?
|
||||
end
|
||||
|
||||
sig {
|
||||
params(node: RuboCop::AST::IfNode, receiver: RuboCop::AST::Node, other: T.nilable(RuboCop::AST::Node))
|
||||
.returns(String)
|
||||
}
|
||||
def message(node, receiver, other)
|
||||
prefer = replacement(receiver, other, node.left_sibling).gsub(/^\s*|\n/, "")
|
||||
current = current(node).gsub(/^\s*|\n/, "")
|
||||
format(MSG, prefer:, current:)
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::IfNode).returns(String) }
|
||||
def current(node)
|
||||
if !node.ternary? && node.source.include?("\n")
|
||||
"#{node.loc.keyword.with(end_pos: node.condition.loc.selector.end_pos).source} ... end"
|
||||
@@ -125,8 +137,15 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
receiver: RuboCop::AST::Node,
|
||||
other: T.nilable(RuboCop::AST::Node),
|
||||
left_sibling: T.nilable(T.any(RuboCop::AST::Node, Symbol)),
|
||||
).returns(String)
|
||||
}
|
||||
def replacement(receiver, other, left_sibling)
|
||||
or_source = if other&.send_type?
|
||||
or_source = if other.is_a?(RuboCop::AST::SendNode)
|
||||
build_source_for_or_method(other)
|
||||
elsif other.nil? || other.nil_type?
|
||||
""
|
||||
@@ -138,6 +157,7 @@ module RuboCop
|
||||
left_sibling ? "(#{replaced})" : replaced
|
||||
end
|
||||
|
||||
sig { params(other: RuboCop::AST::SendNode).returns(String) }
|
||||
def build_source_for_or_method(other)
|
||||
if other.parenthesized? || other.method?("[]") || other.arithmetic_operation? || !other.arguments?
|
||||
" || #{other.source}"
|
||||
@@ -149,6 +169,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).returns(Parser::Source::Range) }
|
||||
def method_range(node)
|
||||
range_between(node.source_range.begin_pos, node.first_argument.source_range.begin_pos - 1)
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ module RuboCop
|
||||
end
|
||||
next if required_deps.all? { |dep| uses_from_macos_or_depends_on.include?(dep) }
|
||||
|
||||
offending_node(found)
|
||||
@offensive_node = found
|
||||
problem "Add `#{kind}` lines above for #{required_deps.map { |req| "`\"#{req}\"`" }.join(" and ")}."
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubocop"
|
||||
@@ -16,6 +16,7 @@ module RuboCop
|
||||
|
||||
# Checks for regex match of pattern in the node and
|
||||
# sets the appropriate instance variables to report the match.
|
||||
sig { params(node: RuboCop::AST::Node, pattern: T.any(Regexp, String)).returns(T.nilable(MatchData)) }
|
||||
def regex_match_group(node, pattern)
|
||||
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
|
||||
match_object = string_repr.match(pattern)
|
||||
@@ -23,15 +24,18 @@ module RuboCop
|
||||
|
||||
node_begin_pos = start_column(node)
|
||||
line_begin_pos = line_start_column(node)
|
||||
@column = if node_begin_pos == line_begin_pos
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos
|
||||
else
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
||||
end
|
||||
@length = match_object.to_s.length
|
||||
@line_no = line_number(node)
|
||||
@source_buf = source_buffer(node)
|
||||
@offensive_node = node
|
||||
@column = T.let(
|
||||
if node_begin_pos == line_begin_pos
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos
|
||||
else
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
||||
end,
|
||||
T.nilable(Integer),
|
||||
)
|
||||
@length = T.let(match_object.to_s.length, T.nilable(Integer))
|
||||
@line_no = T.let(line_number(node), T.nilable(Integer))
|
||||
@source_buf = T.let(source_buffer(node), T.nilable(Parser::Source::Buffer))
|
||||
@offensive_node = T.let(node, T.nilable(RuboCop::AST::Node))
|
||||
@offensive_source_range = T.let(
|
||||
source_range(@source_buf, @line_no, @column, @length),
|
||||
T.nilable(Parser::Source::Range),
|
||||
@@ -40,11 +44,13 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns the begin position of the node's line in source code.
|
||||
sig { params(node: RuboCop::AST::Node).returns(Integer) }
|
||||
def line_start_column(node)
|
||||
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
|
||||
end
|
||||
|
||||
# Returns the begin position of the node in source code.
|
||||
sig { params(node: RuboCop::AST::Node).returns(Integer) }
|
||||
def start_column(node)
|
||||
node.source_range.begin_pos
|
||||
end
|
||||
@@ -62,6 +68,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const.
|
||||
sig { params(node: RuboCop::AST::Node, strip_dynamic: T::Boolean).returns(String) }
|
||||
def string_content(node, strip_dynamic: false)
|
||||
case node.type
|
||||
when :str
|
||||
@@ -77,9 +84,10 @@ module RuboCop
|
||||
end
|
||||
content
|
||||
when :send
|
||||
if node.method?(:+) && (node.receiver.str_type? || node.receiver.dstr_type?)
|
||||
send_node = T.cast(node, RuboCop::AST::SendNode)
|
||||
if send_node.method?(:+) && (send_node.receiver.str_type? || send_node.receiver.dstr_type?)
|
||||
content = string_content(node.receiver)
|
||||
arg = node.arguments.first
|
||||
arg = send_node.arguments.first
|
||||
content += string_content(arg) if arg
|
||||
content
|
||||
else
|
||||
@@ -94,19 +102,22 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(msg: String, block: T.nilable(T.proc.params(corrector: RuboCop::Cop::Corrector).void)).void }
|
||||
def problem(msg, &block)
|
||||
add_offense(@offensive_node, message: msg, &block)
|
||||
end
|
||||
|
||||
# Returns all string nodes among the descendants of given node.
|
||||
sig { params(node: T.nilable(RuboCop::AST::Node)).returns(T::Array[RuboCop::AST::Node]) }
|
||||
def find_strings(node)
|
||||
return [] if node.nil?
|
||||
return [node] if node.str_type?
|
||||
|
||||
node.each_descendant(:str)
|
||||
node.each_descendant(:str).to_a
|
||||
end
|
||||
|
||||
# Returns method_node matching method_name.
|
||||
sig { params(node: RuboCop::AST::Node, method_name: Symbol).returns(T.nilable(RuboCop::AST::Node)) }
|
||||
def find_node_method_by_name(node, method_name)
|
||||
return if node.nil?
|
||||
|
||||
@@ -122,6 +133,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Gets/sets the given node as the offending node when required in custom cops.
|
||||
sig { params(node: T.nilable(RuboCop::AST::Node)).returns(T.nilable(RuboCop::AST::Node)) }
|
||||
def offending_node(node = nil)
|
||||
return @offensive_node if node.nil?
|
||||
|
||||
@@ -129,21 +141,28 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns an array of method call nodes matching method_name inside node with depth first order (child nodes).
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), method_name: Symbol).returns(T::Array[RuboCop::AST::SendNode])
|
||||
}
|
||||
def find_method_calls_by_name(node, method_name)
|
||||
return if node.nil?
|
||||
return [] if node.nil?
|
||||
|
||||
nodes = node.each_child_node(:send).select { |method_node| method_name == method_node.method_name }
|
||||
|
||||
# The top level node can be a method
|
||||
nodes << node if node.send_type? && node.method_name == method_name
|
||||
nodes << node if node.is_a?(RuboCop::AST::SendNode) && node.method_name == method_name
|
||||
|
||||
nodes
|
||||
end
|
||||
|
||||
# Returns an array of method call nodes matching method_name in every descendant of node.
|
||||
# Returns every method call if no method_name is passed.
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), method_name: T.nilable(Symbol))
|
||||
.returns(T::Array[RuboCop::AST::SendNode])
|
||||
}
|
||||
def find_every_method_call_by_name(node, method_name = nil)
|
||||
return if node.nil?
|
||||
return [] if node.nil?
|
||||
|
||||
node.each_descendant(:send).select do |method_node|
|
||||
method_name.nil? ||
|
||||
@@ -156,8 +175,12 @@ module RuboCop
|
||||
# - matches function call: `foo(*args, **kwargs)`
|
||||
# - does not match method calls: `foo.bar(*args, **kwargs)`
|
||||
# - returns every function call if no func_name is passed
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), func_name: T.nilable(Symbol))
|
||||
.returns(T::Array[T.any(RuboCop::AST::BlockNode, RuboCop::AST::SendNode)])
|
||||
}
|
||||
def find_every_func_call_by_name(node, func_name = nil)
|
||||
return if node.nil?
|
||||
return [] if node.nil?
|
||||
|
||||
node.each_descendant(:send).select do |func_node|
|
||||
func_node.receiver.nil? && (func_name.nil? || func_name == func_node.method_name)
|
||||
@@ -166,11 +189,19 @@ module RuboCop
|
||||
|
||||
# Given a method_name and arguments, yields to a block with
|
||||
# matching method passed as a parameter to the block.
|
||||
def find_method_with_args(node, method_name, *args)
|
||||
sig {
|
||||
params(
|
||||
node: T.nilable(RuboCop::AST::Node),
|
||||
method_name: Symbol,
|
||||
args: Object,
|
||||
_block: T.nilable(T.proc.params(method: RuboCop::AST::Node).void),
|
||||
).returns(T::Array[RuboCop::AST::SendNode])
|
||||
}
|
||||
def find_method_with_args(node, method_name, *args, &_block)
|
||||
methods = find_every_method_call_by_name(node, method_name)
|
||||
methods.each do |method|
|
||||
next unless parameters_passed?(method, args)
|
||||
return true unless block_given?
|
||||
return [] unless block_given?
|
||||
|
||||
yield method
|
||||
end
|
||||
@@ -191,7 +222,15 @@ module RuboCop
|
||||
# ```ruby
|
||||
# find_instance_method_call(node, :build, :head?)
|
||||
# ```
|
||||
def find_instance_method_call(node, instance, method_name)
|
||||
sig {
|
||||
params(
|
||||
node: T.nilable(RuboCop::AST::Node),
|
||||
instance: T.any(String, Symbol),
|
||||
method_name: T.nilable(Symbol),
|
||||
_block: T.nilable(T.proc.params(method: RuboCop::AST::SendNode).void),
|
||||
).returns(T.anything)
|
||||
}
|
||||
def find_instance_method_call(node, instance, method_name, &_block)
|
||||
methods = find_every_method_call_by_name(node, method_name)
|
||||
methods.each do |method|
|
||||
next if method.receiver.nil?
|
||||
@@ -214,7 +253,14 @@ module RuboCop
|
||||
# ```ruby
|
||||
# find_instance_call(node, "ARGV")
|
||||
# ```
|
||||
def find_instance_call(node, name)
|
||||
sig {
|
||||
params(
|
||||
node: RuboCop::AST::Node,
|
||||
name: String,
|
||||
_block: T.nilable(T.proc.params(method: RuboCop::AST::Node).void),
|
||||
).returns(T.anything)
|
||||
}
|
||||
def find_instance_call(node, name, &_block)
|
||||
node.each_descendant(:send) do |method_node|
|
||||
next if method_node.receiver.nil?
|
||||
next if method_node.receiver.const_name != name &&
|
||||
@@ -229,7 +275,14 @@ module RuboCop
|
||||
|
||||
# Find CONSTANTs in the source.
|
||||
# If block given, yield matching nodes.
|
||||
def find_const(node, const_name)
|
||||
sig {
|
||||
params(
|
||||
node: T.nilable(RuboCop::AST::Node),
|
||||
const_name: String,
|
||||
_block: T.nilable(T.proc.params(const: RuboCop::AST::Node).void),
|
||||
).returns(T.anything)
|
||||
}
|
||||
def find_const(node, const_name, &_block)
|
||||
return if node.nil?
|
||||
|
||||
node.each_descendant(:const) do |const_node|
|
||||
@@ -243,11 +296,15 @@ module RuboCop
|
||||
end
|
||||
|
||||
# To compare node with appropriate Ruby variable.
|
||||
sig { params(node: T.nilable(RuboCop::AST::Node), var: Object).returns(T::Boolean) }
|
||||
def node_equals?(node, var)
|
||||
node == Parser::CurrentRuby.parse(var.inspect)
|
||||
end
|
||||
|
||||
# Returns a block named block_name inside node.
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), block_name: Symbol).returns(T.nilable(RuboCop::AST::BlockNode))
|
||||
}
|
||||
def find_block(node, block_name)
|
||||
return if node.nil?
|
||||
|
||||
@@ -263,16 +320,26 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns an array of block nodes named block_name inside node.
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), block_name: Symbol).returns(T::Array[RuboCop::AST::BlockNode])
|
||||
}
|
||||
def find_blocks(node, block_name)
|
||||
return if node.nil?
|
||||
return [] if node.nil?
|
||||
|
||||
node.each_child_node(:block).select { |block_node| block_name == block_node.method_name }
|
||||
end
|
||||
|
||||
# Returns an array of block nodes of any depth below node in AST.
|
||||
# If a block is given then yields matching block node to the block!
|
||||
def find_all_blocks(node, block_name)
|
||||
return if node.nil?
|
||||
sig {
|
||||
params(
|
||||
node: T.nilable(RuboCop::AST::Node),
|
||||
block_name: Symbol,
|
||||
_block: T.nilable(T.proc.params(block: RuboCop::AST::BlockNode).void),
|
||||
).returns(T::Array[RuboCop::AST::BlockNode])
|
||||
}
|
||||
def find_all_blocks(node, block_name, &_block)
|
||||
return [] if node.nil?
|
||||
|
||||
blocks = node.each_descendant(:block).select { |block_node| block_name == block_node.method_name }
|
||||
return blocks unless block_given?
|
||||
@@ -285,6 +352,10 @@ module RuboCop
|
||||
|
||||
# Returns a method definition node with method_name.
|
||||
# Returns first method def if method_name is nil.
|
||||
sig {
|
||||
params(node: T.nilable(RuboCop::AST::Node), method_name: T.nilable(Symbol))
|
||||
.returns(T.nilable(RuboCop::AST::Node))
|
||||
}
|
||||
def find_method_def(node, method_name = nil)
|
||||
return if node.nil?
|
||||
|
||||
@@ -303,6 +374,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Check if a block method is called inside a block.
|
||||
sig { params(node: RuboCop::AST::BlockNode, method_name: Symbol).returns(T::Boolean) }
|
||||
def block_method_called_in_block?(node, method_name)
|
||||
node.body.each_child_node do |call_node|
|
||||
next if !call_node.block_type? && !call_node.send_type?
|
||||
@@ -316,8 +388,9 @@ module RuboCop
|
||||
|
||||
# Check if method_name is called among the direct children nodes in the given node.
|
||||
# Check if the node itself is the method.
|
||||
sig { params(node: RuboCop::AST::Node, method_name: Symbol).returns(T::Boolean) }
|
||||
def method_called?(node, method_name)
|
||||
if node.send_type? && node.method_name == method_name
|
||||
if node.is_a?(RuboCop::AST::SendNode) && node.method_name == method_name
|
||||
offending_node(node)
|
||||
return true
|
||||
end
|
||||
@@ -331,6 +404,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Check if method_name is called among every descendant node of given node.
|
||||
sig { params(node: RuboCop::AST::Node, method_name: Symbol).returns(T::Boolean) }
|
||||
def method_called_ever?(node, method_name)
|
||||
node.each_descendant(:send) do |call_node|
|
||||
next if call_node.method_name != method_name
|
||||
@@ -342,6 +416,10 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Checks for precedence; returns the first pair of precedence-violating nodes.
|
||||
sig {
|
||||
params(first_nodes: T::Array[RuboCop::AST::Node], next_nodes: T::Array[RuboCop::AST::Node])
|
||||
.returns(T.nilable([RuboCop::AST::Node, RuboCop::AST::Node]))
|
||||
}
|
||||
def check_precedence(first_nodes, next_nodes)
|
||||
next_nodes.each do |each_next_node|
|
||||
first_nodes.each do |each_first_node|
|
||||
@@ -352,6 +430,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# If first node does not precede next_node, sets appropriate instance variables for reporting.
|
||||
sig { params(first_node: RuboCop::AST::Node, next_node: RuboCop::AST::Node).returns(T::Boolean) }
|
||||
def component_precedes?(first_node, next_node)
|
||||
return false if line_number(first_node) < line_number(next_node)
|
||||
|
||||
@@ -360,21 +439,28 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Check if negation is present in the given node.
|
||||
sig { params(node: RuboCop::AST::Node).returns(T::Boolean) }
|
||||
def expression_negated?(node)
|
||||
return false unless node.parent&.send_type?
|
||||
return false unless node.parent.method_name.equal?(:!)
|
||||
|
||||
offending_node(node.parent)
|
||||
!!offending_node(node.parent)
|
||||
end
|
||||
|
||||
# Returns the array of arguments of the method_node.
|
||||
sig { params(method_node: RuboCop::AST::Node).returns(T::Array[RuboCop::AST::Node]) }
|
||||
def parameters(method_node)
|
||||
method_node.arguments if method_node.send_type? || method_node.block_type?
|
||||
if method_node.is_a?(RuboCop::AST::SendNode) || method_node.is_a?(RuboCop::AST::BlockNode)
|
||||
method_node.arguments
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the given parameters are present in method call
|
||||
# and sets the method call as the offending node.
|
||||
# Params can be string, symbol, array, hash, matching regex.
|
||||
sig { params(method_node: RuboCop::AST::Node, params: T::Array[Object]).returns(T::Boolean) }
|
||||
def parameters_passed?(method_node, params)
|
||||
method_params = parameters(method_node)
|
||||
@offensive_node = method_node
|
||||
@@ -390,36 +476,32 @@ module RuboCop
|
||||
end
|
||||
|
||||
# Returns the ending position of the node in source code.
|
||||
sig { params(node: RuboCop::AST::Node).returns(Integer) }
|
||||
def end_column(node)
|
||||
node.source_range.end_pos
|
||||
end
|
||||
|
||||
# Returns the class node's name, or nil if not a class node.
|
||||
sig { params(node: RuboCop::AST::Node).returns(T.nilable(String)) }
|
||||
def class_name(node)
|
||||
@offensive_node = node
|
||||
node.const_name
|
||||
end
|
||||
|
||||
# Returns the method name for a def node.
|
||||
sig { params(node: RuboCop::AST::Node).returns(T.nilable(Symbol)) }
|
||||
def method_name(node)
|
||||
node.children[0] if node.def_type?
|
||||
end
|
||||
|
||||
# Returns the node size in the source code.
|
||||
def size(node)
|
||||
node.source_range.size
|
||||
end
|
||||
|
||||
# Returns the block length of the block node.
|
||||
def block_size(block)
|
||||
block.loc.end.line - block.loc.begin.line
|
||||
end
|
||||
|
||||
# Returns printable component name.
|
||||
sig { params(component_node: RuboCop::AST::Node).returns(T.nilable(Symbol)) }
|
||||
def format_component(component_node)
|
||||
return component_node.method_name if component_node.send_type? || component_node.block_type?
|
||||
|
||||
method_name(component_node) if component_node.def_type?
|
||||
if component_node.is_a?(RuboCop::AST::SendNode) || component_node.is_a?(RuboCop::AST::BlockNode)
|
||||
component_node.method_name
|
||||
elsif component_node.def_type?
|
||||
method_name(component_node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,16 +68,16 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
offending_node(on_system_node)
|
||||
@offensive_node = on_system_node
|
||||
problem "Instead of using `#{on_system_node.source}` in `#{parent_string}`, " \
|
||||
"use `#{if_statement_string}#{if_conditional}`." do |corrector|
|
||||
block_node = offending_node.parent
|
||||
block_node = @offensive_node.parent
|
||||
next if block_node.type != :block
|
||||
|
||||
# TODO: could fix corrector to handle this but punting for now.
|
||||
next if block_node.single_line?
|
||||
|
||||
source_range = offending_node.source_range.join(offending_node.parent.loc.begin)
|
||||
source_range = @offensive_node.source_range.join(@offensive_node.parent.loc.begin)
|
||||
corrector.replace(source_range, "#{if_statement_string}#{if_conditional}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubocops/shared/helper_functions"
|
||||
@@ -13,10 +13,17 @@ module RuboCop
|
||||
#
|
||||
# @param urls [Array] url/mirror method call nodes
|
||||
# @param regex [Regexp] pattern to match URLs
|
||||
def audit_urls(urls, regex)
|
||||
sig {
|
||||
params(
|
||||
urls: T::Array[T.any(RuboCop::AST::BlockNode, RuboCop::AST::SendNode)],
|
||||
regex: T.any(Regexp, String),
|
||||
_block: T.proc.params(match_object: MatchData, url: String, index: Integer).void,
|
||||
).void
|
||||
}
|
||||
def audit_urls(urls, regex, &_block)
|
||||
urls.each_with_index do |url_node, index|
|
||||
if @type == :cask
|
||||
url_string_node = url_node.first_argument
|
||||
url_string_node = T.cast(url_node, RuboCop::AST::SendNode).first_argument
|
||||
url_string = url_node.source
|
||||
else
|
||||
url_string_node = parameters(url_node).first
|
||||
@@ -34,8 +41,16 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
type: Symbol,
|
||||
urls: T::Array[T.any(RuboCop::AST::BlockNode, RuboCop::AST::SendNode)],
|
||||
mirrors: T::Array[T.any(RuboCop::AST::BlockNode, RuboCop::AST::SendNode)],
|
||||
livecheck_urls: T::Array[String],
|
||||
).void
|
||||
}
|
||||
def audit_url(type, urls, mirrors, livecheck_urls: [])
|
||||
@type = type
|
||||
@type = T.let(type, T.nilable(Symbol))
|
||||
|
||||
# URLs must be ASCII; IDNs must be punycode
|
||||
ascii_pattern = /[^\p{ASCII}]+/
|
||||
@@ -69,7 +84,7 @@ module RuboCop
|
||||
next if livecheck_urls.include?(url)
|
||||
|
||||
fixed = "https://www.apache.org/dyn/closer.lua?path=#{match[1]}"
|
||||
url_parameter_node = parameters(urls.fetch(index)).first
|
||||
url_parameter_node = parameters(urls.fetch(index)).fetch(0)
|
||||
problem "#{url} should be: #{fixed}" do |corrector|
|
||||
corrector.replace(url_parameter_node.source_range, "\"#{fixed}\"")
|
||||
end
|
||||
@@ -87,7 +102,7 @@ module RuboCop
|
||||
|
||||
audit_urls(mirrors, /.*/) do |_, mirror|
|
||||
urls.each do |url|
|
||||
url_string = string_content(parameters(url).first)
|
||||
url_string = string_content(parameters(url).fetch(0))
|
||||
next unless url_string.eql?(mirror)
|
||||
|
||||
problem "URL should not be duplicated as a mirror: #{url_string}"
|
||||
|
||||
@@ -129,8 +129,8 @@ module RuboCop
|
||||
return if formula_tap != "homebrew-core"
|
||||
|
||||
find_method_calls_by_name(body_node, :url).each do |url|
|
||||
next unless string_content(parameters(url).first).match?(/\.git$/)
|
||||
next if url_has_revision?(parameters(url).last)
|
||||
next unless string_content(parameters(url).fetch(0)).match?(/\.git$/)
|
||||
next if url_has_revision?(parameters(url).fetch(-1))
|
||||
|
||||
offending_node(url)
|
||||
problem "Formulae in homebrew/core should specify a revision for Git URLs"
|
||||
@@ -152,8 +152,8 @@ module RuboCop
|
||||
return if formula_tap != "homebrew-core"
|
||||
|
||||
find_method_calls_by_name(body_node, :url).each do |url|
|
||||
next unless string_content(parameters(url).first).match?(/\.git$/)
|
||||
next if url_has_tag?(parameters(url).last)
|
||||
next unless string_content(parameters(url).fetch(0)).match?(/\.git$/)
|
||||
next if url_has_tag?(parameters(url).fetch(-1))
|
||||
|
||||
offending_node(url)
|
||||
problem "Formulae in homebrew/core should specify a tag for Git URLs"
|
||||
|
||||
@@ -106,10 +106,11 @@ module RuboCop
|
||||
@offensive_node = method
|
||||
problem "`uses_from_macos` should not be used when Linux is required." if depends_on_linux
|
||||
|
||||
dep = if parameters(method).first.instance_of?(RuboCop::AST::StrNode)
|
||||
parameters(method).first
|
||||
elsif parameters(method).first.instance_of?(RuboCop::AST::HashNode)
|
||||
parameters(method).first.keys.first
|
||||
first_argument = parameters(method).first
|
||||
dep = if first_argument.instance_of?(RuboCop::AST::StrNode)
|
||||
first_argument
|
||||
elsif first_argument.instance_of?(RuboCop::AST::HashNode)
|
||||
first_argument.keys.first
|
||||
end
|
||||
|
||||
dep_name = string_content(dep)
|
||||
|
||||
@@ -13,7 +13,7 @@ module RuboCop
|
||||
version_node = find_node_method_by_name(formula_nodes.body_node, :version)
|
||||
return unless version_node
|
||||
|
||||
version = string_content(parameters(version_node).first)
|
||||
version = string_content(parameters(version_node).fetch(0))
|
||||
|
||||
problem "Version is set to an empty string" if version.empty?
|
||||
|
||||
|
||||
@@ -11,16 +11,6 @@ class Integer
|
||||
)
|
||||
.returns(Integer)
|
||||
}
|
||||
sig { params(other: T.anything).returns(NilClass) }
|
||||
sig { params(other: T.anything).returns(T.nilable(Integer)) }
|
||||
def <=>(other); end
|
||||
end
|
||||
|
||||
# https://github.com/sorbet/sorbet/pull/9847
|
||||
class IO
|
||||
# Waits until IO is readable and returns a truthy value, or a falsy value when
|
||||
# times out. Returns a truthy value immediately when buffered data is available.
|
||||
#
|
||||
# You must require 'io/wait' to use this method.
|
||||
sig { params(timeout: T.nilable(T.any(Float, Integer, Rational))).returns(T.nilable(T.any(IO, T::Boolean))) }
|
||||
def wait_readable(timeout = nil); end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user