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) == "...")
|
||||
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;
|
||||
|
||||
if (var->valueType()->type == ValueType::Type::CONTAINER) {
|
||||
|
|
|
@ -578,6 +578,13 @@ public:
|
|||
setFlag(fConstexpr, b);
|
||||
}
|
||||
|
||||
bool isExternC() const {
|
||||
return getFlag(fExternC);
|
||||
}
|
||||
void isExternC(bool b) {
|
||||
setFlag(fExternC, b);
|
||||
}
|
||||
|
||||
|
||||
bool isBitfield() const {
|
||||
return mImpl->mBits > 0;
|
||||
|
@ -1114,6 +1121,7 @@ private:
|
|||
fAtAddress = (1 << 24), // @ 0x4000
|
||||
fIncompleteVar = (1 << 25),
|
||||
fConstexpr = (1 << 26),
|
||||
fExternC = (1 << 27),
|
||||
};
|
||||
|
||||
Token::Type mTokType;
|
||||
|
|
|
@ -2499,14 +2499,23 @@ void Tokenizer::simplifyExternC()
|
|||
{
|
||||
if (isC())
|
||||
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()) {
|
||||
if (Token::simpleMatch(tok, "extern \"C\"")) {
|
||||
Token *tok2 = tok->next();
|
||||
if (tok->strAt(2) == "{") {
|
||||
tok->linkAt(2)->deleteThis();
|
||||
tok->deleteNext(2);
|
||||
} else
|
||||
tok->deleteNext();
|
||||
tok->deleteThis();
|
||||
tok2 = tok2->next(); // skip {
|
||||
while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2))
|
||||
tok2->isExternC(true);
|
||||
tok->linkAt(2)->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_nonConst);
|
||||
TEST_CASE(passedByValue_externC);
|
||||
|
||||
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() {
|
||||
check("int f(std::vector<int> x) {\n"
|
||||
" int& i = x[0];\n"
|
||||
|
|
|
@ -154,6 +154,7 @@ private:
|
|||
TEST_CASE(findVariableType1);
|
||||
TEST_CASE(findVariableType2);
|
||||
TEST_CASE(findVariableType3);
|
||||
TEST_CASE(findVariableTypeExternC);
|
||||
|
||||
TEST_CASE(rangeBasedFor);
|
||||
|
||||
|
@ -348,6 +349,7 @@ private:
|
|||
TEST_CASE(findFunction27);
|
||||
TEST_CASE(findFunction28);
|
||||
TEST_CASE(findFunctionContainer);
|
||||
TEST_CASE(findFunctionExternC);
|
||||
|
||||
TEST_CASE(valueTypeMatchParameter); // ValueType::matchParameter
|
||||
|
||||
|
@ -950,6 +952,18 @@ private:
|
|||
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() {
|
||||
GET_SYMBOL_DB("void reset() {\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() {
|
||||
ValueType vt_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0);
|
||||
ValueType vt_const_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0, 1);
|
||||
|
|
Loading…
Reference in New Issue