Skip to content

Resolve 371 #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions change_notes/2024-02-16-fix-fps-a5-1-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- `A5-1-1` - `LiteralValueUsedOutsideTypeInit.ql`:
- Address FP reported in #371. Exclude literals generated by uses of constexpr variables.
- Exclude literals used in class template instantiations.
- Update the alert message to adhere to the style-guide.
- Exclude boolean literals used as template arguments.
- Exclude `u` and `U` prefixed char literals.
- Exclude literals part of a class aggregate literal.
20 changes: 14 additions & 6 deletions cpp/autosar/src/rules/A5-1-1/LiteralValueUsedOutsideTypeInit.ql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.LoggingOperation
import codingstandards.cpp.Literals
import codingstandards.cpp.Cpp14Literal

from Literal l
where
Expand All @@ -35,11 +36,11 @@ where
// Exclude literal 0
not l.getValue() = "0" and
// Exclude character literals
not l instanceof CharLiteral and
not l instanceof Cpp14Literal::CharLiteral and
// Exclude `nullptr`
not l.getType() instanceof NullPointerType and
// Exclude boolean `true` and `false`
not l.getType() instanceof BoolType and
not l instanceof BoolLiteral and
// Exclude empty string
not l.getValue() = "" and
// Template functions use literals to represent calls which are unknown
Expand All @@ -51,7 +52,14 @@ where
// Aggregate literal
not l = any(ArrayOrVectorAggregateLiteral aal).getAnElementExpr(_).getAChild*() and
// Ignore x - 1 expressions
not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1")
select l,
"Literal value " + getTruncatedLiteralText(l) + " used outside of type initialization " +
l.getAPrimaryQlClass()
not exists(SubExpr se | se.getRightOperand() = l and l.getValue() = "1") and
// Exclude compile time computed integral literals as they can appear as integral literals
// when used as non-type template arguments.
// We limit ourselves to integral literals, because floating point literals as non-type
// template arguments are not supported in C++ 14. Those are supported shince C++ 20.
not l instanceof CompileTimeComputedIntegralLiteral and
// Exclude literals to instantiate a class template per example in the standard
// where an type of std::array is intialized with size 5.
not l = any(ClassTemplateInstantiation cti).getATemplateArgument() and
not l = any(ClassAggregateLiteral cal).getAFieldExpr(_)
select l, "Literal value '" + getTruncatedLiteralText(l) + "' used outside of type initialization."
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
| test.cpp:5:9:5:25 | constant string | Literal value "constant string" used outside of type initialization StringLiteral |
| test.cpp:14:23:14:25 | 100 | Literal value 100 used outside of type initialization Literal |
| test.cpp:54:7:54:7 | 1 | Literal value 1 used outside of type initialization Literal |
| test.cpp:75:23:75:28 | test | Literal value "test" used outside of type initialization StringLiteral |
| test.cpp:76:19:76:28 | not okay | Literal value "not okay" used outside of type initialization StringLiteral |
| test.cpp:5:9:5:25 | constant string | Literal value '"constant string"' used outside of type initialization. |
| test.cpp:14:23:14:25 | 100 | Literal value '100' used outside of type initialization. |
| test.cpp:54:7:54:7 | 1 | Literal value '1' used outside of type initialization. |
| test.cpp:75:23:75:28 | test | Literal value '"test"' used outside of type initialization. |
| test.cpp:76:19:76:28 | not okay | Literal value '"not okay"' used outside of type initialization. |
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |
| test.cpp:104:8:104:8 | 4 | Literal value '4' used outside of type initialization. |
38 changes: 37 additions & 1 deletion cpp/autosar/test/rules/A5-1-1/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,46 @@ void test_not_wrapper_stream(std::ostream &os, const char *str) noexcept {
#define MACRO_LOG(test_str) \
do { \
struct test_struct { \
static const char *get_str() { return static_cast<char *>(test_str); } \
static const char *get_str() { \
return static_cast<const char *>(test_str); \
} \
}; \
} while (false)

void f() {
MACRO_LOG("test"); // COMPLIANT - exclusion
}

template <typename T> struct S1 { static constexpr size_t value(); };

template <> struct S1<int> {
static constexpr size_t value() { return sizeof(int); };
};

constexpr size_t g1 = S1<int>::value();
constexpr size_t f1() { return sizeof(int); }

template <typename T, int size> struct S2 {
T m1[size]; // COMPLIANT
T m2[4]; // NON_COMPLIANT
};

template <typename T, T val> struct S3 {
static constexpr T value = val; // COMPLIANT;
};

void test_fp_reported_in_371() {
struct S2<int, 1> l1; // COMPLIANT
struct S2<int, g1> l2; // COMPLIANT
struct S2<int, f1()> l3; // COMPLIANT

S3<char16_t, u'\u03c0'> l4; // COMPLIANT
S3<char16_t, S3<char16_t, u'\u2286'>::value> l5; // COMPLIANT
S3<char32_t, U'🌌'> l6; // COMPLIANT
S3<char32_t, S3<char32_t, U'⭐'>::value> l7; // COMPLIANT

constexpr float l8 = 3.14159f;
#define delta 0.1f
for (float i = 0.0f; i < l8; i += delta) { // COMPLIANT
}
}
22 changes: 22 additions & 0 deletions cpp/common/src/codingstandards/cpp/Cpp14Literal.qll
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,26 @@ module Cpp14Literal {

override string getAPrimaryQlClass() { result = "FloatingLiteral" }
}

/**
* A character literal. For example:
* ```
* char c1 = 'a';
* char16_t c2 = u'a';
* char32_t c3 = U'a';
* wchar_t c4 = L'b';
* ```
*/
class CharLiteral extends StandardLibrary::TextLiteral {
CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*(L|u|U)?'.*") }

override string getAPrimaryQlClass() { result = "CharLiteral" }

/**
* Gets the character of this literal. For example `L'a'` has character `"a"`.
*/
string getCharacter() {
result = this.getValueText().regexpCapture("(?s)\\s*(L|u|U)?'(.*)'", 1)
}
}
}
36 changes: 36 additions & 0 deletions cpp/common/src/codingstandards/cpp/Literals.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import cpp
import codingstandards.cpp.Cpp14Literal

/** Gets `Literal.getValueText()` truncated to at most 20 characters. */
string getTruncatedLiteralText(Literal l) {
Expand All @@ -28,3 +29,38 @@ class Utf16StringLiteral extends StringLiteral {
class Utf32StringLiteral extends StringLiteral {
Utf32StringLiteral() { this.getValueText().regexpMatch("(?s)\\s*U\".*") }
}

/**
* A literal resulting from the use of a constexpr
* variable, or macro expansion.
* We rely on the fact that the value text of a literal is equal to the
* `constexpr` variable or macro name.
*/
class CompileTimeComputedIntegralLiteral extends Literal {
CompileTimeComputedIntegralLiteral() {
this.getUnspecifiedType() instanceof IntegralType and
// Exclude bool, whose value text is true or false, but the value itself
// is 1 or 0.
not this instanceof BoolLiteral and
// Exclude character literals, whose value text is the quoted character, but the value
// is the numeric value of the character.
not this instanceof Cpp14Literal::CharLiteral and
not this.getValueText()
.trim()
.regexpMatch("([0-9][0-9']*|0[xX][0-9a-fA-F']+|0b[01']+)[uU]?([lL]{1,2}|[zZ])?") and
// Exclude class field initializers whose value text equals the initializer expression, e.g., `x(0)`
not any(ConstructorFieldInit cfi).getExpr() = this
}
}

class BoolLiteral extends Literal {
BoolLiteral() {
this.getType() instanceof BoolType
or
// When used as non-type template arguments, bool literals might
// have been converted to a non-bool type.
this.getValue() = "1" and this.getValueText() = "true"
or
this.getValue() = "0" and this.getValueText() = "false"
}
}