-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathAutogeneratedFile.qll
140 lines (134 loc) · 4.68 KB
/
AutogeneratedFile.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
/**
* Provides a class and predicate for recognizing files that are likely to have been generated
* automatically.
*/
import semmle.code.cpp.Comments
import semmle.code.cpp.File
import semmle.code.cpp.Preprocessor
/**
* Holds if comment `c` indicates that it might be in an auto-generated file, for
* example because it contains the text "auto-generated by".
*/
bindingset[comment]
private predicate autogeneratedComment(string comment) {
// ?s = include newlines in anything (`.`)
// ?i = ignore case
exists(string cond |
cond =
// generated by (not mid-sentence)
"(^ generated by[^a-z])|" + "(! generated by[^a-z])|" +
// generated file
"(generated file)|" +
// file [is/was/has been] generated
"(file( is| was| has been)? generated)|" +
// changes made in this file will be lost
"(changes made in this file will be lost)|" +
// do not edit/modify (not mid-sentence)
"(^ do(n't|nt| not) (hand-?)?(edit|modify))|" +
"(! do(n't|nt| not) (hand-?)?(edit|modify))|" +
// do not edit/modify + generated
"(do(n't|nt| not) (hand-?)?(edit|modify).*generated)|" +
"(generated.*do(n't|nt| not) (hand-?)?(edit|modify))" and
comment
.regexpMatch("(?si).*(" +
// replace `generated` with a regexp that also catches things like
// `auto-generated`.
cond.replaceAll("generated", "(auto[\\w-]*[\\s/\\*\\r\\n]*)?generated")
// replace `!` with a regexp for end-of-sentence / separator characters.
.replaceAll("!", "[\\.\\?\\!\\-\\;\\,]")
// replace ` ` with a regexp for one or more whitespace characters
// (including newlines and `/*`).
.replaceAll(" ", "[\\s/\\*\\r\\n]+") + ").*")
)
}
/**
* Holds if the file contains `#line` pragmas that refer to a different file.
* For example, in `parser.c` a pragma `#line 1 "parser.rl"`.
* Such pragmas usually indicate that the file was automatically generated.
*/
predicate hasPragmaDifferentFile(File f) {
exists(PreprocessorLine pl, string s |
pl.getFile() = f and
pl.getHead().splitAt(" ", 1) = s and
/* Zero index is line number, one index is file reference */
not "\"" + f.getAbsolutePath() + "\"" = s and
not "\"" + f.getRelativePath() + "\"" = s and
not "\"" + f.getBaseName() + "\"" = s
)
}
/**
* The line where the first comment in file `f` begins (maximum of 5). This allows
* us to skip past any preprocessor logic or similar code before the first comment.
*/
private int fileFirstComment(File f) {
result =
min(int line |
exists(Comment c |
c.getFile() = f and
c.getLocation().getStartLine() = line and
line < 5
)
).minimum(5)
}
/**
* The line where the initial comments of file `f` end. This is just before the
* first bit of code, excluding anything skipped over by `fileFirstComment`.
*/
private int fileHeaderLimit(File f) {
exists(int fc |
fc = fileFirstComment(f) and
result =
min(int line |
// code ending the initial comments
exists(DeclarationEntry de, Location l |
l = de.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
exists(PreprocessorDirective pd, Location l |
l = pd.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
exists(NamespaceDeclarationEntry nde, Location l |
l = nde.getLocation() and
l.getFile() = f and
line = l.getStartLine() - 1 and
line > fc
)
or
// end of the file
line = f.getMetrics().getNumberOfLines()
or
// rarely, we've seen extremely long sequences of initial comments
// (and/or limitations in the above constraints) cause an overflow of
// the maximum string length. So don't look past 1000 lines regardless.
line = 1000
)
)
}
/**
* Holds if the file is probably an autogenerated file.
*
* A file is probably autogenerated if either of the following heuristics
* hold:
* 1. There is a comment in the start of the file that matches
* 'autogenerated', 'generated by', or a similar phrase.
* 2. There is a `#line` directive referring to a different file.
*/
class AutogeneratedFile extends File {
cached
AutogeneratedFile() {
autogeneratedComment(strictconcat(Comment c |
c.getFile() = this and
c.getLocation().getStartLine() <= fileHeaderLimit(this)
|
c.getContents() order by c.getLocation().getStartLine()
)) or
hasPragmaDifferentFile(this)
}
}