template simplifier: ignore friend templates (#2122)

* template simplifier: ignore friend templates

friend templates were interpreted as variable templates

* fix cppcheck warning
This commit is contained in:
IOBYTE 2019-09-01 03:56:33 -04:00 committed by Daniel Marjamäki
parent d918f76a0d
commit 3a1aec8850
3 changed files with 74 additions and 26 deletions

View File

@ -105,7 +105,12 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &
throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX);
}
isClass(Token::Match(mParamEnd->next(), "class|struct|union %name% <|{|:|;|::"));
isFriend(mParamEnd->strAt(1) == "friend");
const Token *next = mParamEnd->next();
if (isFriend())
next = next->next();
isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::"));
if (mToken->strAt(1) == "<" && !isSpecialization()) {
const Token *end = mToken->next()->findClosingBracket();
isVariadic(end && Token::findmatch(mToken->tokAt(2), "typename|class . . .", end));
@ -119,22 +124,24 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &
throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX);
}
isFunction(tok1->str() == "(");
isVariable(!isClass() && !isAlias() && Token::Match(tok1, "=|;"));
if (isVariable())
isForwardDeclaration(tok1->str() == ";");
else if (!isAlias()) {
if (isFunction())
tok1 = tok1->link()->next();
while (tok1 && !Token::Match(tok1, ";|{")) {
if (tok1->str() == "<")
tok1 = tok1->findClosingBracket();
else if (Token::Match(tok1, "(|[") && tok1->link())
tok1 = tok1->link();
if (tok1)
tok1 = tok1->next();
}
if (tok1)
isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;"));
if (!isFriend()) {
if (isVariable())
isForwardDeclaration(tok1->str() == ";");
else if (!isAlias()) {
if (isFunction())
tok1 = tok1->link()->next();
while (tok1 && !Token::Match(tok1, ";|{")) {
if (tok1->str() == "<")
tok1 = tok1->findClosingBracket();
else if (Token::Match(tok1, "(|[") && tok1->link())
tok1 = tok1->link();
if (tok1)
tok1 = tok1->next();
}
if (tok1)
isForwardDeclaration(tok1->str() == ";");
}
}
// check for member class or function and adjust scope
if ((isFunction() || isClass()) && mNameToken->strAt(-1) == "::") {
@ -983,7 +990,7 @@ void TemplateSimplifier::useDefaultArgumentValues()
void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration)
{
// Ticket #5762: Skip specialization tokens
if (declaration.isSpecialization() || declaration.isAlias())
if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend())
return;
// template parameters with default value has syntax such as:
@ -1395,9 +1402,9 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos)
{
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{|::")) {
namepos = 2;
tok = tok->tokAt(2);
if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) {
namepos = tok->strAt(1) == "friend" ? 3 : 2;
tok = tok->tokAt(namepos);
while (Token::Match(tok, "%type% :: %type%") ||
(Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) {
if (tok->strAt(1) == "::") {
@ -2026,7 +2033,11 @@ void TemplateSimplifier::expandTemplate(
}
}
} else {
if (copy) {
// don't modify friend
if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) {
if (copy)
mTokenList.addtoken(tok3);
} else if (copy) {
// add namespace if necessary
if (!templateDeclaration.scope().empty() &&
(isClass ? tok3->strAt(1) != "(" : true)) {
@ -3154,7 +3165,7 @@ static bool specMatch(
const TemplateSimplifier::TokenAndName &decl)
{
// make sure decl is really a declaration
if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias())
if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend())
return false;
return spec.isSameFamily(decl);
@ -3237,8 +3248,8 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
getTemplateParametersInDeclaration(forwardDecl.token()->tokAt(2), params1);
for (auto & decl : mTemplateDeclarations) {
// skip partializations
if (decl.isPartialSpecialization())
// skip partializations, type aliases and friends
if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend())
continue;
std::vector<const Token *> params2;
@ -3321,6 +3332,8 @@ void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::s
std::cout << " isForwardDeclaration";
if (tokenAndName.isVariadic())
std::cout << " isVariadic";
if (tokenAndName.isFriend())
std::cout << " isFriend";
std::cout << std::endl;
if (tokenAndName.token() && !tokenAndName.paramEnd() && tokenAndName.token()->strAt(1) == "<") {
const Token *end = tokenAndName.token()->next()->findClosingBracket();
@ -3544,13 +3557,13 @@ void TemplateSimplifier::simplifyTemplates(
std::set<std::string> expandedtemplates;
for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) {
if (iter1->isAlias())
if (iter1->isAlias() || iter1->isFriend())
continue;
// get specializations..
std::list<const Token *> specializations;
for (std::list<TokenAndName>::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) {
if (iter2->isAlias())
if (iter2->isAlias() || iter2->isFriend())
continue;
if (iter1->fullName() == iter2->fullName())

View File

@ -87,6 +87,7 @@ public:
fIsPartialSpecialization = (1 << 5), // user partial specialized template
fIsForwardDeclaration = (1 << 6), // forward declaration
fIsVariadic = (1 << 7), // variadic template
fIsFriend = (1 << 8), // friend template
fFamilyMask = (fIsClass | fIsFunction | fIsVariable)
};
@ -114,6 +115,9 @@ public:
void isVariadic(bool state) {
setFlag(fIsVariadic, state);
}
void isFriend(bool state) {
setFlag(fIsFriend, state);
}
/**
* Get specified flag state.
@ -205,6 +209,9 @@ public:
bool isVariadic() const {
return getFlag(fIsVariadic);
}
bool isFriend() const {
return getFlag(fIsFriend);
}
/**
* Get alias start token.

View File

@ -176,6 +176,7 @@ private:
TEST_CASE(template136); // #9287
TEST_CASE(template137); // #9288
TEST_CASE(template138);
TEST_CASE(template139);
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -3364,6 +3365,33 @@ private:
}
}
void template139() {
{
const char code[] = "template<typename T>\n"
"struct Foo {\n"
" template<typename> friend struct Foo;\n"
"};";
const char exp[] = "template < typename T > "
"struct Foo { "
"template < typename > friend struct Foo ; "
"} ;";
ASSERT_EQUALS(exp, tok(code));
}
{
const char code[] = "template<typename T>\n"
"struct Foo {\n"
" template<typename> friend struct Foo;\n"
"} ;\n"
"Foo<int> foo;";
const char exp[] = "struct Foo<int> ; "
"Foo<int> foo ; "
"struct Foo<int> { "
"template < typename > friend struct Foo ; "
"} ;";
ASSERT_EQUALS(exp, tok(code));
}
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n"