rubocop 0.34.2 → 0.35.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/README.md +103 -31
  4. data/config/default.yml +32 -2
  5. data/config/disabled.yml +24 -0
  6. data/config/enabled.yml +20 -2
  7. data/lib/rubocop.rb +13 -0
  8. data/lib/rubocop/ast_node.rb +48 -0
  9. data/lib/rubocop/cli.rb +9 -0
  10. data/lib/rubocop/config.rb +8 -6
  11. data/lib/rubocop/config_loader.rb +30 -8
  12. data/lib/rubocop/cop/commissioner.rb +1 -1
  13. data/lib/rubocop/cop/cop.rb +19 -6
  14. data/lib/rubocop/cop/lint/circular_argument_reference.rb +33 -2
  15. data/lib/rubocop/cop/lint/debugger.rb +9 -56
  16. data/lib/rubocop/cop/lint/end_alignment.rb +29 -9
  17. data/lib/rubocop/cop/lint/eval.rb +6 -2
  18. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +24 -6
  19. data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
  20. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +10 -1
  21. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  22. data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
  23. data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
  24. data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
  25. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  26. data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
  27. data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
  28. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
  29. data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
  30. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
  31. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
  32. data/lib/rubocop/cop/mixin/configurable_naming.rb +1 -1
  33. data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
  34. data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
  35. data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
  36. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  37. data/lib/rubocop/cop/performance/detect.rb +5 -1
  38. data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
  39. data/lib/rubocop/cop/performance/size.rb +1 -1
  40. data/lib/rubocop/cop/performance/string_replacement.rb +14 -8
  41. data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
  42. data/lib/rubocop/cop/style/align_hash.rb +1 -12
  43. data/lib/rubocop/cop/style/align_parameters.rb +19 -7
  44. data/lib/rubocop/cop/style/and_or.rb +42 -13
  45. data/lib/rubocop/cop/style/block_comments.rb +4 -2
  46. data/lib/rubocop/cop/style/block_delimiters.rb +57 -18
  47. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  48. data/lib/rubocop/cop/style/command_literal.rb +2 -10
  49. data/lib/rubocop/cop/style/copyright.rb +5 -3
  50. data/lib/rubocop/cop/style/documentation.rb +9 -6
  51. data/lib/rubocop/cop/style/dot_position.rb +6 -0
  52. data/lib/rubocop/cop/style/double_negation.rb +4 -15
  53. data/lib/rubocop/cop/style/each_with_object.rb +17 -4
  54. data/lib/rubocop/cop/style/empty_line_between_defs.rb +1 -5
  55. data/lib/rubocop/cop/style/encoding.rb +10 -4
  56. data/lib/rubocop/cop/style/extra_spacing.rb +23 -13
  57. data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
  58. data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
  59. data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
  60. data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
  61. data/lib/rubocop/cop/style/for.rb +2 -1
  62. data/lib/rubocop/cop/style/if_unless_modifier.rb +31 -0
  63. data/lib/rubocop/cop/style/indent_hash.rb +67 -37
  64. data/lib/rubocop/cop/style/indentation_width.rb +1 -1
  65. data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
  66. data/lib/rubocop/cop/style/method_call_parentheses.rb +8 -0
  67. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
  68. data/lib/rubocop/cop/style/multiline_operation_indentation.rb +8 -13
  69. data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
  70. data/lib/rubocop/cop/style/next.rb +18 -0
  71. data/lib/rubocop/cop/style/parallel_assignment.rb +57 -15
  72. data/lib/rubocop/cop/style/predicate_name.rb +7 -2
  73. data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
  74. data/lib/rubocop/cop/style/single_line_methods.rb +7 -5
  75. data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
  76. data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
  77. data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
  78. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
  79. data/lib/rubocop/cop/style/trailing_comma.rb +9 -6
  80. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
  81. data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
  82. data/lib/rubocop/cop/style/variable_name.rb +5 -0
  83. data/lib/rubocop/cop/style/word_array.rb +2 -1
  84. data/lib/rubocop/cop/team.rb +17 -4
  85. data/lib/rubocop/cop/util.rb +5 -0
  86. data/lib/rubocop/cop/variable_force/locatable.rb +1 -1
  87. data/lib/rubocop/formatter/base_formatter.rb +1 -1
  88. data/lib/rubocop/formatter/disabled_config_formatter.rb +22 -10
  89. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  90. data/lib/rubocop/node_pattern.rb +390 -0
  91. data/lib/rubocop/options.rb +48 -36
  92. data/lib/rubocop/processed_source.rb +3 -1
  93. data/lib/rubocop/rake_task.rb +1 -1
  94. data/lib/rubocop/remote_config.rb +60 -0
  95. data/lib/rubocop/result_cache.rb +4 -2
  96. data/lib/rubocop/runner.rb +33 -10
  97. data/lib/rubocop/token.rb +2 -1
  98. data/lib/rubocop/version.rb +1 -1
  99. data/lib/rubocop/warning.rb +11 -0
  100. data/relnotes/v0.35.0.md +210 -0
  101. data/rubocop.gemspec +2 -2
  102. metadata +20 -6
@@ -11,6 +11,7 @@ module RuboCop
11
11
 
12
12
  def initialize
13
13
  @options = {}
14
+ @validator = OptionsValidator.new(@options)
14
15
  end
15
16
 
16
17
  def parse(args)
@@ -19,7 +20,7 @@ module RuboCop
19
20
  # `no_color` key, which is created automatically.
20
21
  @options.delete(:no_color)
21
22
 
22
- validate_compatibility
23
+ @validator.validate_compatibility
23
24
 
24
25
  if @options[:stdin] && !args.one?
25
26
  fail ArgumentError, '-s/--stdin requires exactly one path.'
@@ -28,19 +29,6 @@ module RuboCop
28
29
  [@options, args]
29
30
  end
30
31
 
31
- # Cop name validation must be done later than option parsing, so it's not
32
- # called from within this class.
33
- def self.validate_cop_list(names)
34
- return unless names
35
-
36
- namespaces = Cop::Cop.all.types.map { |t| t.to_s.capitalize }
37
- names.each do |name|
38
- next if Cop::Cop.all.any? { |c| c.cop_name == name } ||
39
- namespaces.include?(name)
40
- fail ArgumentError, "Unrecognized cop or namespace: #{name}."
41
- end
42
- end
43
-
44
32
  private
45
33
 
46
34
  def define_options(args)
@@ -60,18 +48,6 @@ module RuboCop
60
48
  end
61
49
  end
62
50
 
63
- def validate_compatibility
64
- if @options.key?(:only) &&
65
- (@options[:only] & %w(Lint/UnneededDisable UnneededDisable)).any?
66
- fail ArgumentError, 'Lint/UnneededDisable can not be used with --only.'
67
- end
68
- if @options.key?(:cache) && !%w(true false).include?(@options[:cache])
69
- fail ArgumentError, '-C/--cache argument must be true or false'
70
- end
71
- return unless (incompat = @options.keys & EXITING_OPTIONS).size > 1
72
- fail ArgumentError, "Incompatible cli options: #{incompat.inspect}"
73
- end
74
-
75
51
  def add_only_options(opts)
76
52
  add_cop_selection_csv_option('except', opts)
77
53
  add_cop_selection_csv_option('only', opts)
@@ -80,9 +56,14 @@ module RuboCop
80
56
 
81
57
  def add_cop_selection_csv_option(option, opts)
82
58
  option(opts, "--#{option} [COP1,COP2,...]") do |list|
83
- @options[:"#{option}"] = list.split(',').map do |c|
84
- Cop::Cop.qualified_cop_name(c, "--#{option} option")
85
- end
59
+ @options[:"#{option}"] =
60
+ if list.empty?
61
+ ['']
62
+ else
63
+ list.split(',').map do |c|
64
+ Cop::Cop.qualified_cop_name(c, "--#{option} option")
65
+ end
66
+ end
86
67
  end
87
68
  end
88
69
 
@@ -90,14 +71,13 @@ module RuboCop
90
71
  option(opts, '-c', '--config FILE')
91
72
 
92
73
  option(opts, '--auto-gen-config') do
93
- validate_auto_gen_config_option(args)
94
74
  @options[:formatters] = [[DEFAULT_FORMATTER],
95
75
  [Formatter::DisabledConfigFormatter,
96
76
  ConfigLoader::AUTO_GENERATED_FILE]]
97
77
  end
98
78
 
99
79
  option(opts, '--exclude-limit COUNT') do
100
- validate_exclude_limit_option(args)
80
+ @validator.validate_exclude_limit_option(args)
101
81
  end
102
82
 
103
83
  option(opts, '--force-exclusion')
@@ -135,6 +115,7 @@ module RuboCop
135
115
  option(opts, '-C', '--cache FLAG')
136
116
  option(opts, '-d', '--debug')
137
117
  option(opts, '-D', '--display-cop-names')
118
+ option(opts, '-E', '--extra-details')
138
119
  option(opts, '-S', '--display-style-guide')
139
120
  option(opts, '-R', '--rails')
140
121
  option(opts, '-l', '--lint') do
@@ -172,13 +153,43 @@ module RuboCop
172
153
  long_opt = args.find { |arg| arg.start_with?('--') }
173
154
  long_opt[2..-1].sub(/ .*/, '').tr('-', '_').to_sym
174
155
  end
156
+ end
157
+
158
+ # Validates option arguments and the options' compatibilty with each other.
159
+ class OptionsValidator
160
+ def initialize(options)
161
+ @options = options
162
+ end
163
+
164
+ # Cop name validation must be done later than option parsing, so it's not
165
+ # called from within Options.
166
+ def self.validate_cop_list(names)
167
+ return unless names
175
168
 
176
- def validate_auto_gen_config_option(args)
177
- return if args.empty?
178
- return if args.size <= 2 && args.first == '--exclude-limit'
169
+ namespaces = Cop::Cop.all.types.map { |t| t.to_s.capitalize }
170
+ names.each do |name|
171
+ next if Cop::Cop.all.any? { |c| c.cop_name == name }
172
+ next if namespaces.include?(name)
173
+ next if %w(Syntax Lint/Syntax).include?(name)
174
+
175
+ fail ArgumentError, "Unrecognized cop or namespace: #{name}."
176
+ end
177
+ end
179
178
 
180
- warn '--auto-gen-config can only be combined with --exclude-limit.'
181
- exit(1)
179
+ def validate_compatibility
180
+ if @options.key?(:only) &&
181
+ (@options[:only] & %w(Lint/UnneededDisable UnneededDisable)).any?
182
+ fail ArgumentError, 'Lint/UnneededDisable can not be used with --only.'
183
+ end
184
+ if @options.key?(:except) &&
185
+ (@options[:except] & %w(Lint/Syntax Syntax)).any?
186
+ fail ArgumentError, 'Syntax checking can not be turned off.'
187
+ end
188
+ if @options.key?(:cache) && !%w(true false).include?(@options[:cache])
189
+ fail ArgumentError, '-C/--cache argument must be true or false'
190
+ end
191
+ return if (incompat = @options.keys & Options::EXITING_OPTIONS).size <= 1
192
+ fail ArgumentError, "Incompatible cli options: #{incompat.inspect}"
182
193
  end
183
194
 
184
195
  def validate_exclude_limit_option(args)
@@ -247,6 +258,7 @@ module RuboCop
247
258
  debug: 'Display debug info.',
248
259
  display_cop_names: 'Display cop names in offense messages.',
249
260
  display_style_guide: 'Display style guide URLs in offense messages.',
261
+ extra_details: 'Display extra details in offense messages.',
250
262
  rails: 'Run extra Rails cops.',
251
263
  lint: 'Run only lint cops.',
252
264
  list_target_files: 'List all files RuboCop will inspect.',
@@ -15,8 +15,10 @@ module RuboCop
15
15
  def self.from_file(path)
16
16
  file = File.read(path)
17
17
  new(file, path)
18
- rescue
18
+ rescue Errno::ENOENT
19
19
  abort("#{Rainbow('rubocop: No such file or directory').red} -- #{path}")
20
+ rescue => ex
21
+ abort("#{Rainbow("rubocop: #{ex.message}").red} -- #{path}")
20
22
  end
21
23
 
22
24
  def initialize(source, path = nil)
@@ -48,7 +48,7 @@ module RuboCop
48
48
  cli = CLI.new
49
49
  puts 'Running RuboCop...' if verbose
50
50
  result = cli.run(options)
51
- abort('RuboCop failed!') if fail_on_error unless result == 0
51
+ abort('RuboCop failed!') if result != 0 && fail_on_error
52
52
  end
53
53
 
54
54
  def full_options
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ require 'net/http'
4
+
5
+ module RuboCop
6
+ # Common methods and behaviors for dealing with remote config files.
7
+ class RemoteConfig
8
+ CACHE_LIFETIME = 24 * 60 * 60
9
+
10
+ def initialize(url)
11
+ @uri = URI.parse(url)
12
+ end
13
+
14
+ def file
15
+ return cache_path unless cache_path_expired?
16
+
17
+ http = Net::HTTP.new(@uri.hostname, @uri.port)
18
+ http.use_ssl = true if @uri.instance_of? URI::HTTPS
19
+
20
+ request = Net::HTTP::Get.new(@uri.request_uri)
21
+ if cache_path_exists?
22
+ request['If-Modified-Since'] = File.stat(cache_path).mtime.rfc2822
23
+ end
24
+ response = http.request(request)
25
+
26
+ cache_path.tap do |f|
27
+ if response.is_a?(Net::HTTPSuccess)
28
+ open f, 'w' do |io|
29
+ io.write response.body
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def cache_path
38
+ ".rubocop-#{cache_name_from_uri}"
39
+ end
40
+
41
+ def cache_path_exists?
42
+ @cache_path_exists ||= File.exist?(cache_path)
43
+ end
44
+
45
+ def cache_path_expired?
46
+ return true unless cache_path_exists?
47
+
48
+ @cache_path_expired ||= begin
49
+ file_age = (Time.now - File.stat(cache_path).mtime).to_f
50
+ (file_age / CACHE_LIFETIME) > 1
51
+ end
52
+ end
53
+
54
+ def cache_name_from_uri
55
+ uri = @uri.clone
56
+ uri.query = nil
57
+ uri.to_s.gsub!(/[^0-9A-Za-z]/, '-')
58
+ end
59
+ end
60
+ end
@@ -3,6 +3,7 @@
3
3
  require 'digest/md5'
4
4
  require 'find'
5
5
  require 'tmpdir'
6
+ require 'etc'
6
7
 
7
8
  module RuboCop
8
9
  # Provides functionality for caching rubocop runs.
@@ -11,7 +12,8 @@ module RuboCop
11
12
  # collisions.
12
13
  def initialize(file, options, config_store, cache_root = nil)
13
14
  cache_root ||= ResultCache.cache_root(config_store)
14
- @path = File.join(cache_root, rubocop_checksum, relevant_options(options),
15
+ @path = File.join(cache_root, rubocop_checksum, RUBY_VERSION,
16
+ relevant_options(options),
15
17
  file_checksum(file, config_store))
16
18
  end
17
19
 
@@ -77,7 +79,7 @@ module RuboCop
77
79
 
78
80
  def self.cache_root(config_store)
79
81
  root = config_store.for('.')['AllCops']['CacheRootDirectory']
80
- root = Dir.tmpdir if root == '/tmp'
82
+ root = File.join(Dir.tmpdir, Etc.getlogin) if root == '/tmp'
81
83
  File.join(root, 'rubocop_cache')
82
84
  end
83
85
 
@@ -15,13 +15,14 @@ module RuboCop
15
15
  end
16
16
  end
17
17
 
18
- attr_reader :errors, :aborting
18
+ attr_reader :errors, :warnings, :aborting
19
19
  alias_method :aborting?, :aborting
20
20
 
21
21
  def initialize(options, config_store)
22
22
  @options = options
23
23
  @config_store = config_store
24
24
  @errors = []
25
+ @warnings = []
25
26
  @aborting = false
26
27
  end
27
28
 
@@ -82,13 +83,16 @@ module RuboCop
82
83
  file_started(file, disabled_line_ranges, comments)
83
84
  else
84
85
  processed_source = get_processed_source(file)
85
- file_started(file, processed_source.disabled_line_ranges,
86
- processed_source.comments)
87
- offenses = do_inspection_loop(file, processed_source)
88
- if cache
89
- cache.save(offenses, processed_source.disabled_line_ranges,
90
- processed_source.comments)
91
- end
86
+ # Use delegators for objects sent to the formatters. These can be
87
+ # updated when the file is re-inspected.
88
+ disabled_line_ranges =
89
+ SimpleDelegator.new(processed_source.disabled_line_ranges)
90
+ comments = SimpleDelegator.new(processed_source.comments)
91
+
92
+ file_started(file, disabled_line_ranges, comments)
93
+ offenses = do_inspection_loop(file, processed_source,
94
+ disabled_line_ranges, comments)
95
+ save_in_cache(cache, offenses, processed_source)
92
96
  end
93
97
 
94
98
  offenses = formatter_set.file_finished(file, offenses.compact.sort.freeze)
@@ -120,7 +124,19 @@ module RuboCop
120
124
  !@options[:auto_correct]
121
125
  end
122
126
 
123
- def do_inspection_loop(file, processed_source)
127
+ def save_in_cache(cache, offenses, processed_source)
128
+ return unless cache
129
+ # Caching results when a cop has crashed would prevent the crash in the
130
+ # next run, since the cop would not be called then. We want crashes to
131
+ # show up the same in each run.
132
+ return if errors.any? || warnings.any?
133
+
134
+ cache.save(offenses, processed_source.disabled_line_ranges,
135
+ processed_source.comments)
136
+ end
137
+
138
+ def do_inspection_loop(file, processed_source, disabled_line_ranges,
139
+ comments)
124
140
  offenses = []
125
141
 
126
142
  # Keep track of the state of the source. If a cop modifies the source
@@ -147,6 +163,10 @@ module RuboCop
147
163
  break unless updated_source_file
148
164
 
149
165
  processed_source = get_processed_source(file)
166
+
167
+ # Update delegators with new objects.
168
+ disabled_line_ranges.__setobj__(processed_source.disabled_line_ranges)
169
+ comments.__setobj__(processed_source.comments)
150
170
  end
151
171
 
152
172
  offenses
@@ -169,6 +189,7 @@ module RuboCop
169
189
  team = Cop::Team.new(mobilized_cop_classes(config), config, @options)
170
190
  offenses = team.inspect_file(processed_source)
171
191
  @errors.concat(team.errors)
192
+ @warnings.concat(team.warnings)
172
193
  [offenses, team.updated_source_file?]
173
194
  end
174
195
 
@@ -177,7 +198,9 @@ module RuboCop
177
198
  @mobilized_cop_classes[config.object_id] ||= begin
178
199
  cop_classes = Cop::Cop.all
179
200
 
180
- [:only, :except].each { |opt| Options.validate_cop_list(@options[opt]) }
201
+ [:only, :except].each do |opt|
202
+ OptionsValidator.validate_cop_list(@options[opt])
203
+ end
181
204
 
182
205
  if @options[:only]
183
206
  cop_classes.select! { |c| c.match?(@options[:only]) }
@@ -14,7 +14,8 @@ module RuboCop
14
14
  def initialize(pos, type, text)
15
15
  @pos = pos
16
16
  @type = type
17
- @text = text
17
+ # Parser token "text" may be a Fixnum
18
+ @text = text.to_s
18
19
  end
19
20
 
20
21
  def to_s
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.34.2'
6
+ STRING = '0.35.0'
7
7
 
8
8
  MSG = '%s (using Parser %s, running on %s %s %s)'
9
9
 
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ # A Warning exception is different from an Offense with severity 'warning'
5
+ # When a Warning is raised, this means that RuboCop was unable to perform a
6
+ # requested operation (such as inspecting or correcting a source file) due to
7
+ # user error
8
+ # For example, a configuration value in .rubocop.yml might be malformed
9
+ class Warning < StandardError
10
+ end
11
+ end
@@ -0,0 +1,210 @@
1
+ Special thanks to [Alex Dowad](https://github.com/alexdowad) who
2
+ contributed an amazing number of bug fixes and improvements for this
3
+ release! Much appreciated!
4
+
5
+ And happy birthday to my beloved granny Vasilka, who turns today 78 years!
6
+ This release is dedicated to her!
7
+
8
+ ### New features
9
+
10
+ * [#2028](https://github.com/bbatsov/rubocop/issues/2028): New config `ExtraDetails` supports addition of `Details` param to all cops to allow extra details on offense to be displayed. ([@tansaku][])
11
+ * [#2036](https://github.com/bbatsov/rubocop/issues/2036): New cop `Style/StabbyLambdaParentheses` will find and correct cases where a stabby lambda's paramaters are not wrapped in parentheses. ([@hmadison][])
12
+ * [#2246](https://github.com/bbatsov/rubocop/pull/2246): `Style/TrailingUnderscoreVariable` will now register an offense for `*_`. ([@rrosenblum][])
13
+ * [#2246](https://github.com/bbatsov/rubocop/pull/2246): `Style/TrailingUnderscoreVariable` now has a configuration to remove named underscore variables (Defaulted to false). ([@rrosenblum][])
14
+ * [#2276](https://github.com/bbatsov/rubocop/pull/2276): New cop `Performance/FixedSize` will register an offense when calling `length`, `size`, or `count` on statically sized objected (strings, symbols, arrays, and hashes). ([@rrosenblum][])
15
+ * New cop `Style/NestedModifier` checks for nested `if`, `unless`, `while` and `until` modifier statements. ([@lumeet][])
16
+ * [#2270](https://github.com/bbatsov/rubocop/pull/2270): Add a new `inherit_gem` configuration to inherit a config file from an installed gem [(originally requested in #290)](https://github.com/bbatsov/rubocop/issues/290). ([@jhansche][])
17
+ * Allow `StyleGuide` parameters in local configuration for all cops, so users can add references to custom style guide documents. ([@cornelius][])
18
+ * `UnusedMethodArgument` cop allows configuration to skip keyword arguments. ([@apiology][])
19
+ * [#2318](https://github.com/bbatsov/rubocop/pull/2318): `Lint/Debugger` cop now checks for `Pry.rescue`. ([@rrosenblum][])
20
+ * [#2277](https://github.com/bbatsov/rubocop/pull/2277): New cop `Style/FirstArrayElementLineBreak` checks for a line break before the first element in a multi-line array. ([@panthomakos][])
21
+ * [#2277](https://github.com/bbatsov/rubocop/pull/2277): New cop `Style/FirstHashElementLineBreak` checks for a line break before the first element in a multi-line hash. ([@panthomakos][])
22
+ * [#2277](https://github.com/bbatsov/rubocop/pull/2277): New cop `Style/FirstMethodArgumentLineBreak` checks for a line break before the first argument in a multi-line method call. ([@panthomakos][])
23
+ * [#2277](https://github.com/bbatsov/rubocop/pull/2277): New cop `Style/FirstMethodParameterLineBreak` checks for a line break before the first parameter in a multi-line method parameter definition. ([@panthomakos][])
24
+ * Add `Rails/PluralizationGrammar` cop, checks for incorrect grammar when using methods like `3.day.ago`, when you should write `3.days.ago`. ([@maxjacobson][])
25
+ * [#2347](https://github.com/bbatsov/rubocop/pull/2347): `Lint/Eval` cop does not warn about "security risk" when eval argument is a string literal without interpolations. ([@alexdowad][])
26
+ * [#2335](https://github.com/bbatsov/rubocop/issues/2335): `Style/VariableName` cop checks naming style of method parameters. ([@alexdowad][])
27
+ * [#2329](https://github.com/bbatsov/rubocop/pull/2329): New style `braces_for_chaining` for `Style/BlockDelimiters` cop enforces braces on a multi-line block if its return value is being chained with another method. ([@panthomakos][])
28
+ * `Lint/LiteralInCondition` warns if a symbol or dynamic symbol is used as a condition. ([@alexdowad][])
29
+ * [#2369](https://github.com/bbatsov/rubocop/issues/2369): `Style/TrailingComma` doesn't add a trailing comma to a multiline method chain which is the only arg to a method call. ([@alexdowad][])
30
+ * `CircularArgumentReference` cop updated to lint for ordinal circular argument references on top of optional keyword arguments. ([@maxjacobson][])
31
+ * Added ability to download shared rubocop config files from remote urls. ([@ptrippett][])
32
+ * [#1601](https://github.com/bbatsov/rubocop/issues/1601): Add `IgnoreEmptyMethods` config parameter for `Lint/UnusedMethodArgument` and `IgnoreEmptyBlocks` config parameter for `Lint/UnusedBlockArgument` cops. ([@alexdowad][])
33
+ * [#1729](https://github.com/bbatsov/rubocop/issues/1729): `Style/MethodDefParentheses` supports new 'require_no_parentheses_except_multiline' style. ([@alexdowad][])
34
+ * [#2173](https://github.com/bbatsov/rubocop/issues/2173): `Style/AlignParameters` also checks parameter alignment for method definitions. ([@alexdowad][])
35
+ * [#1825](https://github.com/bbatsov/rubocop/issues/1825): New `NameWhitelist` configuration parameter for `Style/PredicateName` can be used to suppress errors on known-good predicate names. ([@alexdowad][])
36
+ * `Style/Documentation` recognizes 'Constant = Class.new' as a class definition. ([@alexdowad][])
37
+ * [#1608](https://github.com/bbatsov/rubocop/issues/1608): Add new 'align_braces' style for `Style/IndentHash`. ([@alexdowad][])
38
+ * `Style/Next` can autocorrect. ([@alexdowad][])
39
+
40
+ ### Bug Fixes
41
+
42
+ * [#2265](https://github.com/bbatsov/rubocop/issues/2265): Handle unary `+` in `ExtraSpacing` cop. ([@jonas054][])
43
+ * [#2275](https://github.com/bbatsov/rubocop/pull/2275): Copy default `Exclude` into `Exclude` lists in `.rubocop_todo.yml`. ([@jonas054][])
44
+ * `Style/IfUnlessModifier` accepts blocks followed by a chained call. ([@lumeet][])
45
+ * [#2261](https://github.com/bbatsov/rubocop/issues/2261): Make relative `Exclude` paths in `$HOME/.rubocop_todo.yml` be relative to current directory. ([@jonas054][])
46
+ * [#2286](https://github.com/bbatsov/rubocop/issues/2286): Handle auto-correction of empty method when `AllowIfMethodIsEmpty` is `false` in `Style/SingleLineMethods`. ([@jonas054][])
47
+ * [#2246](https://github.com/bbatsov/rubocop/pull/2246): Do not register an offense for `Style/TrailingUnderscoreVariable` when the underscore variable is preceeded by a splat variable. ([@rrosenblum][])
48
+ * [#2292](https://github.com/bbatsov/rubocop/pull/2292): Results should not be stored in the cache if affected by errors (crashes). ([@jonas054][])
49
+ * [#2280](https://github.com/bbatsov/rubocop/issues/2280): Avoid reporting space between hash literal keys and values in `Style/ExtraSpacing`. ([@jonas054][])
50
+ * [#2284](https://github.com/bbatsov/rubocop/issues/2284): Fix result cache being shared between ruby versions. ([@miquella][])
51
+ * [#2285](https://github.com/bbatsov/rubocop/issues/2285): Fix `ConfigurableNaming#class_emitter_method?` error when handling singleton class methods. ([@palkan][])
52
+ * [#2295](https://github.com/bbatsov/rubocop/issues/2295): Fix Performance/Detect autocorrect to handle rogue newlines. ([@palkan][])
53
+ * [#2294](https://github.com/bbatsov/rubocop/issues/2294): Do not register an offense in `Performance/StringReplacement` for regex with options. ([@rrosenblum][])
54
+ * Fix `Style/UnneededPercentQ` condition for single-quoted literal containing interpolation-like string. ([@eagletmt][])
55
+ * [#2324](https://github.com/bbatsov/rubocop/issues/2324): Handle `--only Lint/Syntax` and `--except Lint/Syntax` correctly. ([@jonas054][])
56
+ * [#2317](https://github.com/bbatsov/rubocop/issues/2317): Handle `case` as an argument correctly in `Lint/EndAlignment`. ([@lumeet][])
57
+ * [#2287](https://github.com/bbatsov/rubocop/issues/2287): Fix auto-correct of lines with only whitespace in `Style/IndentationWidth`. ([@lumeet][])
58
+ * [#2331](https://github.com/bbatsov/rubocop/issues/2331): Do not register an offense in `Performance/Size` for `count` with an argument. ([@rrosenblum][])
59
+ * Handle a backslash at the end of a line in `Style/SpaceAroundOperators`. ([@lumeet][])
60
+ * Don't warn about lack of "leading space" in a =begin/=end comment. ([@alexdowad][])
61
+ * [#2307](https://github.com/bbatsov/rubocop/issues/2307): In `Lint/FormatParameterMismatch`, don't register an offense if either argument to % is not a literal. ([@alexdowad][])
62
+ * [#2356](https://github.com/bbatsov/rubocop/pull/2356): `Style/Encoding` will now place the encoding comment on the second line if the first line is a shebang. ([@rrosenblum][])
63
+ * `Style/InitialIndentation` cop doesn't error out when a line begins with an integer literal. ([@alexdowad][])
64
+ * [#2296](https://github.com/bbatsov/rubocop/issues/2296): In `Style/DotPosition`, don't "correct" (and break) a method call which has a line comment (or blank line) between the dot and the selector. ([@alexdowad][])
65
+ * [#2272](https://github.com/bbatsov/rubocop/issues/2272): `Lint/NonLocalExitFromIterator` does not warn about `return` in a block which is passed to `Module#define_method`. ([@alexdowad][])
66
+ * [#2262](https://github.com/bbatsov/rubocop/issues/2262): Replace `Rainbow` reference with `Colorizable#yellow`. ([@minustehbare][])
67
+ * [#2068](https://github.com/bbatsov/rubocop/issues/2068): Display warning if `Style/Copyright` is misconfigured. ([@alexdowad][])
68
+ * [#2321](https://github.com/bbatsov/rubocop/issues/2321): In `Style/EachWithObject`, don't replace reduce with each_with_object if the accumulator parameter is assigned to in the block. ([@alexdowad][])
69
+ * [#1981](https://github.com/bbatsov/rubocop/issues/1981): `Lint/UselessAssignment` doesn't erroneously identify assignments in identical if branches as useless. ([@alexdowad][])
70
+ * [#2323](https://github.com/bbatsov/rubocop/issues/2323): `Style/IfUnlessModifier` cop parenthesizes autocorrected code when necessary due to operator precedence, to avoid changing its meaning. ([@alexdowad][])
71
+ * [#2003](https://github.com/bbatsov/rubocop/issues/2003): Make `Lint/UnneededDisable` work with `--auto-correct`. ([@jonas054][])
72
+ * Default RuboCop cache dir moved to per-user folders. ([@br3nda][])
73
+ * [#2393](https://github.com/bbatsov/rubocop/pull/2393): `Style/MethodCallParentheses` doesn't fail on `obj.method ||= func()`. ([@alexdowad][])
74
+ * [#2344](https://github.com/bbatsov/rubocop/pull/2344): When autocorrecting, `Style/ParallelAssignment` reorders assignment statements, if necessary, to avoid breaking code. ([@alexdowad][])
75
+ * `Style/MultilineOperationAlignment` does not try to align the receiver and selector of a method call if both are on the LHS of an assignment. ([@alexdowad][])
76
+
77
+ ### Changes
78
+
79
+ * [#2194](https://github.com/bbatsov/rubocop/issues/2194): Allow any options with `--auto-gen-config`. ([@agrimm][])
80
+
81
+ [@bbatsov]: https://github.com/bbatsov
82
+ [@jonas054]: https://github.com/jonas054
83
+ [@yujinakayama]: https://github.com/yujinakayama
84
+ [@dblock]: https://github.com/dblock
85
+ [@nevir]: https://github.com/nevir
86
+ [@daviddavis]: https://github.com/daviddavis
87
+ [@sds]: https://github.com/sds
88
+ [@fancyremarker]: https://github.com/fancyremarker
89
+ [@sinisterchipmunk]: https://github.com/sinisterchipmunk
90
+ [@vonTronje]: https://github.com/vonTronje
91
+ [@agrimm]: https://github.com/agrimm
92
+ [@pmenglund]: https://github.com/pmenglund
93
+ [@chulkilee]: https://github.com/chulkilee
94
+ [@codez]: https://github.com/codez
95
+ [@emou]: https://github.com/emou
96
+ [@skanev]: http://github.com/skanev
97
+ [@claco]: http://github.com/claco
98
+ [@rifraf]: http://github.com/rifraf
99
+ [@scottmatthewman]: https://github.com/scottmatthewman
100
+ [@ma2gedev]: http://github.com/ma2gedev
101
+ [@jeremyolliver]: https://github.com/jeremyolliver
102
+ [@hannestyden]: https://github.com/hannestyden
103
+ [@geniou]: https://github.com/geniou
104
+ [@jkogara]: https://github.com/jkogara
105
+ [@tmorris-fiksu]: https://github.com/tmorris-fiksu
106
+ [@mockdeep]: https://github.com/mockdeep
107
+ [@hiroponz]: https://github.com/hiroponz
108
+ [@tamird]: https://github.com/tamird
109
+ [@fshowalter]: https://github.com/fshowalter
110
+ [@cschramm]: https://github.com/cschramm
111
+ [@bquorning]: https://github.com/bquorning
112
+ [@bcobb]: https://github.com/bcobb
113
+ [@irrationalfab]: https://github.com/irrationalfab
114
+ [@tommeier]: https://github.com/tommeier
115
+ [@sfeldon]: https://github.com/sfeldon
116
+ [@biinari]: https://github.com/biinari
117
+ [@barunio]: https://github.com/barunio
118
+ [@molawson]: https://github.com/molawson
119
+ [@wndhydrnt]: https://github.com/wndhydrnt
120
+ [@ggilder]: https://github.com/ggilder
121
+ [@salbertson]: https://github.com/salbertson
122
+ [@camilleldn]: https://github.com/camilleldn
123
+ [@mcls]: https://github.com/mcls
124
+ [@yous]: https://github.com/yous
125
+ [@vrthra]: https://github.com/vrthra
126
+ [@SkuliOskarsson]: https://github.com/SkuliOskarsson
127
+ [@jspanjers]: https://github.com/jspanjers
128
+ [@sch1zo]: https://github.com/sch1zo
129
+ [@smangelsdorf]: https://github.com/smangelsdorf
130
+ [@mvz]: https://github.com/mvz
131
+ [@jfelchner]: https://github.com/jfelchner
132
+ [@janraasch]: https://github.com/janraasch
133
+ [@jcarbo]: https://github.com/jcarbo
134
+ [@oneamtu]: https://github.com/oneamtu
135
+ [@toy]: https://github.com/toy
136
+ [@Koronen]: https://github.com/Koronen
137
+ [@blainesch]: https://github.com/blainesch
138
+ [@marxarelli]: https://github.com/marxarelli
139
+ [@katieschilling]: https://github.com/katieschilling
140
+ [@kakutani]: https://github.com/kakutani
141
+ [@rrosenblum]: https://github.com/rrosenblum
142
+ [@mattjmcnaughton]: https://github.com/mattjmcnaughton
143
+ [@huerlisi]: https://github.com/huerlisi
144
+ [@volkert]: https://github.com/volkert
145
+ [@lumeet]: https://github.com/lumeet
146
+ [@mmozuras]: https://github.com/mmozuras
147
+ [@d4rk5eed]: https://github.com/d4rk5eed
148
+ [@cshaffer]: https://github.com/cshaffer
149
+ [@eitoball]: https://github.com/eitoball
150
+ [@iainbeeston]: https://github.com/iainbeeston
151
+ [@pimterry]: https://github.com/pimterry
152
+ [@palkan]: https://github.com/palkan
153
+ [@jdoconnor]: https://github.com/jdoconnor
154
+ [@meganemura]: https://github.com/meganemura
155
+ [@zvkemp]: https://github.com/zvkemp
156
+ [@vassilevsky]: https://github.com/vassilevsky
157
+ [@gerry3]: https://github.com/gerry3
158
+ [@ypresto]: https://github.com/ypresto
159
+ [@clowder]: https://github.com/clowder
160
+ [@mudge]: https://github.com/mudge
161
+ [@mzp]: https://github.com/mzp
162
+ [@bankair]: https://github.com/bankair
163
+ [@crimsonknave]: https://github.com/crimsonknave
164
+ [@renuo]: https://github.com/renuo
165
+ [@sdeframond]: https://github.com/sdeframond
166
+ [@til]: https://github.com/til
167
+ [@carhartl]: https://github.com/carhartl
168
+ [@dylandavidson]: https://github.com/dylandavidson
169
+ [@tmr08c]: https://github.com/tmr08c
170
+ [@hbd225]: https://github.com/hbd225
171
+ [@l8nite]: https://github.com/l8nite
172
+ [@sumeet]: https://github.com/sumeet
173
+ [@ojab]: https://github.com/ojab
174
+ [@chastell]: https://github.com/chastell
175
+ [@glasnt]: https://github.com/glasnt
176
+ [@crazydog115]: https://github.com/crazydog115
177
+ [@RGBD]: https://github.com/RGBD
178
+ [@panthomakos]: https://github.com/panthomakos
179
+ [@matugm]: https://github.com/matugm
180
+ [@m1foley]: https://github.com/m1foley
181
+ [@tejasbubane]: https://github.com/tejasbubane
182
+ [@bmorrall]: https://github.com/bmorrall
183
+ [@fphilipe]: https://github.com/fphilipe
184
+ [@gotrevor]: https://github.com/gotrevor
185
+ [@awwaiid]: https://github.com/awwaiid
186
+ [@segiddins]: https://github.com/segiddins
187
+ [@urbanautomaton]: https://github.com/urbanautomaton.com
188
+ [@unmanbearpig]: https://github.com/unmanbearpig
189
+ [@maxjacobson]: https://github.com/maxjacobson
190
+ [@sliuu]: https://github.com/sliuu
191
+ [@edmz]: https://github.com/edmz
192
+ [@syndbg]: https://github.com/syndbg
193
+ [@wli]: https://github.com/wli
194
+ [@caseywebdev]: https://github.com/caseywebdev
195
+ [@MGerrior]: https://github.com/MGerrior
196
+ [@imtayadeway]: https://github.com/imtayadeway
197
+ [@mrfoto]: https://github.com/mrfoto
198
+ [@karreiro]: https://github.com/karreiro
199
+ [@dreyks]: https://github.com/dreyks
200
+ [@hmadison]: https://github.com/hmadison
201
+ [@miquella]: https://github.com/miquella
202
+ [@jhansche]: https://github.com/jhansche
203
+ [@cornelius]: https://github.com/cornelius
204
+ [@eagletmt]: https://github.com/eagletmt
205
+ [@apiology]: https://github.com/apiology
206
+ [@alexdowad]: https://github.com/alexdowad
207
+ [@minustehbare]: https://github.com/minustehbare
208
+ [@tansaku]: https://github.com/tansaku
209
+ [@ptrippett]: https://github.com/ptrippett
210
+ [@br3nda]: https://github.com/br3nda