diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index a14599ccb..f61a1905f 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -649,6 +650,18 @@ bool TemplateSimplifier::getTemplateDeclarations() void TemplateSimplifier::getTemplateInstantiations() { + std::multimap functionNameMap; + + for (const auto & decl : mTemplateDeclarations) { + if (decl.isFunction()) + functionNameMap.insert(std::make_pair(decl.name, &decl)); + } + + for (const auto & decl : mTemplateForwardDeclarations) { + if (decl.isFunction()) + functionNameMap.insert(std::make_pair(decl.name, &decl)); + } + std::list scopeList; const Token *skip = nullptr; @@ -683,7 +696,7 @@ void TemplateSimplifier::getTemplateInstantiations() else if (!isUsing && tok2 && tok2->str() == ";") tok = const_cast(tok2); } - } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|& %name% ::|<") || + } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|& %name% ::|<|(") || Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { std::string scopeName = getScopeName(scopeList); @@ -695,6 +708,75 @@ void TemplateSimplifier::getTemplateInstantiations() qualification += (qualification.empty() ? "" : " :: ") + tok->str(); tok = tok->tokAt(2); } + + // look for function instantiation with type deduction + // fixme: only single argument functions supported + if (tok->strAt(1) == "(") { + std::string fullName = qualification + (qualification.empty() ? "" : " :: ") + tok->str(); + // get all declarations with this name + for (auto pos = functionNameMap.lower_bound(tok->str()); + pos != functionNameMap.upper_bound(tok->str()); ++pos) { + // look for declaration with same qualification + if (pos->second->fullName == fullName) { + // make sure it is a single argument function + if (Token::Match(pos->second->token->tokAt(2), "typename|class %name% >") && + Token::Match(pos->second->nameToken->tokAt(2), "const| %type% &| %name%| )") && + Token::Match(tok->tokAt(2), "%num%|%str%|%char%|%bool% )")) { + tok->insertToken(">"); + switch (tok->tokAt(3)->tokType()) { + case Token::eBoolean: + tok->insertToken("bool"); + break; + case Token::eChar: + if (tok->tokAt(3)->isLong()) + tok->insertToken("wchar_t"); + else + tok->insertToken("char"); + break; + case Token::eString: + tok->insertToken("*"); + if (tok->tokAt(4)->isLong()) + tok->insertToken("wchar_t"); + else + tok->insertToken("char"); + tok->insertToken("const"); + break; + case Token::eNumber: { + MathLib::value num(tok->strAt(3)); + if (num.isFloat()) { + // MathLib::getSuffix doesn't work for floating point numbers + char suffix = tok->strAt(3).back(); + if (suffix == 'f' || suffix == 'F') + tok->insertToken("float"); + else if (suffix == 'l' || suffix == 'L') { + tok->insertToken("double"); + tok->insertToken("long"); + } else + tok->insertToken("double"); + } else if (num.isInt()) { + std::string suffix = MathLib::getSuffix(tok->strAt(3)); + if (suffix.find("LL") != std::string::npos) { + tok->insertToken("long"); + tok->insertToken("long"); + } else if (suffix.find('L') != std::string::npos) + tok->insertToken("long"); + else + tok->insertToken("int"); + if (suffix.find('U') != std::string::npos) + tok->insertToken("unsigned"); + } + break; + } + default: + break; + } + tok->insertToken("<"); + break; + } + } + } + } + if (!Token::Match(tok, "%name% <") || Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast")) continue; diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 8c15f6172..ad3703a04 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -179,6 +179,9 @@ private: // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatch); TEST_CASE(templateParameterWithoutName); // #8602 Template default parameter without name yields syntax error + + TEST_CASE(templateTypeDeduction1); // #8962 + TEST_CASE(templateTypeDeduction2); } std::string tok(const char code[], bool simplify = true, bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { @@ -3106,6 +3109,117 @@ private: " void g();\n" "};n")); } + + void templateTypeDeduction1() { // #8962 + const char code[] = "template\n" + "void f(T n) { (void)n; }\n" + "static void func() {\n" + " f(0);\n" + " f(0u);\n" + " f(0U);\n" + " f(0l);\n" + " f(0L);\n" + " f(0ul);\n" + " f(0UL);\n" + " f(0ll);\n" + " f(0LL);\n" + " f(0ull);\n" + " f(0ULL);\n" + " f(0.0);\n" + " f(0.0f);\n" + " f(0.0F);\n" + " f(0.0l);\n" + " f(0.0L);\n" + " f('c');\n" + " f(L'c');\n" + " f(\"string\");\n" + " f(L\"string\");\n" + " f(true);\n" + " f(false);\n" + "}"; + const char expected[] = "void f ( int n ) ; " + "void f ( unsigned int n ) ; " + "void f ( long n ) ; " + "void f ( unsigned long n ) ; " + "void f ( long long n ) ; " + "void f ( unsigned long long n ) ; " + "void f ( double n ) ; " + "void f ( float n ) ; " + "void f ( long double n ) ; " + "void f ( char n ) ; " + "void f ( wchar_t n ) ; " + "void f ( const char * n ) ; " + "void f ( const wchar_t * n ) ; " + "void f ( bool n ) ; " + "static void func ( ) { " + "f ( 0 ) ; " + "f ( 0u ) ; " + "f ( 0U ) ; " + "f ( 0l ) ; " + "f ( 0L ) ; " + "f ( 0ul ) ; " + "f ( 0UL ) ; " + "f ( 0ll ) ; " + "f ( 0LL ) ; " + "f ( 0ull ) ; " + "f ( 0ULL ) ; " + "f ( 0.0 ) ; " + "f ( 0.0f ) ; " + "f ( 0.0F ) ; " + "f ( 0.0l ) ; " + "f ( 0.0L ) ; " + "f ( 'c' ) ; " + "f ( L'c' ) ; " + "f ( \"string\" ) ; " + "f ( L\"string\" ) ; " + "f ( true ) ; " + "f ( false ) ; " + "} " + "void f ( int n ) { ( void ) n ; } " + "void f ( unsigned int n ) { ( void ) n ; } " + "void f ( long n ) { ( void ) n ; } " + "void f ( unsigned long n ) { ( void ) n ; } " + "void f ( long long n ) { ( void ) n ; } " + "void f ( unsigned long long n ) { ( void ) n ; } " + "void f ( double n ) { ( void ) n ; } " + "void f ( float n ) { ( void ) n ; } " + "void f ( long double n ) { ( void ) n ; } " + "void f ( char n ) { ( void ) n ; } " + "void f ( wchar_t n ) { ( void ) n ; } " + "void f ( const char * n ) { ( void ) n ; } " + "void f ( const wchar_t * n ) { ( void ) n ; } " + "void f ( bool n ) { ( void ) n ; }"; + + ASSERT_EQUALS(expected, tok(code, false)); + ASSERT_EQUALS("", errout.str()); + } + + void templateTypeDeduction2() { + const char code[] = "template\n" + "void f(T t, U u) { }\n" + "static void func() {\n" + " f(0, 0.0);\n" + " f(0.0, 0);\n" + "}"; + + const char expected[] = "void f ( int t , double u ) ; " + "void f ( double t , int u ) ; " + "static void func ( ) { " + "f ( 0 , 0.0 ) ; " + "f ( 0.0, 0 ) ; " + "void f ( int t , double u ) { } " + "void f ( double t , int u ) { } "; + + const char actual[] = "template < typename T , typename U > " + "void f ( T t , U u ) { } " + "static void func ( ) { " + "f ( 0 , 0.0 ) ; " + "f ( 0.0 , 0 ) ; " + "}"; + + TODO_ASSERT_EQUALS(expected, actual, tok(code, false)); + } + }; REGISTER_TEST(TestSimplifyTemplate)