/
inline_comments.cr
105 lines (95 loc) · 3.14 KB
/
inline_comments.cr
1
2
3
4
5
6
7
8
9
10
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
module Ameba
# A module that utilizes inline comments parsing and processing logic.
module InlineComments
COMMENT_DIRECTIVE_REGEX =
/# ameba:(?<action>\w+) (?<rules>\w+(?:\/\w+)?(?:,? \w+(?:\/\w+)?)*)/
# Available actions in the inline comments
enum Action
Disable
Enable
end
# Returns `true` if current location is disabled for a particular rule,
# `false` otherwise.
#
# Location is disabled in two cases:
# 1. The line of the location ends with a comment directive.
# 2. The line above the location is a comment directive.
#
# For example, here are two examples of disabled location:
#
# ```
# # ameba:disable Style/LargeNumbers
# Time.epoch(1483859302)
#
# Time.epoch(1483859302) # ameba:disable Style/LargeNumbers
# ```
#
# But here are examples which are not considered as disabled location:
#
# ```
# # ameba:disable Style/LargeNumbers
# #
# Time.epoch(1483859302)
#
# if use_epoch? # ameba:disable Style/LargeNumbers
# Time.epoch(1483859302)
# end
# ```
def location_disabled?(location : Crystal::Location?, rule)
return false if rule.name.in?(Rule::SPECIAL)
return false unless line_number = location.try &.line_number.try &.- 1
return false unless line = lines[line_number]?
line_disabled?(line, rule) ||
(line_number > 0 &&
(prev_line = lines[line_number - 1]) &&
comment?(prev_line) &&
line_disabled?(prev_line, rule))
end
# Parses inline comment directive. Returns a tuple that consists of
# an action and parsed rules if directive found, nil otherwise.
#
# ```
# line = "# ameba:disable Rule1, Rule2"
# directive = parse_inline_directive(line)
# directive[:action] # => "disable"
# directive[:rules] # => ["Rule1", "Rule2"]
# ```
#
# It ignores the directive if it is commented out.
#
# ```
# line = "# # ameba:disable Rule1, Rule2"
# parse_inline_directive(line) # => nil
# ```
def parse_inline_directive(line)
return unless directive = COMMENT_DIRECTIVE_REGEX.match(line)
return if commented_out?(line.gsub(directive[0], ""))
{
action: directive["action"],
rules: directive["rules"].split(/[\s,]/, remove_empty: true),
}
end
# Returns `true` if the line at the given `line_number` is a comment.
def comment?(line_number : Int32)
return unless line = lines[line_number]?
comment?(line)
end
private def comment?(line : String)
line.lstrip.starts_with? '#'
end
private def line_disabled?(line, rule)
return false unless directive = parse_inline_directive(line)
return false unless Action.parse?(directive[:action]).try(&.disable?)
rules = directive[:rules]
rules.includes?(rule.name) || rules.includes?(rule.group)
end
private def commented_out?(line)
commented = false
lexer = Crystal::Lexer.new(line).tap(&.comments_enabled = true)
Tokenizer.new(lexer).run do |token|
commented = true if token.type.comment?
end
commented
end
end
end