Skip to content

Commit 34d836f

Browse files
authored
Merge pull request #538 from rvermeulen/rvermeulen/fix-371
Resolve 371
2 parents 5edc533 + 3b3fca0 commit 34d836f

File tree

6 files changed

+124
-12
lines changed

6 files changed

+124
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- `A5-1-1` - `LiteralValueUsedOutsideTypeInit.ql`:
2+
- Address FP reported in #371. Exclude literals generated by uses of constexpr variables.
3+
- Exclude literals used in class template instantiations.
4+
- Update the alert message to adhere to the style-guide.
5+
- Exclude boolean literals used as template arguments.
6+
- Exclude `u` and `U` prefixed char literals.
7+
- Exclude literals part of a class aggregate literal.

cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql

+14-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import cpp
1818
import codingstandards.cpp.autosar
1919
import codingstandards.cpp.LoggingOperation
2020
import codingstandards.cpp.Literals
21+
import codingstandards.cpp.Cpp14Literal
2122

2223
from Literal l
2324
where
@@ -35,11 +36,11 @@ where
3536
// Exclude literal 0
3637
not l.getValue() = "0" and
3738
// Exclude character literals
38-
not l instanceof CharLiteral and
39+
not l instanceof Cpp14Literal::CharLiteral and
3940
// Exclude `nullptr`
4041
not l.getType() instanceof NullPointerType and
4142
// Exclude boolean `true` and `false`
42-
not l.getType() instanceof BoolType and
43+
not l instanceof BoolLiteral and
4344
// Exclude empty string
4445
not l.getValue() = "" and
4546
// Template functions use literals to represent calls which are unknown
@@ -51,7 +52,14 @@ where
5152
// Aggregate literal
5253
not l = any(ArrayOrVectorAggregateLiteral aal).getAnElementExpr(_).getAChild*() and
5354
// Ignore x - 1 expressions
54-
not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1")
55-
select l,
56-
"Literal value " + getTruncatedLiteralText(l) + " used outside of type initialization " +
57-
l.getAPrimaryQlClass()
55+
not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1") and
56+
// Exclude compile time computed integral literals as they can appear as integral literals
57+
// when used as non-type template arguments.
58+
// We limit ourselves to integral literals, because floating point literals as non-type
59+
// template arguments are not supported in C++ 14. Those are supported shince C++ 20.
60+
not l instanceof CompileTimeComputedIntegralLiteral and
61+
// Exclude literals to instantiate a class template per example in the standard
62+
// where an type of std::array is intialized with size 5.
63+
not l = any(ClassTemplateInstantiation cti).getATemplateArgument() and
64+
not l = any(ClassAggregateLiteral cal).getAFieldExpr(_)
65+
select l, "Literal value '" + getTruncatedLiteralText(l) + "' used outside of type initialization."
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
| test.cpp:5:9:5:25 | constant string | Literal value "constant string" used outside of type initialization StringLiteral |
2-
| test.cpp:14:23:14:25 | 100 | Literal value 100 used outside of type initialization Literal |
3-
| test.cpp:54:7:54:7 | 1 | Literal value 1 used outside of type initialization Literal |
4-
| test.cpp:75:23:75:28 | test | Literal value "test" used outside of type initialization StringLiteral |
5-
| test.cpp:76:19:76:28 | not okay | Literal value "not okay" used outside of type initialization StringLiteral |
1+
| test.cpp:5:9:5:25 | constant string | Literal value '"constant string"' used outside of type initialization. |
2+
| test.cpp:14:23:14:25 | 100 | Literal value '100' used outside of type initialization. |
3+
| test.cpp:54:7:54:7 | 1 | Literal value '1' used outside of type initialization. |
4+
| test.cpp:75:23:75:28 | test | Literal value '"test"' used outside of type initialization. |
5+
| test.cpp:76:19:76:28 | not okay | Literal value '"not okay"' used outside of type initialization. |
6+
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |
7+
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |
8+
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |

cpp/autosar/test/rules/A5-1-1/test.cpp

+37-1
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,46 @@ void test_not_wrapper_stream(std::ostream &os, const char *str) noexcept {
8080
#define MACRO_LOG(test_str) \
8181
do { \
8282
struct test_struct { \
83-
static const char *get_str() { return static_cast<char *>(test_str); } \
83+
static const char *get_str() { \
84+
return static_cast<const char *>(test_str); \
85+
} \
8486
}; \
8587
} while (false)
8688

8789
void f() {
8890
MACRO_LOG("test"); // COMPLIANT - exclusion
91+
}
92+
93+
template <typename T> struct S1 { static constexpr size_t value(); };
94+
95+
template <> struct S1<int> {
96+
static constexpr size_t value() { return sizeof(int); };
97+
};
98+
99+
constexpr size_t g1 = S1<int>::value();
100+
constexpr size_t f1() { return sizeof(int); }
101+
102+
template <typename T, int size> struct S2 {
103+
T m1[size]; // COMPLIANT
104+
T m2[4]; // NON_COMPLIANT
105+
};
106+
107+
template <typename T, T val> struct S3 {
108+
static constexpr T value = val; // COMPLIANT;
109+
};
110+
111+
void test_fp_reported_in_371() {
112+
struct S2<int, 1> l1; // COMPLIANT
113+
struct S2<int, g1> l2; // COMPLIANT
114+
struct S2<int, f1()> l3; // COMPLIANT
115+
116+
S3<char16_t, u'\u03c0'> l4; // COMPLIANT
117+
S3<char16_t, S3<char16_t, u'\u2286'>::value> l5; // COMPLIANT
118+
S3<char32_t, U'🌌'> l6; // COMPLIANT
119+
S3<char32_t, S3<char32_t, U''>::value> l7; // COMPLIANT
120+
121+
constexpr float l8 = 3.14159f;
122+
#define delta 0.1f
123+
for (float i = 0.0f; i < l8; i += delta) { // COMPLIANT
124+
}
89125
}

cpp/common/src/codingstandards/cpp/Cpp14Literal.qll

+22
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,26 @@ module Cpp14Literal {
8282

8383
override string getAPrimaryQlClass() { result = "FloatingLiteral" }
8484
}
85+
86+
/**
87+
* A character literal. For example:
88+
* ```
89+
* char c1 = 'a';
90+
* char16_t c2 = u'a';
91+
* char32_t c3 = U'a';
92+
* wchar_t c4 = L'b';
93+
* ```
94+
*/
95+
class CharLiteral extends StandardLibrary::TextLiteral {
96+
CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*(L|u|U)?'.*") }
97+
98+
override string getAPrimaryQlClass() { result = "CharLiteral" }
99+
100+
/**
101+
* Gets the character of this literal. For example `L'a'` has character `"a"`.
102+
*/
103+
string getCharacter() {
104+
result = this.getValueText().regexpCapture("(?s)\\s*(L|u|U)?'(.*)'", 1)
105+
}
106+
}
85107
}

cpp/common/src/codingstandards/cpp/Literals.qll

+36
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import cpp
6+
import codingstandards.cpp.Cpp14Literal
67

78
/** Gets `Literal.getValueText()` truncated to at most 20 characters. */
89
string getTruncatedLiteralText(Literal l) {
@@ -28,3 +29,38 @@ class Utf16StringLiteral extends StringLiteral {
2829
class Utf32StringLiteral extends StringLiteral {
2930
Utf32StringLiteral() { this.getValueText().regexpMatch("(?s)\\s*U\".*") }
3031
}
32+
33+
/**
34+
* A literal resulting from the use of a constexpr
35+
* variable, or macro expansion.
36+
* We rely on the fact that the value text of a literal is equal to the
37+
* `constexpr` variable or macro name.
38+
*/
39+
class CompileTimeComputedIntegralLiteral extends Literal {
40+
CompileTimeComputedIntegralLiteral() {
41+
this.getUnspecifiedType() instanceof IntegralType and
42+
// Exclude bool, whose value text is true or false, but the value itself
43+
// is 1 or 0.
44+
not this instanceof BoolLiteral and
45+
// Exclude character literals, whose value text is the quoted character, but the value
46+
// is the numeric value of the character.
47+
not this instanceof Cpp14Literal::CharLiteral and
48+
not this.getValueText()
49+
.trim()
50+
.regexpMatch("([0-9][0-9']*|0[xX][0-9a-fA-F']+|0b[01']+)[uU]?([lL]{1,2}|[zZ])?") and
51+
// Exclude class field initializers whose value text equals the initializer expression, e.g., `x(0)`
52+
not any(ConstructorFieldInit cfi).getExpr() = this
53+
}
54+
}
55+
56+
class BoolLiteral extends Literal {
57+
BoolLiteral() {
58+
this.getType() instanceof BoolType
59+
or
60+
// When used as non-type template arguments, bool literals might
61+
// have been converted to a non-bool type.
62+
this.getValue() = "1" and this.getValueText() = "true"
63+
or
64+
this.getValue() = "0" and this.getValueText() = "false"
65+
}
66+
}

0 commit comments

Comments
 (0)