Class: RuboCop::Cop::Style::RedundantLineContinuation
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
::RuboCop::Cop::AutoCorrector,
::RuboCop::Cop::Base,
::RuboCop::ExcludeLimit,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
|
Instance Chain:
|
|
| Inherits: |
RuboCop::Cop::Base
|
| Defined in: | lib/rubocop/cop/style/redundant_line_continuation.rb |
Overview
Checks for redundant line continuation.
A line continuation is redundant when removing the backslash does not change how the program parses: the source is reparsed without the backslash and the resulting AST is compared to the original. Only backslashes that are pure noise are reported; backslashes that are significant — inside strings, for string concatenation, before an operator or argument that would otherwise start a new statement, and so on — are left alone, as are backslashes in comments.
Constant Summary
-
LINE_CONTINUATION =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 76'\\' -
LINE_CONTINUATION_PATTERN =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 77/(\\\n)/.freeze
-
MSG =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 75'Redundant line continuation.' -
STRING_LITERAL_BEGINNING_TOKEN_TYPES =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 80%i[tSTRING tSTRING_BEG].freeze
-
STRING_LITERAL_ENDING_TOKEN_TYPES =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 79%i[tSTRING tSTRING_END].freeze
-
STRING_TOKEN_TYPES =
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 78%i[tSTRING tSTRING_CONTENT].freeze
::RuboCop::Cop::Base - Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::RangeHelp - Included
::RuboCop::Cop::ReparsedEquivalence - Included
Class Attribute Summary
::RuboCop::Cop::AutoCorrector - Extended
::RuboCop::Cop::Base - Inherited
| .gem_requirements, .lint?, | |
| .support_autocorrect? | Returns if class supports autocorrect. |
| .support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to #on_new_investigation and |
Class Method Summary
::RuboCop::Cop::Base - Inherited
| .autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. |
| .badge | Naming. |
| .callbacks_needed, .cop_name, .department, | |
| .documentation_url | Returns a url to view this cops documentation online. |
| .exclude_from_registry | Call for abstract Cop classes. |
| .inherited, | |
| .joining_forces | Override and return the Force class(es) you need to join. |
| .match? | Returns true if the cop name or the cop namespace matches any of the given names. |
| .new, | |
| .requires_gem | Register a version requirement for the given gem name. |
| .restrict_on_send | Reserved for Commissioner. |
::RuboCop::ExcludeLimit - Extended
| exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
| transform | |
Instance Attribute Summary
::RuboCop::Cop::Base - Inherited
::RuboCop::Cop::AutocorrectLogic - Included
Instance Method Summary
- #on_new_investigation
- #apply_reparse_correction(corrector, range) private
- #first_token_after(tokens, range) private
-
#implicit_string_concatenation?(range) ⇒ Boolean
private
A backslash directly joining two string literals is never redundant: removing it moves the second literal onto its own logical line, severing the implicit string concatenation.
- #inspect_end_of_ruby_code_line_continuation private
- #last_token_before(tokens, range) private
-
#leading_dot_method_chain_with_blank_line?(range) ⇒ Boolean
private
The
parsergem, unlike Ruby itself, accepts a leading-dot method chain continued across a blank line, so the reparse check alone would deem the backslash redundant even though removing it breaks the code on MRI. - #line_continuation_candidates private
- #string_literal_beginning_on_line?(token, line) ⇒ Boolean private
- #string_literal_ending_on_line?(token, line) ⇒ Boolean private
-
#trailing_line_continuation_range(line_number)
private
The backslash is the last character of the line; locate it by the line’s position in the buffer rather than treating the column as an absolute offset (which corrupts an earlier line in multi-line files).
- #within_comment?(range) ⇒ Boolean private
-
#within_string_content?(range) ⇒ Boolean
private
A backslash in string content is never a redundant line continuation (removing it changes the string’s value, which the reparse check would detect); skipping it up front just avoids a useless reparse.
::RuboCop::Cop::ReparsedEquivalence - Included
| #corrected_scope_fragment | The corrections' edits are all contained within the scope, so the corrected fragment can be cut out of the corrected source by adjusting for the edits' length delta. |
| #correction_parses? | Whether the exact correction for |
| #corrections_verify?, #item_range, | |
| #normalize_reparsed_ast | Hook: loosen strict tree equality for corrections that are equivalence-preserving beyond parse identity. |
| #normalized_original, | |
| #parses_equivalently? | Both sides are parsed with the original path so that |
| #preprocess_reparsed_source | Hook: rewrite both sides before parsing. |
| #reparse_scope | The innermost scope that both contains |
| #scope_groups, #verification_too_large?, | |
| #verified_by_reparse | Returns the items whose corrections are verified. |
::RuboCop::Cop::MatchRange - Included
| #each_match_range | Return a new |
| #match_range | For a |
::RuboCop::Cop::RangeHelp - Included
| #add_range, | |
| #arguments_range | A range containing the first to the last argument of a method call or method definition. |
| #column_offset_between, | |
| #contents_range | A range containing only the contents of a literal with delimiters (e.g. |
| #directions, | |
| #effective_column | Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value. |
| #final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range | |
::RuboCop::Cop::Base - Inherited
| #add_global_offense | Adds an offense that has no particular location. |
| #add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. |
| #begin_investigation | Called before any investigation. |
| #callbacks_needed, | |
| #cop_config | Configuration Helpers. |
| #cop_name, #excluded_file?, | |
| #external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: |
| #inspect, | |
| #message | Gets called if no message is specified when calling |
| #name | Alias for Base#cop_name. |
| #offenses, | |
| #on_investigation_end | Called after all on_… |
| #on_new_investigation | Called before all on_… |
| #on_other_file | Called instead of all on_… |
| #parse | There should be very limited reasons for a Cop to do it’s own parsing. |
| #parser_engine, | |
| #ready | Called between investigations. |
| #relevant_file?, | |
| #target_gem_version | Returns a gems locked versions (i.e. |
| #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
| #callback_argument | Reserved for Cop::Cop. |
| #complete_investigation | Called to complete an investigation. |
| #correct, #current_corrector, | |
| #current_offense_locations | Reserved for Commissioner: |
| #current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #matches_absolute_include_pattern?, #range_for_original, #range_from_node_or_range, | |
| #reset_investigation | Actually private methods. |
| #use_corrector | |
::RuboCop::Cop::AutocorrectLogic - Included
::RuboCop::Cop::IgnoredNode - Included
Constructor Details
This class inherits a constructor from RuboCop::Cop::Base
Instance Method Details
#apply_reparse_correction(corrector, range) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 99
def apply_reparse_correction(corrector, range) corrector.remove_leading(range, 1) end
#first_token_after(tokens, range) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 163
def first_token_after(tokens, range) tokens.bsearch { |token| token.begin_pos >= range.end_pos } end
#implicit_string_concatenation?(range) ⇒ Boolean (private)
A backslash directly joining two string literals is never redundant: removing it moves the second literal onto its own logical line, severing the implicit string concatenation. Skipping these up front avoids reparse verification, which is prohibitively slow on generated files gluing thousands of string fragments with line continuations.
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 128
def implicit_string_concatenation?(range) tokens = processed_source.sorted_tokens token_before = last_token_before(tokens, range) return false unless string_literal_ending_on_line?(token_before, range.line) string_literal_beginning_on_line?(first_token_after(tokens, range), range.line + 1) end
#inspect_end_of_ruby_code_line_continuation (private)
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 179
def inspect_end_of_ruby_code_line_continuation last_line_number = processed_source.ast.last_line last_line = processed_source.lines[last_line_number - 1] return unless last_line&.end_with?(LINE_CONTINUATION) range = trailing_line_continuation_range(last_line_number) return if within_comment?(range) return if verified_by_reparse([range], oversized: :verify).empty? add_offense(range) do |corrector| corrector.remove_trailing(range, 1) end end
#last_token_before(tokens, range) (private)
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 158
def last_token_before(tokens, range) index = tokens.bsearch_index { |token| token.end_pos > range.begin_pos } index&.positive? ? tokens[index - 1] : nil end
#leading_dot_method_chain_with_blank_line?(range) ⇒ Boolean (private)
The parser gem, unlike Ruby itself, accepts a leading-dot method
chain continued across a blank line, so the reparse check alone would
deem the backslash redundant even though removing it breaks the code
on MRI. Prism matches Ruby here and does not need this guard.
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 152
def leading_dot_method_chain_with_blank_line?(range) return false unless range.source_line.strip.start_with?('.', '&.') processed_source[range.line].strip.empty? end
#line_continuation_candidates (private)
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 103
def line_continuation_candidates candidates = [] each_match_range(processed_source.ast.source_range, LINE_CONTINUATION_PATTERN) do |range| next if within_comment?(range) || implicit_string_concatenation?(range) next if within_string_content?(range) next if leading_dot_method_chain_with_blank_line?(range) candidates << range end candidates end
#on_new_investigation
[ GitHub ]# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 82
def on_new_investigation return unless processed_source.ast # A backslash is redundant if the source parses to the same AST # without it; verification is the offense logic itself, so oversized # scopes are reparsed regardless of size. verified_by_reparse(line_continuation_candidates, oversized: :verify).each do |range| add_offense(range) do |corrector| corrector.remove_leading(range, 1) end end inspect_end_of_ruby_code_line_continuation end
#string_literal_beginning_on_line?(token, line) ⇒ Boolean (private)
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 173
def string_literal_beginning_on_line?(token, line) return false unless token STRING_LITERAL_BEGINNING_TOKEN_TYPES.include?(token.type) && token.line == line end
#string_literal_ending_on_line?(token, line) ⇒ Boolean (private)
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 167
def string_literal_ending_on_line?(token, line) return false unless token STRING_LITERAL_ENDING_TOKEN_TYPES.include?(token.type) && token.pos.last_line == line end
#trailing_line_continuation_range(line_number) (private)
The backslash is the last character of the line; locate it by the line’s position in the buffer rather than treating the column as an absolute offset (which corrupts an earlier line in multi-line files).
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 196
def trailing_line_continuation_range(line_number) line_range = processed_source.buffer.line_range(line_number) range_between(line_range.end_pos - 1, line_range.end_pos) end
#within_comment?(range) ⇒ Boolean (private)
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 117
def within_comment?(range) processed_source.comments.any? do |comment| comment.source_range.overlaps?(range) end end
#within_string_content?(range) ⇒ Boolean (private)
A backslash in string content is never a redundant line continuation (removing it changes the string’s value, which the reparse check would detect); skipping it up front just avoids a useless reparse.
# File 'lib/rubocop/cop/style/redundant_line_continuation.rb', line 140
def within_string_content?(range) @string_content_ranges ||= processed_source.tokens.filter_map do |token| token.pos if STRING_TOKEN_TYPES.include?(token.type) end @string_content_ranges.any? { |pos| pos.overlaps?(range) } end