From 506a952ad223ab970bc6c5d0ecbd58c6475ca10b Mon Sep 17 00:00:00 2001 From: Steve Mokris Date: Tue, 8 Oct 2019 11:48:09 -0400 Subject: [PATCH] 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"`). --- lib/checkother.cpp | 4 ++++ lib/token.h | 8 ++++++++ lib/tokenize.cpp | 19 ++++++++++++++----- test/testother.cpp | 21 +++++++++++++++++++++ test/testsymboldatabase.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 7170e01b1..c90c8a9b3 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -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) { diff --git a/lib/token.h b/lib/token.h index d9f80645f..88ba31fbd 100644 --- a/lib/token.h +++ b/lib/token.h @@ -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; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index cb3dc83f9..1106dee78 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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 } } } diff --git a/test/testother.cpp b/test/testother.cpp index 79851ee67..294c0092f 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -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 x) {\n" " int& i = x[0];\n" diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 380980491..7c1cf972e 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -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);