Skip to content

Commit fcbe4e7

Browse files
committed
Adopt StandardLibraryNames in the C DeclaredAReservedIdentifier query
1 parent 189d66f commit fcbe4e7

File tree

3 files changed

+316
-21
lines changed

3 files changed

+316
-21
lines changed
Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
1-
| test.c:2:1:2:23 | #define _RESERVED_MACRO | Reserved identifier '_RESERVED_MACRO' is declared. |
2-
| test.c:11:8:11:9 | _s | Reserved identifier '_s' is declared. |
3-
| test.c:15:6:15:7 | _f | Reserved identifier '_f' is declared. |
4-
| test.c:19:7:19:12 | malloc | Reserved identifier 'malloc' is declared. |
5-
| test.c:24:12:24:16 | errno | Reserved identifier 'errno' is declared. |
1+
| test.c:2:1:2:23 | #define _RESERVED_MACRO | Macro '_RESERVED_MACRO' uses a reserved name beginning _ followed by an uppercase letter. |
2+
| test.c:6:1:6:27 | #define _not_reserved_MACRO | Macro '_not_reserved_MACRO' uses a name beginning with _ which is reserved in the ordinary and tag namespaces. |
3+
| test.c:11:8:11:9 | _s | Type '_s' uses a name beginning with _ which is reserved in the tag name space. |
4+
| test.c:15:6:15:7 | _f | Function '_f' uses a name beginning with _ which is reserved in the ordinary name space. |
5+
| test.c:19:7:19:12 | malloc | Function 'malloc' uses a reserved name from the C11 standard library header 'stdlib.h'. |
6+
| test.c:25:5:25:9 | errno | Global variable 'errno' uses a name reserved for a macro from the C11 standard library header 'errno.h'. |
7+
| test.c:25:5:25:9 | errno | Global variable 'errno' uses a reserved name from the C11 standard library header 'errno.h'. |
8+
| test.c:39:16:39:18 | log | Parameter 'log' uses a name reserved for a macro from the C11 standard library header 'tgmath.h'. |
9+
| test.c:43:5:43:16 | _Test_global | Global variable '_Test_global' uses a reserved name beginning _ followed by an uppercase letter. |
10+
| test.c:44:6:44:15 | _Test_func | Function '_Test_func' uses a reserved name beginning _ followed by an uppercase letter. |
11+
| test.c:45:9:45:19 | _Test_param | Parameter '_Test_param' uses a reserved name beginning _ followed by an uppercase letter. |
12+
| test.c:46:7:46:17 | _Test_local | Local variable '_Test_local' uses a reserved name beginning _ followed by an uppercase letter. |
13+
| test.c:47:10:47:27 | _Test_struct_local | Type '_Test_struct_local' uses a reserved name beginning _ followed by an uppercase letter. |
14+
| test.c:49:9:49:20 | _Test_member | Member variable '_Test_member' uses a reserved name beginning _ followed by an uppercase letter. |
15+
| test.c:52:8:52:19 | _Test_struct | Type '_Test_struct' uses a reserved name beginning _ followed by an uppercase letter. |
16+
| test.c:53:7:53:18 | _Test_member | Member variable '_Test_member' uses a reserved name beginning _ followed by an uppercase letter. |
17+
| test.c:55:1:56:3 | #define _TEST_MACRO x | Macro '_TEST_MACRO' uses a reserved name beginning _ followed by an uppercase letter. |
18+
| test.c:60:5:60:24 | __test_double_global | Global variable '__test_double_global' uses a reserved name beginning with __. |
19+
| test.c:61:6:61:23 | __test_double_func | Function '__test_double_func' uses a reserved name beginning with __. |
20+
| test.c:62:9:62:27 | __test_double_param | Parameter '__test_double_param' uses a reserved name beginning with __. |
21+
| test.c:63:7:63:25 | __test_double_local | Local variable '__test_double_local' uses a reserved name beginning with __. |
22+
| test.c:64:10:64:35 | __test_double_struct_local | Type '__test_double_struct_local' uses a reserved name beginning with __. |
23+
| test.c:65:9:65:28 | __test_double_member | Member variable '__test_double_member' uses a reserved name beginning with __. |
24+
| test.c:68:8:68:27 | __test_double_struct | Type '__test_double_struct' uses a reserved name beginning with __. |
25+
| test.c:69:7:69:26 | __test_double_member | Member variable '__test_double_member' uses a reserved name beginning with __. |
26+
| test.c:71:1:71:22 | #define __TEST_MACRO x | Macro '__TEST_MACRO' uses a reserved name beginning with __. |
27+
| test.c:78:5:78:22 | _test_lower_global | Global variable '_test_lower_global' uses a name beginning with _ which is reserved in the ordinary name space. |
28+
| test.c:79:6:79:21 | _test_lower_func | Function '_test_lower_func' uses a name beginning with _ which is reserved in the ordinary name space. |
29+
| test.c:88:8:88:19 | _test_struct | Type '_test_struct' uses a name beginning with _ which is reserved in the tag name space. |
30+
| test.c:91:1:92:3 | #define _test_macro x | Macro '_test_macro' uses a name beginning with _ which is reserved in the ordinary and tag namespaces. |

c/common/test/rules/declaredareservedidentifier/test.c

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,72 @@ void *malloc(int bytes) { // NON_COMPLIANT
2121
return ptr;
2222
}
2323

24-
extern int errno; // NON_COMPLIANT
24+
extern int
25+
errno; // NON_COMPLIANT - eerno is reserved as both a macro and identifier
26+
27+
void output(int a, int b, int c);
28+
29+
#define DEBUG(...) \
30+
output(__VA_ARGS__) // COMPLIANT - using not declaring `__VA_ARGS__`
31+
32+
void test() {
33+
DEBUG(1, 2, 3);
34+
__FUNCTION__; // COMPLIANT - use, not declaration of `__FUNCTION__`
35+
__PRETTY_FUNCTION__; // COMPLIANT - use, not declaration of
36+
// `__PRETTY_FUNCTION__`
37+
}
38+
39+
void test2(int log); // NON_COMPLIANT - tgmath.h defines log as a reserved macro
40+
41+
/* Test _[A-Z] */
42+
43+
int _Test_global; // NON_COMPLIANT - _ followed by capital is reserved
44+
void _Test_func( // NON_COMPLIANT - _ followed by capital is reserved
45+
int _Test_param) { // NON_COMPLIANT - _ followed by capital is reserved
46+
int _Test_local; // NON_COMPLIANT - _ followed by capital is reserved
47+
struct _Test_struct_local { // NON_COMPLIANT - _ followed by capital is
48+
// reserved
49+
int _Test_member; // NON_COMPLIANT - _ followed by capital is reserved
50+
};
51+
}
52+
struct _Test_struct { // NON_COMPLIANT - _ followed by capital is reserved
53+
int _Test_member; // NON_COMPLIANT - _ followed by capital is reserved
54+
};
55+
#define _TEST_MACRO \
56+
x // NON_COMPLIANT - single _ followed by capital is reserved
57+
58+
/* Test __ */
59+
60+
int __test_double_global; // NON_COMPLIANT - double _ is reserved
61+
void __test_double_func( // NON_COMPLIANT - double _ is reserved
62+
int __test_double_param) { // NON_COMPLIANT - double _ is reserved
63+
int __test_double_local; // NON_COMPLIANT - double _ is reserved
64+
struct __test_double_struct_local { // NON_COMPLIANT - double _ is reserved
65+
int __test_double_member; // NON_COMPLIANT - double _ is reserved
66+
};
67+
}
68+
struct __test_double_struct { // NON_COMPLIANT - double _ is reserved
69+
int __test_double_member; // NON_COMPLIANT - double _ is reserved
70+
};
71+
#define __TEST_MACRO x // NON_COMPLIANT - double _ is reserved
72+
73+
/*
74+
* Test _, but not followed by underscore or upper case, which is reserved in
75+
* file scope and ordinary/tag name spaces
76+
*/
77+
78+
int _test_lower_global; // NON_COMPLIANT - _ is reserved in ordinary name space
79+
void _test_lower_func( // NON_COMPLIANT - _ is reserved as a function name in
80+
// ordinary name space
81+
int _test_lower_param) { // COMPLIANT - _ is not reserved in the block name
82+
// space
83+
int _test; // COMPLIANT - _ is not reserved in the block name space
84+
struct _test_struct { // COMPLIANT - _ is not reserved in the block name space
85+
int _test; // COMPLIANT - _ is not reserved in the block name space
86+
};
87+
}
88+
struct _test_struct { // NON_COMPLIANT - _ is reserved in the tag name space
89+
int _test; // COMPLIANT - _ is not reserved in the member name space
90+
};
91+
#define _test_macro \
92+
x // NON_COMPLIANT - _ is reserved for macro names (otherwise you couldn't )

cpp/common/src/codingstandards/cpp/rules/declaredareservedidentifier/DeclaredAReservedIdentifier.qll

Lines changed: 217 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,237 @@
33
*/
44

55
import cpp
6+
import codingstandards.cpp.CKeywords
67
import codingstandards.cpp.Customizations
78
import codingstandards.cpp.Exclusions
8-
import codingstandards.cpp.Naming
9-
import codingstandards.cpp.CKeywords
9+
import codingstandards.cpp.Linkage
10+
import codingstandards.cpp.Macro
11+
import codingstandards.cpp.StandardLibraryNames
1012

1113
abstract class DeclaredAReservedIdentifierSharedQuery extends Query { }
1214

1315
Query getQuery() { result instanceof DeclaredAReservedIdentifierSharedQuery }
1416

15-
query predicate problems(Element m, string message) {
16-
not isExcluded(m, getQuery()) and
17-
exists(string name |
17+
newtype Scope =
18+
FileScope() or
19+
BlockScope() or
20+
FunctionScope() or
21+
MacroScope()
22+
23+
/**
24+
* A C name space according to C11 6.2.3, not to be confused with a C++ namespace.
25+
*/
26+
newtype TCNameSpace =
27+
LabelNameSpace() or
28+
TagNameSpace() or
29+
MemberNameSpace() or
30+
OrdinaryNameSpace() or
31+
// Create a "fake" name space for macro names
32+
MacroNameSpace()
33+
34+
class CNameSpace extends TCNameSpace {
35+
string toString() {
36+
this = LabelNameSpace() and result = "label"
37+
or
38+
this = TagNameSpace() and result = "tag"
39+
or
40+
this = MemberNameSpace() and result = "member"
41+
or
42+
this = OrdinaryNameSpace() and result = "ordinary"
43+
or
44+
this = MacroNameSpace() and result = "macro"
45+
}
46+
}
47+
48+
string getDeclDescription(Declaration d) {
49+
d instanceof Function and result = "Function"
50+
or
51+
d instanceof Parameter and result = "Parameter"
52+
or
53+
d instanceof LocalVariable and result = "Local variable"
54+
or
55+
d instanceof MemberVariable and result = "Member variable"
56+
or
57+
d instanceof GlobalVariable and result = "Global variable"
58+
or
59+
d instanceof UserType and result = "Type"
60+
or
61+
d instanceof EnumConstant and result = "Enum constant"
62+
}
63+
64+
/**
65+
* Whether a C declaration is considered to be at file scope.
66+
*/
67+
predicate isFileScope(Declaration d) {
68+
d.getFile().compiledAsC() and
69+
// Not inside a block - if the declaration is part of a DeclStmt, it is also part of a function
70+
// and therefore enclosed in at least one block
71+
not exists(DeclStmt ds | ds.getADeclaration() = d) and
72+
// Not inside a parameter list
73+
not d instanceof Parameter
74+
}
75+
76+
/**
77+
* The given C program element defines the `identifierName` in the given `Scope` and `CNameSpace`.
78+
*/
79+
predicate isCIdentifier(
80+
Element e, string identifierName, Scope scope, CNameSpace cNameSpace, string identifierDescription
81+
) {
82+
// This only makes sense on C compiled files
83+
e.getFile().compiledAsC() and
84+
(
85+
// An identifier can denote an object; a function; a tag or a member of a structure, union, or
86+
// enumeration; a typedef name; a label name; a macro name; or a macro parameter
87+
e.(Declaration).hasName(identifierName) and
88+
// Exclude any compiler generated identifiers
89+
not e.(Variable).isCompilerGenerated() and
90+
not e.(Function).isCompilerGenerated() and
91+
// Exclude local variables generated by the compiler (but not marked as such by our extractor)
92+
not e.(LocalScopeVariable).hasName(["__func__", "__PRETTY_FUNCTION__", "__FUNCTION__"]) and
93+
// Exclude special "macro"
94+
(
95+
if isFileScope(e)
96+
then
97+
// technically ignoring the function prototype scope, but we don't care for this use case
98+
scope = FileScope()
99+
else scope = BlockScope()
100+
) and
18101
(
19-
m.(Macro).hasName(name) or
20-
m.(Declaration).hasGlobalName(name)
102+
if e instanceof UserType
103+
then cNameSpace = TagNameSpace()
104+
else
105+
if (e instanceof MemberVariable or e instanceof MemberFunction)
106+
then cNameSpace = MemberNameSpace()
107+
else cNameSpace = OrdinaryNameSpace()
21108
) and
22109
(
23-
Naming::Cpp14::hasStandardLibraryMacroName(name)
110+
if exists(getDeclDescription(e))
111+
then identifierDescription = getDeclDescription(e)
112+
else identifierDescription = "Identifier"
113+
)
114+
or
115+
e.(LabelStmt).getName() = identifierName and
116+
scope = FunctionScope() and
117+
cNameSpace = LabelNameSpace() and
118+
identifierDescription = "Label"
119+
or
120+
e.(Macro).hasName(identifierName) and
121+
scope = MacroScope() and
122+
cNameSpace = MacroNameSpace() and
123+
identifierDescription = "Macro"
124+
or
125+
e.(FunctionLikeMacro).getAParameter() = identifierName and
126+
// Exclude __VA_ARGS__ as it is a special macro parameter
127+
not identifierName = "__VA_ARGS__..." and
128+
scope = MacroScope() and
129+
cNameSpace = MacroNameSpace() and
130+
identifierDescription = "Macro parameter"
131+
)
132+
}
133+
134+
module TargetedCLibrary = CStandardLibrary::C11;
135+
136+
query predicate problems(Element m, string message) {
137+
not isExcluded(m, getQuery()) and
138+
exists(
139+
string name, Scope scope, CNameSpace cNameSpace, string reason, string identifierDescription
140+
|
141+
isCIdentifier(m, name, scope, cNameSpace, identifierDescription) and
142+
message = identifierDescription + " '" + name + "' " + reason + "."
143+
|
144+
// C11 7.1.3/1
145+
// > All identifiers that begin with an underscore and either an uppercase letter or another
146+
// > underscore are always reserved for any use.
147+
name.regexpMatch("__.*") and
148+
// Exclude this macro which is intended to be implemented by the user
149+
not name = "__STDC_WANT_LIB_EXT1__" and
150+
reason = "uses a reserved name beginning with __"
151+
or
152+
name.regexpMatch("_[A-Z].*") and
153+
reason = "uses a reserved name beginning _ followed by an uppercase letter"
154+
or
155+
// > All identifiers that begin with an underscore are always reserved for use as identifiers
156+
// > with file scope in both the ordinary and tag name spaces.
157+
name.regexpMatch("_([^A-Z_].*)?") and
158+
scope = FileScope() and
159+
cNameSpace = [OrdinaryNameSpace().(TCNameSpace), TagNameSpace()] and
160+
reason = "uses a name beginning with _ which is reserved in the " + cNameSpace + " name space"
161+
or
162+
name.regexpMatch("_([^A-Z_].*)?") and
163+
scope = MacroScope() and
164+
cNameSpace = MacroNameSpace() and
165+
reason = "uses a name beginning with _ which is reserved in the ordinary and tag namespaces"
166+
or
167+
// > Each macro name in any of the following subclauses (including the future library
168+
// > directions) is reserved for use as specified if any of its associated headers is included;
169+
// > unless explicitly stated otherwise (see 7.1.4).
170+
exists(string header |
171+
TargetedCLibrary::hasMacroName(header, name, _) and
172+
reason =
173+
"uses a name reserved for a macro from the " + TargetedCLibrary::getName() +
174+
" standard library header '" + header + "'"
175+
)
176+
or
177+
// > All identifiers with external linkage in any of the following subclauses (including the
178+
// > future library directions) and errno are always reserved for use as identifiers with
179+
// > external linkage.184
180+
exists(string header |
181+
TargetedCLibrary::hasObjectName(header, _, name, _, "external")
24182
or
25-
Naming::Cpp14::hasStandardLibraryObjectName(name)
183+
TargetedCLibrary::hasFunctionName(header, _, "", name, _, _, "external")
26184
or
27-
Naming::Cpp14::hasStandardLibraryFunctionName(name)
185+
// > 184. The list of reserved identifiers with external linkage includes math_errhandling, setjmp,
186+
// > va_copy, and va_end.
187+
name = "errno" and
188+
header = "errno.h"
28189
or
29-
name.regexpMatch("_[A-Z_].*")
190+
name = "math_errhandling" and
191+
header = "math.h"
30192
or
31-
name.regexpMatch("_.*") and m.(Declaration).hasGlobalName(name)
193+
name = "setjmp" and
194+
header = "setjmp.h"
32195
or
33-
Keywords::isKeyword(name)
34-
) and
35-
message = "Reserved identifier '" + name + "' is declared."
196+
name = "va_copy" and
197+
header = "stdarg.h"
198+
or
199+
name = "va_end" and
200+
header = "stdarg.h"
201+
|
202+
hasExternalLinkage(m) and
203+
reason =
204+
"uses a reserved name from the " + TargetedCLibrary::getName() +
205+
" standard library header '" + header + "'"
206+
)
207+
or
208+
// > Each identifier with file scope listed in any of the following subclauses (including the
209+
// > future library directions) is reserved for use as a macro name and as an identifier with
210+
// > file scope in the same name space if any of its associated headers is included
211+
// Note: we do not consider the requirement of including the associated header
212+
exists(string header |
213+
TargetedCLibrary::hasObjectName(header, _, name, _, _) and
214+
(cNameSpace = OrdinaryNameSpace() or cNameSpace = MacroNameSpace())
215+
or
216+
TargetedCLibrary::hasFunctionName(header, _, "", name, _, _, _) and
217+
(cNameSpace = OrdinaryNameSpace() or cNameSpace = MacroNameSpace())
218+
or
219+
TargetedCLibrary::hasTypeName(header, _, name) and
220+
(cNameSpace = TagNameSpace() or cNameSpace = MacroNameSpace())
221+
or
222+
TargetedCLibrary::hasMemberVariableName(header, _, _, name, _) and
223+
(cNameSpace = OrdinaryNameSpace() or cNameSpace = MacroNameSpace())
224+
|
225+
(
226+
scope = FileScope()
227+
or
228+
scope = MacroScope()
229+
) and
230+
reason =
231+
"uses a reserved name from the " + TargetedCLibrary::getName() +
232+
" standard library header '" + header + "'"
233+
)
234+
or
235+
// C11 6.4.1/2
236+
Keywords::isKeyword(name) and
237+
reason = "it is a C11 keyword"
36238
)
37239
}

0 commit comments

Comments
 (0)