Remove bailout and fix varid for template class member initialized in out-of-line constructor (#8031)

This commit is contained in:
Simon Martin 2017-04-30 08:59:47 +02:00 committed by PKEuS
parent 8a668aa860
commit 28960a8bba
4 changed files with 78 additions and 12 deletions

View File

@ -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% (")) {

View File

@ -2910,8 +2910,10 @@ void Tokenizer::setVarIdPass2()
for (std::list<Token *>::iterator func = allMemberFunctions.begin(); func != allMemberFunctions.end(); ++func) {
Token *tok2 = *func;
if (!Token::Match(tok2, classname.c_str()))
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();

View File

@ -1359,10 +1359,13 @@ 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);
if (onlyCreateTokens)
tokenizer.createTokens(istr, "test.cpp");
else
tokenizer.tokenize(istr, "test.cpp", emptyString);
const Token *_tok = tokenizer.tokens();
@ -1398,6 +1401,11 @@ private:
"template<class T> unsigned A<T>::foo() { return 0; }", 19));
ASSERT_EQUALS(9, templateNamePositionHelper("template<class T, class U> class A { unsigned foo(); }; "
"template<class T, class U> unsigned A<T, U>::foo() { return 0; }", 25));
ASSERT_EQUALS(9, templateNamePositionHelper("template<class T, class U> class A { unsigned foo(); }; "
"template<class T, class U> unsigned A<T, U>::foo() { return 0; }", 25, /*onlyCreateTokens=*/true));
ASSERT_EQUALS(12, templateNamePositionHelper("template<class T> class v {}; "
"template<class T, class U> class A { unsigned foo(); }; "
"template<> unsigned A<int, v<char> >::foo() { return 0; }", 30, /*onlyCreateTokens=*/true));
}
void expandSpecialized() {

View File

@ -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<class T> struct Containter {\n"
" Containter();\n"
" int* mElements;\n"
"};\n"
"template <class T> Containter<T>::Containter() : mElements(nullptr) {}\n"
"Containter<int> 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);