Skip to content

Commit 8bb772d

Browse files
authored
Fix #15044: False negative: unsignedLessThanZero with generic selection (#8861)
1 parent f1c0820 commit 8bb772d

4 files changed

Lines changed: 166 additions & 0 deletions

File tree

lib/symboldatabase.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7842,6 +7842,78 @@ static int getIntegerConstantMacroWidth(const Token* tok) {
78427842
return intnum;
78437843
}
78447844

7845+
void SymbolDatabase::setGenericValueType(Token *par)
7846+
{
7847+
if (!par)
7848+
return;
7849+
7850+
const Token *tok = par->astOperand2();
7851+
std::vector<const Token*> stack;
7852+
7853+
while (tok && tok->str() == ",") {
7854+
stack.push_back(tok);
7855+
tok = tok->astOperand1();
7856+
}
7857+
7858+
if (!tok)
7859+
return;
7860+
7861+
const ValueType *controlVt = tok->valueType();
7862+
7863+
if (!controlVt)
7864+
return;
7865+
7866+
const Token *selected = nullptr;
7867+
7868+
const auto matchVt = [](const ValueType *control, const ValueType *type) {
7869+
// Strip top level qualifiers of controlling expression
7870+
const unsigned int controlMask = ~(1U << control->pointer);
7871+
return control->isTypeEqual(type) &&
7872+
control->sign == type->sign &&
7873+
(static_cast<unsigned int>(control->constness) & controlMask) == static_cast<unsigned int>(type->constness) &&
7874+
(static_cast<unsigned int>(control->volatileness) & controlMask) == static_cast<unsigned int>(type->volatileness);
7875+
};
7876+
7877+
while (!stack.empty()) {
7878+
const Token *comma = stack.back();
7879+
stack.pop_back();
7880+
7881+
const Token *type = comma->next();
7882+
const Token *expr = comma->astOperand2();
7883+
7884+
if (type->str() == "default") {
7885+
if (!selected)
7886+
selected = expr;
7887+
} else {
7888+
ValueType typeVt;
7889+
7890+
if (!parsedecl(type, &typeVt, mDefaultSignedness, mSettings))
7891+
continue;
7892+
7893+
if (matchVt(controlVt, &typeVt)) {
7894+
selected = expr;
7895+
break;
7896+
}
7897+
}
7898+
}
7899+
7900+
if (!selected)
7901+
return;
7902+
7903+
if (selected->valueType()) {
7904+
setValueType(par, *selected->valueType());
7905+
} else {
7906+
const Function *f = selected->function();
7907+
Token *parent = par->astParent();
7908+
7909+
if (f && f->retDef && parent && parent->str() == "(") {
7910+
ValueType returnVt;
7911+
if (parsedecl(f->retDef, &returnVt, mDefaultSignedness, mSettings))
7912+
setValueType(parent, returnVt);
7913+
}
7914+
}
7915+
}
7916+
78457917
void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens)
78467918
{
78477919
if (!tokens)
@@ -7850,7 +7922,18 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
78507922
for (Token *tok = tokens; tok; tok = tok->next())
78517923
tok->setValueType(nullptr);
78527924

7925+
std::vector<Token*> genericClosingParens;
7926+
78537927
for (Token *tok = tokens; tok; tok = tok->next()) {
7928+
if (Token::simpleMatch(tok, "_Generic (")) {
7929+
genericClosingParens.push_back(tok->linkAt(1));
7930+
continue;
7931+
}
7932+
if (!genericClosingParens.empty() && tok == genericClosingParens.back()) {
7933+
setGenericValueType(tok->link());
7934+
genericClosingParens.pop_back();
7935+
continue;
7936+
}
78547937
if (tok->isNumber()) {
78557938
if (MathLib::isFloat(tok->str())) {
78567939
ValueType::Type type = ValueType::Type::DOUBLE;

lib/symboldatabase.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,12 @@ class CPPCHECKLIB SymbolDatabase {
14201420
*/
14211421
void validate() const;
14221422

1423+
/**
1424+
* Set value type for generic selection (_Generic).
1425+
* @param par The opening parenthesis of the _Generic expression.
1426+
*/
1427+
void setGenericValueType(Token *par);
1428+
14231429
/** Set valuetype in provided tokenlist */
14241430
void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr);
14251431

test/testother.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12747,6 +12747,16 @@ class TestOther : public TestFixture {
1274712747
ASSERT_EQUALS("[test.c:8:11]: (style) Checking if unsigned expression 'd.n' is less than zero. [unsignedLessThanZero]\n"
1274812748
"[test.c:12:9]: (style) Checking if unsigned expression 'd.n' is less than zero. [unsignedLessThanZero]\n",
1274912749
errout_str());
12750+
12751+
check("int ifunc(int x);\n"
12752+
"unsigned int ufunc(unsigned int x);\n"
12753+
"void f(void)\n"
12754+
"{\n"
12755+
" unsigned int x = 0;\n"
12756+
" if (_Generic(x, int: ifunc, unsigned int: ufunc)(x) < 0) {}\n"
12757+
"}\n", dinit(CheckOptions, $.cpp = false));
12758+
ASSERT_EQUALS("[test.c:6:57]: (style) Checking if unsigned expression '_Generic(x,int:ifunc,unsigned int:ufunc)(x)' is less than zero. [unsignedLessThanZero]\n",
12759+
errout_str());
1275012760
}
1275112761

1275212762
void doubleMove1() {

test/testsymboldatabase.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ class TestSymbolDatabase : public TestFixture {
596596
TEST_CASE(valueTypeThis);
597597
TEST_CASE(valueTypeChar);
598598
TEST_CASE(valueTypeRValueReference);
599+
TEST_CASE(valueTypeGeneric);
599600

600601
TEST_CASE(variadic1); // #7453
601602
TEST_CASE(variadic2); // #7649
@@ -10416,6 +10417,72 @@ class TestSymbolDatabase : public TestFixture {
1041610417
TODO_ASSERT_EQUALS("", "bool", typeOf("void f(std::string&& s = {})\n", "&&"));
1041710418
}
1041810419

10420+
void valueTypeGeneric() {
10421+
ASSERT_EQUALS("float", typeOf(
10422+
"float floatvar;\n"
10423+
"int intvar;\n"
10424+
"void testfunc() {\n"
10425+
" int controlvar;\n"
10426+
" auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n"
10427+
"}\n", "testvar"));
10428+
10429+
ASSERT_EQUALS("signed int", typeOf(
10430+
"float floatvar;\n"
10431+
"int intvar;\n"
10432+
"void testfunc() {\n"
10433+
" float controlvar;\n"
10434+
" auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n"
10435+
"}\n", "testvar"));
10436+
10437+
ASSERT_EQUALS("float", typeOf(
10438+
"float floatvar;\n"
10439+
"int intvar;\n"
10440+
"void testfunc() {\n"
10441+
" int *const controlvar;\n"
10442+
" auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n"
10443+
"}\n", "testvar"));
10444+
10445+
ASSERT_EQUALS("signed int", typeOf(
10446+
"float floatvar;\n"
10447+
"int intvar;\n"
10448+
"void testfunc() {\n"
10449+
" const int *controlvar;\n"
10450+
" auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n"
10451+
"}\n", "testvar"));
10452+
10453+
ASSERT_EQUALS("float", typeOf(
10454+
"float floatfunc();\n"
10455+
"int intfunc();\n"
10456+
"void testfunc() {\n"
10457+
" int controlvar;\n"
10458+
" auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n"
10459+
"}\n", "testvar"));
10460+
10461+
ASSERT_EQUALS("signed int", typeOf(
10462+
"float floatfunc();\n"
10463+
"int intfunc();\n"
10464+
"void testfunc() {\n"
10465+
" float controlvar;\n"
10466+
" auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n"
10467+
"}\n", "testvar"));
10468+
10469+
ASSERT_EQUALS("float", typeOf(
10470+
"float floatfunc();\n"
10471+
"int intfunc();\n"
10472+
"void testfunc() {\n"
10473+
" int *const controlvar;\n"
10474+
" auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n"
10475+
"}\n", "testvar"));
10476+
10477+
ASSERT_EQUALS("signed int", typeOf(
10478+
"float floatfunc();\n"
10479+
"int intfunc();\n"
10480+
"void testfunc() {\n"
10481+
" const int *controlvar;\n"
10482+
" auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n"
10483+
"}\n", "testvar"));
10484+
}
10485+
1041910486
void variadic1() { // #7453
1042010487
{
1042110488
GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n"

0 commit comments

Comments
 (0)