Skip to content

Commit f5210ac

Browse files
committed
Extract ReservedNames library.
1 parent 1fd72e2 commit f5210ac

File tree

2 files changed

+291
-276
lines changed

2 files changed

+291
-276
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/** A module for representing reserved names in C and C++. */
2+
3+
import cpp
4+
import codingstandards.cpp.ReservedNames
5+
import codingstandards.cpp.CKeywords
6+
import codingstandards.cpp.Linkage
7+
import codingstandards.cpp.Macro
8+
import codingstandards.cpp.StandardLibraryNames
9+
10+
module ReservedNames {
11+
module C11 {
12+
newtype Scope =
13+
FileScope() or
14+
BlockScope() or
15+
FunctionScope() or
16+
MacroScope()
17+
18+
/**
19+
* A C name space according to C11 6.2.3, not to be confused with a C++ namespace.
20+
*/
21+
newtype TCNameSpace =
22+
LabelNameSpace() or
23+
TagNameSpace() or
24+
MemberNameSpace() or
25+
OrdinaryNameSpace() or
26+
// Create a "fake" name space for macro names
27+
MacroNameSpace()
28+
29+
class CNameSpace extends TCNameSpace {
30+
string toString() {
31+
this = LabelNameSpace() and result = "label"
32+
or
33+
this = TagNameSpace() and result = "tag"
34+
or
35+
this = MemberNameSpace() and result = "member"
36+
or
37+
this = OrdinaryNameSpace() and result = "ordinary"
38+
or
39+
this = MacroNameSpace() and result = "macro"
40+
}
41+
}
42+
43+
private string getDeclDescription(Declaration d) {
44+
d instanceof Function and result = "Function"
45+
or
46+
d instanceof Parameter and result = "Parameter"
47+
or
48+
d instanceof LocalVariable and result = "Local variable"
49+
or
50+
d instanceof MemberVariable and result = "Member variable"
51+
or
52+
d instanceof GlobalVariable and result = "Global variable"
53+
or
54+
d instanceof UserType and result = "Type"
55+
or
56+
d instanceof EnumConstant and result = "Enum constant"
57+
}
58+
59+
/**
60+
* Whether a C declaration is considered to be at file scope.
61+
*/
62+
private predicate isFileScope(Declaration d) {
63+
d.getFile().compiledAsC() and
64+
// Not inside a block - if the declaration is part of a DeclStmt, it is also part of a function
65+
// and therefore enclosed in at least one block
66+
not exists(DeclStmt ds | ds.getADeclaration() = d) and
67+
// Not inside a parameter list
68+
not d instanceof Parameter
69+
}
70+
71+
/**
72+
* The given C program element defines the `identifierName` in the given `Scope` and `CNameSpace`.
73+
*/
74+
predicate isCIdentifier(
75+
Element e, string identifierName, Scope scope, CNameSpace cNameSpace,
76+
string identifierDescription
77+
) {
78+
// This only makes sense on C compiled files
79+
e.getFile().compiledAsC() and
80+
(
81+
// An identifier can denote an object; a function; a tag or a member of a structure, union, or
82+
// enumeration; a typedef name; a label name; a macro name; or a macro parameter
83+
e.(Declaration).hasName(identifierName) and
84+
// Exclude any compiler generated identifiers
85+
not e.(Variable).isCompilerGenerated() and
86+
not e.(Function).isCompilerGenerated() and
87+
// Exclude local variables generated by the compiler (but not marked as such by our extractor)
88+
not e.(LocalScopeVariable).hasName(["__func__", "__PRETTY_FUNCTION__", "__FUNCTION__"]) and
89+
// Exclude special "macro"
90+
(
91+
if isFileScope(e)
92+
then
93+
// technically ignoring the function prototype scope, but we don't care for this use case
94+
scope = FileScope()
95+
else scope = BlockScope()
96+
) and
97+
(
98+
if e instanceof UserType
99+
then
100+
if e instanceof TypedefType
101+
then
102+
// Typedef types are in the ordinary namespace
103+
cNameSpace = OrdinaryNameSpace()
104+
else
105+
// Other user-defined types are in the tag namespace
106+
cNameSpace = TagNameSpace()
107+
else
108+
if (e instanceof MemberVariable or e instanceof MemberFunction)
109+
then cNameSpace = MemberNameSpace()
110+
else cNameSpace = OrdinaryNameSpace()
111+
) and
112+
(
113+
if exists(getDeclDescription(e))
114+
then identifierDescription = getDeclDescription(e)
115+
else identifierDescription = "Identifier"
116+
)
117+
or
118+
e.(LabelStmt).getName() = identifierName and
119+
scope = FunctionScope() and
120+
cNameSpace = LabelNameSpace() and
121+
identifierDescription = "Label"
122+
or
123+
e.(Macro).hasName(identifierName) and
124+
scope = MacroScope() and
125+
cNameSpace = MacroNameSpace() and
126+
identifierDescription = "Macro"
127+
or
128+
e.(FunctionLikeMacro).getAParameter() = identifierName and
129+
// Exclude __VA_ARGS__ as it is a special macro parameter
130+
not identifierName = "__VA_ARGS__..." and
131+
scope = MacroScope() and
132+
cNameSpace = MacroNameSpace() and
133+
identifierDescription = "Macro parameter"
134+
)
135+
}
136+
137+
private Macro getGeneratedFrom(Element e) {
138+
isCIdentifier(e, _, _, _, _) and
139+
exists(MacroInvocation mi |
140+
mi = result.getAnInvocation() and
141+
mi.getAGeneratedElement() = e and
142+
not exists(MacroInvocation child |
143+
child.getParentInvocation() = mi and
144+
child.getAGeneratedElement() = e
145+
)
146+
)
147+
}
148+
149+
module TargetedCLibrary = CStandardLibrary::C11;
150+
151+
predicate isAReservedIdentifier(Element m, string message) {
152+
exists(
153+
string name, Scope scope, CNameSpace cNameSpace, string reason, string identifierDescription
154+
|
155+
isCIdentifier(m, name, scope, cNameSpace, identifierDescription) and
156+
// Exclude cases generated from library macros, because the user does not control them
157+
not getGeneratedFrom(m) instanceof LibraryMacro and
158+
message = identifierDescription + " '" + name + "' " + reason + "."
159+
|
160+
// C11 7.1.3/1
161+
// > All identifiers that begin with an underscore and either an uppercase letter or another
162+
// > underscore are always reserved for any use.
163+
name.regexpMatch("__.*") and
164+
// Exclude this macro which is intended to be implemented by the user
165+
not name = "__STDC_WANT_LIB_EXT1__" and
166+
reason = "declares a reserved name beginning with __"
167+
or
168+
name.regexpMatch("_[A-Z].*") and
169+
reason = "declares a reserved name beginning _ followed by an uppercase letter"
170+
or
171+
// > All identifiers that begin with an underscore are always reserved for use as identifiers
172+
// > with file scope in both the ordinary and tag name spaces.
173+
name.regexpMatch("_([^A-Z_].*)?") and
174+
scope = FileScope() and
175+
cNameSpace = [OrdinaryNameSpace().(TCNameSpace), TagNameSpace()] and
176+
reason =
177+
"declares a name beginning with _ which is reserved in the " + cNameSpace + " name space"
178+
or
179+
name.regexpMatch("_([^A-Z_].*)?") and
180+
scope = MacroScope() and
181+
cNameSpace = MacroNameSpace() and
182+
reason =
183+
"declares a name beginning with _ which is reserved in the ordinary and tag namespaces"
184+
or
185+
// > Each macro name in any of the following subclauses (including the future library
186+
// > directions) is reserved for use as specified if any of its associated headers is included;
187+
// > unless explicitly stated otherwise (see 7.1.4).
188+
exists(string header |
189+
TargetedCLibrary::hasMacroName(header, name, _) and
190+
// The relevant header is included directly or transitively by the file
191+
m.getFile().getAnIncludedFile*().getBaseName() = header and
192+
reason =
193+
"declares a name reserved for a macro from the " + TargetedCLibrary::getName() +
194+
" standard library header '" + header + "'"
195+
)
196+
or
197+
// > All identifiers with external linkage in any of the following subclauses (including the
198+
// > future library directions) and errno are always reserved for use as identifiers with
199+
// > external linkage.184
200+
exists(string header |
201+
TargetedCLibrary::hasObjectName(header, _, name, _, "external")
202+
or
203+
TargetedCLibrary::hasFunctionName(header, _, "", name, _, _, "external")
204+
or
205+
// > 184. The list of reserved identifiers with external linkage includes math_errhandling, setjmp,
206+
// > va_copy, and va_end.
207+
name = "errno" and
208+
header = "errno.h"
209+
or
210+
name = "math_errhandling" and
211+
header = "math.h"
212+
or
213+
name = "setjmp" and
214+
header = "setjmp.h"
215+
or
216+
name = "va_copy" and
217+
header = "stdarg.h"
218+
or
219+
name = "va_end" and
220+
header = "stdarg.h"
221+
|
222+
hasExternalLinkage(m) and
223+
reason =
224+
"declares a name which is reserved for external linkage from the " +
225+
TargetedCLibrary::getName() + " standard library header '" + header + "'"
226+
)
227+
or
228+
// > Each identifier with file scope listed in any of the following subclauses (including the
229+
// > future library directions) is reserved for use as a macro name and as an identifier with
230+
// > file scope in the same name space if any of its associated headers is included
231+
//
232+
// Note: these cases are typically already rejected by the compiler, which prohibits redeclaration
233+
// of existing symbols. The macro cases are expected to work, though.
234+
exists(string header |
235+
TargetedCLibrary::hasObjectName(header, _, name, _, _) and
236+
(cNameSpace = OrdinaryNameSpace() or cNameSpace = MacroNameSpace())
237+
or
238+
TargetedCLibrary::hasFunctionName(header, _, "", name, _, _, _) and
239+
(cNameSpace = OrdinaryNameSpace() or cNameSpace = MacroNameSpace())
240+
or
241+
exists(string typeName |
242+
TargetedCLibrary::hasTypeName(header, _, typeName) and
243+
// Strip struct/union/enum prefix
244+
name = typeName.regexpReplaceAll("^(struct|union|enum) ", "")
245+
|
246+
(
247+
if typeName.regexpMatch("^(struct|union|enum) ")
248+
then
249+
// struct, union and enum types are in the tag namespace
250+
cNameSpace = TagNameSpace()
251+
else
252+
// typedef and therefore part of the ordinary namespace
253+
cNameSpace = OrdinaryNameSpace()
254+
)
255+
or
256+
cNameSpace = MacroNameSpace()
257+
)
258+
or
259+
exists(string declaringType, Class c |
260+
TargetedCLibrary::hasMemberVariableName(header, _, declaringType, name, _)
261+
|
262+
// Each declaring type has its own namespace, so check that it's declared in the same
263+
cNameSpace = MemberNameSpace() and
264+
c.getAMember() = m and
265+
c.getSimpleName() = declaringType
266+
or
267+
cNameSpace = MacroNameSpace()
268+
)
269+
|
270+
(
271+
scope = FileScope()
272+
or
273+
scope = MacroScope()
274+
) and
275+
// The relevant header is included directly or transitively by the file
276+
m.getFile().getAnIncludedFile*().getBaseName() = header and
277+
reason =
278+
"declares a reserved name from the " + TargetedCLibrary::getName() +
279+
" standard library header '" + header +
280+
"' which is included directly or indirectly in this translation unit"
281+
)
282+
or
283+
// C11 6.4.1/2
284+
Keywords::isKeyword(name) and
285+
reason = "declares a reserved name which is a C11 keyword"
286+
)
287+
}
288+
}
289+
}

0 commit comments

Comments
 (0)