Skip to content

Commit 7e339c5

Browse files
Merge pull request #1106 from github/michaelrfairhust/toolchain2-be-iso-cpp17-compliant
Initial implementation of rule 4-1-1, must be standard C++17
2 parents d6a6d46 + e33289f commit 7e339c5

File tree

9 files changed

+434
-2
lines changed

9 files changed

+434
-2
lines changed

cpp/common/src/codingstandards/cpp/Extensions.qll

Lines changed: 227 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,231 @@ import cpp
66
abstract class CompilerExtension extends Locatable { }
77

88
/**
9-
* Common base class for modeling compiler extensions in CPP.
9+
* A usage of a compiler extension in C++ code, such as non-standard attributes or built-in function
10+
* calls.
1011
*/
11-
abstract class CPPCompilerExtension extends CompilerExtension { }
12+
abstract class CPPCompilerExtension extends CompilerExtension {
13+
abstract string getMessage();
14+
}
15+
16+
/**
17+
* An `Attribute` that may be a `GnuAttribute` or `Declspec`, or `MicrosoftAttribute`, etc.
18+
*
19+
* There are language extensions such as GNU `__attribute__`, Microsoft `__declspec` or
20+
* `[attribute]` syntax.
21+
*/
22+
class CPPAttributeExtension extends CPPCompilerExtension, Attribute {
23+
CPPAttributeExtension() { not this instanceof StdAttribute and not this instanceof AlignAs }
24+
25+
override string getMessage() {
26+
result =
27+
"Use of attribute '" + getName() +
28+
"' is a compiler extension and is not portable to other compilers."
29+
}
30+
}
31+
32+
/**
33+
* A `StdAttribute` within a compiler specific namespace such as `[[gnu::weak]]`.
34+
*/
35+
class CPPNamespacedStdAttributeExtension extends CPPCompilerExtension, StdAttribute {
36+
CPPNamespacedStdAttributeExtension() { exists(this.getNamespace()) and not getNamespace() = "" }
37+
38+
override string getMessage() {
39+
result =
40+
"Use of attribute '" + getName() + "' in namespace '" + getNamespace() +
41+
"' is a compiler extension and is not portable to other compilers."
42+
}
43+
}
44+
45+
/**
46+
* A `StdAttribute` with a name not recognized as part of the C++17 standard.
47+
*
48+
* Only the listed names are valid C++17. Namespaced attributes are handled by
49+
* `CPPNamespacedStdAttributeExtension` and not considered here.
50+
*/
51+
class CPPUnrecognizedAttributeExtension extends CPPCompilerExtension, StdAttribute {
52+
CPPUnrecognizedAttributeExtension() {
53+
not this instanceof CPPNamespacedStdAttributeExtension and
54+
not getName() in [
55+
"maybe_unused", "nodiscard", "noreturn", "deprecated", "carries_dependency", "fallthrough"
56+
]
57+
}
58+
59+
override string getMessage() {
60+
result = "Use of unrecognized or non-C++17 attribute '" + getName() + "'."
61+
}
62+
}
63+
64+
/**
65+
* A `FunctionCall` of a compiler-specific builtin function.
66+
*
67+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
68+
*/
69+
class CPPBuiltinFunctionExtension extends CPPCompilerExtension, FunctionCall {
70+
CPPBuiltinFunctionExtension() {
71+
getTarget().getName().indexOf("__builtin_") = 0 or
72+
getTarget().getName().indexOf("__sync_") = 0 or
73+
getTarget().getName().indexOf("__atomic_") = 0
74+
}
75+
76+
override string getMessage() {
77+
result =
78+
"Call to builtin function '" + getTarget().getName() +
79+
"' is a compiler extension and is not portable to other compilers."
80+
}
81+
}
82+
83+
/**
84+
* A `StmtExpr`, which uses `({ <stmts> })` syntax, which is a GNU extension.
85+
*
86+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
87+
*/
88+
class CPPStmtExprExtension extends CPPCompilerExtension, StmtExpr {
89+
override string getMessage() {
90+
result =
91+
"Statement expressions are a compiler extension and are not portable to other compilers."
92+
}
93+
}
94+
95+
/**
96+
* A `ConditionalExpr` using GNU-style omitted middle operand such as `x ?: y`.
97+
*
98+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Conditionals.html
99+
*/
100+
class CPPTerseTernaryExtension extends CPPCompilerExtension, ConditionalExpr {
101+
CPPTerseTernaryExtension() { getCondition() = getElse() or getCondition() = getThen() }
102+
103+
override string getMessage() {
104+
result =
105+
"Ternaries with omitted middle operands are a compiler extension and are not portable to " +
106+
"other compilers."
107+
}
108+
}
109+
110+
/**
111+
* A non-standard `Type` that is only available as a compiler extension, such as `__int128`,
112+
* `_Decimal32`, `_Decimal64`, `_Decimal128`, or `__float128`.
113+
*
114+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/__int128.html
115+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Decimal-Float.html
116+
*/
117+
class CPPExtensionType extends Type {
118+
CPPExtensionType() {
119+
this instanceof Int128Type or
120+
this instanceof Decimal128Type or
121+
this instanceof Decimal32Type or
122+
this instanceof Decimal64Type or
123+
this instanceof Float128Type
124+
}
125+
}
126+
127+
/**
128+
* A `DeclarationEntry` using an extended type such as `__int128`, `_Decimal32`, `_Decimal64`,
129+
* `_Decimal128`, or `__float128`.
130+
*
131+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/__int128.html
132+
*/
133+
class CPPExtensionTypeUsage extends CPPCompilerExtension, DeclarationEntry {
134+
CPPExtensionType extendedType;
135+
136+
CPPExtensionTypeUsage() { extendedType = getType() }
137+
138+
override string getMessage() {
139+
result =
140+
"Declaration of variable '" + getName() + "' as type '" + extendedType.getName() +
141+
"' requires a compiler extension and is not portable to other compilers."
142+
}
143+
}
144+
145+
/**
146+
* A `DeclarationEntry` using a zero-length array, which is a non-standard way to declare a flexible
147+
* array member.
148+
*
149+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
150+
*/
151+
class CPPZeroLengthArraysExtension extends CPPCompilerExtension, DeclarationEntry {
152+
ArrayType array;
153+
154+
CPPZeroLengthArraysExtension() {
155+
array = getType() and
156+
array.getArraySize() = 0
157+
}
158+
159+
override string getMessage() {
160+
result =
161+
"Declaration of variable '" + getName() + "' as a zero-length array (of '" +
162+
array.getBaseType() +
163+
"') requires a compiler extension and is not portable to other compilers."
164+
}
165+
}
166+
167+
/**
168+
* A `Field` with a variable-length array type in a struct member (not C++17 compliant).
169+
*
170+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html
171+
*/
172+
class CPPVariableLengthArraysExtension extends CPPCompilerExtension, VlaDeclStmt {
173+
override string getMessage() {
174+
result =
175+
"Variable length array (used in '" + this +
176+
"') are a compiler extension and are not portable to other compilers."
177+
}
178+
}
179+
180+
/**
181+
* A `PreprocessorIfdef` using a builtin preprocessor feature such as `__has_builtin`,
182+
* `__has_include`, etc., which are non-standard clang extensions.
183+
*
184+
* Reference: https://clang.llvm.org/docs/LanguageExtensions.html
185+
*/
186+
class CPPConditionalDefineExtension extends CPPCompilerExtension, PreprocessorIfdef {
187+
string feature;
188+
189+
CPPConditionalDefineExtension() {
190+
feature =
191+
[
192+
"__has_builtin", "__has_constexpr_builtin", "__has_feature", "__has_extension",
193+
"__has_attribute", "__has_declspec_attribute", "__is_identifier", "__has_include",
194+
"__has_include_next", "__has_warning", "__has_cpp_attribute"
195+
] and
196+
exists(toString().indexOf(feature))
197+
}
198+
199+
override string getMessage() {
200+
result =
201+
"Call to builtin preprocessor feature '" + feature +
202+
"' is a compiler extension and is not portable to other compilers."
203+
}
204+
}
205+
206+
/**
207+
* A `PreprocessorDirective` that is a non-standard compiler extension, such as `#pragma`, `#error`,
208+
* or `#warning`.
209+
*/
210+
class CPPPreprocessorDirectiveExtension extends CPPCompilerExtension, PreprocessorDirective {
211+
string kind;
212+
213+
CPPPreprocessorDirectiveExtension() {
214+
this instanceof PreprocessorPragma and kind = "#pragma " + getHead()
215+
or
216+
this instanceof PreprocessorError and kind = "#error"
217+
or
218+
this instanceof PreprocessorWarning and kind = "#warning"
219+
}
220+
221+
override string getMessage() {
222+
result = "Use of non-standard preprocessor directive '" + kind + "' is a compiler extension."
223+
}
224+
}
225+
226+
/**
227+
* A `BuiltInOperation` which describes certain non-standard syntax such as type trait operations,
228+
* for example GNU `__is_abstract(T)`, `__is_same(T, U)`, etc.
229+
*
230+
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
231+
*/
232+
class CPPBuiltinOperationExtension extends CPPCompilerExtension, BuiltInOperation {
233+
override string getMessage() {
234+
result = "Use of built-in operation '" + toString() + "' is a compiler extension."
235+
}
236+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import Statements
9595
import Strings
9696
import Templates
9797
import Toolchain
98+
import Toolchain2
9899
import Toolchain3
99100
import Trigraph
100101
import TrustBoundaries
@@ -197,6 +198,7 @@ newtype TCPPQuery =
197198
TStringsPackageQuery(StringsQuery q) or
198199
TTemplatesPackageQuery(TemplatesQuery q) or
199200
TToolchainPackageQuery(ToolchainQuery q) or
201+
TToolchain2PackageQuery(Toolchain2Query q) or
200202
TToolchain3PackageQuery(Toolchain3Query q) or
201203
TTrigraphPackageQuery(TrigraphQuery q) or
202204
TTrustBoundariesPackageQuery(TrustBoundariesQuery q) or
@@ -299,6 +301,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
299301
isStringsQueryMetadata(query, queryId, ruleId, category) or
300302
isTemplatesQueryMetadata(query, queryId, ruleId, category) or
301303
isToolchainQueryMetadata(query, queryId, ruleId, category) or
304+
isToolchain2QueryMetadata(query, queryId, ruleId, category) or
302305
isToolchain3QueryMetadata(query, queryId, ruleId, category) or
303306
isTrigraphQueryMetadata(query, queryId, ruleId, category) or
304307
isTrustBoundariesQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Toolchain2Query = TCompilerLanguageExtensionsUsedQuery()
7+
8+
predicate isToolchain2QueryMetadata(Query query, string queryId, string ruleId, string category) {
9+
query =
10+
// `Query` instance for the `compilerLanguageExtensionsUsed` query
11+
Toolchain2Package::compilerLanguageExtensionsUsedQuery() and
12+
queryId =
13+
// `@id` for the `compilerLanguageExtensionsUsed` query
14+
"cpp/misra/compiler-language-extensions-used" and
15+
ruleId = "RULE-4-1-1" and
16+
category = "required"
17+
}
18+
19+
module Toolchain2Package {
20+
Query compilerLanguageExtensionsUsedQuery() {
21+
//autogenerate `Query` type
22+
result =
23+
// `Query` type for `compilerLanguageExtensionsUsed` query
24+
TQueryCPP(TToolchain2PackageQuery(TCompilerLanguageExtensionsUsedQuery()))
25+
}
26+
}

cpp/common/test/includes/standard-library/type_traits.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ template <typename T, unsigned int N> struct remove_extent<T[N]> {
6262
typedef T type;
6363
};
6464

65+
template <typename T> struct is_abstract {
66+
const static bool value = false;
67+
constexpr operator bool() { return value; }
68+
};
69+
70+
template <typename T> bool is_abstract_v = is_abstract<T>::value;
71+
72+
template <typename T, typename U> struct is_same {
73+
const static bool value = false;
74+
constexpr operator bool() { return value; }
75+
};
76+
77+
template <typename T, typename U> bool is_same_v = is_same<T, U>::value;
78+
6579
template <class T> struct is_trivially_copy_constructible {
6680
const static bool value = true;
6781
constexpr operator bool() { return value; }
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @id cpp/misra/compiler-language-extensions-used
3+
* @name RULE-4-1-1: A program shall conform to ISO/IEC 14882:2017 (C++17)
4+
* @description Language extensions are compiler-specific features that are not part of the C++17
5+
* standard. Using these extensions reduces portability and may lead to unpredictable
6+
* behavior when code is compiled with different compilers or compiler versions.
7+
* @kind problem
8+
* @precision high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-4-1-1
11+
* scope/system
12+
* maintainability
13+
* portability
14+
* external/misra/enforcement/undecidable
15+
* external/misra/obligation/required
16+
*/
17+
18+
import cpp
19+
import codingstandards.cpp.misra
20+
import codingstandards.cpp.Extensions
21+
import codingstandards.cpp.AlertReporting
22+
23+
from Element unwrapped, CPPCompilerExtension e
24+
where
25+
not isExcluded([e, unwrapped], Toolchain2Package::compilerLanguageExtensionsUsedQuery()) and
26+
unwrapped = MacroUnwrapper<CPPCompilerExtension>::unwrapElement(e)
27+
select unwrapped, e.getMessage()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
| test.cpp:11:3:11:19 | maybe_unused | Use of attribute 'maybe_unused' in namespace 'gnu' is a compiler extension and is not portable to other compilers. |
2+
| test.cpp:12:16:12:22 | aligned | Use of attribute 'aligned' is a compiler extension and is not portable to other compilers. |
3+
| test.cpp:13:16:13:21 | unused | Use of attribute 'unused' is a compiler extension and is not portable to other compilers. |
4+
| test.cpp:14:21:14:28 | noreturn | Use of attribute 'noreturn' is a compiler extension and is not portable to other compilers. |
5+
| test.cpp:15:3:15:17 | deprecated | Use of attribute 'deprecated' in namespace 'gnu' is a compiler extension and is not portable to other compilers. |
6+
| test.cpp:16:3:16:23 | nonstandard_attribute | Use of unrecognized or non-C++17 attribute 'nonstandard_attribute'. |
7+
| test.cpp:19:3:19:20 | call to __builtin_popcount | Call to builtin function '__builtin_popcount' is a compiler extension and is not portable to other compilers. |
8+
| test.cpp:20:3:20:18 | call to __builtin_expect | Call to builtin function '__builtin_expect' is a compiler extension and is not portable to other compilers. |
9+
| test.cpp:21:3:21:22 | call to __sync_fetch_and_add_4 | Call to builtin function '__sync_fetch_and_add_4' is a compiler extension and is not portable to other compilers. |
10+
| test.cpp:23:3:23:20 | __is_abstract | Use of built-in operation '__is_abstract' is a compiler extension. |
11+
| test.cpp:24:3:24:22 | __is_same | Use of built-in operation '__is_same' is a compiler extension. |
12+
| test.cpp:30:3:33:4 | (statement expression) | Statement expressions are a compiler extension and are not portable to other compilers. |
13+
| test.cpp:34:3:34:12 | ... ? ... : ... | Ternaries with omitted middle operands are a compiler extension and are not portable to other compilers. |
14+
| test.cpp:36:12:36:13 | definition of l0 | Declaration of variable 'l0' as type '__int128' requires a compiler extension and is not portable to other compilers. |
15+
| test.cpp:45:20:45:30 | fallthrough | Use of attribute 'fallthrough' is a compiler extension and is not portable to other compilers. |
16+
| test.cpp:47:7:47:22 | fallthrough | Use of attribute 'fallthrough' in namespace 'gnu' is a compiler extension and is not portable to other compilers. |
17+
| test.cpp:55:3:55:29 | __builtin_va_start | Use of built-in operation '__builtin_va_start' is a compiler extension. |
18+
| test.cpp:60:7:60:8 | definition of m1 | Declaration of variable 'm1' as a zero-length array (of 'int') requires a compiler extension and is not portable to other compilers. |
19+
| test.cpp:64:31:64:41 | vector_size | Use of attribute 'vector_size' is a compiler extension and is not portable to other compilers. |
20+
| test.cpp:66:1:66:20 | #ifdef __has_builtin | Call to builtin preprocessor feature '__has_builtin' is a compiler extension and is not portable to other compilers. |
21+
| test.cpp:72:1:72:12 | #pragma once | Use of non-standard preprocessor directive '#pragma once' is a compiler extension. |
22+
| test.cpp:73:1:73:27 | #pragma GCC diagnostic push | Use of non-standard preprocessor directive '#pragma GCC diagnostic push' is a compiler extension. |
23+
| test.cpp:74:1:74:28 | #warning "This is a warning" | Use of non-standard preprocessor directive '#warning' is a compiler extension. |
24+
| test.cpp:76:1:76:40 | #warning "preceding spaces is common" | Use of non-standard preprocessor directive '#warning' is a compiler extension. |
25+
| test.cpp:81:7:81:7 | VLA declaration | Variable length array (used in 'VLA declaration') are a compiler extension and are not portable to other compilers. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-4-1-1/CompilerLanguageExtensionsUsed.ql

0 commit comments

Comments
 (0)