Parse `extern "C"`, and use it to avoid FP reference warnings (#2234)
Previously, cppcheck discarded the `extern "C"` specifier. This patch modifies cppcheck to parse each as a Scope in the symbol database, then uses that scope to avoid false positives when making recommendations about changing a function argument to be a reference (since variable references is a C++ feature, unavailable in C, and thus unavailable in `extern "C"`).
This commit is contained in:
parent
9f40341ba8
commit
506a952ad2
|
@ -1232,6 +1232,10 @@ void CheckOther::checkPassByReference()
|
||||||
if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...")
|
if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...")
|
||||||
continue; // references could not be used as va_start parameters (#5824)
|
continue; // references could not be used as va_start parameters (#5824)
|
||||||
|
|
||||||
|
if ((var->declEndToken() && var->declEndToken()->isExternC()) ||
|
||||||
|
(var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC()))
|
||||||
|
continue; // references cannot be used in functions in extern "C" blocks
|
||||||
|
|
||||||
bool inconclusive = false;
|
bool inconclusive = false;
|
||||||
|
|
||||||
if (var->valueType()->type == ValueType::Type::CONTAINER) {
|
if (var->valueType()->type == ValueType::Type::CONTAINER) {
|
||||||
|
|
|
@ -578,6 +578,13 @@ public:
|
||||||
setFlag(fConstexpr, b);
|
setFlag(fConstexpr, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isExternC() const {
|
||||||
|
return getFlag(fExternC);
|
||||||
|
}
|
||||||
|
void isExternC(bool b) {
|
||||||
|
setFlag(fExternC, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isBitfield() const {
|
bool isBitfield() const {
|
||||||
return mImpl->mBits > 0;
|
return mImpl->mBits > 0;
|
||||||
|
@ -1114,6 +1121,7 @@ private:
|
||||||
fAtAddress = (1 << 24), // @ 0x4000
|
fAtAddress = (1 << 24), // @ 0x4000
|
||||||
fIncompleteVar = (1 << 25),
|
fIncompleteVar = (1 << 25),
|
||||||
fConstexpr = (1 << 26),
|
fConstexpr = (1 << 26),
|
||||||
|
fExternC = (1 << 27),
|
||||||
};
|
};
|
||||||
|
|
||||||
Token::Type mTokType;
|
Token::Type mTokType;
|
||||||
|
|
|
@ -2499,14 +2499,23 @@ void Tokenizer::simplifyExternC()
|
||||||
{
|
{
|
||||||
if (isC())
|
if (isC())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Add attributes to all tokens within `extern "C"` inlines and blocks, and remove the `extern "C"` tokens.
|
||||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||||
if (Token::simpleMatch(tok, "extern \"C\"")) {
|
if (Token::simpleMatch(tok, "extern \"C\"")) {
|
||||||
|
Token *tok2 = tok->next();
|
||||||
if (tok->strAt(2) == "{") {
|
if (tok->strAt(2) == "{") {
|
||||||
tok->linkAt(2)->deleteThis();
|
tok2 = tok2->next(); // skip {
|
||||||
tok->deleteNext(2);
|
while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2))
|
||||||
} else
|
tok2->isExternC(true);
|
||||||
tok->deleteNext();
|
tok->linkAt(2)->deleteThis(); // }
|
||||||
tok->deleteThis();
|
tok->deleteNext(2); // "C" {
|
||||||
|
} else {
|
||||||
|
while ((tok2 = tok2->next()) && !Token::simpleMatch(tok2, ";"))
|
||||||
|
tok2->isExternC(true);
|
||||||
|
tok->deleteNext(); // "C"
|
||||||
|
}
|
||||||
|
tok->deleteThis(); // extern
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ private:
|
||||||
|
|
||||||
TEST_CASE(passedByValue);
|
TEST_CASE(passedByValue);
|
||||||
TEST_CASE(passedByValue_nonConst);
|
TEST_CASE(passedByValue_nonConst);
|
||||||
|
TEST_CASE(passedByValue_externC);
|
||||||
|
|
||||||
TEST_CASE(constVariable);
|
TEST_CASE(constVariable);
|
||||||
|
|
||||||
|
@ -1707,6 +1708,26 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void passedByValue_externC() {
|
||||||
|
check("struct X { int a[5]; }; void f(X v) { }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str());
|
||||||
|
|
||||||
|
check("extern \"C\" { struct X { int a[5]; }; void f(X v) { } }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct X { int a[5]; }; extern \"C\" void f(X v) { }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct X { int a[5]; }; void f(const X v);");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str());
|
||||||
|
|
||||||
|
check("extern \"C\" { struct X { int a[5]; }; void f(const X v); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct X { int a[5]; }; extern \"C\" void f(const X v) { }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void constVariable() {
|
void constVariable() {
|
||||||
check("int f(std::vector<int> x) {\n"
|
check("int f(std::vector<int> x) {\n"
|
||||||
" int& i = x[0];\n"
|
" int& i = x[0];\n"
|
||||||
|
|
|
@ -154,6 +154,7 @@ private:
|
||||||
TEST_CASE(findVariableType1);
|
TEST_CASE(findVariableType1);
|
||||||
TEST_CASE(findVariableType2);
|
TEST_CASE(findVariableType2);
|
||||||
TEST_CASE(findVariableType3);
|
TEST_CASE(findVariableType3);
|
||||||
|
TEST_CASE(findVariableTypeExternC);
|
||||||
|
|
||||||
TEST_CASE(rangeBasedFor);
|
TEST_CASE(rangeBasedFor);
|
||||||
|
|
||||||
|
@ -348,6 +349,7 @@ private:
|
||||||
TEST_CASE(findFunction27);
|
TEST_CASE(findFunction27);
|
||||||
TEST_CASE(findFunction28);
|
TEST_CASE(findFunction28);
|
||||||
TEST_CASE(findFunctionContainer);
|
TEST_CASE(findFunctionContainer);
|
||||||
|
TEST_CASE(findFunctionExternC);
|
||||||
|
|
||||||
TEST_CASE(valueTypeMatchParameter); // ValueType::matchParameter
|
TEST_CASE(valueTypeMatchParameter); // ValueType::matchParameter
|
||||||
|
|
||||||
|
@ -950,6 +952,18 @@ private:
|
||||||
ASSERT(avar && avar->type() != nullptr);
|
ASSERT(avar && avar->type() != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void findVariableTypeExternC() {
|
||||||
|
GET_SYMBOL_DB("extern \"C\" { typedef int INT; }\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" INT x = 3;\n"
|
||||||
|
"}");
|
||||||
|
(void)db;
|
||||||
|
const Variable* avar = Token::findsimplematch(tokenizer.tokens(), "x")->variable();
|
||||||
|
ASSERT(avar);
|
||||||
|
ASSERT(avar->valueType() != nullptr);
|
||||||
|
ASSERT(avar->valueType()->str() == "signed int");
|
||||||
|
}
|
||||||
|
|
||||||
void rangeBasedFor() {
|
void rangeBasedFor() {
|
||||||
GET_SYMBOL_DB("void reset() {\n"
|
GET_SYMBOL_DB("void reset() {\n"
|
||||||
" for(auto& e : array)\n"
|
" for(auto& e : array)\n"
|
||||||
|
@ -5624,6 +5638,16 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void findFunctionExternC() {
|
||||||
|
GET_SYMBOL_DB("extern \"C\" { void foo(int); }\n"
|
||||||
|
"void bar() {\n"
|
||||||
|
" foo(42);\n"
|
||||||
|
"}");
|
||||||
|
const Token *a = Token::findsimplematch(tokenizer.tokens(), "foo ( 42 )");
|
||||||
|
ASSERT(a);
|
||||||
|
ASSERT(a->function());
|
||||||
|
}
|
||||||
|
|
||||||
void valueTypeMatchParameter() {
|
void valueTypeMatchParameter() {
|
||||||
ValueType vt_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0);
|
ValueType vt_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0);
|
||||||
ValueType vt_const_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0, 1);
|
ValueType vt_const_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0, 1);
|
||||||
|
|
Loading…
Reference in New Issue