Skip to content

Commit 3a1aec8

Browse files
IOBYTEdanmar
authored andcommitted
template simplifier: ignore friend templates (#2122)
* template simplifier: ignore friend templates friend templates were interpreted as variable templates * fix cppcheck warning
1 parent d918f76 commit 3a1aec8

3 files changed

Lines changed: 73 additions & 25 deletions

File tree

lib/templatesimplifier.cpp

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,12 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &
105105
throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX);
106106
}
107107

108-
isClass(Token::Match(mParamEnd->next(), "class|struct|union %name% <|{|:|;|::"));
108+
isFriend(mParamEnd->strAt(1) == "friend");
109+
const Token *next = mParamEnd->next();
110+
if (isFriend())
111+
next = next->next();
112+
113+
isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::"));
109114
if (mToken->strAt(1) == "<" && !isSpecialization()) {
110115
const Token *end = mToken->next()->findClosingBracket();
111116
isVariadic(end && Token::findmatch(mToken->tokAt(2), "typename|class . . .", end));
@@ -119,22 +124,24 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &
119124
throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX);
120125
}
121126
isFunction(tok1->str() == "(");
122-
isVariable(!isClass() && !isAlias() && Token::Match(tok1, "=|;"));
123-
if (isVariable())
124-
isForwardDeclaration(tok1->str() == ";");
125-
else if (!isAlias()) {
126-
if (isFunction())
127-
tok1 = tok1->link()->next();
128-
while (tok1 && !Token::Match(tok1, ";|{")) {
129-
if (tok1->str() == "<")
130-
tok1 = tok1->findClosingBracket();
131-
else if (Token::Match(tok1, "(|[") && tok1->link())
132-
tok1 = tok1->link();
127+
isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;"));
128+
if (!isFriend()) {
129+
if (isVariable())
130+
isForwardDeclaration(tok1->str() == ";");
131+
else if (!isAlias()) {
132+
if (isFunction())
133+
tok1 = tok1->link()->next();
134+
while (tok1 && !Token::Match(tok1, ";|{")) {
135+
if (tok1->str() == "<")
136+
tok1 = tok1->findClosingBracket();
137+
else if (Token::Match(tok1, "(|[") && tok1->link())
138+
tok1 = tok1->link();
139+
if (tok1)
140+
tok1 = tok1->next();
141+
}
133142
if (tok1)
134-
tok1 = tok1->next();
143+
isForwardDeclaration(tok1->str() == ";");
135144
}
136-
if (tok1)
137-
isForwardDeclaration(tok1->str() == ";");
138145
}
139146
// check for member class or function and adjust scope
140147
if ((isFunction() || isClass()) && mNameToken->strAt(-1) == "::") {
@@ -983,7 +990,7 @@ void TemplateSimplifier::useDefaultArgumentValues()
983990
void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration)
984991
{
985992
// Ticket #5762: Skip specialization tokens
986-
if (declaration.isSpecialization() || declaration.isAlias())
993+
if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend())
987994
return;
988995

989996
// template parameters with default value has syntax such as:
@@ -1395,9 +1402,9 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
13951402

13961403
bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos)
13971404
{
1398-
if (Token::Match(tok, "> class|struct|union %type% :|<|;|{|::")) {
1399-
namepos = 2;
1400-
tok = tok->tokAt(2);
1405+
if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) {
1406+
namepos = tok->strAt(1) == "friend" ? 3 : 2;
1407+
tok = tok->tokAt(namepos);
14011408
while (Token::Match(tok, "%type% :: %type%") ||
14021409
(Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) {
14031410
if (tok->strAt(1) == "::") {
@@ -2026,7 +2033,11 @@ void TemplateSimplifier::expandTemplate(
20262033
}
20272034
}
20282035
} else {
2029-
if (copy) {
2036+
// don't modify friend
2037+
if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) {
2038+
if (copy)
2039+
mTokenList.addtoken(tok3);
2040+
} else if (copy) {
20302041
// add namespace if necessary
20312042
if (!templateDeclaration.scope().empty() &&
20322043
(isClass ? tok3->strAt(1) != "(" : true)) {
@@ -3154,7 +3165,7 @@ static bool specMatch(
31543165
const TemplateSimplifier::TokenAndName &decl)
31553166
{
31563167
// make sure decl is really a declaration
3157-
if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias())
3168+
if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend())
31583169
return false;
31593170

31603171
return spec.isSameFamily(decl);
@@ -3237,8 +3248,8 @@ void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues()
32373248
getTemplateParametersInDeclaration(forwardDecl.token()->tokAt(2), params1);
32383249

32393250
for (auto & decl : mTemplateDeclarations) {
3240-
// skip partializations
3241-
if (decl.isPartialSpecialization())
3251+
// skip partializations, type aliases and friends
3252+
if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend())
32423253
continue;
32433254

32443255
std::vector<const Token *> params2;
@@ -3321,6 +3332,8 @@ void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::s
33213332
std::cout << " isForwardDeclaration";
33223333
if (tokenAndName.isVariadic())
33233334
std::cout << " isVariadic";
3335+
if (tokenAndName.isFriend())
3336+
std::cout << " isFriend";
33243337
std::cout << std::endl;
33253338
if (tokenAndName.token() && !tokenAndName.paramEnd() && tokenAndName.token()->strAt(1) == "<") {
33263339
const Token *end = tokenAndName.token()->next()->findClosingBracket();
@@ -3544,13 +3557,13 @@ void TemplateSimplifier::simplifyTemplates(
35443557
std::set<std::string> expandedtemplates;
35453558

35463559
for (std::list<TokenAndName>::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) {
3547-
if (iter1->isAlias())
3560+
if (iter1->isAlias() || iter1->isFriend())
35483561
continue;
35493562

35503563
// get specializations..
35513564
std::list<const Token *> specializations;
35523565
for (std::list<TokenAndName>::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) {
3553-
if (iter2->isAlias())
3566+
if (iter2->isAlias() || iter2->isFriend())
35543567
continue;
35553568

35563569
if (iter1->fullName() == iter2->fullName())

lib/templatesimplifier.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class CPPCHECKLIB TemplateSimplifier {
8787
fIsPartialSpecialization = (1 << 5), // user partial specialized template
8888
fIsForwardDeclaration = (1 << 6), // forward declaration
8989
fIsVariadic = (1 << 7), // variadic template
90+
fIsFriend = (1 << 8), // friend template
9091
fFamilyMask = (fIsClass | fIsFunction | fIsVariable)
9192
};
9293

@@ -114,6 +115,9 @@ class CPPCHECKLIB TemplateSimplifier {
114115
void isVariadic(bool state) {
115116
setFlag(fIsVariadic, state);
116117
}
118+
void isFriend(bool state) {
119+
setFlag(fIsFriend, state);
120+
}
117121

118122
/**
119123
* Get specified flag state.
@@ -205,6 +209,9 @@ class CPPCHECKLIB TemplateSimplifier {
205209
bool isVariadic() const {
206210
return getFlag(fIsVariadic);
207211
}
212+
bool isFriend() const {
213+
return getFlag(fIsFriend);
214+
}
208215

209216
/**
210217
* Get alias start token.

test/testsimplifytemplate.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ class TestSimplifyTemplate : public TestFixture {
176176
TEST_CASE(template136); // #9287
177177
TEST_CASE(template137); // #9288
178178
TEST_CASE(template138);
179+
TEST_CASE(template139);
179180
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
180181
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
181182
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@@ -3364,6 +3365,33 @@ class TestSimplifyTemplate : public TestFixture {
33643365
}
33653366
}
33663367

3368+
void template139() {
3369+
{
3370+
const char code[] = "template<typename T>\n"
3371+
"struct Foo {\n"
3372+
" template<typename> friend struct Foo;\n"
3373+
"};";
3374+
const char exp[] = "template < typename T > "
3375+
"struct Foo { "
3376+
"template < typename > friend struct Foo ; "
3377+
"} ;";
3378+
ASSERT_EQUALS(exp, tok(code));
3379+
}
3380+
{
3381+
const char code[] = "template<typename T>\n"
3382+
"struct Foo {\n"
3383+
" template<typename> friend struct Foo;\n"
3384+
"} ;\n"
3385+
"Foo<int> foo;";
3386+
const char exp[] = "struct Foo<int> ; "
3387+
"Foo<int> foo ; "
3388+
"struct Foo<int> { "
3389+
"template < typename > friend struct Foo ; "
3390+
"} ;";
3391+
ASSERT_EQUALS(exp, tok(code));
3392+
}
3393+
}
3394+
33673395
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
33683396
const char code[] = "template <typename T> struct C {};\n"
33693397
"template <typename T> struct S {a};\n"

0 commit comments

Comments
 (0)