From 999d2f797c324638997a09d0a99d6a1466ffed37 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Wed, 24 Jul 2019 13:20:19 -0400 Subject: [PATCH] Fix #9225 (Crash on valid C++14 code) (#2031) * Fix #9225 (Crash on valid C++14 code) This only fixes the crash. Specialization of nested templates is still broken. * fix cppcheck warnings * fixed another cppcheck warning --- lib/templatesimplifier.cpp | 60 ++++++++++++++++++++--- lib/tokenize.cpp | 2 +- test/testgarbage.cpp | 1 - test/testsimplifytemplate.cpp | 91 +++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 64ada84f3..02414cf48 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -79,7 +79,21 @@ TemplateSimplifier::TokenAndName::TokenAndName(Token *tok, const std::string &s, // only set flags for declaration if (token && nameToken && paramEnd) { isSpecialization(Token::simpleMatch(token, "template < >")); - isPartialSpecialization(!isSpecialization() && nameToken->strAt(1) == "<"); + + if (!isSpecialization()) { + if (Token::simpleMatch(token->next()->findClosingBracket(), "> template <")) { + const Token * temp = nameToken->tokAt(-2); + while (Token::Match(temp, ">|%name% ::")) { + if (temp->str() == ">") + temp = temp->findOpeningBracket()->previous(); + else + temp = temp->tokAt(-2); + } + isPartialSpecialization(temp->strAt(1) == "<"); + } else + isPartialSpecialization(nameToken->strAt(1) == "<"); + } + isAlias(paramEnd->strAt(1) == "using"); if (isAlias() && isPartialSpecialization()) { @@ -775,6 +789,19 @@ bool TemplateSimplifier::getTemplateDeclarations() // ignore template template parameter if (tok->strAt(-1) == "<") continue; + // ignore nested template + if (tok->strAt(-1) == ">") + continue; + // skip to last nested template parameter + const Token *tok1 = tok; + while (tok1 && tok1->next() && Token::simpleMatch(tok1->next()->findClosingBracket(), "> template <")) { + const Token *closing = tok1->next()->findClosingBracket(); + if (!closing) + syntaxError(tok1->next()); + tok1 = closing->next(); + } + if (!tok1) + syntaxError(tok); // Some syntax checks, see #6865 if (!tok->tokAt(2)) syntaxError(tok->next()); @@ -782,7 +809,7 @@ bool TemplateSimplifier::getTemplateDeclarations() !Token::Match(tok->tokAt(3), "%name%|.|,|=|>")) syntaxError(tok->next()); codeWithTemplates = true; - const Token * const parmEnd = tok->next()->findClosingBracket(); + const Token * const parmEnd = tok1->next()->findClosingBracket(); for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { if (tok2->str() == "(" && tok2->link()) tok2 = tok2->link(); @@ -1403,9 +1430,9 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *to if (Token::Match(tok->next(), ";|{")) return false; // skip decltype(...) - else if (Token::simpleMatch(tok, "decltype (")) { - const Token * end = tok->linkAt(1); - while (tok && tok != end) { + else if (Token::simpleMatch(tok->next(), "decltype (")) { + const Token * end = tok->linkAt(2)->previous(); + while (tok && tok->next() && tok != end) { tok = tok->next(); namepos++; } @@ -1863,6 +1890,7 @@ void TemplateSimplifier::expandTemplate( if (tok5) tok5 = tok5->next(); // copy return type + std::stack brackets2; // holds "(" and "{" tokens while (tok5 && tok5 != tok3) { // replace name if found if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name) { @@ -1924,8 +1952,28 @@ void TemplateSimplifier::expandTemplate( } } } - if (!added) + if (!added) { mTokenList.addtoken(tok5, tok5->linenr(), tok5->fileIndex()); + Token *back = mTokenList.back(); + if (Token::Match(back, "{|(|[")) { + brackets2.push(back); + } else if (back->str() == "}") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "{"); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } else if (back->str() == ")") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "("); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } else if (back->str() == "]") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "["); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } + } } tok5 = tok5->next(); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 9b91a5c6c..5caffdc63 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -111,7 +111,7 @@ const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &end tok = tok->linkAt(1)->next(); if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) tok = tok->linkAt(1)->next(); - if (tok && tok->str() == ".") { // trailing return type + if (tok && tok->originalName() == "->") { // trailing return type for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) if (tok->link() && Token::Match(tok, "<|[|(")) tok = tok->link(); diff --git a/test/testgarbage.cpp b/test/testgarbage.cpp index 432919b17..d153b7513 100644 --- a/test/testgarbage.cpp +++ b/test/testgarbage.cpp @@ -1592,7 +1592,6 @@ private: void garbageCode203() { // #8972 checkCode("{ > () {} }"); checkCode("template <> a > ::b();"); - ASSERT_THROW(checkCode("{ template class b { } template <> template c() b::e() { } template b; }"), InternalError); } void garbageCode204() { diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index fead158e8..7fc3e9352 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -164,6 +164,7 @@ private: TEST_CASE(template124); // #9197 TEST_CASE(template125); TEST_CASE(template126); // #9217 + TEST_CASE(template127); // #9225 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) @@ -2943,6 +2944,96 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template127() { // #9225 + { + const char code[] = "template struct a {\n" + " template constexpr decltype(auto) operator()(b &&) const;\n" + "};\n" + "a c;\n" + "template \n" + "template \n" + "constexpr decltype(auto) a::operator()(b &&) const {}"; + const char exp[] = "struct a ; " + "a c ; " + "template < typename d > " + "template < typename b > " + "const decltype ( auto ) a < d > :: operator() ( b & & ) const { } " + "struct a { " + "template < typename b > const decltype ( auto ) operator() ( b & & ) const ; " + "} ;"; + const char act[] = "struct a ; " + "a c ; " + "template < typename d > " + "template < typename b > " + "const decltype ( auto ) a < d > :: operator() ( b & & ) const { } " + "struct a { " + "template < typename b > const decltype ( auto ) operator() ( b & & ) const ; " + "} ; " + "const decltype ( auto ) a :: operator() ( b & & ) const { }"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + { + const char code[] = "template struct a {\n" + " template static void foo();\n" + "};\n" + "a c;\n" + "template \n" + "template \n" + "void a::foo() {}\n" + "void bar() { a::foo(); }"; + const char exp[] = "struct a ; " + "a c ; " + "template < typename d > " + "template < typename b > " + "void a < d > :: foo ( ) { } " + "void bar ( ) { a :: foo < char > ( ) ; } " + "struct a { " + "template < typename b > static void foo ( ) ; " + "static void foo ( ) ; " + "} ; " + "void a :: foo ( ) { }"; + const char act[] = "struct a ; " + "a c ; " + "template < typename d > " + "template < typename b > " + "void a < d > :: foo ( ) { } " + "void bar ( ) { a :: foo < char > ( ) ; } " + "struct a { " + "template < typename b > static void foo ( ) ; " + "} ; " + "void a :: foo ( ) { }"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + { + const char code[] = "template struct a {\n" + " template static void foo();\n" + "};\n" + "template \n" + "template \n" + "void a::foo() {}\n" + "void bar() { a::foo(); }"; + const char exp[] = "struct a ; " + "template < typename d > " + "template < typename b > " + "void a < d > :: foo ( ) { } " + "void bar ( ) { a :: foo < char > ( ) ; } " + "struct a { " + "static void foo ( ) ; " + "} ; " + "void a :: foo ( ) { }"; + const char act[] = "struct a ; " + "template < typename d > " + "template < typename b > " + "void a < d > :: foo ( ) { } " + "void bar ( ) { a :: foo < char > ( ) ; } " + "struct a { " + "template < typename b > static void foo ( ) ; " + "} ; " + "void a :: foo ( ) { }"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n"