rubocop 0.61.1 → 0.62.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f3af102582affbb1b9735d2af44ac0dccb270ab
4
- data.tar.gz: cca031307df0c706e0b1f65254659dfc7ee04686
3
+ metadata.gz: dd76265ab6d79dd4e54127c4969d0af755732cfc
4
+ data.tar.gz: 12ff96a7380e7a32ea291237754f66d0450605f7
5
5
  SHA512:
6
- metadata.gz: f38a3804680b872219520c3e651c6bd863b5adadacb1729d6e251e76288192981b12f5d257f91ed704bcf53985b3770cdf0a309d1c008e3d26074fdba1447361
7
- data.tar.gz: 82f6ab2d61dd493269f3550ed0ba36672c3912b72073d12ea5d1f1ba0825a3fd3b40af339dd6b8cb7dbd5a093dae10b9bd60db058535f8ef1b730ae8a15fae11
6
+ metadata.gz: 91eb27b2b5c06d550cf06d83782b53e0ff05245ec079a01ec9158e06fc00ab61324c5d6b24db0bbdc68992bdce4d051375f9e9f5224e9401c3b2f20fa6023c1f
7
+ data.tar.gz: c7a1c9f7f0e3d50f95c9b7e0a488cfdb31842b93e2f427ee14921429d4f64cae9ab9d0659b9775c60e5a19341615f29a4c5949451c7a4b563d7b9ca0d8994212
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/rubocop.svg)](https://badge.fury.io/rb/rubocop)
2
2
  [![CircleCI Status](https://circleci.com/gh/rubocop-hq/rubocop/tree/master.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop/tree/master)
3
3
  [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/sj3ye7n5690d0nvg?svg=true)](https://ci.appveyor.com/project/bbatsov/rubocop)
4
- [![Coverage Status](https://img.shields.io/codeclimate/coverage/github/bbatsov/rubocop.svg)](https://codeclimate.com/github/bbatsov/rubocop)
4
+ [![Coverage Status](https://api.codeclimate.com/v1/badges/ad6e76460499c8c99697/test_coverage)](https://codeclimate.com/github/bbatsov/rubocop)
5
5
  [![Code Climate](https://codeclimate.com/github/bbatsov/rubocop/badges/gpa.svg)](https://codeclimate.com/github/bbatsov/rubocop)
6
6
  [![Inline docs](https://inch-ci.org/github/bbatsov/rubocop.svg)](https://inch-ci.org/github/bbatsov/rubocop)
7
7
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=rubocop&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=rubocop&package-manager=bundler&version-scheme=semver)
@@ -53,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
53
53
  might want to use a conservative version locking in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 0.61.1', require: false
56
+ gem 'rubocop', '~> 0.62.0', require: false
57
57
  ```
58
58
 
59
59
  ## Quickstart
@@ -2399,6 +2399,12 @@ Rails/LexicallyScopedActionFilter:
2399
2399
  Include:
2400
2400
  - app/controllers/**/*.rb
2401
2401
 
2402
+ Rails/LinkToBlank:
2403
+ Description: 'Checks that `link_to` with a `target: "_blank"` have a `rel: "noopener"` option passed to them.'
2404
+ Reference: https://mathiasbynens.github.io/rel-noopener/
2405
+ Enabled: true
2406
+ VersionAdded: '0.62'
2407
+
2402
2408
  Rails/NotNullColumn:
2403
2409
  Description: 'Do not add a NOT NULL column without a default value'
2404
2410
  Enabled: true
@@ -564,9 +564,9 @@ require_relative 'rubocop/cop/rails/blank'
564
564
  require_relative 'rubocop/cop/rails/bulk_change_table'
565
565
  require_relative 'rubocop/cop/rails/create_table_with_timestamps'
566
566
  require_relative 'rubocop/cop/rails/date'
567
- require_relative 'rubocop/cop/rails/dynamic_find_by'
568
567
  require_relative 'rubocop/cop/rails/delegate'
569
568
  require_relative 'rubocop/cop/rails/delegate_allow_blank'
569
+ require_relative 'rubocop/cop/rails/dynamic_find_by'
570
570
  require_relative 'rubocop/cop/rails/enum_uniqueness'
571
571
  require_relative 'rubocop/cop/rails/environment_comparison'
572
572
  require_relative 'rubocop/cop/rails/exit'
@@ -579,18 +579,19 @@ require_relative 'rubocop/cop/rails/http_positional_arguments'
579
579
  require_relative 'rubocop/cop/rails/http_status'
580
580
  require_relative 'rubocop/cop/rails/inverse_of'
581
581
  require_relative 'rubocop/cop/rails/lexically_scoped_action_filter'
582
+ require_relative 'rubocop/cop/rails/link_to_blank'
582
583
  require_relative 'rubocop/cop/rails/not_null_column'
583
- require_relative 'rubocop/cop/rails/output_safety'
584
584
  require_relative 'rubocop/cop/rails/output'
585
+ require_relative 'rubocop/cop/rails/output_safety'
585
586
  require_relative 'rubocop/cop/rails/pluralization_grammar'
586
587
  require_relative 'rubocop/cop/rails/presence'
587
588
  require_relative 'rubocop/cop/rails/present'
588
589
  require_relative 'rubocop/cop/rails/read_write_attribute'
589
590
  require_relative 'rubocop/cop/rails/redundant_receiver_in_with_options'
590
591
  require_relative 'rubocop/cop/rails/refute_methods'
592
+ require_relative 'rubocop/cop/rails/relative_date_constant'
591
593
  require_relative 'rubocop/cop/rails/request_referer'
592
594
  require_relative 'rubocop/cop/rails/reversible_migration'
593
- require_relative 'rubocop/cop/rails/relative_date_constant'
594
595
  require_relative 'rubocop/cop/rails/safe_navigation'
595
596
  require_relative 'rubocop/cop/rails/save_bang'
596
597
  require_relative 'rubocop/cop/rails/scope_args'
@@ -7,9 +7,14 @@ module RuboCop
7
7
  class CLI
8
8
  include Formatter::TextUtil
9
9
 
10
- SKIPPED_PHASE_1 = 'Phase 1 of 2: run Metrics/LineLength cop (skipped ' \
11
- 'because the default Metrics/LineLength:Max is ' \
12
- 'overridden)'.freeze
10
+ PHASE_1 = 'Phase 1 of 2: run Metrics/LineLength cop'.freeze
11
+ PHASE_2 = 'Phase 2 of 2: run all cops'.freeze
12
+
13
+ PHASE_1_OVERRIDDEN = '(skipped because the default Metrics/LineLength:Max' \
14
+ ' is overridden)'.freeze
15
+ PHASE_1_DISABLED = '(skipped because Metrics/LineLength is ' \
16
+ 'disabled)'.freeze
17
+
13
18
  STATUS_SUCCESS = 0
14
19
  STATUS_OFFENSES = 1
15
20
  STATUS_ERROR = 2
@@ -64,28 +69,47 @@ module RuboCop
64
69
  def execute_runners(paths)
65
70
  if @options[:auto_gen_config]
66
71
  reset_config_and_auto_gen_file
67
- line_length_contents =
68
- if max_line_length(@config_store.for(Dir.pwd)) ==
69
- max_line_length(ConfigLoader.default_configuration)
70
- run_line_length_cop_auto_gen_config(paths)
71
- else
72
- puts Rainbow(SKIPPED_PHASE_1).yellow
73
- ''
74
- end
72
+ line_length_contents = maybe_run_line_length_cop(paths)
75
73
  run_all_cops_auto_gen_config(line_length_contents, paths)
76
74
  else
77
75
  execute_runner(paths)
78
76
  end
79
77
  end
80
78
 
79
+ def maybe_run_line_length_cop(paths)
80
+ if !line_length_enabled?(@config_store.for(Dir.pwd))
81
+ puts Rainbow("#{PHASE_1} #{PHASE_1_DISABLED}").yellow
82
+ ''
83
+ elsif !same_max_line_length?(
84
+ @config_store.for(Dir.pwd), ConfigLoader.default_configuration
85
+ )
86
+ puts Rainbow("#{PHASE_1} #{PHASE_1_OVERRIDDEN}").yellow
87
+ ''
88
+ else
89
+ run_line_length_cop_auto_gen_config(paths)
90
+ end
91
+ end
92
+
93
+ def line_length_enabled?(config)
94
+ line_length_cop(config)['Enabled']
95
+ end
96
+
97
+ def same_max_line_length?(config1, config2)
98
+ max_line_length(config1) == max_line_length(config2)
99
+ end
100
+
81
101
  def max_line_length(config)
82
- config.for_cop('Metrics/LineLength')['Max']
102
+ line_length_cop(config)['Max']
103
+ end
104
+
105
+ def line_length_cop(config)
106
+ config.for_cop('Metrics/LineLength')
83
107
  end
84
108
 
85
109
  # Do an initial run with only Metrics/LineLength so that cops that depend
86
110
  # on Metrics/LineLength:Max get the correct value for that parameter.
87
111
  def run_line_length_cop_auto_gen_config(paths)
88
- puts Rainbow('Phase 1 of 2: run Metrics/LineLength cop').yellow
112
+ puts Rainbow(PHASE_1).yellow
89
113
  @options[:only] = ['Metrics/LineLength']
90
114
  execute_runner(paths)
91
115
  @options.delete(:only)
@@ -98,7 +122,7 @@ module RuboCop
98
122
  end
99
123
 
100
124
  def run_all_cops_auto_gen_config(line_length_contents, paths)
101
- puts Rainbow('Phase 2 of 2: run all cops').yellow
125
+ puts Rainbow(PHASE_2).yellow
102
126
  result = execute_runner(paths)
103
127
  # This run was made with the current maximum length allowed, so append
104
128
  # the saved setting for LineLength.
@@ -16,6 +16,9 @@ module RuboCop
16
16
 
17
17
  COMMON_PARAMS = %w[Exclude Include Severity inherit_mode
18
18
  AutoCorrect StyleGuide Details].freeze
19
+ INTERNAL_PARAMS = %w[Description StyleGuide VersionAdded
20
+ VersionChanged Reference Safe SafeAutoCorrect].freeze
21
+
19
22
  # 2.2 is the oldest officially supported Ruby version.
20
23
  DEFAULT_RUBY_VERSION = 2.2
21
24
  KNOWN_RUBIES = [2.2, 2.3, 2.4, 2.5, 2.6].freeze
@@ -509,12 +512,17 @@ module RuboCop
509
512
  def validate_parameter_names(valid_cop_names)
510
513
  valid_cop_names.each do |name|
511
514
  validate_section_presence(name)
515
+ default_config = ConfigLoader.default_configuration[name]
516
+
512
517
  self[name].each_key do |param|
513
- next if COMMON_PARAMS.include?(param) ||
514
- ConfigLoader.default_configuration[name].key?(param)
518
+ next if COMMON_PARAMS.include?(param) || default_config.key?(param)
519
+
520
+ message =
521
+ "Warning: #{name} does not support #{param} parameter.\n\n" \
522
+ "Supported parameters are:\n\n" \
523
+ " - #{(default_config.keys - INTERNAL_PARAMS).join("\n - ")}\n"
515
524
 
516
- warn Rainbow("Warning: unrecognized parameter #{name}:#{param} " \
517
- "found in #{smart_loaded_path}").yellow
525
+ warn Rainbow(message).yellow.to_s
518
526
  end
519
527
  end
520
528
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  class AccessModifierIndentation < Cop
35
35
  include Alignment
36
36
  include ConfigurableEnforcedStyle
37
+ include RangeHelp
37
38
 
38
39
  MSG = '%<style>s access modifiers like `%<node>s`.'.freeze
39
40
 
@@ -53,8 +54,6 @@ module RuboCop
53
54
  end
54
55
 
55
56
  def on_block(node)
56
- return unless node.class_constructor?
57
-
58
57
  check_body(node.body, node)
59
58
  end
60
59
 
@@ -69,14 +68,13 @@ module RuboCop
69
68
  return unless body.begin_type?
70
69
 
71
70
  modifiers = body.each_child_node(:send).select(&:access_modifier?)
72
- class_column = node.source_range.column
71
+ end_range = node.loc.end
73
72
 
74
- modifiers.each { |modifier| check_modifier(modifier, class_column) }
73
+ modifiers.each { |modifier| check_modifier(modifier, end_range) }
75
74
  end
76
75
 
77
- def check_modifier(send_node, class_start_col)
78
- access_modifier_start_col = send_node.source_range.column
79
- offset = access_modifier_start_col - class_start_col
76
+ def check_modifier(send_node, end_range)
77
+ offset = column_offset_between(send_node.source_range, end_range)
80
78
 
81
79
  @column_delta = expected_indent_offset - offset
82
80
  if @column_delta.zero?
@@ -118,7 +118,7 @@ module RuboCop
118
118
  def check_alignment(base_range, else_range)
119
119
  return unless begins_its_line?(else_range)
120
120
 
121
- @column_delta = effective_column(base_range) - else_range.column
121
+ @column_delta = column_offset_between(base_range, else_range)
122
122
  return if @column_delta.zero?
123
123
 
124
124
  message = format(
@@ -29,20 +29,33 @@ module RuboCop
29
29
  MSG_BEFORE_AND_AFTER = 'Keep a blank line before and after ' \
30
30
  '`%<modifier>s`.'.freeze
31
31
 
32
+ def initialize(config = nil, options = nil)
33
+ super
34
+
35
+ @block_line = nil
36
+ end
37
+
32
38
  def on_class(node)
33
39
  _name, superclass, _body = *node
34
40
 
35
- @class_or_module_def = superclass || node.source_range
41
+ @class_or_module_def_first_line = if superclass
42
+ superclass.first_line
43
+ else
44
+ node.source_range.first_line
45
+ end
46
+ @class_or_module_def_last_line = node.source_range.last_line
36
47
  end
37
48
 
38
49
  def on_module(node)
39
- @class_or_module_def = node.source_range
50
+ @class_or_module_def_first_line = node.source_range.first_line
51
+ @class_or_module_def_last_line = node.source_range.last_line
40
52
  end
41
53
 
42
54
  def on_sclass(node)
43
55
  self_node, _body = *node
44
56
 
45
- @class_or_module_def = self_node.source_range
57
+ @class_or_module_def_first_line = self_node.source_range.first_line
58
+ @class_or_module_def_last_line = self_node.source_range.last_line
46
59
  end
47
60
 
48
61
  def on_block(node)
@@ -100,9 +113,9 @@ module RuboCop
100
113
  end
101
114
 
102
115
  def class_def?(line)
103
- return false unless @class_or_module_def
116
+ return false unless @class_or_module_def_first_line
104
117
 
105
- line == @class_or_module_def.first_line + 1
118
+ line == @class_or_module_def_first_line + 1
106
119
  end
107
120
 
108
121
  def block_start?(line)
@@ -112,9 +125,9 @@ module RuboCop
112
125
  end
113
126
 
114
127
  def body_end?(line)
115
- return false unless @class_or_module_def
128
+ return false unless @class_or_module_def_last_line
116
129
 
117
- line == @class_or_module_def.last_line - 1
130
+ line == @class_or_module_def_last_line - 1
118
131
  end
119
132
 
120
133
  def message(node)
@@ -234,7 +234,7 @@ module RuboCop
234
234
  def check_indentation(base_loc, body_node, style = 'normal')
235
235
  return unless indentation_to_check?(base_loc, body_node)
236
236
 
237
- indentation = body_node.loc.column - effective_column(base_loc)
237
+ indentation = column_offset_between(body_node.loc, base_loc)
238
238
  @column_delta = configured_indentation_width - indentation
239
239
  return if @column_delta.zero?
240
240
 
@@ -119,7 +119,7 @@ module RuboCop
119
119
  ancestor_node.kwbegin_type?
120
120
 
121
121
  assignment_node = assignment_node(ancestor_node)
122
- return assignment_node unless assignment_node.nil?
122
+ return assignment_node if same_line?(ancestor_node, assignment_node)
123
123
 
124
124
  access_modifier_node = access_modifier_node(ancestor_node)
125
125
  return access_modifier_node unless access_modifier_node.nil?
@@ -33,10 +33,15 @@ module RuboCop
33
33
  #
34
34
  # # bad
35
35
  # array = [ a, [ b, c ] ]
36
+ # array = [
37
+ # [ a ],
38
+ # [ b, c ]
39
+ # ]
36
40
  #
37
41
  # # good
38
42
  # array = [ a, [ b, c ]]
39
- #
43
+ # array = [[ a ],
44
+ # [ b, c ]]
40
45
  #
41
46
  # @example EnforcedStyleForEmptyBrackets: no_space (default)
42
47
  # # The `no_space` EnforcedStyleForEmptyBrackets style enforces that
@@ -5,8 +5,7 @@ module RuboCop
5
5
  # Functions for checking the alignment of the `end` keyword.
6
6
  module EndKeywordAlignment
7
7
  include ConfigurableEnforcedStyle
8
-
9
- BYTE_ORDER_MARK = 0xfeff # The Unicode codepoint
8
+ include RangeHelp
10
9
 
11
10
  MSG = '`end` at %<end_line>d, %<end_col>d is not aligned with ' \
12
11
  '`%<source>s` at %<align_line>d, %<align_col>d.'.freeze
@@ -36,7 +35,7 @@ module RuboCop
36
35
  def matching_ranges(end_loc, align_ranges)
37
36
  align_ranges.select do |_, range|
38
37
  range.line == end_loc.line ||
39
- effective_column(range) == end_loc.column
38
+ column_offset_between(range, end_loc).zero?
40
39
  end
41
40
  end
42
41
 
@@ -66,19 +65,6 @@ module RuboCop
66
65
  def line_break_before_keyword?(whole_expression, rhs)
67
66
  rhs.first_line > whole_expression.line
68
67
  end
69
-
70
- # Returns the column attribute of the range, except if the range is on
71
- # the first line and there's a byte order mark at the beginning of that
72
- # line, in which case 1 is subtracted from the column value. This gives
73
- # the column as it appears when viewing the file in an editor.
74
- def effective_column(range)
75
- if range.line == 1 &&
76
- @processed_source.raw_source.codepoints.first == BYTE_ORDER_MARK
77
- range.column - 1
78
- else
79
- range.column
80
- end
81
- end
82
68
  end
83
69
  end
84
70
  end
@@ -6,6 +6,8 @@ module RuboCop
6
6
  module RangeHelp
7
7
  private
8
8
 
9
+ BYTE_ORDER_MARK = 0xfeff # The Unicode codepoint
10
+
9
11
  def source_range(source_buffer, line_number, column, length = 1)
10
12
  if column.is_a?(Range)
11
13
  column_index = column.begin
@@ -72,8 +74,25 @@ module RuboCop
72
74
  .intersect(buffer.source_range)
73
75
  end
74
76
 
77
+ def column_offset_between(base_range, range)
78
+ effective_column(base_range) - effective_column(range)
79
+ end
80
+
75
81
  ## Helpers for above range methods. Do not use inside Cops.
76
82
 
83
+ # Returns the column attribute of the range, except if the range is on
84
+ # the first line and there's a byte order mark at the beginning of that
85
+ # line, in which case 1 is subtracted from the column value. This gives
86
+ # the column as it appears when viewing the file in an editor.
87
+ def effective_column(range)
88
+ if range.line == 1 &&
89
+ @processed_source.raw_source.codepoints.first == BYTE_ORDER_MARK
90
+ range.column - 1
91
+ else
92
+ range.column
93
+ end
94
+ end
95
+
77
96
  def directions(side)
78
97
  if side == :both
79
98
  [true, true]
@@ -63,7 +63,7 @@ module RuboCop
63
63
  when :comma
64
64
  multiline?(node) && no_elements_on_same_line?(node)
65
65
  when :consistent_comma
66
- multiline?(node)
66
+ multiline?(node) && !method_name_and_arguments_on_same_line?(node)
67
67
  else
68
68
  false
69
69
  end
@@ -77,7 +77,7 @@ module RuboCop
77
77
  end
78
78
 
79
79
  def heredoc?(source_after_last_item)
80
- source_after_last_item =~ /\w/
80
+ source_after_last_item !~ /^\s*#/ && source_after_last_item =~ /\w/
81
81
  end
82
82
 
83
83
  # Returns true if the node has round/square/curly brackets.
@@ -92,6 +92,12 @@ module RuboCop
92
92
  node.multiline? && !allowed_multiline_argument?(node)
93
93
  end
94
94
 
95
+ def method_name_and_arguments_on_same_line?(node)
96
+ node.send_type? &&
97
+ node.loc.selector.line == node.arguments.last.last_line &&
98
+ node.last_line == node.arguments.last.last_line
99
+ end
100
+
95
101
  # A single argument with the closing bracket on the same line as the end
96
102
  # of the argument is not considered multiline, even if the argument
97
103
  # itself might span multiple lines.
@@ -43,6 +43,11 @@ module RuboCop
43
43
  # if foo.blank?
44
44
  # something
45
45
  # end
46
+ #
47
+ # # good
48
+ # def blank?
49
+ # !present?
50
+ # end
46
51
  class Blank < Cop
47
52
  MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
48
53
  MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
@@ -70,6 +75,8 @@ module RuboCop
70
75
 
71
76
  def_node_matcher :not_present?, '(send (send $_ :present?) :!)'
72
77
 
78
+ def_node_matcher :defining_blank?, '(def :blank? (args) ...)'
79
+
73
80
  def_node_matcher :unless_present?, <<-PATTERN
74
81
  (:if $(send $_ :present?) {nil? (...)} ...)
75
82
  PATTERN
@@ -78,6 +85,9 @@ module RuboCop
78
85
  return unless cop_config['NotPresent']
79
86
 
80
87
  not_present?(node) do |receiver|
88
+ # accepts !present? if its in the body of a `blank?` method
89
+ next if defining_blank?(node.parent)
90
+
81
91
  add_offense(node,
82
92
  message: format(MSG_NOT_PRESENT,
83
93
  prefer: replacement(receiver),
@@ -26,7 +26,6 @@ module RuboCop
26
26
  # Date.yesterday
27
27
  # Date.today
28
28
  # date.to_time
29
- # date.to_time_in_current_zone
30
29
  #
31
30
  # # good
32
31
  # Time.zone.today
@@ -42,7 +41,7 @@ module RuboCop
42
41
  # Time.zone.today - 1.day
43
42
  # Date.current
44
43
  # Date.yesterday
45
- # date.to_time_in_current_zone
44
+ # date.in_time_zone
46
45
  #
47
46
  class Date < Cop
48
47
  include ConfigurableEnforcedStyle
@@ -55,6 +54,13 @@ module RuboCop
55
54
 
56
55
  BAD_DAYS = %i[today current yesterday tomorrow].freeze
57
56
 
57
+ DEPRECATED_METHODS = [
58
+ { deprecated: 'to_time_in_current_zone', relevant: 'in_time_zone' }
59
+ ].freeze
60
+
61
+ DEPRECATED_MSG = '`%<deprecated>s` is deprecated. ' \
62
+ 'Use `%<relevant>s` instead.'.freeze
63
+
58
64
  def on_const(node)
59
65
  mod, klass = *node.children
60
66
  # we should only check core Date class (`Date` or `::Date`)
@@ -68,12 +74,25 @@ module RuboCop
68
74
 
69
75
  return if safe_chain?(node) || safe_to_time?(node)
70
76
 
77
+ check_deprecated_methods(node)
78
+
71
79
  add_offense(node, location: :selector,
72
80
  message: format(MSG_SEND, method: node.method_name))
73
81
  end
74
82
 
75
83
  private
76
84
 
85
+ def check_deprecated_methods(node)
86
+ DEPRECATED_METHODS.each do |relevant:, deprecated:|
87
+ next unless node.method_name == deprecated.to_sym
88
+
89
+ add_offense(node, location: :selector,
90
+ message: format(DEPRECATED_MSG,
91
+ deprecated: deprecated,
92
+ relevant: relevant))
93
+ end
94
+ end
95
+
77
96
  def check_date_node(node)
78
97
  chain = extract_method_chain(node)
79
98
 
@@ -124,7 +143,7 @@ module RuboCop
124
143
  end
125
144
 
126
145
  def bad_methods
127
- style == :strict ? %i[to_time to_time_in_current_zone] : [:to_time]
146
+ %i[to_time to_time_in_current_zone]
128
147
  end
129
148
 
130
149
  def good_methods
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for calls to `link_to` that contain a
7
+ # `target: '_blank'` but no `rel: 'noopener'`. This can be a security
8
+ # risk as the loaded page will have control over the previous page
9
+ # and could change its location for phishing purposes.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # link_to 'Click here', url, target: '_blank'
14
+ #
15
+ # # good
16
+ # link_to 'Click here', url, target: '_blank', rel: 'noopener'
17
+ class LinkToBlank < Cop
18
+ MSG = 'Specify a `:rel` option containing noopener.'.freeze
19
+
20
+ def_node_matcher :blank_target?, <<-PATTERN
21
+ (pair {(sym :target) (str "target")} (str "_blank"))
22
+ PATTERN
23
+
24
+ def_node_matcher :includes_noopener?, <<-PATTERN
25
+ (pair {(sym :rel) (str "rel")} (str #contains_noopener?))
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless node.method?(:link_to)
30
+
31
+ option_nodes = node.each_child_node(:hash)
32
+
33
+ option_nodes.map(&:children).each do |options|
34
+ blank = options.find { |o| blank_target?(o) }
35
+ if blank && options.none? { |o| includes_noopener?(o) }
36
+ add_offense(blank)
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def contains_noopener?(str)
44
+ return false unless str
45
+
46
+ str.split(' ').include?('noopener')
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -66,6 +66,8 @@ module RuboCop
66
66
  MSG = 'Tagging a string as html safe may be a security risk.'.freeze
67
67
 
68
68
  def on_send(node)
69
+ return if non_interpolated_string?(node)
70
+
69
71
  return unless looks_like_rails_html_safe?(node) ||
70
72
  looks_like_rails_raw?(node) ||
71
73
  looks_like_rails_safe_concat?(node)
@@ -76,6 +78,10 @@ module RuboCop
76
78
 
77
79
  private
78
80
 
81
+ def non_interpolated_string?(node)
82
+ node.receiver && node.receiver.str_type? && !node.receiver.dstr_type?
83
+ end
84
+
79
85
  def looks_like_rails_html_safe?(node)
80
86
  node.receiver && node.method?(:html_safe) && !node.arguments?
81
87
  end
@@ -80,12 +80,20 @@ module RuboCop
80
80
 
81
81
  if options
82
82
  corrector.replace(options.loc.expression,
83
- "#{validate_type}: { #{options.source} }")
83
+ "#{validate_type}: #{braced_options(options)}")
84
84
  else
85
85
  corrector.insert_after(node.loc.expression,
86
86
  ", #{validate_type}: true")
87
87
  end
88
88
  end
89
+
90
+ def braced_options(options)
91
+ if options.braces?
92
+ options.source
93
+ else
94
+ "{ #{options.source} }"
95
+ end
96
+ end
89
97
  end
90
98
  end
91
99
  end
@@ -87,7 +87,7 @@ module RuboCop
87
87
  end
88
88
 
89
89
  def ruby19_no_mixed_keys_check(pairs)
90
- if force_hash_rockets?
90
+ if force_hash_rockets?(pairs)
91
91
  check(pairs, ':', MSG_HASH_ROCKETS)
92
92
  elsif sym_indices?(pairs)
93
93
  check(pairs, '=>', MSG_19)
@@ -106,7 +106,7 @@ module RuboCop
106
106
 
107
107
  def autocorrect(node)
108
108
  lambda do |corrector|
109
- if style == :hash_rockets || force_hash_rockets?
109
+ if style == :hash_rockets || force_hash_rockets?(node.parent.pairs)
110
110
  autocorrect_hash_rockets(corrector, node)
111
111
  elsif style == :ruby19_no_mixed_keys || style == :no_mixed_keys
112
112
  autocorrect_no_mixed_keys(corrector, node)
@@ -204,11 +204,9 @@ module RuboCop
204
204
  end
205
205
  end
206
206
 
207
- def force_hash_rockets?(pairs = [])
208
- @force_hash_rockets ||= begin
209
- cop_config['UseHashRocketsWithSymbolValues'] &&
210
- pairs.map(&:value).any?(&:sym_type?)
211
- end
207
+ def force_hash_rockets?(pairs)
208
+ cop_config['UseHashRocketsWithSymbolValues'] &&
209
+ pairs.map(&:value).any?(&:sym_type?)
212
210
  end
213
211
  end
214
212
  end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  # to `true` allows the presence of parentheses in multi-line method
22
22
  # calls.
23
23
  #
24
- # @example EnforcedStyle: require_parentheses
24
+ # @example EnforcedStyle: require_parentheses (default)
25
25
  #
26
26
  #
27
27
  # # bad
@@ -156,7 +156,7 @@ module RuboCop
156
156
  return if node.implicit_call?
157
157
  return if super_call_without_arguments?(node)
158
158
  return if camel_case_method_call_without_arguments?(node)
159
- return if eligible_for_parentheses_presence?(node)
159
+ return if legitimate_call_with_parentheses?(node)
160
160
 
161
161
  add_offense(node, location: node.loc.begin.join(node.loc.end))
162
162
  end
@@ -225,10 +225,11 @@ module RuboCop
225
225
  node.camel_case_method? && node.arguments.none?
226
226
  end
227
227
 
228
- def eligible_for_parentheses_presence?(node)
228
+ def legitimate_call_with_parentheses?(node)
229
229
  call_in_literals?(node) ||
230
230
  call_with_ambiguous_arguments?(node) ||
231
231
  call_in_logical_operators?(node) ||
232
+ call_in_optional_arguments?(node) ||
232
233
  allowed_multiline_call_with_parentheses?(node) ||
233
234
  allowed_chained_call_with_parentheses?(node)
234
235
  end
@@ -237,6 +238,7 @@ module RuboCop
237
238
  node.parent &&
238
239
  (node.parent.pair_type? ||
239
240
  node.parent.array_type? ||
241
+ splat?(node.parent) ||
240
242
  ternary_if?(node.parent))
241
243
  end
242
244
 
@@ -246,21 +248,26 @@ module RuboCop
246
248
  node.parent.descendants.any?(&method(:logical_operator?)))
247
249
  end
248
250
 
251
+ def call_in_optional_arguments?(node)
252
+ node.parent && node.parent.optarg_type?
253
+ end
254
+
249
255
  def call_with_ambiguous_arguments?(node)
250
256
  call_with_braced_block?(node) ||
251
- call_as_argument?(node) ||
257
+ call_as_argument_or_chain?(node) ||
252
258
  hash_literal_in_arguments?(node) ||
253
259
  node.descendants.any? do |n|
254
- splat?(n) || ternary_if?(n) || logical_operator?(n)
260
+ ambigious_literal?(n) || logical_operator?(n) ||
261
+ call_with_braced_block?(n)
255
262
  end
256
263
  end
257
264
 
258
265
  def call_with_braced_block?(node)
259
- node.block_node && node.block_node.braces?
266
+ node.send_type? && node.block_node && node.block_node.braces?
260
267
  end
261
268
 
262
- def call_as_argument?(node)
263
- node.parent && node.parent.send_type?
269
+ def call_as_argument_or_chain?(node)
270
+ node.parent && (node.parent.send_type? || node.parent.csend_type?)
264
271
  end
265
272
 
266
273
  def hash_literal_in_arguments?(node)
@@ -275,7 +282,7 @@ module RuboCop
275
282
  end
276
283
 
277
284
  def allowed_chained_call_with_parentheses?(node)
278
- return unless cop_config['AllowParenthesesInChaining']
285
+ return false unless cop_config['AllowParenthesesInChaining']
279
286
 
280
287
  previous = node.descendants.first
281
288
  return false unless previous && previous.send_type?
@@ -284,6 +291,10 @@ module RuboCop
284
291
  allowed_chained_call_with_parentheses?(previous)
285
292
  end
286
293
 
294
+ def ambigious_literal?(node)
295
+ splat?(node) || ternary_if?(node) || regexp_slash_literal?(node)
296
+ end
297
+
287
298
  def splat?(node)
288
299
  node.splat_type? || node.kwsplat_type? || node.block_pass_type?
289
300
  end
@@ -299,6 +310,10 @@ module RuboCop
299
310
  def hash_literal?(node)
300
311
  node.hash_type? && node.braces?
301
312
  end
313
+
314
+ def regexp_slash_literal?(node)
315
+ node.regexp_type? && node.loc.begin.source == '/'
316
+ end
302
317
  end
303
318
  end
304
319
  end
@@ -21,6 +21,18 @@ module RuboCop
21
21
  # # ...
22
22
  # end
23
23
  #
24
+ # In case there are private methods, the cop won't be activated.
25
+ # Otherwise, it forces to change the flow of the default code.
26
+ #
27
+ # @example EnforcedStyle: module_function (default)
28
+ # # good
29
+ # module Test
30
+ # extend self
31
+ # # ...
32
+ # private
33
+ # # ...
34
+ # end
35
+ #
24
36
  # @example EnforcedStyle: extend_self
25
37
  # # bad
26
38
  # module Test
@@ -46,6 +58,7 @@ module RuboCop
46
58
 
47
59
  def_node_matcher :module_function_node?, '(send nil? :module_function)'
48
60
  def_node_matcher :extend_self_node?, '(send nil? :extend self)'
61
+ def_node_matcher :private_directive?, '(send nil? :private ...)'
49
62
 
50
63
  def on_module(node)
51
64
  _name, body = *node
@@ -71,8 +84,10 @@ module RuboCop
71
84
  def each_wrong_style(nodes)
72
85
  case style
73
86
  when :module_function
87
+ private_directive = nodes.any? { |node| private_directive?(node) }
88
+
74
89
  nodes.each do |node|
75
- yield node if extend_self_node?(node)
90
+ yield node if extend_self_node?(node) && !private_directive
76
91
  end
77
92
  when :extend_self
78
93
  nodes.each do |node|
@@ -79,7 +79,7 @@ module RuboCop
79
79
 
80
80
  def preferred_delimiters_for(type)
81
81
  PreferredDelimiters
82
- .new(type, @config, @preferred_delimiters)
82
+ .new(type, @config, nil)
83
83
  .delimiters
84
84
  end
85
85
 
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # method(1, 2,)
11
11
  #
12
12
  # # good
13
+ # method(1, 2)
14
+ #
15
+ # # good
13
16
  # method(
14
17
  # 1, 2,
15
18
  # 3,
@@ -26,6 +29,9 @@ module RuboCop
26
29
  # method(1, 2,)
27
30
  #
28
31
  # # good
32
+ # method(1, 2)
33
+ #
34
+ # # good
29
35
  # method(
30
36
  # 1,
31
37
  # 2,
@@ -36,6 +42,9 @@ module RuboCop
36
42
  # method(1, 2,)
37
43
  #
38
44
  # # good
45
+ # method(1, 2)
46
+ #
47
+ # # good
39
48
  # method(
40
49
  # 1,
41
50
  # 2
@@ -38,7 +38,7 @@ module RuboCop
38
38
  def file_finished(file, offenses)
39
39
  offenses.each do |o|
40
40
  @cops_with_offenses[o.cop_name] += 1
41
- @files_with_offenses[o.cop_name] ||= []
41
+ @files_with_offenses[o.cop_name] ||= Set.new
42
42
  @files_with_offenses[o.cop_name] << file
43
43
  end
44
44
  end
@@ -85,7 +85,7 @@ module RuboCop
85
85
  def output_cop(cop_name, offense_count)
86
86
  output.puts
87
87
  cfg = self.class.config_to_allow_offenses[cop_name] || {}
88
- set_max(cfg, offense_count)
88
+ set_max(cfg, cop_name)
89
89
 
90
90
  # To avoid malformed YAML when potentially reading the config in
91
91
  # #excludes, we use an output buffer and append it to the actual output
@@ -96,13 +96,14 @@ module RuboCop
96
96
  output.puts(output_buffer.string)
97
97
  end
98
98
 
99
- def set_max(cfg, offense_count)
99
+ def set_max(cfg, cop_name)
100
100
  return unless cfg[:exclude_limit]
101
101
 
102
102
  # In case auto_gen_only_exclude is set, only modify the maximum if the
103
103
  # files are not excluded one by one.
104
- if !@options[:auto_gen_only_exclude] || offense_count > @exclude_limit
105
- cfg.merge! cfg[:exclude_limit]
104
+ if !@options[:auto_gen_only_exclude] ||
105
+ @files_with_offenses[cop_name].size > @exclude_limit
106
+ cfg.merge!(cfg[:exclude_limit])
106
107
  end
107
108
 
108
109
  # Remove already used exclude_limit.
@@ -171,7 +172,7 @@ module RuboCop
171
172
  def output_offending_files(output_buffer, cfg, cop_name)
172
173
  return unless cfg.empty?
173
174
 
174
- offending_files = @files_with_offenses[cop_name].uniq.sort
175
+ offending_files = @files_with_offenses[cop_name].sort
175
176
  if offending_files.count > @exclude_limit
176
177
  output_buffer.puts ' Enabled: false'
177
178
  else
@@ -184,8 +185,8 @@ module RuboCop
184
185
  parent = Pathname.new(Dir.pwd)
185
186
 
186
187
  output_buffer.puts ' Exclude:'
187
- excludes(offending_files, cop_name, parent).each do |file|
188
- output_exclude_path(output_buffer, file, parent)
188
+ excludes(offending_files, cop_name, parent).each do |exclude_path|
189
+ output_exclude_path(output_buffer, exclude_path, parent)
189
190
  end
190
191
  end
191
192
 
@@ -201,12 +202,17 @@ module RuboCop
201
202
  ((cfg['Exclude'] || []) + offending_files).uniq
202
203
  end
203
204
 
204
- def output_exclude_path(output_buffer, file, parent)
205
- file_path = Pathname.new(file)
205
+ def output_exclude_path(output_buffer, exclude_path, parent)
206
+ # exclude_path is either relative path, an absolute path, or a regexp.
207
+ file_path = Pathname.new(exclude_path)
206
208
  relative = file_path.relative_path_from(parent)
207
209
  output_buffer.puts " - '#{relative}'"
208
210
  rescue ArgumentError
211
+ file = exclude_path
209
212
  output_buffer.puts " - '#{file}'"
213
+ rescue TypeError
214
+ regexp = exclude_path
215
+ output_buffer.puts " - !ruby/regexp /#{regexp.source}/"
210
216
  end
211
217
  end
212
218
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'set'
4
+ require 'open3'
4
5
 
5
6
  module RuboCop
6
7
  # This class finds target files to inspect by scanning the directory tree
@@ -59,10 +60,13 @@ module RuboCop
59
60
  end
60
61
  all_files = find_files(base_dir, File::FNM_DOTMATCH)
61
62
  hidden_files = Set.new(all_files - find_files(base_dir, 0))
63
+
64
+ git_files = ls_git_files(base_dir)
65
+
62
66
  base_dir_config = @config_store.for(base_dir)
63
67
 
64
68
  target_files = all_files.select do |file|
65
- to_inspect?(file, hidden_files, base_dir_config)
69
+ to_inspect?(file, git_files, hidden_files, base_dir_config)
66
70
  end
67
71
 
68
72
  # Most recently modified file first.
@@ -71,13 +75,6 @@ module RuboCop
71
75
  target_files
72
76
  end
73
77
 
74
- def to_inspect?(file, hidden_files, base_dir_config)
75
- return false if base_dir_config.file_to_exclude?(file)
76
- return true if !hidden_files.include?(file) && ruby_file?(file)
77
-
78
- base_dir_config.file_to_include?(file)
79
- end
80
-
81
78
  # Search for files recursively starting at the given base directory using
82
79
  # the given flags that determine how the match is made. Excluded files will
83
80
  # be removed later by the caller, but as an optimization find_files removes
@@ -100,6 +97,31 @@ module RuboCop
100
97
  Dir.glob(pattern, flags).select { |path| FileTest.file?(path) }
101
98
  end
102
99
 
100
+ private
101
+
102
+ def ls_git_files(base_dir)
103
+ return if `sh -c 'command -v git'`.empty?
104
+
105
+ output, _error, status = Open3.capture3(
106
+ 'git', 'ls-files', '-z', base_dir,
107
+ '--exclude-standard', '--others', '--cached', '--modified'
108
+ )
109
+
110
+ return unless status.success?
111
+
112
+ output.split("\0").map { |git_file| "#{base_dir}/#{git_file}" }
113
+ end
114
+
115
+ def to_inspect?(file, git_files, hidden_files, base_dir_config)
116
+ return false if base_dir_config.file_to_exclude?(file)
117
+
118
+ if !hidden_files.include?(file) && ruby_file?(file)
119
+ return git_files.nil? || git_files.include?(file)
120
+ end
121
+
122
+ base_dir_config.file_to_include?(file)
123
+ end
124
+
103
125
  def toplevel_dirs(base_dir, flags)
104
126
  Dir.glob(File.join(base_dir, '*'), flags).select do |dir|
105
127
  File.directory?(dir) && !dir.end_with?('/.', '/..')
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.61.1'.freeze
6
+ STRING = '0.62.0'.freeze
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, running on ' \
9
9
  '%<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'.freeze
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.61.1
4
+ version: 0.62.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-12-06 00:00:00.000000000 Z
13
+ date: 2019-01-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: jaro_winkler
@@ -126,16 +126,22 @@ dependencies:
126
126
  name: bundler
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 1.3.0
132
+ - - "<"
130
133
  - !ruby/object:Gem::Version
131
- version: '1.3'
134
+ version: '3.0'
132
135
  type: :development
133
136
  prerelease: false
134
137
  version_requirements: !ruby/object:Gem::Requirement
135
138
  requirements:
136
- - - "~>"
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 1.3.0
142
+ - - "<"
137
143
  - !ruby/object:Gem::Version
138
- version: '1.3'
144
+ version: '3.0'
139
145
  - !ruby/object:Gem::Dependency
140
146
  name: rack
141
147
  requirement: !ruby/object:Gem::Requirement
@@ -552,6 +558,7 @@ files:
552
558
  - lib/rubocop/cop/rails/http_status.rb
553
559
  - lib/rubocop/cop/rails/inverse_of.rb
554
560
  - lib/rubocop/cop/rails/lexically_scoped_action_filter.rb
561
+ - lib/rubocop/cop/rails/link_to_blank.rb
555
562
  - lib/rubocop/cop/rails/not_null_column.rb
556
563
  - lib/rubocop/cop/rails/output.rb
557
564
  - lib/rubocop/cop/rails/output_safety.rb