rubocop 0.74.0 → 0.75.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/config/default.yml +28 -4
  4. data/lib/rubocop.rb +6 -1
  5. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +1 -12
  6. data/lib/rubocop/comment_config.rb +3 -2
  7. data/lib/rubocop/config.rb +4 -0
  8. data/lib/rubocop/config_loader.rb +20 -2
  9. data/lib/rubocop/config_loader_resolver.rb +2 -2
  10. data/lib/rubocop/config_obsoletion.rb +12 -0
  11. data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
  12. data/lib/rubocop/cop/cop.rb +4 -3
  13. data/lib/rubocop/cop/correctors/alignment_corrector.rb +43 -17
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +2 -2
  15. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  16. data/lib/rubocop/cop/generator.rb +3 -3
  17. data/lib/rubocop/cop/generator/configuration_injector.rb +9 -4
  18. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  19. data/lib/rubocop/cop/layout/block_alignment.rb +2 -2
  20. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +22 -7
  22. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +2 -2
  23. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +2 -2
  24. data/lib/rubocop/cop/layout/extra_spacing.rb +0 -6
  25. data/lib/rubocop/cop/layout/indent_assignment.rb +10 -1
  26. data/lib/rubocop/cop/layout/indent_heredoc.rb +1 -1
  27. data/lib/rubocop/cop/layout/multiline_block_layout.rb +24 -2
  28. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +5 -1
  29. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +1 -1
  30. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +7 -0
  31. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +2 -0
  32. data/lib/rubocop/cop/lint/assignment_in_condition.rb +17 -4
  33. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  34. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +10 -36
  35. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  36. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +91 -0
  37. data/lib/rubocop/cop/lint/unneeded_cop_disable_directive.rb +1 -1
  38. data/lib/rubocop/cop/lint/unused_block_argument.rb +22 -6
  39. data/lib/rubocop/cop/lint/unused_method_argument.rb +23 -5
  40. data/lib/rubocop/cop/lint/void.rb +3 -22
  41. data/lib/rubocop/cop/message_annotator.rb +16 -7
  42. data/lib/rubocop/cop/migration/department_name.rb +44 -0
  43. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  44. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  45. data/lib/rubocop/cop/mixin/safe_mode.rb +2 -0
  46. data/lib/rubocop/cop/naming/method_name.rb +12 -1
  47. data/lib/rubocop/cop/naming/variable_name.rb +1 -0
  48. data/lib/rubocop/cop/offense.rb +18 -7
  49. data/lib/rubocop/cop/registry.rb +22 -1
  50. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -0
  51. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  52. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +29 -10
  53. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  54. data/lib/rubocop/cop/style/commented_keyword.rb +8 -2
  55. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -4
  56. data/lib/rubocop/cop/style/documentation_method.rb +44 -0
  57. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +8 -2
  58. data/lib/rubocop/cop/style/expand_path_arguments.rb +1 -1
  59. data/lib/rubocop/cop/style/format_string_token.rb +18 -69
  60. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +18 -33
  61. data/lib/rubocop/cop/style/if_unless_modifier.rb +51 -15
  62. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  63. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -25
  64. data/lib/rubocop/cop/style/mixin_usage.rb +11 -1
  65. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  66. data/lib/rubocop/cop/style/nested_modifier.rb +18 -2
  67. data/lib/rubocop/cop/style/or_assignment.rb +6 -1
  68. data/lib/rubocop/cop/style/parentheses_around_condition.rb +14 -0
  69. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -4
  70. data/lib/rubocop/cop/style/redundant_return.rb +12 -0
  71. data/lib/rubocop/cop/style/redundant_self.rb +18 -1
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +24 -0
  73. data/lib/rubocop/cop/style/safe_navigation.rb +17 -0
  74. data/lib/rubocop/cop/style/semicolon.rb +11 -0
  75. data/lib/rubocop/cop/style/single_line_methods.rb +8 -1
  76. data/lib/rubocop/cop/style/ternary_parentheses.rb +19 -0
  77. data/lib/rubocop/cop/utils/format_string.rb +128 -0
  78. data/lib/rubocop/cop/variable_force/variable.rb +15 -2
  79. data/lib/rubocop/core_ext/string.rb +0 -24
  80. data/lib/rubocop/formatter/clang_style_formatter.rb +8 -3
  81. data/lib/rubocop/formatter/emacs_style_formatter.rb +22 -9
  82. data/lib/rubocop/formatter/file_list_formatter.rb +1 -1
  83. data/lib/rubocop/formatter/formatter_set.rb +16 -15
  84. data/lib/rubocop/formatter/pacman_formatter.rb +80 -0
  85. data/lib/rubocop/formatter/simple_text_formatter.rb +16 -4
  86. data/lib/rubocop/formatter/tap_formatter.rb +17 -4
  87. data/lib/rubocop/magic_comment.rb +4 -0
  88. data/lib/rubocop/options.rb +5 -16
  89. data/lib/rubocop/runner.rb +14 -8
  90. data/lib/rubocop/version.rb +1 -1
  91. metadata +6 -3
  92. data/lib/rubocop/cop/mixin/ignored_method_patterns.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7299e4e21bbddcdbfec2725fec41c384d50867b70a986d87ea0f6f7f08dd3ebe
4
- data.tar.gz: 504dae598bbb526afed6bc9ecc56d86a27656d535bf52e30c85e7ab005af99f3
3
+ metadata.gz: 354a14aa737b4743d04d65393e8caae409513d2302b31aca36090441100f1bdb
4
+ data.tar.gz: 7bdbb625ae97e5e0f1a5ca6bfe0d93c71ac4a86a566ba13b987a293949486e51
5
5
  SHA512:
6
- metadata.gz: 57b94e3a5a06724249509077ada410188ad29b52ae6dfda9d8b56582cc08bdb443a60e75744abf0d2ac664b3124db18e3e5382f72ccacfd4be64faf2c489b0ae
7
- data.tar.gz: fb563b7256b89462268c6b3dc4b5cbf626764d2a3097dc5a332187a78c4f7d9d0c80c6dd2ba83692d48ba354696467329a87acf25474261a24bd9bffcd42f35a
6
+ metadata.gz: bf54a562c567c28f118f5bf6bf0a9fb9e5987351559a001f80de025cb08793cd546731e80d3690dcce3ce8e0eda96381563ae307a5b0df303322c75331b8f14b
7
+ data.tar.gz: 76fb85901be4d90de0697874bf0bea52c2a23e2b39fa9607ec797d6768465c4f903bc2645ebf41a504f526064aee2e0000f7bbd2701735787425770682b79ec8
data/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  [![Patreon](https://img.shields.io/badge/patreon-donate-orange.svg)](https://www.patreon.com/bbatsov)
10
10
  [![OpenCollective](https://opencollective.com/rubocop/backers/badge.svg)](#open-collective-backers)
11
11
  [![OpenCollective](https://opencollective.com/rubocop/sponsors/badge.svg)](#open-collective-sponsors)
12
+ [![Tidelift](https://tidelift.com/badges/package/rubygems/rubocop)](https://tidelift.com/subscription/pkg/rubygems-rubocop?utm_source=rubygems-rubocop&utm_medium=referral&utm_campaign=readme)
12
13
 
13
14
  <p align="center">
14
15
  <img src="https://raw.githubusercontent.com/rubocop-hq/rubocop/master/logo/rubo-logo-horizontal.png" alt="RuboCop Logo"/>
@@ -52,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
52
53
  might want to use a conservative version lock in your `Gemfile`:
53
54
 
54
55
  ```rb
55
- gem 'rubocop', '~> 0.74.0', require: false
56
+ gem 'rubocop', '~> 0.75.1', require: false
56
57
  ```
57
58
 
58
59
  ## Quickstart
@@ -1591,6 +1591,11 @@ Lint/ScriptPermission:
1591
1591
  VersionAdded: '0.49'
1592
1592
  VersionChanged: '0.50'
1593
1593
 
1594
+ Lint/SendWithMixinArgument:
1595
+ Description: 'Checks for `send` method when using mixin.'
1596
+ Enabled: true
1597
+ VersionAdded: '0.75'
1598
+
1594
1599
  Lint/ShadowedArgument:
1595
1600
  Description: 'Avoid reassigning arguments before they were used.'
1596
1601
  Enabled: true
@@ -1746,7 +1751,7 @@ Metrics/AbcSize:
1746
1751
  branches, and conditions.
1747
1752
  Reference:
1748
1753
  - http://c2.com/cgi/wiki?AbcMetric
1749
- - https://en.wikipedia.org/wiki/ABC_Software_Metric'
1754
+ - https://en.wikipedia.org/wiki/ABC_Software_Metric
1750
1755
  Enabled: true
1751
1756
  VersionAdded: '0.27'
1752
1757
  VersionChanged: '0.66'
@@ -1810,7 +1815,7 @@ Metrics/LineLength:
1810
1815
  - https
1811
1816
  # The IgnoreCopDirectives option causes the LineLength rule to ignore cop
1812
1817
  # directives like '# rubocop: enable ...' when calculating a line's length.
1813
- IgnoreCopDirectives: false
1818
+ IgnoreCopDirectives: true
1814
1819
  # The IgnoredPatterns option is a list of !ruby/regexp and/or string
1815
1820
  # elements. Strings will be converted to Regexp objects. A line that matches
1816
1821
  # any regular expression listed in this option will be ignored by LineLength.
@@ -1849,6 +1854,14 @@ Metrics/PerceivedComplexity:
1849
1854
  VersionAdded: '0.25'
1850
1855
  Max: 7
1851
1856
 
1857
+ ################## Migration #############################
1858
+
1859
+ Migration/DepartmentName:
1860
+ Description: >-
1861
+ Check that cop names in rubocop:disable (etc) comments are
1862
+ given with department name.
1863
+ Enabled: false
1864
+
1852
1865
  #################### Naming ##############################
1853
1866
 
1854
1867
  Naming/AccessorMethodName:
@@ -1984,6 +1997,13 @@ Naming/MethodName:
1984
1997
  SupportedStyles:
1985
1998
  - snake_case
1986
1999
  - camelCase
2000
+ # Method names matching patterns are always allowed.
2001
+ #
2002
+ # IgnoredPatterns:
2003
+ # - '\A\s*onSelectionBulkChange\s*'
2004
+ # - '\A\s*onSelectionCleared\s*'
2005
+ #
2006
+ IgnoredPatterns: []
1987
2007
 
1988
2008
  Naming/PredicateName:
1989
2009
  Description: 'Check the names of predicate methods.'
@@ -2706,7 +2726,7 @@ Style/FormatStringToken:
2706
2726
  - template
2707
2727
  - unannotated
2708
2728
  VersionAdded: '0.49'
2709
- VersionChanged: '0.52'
2729
+ VersionChanged: '0.75'
2710
2730
 
2711
2731
  Style/FrozenStringLiteralComment:
2712
2732
  Description: >-
@@ -2899,6 +2919,7 @@ Style/MethodCallWithArgsParentheses:
2899
2919
  VersionChanged: '0.61'
2900
2920
  IgnoreMacros: true
2901
2921
  IgnoredMethods: []
2922
+ IgnoredPatterns: []
2902
2923
  IncludedMacros: []
2903
2924
  AllowParenthesesInMultilineCall: false
2904
2925
  AllowParenthesesInChaining: false
@@ -3586,6 +3607,8 @@ Style/StringHashKeys:
3586
3607
  StyleGuide: '#symbols-as-keys'
3587
3608
  Enabled: false
3588
3609
  VersionAdded: '0.52'
3610
+ VersionChanged: '0.75'
3611
+ Safe: false
3589
3612
 
3590
3613
  Style/StringLiterals:
3591
3614
  Description: 'Checks if uses of quotes match the configured preference.'
@@ -3904,8 +3927,9 @@ Style/YodaCondition:
3904
3927
  - require_for_all_comparison_operators
3905
3928
  # enforce yoda only for equality operators: `!=` and `==`
3906
3929
  - require_for_equality_operators_only
3930
+ Safe: false
3907
3931
  VersionAdded: '0.49'
3908
- VersionChanged: '0.63'
3932
+ VersionChanged: '0.75'
3909
3933
 
3910
3934
  Style/ZeroLengthPredicate:
3911
3935
  Description: 'Use #empty? when testing for objects of length 0.'
@@ -120,7 +120,6 @@ require_relative 'rubocop/cop/mixin/frozen_string_literal'
120
120
  require_relative 'rubocop/cop/mixin/hash_alignment'
121
121
  require_relative 'rubocop/cop/mixin/ignored_pattern'
122
122
  require_relative 'rubocop/cop/mixin/ignored_methods'
123
- require_relative 'rubocop/cop/mixin/ignored_method_patterns'
124
123
  require_relative 'rubocop/cop/mixin/integer_node'
125
124
  require_relative 'rubocop/cop/mixin/interpolation'
126
125
  require_relative 'rubocop/cop/mixin/match_range'
@@ -158,6 +157,10 @@ require_relative 'rubocop/cop/mixin/trailing_comma'
158
157
  require_relative 'rubocop/cop/mixin/uncommunicative_name'
159
158
  require_relative 'rubocop/cop/mixin/unused_argument'
160
159
 
160
+ require_relative 'rubocop/cop/utils/format_string'
161
+
162
+ require_relative 'rubocop/cop/migration/department_name'
163
+
161
164
  require_relative 'rubocop/cop/correctors/alignment_corrector'
162
165
  require_relative 'rubocop/cop/correctors/condition_corrector'
163
166
  require_relative 'rubocop/cop/correctors/each_to_for_corrector'
@@ -329,6 +332,7 @@ require_relative 'rubocop/cop/lint/safe_navigation_consistency'
329
332
  require_relative 'rubocop/cop/lint/safe_navigation_chain'
330
333
  require_relative 'rubocop/cop/lint/safe_navigation_with_empty'
331
334
  require_relative 'rubocop/cop/lint/script_permission'
335
+ require_relative 'rubocop/cop/lint/send_with_mixin_argument'
332
336
  require_relative 'rubocop/cop/lint/shadowed_argument'
333
337
  require_relative 'rubocop/cop/lint/shadowed_exception'
334
338
  require_relative 'rubocop/cop/lint/shadowing_outer_local_variable'
@@ -578,6 +582,7 @@ require_relative 'rubocop/formatter/progress_formatter'
578
582
  require_relative 'rubocop/formatter/quiet_formatter'
579
583
  require_relative 'rubocop/formatter/tap_formatter'
580
584
  require_relative 'rubocop/formatter/worst_offenders_formatter'
585
+ require_relative 'rubocop/formatter/pacman_formatter'
581
586
  # relies on progress formatter
582
587
  require_relative 'rubocop/formatter/auto_gen_config_formatter'
583
588
 
@@ -222,22 +222,11 @@ module RuboCop
222
222
 
223
223
  def_node_matcher :macro_scope?, <<~PATTERN
224
224
  {^{({sclass class module block} ...) class_constructor?}
225
- ^^#ascend_macro_scope?
225
+ ^^{({sclass class module block} ... ({begin if} ...)) class_constructor?}
226
226
  ^#macro_kwbegin_wrapper?
227
227
  #root_node?}
228
228
  PATTERN
229
229
 
230
- def_node_matcher :wrapped_macro_scope?, <<~PATTERN
231
- {({sclass class module block} ... ({begin if} ...)) class_constructor?}
232
- PATTERN
233
-
234
- def ascend_macro_scope?(ancestor)
235
- return true if wrapped_macro_scope?(ancestor)
236
- return false if root_node?(ancestor)
237
-
238
- ascend_macro_scope?(ancestor.parent)
239
- end
240
-
241
230
  # Check if a node's parent is a kwbegin wrapper within a macro scope
242
231
  #
243
232
  # @param parent [Node] parent of the node being checked
@@ -11,7 +11,8 @@ module RuboCop
11
11
  COPS_PATTERN = "(all|#{COP_NAMES_PATTERN})"
12
12
 
13
13
  COMMENT_DIRECTIVE_REGEXP = Regexp.new(
14
- ('# rubocop : ((?:dis|en)able)\b ' + COPS_PATTERN).gsub(' ', '\s*')
14
+ ('# rubocop : ((?:disable|enable|todo))\b ' + COPS_PATTERN)
15
+ .gsub(' ', '\s*')
15
16
  )
16
17
 
17
18
  CopAnalysis = Struct.new(:line_ranges, :start_line_number)
@@ -141,7 +142,7 @@ module RuboCop
141
142
  cop_names =
142
143
  cops_string == 'all' ? all_cop_names : cops_string.split(/,\s*/)
143
144
 
144
- disabled = (switch == 'disable')
145
+ disabled = %w[disable todo].include?(switch)
145
146
 
146
147
  [cop_names, disabled]
147
148
  end
@@ -96,6 +96,10 @@ module RuboCop
96
96
  @for_cop[cop.respond_to?(:cop_name) ? cop.cop_name : cop]
97
97
  end
98
98
 
99
+ def for_department(department_name)
100
+ @for_cop[department_name]
101
+ end
102
+
99
103
  def for_all_cops
100
104
  @for_all_cops ||= self['AllCops'] || {}
101
105
  end
@@ -199,6 +199,8 @@ module RuboCop
199
199
  raise(TypeError, "Malformed configuration in #{absolute_path}")
200
200
  end
201
201
 
202
+ check_cop_config_value(hash)
203
+
202
204
  hash
203
205
  end
204
206
 
@@ -220,6 +222,22 @@ module RuboCop
220
222
  end
221
223
  end
222
224
 
225
+ def check_cop_config_value(hash, parent = nil)
226
+ hash.each do |key, value|
227
+ check_cop_config_value(value, key) if value.is_a?(Hash)
228
+
229
+ next unless %w[Enabled
230
+ Safe
231
+ SafeAutoCorrect
232
+ AutoCorrect].include?(key) && value.is_a?(String)
233
+
234
+ abort(
235
+ "Property #{Rainbow(key).yellow} of cop #{Rainbow(parent).yellow}" \
236
+ " is supposed to be a boolean and #{Rainbow(value).yellow} is not."
237
+ )
238
+ end
239
+ end
240
+
223
241
  # Read the specified file, or exit with a friendly, concise message on
224
242
  # stderr. Care is taken to use the standard OS exit code for a "file not
225
243
  # found" error.
@@ -240,11 +258,11 @@ module RuboCop
240
258
  yaml_code,
241
259
  permitted_classes: [Regexp, Symbol],
242
260
  permitted_symbols: [],
243
- aliases: false,
261
+ aliases: true,
244
262
  filename: filename
245
263
  )
246
264
  else
247
- YAML.safe_load(yaml_code, [Regexp, Symbol], [], false, filename)
265
+ YAML.safe_load(yaml_code, [Regexp, Symbol], [], true, filename)
248
266
  end
249
267
  end
250
268
  end
@@ -73,7 +73,7 @@ module RuboCop
73
73
 
74
74
  opts = { inherit_mode: config['inherit_mode'] || {},
75
75
  unset_nil: unset_nil }
76
- Config.new(merge(default_configuration, config, opts), config_file)
76
+ Config.new(merge(default_configuration, config, **opts), config_file)
77
77
  end
78
78
 
79
79
  # Return a recursive merge of two hashes. That is, a normal hash merge,
@@ -92,7 +92,7 @@ module RuboCop
92
92
  elsif should_union?(base_hash, key, opts[:inherit_mode])
93
93
  result[key] = base_hash[key] | derived_hash[key]
94
94
  elsif opts[:debug]
95
- warn_on_duplicate_setting(base_hash, derived_hash, key, opts)
95
+ warn_on_duplicate_setting(base_hash, derived_hash, key, **opts)
96
96
  end
97
97
  end
98
98
  result
@@ -119,6 +119,18 @@ module RuboCop
119
119
  cops: 'Rails/UniqBeforePluck',
120
120
  parameters: 'EnforcedMode',
121
121
  alternative: '`EnforcedMode` has been renamed to `EnforcedStyle`'
122
+ },
123
+ {
124
+ cops: 'Style/MethodCallWithArgsParentheses',
125
+ parameters: 'IgnoredMethodPatterns',
126
+ alternative: '`IgnoredMethodPatterns` has been renamed to ' \
127
+ '`IgnoredPatterns`'
128
+ },
129
+ {
130
+ cops: %w[Performance/Count Performance/Detect],
131
+ parameters: 'SafeMode',
132
+ alternative: '`SafeMode` has been removed. ' \
133
+ 'Use `SafeAutoCorrect` instead.'
122
134
  }
123
135
  ].freeze
124
136
 
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  def disable_offense(node)
41
41
  range = node.location.expression
42
- eol_comment = " # rubocop:disable #{cop_name}"
42
+ eol_comment = " # rubocop:todo #{cop_name}"
43
43
  needed_line_length = range.column +
44
44
  (range.source_line + eol_comment).length
45
45
  if needed_line_length <= max_line_length
@@ -90,7 +90,7 @@ module RuboCop
90
90
 
91
91
  corrector.insert_before(
92
92
  range_with_newline,
93
- "#{leading_whitespace}# rubocop:disable #{cop_name}\n"
93
+ "#{leading_whitespace}# rubocop:todo #{cop_name}\n"
94
94
  )
95
95
  corrector.insert_after(
96
96
  range_with_newline,
@@ -160,10 +160,11 @@ module RuboCop
160
160
  return :uncorrected unless correction
161
161
 
162
162
  @corrections << Correction.new(correction, node, self)
163
+ :corrected
163
164
  elsif disable_uncorrectable?
164
165
  disable_uncorrectable(node)
166
+ :corrected_with_todo
165
167
  end
166
- :corrected
167
168
  end
168
169
 
169
170
  def reason_to_not_correct(node)
@@ -224,8 +225,8 @@ module RuboCop
224
225
 
225
226
  def annotate(message)
226
227
  RuboCop::Cop::MessageAnnotator.new(
227
- config, cop_config, @options
228
- ).annotate(message, name)
228
+ config, cop_name, cop_config, @options
229
+ ).annotate(message)
229
230
  end
230
231
 
231
232
  def file_name_matches_any?(file, parameter, default_result)
@@ -19,10 +19,12 @@ module RuboCop
19
19
  expr = node.respond_to?(:loc) ? node.loc.expression : node
20
20
  return if block_comment_within?(expr)
21
21
 
22
+ taboo_ranges = inside_string_ranges(node)
23
+
22
24
  lambda do |corrector|
23
25
  each_line(expr) do |line_begin_pos|
24
26
  autocorrect_line(corrector, line_begin_pos, expr, column_delta,
25
- heredoc_ranges(node))
27
+ taboo_ranges)
26
28
  end
27
29
  end
28
30
  end
@@ -39,27 +41,48 @@ module RuboCop
39
41
  private
40
42
 
41
43
  def autocorrect_line(corrector, line_begin_pos, expr, column_delta,
42
- heredoc_ranges)
44
+ taboo_ranges)
43
45
  range = calculate_range(expr, line_begin_pos, column_delta)
44
- # We must not change indentation of heredoc strings.
45
- return if heredoc_ranges.any? { |h| within?(range, h) }
46
+ # We must not change indentation of heredoc strings or inside other
47
+ # string literals
48
+ return if taboo_ranges.any? { |t| within?(range, t) }
46
49
 
47
50
  if column_delta.positive?
48
- unless range.source == "\n"
49
- # TODO: Fix ranges instead of using `begin`
50
- corrector.insert_before(range.begin, ' ' * column_delta)
51
+ unless range.resize(1).source == "\n"
52
+ corrector.insert_before(range, ' ' * column_delta)
51
53
  end
52
54
  elsif range.source =~ /\A[ \t]+\z/
53
55
  remove(range, corrector)
54
56
  end
55
57
  end
56
58
 
57
- def heredoc_ranges(node)
59
+ def inside_string_ranges(node)
58
60
  return [] unless node.is_a?(Parser::AST::Node)
59
61
 
60
- node.each_node(:dstr)
61
- .select(&:heredoc?)
62
- .map { |n| n.loc.heredoc_body.join(n.loc.heredoc_end) }
62
+ node.each_node(:str, :dstr, :xstr).map { |n| inside_string_range(n) }
63
+ .compact
64
+ end
65
+
66
+ def inside_string_range(node)
67
+ loc = node.location
68
+
69
+ if node.heredoc?
70
+ loc.heredoc_body.join(loc.heredoc_end)
71
+ elsif delimited_string_literal?(node)
72
+ loc.begin.end.join(loc.end.begin)
73
+ end
74
+ end
75
+
76
+ # Some special kinds of string literals are not composed of literal
77
+ # characters between two delimiters:
78
+ # - The source map of `?a` responds to :begin and :end but its end is
79
+ # nil.
80
+ # - The source map of `__FILE__` responds to neither :begin nor :end.
81
+ def delimited_string_literal?(node)
82
+ loc = node.location
83
+
84
+ loc.respond_to?(:begin) && loc.begin &&
85
+ loc.respond_to?(:end) && loc.end
63
86
  end
64
87
 
65
88
  def block_comment_within?(expr)
@@ -69,15 +92,18 @@ module RuboCop
69
92
  end
70
93
 
71
94
  def calculate_range(expr, line_begin_pos, column_delta)
95
+ if column_delta.positive?
96
+ return range_between(line_begin_pos, line_begin_pos)
97
+ end
98
+
72
99
  starts_with_space =
73
100
  expr.source_buffer.source[line_begin_pos].start_with?(' ')
74
- pos_to_remove = if column_delta.positive? || starts_with_space
75
- line_begin_pos
76
- else
77
- line_begin_pos - column_delta.abs
78
- end
79
101
 
80
- range_between(pos_to_remove, pos_to_remove + column_delta.abs)
102
+ if starts_with_space
103
+ range_between(line_begin_pos, line_begin_pos + column_delta.abs)
104
+ else
105
+ range_between(line_begin_pos - column_delta.abs, line_begin_pos)
106
+ end
81
107
  end
82
108
 
83
109
  def remove(range, corrector)
@@ -40,8 +40,8 @@ module RuboCop
40
40
  range_with_surrounding_space(range: node.loc.end, side: :left)
41
41
  )
42
42
 
43
- corrector.insert_after(
44
- last_element_range_with_trailing_comma(node),
43
+ corrector.insert_before(
44
+ last_element_range_with_trailing_comma(node).end,
45
45
  node.loc.end.source
46
46
  )
47
47
  end
@@ -98,7 +98,7 @@ module RuboCop
98
98
  end
99
99
 
100
100
  def fix_escaped_content(word_node, escape, delimiters)
101
- content = word_node.children.first.to_s
101
+ content = +word_node.children.first.to_s
102
102
  content = escape_string(content) if escape
103
103
  substitute_escaped_delimiters(content, delimiters)
104
104
  content