Class: RuboCop::DirectiveComment

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/directive_comment.rb

Overview

This class wraps the Parser::Source::Comment object that represents a special rubocop:disable and rubocop:enable comment and exposes what cops it contains.

Constant Summary collapse

LINT_DEPARTMENT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'Lint'
LINT_REDUNDANT_DIRECTIVE_COP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"#{LINT_DEPARTMENT}/RedundantCopDisableDirective"
LINT_SYNTAX_COP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"#{LINT_DEPARTMENT}/Syntax"
STYLE_DISABLE_COPS_DIRECTIVE_COP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'Style/DisableCopsWithinSourceCodeDirective'
COP_NAME_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'([A-Za-z]\w+/)*(?:[A-Za-z]\w+)'
COP_NAME_PATTERN_NC =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'(?:[A-Za-z]\w+/)*[A-Za-z]\w+'
COP_NAMES_PATTERN_NC =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"(?:#{COP_NAME_PATTERN_NC} , )*#{COP_NAME_PATTERN_NC}"
COP_NAMES_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}"
COPS_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"(all|#{COP_NAMES_PATTERN})"
PUSH_POP_ARGS_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"([+\\-]#{COP_NAME_PATTERN_NC}(?:\\s+[+\\-]#{COP_NAME_PATTERN_NC})*)"
AVAILABLE_MODES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%w[disable enable todo push pop disable-next todo-next].freeze
MODES_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Longest first, so a -next mode is not matched as its prefix (- is a word boundary).

AVAILABLE_MODES.sort_by { |mode| -mode.length }.join('|').freeze
DIRECTIVE_MARKER_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'# rubocop : '
DIRECTIVE_MARKER_REGEXP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Regexp.new(DIRECTIVE_MARKER_PATTERN.gsub(' ', '\s*'))
DIRECTIVE_HEADER_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"#{DIRECTIVE_MARKER_PATTERN}((?:#{MODES_PATTERN}))\\b"
DIRECTIVE_COMMENT_REGEXP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Regexp.new(
  "#{DIRECTIVE_HEADER_PATTERN}(?:\\s+#{COPS_PATTERN}|\\s+#{PUSH_POP_ARGS_PATTERN})?"
    .gsub(' ', '\s*')
)
TRAILING_COMMENT_MARKER =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'--'
MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Regexp.new(
  "\\A#{DIRECTIVE_HEADER_PATTERN}\\s*\\z".gsub(' ', '\s*')
)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(comment, cop_registry = Cop::Registry.global) ⇒ DirectiveComment

Returns a new instance of DirectiveComment.



58
59
60
61
62
63
64
# File 'lib/rubocop/directive_comment.rb', line 58

def initialize(comment, cop_registry = Cop::Registry.global)
  @comment = comment
  @cop_registry = cop_registry
  match_data = comment.text.match(DIRECTIVE_COMMENT_REGEXP)
  @match_data = match_data&.pre_match&.match?(/\A#\s*\z/) ? nil : match_data
  @mode, @cops = match_captures
end

Instance Attribute Details

#commentObject (readonly)

Returns the value of attribute comment.



56
57
58
# File 'lib/rubocop/directive_comment.rb', line 56

def comment
  @comment
end

#cop_registryObject (readonly)

Returns the value of attribute cop_registry.



56
57
58
# File 'lib/rubocop/directive_comment.rb', line 56

def cop_registry
  @cop_registry
end

#copsObject (readonly)

Returns the value of attribute cops.



56
57
58
# File 'lib/rubocop/directive_comment.rb', line 56

def cops
  @cops
end

#modeObject (readonly)

Returns the value of attribute mode.



56
57
58
# File 'lib/rubocop/directive_comment.rb', line 56

def mode
  @mode
end

Class Method Details

.before_comment(line) ⇒ Object



52
53
54
# File 'lib/rubocop/directive_comment.rb', line 52

def self.before_comment(line)
  line.split(DIRECTIVE_COMMENT_REGEXP).first
end

Instance Method Details

#all_cops?Boolean

Checks if all cops specified in this directive

Returns:

  • (Boolean)


182
183
184
# File 'lib/rubocop/directive_comment.rb', line 182

def all_cops?
  cops == 'all'
end

#cop_namesObject

Returns array of specified in this directive cop names



187
188
189
# File 'lib/rubocop/directive_comment.rb', line 187

def cop_names
  @cop_names ||= all_cops? ? all_cop_names : parsed_cop_names
end

#department_namesObject

Returns array of specified in this directive department names when all department disabled



198
199
200
# File 'lib/rubocop/directive_comment.rb', line 198

def department_names
  raw_cop_names.select { |cop| department?(cop) }
end

#directive_countObject



212
213
214
# File 'lib/rubocop/directive_comment.rb', line 212

def directive_count
  raw_cop_names.count
end

#disable_next?Boolean

Checks if this directive disables cops for the next statement only

Returns:

  • (Boolean)


147
148
149
# File 'lib/rubocop/directive_comment.rb', line 147

def disable_next?
  %w[disable-next todo-next].include?(mode)
end

#disabled?Boolean

Checks if this directive disables cops

Returns:

  • (Boolean)


142
143
144
# File 'lib/rubocop/directive_comment.rb', line 142

def disabled?
  %w[disable todo].include?(mode) || disable_next?
end

#disabled_all?Boolean

Checks if this directive disables all cops

Returns:

  • (Boolean)


177
178
179
# File 'lib/rubocop/directive_comment.rb', line 177

def disabled_all?
  disabled? && all_cops?
end

#enabled?Boolean

Checks if this directive enables cops

Returns:

  • (Boolean)


152
153
154
# File 'lib/rubocop/directive_comment.rb', line 152

def enabled?
  mode == 'enable'
end

#enabled_all?Boolean

Checks if this directive enables all cops

Returns:

  • (Boolean)


172
173
174
# File 'lib/rubocop/directive_comment.rb', line 172

def enabled_all?
  !disabled? && all_cops?
end

#in_directive_department?(cop) ⇒ Boolean

Checks if directive departments include cop

Returns:

  • (Boolean)


203
204
205
# File 'lib/rubocop/directive_comment.rb', line 203

def in_directive_department?(cop)
  department_names.any? { |department| cop.start_with?(department) }
end

#line_numberObject

Returns line number for directive



217
218
219
# File 'lib/rubocop/directive_comment.rb', line 217

def line_number
  comment.source_range.line
end

#malformed?Boolean

Checks if the comment is malformed as a # rubocop: directive

Returns:

  • (Boolean)


72
73
74
75
76
77
78
# File 'lib/rubocop/directive_comment.rb', line 72

def malformed?
  return true if !start_with_marker? || @match_data.nil?
  return true if missing_cop_name?

  tail = @match_data.post_match.lstrip
  !(tail.empty? || tail.start_with?(TRAILING_COMMENT_MARKER))
end

#match?(cop_names) ⇒ Boolean

Checks if this directive contains all the given cop names

Returns:

  • (Boolean)


105
106
107
# File 'lib/rubocop/directive_comment.rb', line 105

def match?(cop_names)
  parsed_cop_names.uniq.sort == cop_names.uniq.sort
end

#match_capturesObject

Returns match captures to directive comment pattern



131
132
133
134
135
136
137
138
139
# File 'lib/rubocop/directive_comment.rb', line 131

def match_captures
  @match_captures ||= @match_data && begin
    captures = @match_data.captures
    mode = captures[0]
    # COPS_PATTERN is at captures[1], PUSH_POP_ARGS_PATTERN is at captures[4]
    cops = captures[1] || captures[4]
    [mode, cops]
  end
end

#missing_cop_name?Boolean

Checks if the directive comment is missing a cop name

Returns:

  • (Boolean)


81
82
83
84
85
# File 'lib/rubocop/directive_comment.rb', line 81

def missing_cop_name?
  return false if push? || pop?

  MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP.match?(comment.text)
end

#overridden_by_department?(cop) ⇒ Boolean

Checks if cop department has already used in directive comment

Returns:

  • (Boolean)


208
209
210
# File 'lib/rubocop/directive_comment.rb', line 208

def overridden_by_department?(cop)
  in_directive_department?(cop) && raw_cop_names.include?(cop)
end

#pop?Boolean

Checks if this directive is a pop

Returns:

  • (Boolean)


162
163
164
# File 'lib/rubocop/directive_comment.rb', line 162

def pop?
  mode == 'pop'
end

#push?Boolean

Checks if this directive is a push

Returns:

  • (Boolean)


157
158
159
# File 'lib/rubocop/directive_comment.rb', line 157

def push?
  mode == 'push'
end

#push_argsObject

Returns the push arguments as a hash of cop names with their operations



167
168
169
# File 'lib/rubocop/directive_comment.rb', line 167

def push_args
  @push_args ||= parse_push_args
end

#rangeObject



109
110
111
112
113
114
115
# File 'lib/rubocop/directive_comment.rb', line 109

def range
  match = comment.text.match(DIRECTIVE_COMMENT_REGEXP)
  begin_pos = comment.source_range.begin_pos
  Parser::Source::Range.new(
    comment.source_range.source_buffer, begin_pos + match.begin(0), begin_pos + match.end(0)
  )
end

#range_with_reasonObject

#range stops at the cop list, so a -- reason sits outside it. The reason documents the directive and means nothing once the directive is gone, so removal has to cover both. Any other trailing text is an ordinary comment and is left alone.



120
121
122
123
124
125
126
127
128
# File 'lib/rubocop/directive_comment.rb', line 120

def range_with_reason
  directive_range = range
  trailing = Parser::Source::Range.new(
    comment.source_range.source_buffer, directive_range.end_pos, comment.source_range.end_pos
  )
  return directive_range unless trailing.source.lstrip.start_with?(TRAILING_COMMENT_MARKER)

  directive_range.with(end_pos: comment.source_range.end_pos)
end

#raw_cop_namesObject

Returns an array of cops for this directive comment, without resolving departments



192
193
194
# File 'lib/rubocop/directive_comment.rb', line 192

def raw_cop_names
  @raw_cop_names ||= (cops || '').split(/,\s*/)
end

#reasonObject

The text of the directive's optional -- trailing comment, or nil when there is none.



89
90
91
92
93
94
95
96
97
# File 'lib/rubocop/directive_comment.rb', line 89

def reason
  return unless @match_data

  tail = @match_data.post_match.lstrip
  return unless tail.start_with?(TRAILING_COMMENT_MARKER)

  reason = tail.delete_prefix(TRAILING_COMMENT_MARKER).strip
  reason unless reason.empty?
end

#single_line?Boolean

Checks if this directive relates to single line

Returns:

  • (Boolean)


100
101
102
# File 'lib/rubocop/directive_comment.rb', line 100

def single_line?
  !comment.text.start_with?(DIRECTIVE_COMMENT_REGEXP)
end

#start_with_marker?Boolean

Checks if the comment starts with # rubocop: marker

Returns:

  • (Boolean)


67
68
69
# File 'lib/rubocop/directive_comment.rb', line 67

def start_with_marker?
  comment.text.start_with?(DIRECTIVE_MARKER_REGEXP)
end