Fix 11721: False positive: constParameterReference with overloaded template function (#5151)

This commit is contained in:
Paul Fultz II 2023-06-17 04:31:47 -05:00 committed by GitHub
parent 7075b6e61d
commit d6e3182441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 14 deletions

View File

@ -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<const Token *> &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<const Token*>& 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::vector<const Token
args.push_back(argToken);
}
static bool isConstMethod(const Token* nameToken)
{
const Token* functionToken = getFunctionToken(nameToken);
if (!functionToken)
return false;
const Token* endToken = functionToken->link();
return Token::simpleMatch(endToken, ") const");
}
static bool areAllParamsTypes(const std::vector<const Token *> &params)
{
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<std::string, int> 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<const Token*> xargs;
getFunctionArguments(x.nameToken(), xargs);
std::vector<const Token*> 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<std::string> expandedtemplates;
for (std::list<TokenAndName>::const_reverse_iterator iter1 = mTemplateDeclarations.crbegin(); iter1 != mTemplateDeclarations.crend(); ++iter1) {

View File

@ -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 <class T>\n"
" void mutate();\n"
"};\n"
"struct b {};\n"
"template <class T>\n"
"void f(a& x) {\n"
" x.mutate<T>();\n"
"}\n"
"template <class T>\n"
"void f(const b&)\n"
"{}\n"
"void g(a& c) { f<int>(c); }\n");
ASSERT_EQUALS("", errout.str());
check("struct S {\n"
" template <typename T>\n"
" T* g() {\n"
" return reinterpret_cast<T*>(m);\n"
" }\n"
" template <typename T>\n"
" const T* g() const {\n"
" return reinterpret_cast<const T*>(m);\n"
" }\n"
" char* m;\n"
"};\n"
"void f(S& s) {\n"
" const int* p = s.g<int>();\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void constParameterCallback() {

View File

@ -4064,11 +4064,11 @@ private:
" f<T>(0);\n"
"}\n";
const char exp2[] = "template < typename T > void f ( ) ; "
"void f<int> ( int ) ; "
"void f<char> ( int ) ; "
"void f<int> ( int ) ; "
"void g ( ) { f<int> ( ) ; f<char> ( 1 ) ; } "
"void f<int> ( ) { f<int> ( 0 ) ; } "
"void f<char> ( int ) { }";
"void f<char> ( int ) { } "
"void f<int> ( ) { f<int> ( 0 ) ; }";
ASSERT_EQUALS(exp2, tok(code2));
}