Skip to content

Commit

Permalink
Add InternalAffairs/NodeFirstOrLastArgument cop
Browse files Browse the repository at this point in the history
This cop checks for `node.arguments.first` and `node.arguments.last`,
and corrects them to `node.first_argument` and `node.last_argument`,
respectively.

Version 1.30 of `rubocop-ast` adds support for `first_argument` on block
nodes, and is therefore required for this cop not to produce false
positives for those node types.
  • Loading branch information
sambostock authored and bbatsov committed Nov 5, 2023
1 parent 94161a8 commit d3fb79b
Show file tree
Hide file tree
Showing 29 changed files with 136 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#12305](https://github.com/rubocop/rubocop/pull/12305): Require `rubocop-ast` version 1.30 or greater. ([@sambostock][])
4 changes: 2 additions & 2 deletions lib/rubocop/cop/bundler/gem_comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ def contains_checked_options?(node)
end

def gem_options(node)
return [] unless node.arguments.last&.type == :hash
return [] unless node.last_argument&.type == :hash

node.arguments.last.keys.map(&:value)
node.last_argument.keys.map(&:value)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class DeprecatedAttributeAssignment < Base
def on_block(block_node)
return unless gem_specification(block_node)

block_parameter = block_node.arguments.first.source
block_parameter = block_node.first_argument.source

assignment = block_node.descendants.detect do |node|
use_deprecated_attributes?(node, block_parameter)
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/internal_affairs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require_relative 'internal_affairs/method_name_end_with'
require_relative 'internal_affairs/method_name_equal'
require_relative 'internal_affairs/node_destructuring'
require_relative 'internal_affairs/node_first_or_last_argument'
require_relative 'internal_affairs/node_matcher_directive'
require_relative 'internal_affairs/node_type_predicate'
require_relative 'internal_affairs/numblock_handler'
Expand Down
53 changes: 53 additions & 0 deletions lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

module RuboCop
module Cop
module InternalAffairs
# Checks for the use of `node.arguments.first` or `node.arguments.last` and
# suggests the use of `node.first_argument` or `node.last_argument` instead.
#
# @example
# # bad
# node.arguments.first
# node.arguments[0]
# node.arguments.last
# node.arguments[-1]
#
# # good
# node.first_argument
# node.last_argument
#
class NodeFirstOrLastArgument < Base
extend AutoCorrector
include RangeHelp

MSG = 'Use `#%<correct>s` instead of `#%<incorrect>s`.'
RESTRICT_ON_SEND = %i[arguments].freeze

# @!method arguments_first_or_last?(node)
def_node_matcher :arguments_first_or_last?, <<~PATTERN
{
(send (send !nil? :arguments) ${:first :last})
(send (send !nil? :arguments) :[] (int ${0 -1}))
}
PATTERN

def on_send(node)
arguments_first_or_last?(node.parent) do |end_or_index|
range = range_between(node.loc.selector.begin_pos, node.parent.source_range.end_pos)
correct = case end_or_index
when :first, 0 then 'first_argument'
when :last, -1 then 'last_argument'
else raise "Unknown end_or_index: #{end_or_index}"
end
message = format(MSG, correct: correct, incorrect: range.source)

add_offense(range, message: message) do |corrector|
corrector.replace(range, correct)
end
end
end
end
end
end
end
4 changes: 2 additions & 2 deletions lib/rubocop/cop/internal_affairs/node_matcher_directive.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def on_send(node)
return if node.arguments.none?
return unless valid_method_name?(node)

actual_name = node.arguments.first.value
actual_name = node.first_argument.value
directives = method_directives(node)
return too_many_directives(node) if directives.size > 1

Expand All @@ -53,7 +53,7 @@ def on_send(node)
private

def valid_method_name?(node)
node.arguments.first.str_type? || node.arguments.first.sym_type?
node.first_argument.str_type? || node.first_argument.sym_type?
end

def method_directives(node)
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/layout/argument_alignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def flattened_arguments(node)

def arguments_with_last_arg_pairs(node)
items = node.arguments[0..-2]
last_arg = node.arguments.last
last_arg = node.last_argument

if last_arg.hash_type? && !last_arg.braces?
items += last_arg.pairs
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/layout/first_parameter_indentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def check(def_node)
return if ignored_node?(def_node)

left_parenthesis = def_node.arguments.loc.begin
first_elem = def_node.arguments.first
first_elem = def_node.first_argument
return unless first_elem
return if same_line?(first_elem, left_parenthesis)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def fix_closing_parenthesis(node, corrector)
end

def add_correct_closing_paren(node, corrector)
corrector.insert_after(node.arguments.last, ')')
corrector.insert_after(node.last_argument, ')')
end

def remove_incorrect_closing_paren(node, corrector)
Expand Down Expand Up @@ -271,7 +271,7 @@ def fix_external_trailing_comma(node, corrector)
def add_correct_external_trailing_comma(node, corrector)
return unless external_trailing_comma?(node)

corrector.insert_after(node.arguments.last, ',')
corrector.insert_after(node.last_argument, ',')
end

def remove_incorrect_external_trailing_comma(node, corrector)
Expand Down
6 changes: 3 additions & 3 deletions lib/rubocop/cop/lint/erb_new_arguments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def on_send(node)
private

def autocorrect(corrector, node)
str_arg = node.arguments[0].source
str_arg = node.first_argument.source

kwargs = build_kwargs(node)
overridden_kwargs = override_by_legacy_args(kwargs, node)
Expand All @@ -121,11 +121,11 @@ def correct_arguments?(arguments)
end

def build_kwargs(node)
return [nil, nil] unless node.arguments.last.hash_type?
return [nil, nil] unless node.last_argument.hash_type?

trim_mode_arg, eoutvar_arg = nil

node.arguments.last.pairs.each do |pair|
node.last_argument.pairs.each do |pair|
case pair.key.source
when 'trim_mode'
trim_mode_arg = "trim_mode: #{pair.value.source}"
Expand Down
8 changes: 3 additions & 5 deletions lib/rubocop/cop/lint/non_deterministic_require_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def on_block_pass(node)
parent_node = node.parent

add_offense(parent_node) do |corrector|
if parent_node.arguments.last&.block_pass_type?
if parent_node.last_argument&.block_pass_type?
correct_block_pass(corrector, parent_node)
else
correct_block(corrector, parent_node)
Expand All @@ -116,7 +116,7 @@ def correct_block(corrector, node)

def correct_block_pass(corrector, node)
if unsorted_dir_glob_pass?(node)
block_arg = node.arguments.last
block_arg = node.last_argument

corrector.remove(last_arg_range(node))
corrector.insert_after(node, ".sort.each(#{block_arg.source})")
Expand All @@ -130,9 +130,7 @@ def correct_block_pass(corrector, node)
# @return [Parser::Source::Range]
#
def last_arg_range(node)
node.arguments.last.source_range.with(
begin_pos: node.arguments[-2].source_range.end_pos
)
node.last_argument.source_range.with(begin_pos: node.arguments[-2].source_range.end_pos)
end

def unsorted_dir_loop?(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class TrailingCommaInAttributeDeclaration < Base
MSG = 'Avoid leaving a trailing comma in attribute declarations.'

def on_send(node)
return unless node.attribute_accessor? && node.arguments.last.def_type?
return unless node.attribute_accessor? && node.last_argument.def_type?

trailing_comma = trailing_comma_range(node)

Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/mixin/check_line_breakable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def process_args(args)

# @api private
def already_on_multiple_lines?(node)
return node.first_line != node.arguments.last.last_line if node.def_type?
return node.first_line != node.last_argument.last_line if node.def_type?

!node.single_line?
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/naming/block_forwarding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def self.autocorrect_incompatible_with
def on_def(node)
return if node.arguments.empty?

last_argument = node.arguments.last
last_argument = node.last_argument
return if expected_block_forwarding_style?(node, last_argument)

register_offense(last_argument, node)
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/naming/memoized_instance_variable_name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def on_or_asgn(node)

# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def on_defined?(node)
arg = node.arguments.first
arg = node.first_argument
return false unless arg.ivar_type?

method_node, method_name = find_definition(node)
Expand Down
4 changes: 2 additions & 2 deletions lib/rubocop/cop/style/access_modifier_declarations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ def message(range)

def find_corresponding_def_node(node)
if access_modifier_with_symbol?(node)
method_name = node.arguments.first.value
method_name = node.first_argument.value
node.parent.each_child_node(:def).find do |child|
child.method?(method_name)
end
else
node.arguments.first
node.first_argument
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/accessor_grouping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def separate_accessors(node)
*processed_source.ast_with_comments[arg].map(&:text),
"#{node.method_name} #{arg.source}"
]
if arg == node.arguments.first
if arg == node.first_argument
lines
else
indent = ' ' * node.loc.column
Expand Down
8 changes: 4 additions & 4 deletions lib/rubocop/cop/style/case_like_if.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def find_target_in_send_node(node)
when :==, :eql?, :equal?
find_target_in_equality_node(node)
when :===
node.arguments.first
node.first_argument
when :include?, :cover?
find_target_in_include_or_cover_node(node)
when :match, :match?, :=~
Expand All @@ -134,7 +134,7 @@ def find_target_in_send_node(node)
end

def find_target_in_equality_node(node)
argument = node.arguments.first
argument = node.first_argument
receiver = node.receiver
return unless argument && receiver

Expand All @@ -152,7 +152,7 @@ def find_target_in_include_or_cover_node(node)
end

def find_target_in_match_node(node)
argument = node.arguments.first
argument = node.first_argument
receiver = node.receiver
return unless receiver

Expand Down Expand Up @@ -185,7 +185,7 @@ def collect_conditions(node, target, conditions)
def condition_from_send_node(node, target)
case node.method_name
when :is_a?
node.arguments.first if node.receiver == target
node.first_argument if node.receiver == target
when :==, :eql?, :equal?
condition_from_equality_node(node, target)
when :=~, :match, :match?
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/empty_literal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def first_argument_unparenthesized?(node)
parent = node.parent
return false unless parent && %i[send super zsuper].include?(parent.type)

node.equal?(parent.arguments.first) && !parentheses?(node.parent)
node.equal?(parent.first_argument) && !parentheses?(node.parent)
end

def replacement_range(node)
Expand Down
6 changes: 3 additions & 3 deletions lib/rubocop/cop/style/eval_with_location.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def on_send(node)
# are considered.
return if node.method?(:eval) && !valid_eval_receiver?(node.receiver)

code = node.arguments.first
code = node.first_argument
return unless code && (code.str_type? || code.dstr_type?)

check_location(node, code)
Expand Down Expand Up @@ -165,7 +165,7 @@ def check_file(node, file_node)
end

def check_line(node, code)
line_node = node.arguments.last
line_node = node.last_argument
line_diff = line_difference(line_node, code)
if line_diff.zero?
add_offense_for_same_line(node, line_node)
Expand Down Expand Up @@ -227,7 +227,7 @@ def add_offense_for_missing_location(node, code)
end

def missing_line(node, code)
line_diff = line_difference(node.arguments.last, code)
line_diff = line_difference(node.last_argument, code)
sign = line_diff.positive? ? :+ : :-
expected_line(sign, line_diff)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/rubocop/cop/style/explicit_block_argument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def on_yield(node)

def extract_block_name(def_node)
if def_node.block_argument?
def_node.arguments.last.name
def_node.last_argument.name
else
'block'
end
Expand Down Expand Up @@ -127,7 +127,7 @@ def call_like?(node)
end

def insert_argument(node, corrector, block_name)
last_arg = node.arguments.last
last_arg = node.last_argument
arg_range = range_with_surrounding_comma(last_arg.source_range, :right)
replacement = " &#{block_name}"
replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/method_call_with_args_parentheses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def args_end(node)
def args_parenthesized?(node)
return false unless node.arguments.one?

first_node = node.arguments.first
first_node = node.first_argument
first_node.begin_type? && first_node.parenthesized_call?
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/method_def_parentheses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def anonymous_arguments?(node)
return true if node.arguments.any? do |arg|
arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
end
return false unless (last_argument = node.arguments.last)
return false unless (last_argument = node.last_argument)

last_argument.blockarg_type? && last_argument.name.nil?
end
Expand Down
4 changes: 2 additions & 2 deletions lib/rubocop/cop/style/redundant_argument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def on_send(node)
return unless redundant_argument?(node)

offense_range = argument_range(node)
message = format(MSG, arg: node.arguments.first.source)
message = format(MSG, arg: node.first_argument.source)

add_offense(offense_range, message: message) do |corrector|
corrector.remove(offense_range)
Expand All @@ -80,7 +80,7 @@ def redundant_argument?(node)
redundant_argument = redundant_arg_for_method(node.method_name.to_s)
return false if redundant_argument.nil?

node.arguments.first == redundant_argument
node.first_argument == redundant_argument
end

def redundant_arg_for_method(method_name)
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/redundant_sort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def suffix(sorter)
end

def arg_node(node)
node.arguments.first
node.first_argument
end

def arg_value(node)
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/select_by_regexp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def find_regexp(node, block)
return node.child_nodes.first if node.match_with_lvasgn_type?

if node.receiver.lvar_type? &&
(block.numblock_type? || node.receiver.source == block.arguments.first.source)
(block.numblock_type? || node.receiver.source == block.first_argument.source)
node.first_argument
elsif node.first_argument.lvar_type?
node.receiver
Expand Down

0 comments on commit d3fb79b

Please sign in to comment.