From d6e3182441c63d94adbaf274e945407c8363e607 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Sat, 17 Jun 2023 04:31:47 -0500 Subject: [PATCH] Fix 11721: False positive: constParameterReference with overloaded template function (#5151) --- lib/templatesimplifier.cpp | 89 ++++++++++++++++++++++++++++++----- test/testother.cpp | 31 ++++++++++++ test/testsimplifytemplate.cpp | 6 +-- 3 files changed, 112 insertions(+), 14 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 4add01fc9..c9b1faada 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -726,21 +726,28 @@ void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope mTemplateInstantiations.emplace_back(std::move(instantiation)); } -static void getFunctionArguments(const Token *nameToken, std::vector &args) +static const Token* getFunctionToken(const Token* nameToken) { - const Token *argToken; + if (Token::Match(nameToken, "%name% (")) + return nameToken->next(); - if (nameToken->strAt(1) == "(") - argToken = nameToken->tokAt(2); - else if (nameToken->strAt(1) == "<") { - const Token *end = nameToken->next()->findClosingBracket(); - if (end) - argToken = end->tokAt(2); - else - return; - } else + if (Token::Match(nameToken, "%name% <")) { + const Token* end = nameToken->next()->findClosingBracket(); + if (Token::simpleMatch(end, "> (")) + return end->next(); + } + + return nullptr; +} + +static void getFunctionArguments(const Token* nameToken, std::vector& args) +{ + const Token* functionToken = getFunctionToken(nameToken); + if (!functionToken) return; + const Token* argToken = functionToken->next(); + if (argToken->str() == ")") return; @@ -750,6 +757,15 @@ static void getFunctionArguments(const Token *nameToken, std::vectorlink(); + return Token::simpleMatch(endToken, ") const"); +} + static bool areAllParamsTypes(const std::vector ¶ms) { if (params.empty()) @@ -3826,6 +3842,57 @@ void TemplateSimplifier::simplifyTemplates( if (mSettings.debugtemplate) printOut("### Template Simplifier pass " + std::to_string(passCount + 1) + " ###"); + // Keep track of the order the names appear so sort can preserve that order + std::unordered_map nameOrdinal; + int ordinal = 0; + for (const auto& decl : mTemplateDeclarations) { + nameOrdinal.emplace(decl.fullName(), ordinal++); + } + + auto score = [&](const Token* arg) { + int i = 0; + for (const Token* tok = arg; tok; tok = tok->next()) { + if (tok->str() == ",") + return i; + else if (tok->link() && Token::Match(tok, "(|{|[")) + tok = tok->link(); + else if (tok->str() == "<") { + const Token* temp = tok->findClosingBracket(); + if (temp) + tok = temp; + } else if (Token::Match(tok, ")|;")) + return i; + else if (Token::simpleMatch(tok, "const")) + i--; + } + return 0; + }; + // Sort so const parameters come first in the list + mTemplateDeclarations.sort([&](const TokenAndName& x, const TokenAndName& y) { + if (x.fullName() != y.fullName()) + return nameOrdinal.at(x.fullName()) < nameOrdinal.at(y.fullName()); + if (x.isFunction() && y.isFunction()) { + std::vector xargs; + getFunctionArguments(x.nameToken(), xargs); + std::vector yargs; + getFunctionArguments(y.nameToken(), yargs); + if (xargs.size() != yargs.size()) + return xargs.size() < yargs.size(); + if (isConstMethod(x.nameToken()) != isConstMethod(y.nameToken())) + return isConstMethod(x.nameToken()); + return std::lexicographical_compare(xargs.begin(), + xargs.end(), + yargs.begin(), + yargs.end(), + [&](const Token* xarg, const Token* yarg) { + if (xarg != yarg) + return score(xarg) < score(yarg); + return false; + }); + } + return false; + }); + std::set expandedtemplates; for (std::list::const_reverse_iterator iter1 = mTemplateDeclarations.crbegin(); iter1 != mTemplateDeclarations.crend(); ++iter1) { diff --git a/test/testother.cpp b/test/testother.cpp index 6dbf1842c..e7f91ffda 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -3219,6 +3219,37 @@ private: "[test.cpp:6]: (style) Parameter 'a' can be declared as pointer to const\n" "[test.cpp:9]: (style) Parameter 'a' can be declared as pointer to const\n", errout.str()); + + check("struct a {\n" + " template \n" + " void mutate();\n" + "};\n" + "struct b {};\n" + "template \n" + "void f(a& x) {\n" + " x.mutate();\n" + "}\n" + "template \n" + "void f(const b&)\n" + "{}\n" + "void g(a& c) { f(c); }\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct S {\n" + " template \n" + " T* g() {\n" + " return reinterpret_cast(m);\n" + " }\n" + " template \n" + " const T* g() const {\n" + " return reinterpret_cast(m);\n" + " }\n" + " char* m;\n" + "};\n" + "void f(S& s) {\n" + " const int* p = s.g();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void constParameterCallback() { diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 236ec20de..92e66e36f 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -4064,11 +4064,11 @@ private: " f(0);\n" "}\n"; const char exp2[] = "template < typename T > void f ( ) ; " - "void f ( int ) ; " "void f ( int ) ; " + "void f ( int ) ; " "void g ( ) { f ( ) ; f ( 1 ) ; } " - "void f ( ) { f ( 0 ) ; } " - "void f ( int ) { }"; + "void f ( int ) { } " + "void f ( ) { f ( 0 ) ; }"; ASSERT_EQUALS(exp2, tok(code2)); }