Skip to content
Open
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
83 changes: 83 additions & 0 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7842,6 +7842,78 @@ static int getIntegerConstantMacroWidth(const Token* tok) {
return intnum;
}

void SymbolDatabase::setGenericValueType(Token *par)
{
if (!par)
return;

const Token *tok = par->astOperand2();
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
std::vector<const Token*> 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<unsigned int>(control->constness) & controlMask) == static_cast<unsigned int>(type->constness) &&
(static_cast<unsigned int>(control->volatileness) & controlMask) == static_cast<unsigned int>(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;

if (!parsedecl(type, &typeVt, mDefaultSignedness, mSettings))
continue;

if (matchVt(controlVt, &typeVt)) {
selected = expr;
break;
}
}
}

if (!selected)
return;

if (selected->valueType()) {
setValueType(par, *selected->valueType());
} else {
const Function *f = selected->function();
Token *parent = par->astParent();

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)
{
if (!tokens)
Expand All @@ -7850,7 +7922,18 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
for (Token *tok = tokens; tok; tok = tok->next())
tok->setValueType(nullptr);

std::vector<Token*> 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;
Expand Down
6 changes: 6 additions & 0 deletions lib/symboldatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 10 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
67 changes: 67 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
Loading