Class: PlatformosCheck::Offense

Inherits:
Object
  • Object
show all
Includes:
PositionHelper
Defined in:
lib/platformos_check/offense.rb

Constant Summary collapse

MAX_SOURCE_EXCERPT_SIZE =
120

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PositionHelper

#bounded, #from_index_to_row_column, #from_row_column_to_index

Constructor Details

#initialize(check:, message: nil, app_file: nil, node: nil, markup: nil, line_number: nil, node_markup_offset: 0, correction: nil) ⇒ Offense

Returns a new instance of Offense.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/platformos_check/offense.rb', line 11

def initialize(
  check:, # instance of a PlatformosCheck::Check
  message: nil, # error message for the offense
  app_file: nil, # AppFile
  node: nil, # Node
  markup: nil, # string
  line_number: nil, # line number of the error (1-indexed)
  # node_markup_offset is the index inside node.markup to start
  # looking for markup :mindblow:.
  # This is so we can accurately highlight node substrings.
  # e.g. if we have the following scenario in which we
  # want to highlight the middle comma
  #   * node.markup == "replace ',',', '"
  #   * markup == ","
  # Then we need some way of telling our Position class to start
  # looking for the second comma. This is done with node_markup_offset.
  # More context can be found in #376.
  node_markup_offset: 0,
  correction: nil # block
)
  @check = check
  @correction = correction

  if message
    @message = message
  elsif defined?(check.class::MESSAGE)
    @message = check.class::MESSAGE
  else
    raise ArgumentError, "message required"
  end

  @node = node
  @app_file = node&.app_file || app_file
  @markup = markup || node&.markup

  raise ArgumentError, "Offense markup cannot be an empty string" if @markup.is_a?(String) && @markup.empty?

  @line_number = line_number || @node&.line_number

  @position = Position.new(
    @markup,
    @app_file&.source,
    line_number_1_indexed: @line_number,
    node_markup_offset:,
    node_markup: node&.markup
  )
end

Instance Attribute Details

#app_fileObject (readonly)

Returns the value of attribute app_file.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def app_file
  @app_file
end

#checkObject (readonly)

Returns the value of attribute check.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def check
  @check
end

#correctionObject (readonly)

Returns the value of attribute correction.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def correction
  @correction
end

#line_numberObject (readonly)

Returns the value of attribute line_number.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def line_number
  @line_number
end

#markupObject (readonly)

Returns the value of attribute markup.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def markup
  @markup
end

#messageObject (readonly)

Returns the value of attribute message.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def message
  @message
end

#nodeObject (readonly)

Returns the value of attribute node.



9
10
11
# File 'lib/platformos_check/offense.rb', line 9

def node
  @node
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



189
190
191
192
193
194
195
196
# File 'lib/platformos_check/offense.rb', line 189

def ==(other)
  other.is_a?(Offense) &&
    code_name == other.code_name &&
    message == other.message &&
    location == other.location &&
    start_index == other.start_index &&
    end_index == other.end_index
end

#check_nameObject



122
123
124
# File 'lib/platformos_check/offense.rb', line 122

def check_name
  StringHelpers.demodulize(check.class.name)
end

#code_nameObject



110
111
112
# File 'lib/platformos_check/offense.rb', line 110

def code_name
  check.code_name
end

#correct(corrector = nil) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/platformos_check/offense.rb', line 148

def correct(corrector = nil)
  if correctable?
    corrector ||= Corrector.new(app_file:)
    correction.call(corrector)
  end
rescue StandardError => e
  PlatformosCheck.bug(<<~EOS)
    Exception while running `Offense#correct`:
    ```
    #{e.class}: #{e.message}
      #{e.backtrace.join("\n  ")}
    ```

    Offense:
    ```
    #{JSON.pretty_generate(to_h)}
    ```
    Check options:
    ```
    #{check.options.pretty_inspect}
    ```
    Markup:
    ```
    #{markup}
    ```
    Node.Markup:
    ```
    #{node&.markup}
    ```
  EOS
  exit(2)
end

#correctable?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/platformos_check/offense.rb', line 144

def correctable?
  !!correction
end

#docObject



130
131
132
# File 'lib/platformos_check/offense.rb', line 130

def doc
  check.doc
end

#end_columnObject



106
107
108
# File 'lib/platformos_check/offense.rb', line 106

def end_column
  @position.end_column
end

#end_indexObject



98
99
100
# File 'lib/platformos_check/offense.rb', line 98

def end_index
  @position.end_index
end

#end_rowObject



102
103
104
# File 'lib/platformos_check/offense.rb', line 102

def end_row
  @position.end_row
end

#in_range?(other_range) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
# File 'lib/platformos_check/offense.rb', line 72

def in_range?(other_range)
  # Zero length ranges are OK and considered the same as size 1 ranges
  other_range = other_range.first..other_range.end if other_range.size == 0 # rubocop:disable Style/ZeroLengthPredicate
  range.cover?(other_range) || other_range.cover?(range)
end

#locationObject



134
135
136
137
# File 'lib/platformos_check/offense.rb', line 134

def location
  tokens = [app_file&.relative_path, line_number].compact
  tokens.join(":") if tokens.any?
end

#location_rangeObject



139
140
141
142
# File 'lib/platformos_check/offense.rb', line 139

def location_range
  tokens = [app_file&.relative_path, start_index, end_index].compact
  tokens.join(":") if tokens.any?
end

#markup_start_in_excerptObject



114
115
116
# File 'lib/platformos_check/offense.rb', line 114

def markup_start_in_excerpt
  source_excerpt.index(markup) if markup
end

#rangeObject



78
79
80
81
82
83
84
# File 'lib/platformos_check/offense.rb', line 78

def range
  @range ||= if start_index == end_index
               (start_index..end_index)
             else
               (start_index...end_index) # end_index is excluded
             end
end

#severityObject



118
119
120
# File 'lib/platformos_check/offense.rb', line 118

def severity
  check.severity
end

#single_file?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'lib/platformos_check/offense.rb', line 185

def single_file?
  check.single_file?
end

#source_excerptObject



59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/platformos_check/offense.rb', line 59

def source_excerpt
  return unless line_number

  @source_excerpt ||= begin
    excerpt = app_file.source_excerpt(line_number)
    if excerpt.size > MAX_SOURCE_EXCERPT_SIZE
      excerpt[0, MAX_SOURCE_EXCERPT_SIZE - 3] + '...'
    else
      excerpt
    end
  end
end

#start_columnObject



94
95
96
# File 'lib/platformos_check/offense.rb', line 94

def start_column
  @position.start_column
end

#start_indexObject



86
87
88
# File 'lib/platformos_check/offense.rb', line 86

def start_index
  @position.start_index
end

#start_rowObject



90
91
92
# File 'lib/platformos_check/offense.rb', line 90

def start_row
  @position.start_row
end

#to_hObject



215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/platformos_check/offense.rb', line 215

def to_h
  {
    check: check.code_name,
    path: app_file&.relative_path,
    severity: check.severity_value,
    start_row:,
    start_column:,
    end_row:,
    end_column:,
    message:
  }
end

#to_sObject



199
200
201
202
203
204
205
# File 'lib/platformos_check/offense.rb', line 199

def to_s
  if app_file
    "#{message} at #{location}"
  else
    message
  end
end

#to_s_rangeObject



207
208
209
210
211
212
213
# File 'lib/platformos_check/offense.rb', line 207

def to_s_range
  if app_file
    "#{message} at #{location_range}"
  else
    message
  end
end

#versionObject



126
127
128
# File 'lib/platformos_check/offense.rb', line 126

def version
  app_file&.version
end

#whole_platformos_app?Boolean

Returns:

  • (Boolean)


181
182
183
# File 'lib/platformos_check/offense.rb', line 181

def whole_platformos_app?
  check.whole_platformos_app?
end