diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index f89ca04b6..dc608f974 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4723,6 +4723,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) elseif(); + simplifyOverloadedOperators(); + validate(); list.front()->assignIndexes(); @@ -11210,6 +11212,69 @@ void Tokenizer::simplifyOperatorName() } } +void Tokenizer::simplifyOverloadedOperators() +{ + if (isC()) + return; + std::set classNames; + std::set classVars; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName()) + continue; + + // Get classes that have operator() member + if (Token::Match(tok, "class|struct %name% [:{]")) { + int indent = 0; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->str() == "}") + break; + else if (indent == 0 && tok2->str() == ";") + break; + else if (tok2->str() == "{") { + if (indent == 0) + ++indent; + else + tok2 = tok2->link(); + } else if (indent == 1 && Token::simpleMatch(tok2, "operator() (") && isFunctionHead(tok2->next(), ";{")) { + classNames.insert(tok->strAt(1)); + } + } + } + + // Get variables that have operator() member + if (Token::Match(tok, "%type% &| %var%") && classNames.find(tok->str()) != classNames.end()) { + tok = tok->next(); + while (!tok->isName()) + tok = tok->next(); + classVars.insert(tok->varId()); + } + + // Simplify operator() calls + if (Token::Match(tok, "%var% (") && classVars.find(tok->varId()) != classVars.end()) { + // constructor init list.. + if (Token::Match(tok->previous(), "[:,]")) { + const Token *start = tok->previous(); + while (Token::simpleMatch(start, ",")) { + if (Token::simpleMatch(start->previous(), ")")) + start = start->linkAt(-1); + if (Token::Match(start->previous(), "%name%")) + start = start->tokAt(-2); + } + const Token *after = tok->linkAt(1); + while (Token::Match(after, ")|} , %name% (|{")) + after = after->linkAt(3); + + // Do not simplify initlist + if (Token::simpleMatch(start, ":") && Token::simpleMatch(after, ") {")) + continue; + } + + tok->insertToken("operator()"); + tok->insertToken("."); + } + } +} + // remove unnecessary member qualification.. void Tokenizer::removeUnnecessaryQualification() { diff --git a/lib/tokenize.h b/lib/tokenize.h index 543a6cb74..11526883e 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -743,6 +743,9 @@ private: */ void simplifyOperatorName(); + /** simplify overloaded operators: 'obj(123)' => 'obj . operator() ( 123 )' */ + void simplifyOverloadedOperators(); + /** * Remove [[attribute]] (C++11 and later) from TokenList */ diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 1730ae59c..303282942 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -378,6 +378,8 @@ private: TEST_CASE(findFunctionExternC); TEST_CASE(findFunctionGlobalScope); // ::foo + TEST_CASE(overloadedFunction1); + TEST_CASE(valueTypeMatchParameter); // ValueType::matchParameter TEST_CASE(noexceptFunction1); @@ -6178,6 +6180,19 @@ private: ASSERT(bar->function()); } + void overloadedFunction1() { + GET_SYMBOL_DB("struct S {\n" + " int operator()(int);\n" + "};\n" + "\n" + "void foo(S x) {\n" + " x(123);\n" + "}"); + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "x . operator() ( 123 )"); + ASSERT(tok); + ASSERT(tok->tokAt(2)->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); @@ -6291,15 +6306,10 @@ private: " :a(std::move(b.a)) { }\n" "};\n"); ASSERT_EQUALS("", errout.str()); - ASSERT_EQUALS(true, db != nullptr); // not null - - if (db) { - const Scope *b = db->findScopeByName("B"); - ASSERT_EQUALS(true, b != nullptr); - if (b) { - CLASS_FUNC(B, b, true); - } - } + ASSERT(db != nullptr); // not null + const Scope *b = db->findScopeByName("B"); + ASSERT(b != nullptr); + CLASS_FUNC(B, b, true); } #define FUNC_THROW(x) do { \ @@ -6314,14 +6324,12 @@ private: "void func3() throw(int);\n" "void func4() throw(int) { }\n"); ASSERT_EQUALS("", errout.str()); - ASSERT_EQUALS(true, db != nullptr); // not null + ASSERT(db != nullptr); // not null - if (db) { - FUNC_THROW(func1); - FUNC_THROW(func2); - FUNC_THROW(func3); - FUNC_THROW(func4); - } + FUNC_THROW(func1); + FUNC_THROW(func2); + FUNC_THROW(func3); + FUNC_THROW(func4); } #define CLASS_FUNC_THROW(x, y) do { \ diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 45b264b4b..a723b2dde 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -416,6 +416,8 @@ private: TEST_CASE(simplifyOperatorName26); TEST_CASE(simplifyOperatorName27); + TEST_CASE(simplifyOverloadedOperators1); + TEST_CASE(simplifyNullArray); // Some simple cleanups of unhandled macros in the global scope @@ -6636,6 +6638,20 @@ private: tokenizeAndStringify(code)); } + void simplifyOverloadedOperators1() { + const char code[] = "struct S { void operator()(int); };\n" + "\n" + "void foo(S x) {\n" + " x(123);\n" + "}"; + ASSERT_EQUALS("struct S { void operator() ( int ) ; } ;\n" + "\n" + "void foo ( S x ) {\n" + "x . operator() ( 123 ) ;\n" + "}", + tokenizeAndStringify(code)); + } + void simplifyNullArray() { ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;")); }