diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 7a0148a24a2..7f154b9f165 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,69 @@ static int getIntegerConstantMacroWidth(const Token* tok) { return intnum; } +void SymbolDatabase::setGenericValueType(Token *par) +{ + if (!par) + return; + + 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 unsigned int controlMask = ~(1U << control->pointer); + return control->isTypeEqual(type) && + control->sign == type->sign && + (static_cast(control->constness) & controlMask) == static_cast(type->constness) && + (static_cast(control->volatileness) & controlMask) == static_cast(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 +7915,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); 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..8ecd2a140c1 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"