From 8a6c401d30af954dc127245757b7c485efb2d2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Sep 2026 10:58:23 +0200 Subject: [PATCH 1/5] add tests --- test/testother.cpp | 10 ++++++ test/testsymboldatabase.cpp | 67 +++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 1c1c8640461..7958decad3d 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -12707,6 +12707,16 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.c:8:11]: (style) Checking if unsigned expression 'd.n' is less than zero. [unsignedLessThanZero]\n" "[test.c:12:9]: (style) Checking if unsigned expression 'd.n' is less than zero. [unsignedLessThanZero]\n", errout_str()); + + check("int ifunc(int x);\n" + "unsigned int ufunc(unsigned int x);\n" + "void f(void)\n" + "{\n" + " unsigned int x = 0;\n" + " if (_Generic(x, int: ifunc, unsigned int: ufunc)(x) < 0) {}\n" + "}\n", dinit(CheckOptions, $.cpp = false)); + 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", + errout_str()); } void doubleMove1() { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index dfb61665b8a..0599d80bd16 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -595,6 +595,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(valueTypeThis); TEST_CASE(valueTypeChar); TEST_CASE(valueTypeRValueReference); + TEST_CASE(valueTypeGeneric); TEST_CASE(variadic1); // #7453 TEST_CASE(variadic2); // #7649 @@ -10401,6 +10402,72 @@ class TestSymbolDatabase : public TestFixture { TODO_ASSERT_EQUALS("", "bool", typeOf("void f(std::string&& s = {})\n", "&&")); } + void valueTypeGeneric() { + ASSERT_EQUALS("float", typeOf( + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " int controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" + "}\n", "testvar")); + + ASSERT_EQUALS("signed int", typeOf( + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " float controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" + "}\n", "testvar")); + + ASSERT_EQUALS("float", typeOf( + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " int *const controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" + "}\n", "testvar")); + + ASSERT_EQUALS("signed int", typeOf( + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " const int *controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" + "}\n", "testvar")); + + ASSERT_EQUALS("float", typeOf( + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " int controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); + + ASSERT_EQUALS("signed int", typeOf( + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " float controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); + + ASSERT_EQUALS("float", typeOf( + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " int *const controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); + + ASSERT_EQUALS("signed int", typeOf( + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " const int *controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); + } + void variadic1() { // #7453 { GET_SYMBOL_DB("CBase* create(const char *c1, ...);\n" From 796e8121a7dce3468aebb818a57a717d330e098f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 15 Sep 2026 15:37:58 +0200 Subject: [PATCH 2/5] fix --- lib/symboldatabase.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++ lib/symboldatabase.h | 6 ++++ 2 files changed, 79 insertions(+) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 7a0148a24a2..f20b2d589bf 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -7789,6 +7789,8 @@ static const Function* getFunction(const Token* tok) { return nullptr; if (tok->function() && tok->function()->retDef) return tok->function(); + if (tok->str() == ")" && tok->link() && Token::simpleMatch(tok->link()->previous(), "_Generic (")) + return tok->link()->function(); if (const Variable* lvar = tok->variable()) { // lambda const Function* lambda{}; if (Token::Match(lvar->nameToken()->next(), "; %varid% = [", lvar->declarationId())) @@ -7842,6 +7844,66 @@ static int getIntegerConstantMacroWidth(const Token* tok) { return intnum; } +void SymbolDatabase::setGenericValueType(Token *par) +{ + const Token *tok = par->astOperand2(); + std::vector stack; + + while (tok && tok->str() == ",") { + stack.push_back(tok); + tok = tok->astOperand1(); + } + + if (!tok) + return; + + const ValueType *controlVt = tok->valueType(); + + if (!controlVt) + return; + + const Token *selected = nullptr; + + const auto matchVt = [](const ValueType *control, const ValueType *type) { + // Strip top level qualifiers of controlling expression + const nonneg int controlMask = ~(1 << control->pointer); + return control->isTypeEqual(type) && + control->sign == type->sign && + (control->constness & controlMask) == type->constness && + (control->volatileness & controlMask) == type->volatileness; + }; + + while (!stack.empty()) { + const Token *comma = stack.back(); + stack.pop_back(); + + const Token *type = comma->next(); + const Token *expr = comma->astOperand2(); + + if (type->str() == "default") { + if (!selected) + selected = expr; + } else { + ValueType typeVt; + parsedecl(type, &typeVt, mDefaultSignedness, mSettings); + + if (matchVt(controlVt, &typeVt)) { + selected = expr; + break; + } + } + } + + if (!selected) + return; + + if (selected->valueType()) + setValueType(par, *selected->valueType()); + + if (selected->function()) + par->function(selected->function()); +} + void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) { if (!tokens) @@ -7850,7 +7912,18 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to for (Token *tok = tokens; tok; tok = tok->next()) tok->setValueType(nullptr); + std::vector genericClosingParens; + for (Token *tok = tokens; tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "_Generic (")) { + genericClosingParens.push_back(tok->linkAt(1)); + continue; + } + if (!genericClosingParens.empty() && tok == genericClosingParens.back()) { + setGenericValueType(tok->link()); + genericClosingParens.pop_back(); + continue; + } if (tok->isNumber()) { if (MathLib::isFloat(tok->str())) { ValueType::Type type = ValueType::Type::DOUBLE; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 86ce9419999..08c5382929e 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1420,6 +1420,12 @@ class CPPCHECKLIB SymbolDatabase { */ void validate() const; + /** + * Set value type for generic selection (_Generic). + * @param par The opening parenthesis of the _Generic expression. + */ + void setGenericValueType(Token *par); + /** Set valuetype in provided tokenlist */ void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr); From 91b3396f751d5747eed67969f5f12ab412c3c9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Sep 2026 13:16:41 +0200 Subject: [PATCH 3/5] format --- test/testsymboldatabase.cpp | 96 ++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 0599d80bd16..8ecd2a140c1 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -10404,68 +10404,68 @@ class TestSymbolDatabase : public TestFixture { void valueTypeGeneric() { ASSERT_EQUALS("float", typeOf( - "float floatvar;\n" - "int intvar;\n" - "void testfunc() {\n" - " int controlvar;\n" - " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" - "}\n", "testvar")); + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " int controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" + "}\n", "testvar")); ASSERT_EQUALS("signed int", typeOf( - "float floatvar;\n" - "int intvar;\n" - "void testfunc() {\n" - " float controlvar;\n" - " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" - "}\n", "testvar")); + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " float controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatvar, default: intvar);\n" + "}\n", "testvar")); ASSERT_EQUALS("float", typeOf( - "float floatvar;\n" - "int intvar;\n" - "void testfunc() {\n" - " int *const controlvar;\n" - " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" - "}\n", "testvar")); + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " int *const controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" + "}\n", "testvar")); ASSERT_EQUALS("signed int", typeOf( - "float floatvar;\n" - "int intvar;\n" - "void testfunc() {\n" - " const int *controlvar;\n" - " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" - "}\n", "testvar")); + "float floatvar;\n" + "int intvar;\n" + "void testfunc() {\n" + " const int *controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatvar, default: intvar);\n" + "}\n", "testvar")); ASSERT_EQUALS("float", typeOf( - "float floatfunc();\n" - "int intfunc();\n" - "void testfunc() {\n" - " int controlvar;\n" - " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" - "}\n", "testvar")); + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " int controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); ASSERT_EQUALS("signed int", typeOf( - "float floatfunc();\n" - "int intfunc();\n" - "void testfunc() {\n" - " float controlvar;\n" - " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" - "}\n", "testvar")); + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " float controlvar;\n" + " auto testvar = _Generic(controlvar, int: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); ASSERT_EQUALS("float", typeOf( - "float floatfunc();\n" - "int intfunc();\n" - "void testfunc() {\n" - " int *const controlvar;\n" - " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" - "}\n", "testvar")); + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " int *const controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); ASSERT_EQUALS("signed int", typeOf( - "float floatfunc();\n" - "int intfunc();\n" - "void testfunc() {\n" - " const int *controlvar;\n" - " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" - "}\n", "testvar")); + "float floatfunc();\n" + "int intfunc();\n" + "void testfunc() {\n" + " const int *controlvar;\n" + " auto testvar = _Generic(controlvar, int*: floatfunc, default: intfunc)();\n" + "}\n", "testvar")); } void variadic1() { // #7453 From a264f3483085b977c6c48d417f2041262f0ebdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 16 Sep 2026 13:19:50 +0200 Subject: [PATCH 4/5] ci fixes --- lib/symboldatabase.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index f20b2d589bf..7f154b9f165 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -7846,6 +7846,9 @@ static int getIntegerConstantMacroWidth(const Token* tok) { void SymbolDatabase::setGenericValueType(Token *par) { + if (!par) + return; + const Token *tok = par->astOperand2(); std::vector stack; @@ -7866,11 +7869,11 @@ void SymbolDatabase::setGenericValueType(Token *par) const auto matchVt = [](const ValueType *control, const ValueType *type) { // Strip top level qualifiers of controlling expression - const nonneg int controlMask = ~(1 << control->pointer); + const unsigned int controlMask = ~(1U << control->pointer); return control->isTypeEqual(type) && control->sign == type->sign && - (control->constness & controlMask) == type->constness && - (control->volatileness & controlMask) == type->volatileness; + (static_cast(control->constness) & controlMask) == static_cast(type->constness) && + (static_cast(control->volatileness) & controlMask) == static_cast(type->volatileness); }; while (!stack.empty()) { From d295cc738d21b485b1921643504f997f9a67c771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 17 Sep 2026 10:38:52 +0200 Subject: [PATCH 5/5] propagate return value instead of setting function for parenthesis --- lib/symboldatabase.cpp | 19 +++++++++++++------ test/testother.cpp | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 7f154b9f165..c2ce2db812b 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -7789,8 +7789,6 @@ static const Function* getFunction(const Token* tok) { return nullptr; if (tok->function() && tok->function()->retDef) return tok->function(); - if (tok->str() == ")" && tok->link() && Token::simpleMatch(tok->link()->previous(), "_Generic (")) - return tok->link()->function(); if (const Variable* lvar = tok->variable()) { // lambda const Function* lambda{}; if (Token::Match(lvar->nameToken()->next(), "; %varid% = [", lvar->declarationId())) @@ -7888,7 +7886,9 @@ void SymbolDatabase::setGenericValueType(Token *par) selected = expr; } else { ValueType typeVt; - parsedecl(type, &typeVt, mDefaultSignedness, mSettings); + + if (!parsedecl(type, &typeVt, mDefaultSignedness, mSettings)) + continue; if (matchVt(controlVt, &typeVt)) { selected = expr; @@ -7900,11 +7900,18 @@ void SymbolDatabase::setGenericValueType(Token *par) if (!selected) return; - if (selected->valueType()) + if (selected->valueType()) { setValueType(par, *selected->valueType()); + } else { + const Function *f = selected->function(); + Token *parent = par->astParent(); - if (selected->function()) - par->function(selected->function()); + if (f && f->retDef && parent && parent->str() == "(") { + ValueType returnVt; + if (parsedecl(f->retDef, &returnVt, mDefaultSignedness, mSettings)) + setValueType(parent, returnVt); + } + } } void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) diff --git a/test/testother.cpp b/test/testother.cpp index 7958decad3d..9e4334aef30 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -12715,7 +12715,7 @@ class TestOther : public TestFixture { " unsigned int x = 0;\n" " if (_Generic(x, int: ifunc, unsigned int: ufunc)(x) < 0) {}\n" "}\n", dinit(CheckOptions, $.cpp = false)); - 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", + 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", errout_str()); }