rubocop 1.32.0 → 1.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +45 -16
  4. data/config/obsoletion.yml +23 -1
  5. data/lib/rubocop/cache_config.rb +29 -0
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
  7. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  8. data/lib/rubocop/config_finder.rb +68 -0
  9. data/lib/rubocop/config_loader.rb +5 -45
  10. data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
  11. data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
  12. data/lib/rubocop/config_obsoletion.rb +7 -2
  13. data/lib/rubocop/cop/layout/block_end_newline.rb +32 -5
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  15. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +21 -8
  16. data/lib/rubocop/cop/lint/debugger.rb +11 -1
  17. data/lib/rubocop/cop/lint/empty_conditional_body.rb +60 -1
  18. data/lib/rubocop/cop/lint/number_conversion.rb +24 -8
  19. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  20. data/lib/rubocop/cop/metrics/block_length.rb +6 -7
  21. data/lib/rubocop/cop/metrics/method_length.rb +8 -8
  22. data/lib/rubocop/cop/mixin/allowed_methods.rb +15 -1
  23. data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -1
  24. data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
  25. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -9
  26. data/lib/rubocop/cop/naming/predicate_name.rb +24 -3
  27. data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
  28. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
  29. data/lib/rubocop/cop/style/class_equality_comparison.rb +32 -7
  30. data/lib/rubocop/cop/style/empty_heredoc.rb +15 -1
  31. data/lib/rubocop/cop/style/format_string_token.rb +21 -8
  32. data/lib/rubocop/cop/style/hash_except.rb +0 -4
  33. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
  34. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
  35. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -6
  36. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -8
  37. data/lib/rubocop/cop/style/redundant_condition.rb +19 -4
  38. data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
  39. data/lib/rubocop/cop/style/symbol_proc.rb +29 -9
  40. data/lib/rubocop/result_cache.rb +22 -20
  41. data/lib/rubocop/server/cache.rb +33 -1
  42. data/lib/rubocop/server/cli.rb +19 -2
  43. data/lib/rubocop/version.rb +1 -1
  44. data/lib/rubocop.rb +0 -1
  45. metadata +5 -4
  46. data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 270faf06487f8a915ef6293944db136da6fb51ec09b5613be4b848a471c858bb
4
- data.tar.gz: 1c61746959019453862c2e90b8d3250ce3f9815b1eae286f1fd5b73fb267a1cb
3
+ metadata.gz: 141b5fc0ee48c7fdf1fc347bb2b59b59933f6d46f575cfb4f380bfc101090345
4
+ data.tar.gz: 862c5d1952e777ec19bd1b8782c9e98e065c3d80db7a7d2c91da4406afeb886c
5
5
  SHA512:
6
- metadata.gz: 86966e7ff42d0f514a447c8313cc9bdc13020ec1d375e584dba8ebae5bb535a1238380a57d55dfd697c735b3f9259f59939563f4b504f08570304e4ec1c2c7af
7
- data.tar.gz: ce35c98b8b51b8b59c6c2aeac880a0618067a68f7080a2e44408a7e283471b2b57f84e57c127144632b61eaa8edf6e4369d4702c2baa222cb3b37fad38606bd5
6
+ metadata.gz: 2be012822408e3aa884dfe85e8a2153144f53d47550a0f7233a60448a8ab18c5c5411959742a4cb9652e972451f05cf0d447451eba53e46260baefa8f2c234e6
7
+ data.tar.gz: 063e8ef0056793c86aee9f952db454eac5422c369bdf1ce4a719fcc21279214b86a800f58229b0ade17b963fb2c8e49608bd1883c840d8370b207b3aa4ee916c
data/README.md CHANGED
@@ -46,14 +46,14 @@ If you'd rather install RuboCop using `bundler`, add a line for it in your `Gemf
46
46
  gem 'rubocop', require: false
47
47
  ```
48
48
 
49
- RuboCop is stable between major versions, both in terms of API and cop configuration.
49
+ RuboCop is stable between minor versions, both in terms of API and cop configuration.
50
50
  We aim to ease the maintenance of RuboCop extensions and the upgrades between RuboCop
51
51
  releases. All big changes are reserved for major releases.
52
52
  To prevent an unwanted RuboCop update you might want to use a conservative version lock
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.32', require: false
56
+ gem 'rubocop', '~> 1.33', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1502,7 +1502,9 @@ Lint/AmbiguousBlockAssociation:
1502
1502
  Enabled: true
1503
1503
  VersionAdded: '0.48'
1504
1504
  VersionChanged: '1.13'
1505
- IgnoredMethods: []
1505
+ AllowedMethods: []
1506
+ AllowedPatterns: []
1507
+ IgnoredMethods: [] # deprecated
1506
1508
 
1507
1509
  Lint/AmbiguousOperator:
1508
1510
  Description: >-
@@ -1753,6 +1755,7 @@ Lint/EmptyConditionalBody:
1753
1755
  Enabled: true
1754
1756
  AllowComments: true
1755
1757
  VersionAdded: '0.89'
1758
+ VersionChanged: '1.33'
1756
1759
 
1757
1760
  Lint/EmptyEnsure:
1758
1761
  Description: 'Checks for empty ensure block.'
@@ -1970,6 +1973,7 @@ Lint/NoReturnInBeginEndBlocks:
1970
1973
 
1971
1974
  Lint/NonAtomicFileOperation:
1972
1975
  Description: Checks for non-atomic file operations.
1976
+ StyleGuide: '#atomic-file-operations'
1973
1977
  Enabled: pending
1974
1978
  VersionAdded: '1.31'
1975
1979
  SafeAutoCorrect: false
@@ -1991,7 +1995,9 @@ Lint/NumberConversion:
1991
1995
  VersionAdded: '0.53'
1992
1996
  VersionChanged: '1.1'
1993
1997
  SafeAutoCorrect: false
1994
- IgnoredMethods: []
1998
+ AllowedMethods: []
1999
+ AllowedPatterns: []
2000
+ IgnoredMethods: [] # deprecated
1995
2001
  IgnoredClasses:
1996
2002
  - Time
1997
2003
  - DateTime
@@ -2443,7 +2449,9 @@ Metrics/AbcSize:
2443
2449
  VersionChanged: '1.5'
2444
2450
  # The ABC size is a calculated magnitude, so this number can be an Integer or
2445
2451
  # a Float.
2446
- IgnoredMethods: []
2452
+ AllowedMethods: []
2453
+ AllowedPatterns: []
2454
+ IgnoredMethods: [] # deprecated
2447
2455
  CountRepeatedAttributes: true
2448
2456
  Max: 17
2449
2457
 
@@ -2456,10 +2464,12 @@ Metrics/BlockLength:
2456
2464
  Max: 25
2457
2465
  CountAsOne: []
2458
2466
  ExcludedMethods: [] # deprecated, retained for backwards compatibility
2459
- IgnoredMethods:
2467
+ AllowedMethods:
2460
2468
  # By default, exclude the `#refine` method, as it tends to have larger
2461
2469
  # associated blocks.
2462
2470
  - refine
2471
+ AllowedPatterns: []
2472
+ IgnoredMethods: [] # deprecated
2463
2473
  Exclude:
2464
2474
  - '**/*.gemspec'
2465
2475
 
@@ -2489,7 +2499,9 @@ Metrics/CyclomaticComplexity:
2489
2499
  Enabled: true
2490
2500
  VersionAdded: '0.25'
2491
2501
  VersionChanged: '0.81'
2492
- IgnoredMethods: []
2502
+ AllowedMethods: []
2503
+ AllowedPatterns: []
2504
+ IgnoredMethods: [] # deprecated
2493
2505
  Max: 7
2494
2506
 
2495
2507
  Metrics/MethodLength:
@@ -2502,7 +2514,9 @@ Metrics/MethodLength:
2502
2514
  Max: 10
2503
2515
  CountAsOne: []
2504
2516
  ExcludedMethods: [] # deprecated, retained for backwards compatibility
2505
- IgnoredMethods: []
2517
+ AllowedMethods: []
2518
+ AllowedPatterns: []
2519
+ IgnoredMethods: [] # deprecated
2506
2520
 
2507
2521
  Metrics/ModuleLength:
2508
2522
  Description: 'Avoid modules longer than 100 lines of code.'
@@ -2530,7 +2544,9 @@ Metrics/PerceivedComplexity:
2530
2544
  Enabled: true
2531
2545
  VersionAdded: '0.25'
2532
2546
  VersionChanged: '0.81'
2533
- IgnoredMethods: []
2547
+ AllowedMethods: []
2548
+ AllowedPatterns: []
2549
+ IgnoredMethods: [] # deprecated
2534
2550
  Max: 8
2535
2551
 
2536
2552
  ################## Migration #############################
@@ -3061,7 +3077,7 @@ Style/BlockDelimiters:
3061
3077
  # This looks at the usage of a block's method to determine its type (e.g. is
3062
3078
  # the result of a `map` assigned to a variable or passed to another
3063
3079
  # method) but exceptions are permitted in the `ProceduralMethods`,
3064
- # `FunctionalMethods` and `IgnoredMethods` sections below.
3080
+ # `FunctionalMethods` and `AllowedMethods` sections below.
3065
3081
  - semantic
3066
3082
  # The `braces_for_chaining` style enforces braces around single line blocks
3067
3083
  # and do..end around multi-line blocks, except for multi-line blocks whose
@@ -3102,7 +3118,7 @@ Style/BlockDelimiters:
3102
3118
  - let!
3103
3119
  - subject
3104
3120
  - watch
3105
- IgnoredMethods:
3121
+ AllowedMethods:
3106
3122
  # Methods that can be either procedural or functional and cannot be
3107
3123
  # categorised from their usage alone, e.g.
3108
3124
  #
@@ -3119,6 +3135,8 @@ Style/BlockDelimiters:
3119
3135
  - lambda
3120
3136
  - proc
3121
3137
  - it
3138
+ AllowedPatterns: []
3139
+ IgnoredMethods: [] # deprecated
3122
3140
  # The AllowBracesOnProceduralOneLiners option is ignored unless the
3123
3141
  # EnforcedStyle is set to `semantic`. If so:
3124
3142
  #
@@ -3222,10 +3240,12 @@ Style/ClassEqualityComparison:
3222
3240
  StyleGuide: '#instance-of-vs-class-comparison'
3223
3241
  Enabled: true
3224
3242
  VersionAdded: '0.93'
3225
- IgnoredMethods:
3243
+ AllowedMethods:
3226
3244
  - ==
3227
3245
  - equal?
3228
3246
  - eql?
3247
+ AllowedPatterns: []
3248
+ IgnoredMethods: [] # deprecated
3229
3249
 
3230
3250
  Style/ClassMethods:
3231
3251
  Description: 'Use self when defining module/class methods.'
@@ -3687,7 +3707,9 @@ Style/FormatStringToken:
3687
3707
  MaxUnannotatedPlaceholdersAllowed: 1
3688
3708
  VersionAdded: '0.49'
3689
3709
  VersionChanged: '1.0'
3690
- IgnoredMethods: []
3710
+ AllowedMethods: []
3711
+ AllowedPatterns: []
3712
+ IgnoredMethods: [] # deprecated
3691
3713
 
3692
3714
  Style/FrozenStringLiteralComment:
3693
3715
  Description: >-
@@ -4006,7 +4028,8 @@ Style/MethodCallWithArgsParentheses:
4006
4028
  VersionAdded: '0.47'
4007
4029
  VersionChanged: '1.7'
4008
4030
  IgnoreMacros: true
4009
- IgnoredMethods: []
4031
+ AllowedMethods: []
4032
+ IgnoredMethods: [] # deprecated
4010
4033
  AllowedPatterns: []
4011
4034
  IgnoredPatterns: [] # deprecated
4012
4035
  IncludedMacros: []
@@ -4023,7 +4046,9 @@ Style/MethodCallWithoutArgsParentheses:
4023
4046
  Description: 'Do not use parentheses for method calls with no arguments.'
4024
4047
  StyleGuide: '#method-invocation-parens'
4025
4048
  Enabled: true
4026
- IgnoredMethods: []
4049
+ AllowedMethods: []
4050
+ AllowedPatterns: []
4051
+ IgnoredMethods: [] # deprecated
4027
4052
  VersionAdded: '0.47'
4028
4053
  VersionChanged: '0.55'
4029
4054
 
@@ -4393,7 +4418,9 @@ Style/NumericPredicate:
4393
4418
  SupportedStyles:
4394
4419
  - predicate
4395
4420
  - comparison
4396
- IgnoredMethods: []
4421
+ AllowedMethods: []
4422
+ AllowedPatterns: []
4423
+ IgnoredMethods: [] # deprecated
4397
4424
  # Exclude RSpec specs because assertions like `expect(1).to be > 0` cause
4398
4425
  # false positives.
4399
4426
  Exclude:
@@ -5030,11 +5057,13 @@ Style/SymbolProc:
5030
5057
  VersionAdded: '0.26'
5031
5058
  VersionChanged: '1.28'
5032
5059
  AllowMethodsWithArguments: false
5033
- # A list of method names to be ignored by the check.
5060
+ # A list of method names to be always allowed by the check.
5034
5061
  # The names should be fairly unique, otherwise you'll end up ignoring lots of code.
5035
- IgnoredMethods:
5062
+ AllowedMethods:
5036
5063
  - respond_to
5037
5064
  - define_method
5065
+ AllowedPatterns: []
5066
+ IgnoredMethods: [] # deprecated
5038
5067
  AllowComments: false
5039
5068
 
5040
5069
  Style/TernaryParentheses:
@@ -187,7 +187,9 @@ changed_parameters:
187
187
  - Metrics/BlockLength
188
188
  - Metrics/MethodLength
189
189
  parameters: ExcludedMethods
190
- alternative: IgnoredMethods
190
+ alternatives:
191
+ - AllowedMethods
192
+ - AllowedPatterns
191
193
  severity: warning
192
194
  - cops: Lint/Debugger
193
195
  parameters: DebuggerReceivers
@@ -202,6 +204,26 @@ changed_parameters:
202
204
  parameters: IgnoredPatterns
203
205
  alternative: AllowedPatterns
204
206
  severity: warning
207
+ - cops:
208
+ - Lint/AmbiguousBlockAssociation
209
+ - Lint/NumberConversion
210
+ - Metrics/AbcSize
211
+ - Metrics/BlockLength
212
+ - Metrics/CyclomaticComplexity
213
+ - Metrics/MethodLength
214
+ - Metrics/PerceivedComplexity
215
+ - Style/BlockDelimiters
216
+ - Style/ClassEqualityComparison
217
+ - Style/FormatStringToken
218
+ - Style/MethodCallWithArgsParentheses
219
+ - Style/MethodCallWithoutArgsParentheses
220
+ - Style/NumericPredicate
221
+ - Style/SymbolLiteral
222
+ parameters: IgnoredMethods
223
+ alternatives:
224
+ - AllowedMethods
225
+ - AllowedPatterns
226
+ severity: warning
205
227
 
206
228
  # Enforced styles that have been removed or replaced
207
229
  changed_enforced_styles:
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This class represents the cache config of the caching RuboCop runs.
5
+ # @api private
6
+ class CacheConfig
7
+ def self.root_dir
8
+ root = ENV.fetch('RUBOCOP_CACHE_ROOT', nil)
9
+ root ||= yield
10
+ root ||= if ENV.key?('XDG_CACHE_HOME')
11
+ # Include user ID in the path to make sure the user has write
12
+ # access.
13
+ File.join(ENV.fetch('XDG_CACHE_HOME'), Process.uid.to_s)
14
+ else
15
+ # On FreeBSD, the /home path is a symbolic link to /usr/home
16
+ # and the $HOME environment variable returns the /home path.
17
+ #
18
+ # As $HOME is a built-in environment variable, FreeBSD users
19
+ # always get a warning message.
20
+ #
21
+ # To avoid raising warn log messages on FreeBSD, we retrieve
22
+ # the real path of the home folder.
23
+ File.join(File.realpath(Dir.home), '.cache')
24
+ end
25
+
26
+ File.join(root, 'rubocop_cache')
27
+ end
28
+ end
29
+ end
@@ -98,7 +98,7 @@ module RuboCop
98
98
  def add_inheritance_from_auto_generated_file(config_file)
99
99
  file_string = " #{relative_path_to_todo_from_options_config}"
100
100
 
101
- config_file ||= ConfigLoader::DOTFILE
101
+ config_file ||= ConfigFinder::DOTFILE
102
102
 
103
103
  if File.exist?(config_file)
104
104
  files = Array(ConfigLoader.load_yaml_configuration(config_file)['inherit_from'])
@@ -113,7 +113,7 @@ module RuboCop
113
113
  write_config_file(config_file, file_string, rubocop_yml_contents)
114
114
 
115
115
  puts "Added inheritance from `#{relative_path_to_todo_from_options_config}` " \
116
- "in `#{ConfigLoader::DOTFILE}`."
116
+ "in `#{ConfigFinder::DOTFILE}`."
117
117
  end
118
118
 
119
119
  def existing_configuration(config_file)
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Generate a .rubocop.yml file in the current directory.
7
7
  # @api private
8
8
  class InitDotfile < Base
9
- DOTFILE = ConfigLoader::DOTFILE
9
+ DOTFILE = ConfigFinder::DOTFILE
10
10
 
11
11
  self.command_name = :init
12
12
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'file_finder'
4
+
5
+ module RuboCop
6
+ # This class has methods related to finding configuration path.
7
+ # @api private
8
+ class ConfigFinder
9
+ DOTFILE = '.rubocop.yml'
10
+ XDG_CONFIG = 'config.yml'
11
+ RUBOCOP_HOME = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
12
+ DEFAULT_FILE = File.join(RUBOCOP_HOME, 'config', 'default.yml')
13
+
14
+ class << self
15
+ include FileFinder
16
+
17
+ attr_writer :project_root
18
+
19
+ def find_config_path(target_dir)
20
+ find_project_dotfile(target_dir) || find_user_dotfile || find_user_xdg_config ||
21
+ DEFAULT_FILE
22
+ end
23
+
24
+ # Returns the path RuboCop inferred as the root of the project. No file
25
+ # searches will go past this directory.
26
+ def project_root
27
+ @project_root ||= find_project_root
28
+ end
29
+
30
+ private
31
+
32
+ def find_project_root
33
+ pwd = Dir.pwd
34
+ gems_file = find_last_file_upwards('Gemfile', pwd) || find_last_file_upwards('gems.rb', pwd)
35
+ return unless gems_file
36
+
37
+ File.dirname(gems_file)
38
+ end
39
+
40
+ def find_project_dotfile(target_dir)
41
+ find_file_upwards(DOTFILE, target_dir, project_root)
42
+ end
43
+
44
+ def find_user_dotfile
45
+ return unless ENV.key?('HOME')
46
+
47
+ file = File.join(Dir.home, DOTFILE)
48
+
49
+ return file if File.exist?(file)
50
+ end
51
+
52
+ def find_user_xdg_config
53
+ xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
54
+ xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
55
+
56
+ return xdg_config if File.exist?(xdg_config)
57
+ end
58
+
59
+ def expand_path(path)
60
+ File.expand_path(path)
61
+ rescue ArgumentError
62
+ # Could happen because HOME or ID could not be determined. Fall back on
63
+ # using the path literally in that case.
64
+ path
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,6 +3,7 @@
3
3
  require 'erb'
4
4
  require 'yaml'
5
5
  require 'pathname'
6
+ require_relative 'config_finder'
6
7
 
7
8
  module RuboCop
8
9
  # Raised when a RuboCop configuration file is not found.
@@ -15,8 +16,7 @@ module RuboCop
15
16
  # during a run of the rubocop program, if files in several
16
17
  # directories are inspected.
17
18
  class ConfigLoader
18
- DOTFILE = '.rubocop.yml'
19
- XDG_CONFIG = 'config.yml'
19
+ DOTFILE = ConfigFinder::DOTFILE
20
20
  RUBOCOP_HOME = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
21
21
  DEFAULT_FILE = File.join(RUBOCOP_HOME, 'config', 'default.yml')
22
22
 
@@ -25,7 +25,7 @@ module RuboCop
25
25
 
26
26
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
27
27
  :ignore_unrecognized_cops
28
- attr_writer :default_configuration, :project_root
28
+ attr_writer :default_configuration
29
29
  attr_reader :loaded_features
30
30
 
31
31
  alias debug? debug
@@ -95,8 +95,7 @@ module RuboCop
95
95
  # user's home directory is checked. If there's no .rubocop.yml
96
96
  # there either, the path to the default file is returned.
97
97
  def configuration_file_for(target_dir)
98
- find_project_dotfile(target_dir) || find_user_dotfile ||
99
- find_user_xdg_config || DEFAULT_FILE
98
+ ConfigFinder.find_config_path(target_dir)
100
99
  end
101
100
 
102
101
  def configuration_from_file(config_file, check: true)
@@ -122,7 +121,7 @@ module RuboCop
122
121
  end
123
122
 
124
123
  def add_excludes_from_files(config, config_file)
125
- exclusion_file = find_last_file_upwards(DOTFILE, config_file, project_root)
124
+ exclusion_file = find_last_file_upwards(DOTFILE, config_file, ConfigFinder.project_root)
126
125
 
127
126
  return unless exclusion_file
128
127
  return if PathUtil.relative_path(exclusion_file) == PathUtil.relative_path(config_file)
@@ -138,12 +137,6 @@ module RuboCop
138
137
  end
139
138
  end
140
139
 
141
- # Returns the path RuboCop inferred as the root of the project. No file
142
- # searches will go past this directory.
143
- def project_root
144
- @project_root ||= find_project_root
145
- end
146
-
147
140
  PENDING_BANNER = <<~BANNER
148
141
  The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
149
142
 
@@ -187,39 +180,6 @@ module RuboCop
187
180
  File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
188
181
  end
189
182
 
190
- def find_project_dotfile(target_dir)
191
- find_file_upwards(DOTFILE, target_dir, project_root)
192
- end
193
-
194
- def find_project_root
195
- pwd = Dir.pwd
196
- gems_file = find_last_file_upwards('Gemfile', pwd) || find_last_file_upwards('gems.rb', pwd)
197
- return unless gems_file
198
-
199
- File.dirname(gems_file)
200
- end
201
-
202
- def find_user_dotfile
203
- return unless ENV.key?('HOME')
204
-
205
- file = File.join(Dir.home, DOTFILE)
206
- return file if File.exist?(file)
207
- end
208
-
209
- def find_user_xdg_config
210
- xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
211
- xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
212
- return xdg_config if File.exist?(xdg_config)
213
- end
214
-
215
- def expand_path(path)
216
- File.expand_path(path)
217
- rescue ArgumentError
218
- # Could happen because HOME or ID could not be determined. Fall back on
219
- # using the path literally in that case.
220
- path
221
- end
222
-
223
183
  def resolver
224
184
  @resolver ||= ConfigLoaderResolver.new
225
185
  end
@@ -12,6 +12,11 @@ module RuboCop
12
12
 
13
13
  if alternative
14
14
  "#{base}\n`#{parameter}` has been renamed to `#{alternative.chomp}`."
15
+ elsif alternatives
16
+ "#{base}\n`#{parameter}` has been renamed to #{to_sentence(alternatives.map do |item|
17
+ "`#{item}`"
18
+ end,
19
+ connector: 'and/or')}."
15
20
  else
16
21
  "#{base}\n#{reason.chomp}"
17
22
  end
@@ -32,6 +32,10 @@ module RuboCop
32
32
  metadata['alternative']
33
33
  end
34
34
 
35
+ def alternatives
36
+ metadata['alternatives']
37
+ end
38
+
35
39
  def reason
36
40
  metadata['reason']
37
41
  end
@@ -47,10 +47,15 @@ module RuboCop
47
47
 
48
48
  # Default rules for obsoletions are in config/obsoletion.yml
49
49
  # Additional rules files can be added with `RuboCop::ConfigObsoletion.files << filename`
50
- def load_rules
50
+ def load_rules # rubocop:disable Metrics/AbcSize
51
51
  rules = self.class.files.each_with_object({}) do |filename, hash|
52
52
  hash.merge!(YAML.safe_load(File.read(filename))) do |_key, first, second|
53
- first.merge(second)
53
+ case first
54
+ when Hash
55
+ first.merge(second)
56
+ when Array
57
+ first.concat(second)
58
+ end
54
59
  end
55
60
  end
56
61
 
@@ -36,24 +36,51 @@ module RuboCop
36
36
  # If the end is on its own line, there is no offense
37
37
  return if begins_its_line?(node.loc.end)
38
38
 
39
- add_offense(node.loc.end, message: message(node)) do |corrector|
40
- corrector.replace(delimiter_range(node), "\n#{node.loc.end.source}#{offset(node)}")
41
- end
39
+ register_offense(node)
42
40
  end
43
41
 
44
42
  private
45
43
 
44
+ def register_offense(node)
45
+ add_offense(node.loc.end, message: message(node)) do |corrector|
46
+ offense_range = offense_range(node)
47
+ replacement = "\n#{offense_range.source.strip}"
48
+
49
+ if (heredoc = last_heredoc_argument(node.body))
50
+ corrector.remove(offense_range)
51
+ corrector.insert_after(heredoc.loc.heredoc_end, replacement)
52
+ else
53
+ corrector.replace(offense_range, replacement)
54
+ end
55
+ end
56
+ end
57
+
46
58
  def message(node)
47
59
  format(MSG, line: node.loc.end.line, column: node.loc.end.column + 1)
48
60
  end
49
61
 
50
- def delimiter_range(node)
62
+ def last_heredoc_argument(node)
63
+ return unless (arguments = node&.arguments)
64
+
65
+ heredoc = arguments.reverse.detect(&:heredoc?)
66
+ return heredoc if heredoc
67
+
68
+ last_heredoc_argument(node.children.first)
69
+ end
70
+
71
+ def offense_range(node)
51
72
  Parser::Source::Range.new(
52
73
  node.loc.expression.source_buffer,
53
74
  node.children.compact.last.loc.expression.end_pos,
54
- node.loc.expression.end_pos
75
+ end_of_method_chain(node).loc.expression.end_pos
55
76
  )
56
77
  end
78
+
79
+ def end_of_method_chain(node)
80
+ return node unless node.parent&.call_type?
81
+
82
+ end_of_method_chain(node.parent)
83
+ end
57
84
  end
58
85
  end
59
86
  end
@@ -153,7 +153,8 @@ module RuboCop
153
153
  MSG = 'Indent the first argument one step more than %<base>s.'
154
154
 
155
155
  def on_send(node)
156
- return if style != :consistent && enforce_first_argument_with_fixed_indentation?
156
+ return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
157
+ !enable_layout_first_method_argument_line_break?
157
158
  return if !node.arguments? || bare_operator?(node) || node.setter_method?
158
159
 
159
160
  indent = base_indentation(node) + configured_indentation_width
@@ -267,6 +268,10 @@ module RuboCop
267
268
  argument_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
268
269
  end
269
270
 
271
+ def enable_layout_first_method_argument_line_break?
272
+ config.for_cop('Layout/FirstMethodArgumentLineBreak')['Enabled']
273
+ end
274
+
270
275
  def argument_alignment_config
271
276
  config.for_cop('Layout/ArgumentAlignment')
272
277
  end
@@ -6,8 +6,8 @@ module RuboCop
6
6
  # Checks for ambiguous block association with method
7
7
  # when param passed without parentheses.
8
8
  #
9
- # This cop can customize ignored methods with `IgnoredMethods`.
10
- # By default, there are no methods to ignored.
9
+ # This cop can customize allowed methods with `AllowedMethods`.
10
+ # By default, there are no methods to allowed.
11
11
  #
12
12
  # @example
13
13
  #
@@ -30,18 +30,30 @@ module RuboCop
30
30
  # # Lambda arguments require no disambiguation
31
31
  # foo = ->(bar) { bar.baz }
32
32
  #
33
- # @example IgnoredMethods: [] (default)
33
+ # @example AllowedMethods: [] (default)
34
34
  #
35
35
  # # bad
36
36
  # expect { do_something }.to change { object.attribute }
37
37
  #
38
- # @example IgnoredMethods: [change]
38
+ # @example AllowedMethods: [change]
39
39
  #
40
40
  # # good
41
41
  # expect { do_something }.to change { object.attribute }
42
42
  #
43
+ # @example AllowedPatterns: [] (default)
44
+ #
45
+ # # bad
46
+ # expect { do_something }.to change { object.attribute }
47
+ #
48
+ # @example AllowedPatterns: [/change/]
49
+ #
50
+ # # good
51
+ # expect { do_something }.to change { object.attribute }
52
+ # expect { do_something }.to not_change { object.attribute }
53
+ #
43
54
  class AmbiguousBlockAssociation < Base
44
- include IgnoredMethods
55
+ include AllowedMethods
56
+ include AllowedPattern
45
57
 
46
58
  MSG = 'Parenthesize the param `%<param>s` to make sure that the ' \
47
59
  'block will be associated with the `%<method>s` method ' \
@@ -52,7 +64,7 @@ module RuboCop
52
64
 
53
65
  return unless ambiguous_block_association?(node)
54
66
  return if node.parenthesized? || node.last_argument.lambda? || node.last_argument.proc? ||
55
- allowed_method?(node)
67
+ allowed_method_pattern?(node)
56
68
 
57
69
  message = message(node)
58
70
 
@@ -66,9 +78,10 @@ module RuboCop
66
78
  send_node.last_argument.block_type? && !send_node.last_argument.send_node.arguments?
67
79
  end
68
80
 
69
- def allowed_method?(node)
81
+ def allowed_method_pattern?(node)
70
82
  node.assignment? || node.operator_method? || node.method?(:[]) ||
71
- ignored_method?(node.last_argument.send_node.source)
83
+ allowed_method?(node.last_argument.method_name) ||
84
+ matches_allowed_pattern?(node.last_argument.method_name)
72
85
  end
73
86
 
74
87
  def message(send_node)