-
Gautham Goli authoredGautham Goli authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
formula_cop.rb 4.95 KiB
module RuboCop
module Cop
module Homebrew
class FormulaCop < Cop
@registry = Cop.registry
def on_class(node)
# This method is called by RuboCop and is the main entry point
file_path = processed_source.buffer.name
return unless file_path_allowed?(file_path)
class_node, parent_class_node, body = *node
return unless formula_class?(parent_class_node)
return unless respond_to?(:audit_formula)
@formula_name = class_name(class_node)
audit_formula(node, class_node, parent_class_node, body)
end
def regex_match_group(node, pattern)
# Checks for regex match of pattern in the node and
# Sets the appropriate instance variables to report the match
string_repr = string_content(node)
match_object = string_repr.match(pattern)
return unless match_object
node_begin_pos = start_column(node)
line_begin_pos = line_start_column(node)
@column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1
@length = match_object.to_s.length
@line_no = line_number(node)
@source_buf = source_buffer(node)
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
@offensive_node = node
match_object
end
def find_node_method_by_name(node, method_name)
# Returns method_node matching method_name
return if node.nil?
node.each_child_node(:send) do |method_node|
next unless method_node.method_name == method_name
@offensive_node = method_node
@offense_source_range = method_node.source_range
return method_node
end
# If not found then, parent node becomes the offensive node
@offensive_node = node.parent
@offense_source_range = node.parent.source_range
nil
end
def find_block(node, block_name)
# Returns a block named block_name inside node
return if node.nil?
node.each_child_node(:block) do |block_node|
next if block_node.method_name != block_name
@offensive_node = block_node
@offense_source_range = block_node.source_range
return block_node
end
# If not found then, parent node becomes the offensive node
@offensive_node = node.parent
@offense_source_range = node.parent.source_range
nil
end
def method_called?(node, method_name)
# Check if a method is called inside a block
block_body = node.children[2]
block_body.each_child_node(:send) do |call_node|
next unless call_node.method_name == method_name
@offensive_node = call_node
@offense_source_range = call_node.source_range
return true
end
false
end
def parameters(method_node)
# Returns the array of arguments of the method_node
return unless method_node.send_type?
method_node.method_args
end
def line_start_column(node)
# Returns the begin position of the node's line in source code
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
end
def start_column(node)
# Returns the begin position of the node in source code
node.source_range.begin_pos
end
def line_number(node)
# Returns the line number of the node
node.loc.line
end
def class_name(node)
# Returns the class node's name, nil if not a class node
@offensive_node = node
@offense_source_range = node.source_range
node.const_name
end
def size(node)
# Returns the node size in the source code
node.source_range.size
end
def block_size(block)
# Returns the block length of the block node
block_length(block)
end
def source_buffer(node)
# Source buffer is required as an argument to report style violations
node.source_range.source_buffer
end
def string_content(node)
# Returns the string representation if node is of type str
node.str_content if node.type == :str
end
def problem(msg)
add_offense(@offensive_node, @offense_source_range, msg)
end
private
def formula_class?(parent_class_node)
parent_class_node && parent_class_node.const_name == "Formula"
end
def file_path_allowed?(file_path)
paths_to_exclude = [%r{/Library/Homebrew/compat/},
%r{/Library/Homebrew/test/}]
return true if file_path.nil? # file_path is nil when source is directly passed to the cop eg., in specs
file_path !~ Regexp.union(paths_to_exclude)
end
end
end
end
end