rubocop 1.13.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
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