Copy template default argument values from forward declaration to declaration. (#1447)

It is possible to define default template parameter values in forward
declarations and not define any in the actual declaration.  Cppcheck
ignores forward declarations and only uses the default values in the
actual declaration so default values in forward declarations are copied
to the actual declaration when necessary.
This commit is contained in:
IOBYTE 2018-10-24 08:38:59 -04:00 committed by Daniel Marjamäki
parent be1ff268c0
commit 0763fdbfad
3 changed files with 134 additions and 10 deletions

View File

@ -488,7 +488,7 @@ static void setScopeInfo(const Token *tok, std::list<ScopeInfo2> *scopeInfo)
}
}
std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates)
std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDeclarations(bool &codeWithTemplates, bool forward)
{
std::list<ScopeInfo2> scopeInfo;
std::list<TokenAndName> declarations;
@ -513,13 +513,21 @@ std::list<TemplateSimplifier::TokenAndName> TemplateSimplifier::getTemplateDecla
else if (tok2->str() == ")")
break;
// Just a declaration => ignore this
else if (tok2->str() == ";")
else if (tok2->str() == ";") {
if (forward) {
const int namepos = getTemplateNamePosition(parmEnd, forward);
if (namepos > 0)
declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos)));
}
break;
}
// Implementation => add to "templates"
else if (tok2->str() == "{") {
const int namepos = getTemplateNamePosition(parmEnd);
if (namepos > 0)
declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos)));
if (!forward) {
const int namepos = getTemplateNamePosition(parmEnd, forward);
if (namepos > 0)
declarations.emplace_back(tok, getScopeName(scopeInfo), getFullName(scopeInfo, parmEnd->strAt(namepos)));
}
break;
}
}
@ -915,11 +923,12 @@ static bool getTemplateNamePositionTemplateMember(const Token *tok, int &namepos
return false;
}
int TemplateSimplifier::getTemplateNamePosition(const Token *tok)
int TemplateSimplifier::getTemplateNamePosition(const Token *tok, bool forward)
{
// get the position of the template name
int namepos = 0, starAmpPossiblePosition = 0;
if (Token::Match(tok, "> class|struct|union %type% {|:|<"))
if ((forward && Token::Match(tok, "> class|struct|union %type% :|<|;")) ||
(!forward && Token::Match(tok, "> class|struct|union %type% {|:|<")))
namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2;
@ -1482,7 +1491,7 @@ const Token * TemplateSimplifier::getTemplateParametersInDeclaration(
{
typeParametersInDeclaration.clear();
for (; tok && tok->str() != ">"; tok = tok->next()) {
if (Token::Match(tok, "%name% ,|>"))
if (Token::Match(tok, "%name% ,|>|="))
typeParametersInDeclaration.push_back(tok);
}
return tok;
@ -1887,6 +1896,39 @@ void TemplateSimplifier::replaceTemplateUsage(Token * const instantiationToken,
}
}
void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
{
// get all forward declarations
bool dummy;
std::list<TokenAndName> forwardTemplateDeclarations = getTemplateDeclarations(dummy, true);
// try to locate a matching declaration for each forward declaration
for (const auto & forwardDecl : forwardTemplateDeclarations) {
std::vector<const Token *> params1;
getTemplateParametersInDeclaration(forwardDecl.token, params1);
for (auto & decl : mTemplateDeclarations) {
std::vector<const Token *> params2;
getTemplateParametersInDeclaration(decl.token, params2);
// make sure the number of arguments match
if (params1.size() == params2.size()) {
// make sure the scopes and names match
if (forwardDecl.scope == decl.scope && forwardDecl.name == decl.name) {
for (size_t k = 0; k < params1.size(); k++) {
// copy default value to declaration if not present
if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") {
const_cast<Token *>(params2[k])->insertToken(params1[k]->strAt(2));
const_cast<Token *>(params2[k])->insertToken(params1[k]->strAt(1));
}
}
}
}
}
}
}
void TemplateSimplifier::simplifyTemplates(
const std::time_t maxtime,
@ -1929,6 +1971,9 @@ void TemplateSimplifier::simplifyTemplates(
mTemplateDeclarations = getTemplateDeclarations(codeWithTemplates);
// Copy default argument values from forward declaration to declaration
fixForwardDeclaredDefaultArgumentValues();
// Locate possible instantiations of templates..
getTemplateInstantiations();

View File

@ -89,10 +89,11 @@ public:
/**
* Match template declaration/instantiation
* @param tok The ">" token e.g. before "class"
* @param forward declaration or forward declaration
* @return -1 to bail out or positive integer to identity the position
* of the template name.
*/
static int getTemplateNamePosition(const Token *tok);
static int getTemplateNamePosition(const Token *tok, bool forward = false);
/**
* Simplify templates
@ -122,15 +123,23 @@ public:
private:
/**
* Get template declarations
* @param codeWithTemplates set to true if code has templates
* @param forward declaration or forward declaration
* @return list of template declarations
*/
std::list<TokenAndName> getTemplateDeclarations(bool &codeWithTemplates);
std::list<TokenAndName> getTemplateDeclarations(bool &codeWithTemplates, bool forward = false);
/**
* Get template instantiations
*/
void getTemplateInstantiations();
/**
* Fix forward declared default argument values by copying them
* when they are not present in the declaration.
*/
void fixForwardDeclaredDefaultArgumentValues();
/**
* simplify template instantiations (use default argument values)
*/

View File

@ -111,6 +111,7 @@ private:
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
TEST_CASE(template_unhandled);
TEST_CASE(template_default_parameter);
TEST_CASE(template_forward_declared_default_parameter);
TEST_CASE(template_default_type);
TEST_CASE(template_typename);
TEST_CASE(template_constructor); // #3152 - template constructor is removed
@ -1412,6 +1413,75 @@ private:
}
}
void template_forward_declared_default_parameter() {
{
const char code[] = "template <class T, int n=3> class A;\n"
"template <class T, int n>\n"
"class A\n"
"{ T ar[n]; };\n"
"\n"
"void f()\n"
"{\n"
" A<int,2> a1;\n"
" A<int> a2;\n"
"}\n";
const char wanted[] = "void f ( ) "
"{"
" A<int,2> a1 ;"
" A<int,3> a2 ; "
"} "
"class A<int,2> "
"{ int ar [ 2 ] ; } ; "
"class A<int,3> "
"{ int ar [ 3 ] ; } ;";
const char current[] = "template < class T , int n = 3 > class A ; "
"void f ( ) "
"{"
" A<int,2> a1 ;"
" A<int,3> a2 ; "
"} "
"class A<int,2> "
"{ int ar [ 2 ] ; } ; "
"class A<int,3> "
"{ int ar [ 3 ] ; } ;";
TODO_ASSERT_EQUALS(wanted, current, tok(code));
}
{
const char code[] = "template <class, int = 3> class A;\n"
"template <class T, int n>\n"
"class A\n"
"{ T ar[n]; };\n"
"\n"
"void f()\n"
"{\n"
" A<int,2> a1;\n"
" A<int> a2;\n"
"}\n";
const char wanted[] = "void f ( ) "
"{"
" A<int,2> a1 ;"
" A<int,3> a2 ; "
"} "
"class A<int,2> "
"{ int ar [ 2 ] ; } ; "
"class A<int,3> "
"{ int ar [ 3 ] ; } ;";
const char current[] = "template < class , int = 3 > class A ; "
"void f ( ) "
"{"
" A<int,2> a1 ;"
" A<int,3> a2 ; "
"} "
"class A<int,2> "
"{ int ar [ 2 ] ; } ; "
"class A<int,3> "
"{ int ar [ 3 ] ; } ;";
TODO_ASSERT_EQUALS(wanted, current, tok(code));
}
}
void template_default_type() {
const char code[] = "template <typename T, typename U=T>\n"
"class A\n"