-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathMultipleInclusion.qll
More file actions
169 lines (149 loc) · 5.4 KB
/
MultipleInclusion.qll
File metadata and controls
169 lines (149 loc) · 5.4 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* Provides definitions related to _include guards_: techniques for preventing
* multiple inclusion of header files.
*/
import cpp
/**
* A header file with an include guard.
*/
abstract class IncludeGuardedHeader extends HeaderFile { }
/**
* A header file that uses a non-portable mechanism to prevent multiple
* inclusion.
*/
abstract class BadIncludeGuard extends IncludeGuardedHeader {
/** Gets the element to blame for this bad include guard pattern. */
abstract Element blame();
}
/**
* A header file with the correct include guard: `#ifndef` (or equivalent),
* `#define`, and `#endif`.
*/
class CorrectIncludeGuard extends IncludeGuardedHeader {
CorrectIncludeGuard() { correctIncludeGuard(this, _, _, _, _) }
/** Gets the name of the preprocessor define used to prevent multiple inclusion of this file. */
string getIncludeGuardName() { correctIncludeGuard(this, _, _, _, result) }
/** Gets the preprocessor macro used to prevent multiple inclusion of this file. */
Macro getDefine() { correctIncludeGuard(this, _, result, _, _) }
/** Gets the `#ifndef` or `#if` directive used to prevent multiple inclusion of this file. */
PreprocessorDirective getIfndef() { correctIncludeGuard(this, result, _, _, _) }
/** Gets the `#endif` directive closing this file. */
PreprocessorEndif getEndif() { correctIncludeGuard(this, _, _, result, _) }
}
/**
* A file with no code in it.
*/
class EmptyFile extends IncludeGuardedHeader {
EmptyFile() { this.(MetricFile).getNumberOfLinesOfCode() = 0 }
}
private predicate hasMacro(HeaderFile hf, string name, Macro define) {
define.getFile() = hf and define.getName() = name
}
/**
* Holds if `hf` begins with an `#ifndef` or `#if` directive `ifndef`, to test
* the macro named `includeGuard`, and ends with the matching `endif`.
*/
predicate hasIncludeGuard(
HeaderFile hf, PreprocessorDirective ifndef, PreprocessorEndif endif, string includeGuard
) {
startsWithIfndef(hf, ifndef, includeGuard) and
endsWithEndif(hf, endif) and
endif.getIf() = ifndef
}
/**
* Holds if `hf` uses a valid include guard with the macro named `includeGuard`
* and the preprocessor directives `ifndef`, `define`, and `endif`. This
* analysis is also exposed in an object-oriented style through the class
* `CorrectIncludeGuard`.
*/
pragma[noopt]
predicate correctIncludeGuard(
HeaderFile hf, PreprocessorDirective ifndef, Macro define, PreprocessorEndif endif,
string includeGuard
) {
hasIncludeGuard(hf, ifndef, endif, includeGuard) and
hasMacro(hf, includeGuard, define) and
// we already know the ifndef is first and the endif last, so we just need
// to check there is nothing before the define that isn't the ifndef.
not exists(
int relevant, Location ifndefLocation, int ifndefLine, Location defineLocation, int defineLine
|
includeGuardRelevantLine(hf, relevant) and
ifndefLocation = ifndef.getLocation() and
ifndefLine = ifndefLocation.getStartLine() and
relevant != ifndefLine and
defineLocation = define.getLocation() and
defineLine = defineLocation.getStartLine() and
relevant < defineLine
)
}
/**
* Holds if `hf` begins with an `#ifndef` or `#if` directive `ifndef`, to test
* the macro named `macroName`.
*/
predicate startsWithIfndef(HeaderFile hf, PreprocessorDirective ifndef, string macroName) {
ifndefDirective(ifndef, macroName) and
exists(Location loc |
loc = ifndef.getLocation() and
loc.getFile() = hf and
loc.getStartLine() = min(int l | includeGuardRelevantLine(hf, l))
)
}
private predicate endifLocation(PreprocessorEndif endif, File f, int line) {
endif.getFile() = f and
endif.getLocation().getStartLine() = line
}
private predicate lastEndifLocation(PreprocessorEndif endif, File f, int line) {
endifLocation(endif, f, line) and
line = max(int line2 | endifLocation(_, f, line2))
}
/**
* Holds if `hf` ends with `endif`.
*/
predicate endsWithEndif(HeaderFile hf, PreprocessorEndif endif) {
exists(int line | lastEndifLocation(endif, hf, line) |
line = max(int l | includeGuardRelevantLine(hf, l) | l)
)
}
private predicate includeGuardRelevantLine(HeaderFile hf, int line) {
exists(Location l | l.getFile() = hf and line = l.getStartLine() |
// any declaration
exists(Declaration d | l = d.getADeclarationLocation())
or
// most preprocessor directives
exists(PreprocessorDirective p |
l = p.getLocation() and
// included files may be outside the include guards, as they
// should contain an include guarding mechanism of their own.
not p instanceof Include
)
)
}
/**
* Holds if `ppd` is effectively an `#ifndef` directive that tests `macro`.
* This includes `#if !defined(macro)`.
*/
predicate ifndefDirective(PreprocessorDirective ppd, string macro) {
ppd instanceof PreprocessorIfndef and macro = ppd.getHead()
or
ppd instanceof PreprocessorIf and
exists(string head | head = ppd.getHead() |
macro =
head.replaceAll("(", " ")
.replaceAll(")", "")
.replaceAll("\t", " ")
.regexpCapture("[ ]*![ ]*defined[ ]+([^ ]*)[ ]*", 1)
.trim()
)
}
/**
* A header file with the `#pragma once` include guard.
*/
class PragmaOnceIncludeGuard extends BadIncludeGuard {
PragmaOnceIncludeGuard() {
exists(PreprocessorPragma p | p.getFile() = this and p.getHead() = "once")
}
override Element blame() {
exists(PreprocessorPragma p | p.getFile() = this and p = result and p.getHead() = "once")
}
}