-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathQualifiedName.qll
More file actions
298 lines (250 loc) · 9.31 KB
/
QualifiedName.qll
File metadata and controls
298 lines (250 loc) · 9.31 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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/**
* INTERNAL: Do not use. Provides classes and predicates for getting names of
* declarations, especially qualified names. Import this library `private` and
* qualified.
*
* This file contains classes that mirror the standard AST classes for C++, but
* these classes are only concerned with naming.
*/
private import semmle.code.cpp.Declaration as D
class Namespace extends @namespace {
string toString() { result = "QualifiedName Namespace" }
string getName() { namespaces(this, result) }
string getQualifiedName() {
if namespacembrs(_, this)
then
exists(Namespace ns |
namespacembrs(ns, pragma[only_bind_out](this)) and
result = ns.getQualifiedName() + "::" + this.getName()
)
else result = this.getName()
}
/**
* Gets a namespace qualifier, like `"namespace1::namespace2"`, through which
* the members of this namespace can be named. When `inline namespace` is
* used, this predicate may have multiple results.
*
* This predicate does not take namespace aliases into account. Unlike inline
* namespaces, specialization of templates cannot happen through an alias.
* Aliases are also local to the compilation unit, while inline namespaces
* affect the whole program.
*/
string getAQualifierForMembers() {
if namespacembrs(_, this)
then
exists(Namespace ns | namespacembrs(ns, pragma[only_bind_out](this)) |
result = ns.getAQualifierForMembers() + "::" + this.getName()
or
// If this is an inline namespace, its members are also visible in any
// namespace where the members of the parent are visible.
namespace_inline(this) and
result = ns.getAQualifierForMembers()
)
else result = this.getName()
}
Declaration getADeclaration() {
if this.getName() = ""
then result.isTopLevel() and not namespacembrs(_, result)
else namespacembrs(this, result)
}
}
class Declaration extends @declaration {
string toString() { result = "QualifiedName Declaration" }
/** Gets the name of this declaration. */
final string getName() { result = this.(D::Declaration).getName() }
string getTypeQualifierWithoutArgs() {
exists(UserType declaringType |
declaringType = this.(EnumConstant).getDeclaringEnum()
or
declaringType = this.getDeclaringType()
|
result = getTypeQualifierForMembersWithoutArgs(declaringType)
)
}
string getTypeQualifierWithArgs() {
exists(UserType declaringType |
declaringType = this.(EnumConstant).getDeclaringEnum()
or
declaringType = this.getDeclaringType()
|
result = getTypeQualifierForMembersWithArgs(declaringType)
)
}
Namespace getNamespace() {
// Top level declaration in a namespace ...
result.getADeclaration() = this
or
// ... or nested in another structure.
exists(Declaration m | m = this and result = m.getDeclaringType().getNamespace())
or
exists(EnumConstant c | c = this and result = c.getDeclaringEnum().getNamespace())
}
predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) {
declarationHasQualifiedName(baseName, typeQualifier, namespaceQualifier, this)
}
string getQualifiedName() {
exists(string ns, string name |
ns = this.getNamespace().getQualifiedName() and
name = this.getName() and
this.canHaveQualifiedName()
|
exists(string t | t = this.getTypeQualifierWithArgs() |
if ns != "" then result = ns + "::" + t + "::" + name else result = t + "::" + name
)
or
not hasTypeQualifier(this) and
if ns != "" then result = ns + "::" + name else result = name
)
}
predicate canHaveQualifiedName() {
this.hasDeclaringType()
or
this instanceof EnumConstant
or
this instanceof Function
or
this instanceof UserType
or
this instanceof GlobalOrNamespaceVariable
}
predicate isTopLevel() {
not (
this.isMember() or
this instanceof FriendDecl or
this instanceof EnumConstant or
this instanceof Parameter or
this instanceof ProxyClass or
this instanceof LocalVariable or
this instanceof TypeTemplateParameter or
this.(UserType).isLocal()
)
}
/** Holds if this declaration is a member of a class/struct/union. */
predicate isMember() { this.hasDeclaringType() }
/** Holds if this declaration is a member of a class/struct/union. */
predicate hasDeclaringType() { exists(this.getDeclaringType()) }
/**
* Gets the class where this member is declared, if it is a member.
* For templates, both the template itself and all instantiations of
* the template are considered to have the same declaring class.
*/
UserType getDeclaringType() { this = result.getAMember() }
}
class Variable extends Declaration, @variable {
VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
}
class TemplateVariable extends Variable {
TemplateVariable() { is_variable_template(this) }
Variable getAnInstantiation() { variable_instantiation(result, this) }
}
class LocalScopeVariable extends Variable, @localscopevariable { }
class LocalVariable extends LocalScopeVariable, @localvariable { }
/**
* A particular declaration or definition of a C/C++ variable.
*/
class VariableDeclarationEntry extends @var_decl {
string toString() { result = "QualifiedName DeclarationEntry" }
Variable getDeclaration() { result = this.getVariable() }
/**
* Gets the variable which is being declared or defined.
*/
Variable getVariable() { var_decls(this, result, _, _, _) }
predicate isDefinition() { var_def(this) }
string getName() { var_decls(this, _, _, result, _) and result != "" }
}
class Parameter extends LocalScopeVariable, @parameter { }
class GlobalOrNamespaceVariable extends Variable, @globalvariable { }
// Unlike the usual `EnumConstant`, this one doesn't have a
// `getDeclaringType()`. This simplifies the recursive computation of type
// qualifier names since it can assume that any declaration with a
// `getDeclaringType()` should use that type in its type qualifier name.
class EnumConstant extends Declaration, @enumconstant {
UserType getDeclaringEnum() { enumconstants(this, result, _, _, _, _) }
}
class Function extends Declaration, @function {
predicate isConstructedFrom(Function f) { function_instantiation(this, f) }
Parameter getParameter(int n) { params(result, this, n, _) }
}
class TemplateFunction extends Function {
TemplateFunction() { is_function_template(this) and function_template_argument(this, _, _) }
Function getAnInstantiation() {
function_instantiation(result, this) and
not exists(@fun_decl fd | fun_decls(fd, this, _, _, _) and fun_specialized(fd))
}
}
class UserType extends Declaration, @usertype {
predicate isLocal() { enclosingfunction(this, _) }
// Gets a member of this class, if it's a class.
Declaration getAMember() {
exists(Declaration d | member(this, _, d) |
result = d or
result = d.(TemplateClass).getAnInstantiation() or
result = d.(TemplateFunction).getAnInstantiation() or
result = d.(TemplateVariable).getAnInstantiation()
)
}
}
class ProxyClass extends UserType {
ProxyClass() { usertypes(this, _, 9) }
}
class TypeTemplateParameter extends UserType {
TypeTemplateParameter() { usertypes(this, _, [7, 8]) }
}
class TemplateClass extends UserType {
TemplateClass() { usertypes(this, _, [15, 16, 17]) }
UserType getAnInstantiation() {
class_instantiation(result, this) and
class_template_argument(result, _, _)
}
}
class FriendDecl extends Declaration, @frienddecl {
UserType getDeclaringClass() { frienddecls(this, result, _, _) }
}
private string getUserTypeNameWithArgs(UserType t) { usertypes(t, result, _) }
private string getUserTypeNameWithoutArgs(UserType t) {
result = getUserTypeNameWithArgs(t).splitAt("<", 0)
}
private predicate hasTypeQualifier(Declaration d) {
d instanceof EnumConstant
or
d.hasDeclaringType()
}
private string getTypeQualifierForMembersWithArgs(UserType t) {
result = t.getTypeQualifierWithArgs() + "::" + getUserTypeNameWithArgs(t)
or
not hasTypeQualifier(t) and
result = getUserTypeNameWithArgs(t)
}
private string getTypeQualifierForMembersWithoutArgs(UserType t) {
result = t.getTypeQualifierWithoutArgs() + "::" + getUserTypeNameWithoutArgs(t)
or
not hasTypeQualifier(t) and
result = getUserTypeNameWithoutArgs(t)
}
// The order of parameters on this predicate is chosen to match the most common
// use case: finding a declaration that has a specific name. The declaration
// comes last because it's the output.
cached
private predicate declarationHasQualifiedName(
string baseName, string typeQualifier, string namespaceQualifier, Declaration d
) {
namespaceQualifier = d.getNamespace().getAQualifierForMembers() and
(
if hasTypeQualifier(d)
then typeQualifier = d.getTypeQualifierWithoutArgs()
else typeQualifier = ""
) and
(
baseName = getUserTypeNameWithoutArgs(d)
or
// If a declaration isn't a `UserType`, there are two ways it can still
// contain `<`:
// 1. If it's `operator<` or `operator<<`.
// 2. If it's a conversion operator like `operator TemplateClass<Arg>`.
// Perhaps these names ought to be fixed up, but we don't do that
// currently.
not d instanceof UserType and
baseName = d.getName()
) and
d.canHaveQualifiedName()
}