Skip to content

Commit

Permalink
[Fix rubocop#11122] Add new Style/RedundantLineContinuation cop
Browse files Browse the repository at this point in the history
  • Loading branch information
ydah committed Mar 25, 2023
1 parent 75057a8 commit 694ab77
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 2 deletions.
5 changes: 5 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4878,6 +4878,11 @@ Style/RedundantInterpolation:
VersionAdded: '0.76'
VersionChanged: '1.30'

Style/RedundantLineContinuation:
Description: 'Check for redundant line continuation.'
Enabled: pending
VersionAdded: '<<next>>'

Style/RedundantParentheses:
Description: "Checks for parentheses that seem not to serve any purpose."
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@
require_relative 'rubocop/cop/style/redundant_file_extension_in_require'
require_relative 'rubocop/cop/style/redundant_heredoc_delimiter_quotes'
require_relative 'rubocop/cop/style/redundant_initialize'
require_relative 'rubocop/cop/style/redundant_line_continuation'
require_relative 'rubocop/cop/style/redundant_self_assignment'
require_relative 'rubocop/cop/style/redundant_self_assignment_branch'
require_relative 'rubocop/cop/style/require_order'
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/internal_affairs/cop_description.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CopDescription < Base
MSG = 'Description should be started with %<suggestion>s instead of `This cop ...`.'

SPECIAL_WORDS = %w[is can could should will would must may].freeze
COP_DESC_OFFENSE_REGEX = \
COP_DESC_OFFENSE_REGEX =
/^\s+# This cop (?<special>#{SPECIAL_WORDS.join('|')})?\s*(?<word>.+?) .*/.freeze
REPLACEMENT_REGEX = /^\s+# This cop (#{SPECIAL_WORDS.join('|')})?\s*(.+?) /.freeze

Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/hash_except.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def not_included?(negated, body)

def safe_to_register_offense?(block, except_key)
extracted = extract_body_if_negated(block.body)
if extracted.method?('in?') || extracted.method?('include?') || \
if extracted.method?('in?') || extracted.method?('include?') ||
extracted.method?('exclude?')
return true
end
Expand Down
142 changes: 142 additions & 0 deletions lib/rubocop/cop/style/redundant_line_continuation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# Check for redundant line continuation.
#
# This cop marks a line continuation as redundant if removing the backslash
# does not result in a syntax error.
# However, a backslash at the end of a comment or
# for string concatenation is not redundant and is not considered an offense.
#
# @example
# # bad
# foo. \
# bar
# foo \
# &.bar \
# .baz
#
# # good
# foo.
# bar
# foo
# &.bar
# .baz
#
# # bad
# [foo, \
# bar]
# {foo: \
# bar}
#
# # good
# [foo,
# bar]
# {foo:
# bar}
#
# # bad
# foo(bar, \
# baz)
#
# # good
# foo(bar,
# baz)
#
# # also good - backslash in string concatenation is not redundant
# foo('bar' \
# 'baz')
#
# # also good - backslash at the end of a comment is not redundant
# foo(bar, # \
# baz)
#
# # also good - backslash at the line following the newline begins with a + or -,
# # it is not redundant
# 1 \
# + 2 \
# - 3
#
# # also good - backslash with newline between the method name and its arguments,
# # it is not redundant.
# some_method \
# (argument)
#
class RedundantLineContinuation < Base
include MatchRange
extend AutoCorrector

MSG = 'Redundant line continuation.'

def on_new_investigation
return unless processed_source.ast

each_match_range(processed_source.ast.source_range, /(\\\n)/) do |range|
next if require_line_continuation?(range)
next unless redundant_line_continuation?(range)

add_offense(range) do |corrector|
corrector.remove_leading(range, 1)
end
end
end

private

def require_line_continuation?(range)
!ends_with_backslash_without_comment?(range.source_line) ||
string_concatenation?(range.source_line) ||
starts_with_plus_or_minus?(processed_source[range.line])
end

def ends_with_backslash_without_comment?(source_line)
source_line.gsub(/#.+/, '').end_with?('\\')
end

def string_concatenation?(source_line)
/["']\s*\\\z/.match?(source_line)
end

def redundant_line_continuation?(range)
return true unless (node = find_node_for_line(range.line))
return false if argument_newline?(node)

parse(node.source.gsub(/\\\n/, "\n")).valid_syntax?
end

def argument_newline?(node)
node = node.children.first if node.root? && node.begin_type?
return if !node.send_type? || node.arguments.empty?

node.loc.selector.line != node.first_argument.loc.line
end

def find_node_for_line(line)
processed_source.ast.each_node do |node|
return node if same_line?(node, line)
end
end

def same_line?(node, line)
return unless (source_range = node.source_range)

if node.is_a?(AST::StrNode)
if node.heredoc?
(node.loc.heredoc_body.line..node.loc.heredoc_body.last_line).cover?(line)
else
(source_range.line..source_range.last_line).cover?(line)
end
else
source_range.line == line
end
end

def starts_with_plus_or_minus?(source_line)
%r{\A\s*[+\-*/%]}.match?(source_line)
end
end
end
end
end

0 comments on commit 694ab77

Please sign in to comment.