diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 9883efaa1..b6cc869c7 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -577,14 +577,12 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam const bool constructor = func.isConstructor(); const bool destructor = func.isDestructor(); if (!func.hasBody()) { - if (destructor) { // implementation for destructor is not seen => assume it deallocates all variables properly + if (destructor && !func.isDefault()) { // implementation for destructor is not seen and not defaulted => assume it deallocates all variables properly deallocInDestructor = true; memberDealloc = CheckMemoryLeak::Many; } continue; } - if (!func.functionScope) // defaulted destructor - continue; bool body = false; const Token *end = func.functionScope->bodyEnd; for (const Token *tok = func.arg->link(); tok != end; tok = tok->next()) { diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index b516e0e73..5eb69a48c 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -3167,18 +3167,17 @@ void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const To Function * func = const_cast(it->second); if (!func->hasBody()) { if (func->argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { - if (func->type == Function::eDestructor && destructor) { - func->hasBody(true); - } else if (func->type != Function::eDestructor && !destructor) { - // normal function? - const Token *closeParen = (*tok)->next()->link(); - if (closeParen) { - const Token *eq = mTokenizer->isFunctionHead(closeParen, ";"); - if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { - func->isDefault(true); - return; - } - + const Token *closeParen = (*tok)->next()->link(); + if (closeParen) { + const Token *eq = mTokenizer->isFunctionHead(closeParen, ";"); + if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { + func->isDefault(true); + return; + } + if (func->type == Function::eDestructor && destructor) { + func->hasBody(true); + } else if (func->type != Function::eDestructor && !destructor) { + // normal function? const bool hasConstKeyword = closeParen->next()->str() == "const"; if ((func->isConst() == hasConstKeyword) && (func->hasLvalRefQualifier() == lval) && @@ -3234,22 +3233,17 @@ void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok) // syntax error? if (!newScope->bodyEnd) { - scopeList.pop_back(); - while (tok1->next()) - tok1 = tok1->next(); - *scope = nullptr; - *tok = tok1; - return; + mTokenizer->unmatchedToken(tok1); + } else { + (*scope)->nestedList.push_back(newScope); + *scope = newScope; } - - (*scope)->nestedList.push_back(newScope); - *scope = newScope; - *tok = tok1; - } else { + } else if (tok1 && Token::Match(tok1->tokAt(-2), "= default|delete ;")) { scopeList.pop_back(); - *scope = nullptr; - *tok = nullptr; + } else { + throw InternalError(*tok, "Analysis failed (function not recognized). If the code is valid then please report this failure."); } + *tok = tok1; } bool Type::isClassType() const diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index f34dd8c5d..6d2f143d1 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -1613,7 +1613,7 @@ static Token * createAstAtToken(Token *tok, bool cpp) Token::Match(typetok->previous(), "%name% ( !!*") && typetok->previous()->varId() == 0 && !typetok->previous()->isKeyword() && - Token::Match(typetok->link(), ") const|;|{")) + (Token::Match(typetok->link(), ") const|;|{") || Token::Match(typetok->link(), ") const| = delete ;"))) return typetok; } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 89651fd05..2b2b6d866 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -363,6 +363,7 @@ private: TEST_CASE(symboldatabase99); // #10864 TEST_CASE(symboldatabase100); // #10174 TEST_CASE(symboldatabase101); + TEST_CASE(symboldatabase102); TEST_CASE(createSymbolDatabaseFindAllScopes1); TEST_CASE(createSymbolDatabaseFindAllScopes2); @@ -4581,6 +4582,25 @@ private: ASSERT(db->scopeList.back().functionList.size() == 1); ASSERT(db->scopeList.back().functionList.front().isDefault() == true); } + { + GET_SYMBOL_DB("class C { ~C(); };\n" + "C::~C() = default;"); + ASSERT(db->scopeList.size() == 2); + ASSERT(db->scopeList.back().functionList.size() == 1); + ASSERT(db->scopeList.back().functionList.front().isDefault() == true); + ASSERT(db->scopeList.back().functionList.front().isDestructor() == true); + } + { + GET_SYMBOL_DB("namespace ns {\n" + "class C { ~C(); };\n" + "}\n" + "using namespace ns;\n" + "C::~C() = default;"); + ASSERT(db->scopeList.size() == 3); + ASSERT(db->scopeList.back().functionList.size() == 1); + ASSERT(db->scopeList.back().functionList.front().isDefault() == true); + ASSERT(db->scopeList.back().functionList.front().isDestructor() == true); + } } void symboldatabase80() { // #9389 @@ -5013,6 +5033,15 @@ private: ASSERT(it->tokAt(2)->variable()); } + void symboldatabase102() { + GET_SYMBOL_DB("std::string f() = delete;\n" + "void g() {}"); + ASSERT(db); + ASSERT(db->scopeList.size() == 2); + ASSERT(db->scopeList.front().type == Scope::eGlobal); + ASSERT(db->scopeList.back().className == "g"); + } + void createSymbolDatabaseFindAllScopes1() { GET_SYMBOL_DB("void f() { union {int x; char *p;} a={0}; }"); ASSERT(db->scopeList.size() == 3); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index ac61fe39b..0fb27a9bb 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -6321,6 +6321,13 @@ private: ASSERT_EQUALS("1f23,(+4+", testAst("1+f(2,3)+4")); ASSERT_EQUALS("1f2a&,(+", testAst("1+f(2,&a)")); ASSERT_EQUALS("argv[", testAst("int f(char argv[]);")); + ASSERT_EQUALS("", testAst("void f();")); + ASSERT_EQUALS("", testAst("void f() {}")); + ASSERT_EQUALS("", testAst("int f() = delete;")); + ASSERT_EQUALS("", testAst("a::b f();")); + ASSERT_EQUALS("", testAst("a::b f() {}")); + ASSERT_EQUALS("", testAst("a::b f() = delete;")); + ASSERT_EQUALS("constdelete=", testAst("int f() const = delete;")); ASSERT_EQUALS("", testAst("extern unsigned f(const char *);")); ASSERT_EQUALS("charformat*...,", testAst("extern void f(const char *format, ...);")); ASSERT_EQUALS("int((void,", testAst("extern int for_each_commit_graft(int (*)(int*), void *);"));