Skip to content

Commit

Permalink
Split MethodCallWithArgsParentheses
Browse files Browse the repository at this point in the history
  • Loading branch information
buehmann authored and bbatsov committed Jan 4, 2020
1 parent b6bda3f commit 0565f7a
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 209 deletions.
4 changes: 4 additions & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,11 @@
require_relative 'rubocop/cop/style/lambda_call'
require_relative 'rubocop/cop/style/line_end_concatenation'
require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
# rubocop:disable Layout/LineLength
require_relative 'rubocop/cop/style/method_call_with_args_parentheses'
require_relative 'rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses'
require_relative 'rubocop/cop/style/method_call_with_args_parentheses/require_parentheses'
# rubocop:enable Layout/LineLength
require_relative 'rubocop/cop/style/method_called_on_do_end_block'
require_relative 'rubocop/cop/style/method_def_parentheses'
require_relative 'rubocop/cop/style/method_missing_super'
Expand Down
15 changes: 11 additions & 4 deletions lib/rubocop/cli/command/show_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ module Command
class ShowCops < Base
self.command_name = :show_cops

def initialize(env)
super

# Load the configs so the require()s are done for custom cops
@config = @config_store.for(Dir.pwd)
end

def run
print_available_cops
end

private

def print_available_cops
# Load the configs so the require()s are done for custom cops
@config_store.for(Dir.pwd)
registry = Cop::Cop.registry
show_all = @options[:show_cops].empty?

Expand Down Expand Up @@ -46,7 +51,9 @@ def print_cops_of_department(registry, department, show_all)

def print_cop_details(cops)
cops.each do |cop|
puts '# Supports --auto-correct' if cop.new.support_autocorrect?
if cop.new(@config).support_autocorrect?
puts '# Supports --auto-correct'
end
puts "#{cop.cop_name}:"
puts config_lines(cop)
puts
Expand All @@ -64,7 +71,7 @@ def cops_of_department(cops, department)
end

def config_lines(cop)
cnf = @config_store.for(Dir.pwd).for_cop(cop)
cnf = @config.for_cop(cop)
cnf.to_yaml.lines.to_a.drop(1).map { |line| ' ' + line }
end
end
Expand Down
209 changes: 4 additions & 205 deletions lib/rubocop/cop/style/method_call_with_args_parentheses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ module Style
#
# @example EnforcedStyle: require_parentheses (default)
#
#
# # bad
# array.delete e
#
Expand Down Expand Up @@ -149,94 +148,18 @@ class MethodCallWithArgsParentheses < Cop
include IgnoredMethods
include IgnoredPattern

TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze

def on_send(node)
case style
when :require_parentheses
add_offense_for_require_parentheses(node)
when :omit_parentheses
add_offense_for_omit_parentheses(node)
end
end
alias on_csend on_send
alias on_super on_send
alias on_yield on_send

def autocorrect(node)
case style
when :require_parentheses
autocorrect_for_require_parentheses(node)
when :omit_parentheses
autocorrect_for_omit_parentheses(node)
end
end

def message(_node = nil)
def initialize(*)
super
case style
when :require_parentheses
'Use parentheses for method calls with arguments.'
extend RequireParentheses
when :omit_parentheses
'Omit parentheses for method calls with arguments.'
extend OmitParentheses
end
end

private

def add_offense_for_require_parentheses(node)
return if ignored_method?(node.method_name)
return if matches_ignored_pattern?(node.method_name)
return if eligible_for_parentheses_omission?(node)
return unless node.arguments? && !node.parenthesized?

add_offense(node)
end

def add_offense_for_omit_parentheses(node)
return unless node.parenthesized?
return if node.implicit_call?
return if super_call_without_arguments?(node)
return if allowed_camel_case_method_call?(node)
return if legitimate_call_with_parentheses?(node)

add_offense(node, location: node.loc.begin.join(node.loc.end))
end

def autocorrect_for_require_parentheses(node)
lambda do |corrector|
corrector.replace(args_begin(node), '(')

unless args_parenthesized?(node)
corrector.insert_after(args_end(node), ')')
end
end
end

def autocorrect_for_omit_parentheses(node)
lambda do |corrector|
if parentheses_at_the_end_of_multiline_call?(node)
corrector.replace(args_begin(node), ' \\')
else
corrector.replace(args_begin(node), ' ')
end
corrector.remove(node.loc.end)
end
end

def eligible_for_parentheses_omission?(node)
node.operator_method? || node.setter_method? || ignored_macro?(node)
end

def included_macros_list
cop_config.fetch('IncludedMacros', []).map(&:to_sym)
end

def ignored_macro?(node)
cop_config['IgnoreMacros'] &&
node.macro? &&
!included_macros_list.include?(node.method_name)
end

def args_begin(node)
loc = node.loc
selector =
Expand All @@ -256,130 +179,6 @@ def args_parenthesized?(node)
first_node = node.arguments.first
first_node.begin_type? && first_node.parenthesized_call?
end

def parentheses_at_the_end_of_multiline_call?(node)
node.multiline? &&
node.loc.begin.source_line
.gsub(TRAILING_WHITESPACE_REGEX, '')
.end_with?('(')
end

def super_call_without_arguments?(node)
node.super_type? && node.arguments.none?
end

def allowed_camel_case_method_call?(node)
node.camel_case_method? &&
(node.arguments.none? ||
cop_config['AllowParenthesesInCamelCaseMethod'])
end

def legitimate_call_with_parentheses?(node)
call_in_literals?(node) ||
call_with_ambiguous_arguments?(node) ||
call_in_logical_operators?(node) ||
call_in_optional_arguments?(node) ||
allowed_multiline_call_with_parentheses?(node) ||
allowed_chained_call_with_parentheses?(node)
end

def call_in_literals?(node)
node.parent &&
(node.parent.pair_type? ||
node.parent.array_type? ||
node.parent.range_type? ||
splat?(node.parent) ||
ternary_if?(node.parent))
end

def call_in_logical_operators?(node)
node.parent &&
(logical_operator?(node.parent) ||
node.parent.send_type? &&
node.parent.arguments.any?(&method(:logical_operator?)))
end

def call_in_optional_arguments?(node)
node.parent &&
(node.parent.optarg_type? || node.parent.kwoptarg_type?)
end

def call_with_ambiguous_arguments?(node)
call_with_braced_block?(node) ||
call_as_argument_or_chain?(node) ||
hash_literal_in_arguments?(node) ||
node.descendants.any? do |n|
ambigious_literal?(n) || logical_operator?(n) ||
call_with_braced_block?(n)
end
end

def call_with_braced_block?(node)
(node.send_type? || node.super_type?) &&
node.block_node && node.block_node.braces?
end

def call_as_argument_or_chain?(node)
node.parent &&
(node.parent.send_type? && !assigned_before?(node.parent, node) ||
node.parent.csend_type? || node.parent.super_type?)
end

def hash_literal_in_arguments?(node)
node.arguments.any? do |n|
hash_literal?(n) ||
n.send_type? && node.descendants.any?(&method(:hash_literal?))
end
end

def allowed_multiline_call_with_parentheses?(node)
cop_config['AllowParenthesesInMultilineCall'] && node.multiline?
end

def allowed_chained_call_with_parentheses?(node)
return false unless cop_config['AllowParenthesesInChaining']

previous = node.descendants.first
return false unless previous&.send_type?

previous.parenthesized? ||
allowed_chained_call_with_parentheses?(previous)
end

def ambigious_literal?(node)
splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) ||
unary_literal?(node)
end

def splat?(node)
node.splat_type? || node.kwsplat_type? || node.block_pass_type?
end

def ternary_if?(node)
node.if_type? && node.ternary?
end

def logical_operator?(node)
(node.and_type? || node.or_type?) && node.logical_operator?
end

def hash_literal?(node)
node.hash_type? && node.braces?
end

def regexp_slash_literal?(node)
node.regexp_type? && node.loc.begin.source == '/'
end

def unary_literal?(node)
node.numeric_type? && node.sign? ||
node.parent&.send_type? && node.parent&.unary_operation?
end

def assigned_before?(node, target)
node.assignment? &&
node.loc.operator.begin < target.loc.begin
end
end
end
end
Expand Down
Loading

0 comments on commit 0565f7a

Please sign in to comment.