-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
CommentedOut.qll
143 lines (130 loc) · 5.06 KB
/
CommentedOut.qll
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/** Provides predicates for recognizing commented-out code. */
import semmle.javascript.Comments
/** Gets a line in comment `c` that looks like commented-out code. */
private string getALineOfCommentedOutCode(Comment c) {
result = c.getLine(_) and
// line ends with ';', '{', or '}', optionally followed by a comma,
(
result.regexpMatch(".*[;{}],?\\s*") and
// but it doesn't look like a JSDoc-like annotation
not result.regexpMatch(".*@\\w+\\s*\\{.*\\}\\s*") and
// and it does not contain three consecutive words (which is uncommon in code)
not result.regexpMatch("[^'\\\"]*\\w\\s++\\w++\\s++\\w[^'\\\"]*")
or
// line is part of a block comment and ends with something that looks
// like a line comment; character before '//' must not be ':' to
// avoid matching URLs
not c instanceof SlashSlashComment and
result.regexpMatch("(.*[^:]|^)//.*[^/].*")
or
// similar, but don't be fooled by '//// this kind of comment' and
// '//// this kind of comment ////'
c instanceof SlashSlashComment and
result.regexpMatch("/*([^/].*[^:]|[^:/])//.*[^/].*") and
// exclude externalization comments
not result.regexpMatch(".*\\$NON-NLS-\\d+\\$.*")
)
}
/**
* Holds if `c` is a comment containing code examples, and hence should be
* disregarded when looking for commented-out code.
*/
private predicate containsCodeExample(Comment c) {
c.getText().matches(["%<pre>%</pre>%", "%<code>%</code>%", "%@example%", "%```%"])
}
/**
* Gets a comment that belongs to a run of consecutive comments in file `f`
* starting with `c`, where `c` itself contains commented-out code, but the comment
* preceding it, if any, does not.
*/
private Comment getCommentInRun(File f, Comment c) {
exists(int n |
c.onLines(f, n, _) and
countCommentedOutLines(c) > 0 and
not exists(Comment d | d.onLines(f, _, n - 1) | countCommentedOutLines(d) > 0)
) and
(
result = c
or
exists(Comment prev, int n |
prev = getCommentInRun(f, c) and
prev.onLines(f, _, n) and
result.onLines(f, n + 1, _)
)
)
}
/**
* Gets a comment that follows `c` in a run of consecutive comments and
* does not contain a code example.
*/
private Comment getRelevantCommentInRun(Comment c) {
result = getCommentInRun(_, c) and not containsCodeExample(result)
}
/** Gets the number of lines in comment `c` that look like commented-out code. */
private int countCommentedOutLines(Comment c) { result = count(getALineOfCommentedOutCode(c)) }
/** Gets the number of non-blank lines in comment `c`. */
private int countNonBlankLines(Comment c) {
result = count(string line | line = c.getLine(_) and not line.regexpMatch("\\s*"))
}
/**
* Gets the number of lines in comment `c` and subsequent comments that look like
* they contain commented-out code.
*/
private int countCommentedOutLinesInRun(Comment c) {
result = sum(Comment d | d = getRelevantCommentInRun(c) | countCommentedOutLines(d))
}
/** Gets the number of non-blank lines in `c` and subsequent comments. */
private int countNonBlankLinesInRun(Comment c) {
result = sum(Comment d | d = getRelevantCommentInRun(c) | countNonBlankLines(d))
}
/**
* A run of consecutive comments containing a high percentage of lines
* that look like commented-out code.
*
* This is represented by the comment that starts the run, with a special
* `hasLocationInfo` implementation that assigns it the entire run as its location.
*/
class CommentedOutCode extends Comment {
CommentedOutCode() {
exists(int codeLines, int nonBlankLines |
countCommentedOutLines(this) > 0 and
not exists(Comment prev | this = getCommentInRun(_, prev) and this != prev) and
nonBlankLines = countNonBlankLinesInRun(this) and
codeLines = countCommentedOutLinesInRun(this) and
nonBlankLines > 0 and
2 * codeLines > nonBlankLines
)
}
/**
* Gets the number of lines in this run of comments
* that look like they contain commented-out code.
*/
int getNumCodeLines() { result = countCommentedOutLinesInRun(this) }
/**
* Gets the number of non-blank lines in this run of comments.
*/
int getNumNonBlankLines() { result = countNonBlankLinesInRun(this) }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
exists(Location loc, File f | loc = this.getLocation() and f = loc.getFile() |
filepath = f.getAbsolutePath() and
startline = loc.getStartLine() and
startcolumn = loc.getStartColumn() and
exists(Location last |
last = getCommentInRun(f, this).getLocation() and
last.getEndLine() = max(getCommentInRun(f, this).getLocation().getEndLine())
|
endline = last.getEndLine() and
endcolumn = last.getEndColumn()
)
)
}
}