diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 6e3eda8fb5f..715c1c320e5 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -737,6 +737,33 @@ void SymbolDatabase::createSymbolDatabaseFindAllScopes() } else { tok = tok->link(); } + } else if (Token::Match(tok, "%name% (")) { + if (Token::simpleMatch(tok->linkAt(1), ") ;")) { + const Token *funcStart = nullptr; + const Token *argStart = nullptr; + const Token *declEnd = nullptr; + if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { + if (declEnd && declEnd->str() == ";") { + bool newFunc = true; // Is this function already in the database? + auto range = scope->functionMap.equal_range(tok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + if (it->second->argsMatch(scope, it->second->argDef, argStart, emptyString, 0)) { + newFunc = false; + break; + } + } + // save function prototype in database + if (newFunc) { + Function function(tok, scope, funcStart, argStart); + if (function.isExtern()) { + scope->addFunction(std::move(function)); + tok = declEnd; + } + } + continue; + } + } + } } // syntax error? if (!scope) @@ -5902,6 +5929,10 @@ const Function* SymbolDatabase::findFunction(const Token* const tok) const // find the scope this function is in const Scope *currScope = tok->scope(); while (currScope && currScope->isExecutable()) { + if (const Function* f = currScope->findFunction(tok)) { + if (f->isExtern()) + return f; + } if (currScope->functionOf) currScope = currScope->functionOf; else diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 670ca8f6a22..7a4c7622b0c 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -270,6 +270,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(memberFunctionOfUnknownClassMacro2); TEST_CASE(memberFunctionOfUnknownClassMacro3); TEST_CASE(functionLinkage); + TEST_CASE(externalFunctionsInsideAFunction); // #12420 TEST_CASE(classWithFriend); @@ -2361,6 +2362,22 @@ class TestSymbolDatabase : public TestFixture { ASSERT(f && f->function() && !f->function()->isExtern() && f->function()->retDef->str() == "void"); } + void externalFunctionsInsideAFunction() { + GET_SYMBOL_DB("void foo( void )\n" + "{\n" + " extern void bar( void );\n" + " bar();\n" + "}\n"); + + ASSERT(db && errout.str().empty()); + + const Token *f = Token::findsimplematch(tokenizer.tokens(), "bar"); + ASSERT(f && f->function() && f->function()->isExtern() && f == f->function()->tokenDef && f->function()->retDef->str() == "void"); + + const Token *call = Token::findsimplematch(f->next(), "bar"); + ASSERT(call && call->function() == f->function()); + } + void classWithFriend() { GET_SYMBOL_DB("class Foo {}; class Bar1 { friend class Foo; }; class Bar2 { friend Foo; };"); // 3 scopes: Global, 3 classes