rubocop 1.13.0 → 1.17.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +68 -8
  4. data/lib/rubocop.rb +9 -0
  5. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -3
  6. data/lib/rubocop/cop/bundler/gem_version.rb +99 -0
  7. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  8. data/lib/rubocop/cop/layout/argument_alignment.rb +29 -11
  9. data/lib/rubocop/cop/layout/case_indentation.rb +57 -9
  10. data/lib/rubocop/cop/layout/dot_position.rb +7 -1
  11. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +13 -15
  12. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +12 -0
  13. data/lib/rubocop/cop/layout/hash_alignment.rb +34 -9
  14. data/lib/rubocop/cop/layout/indentation_width.rb +13 -2
  15. data/lib/rubocop/cop/layout/redundant_line_break.rb +24 -10
  16. data/lib/rubocop/cop/layout/single_line_block_chain.rb +53 -0
  17. data/lib/rubocop/cop/layout/space_around_keyword.rb +28 -0
  18. data/lib/rubocop/cop/layout/space_around_operators.rb +6 -0
  19. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +83 -39
  20. data/lib/rubocop/cop/lint/empty_block.rb +18 -2
  21. data/lib/rubocop/cop/lint/empty_in_pattern.rb +62 -0
  22. data/lib/rubocop/cop/lint/literal_as_condition.rb +13 -1
  23. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +32 -17
  24. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +105 -74
  26. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -0
  27. data/lib/rubocop/cop/lint/symbol_conversion.rb +2 -12
  28. data/lib/rubocop/cop/lint/unreachable_loop.rb +12 -2
  29. data/lib/rubocop/cop/lint/unused_block_argument.rb +7 -1
  30. data/lib/rubocop/cop/lint/void.rb +1 -1
  31. data/lib/rubocop/cop/migration/department_name.rb +3 -1
  32. data/lib/rubocop/cop/mixin/check_line_breakable.rb +19 -3
  33. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
  34. data/lib/rubocop/cop/mixin/gem_declaration.rb +13 -0
  35. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +14 -3
  36. data/lib/rubocop/cop/mixin/string_literals_help.rb +3 -5
  37. data/lib/rubocop/cop/mixin/symbol_help.rb +13 -0
  38. data/lib/rubocop/cop/style/class_and_module_children.rb +17 -5
  39. data/lib/rubocop/cop/style/empty_literal.rb +8 -1
  40. data/lib/rubocop/cop/style/hash_each_methods.rb +18 -1
  41. data/lib/rubocop/cop/style/identical_conditional_branches.rb +58 -8
  42. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -4
  43. data/lib/rubocop/cop/style/in_pattern_then.rb +56 -0
  44. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -1
  45. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +62 -0
  46. data/lib/rubocop/cop/style/multiline_when_then.rb +2 -11
  47. data/lib/rubocop/cop/style/negated_if_else_condition.rb +17 -9
  48. data/lib/rubocop/cop/style/nil_lambda.rb +29 -12
  49. data/lib/rubocop/cop/style/quoted_symbols.rb +110 -0
  50. data/lib/rubocop/cop/style/raise_args.rb +2 -0
  51. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  52. data/lib/rubocop/cop/style/redundant_self.rb +24 -2
  53. data/lib/rubocop/cop/style/regexp_literal.rb +9 -1
  54. data/lib/rubocop/cop/style/single_line_methods.rb +8 -3
  55. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -5
  56. data/lib/rubocop/cop/style/string_literals.rb +1 -0
  57. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +1 -0
  58. data/lib/rubocop/cop/style/top_level_method_definition.rb +83 -0
  59. data/lib/rubocop/cop/style/trivial_accessors.rb +65 -0
  60. data/lib/rubocop/cop/style/when_then.rb +6 -2
  61. data/lib/rubocop/cop/variable_force/branch.rb +15 -0
  62. data/lib/rubocop/directive_comment.rb +58 -6
  63. data/lib/rubocop/formatter/junit_formatter.rb +21 -6
  64. data/lib/rubocop/options.rb +14 -20
  65. data/lib/rubocop/rake_task.rb +1 -1
  66. data/lib/rubocop/remote_config.rb +10 -2
  67. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  68. data/lib/rubocop/target_finder.rb +9 -2
  69. data/lib/rubocop/target_ruby.rb +1 -1
  70. data/lib/rubocop/version.rb +1 -1
  71. metadata +18 -9
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Newcomers to ruby applications may write top-level methods,
7
+ # when ideally they should be organized in appropriate classes or modules.
8
+ # This cop looks for definitions of top-level methods and warns about them.
9
+ #
10
+ # However for ruby scripts it is perfectly fine to use top-level methods.
11
+ # Hence this cop is disabled by default.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # def some_method
16
+ # end
17
+ #
18
+ # # bad
19
+ # def self.some_method
20
+ # end
21
+ #
22
+ # # bad
23
+ # define_method(:foo) { puts 1 }
24
+ #
25
+ # # good
26
+ # module Foo
27
+ # def some_method
28
+ # end
29
+ # end
30
+ #
31
+ # # good
32
+ # class Foo
33
+ # def self.some_method
34
+ # end
35
+ # end
36
+ #
37
+ # # good
38
+ # Struct.new do
39
+ # def some_method
40
+ # end
41
+ # end
42
+ #
43
+ # # good
44
+ # class Foo
45
+ # define_method(:foo) { puts 1 }
46
+ # end
47
+ class TopLevelMethodDefinition < Base
48
+ MSG = 'Do not define methods at the top-level.'
49
+
50
+ RESTRICT_ON_SEND = %i[define_method].freeze
51
+
52
+ def on_def(node)
53
+ return unless top_level_method_definition?(node)
54
+
55
+ add_offense(node)
56
+ end
57
+ alias on_defs on_def
58
+ alias on_send on_def
59
+
60
+ def on_block(node)
61
+ return unless define_method_block?(node) && top_level_method_definition?(node)
62
+
63
+ add_offense(node)
64
+ end
65
+
66
+ private
67
+
68
+ def top_level_method_definition?(node)
69
+ if node.parent&.begin_type?
70
+ node.parent.root?
71
+ else
72
+ node.root?
73
+ end
74
+ end
75
+
76
+ # @!method define_method_block?(node)
77
+ def_node_matcher :define_method_block?, <<~PATTERN
78
+ (block (send _ {:define_method} _) ...)
79
+ PATTERN
80
+ end
81
+ end
82
+ end
83
+ end
@@ -27,6 +27,71 @@ module RuboCop
27
27
  # class << self
28
28
  # attr_reader :baz
29
29
  # end
30
+ #
31
+ # @example ExactNameMatch: true (default)
32
+ # # good
33
+ # def name
34
+ # @other_name
35
+ # end
36
+ #
37
+ # @example ExactNameMatch: false
38
+ # # bad
39
+ # def name
40
+ # @other_name
41
+ # end
42
+ #
43
+ # @example AllowPredicates: true (default)
44
+ # # good
45
+ # def foo?
46
+ # @foo
47
+ # end
48
+ #
49
+ # @example AllowPredicates: false
50
+ # # bad
51
+ # def foo?
52
+ # @foo
53
+ # end
54
+ #
55
+ # # good
56
+ # attr_reader :foo
57
+ #
58
+ # @example AllowDSLWriters: true (default)
59
+ # # good
60
+ # def on_exception(action)
61
+ # @on_exception=action
62
+ # end
63
+ #
64
+ # @example AllowDSLWriters: false
65
+ # # bad
66
+ # def on_exception(action)
67
+ # @on_exception=action
68
+ # end
69
+ #
70
+ # # good
71
+ # attr_writer :on_exception
72
+ #
73
+ # @example IgnoreClassMethods: false (default)
74
+ # # bad
75
+ # def self.foo
76
+ # @foo
77
+ # end
78
+ #
79
+ # # good
80
+ # class << self
81
+ # attr_reader :foo
82
+ # end
83
+ #
84
+ # @example IgnoreClassMethods: true
85
+ # # good
86
+ # def self.foo
87
+ # @foo
88
+ # end
89
+ #
90
+ # @example AllowedMethods: ['allowed_method']
91
+ # # good
92
+ # def allowed_method
93
+ # @foo
94
+ # end
30
95
  class TrivialAccessors < Base
31
96
  include AllowedMethods
32
97
  extend AutoCorrector
@@ -20,12 +20,16 @@ module RuboCop
20
20
  class WhenThen < Base
21
21
  extend AutoCorrector
22
22
 
23
- MSG = 'Do not use `when x;`. Use `when x then` instead.'
23
+ MSG = 'Do not use `when %<expression>s;`. Use `when %<expression>s then` instead.'
24
24
 
25
25
  def on_when(node)
26
26
  return if node.multiline? || node.then? || !node.body
27
27
 
28
- add_offense(node.loc.begin) { |corrector| corrector.replace(node.loc.begin, ' then') }
28
+ message = format(MSG, expression: node.conditions.map(&:source).join(', '))
29
+
30
+ add_offense(node.loc.begin, message: message) do |corrector|
31
+ corrector.replace(node.loc.begin, ' then')
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -226,6 +226,21 @@ module RuboCop
226
226
  end
227
227
  end
228
228
 
229
+ # case target
230
+ # in pattern # in_pattern
231
+ # else
232
+ # else_body
233
+ # end
234
+ class CaseMatch < Base
235
+ define_predicate :target?, child_index: 0
236
+ define_predicate :in_pattern?, child_index: 1..-2
237
+ define_predicate :else_body?, child_index: -1
238
+
239
+ def always_run?
240
+ target?
241
+ end
242
+ end
243
+
229
244
  # for element in collection
230
245
  # loop_body
231
246
  # end
@@ -6,7 +6,9 @@ module RuboCop
6
6
  # cops it contains.
7
7
  class DirectiveComment
8
8
  # @api private
9
- REDUNDANT_COP = 'Lint/RedundantCopDisableDirective'
9
+ REDUNDANT_DIRECTIVE_COP_DEPARTMENT = 'Lint'
10
+ # @api private
11
+ REDUNDANT_DIRECTIVE_COP = "#{REDUNDANT_DIRECTIVE_COP_DEPARTMENT}/RedundantCopDisableDirective"
10
12
  # @api private
11
13
  COP_NAME_PATTERN = '([A-Z]\w+/)*(?:[A-Z]\w+)'
12
14
  # @api private
@@ -23,10 +25,11 @@ module RuboCop
23
25
  line.split(DIRECTIVE_COMMENT_REGEXP).first
24
26
  end
25
27
 
26
- attr_reader :comment, :mode, :cops
28
+ attr_reader :comment, :cop_registry, :mode, :cops
27
29
 
28
- def initialize(comment)
30
+ def initialize(comment, cop_registry = Cop::Registry.global)
29
31
  @comment = comment
32
+ @cop_registry = cop_registry
30
33
  @mode, @cops = match_captures
31
34
  end
32
35
 
@@ -41,7 +44,11 @@ module RuboCop
41
44
  end
42
45
 
43
46
  def range
44
- comment.location.expression
47
+ match = comment.text.match(DIRECTIVE_COMMENT_REGEXP)
48
+ begin_pos = comment.loc.expression.begin_pos
49
+ Parser::Source::Range.new(
50
+ comment.loc.expression.source_buffer, begin_pos + match.begin(0), begin_pos + match.end(0)
51
+ )
45
52
  end
46
53
 
47
54
  # Returns match captures to directive comment pattern
@@ -64,6 +71,11 @@ module RuboCop
64
71
  !disabled? && all_cops?
65
72
  end
66
73
 
74
+ # Checks if this directive disables all cops
75
+ def disabled_all?
76
+ disabled? && all_cops?
77
+ end
78
+
67
79
  # Checks if all cops specified in this directive
68
80
  def all_cops?
69
81
  cops == 'all'
@@ -74,6 +86,26 @@ module RuboCop
74
86
  @cop_names ||= all_cops? ? all_cop_names : parsed_cop_names
75
87
  end
76
88
 
89
+ # Returns array of specified in this directive department names
90
+ # when all department disabled
91
+ def department_names
92
+ splitted_cops_string.select { |cop| department?(cop) }
93
+ end
94
+
95
+ # Checks if directive departments include cop
96
+ def in_directive_department?(cop)
97
+ department_names.any? { |department| cop.start_with?(department) }
98
+ end
99
+
100
+ # Checks if cop department has already used in directive comment
101
+ def overridden_by_department?(cop)
102
+ in_directive_department?(cop) && splitted_cops_string.include?(cop)
103
+ end
104
+
105
+ def directive_count
106
+ splitted_cops_string.count
107
+ end
108
+
77
109
  # Returns line number for directive
78
110
  def line_number
79
111
  comment.loc.expression.line
@@ -81,12 +113,32 @@ module RuboCop
81
113
 
82
114
  private
83
115
 
84
- def parsed_cop_names
116
+ def splitted_cops_string
85
117
  (cops || '').split(/,\s*/)
86
118
  end
87
119
 
120
+ def parsed_cop_names
121
+ splitted_cops_string.map do |name|
122
+ department?(name) ? cop_names_for_department(name) : name
123
+ end.flatten
124
+ end
125
+
126
+ def department?(name)
127
+ cop_registry.department?(name)
128
+ end
129
+
88
130
  def all_cop_names
89
- Cop::Registry.global.names - [REDUNDANT_COP]
131
+ exclude_redundant_directive_cop(cop_registry.names)
132
+ end
133
+
134
+ def cop_names_for_department(department)
135
+ names = cop_registry.names_for_department(department)
136
+ has_redundant_directive_cop = department == REDUNDANT_DIRECTIVE_COP_DEPARTMENT
137
+ has_redundant_directive_cop ? exclude_redundant_directive_cop(names) : names
138
+ end
139
+
140
+ def exclude_redundant_directive_cop(cops)
141
+ cops - [REDUNDANT_DIRECTIVE_COP]
90
142
  end
91
143
  end
92
144
  end
@@ -22,9 +22,13 @@ module RuboCop
22
22
  testsuites = REXML::Element.new('testsuites', @document)
23
23
  testsuite = REXML::Element.new('testsuite', testsuites)
24
24
  @testsuite = testsuite.tap { |element| element.add_attributes('name' => 'rubocop') }
25
+
26
+ reset_count
25
27
  end
26
28
 
27
29
  def file_finished(file, offenses)
30
+ @inspected_file_count += 1
31
+
28
32
  # TODO: Returns all cops with the same behavior as
29
33
  # the original rubocop-junit-formatter.
30
34
  # https://github.com/mikian/rubocop-junit-formatter/blob/v0.1.4/lib/rubocop/formatter/junit_formatter.rb#L9
@@ -32,15 +36,11 @@ module RuboCop
32
36
  # In the future, it would be preferable to return only enabled cops.
33
37
  Cop::Registry.all.each do |cop|
34
38
  target_offenses = offenses_for_cop(offenses, cop)
39
+ @offense_count += target_offenses.count
35
40
 
36
41
  next unless relevant_for_output?(options, target_offenses)
37
42
 
38
- REXML::Element.new('testcase', @testsuite).tap do |testcase|
39
- testcase.attributes['classname'] = classname_attribute_value(file)
40
- testcase.attributes['name'] = cop.cop_name
41
-
42
- add_failure_to(testcase, target_offenses, cop.cop_name)
43
- end
43
+ add_testcase_element_to_testsuite_element(file, target_offenses, cop)
44
44
  end
45
45
  end
46
46
 
@@ -52,16 +52,31 @@ module RuboCop
52
52
  all_offenses.select { |offense| offense.cop_name == cop.cop_name }
53
53
  end
54
54
 
55
+ def add_testcase_element_to_testsuite_element(file, target_offenses, cop)
56
+ REXML::Element.new('testcase', @testsuite).tap do |testcase|
57
+ testcase.attributes['classname'] = classname_attribute_value(file)
58
+ testcase.attributes['name'] = cop.cop_name
59
+
60
+ add_failure_to(testcase, target_offenses, cop.cop_name)
61
+ end
62
+ end
63
+
55
64
  def classname_attribute_value(file)
56
65
  file.gsub(/\.rb\Z/, '').gsub("#{Dir.pwd}/", '').tr('/', '.')
57
66
  end
58
67
 
59
68
  def finished(_inspected_files)
69
+ @testsuite.add_attributes('tests' => @inspected_file_count, 'failures' => @offense_count)
60
70
  @document.write(output, 2)
61
71
  end
62
72
 
63
73
  private
64
74
 
75
+ def reset_count
76
+ @inspected_file_count = 0
77
+ @offense_count = 0
78
+ end
79
+
65
80
  def add_failure_to(testcase, offenses, cop_name)
66
81
  # One failure per offense. Zero failures is a passing test case,
67
82
  # for most surefire/nUnit parsers.
@@ -297,7 +297,7 @@ module RuboCop
297
297
  validate_auto_gen_config
298
298
  validate_auto_correct
299
299
  validate_display_only_failed
300
- validate_parallel
300
+ disable_parallel_when_invalid_option_combo
301
301
 
302
302
  return if incompatible_options.size <= 1
303
303
 
@@ -334,33 +334,27 @@ module RuboCop
334
334
  format('--disable-uncorrectable can only be used together with --auto-correct.')
335
335
  end
336
336
 
337
- def validate_parallel
337
+ def disable_parallel_when_invalid_option_combo
338
338
  return unless @options.key?(:parallel)
339
339
 
340
- if @options[:cache] == 'false'
341
- raise OptionArgumentError, '-P/--parallel uses caching to speed up ' \
342
- 'execution, so combining with --cache ' \
343
- 'false is not allowed.'
344
- end
345
-
346
- disable_parallel_when_invalid_combo
347
- end
340
+ invalid_options = [
341
+ { name: :auto_gen_config, value: true, flag: '--auto-gen-config' },
342
+ { name: :fail_fast, value: true, flag: '-F/--fail-fast.' },
343
+ { name: :auto_correct, value: true, flag: '--auto-correct.' },
344
+ { name: :cache, value: 'false', flag: '--cache false' }
345
+ ]
348
346
 
349
- def disable_parallel_when_invalid_combo
350
- combos = {
351
- auto_gen_config: '--auto-gen-config',
352
- fail_fast: '-F/--fail-fast.',
353
- auto_correct: '--auto-correct.'
354
- }
355
-
356
- invalid_combos = combos.select { |key, _flag| @options.key?(key) }
347
+ invalid_flags = invalid_options.each_with_object([]) do |option, flags|
348
+ # `>` rather than `>=` because `@options` will also contain `parallel: true`
349
+ flags << option[:flag] if @options > { option[:name] => option[:value] }
350
+ end
357
351
 
358
- return if invalid_combos.empty?
352
+ return if invalid_flags.empty?
359
353
 
360
354
  @options.delete(:parallel)
361
355
 
362
356
  puts '-P/--parallel is being ignored because ' \
363
- "it is not compatible with #{invalid_combos.values.join(', ')}"
357
+ "it is not compatible with #{invalid_flags.join(', ')}"
364
358
  end
365
359
 
366
360
  def only_includes_redundant_disable?
@@ -70,7 +70,7 @@ module RuboCop
70
70
  options = full_options.unshift('--auto-correct-all')
71
71
  # `parallel` will automatically be removed from the options internally.
72
72
  # This is a nice to have to suppress the warning message
73
- # about parallel and auto-corrent not being compatible.
73
+ # about parallel and auto-correct not being compatible.
74
74
  options.delete('--parallel')
75
75
  run_cli(verbose, options)
76
76
  end
@@ -55,6 +55,7 @@ module RuboCop
55
55
  def generate_request(uri)
56
56
  request = Net::HTTP::Get.new(uri.request_uri)
57
57
 
58
+ request.basic_auth(uri.user, uri.password) if uri.user
58
59
  request['If-Modified-Since'] = File.stat(cache_path).mtime.rfc2822 if cache_path_exists?
59
60
 
60
61
  yield request
@@ -70,7 +71,7 @@ module RuboCop
70
71
  begin
71
72
  response.error!
72
73
  rescue StandardError => e
73
- message = "#{e.message} while downloading remote config file #{uri}"
74
+ message = "#{e.message} while downloading remote config file #{cloned_url}"
74
75
  raise e, message
75
76
  end
76
77
  end
@@ -94,9 +95,16 @@ module RuboCop
94
95
  end
95
96
 
96
97
  def cache_name_from_uri
97
- uri = @uri.clone
98
+ uri = cloned_url
98
99
  uri.query = nil
99
100
  uri.to_s.gsub!(/[^0-9A-Za-z]/, '-')
100
101
  end
102
+
103
+ def cloned_url
104
+ uri = @uri.clone
105
+ uri.user = nil if uri.user
106
+ uri.password = nil if uri.password
107
+ uri
108
+ end
101
109
  end
102
110
  end