From 28960a8bba16c864a833f696ea82dfd9798c6d97 Mon Sep 17 00:00:00 2001 From: Simon Martin Date: Sun, 30 Apr 2017 08:59:47 +0200 Subject: [PATCH] Remove bailout and fix varid for template class member initialized in out-of-line constructor (#8031) --- lib/templatesimplifier.cpp | 38 +++++++++++++++++++++++++++-------- lib/tokenize.cpp | 6 ++++-- test/testsimplifytemplate.cpp | 12 +++++++++-- test/testtokenize.cpp | 34 +++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 00494f844..4ed2f6ba3 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -710,6 +710,33 @@ bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::stri return true; } +// Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template member functions, +// hence this pattern: "> %type% [%type%] < ... > :: %type% (" +bool getTemplateNamePositionTemplateMember(const Token *tok, int &namepos) +{ + if (!Token::Match(tok, "> %type% <") && !Token::Match(tok, "> %type% %type% <")) + return false; + + int currPos = 0; + currPos = 2 + (Token::Match(tok, "> %type% %type%")); + + // Find the end of the template argument list + const Token *templateParmEnd = tok->linkAt(currPos); + if (!templateParmEnd) + templateParmEnd = tok->tokAt(currPos)->findClosingBracket(); + if (!templateParmEnd) + return false; + + if (Token::Match(templateParmEnd->next(), ":: %type% (")) { + // We have a match, and currPos points at the template list opening '<'. Move it to the closing '>' + for (const Token *tok2 = tok->tokAt(currPos) ; tok2 != templateParmEnd ; tok2 = tok2->next()) + ++currPos; + namepos = currPos + 2; + return true; + } + return false; +} + int TemplateSimplifier::getTemplateNamePosition(const Token *tok) { // get the position of the template name @@ -720,14 +747,9 @@ int TemplateSimplifier::getTemplateNamePosition(const Token *tok) namepos = 2; else if (Token::Match(tok, "> %type% %type% *|&| %type% (")) namepos = 3; - else if ((Token::Match(tok, "> %type% <") && Token::Match(tok->linkAt(2), "> :: %type% (")) || - (Token::Match(tok, "> %type% %type% <") && Token::Match(tok->linkAt(3), "> :: %type% ("))) { - namepos = 2 + (Token::Match(tok, "> %type% %type%")); - const Token *end = tok->linkAt(namepos); - for (const Token *tok2 = tok->tokAt(namepos) ; tok2 != end ; tok2 = tok2->next()) - ++namepos; - namepos += 2; - } else if (Token::Match(tok, "> %type% *|&| %type% :: %type% (")) { + else if (getTemplateNamePositionTemplateMember(tok, namepos)) + ; + else if (Token::Match(tok, "> %type% *|&| %type% :: %type% (")) { namepos = 4; starAmpPossiblePosition = 2; } else if (Token::Match(tok, "> %type% %type% *|&| %type% :: %type% (")) { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 567773cb5..5f58be34e 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -2910,8 +2910,10 @@ void Tokenizer::setVarIdPass2() for (std::list::iterator func = allMemberFunctions.begin(); func != allMemberFunctions.end(); ++func) { Token *tok2 = *func; - if (!Token::Match(tok2, classname.c_str())) - continue; + if (!Token::Match(tok2, classname.c_str())) { + if (tok2->str() != classname) // #8031: Both could be "A < B >" and if so, one must not bail out + continue; + } if (Token::Match(tok2, "%name% <")) tok2 = tok2->next()->findClosingBracket(); diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index 1e52c5c2a..1e8355202 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -1359,11 +1359,14 @@ private: } // Helper function to unit test TemplateSimplifier::getTemplateNamePosition - int templateNamePositionHelper(const char code[], unsigned offset = 0) { + int templateNamePositionHelper(const char code[], unsigned offset = 0, bool onlyCreateTokens = false) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp", emptyString); + if (onlyCreateTokens) + tokenizer.createTokens(istr, "test.cpp"); + else + tokenizer.tokenize(istr, "test.cpp", emptyString); const Token *_tok = tokenizer.tokens(); for (unsigned i = 0 ; i < offset ; ++i) @@ -1398,6 +1401,11 @@ private: "template unsigned A::foo() { return 0; }", 19)); ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " "template unsigned A::foo() { return 0; }", 25)); + ASSERT_EQUALS(9, templateNamePositionHelper("template class A { unsigned foo(); }; " + "template unsigned A::foo() { return 0; }", 25, /*onlyCreateTokens=*/true)); + ASSERT_EQUALS(12, templateNamePositionHelper("template class v {}; " + "template class A { unsigned foo(); }; " + "template<> unsigned A >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true)); } void expandSpecialized() { diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 960d80fee..c9fb4be01 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -63,6 +63,7 @@ private: TEST_CASE(tokenize31); // #3503 (Wrong handling of member function taking function pointer as argument) TEST_CASE(tokenize32); // #5884 (fsanitize=undefined: left shift of negative value -10000 in lib/templatesimplifier.cpp:852:46) TEST_CASE(tokenize33); // #5780 Various crashes on valid template code + TEST_CASE(tokenize34); // #8031 TEST_CASE(validate); @@ -785,6 +786,39 @@ private: tokenizeAndStringify(code, true); } + void tokenize34() { // #8031 + { + const char code[] = "struct Containter {\n" + " Containter();\n" + " int* mElements;\n" + "};\n" + "Containter::Containter() : mElements(nullptr) {}\n" + "Containter intContainer;"; + const char exp [] = "1: struct Containter {\n" + "2: Containter ( ) ;\n" + "3: int * mElements@1 ;\n" + "4: } ;\n" + "5: Containter :: Containter ( ) : mElements@1 ( nullptr ) { }\n" + "6: Containter intContainer@2 ;\n"; + ASSERT_EQUALS(exp, tokenizeDebugListing(code, /*simplify=*/true)); + } + { + const char code[] = "template struct Containter {\n" + " Containter();\n" + " int* mElements;\n" + "};\n" + "template Containter::Containter() : mElements(nullptr) {}\n" + "Containter intContainer;"; + const char exp [] = "5: template < class T > Containter < T > :: Containter ( ) : mElements ( nullptr ) { }\n" + "6: Containter < int > intContainer@1 ; struct Containter < int > {\n" + "2: Containter < int > ( ) ;\n" + "3: int * mElements@2 ;\n" + "4: } ;\n" + "5: Containter < int > :: Containter ( ) : mElements@2 ( nullptr ) { }\n"; + ASSERT_EQUALS(exp, tokenizeDebugListing(code, /*simplify=*/true)); + } + } + void validate() { // C++ code in C file ASSERT_THROW(tokenizeAndStringify(";using namespace std;",false,false,Settings::Native,"test.c"), InternalError);