diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 4e249530f..6d8a936e7 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -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 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 expandedtemplates; for (std::list::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { - if (iter1->isAlias()) + if (iter1->isAlias() || iter1->isFriend()) continue; // get specializations.. std::list specializations; for (std::list::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) { - if (iter2->isAlias()) + if (iter2->isAlias() || iter2->isFriend()) continue; if (iter1->fullName() == iter2->fullName()) diff --git a/lib/templatesimplifier.h b/lib/templatesimplifier.h index 82dd27b2a..2508340d6 100644 --- a/lib/templatesimplifier.h +++ b/lib/templatesimplifier.h @@ -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. diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index bea88335b..31fb07129 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -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 struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) @@ -3364,6 +3365,33 @@ private: } } + void template139() { + { + const char code[] = "template\n" + "struct Foo {\n" + " template 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\n" + "struct Foo {\n" + " template friend struct Foo;\n" + "} ;\n" + "Foo foo;"; + const char exp[] = "struct Foo ; " + "Foo foo ; " + "struct Foo { " + "template < typename > friend struct Foo ; " + "} ;"; + ASSERT_EQUALS(exp, tok(code)); + } + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n"