Refactor Tokenizer::simplifyUsing to use continue to reduce indentation (#1967)

* Refactor Tokenizer::simplifyUsing to use continue to reduce indentation

added function findTemplateDeclarationEnd to skip template declarations
to reduce duplicate code

* fix travis build
This commit is contained in:
IOBYTE 2019-07-07 12:33:33 -04:00 committed by Daniel Marjamäki
parent db43dcd601
commit e551057f59
4 changed files with 341 additions and 303 deletions

View File

@ -545,6 +545,43 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok)
return 0; return 0;
} }
const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok)
{
return const_cast<const Token *>(findTemplateDeclarationEnd(const_cast<Token *>(tok)));
}
Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok)
{
if (Token::simpleMatch(tok, "template <")) {
tok = tok->next()->findClosingBracket();
if (tok)
tok = tok->next();
}
if (!tok)
return nullptr;
Token * tok2 = tok;
while (tok2 && !Token::Match(tok2, ";|{")) {
if (tok2->str() == "<")
tok2 = tok2->findClosingBracket();
else if (Token::Match(tok2, "(|[") && tok2->link())
tok2 = tok2->link();
if (tok2)
tok2 = tok2->next();
}
if (tok2 && tok2->str() == "{") {
tok = tok2->link();
if (tok && tok->strAt(1) == ";")
tok = tok->next();
} else if (tok2 && tok2->str() == ";")
tok = tok2;
else
tok = nullptr;
return tok;
}
void TemplateSimplifier::eraseTokens(Token *begin, const Token *end) void TemplateSimplifier::eraseTokens(Token *begin, const Token *end)
{ {
if (!begin || begin == end) if (!begin || begin == end)
@ -829,18 +866,9 @@ void TemplateSimplifier::getTemplateInstantiations()
// #7914 // #7914
// Ignore template instantiations within template definitions: they will only be // Ignore template instantiations within template definitions: they will only be
// handled if the definition is actually instantiated // handled if the definition is actually instantiated
Token * tok2 = tok->next();
while (tok2 && !Token::Match(tok2, ";|{")) { Token * tok2 = findTemplateDeclarationEnd(tok->next());
if (tok2->str() == "<") if (tok2)
tok2 = tok2->findClosingBracket();
else if (Token::Match(tok2, "(|[") && tok2->link())
tok2 = tok2->link();
if (tok2)
tok2 = tok2->next();
}
if (tok2 && tok2->str() == "{")
tok = tok2->link();
else if (tok2 && tok2->str() == ";")
tok = tok2; tok = tok2;
} }
} else if (Token::Match(tok, "template using %name% <")) { } else if (Token::Match(tok, "template using %name% <")) {

View File

@ -222,6 +222,14 @@ public:
} }
}; };
/**
* Find last token of a template declaration.
* @param tok start token of declaration "template" or token after "template < ... >"
* @return last token of declaration or nullptr if syntax error
*/
static Token *findTemplateDeclarationEnd(Token *tok);
static const Token *findTemplateDeclarationEnd(const Token *tok);
/** /**
* Match template declaration/instantiation * Match template declaration/instantiation
* @param instance template instantiation * @param instance template instantiation

View File

@ -1878,8 +1878,6 @@ bool Tokenizer::simplifyUsing()
{ {
bool substitute = false; bool substitute = false;
std::list<ScopeInfo3> scopeList; std::list<ScopeInfo3> scopeList;
bool inTemplateDefinition = false;
const Token *endOfTemplateDefinition = nullptr;
struct Using { struct Using {
Using(Token *start, Token *end) : startTok(start), endTok(end) { } Using(Token *start, Token *end) : startTok(start), endTok(end) { }
Token *startTok; Token *startTok;
@ -1901,301 +1899,274 @@ bool Tokenizer::simplifyUsing()
setScopeInfo(tok, &scopeList); setScopeInfo(tok, &scopeList);
} }
if (inTemplateDefinition) { // skip template declarations
if (!endOfTemplateDefinition) { if (Token::Match(tok, "template < !!>")) {
if (tok->str() == "{") Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok);
endOfTemplateDefinition = tok->link(); if (endToken)
else if (tok->str() == ";") tok = endToken;
endOfTemplateDefinition = tok; continue;
}
// look for non-template type aliases
if (!(tok->strAt(-1) != ">" &&
(Token::Match(tok, "using %name% = ::| %name%") ||
(Token::Match(tok, "using %name% [ [") &&
Token::Match(tok->linkAt(2), "] ] = ::| %name%")))))
continue;
std::list<ScopeInfo3> scopeList1;
scopeList1.emplace_back("", nullptr);
std::string name = tok->strAt(1);
const Token *nameToken = tok->next();
std::string scope = getScopeName(scopeList);
Token *usingStart = tok;
Token *start;
if (tok->strAt(2) == "=")
start = tok->tokAt(3);
else
start = tok->linkAt(2)->tokAt(3);
Token *usingEnd = findSemicolon(start);
if (!usingEnd)
continue;
// Move struct defined in using out of using.
// using T = struct t { }; => struct t { }; using T = struct t;
// fixme: this doesn't handle attributes
if (Token::Match(start, "struct|union|enum %name%| {")) {
if (start->strAt(1) != "{") {
Token *structEnd = start->linkAt(2);
structEnd->insertToken(";", "");
list.copyTokens(structEnd->next(), tok, start->next());
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
} else {
Token *structEnd = start->linkAt(1);
structEnd->insertToken(";", "");
std::string newName;
if (structEnd->strAt(2) == ";")
newName = name;
else
newName = "Unnamed" + MathLib::toString(mUnnamedCount++);
list.copyTokens(structEnd->next(), tok, start);
structEnd->tokAt(5)->insertToken(newName, "");
start->insertToken(newName, "");
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
} }
if (tok == endOfTemplateDefinition) { }
inTemplateDefinition = false;
endOfTemplateDefinition = nullptr; // remove 'typename' and 'template'
else if (start->str() == "typename") {
start->deleteThis();
Token *temp = start;
while (Token::Match(temp, "%name% ::"))
temp = temp->tokAt(2);
if (Token::Match(temp, "template %name%"))
temp->deleteThis();
}
// Unfortunately we have to start searching from the beginning
// of the token stream because templates are instantiated at
// the end of the token stream and it may be used before then.
std::string scope1;
bool skip = false; // don't erase type aliases we can't parse
for (Token* tok1 = list.front(); tok1; tok1 = tok1->next()) {
if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") ||
Token::Match(tok1, "using namespace %name% ;|::")) {
setScopeInfo(tok1, &scopeList1, true);
scope1 = getScopeName(scopeList1);
continue; continue;
} }
}
if (tok->str()=="template") { // skip template definitions
if (Token::Match(tok->next(), "< !!>")) if (Token::Match(tok1, "template < !!>")) {
inTemplateDefinition = true; Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok1);
else if (endToken)
inTemplateDefinition = false; tok1 = endToken;
} continue;
if (!inTemplateDefinition) {
// look for non-template type aliases
if (tok->strAt(-1) != ">" &&
(Token::Match(tok, "using %name% = ::| %name%") ||
(Token::Match(tok, "using %name% [ [") &&
Token::Match(tok->linkAt(2), "] ] = ::| %name%")))) {
std::list<ScopeInfo3> scopeList1;
scopeList1.emplace_back("", nullptr);
std::string name = tok->strAt(1);
const Token *nameToken = tok->next();
std::string scope = getScopeName(scopeList);
Token *usingStart = tok;
Token *start;
if (tok->strAt(2) == "=")
start = tok->tokAt(3);
else
start = tok->linkAt(2)->tokAt(3);
Token *usingEnd = findSemicolon(start);
if (!usingEnd)
continue;
// Move struct defined in using out of using.
// using T = struct t { }; => struct t { }; using T = struct t;
// fixme: this doesn't handle attributes
if (Token::Match(start, "struct|union|enum %name%| {")) {
if (start->strAt(1) != "{") {
Token *structEnd = start->linkAt(2);
structEnd->insertToken(";", "");
list.copyTokens(structEnd->next(), tok, start->next());
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
} else {
Token *structEnd = start->linkAt(1);
structEnd->insertToken(";", "");
std::string newName;
if (structEnd->strAt(2) == ";")
newName = name;
else
newName = "Unnamed" + MathLib::toString(mUnnamedCount++);
list.copyTokens(structEnd->next(), tok, start);
structEnd->tokAt(5)->insertToken(newName, "");
start->insertToken(newName, "");
usingStart = structEnd->tokAt(2);
nameToken = usingStart->next();
if (usingStart->strAt(2) == "=")
start = usingStart->tokAt(3);
else
start = usingStart->linkAt(2)->tokAt(3);
usingEnd = findSemicolon(start);
tok->deleteThis();
tok->deleteThis();
tok->deleteThis();
tok = usingStart;
}
}
// remove 'typename' and 'template'
else if (start->str() == "typename") {
start->deleteThis();
Token *temp = start;
while (Token::Match(temp, "%name% ::"))
temp = temp->tokAt(2);
if (Token::Match(temp, "template %name%"))
temp->deleteThis();
}
// Unfortunately we have to start searching from the beginning
// of the token stream because templates are instantiated at
// the end of the token stream and it may be used before then.
std::string scope1;
bool skip = false; // don't erase type aliases we can't parse
for (Token* tok1 = list.front(); tok1; tok1 = tok1->next()) {
if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") ||
Token::Match(tok1, "using namespace %name% ;|::")) {
setScopeInfo(tok1, &scopeList1, true);
scope1 = getScopeName(scopeList1);
continue;
}
// skip template definitions
if (Token::Match(tok1, "template < !!>")) {
tok1 = tok1->next()->findClosingBracket();
if (tok1) {
Token * tok2 = tok1->next();
while (tok2 && !Token::Match(tok2, ";|{")) {
if (tok2->str() == "<")
tok2 = tok2->findClosingBracket();
else if (Token::Match(tok2, "(|[") && tok2->link())
tok2 = tok2->link();
if (tok2)
tok2 = tok2->next();
}
if (tok2 && tok2->str() == "{")
tok1 = tok2->link();
else if (tok2 && tok2->str() == ";")
tok1 = tok2;
}
continue;
}
if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
continue;
// remove the qualification
std::string fullScope = scope;
while (tok1->strAt(-1) == "::") {
if (fullScope == tok1->strAt(-2)) {
tok1->deletePrevious();
tok1->deletePrevious();
break;
} else {
const std::string::size_type idx = fullScope.rfind(" ");
if (idx == std::string::npos)
break;
if (tok1->strAt(-2) == fullScope.substr(idx + 1)) {
tok1->deletePrevious();
tok1->deletePrevious();
fullScope.resize(idx - 3);
} else
break;
}
}
Token * arrayStart = nullptr;
// parse the type
Token *type = start;
if (type->str() == "::") {
type = type->next();
while (Token::Match(type, "%type% ::"))
type = type->tokAt(2);
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type% ::")) {
do {
type = type->tokAt(2);
} while (Token::Match(type, "%type% ::"));
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType()))
type = type->next();
type = type->next();
while (Token::Match(type, "%type%") &&
(type->isStandardType() || Token::Match(type, "unsigned|signed"))) {
type = type->next();
}
bool atEnd = false;
while (!atEnd) {
if (type && type->str() == "::") {
type = type->next();
}
if (Token::Match(type, "%type%") &&
type->next() && !Token::Match(type->next(), "[|;|,|(")) {
type = type->next();
} else if (Token::simpleMatch(type, "const (")) {
type = type->next();
atEnd = true;
} else
atEnd = true;
}
} else
syntaxError(type);
// check for invalid input
if (!type)
syntaxError(tok1);
// check for template
if (type->str() == "<") {
type = type->findClosingBracket();
while (type && Token::Match(type->next(), ":: %type%"))
type = type->tokAt(2);
if (!type) {
syntaxError(tok1);
}
while (Token::Match(type->next(), "const|volatile"))
type = type->next();
type = type->next();
}
// check for pointers and references
std::list<std::string> pointers;
while (Token::Match(type, "*|&|&&|const")) {
pointers.push_back(type->str());
type = type->next();
}
// check for array
if (type && type->str() == "[") {
do {
if (!arrayStart)
arrayStart = type;
bool atEnd = false;
while (!atEnd) {
while (type->next() && !Token::Match(type->next(), ";|,")) {
type = type->next();
}
if (!type->next())
syntaxError(type); // invalid input
else if (type->next()->str() == ";")
atEnd = true;
else if (type->str() == "]")
atEnd = true;
else
type = type->next();
}
type = type->next();
} while (type && type->str() == "[");
}
Token* after = tok1->next();
// check if type was parsed
if (type && type == usingEnd) {
// check for array syntax and add type around variable
if (arrayStart) {
if (Token::Match(tok1->next(), "%name%")) {
list.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
list.copyTokens(tok1, start, arrayStart->previous());
tok1->deleteThis();
substitute = true;
}
} else {
// just replace simple type aliases
list.copyTokens(tok1, start, usingEnd->previous());
tok1->deleteThis();
substitute = true;
}
} else {
skip = true;
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
if (!str.empty())
str += ' ';
str += tok3->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &list, Severity::debug, "debug",
"Failed to parse \'" + str + "\'. The checking continues anyway.", false));
}
}
tok1 = after;
}
if (!skip)
usingList.emplace_back(usingStart, usingEnd);
} }
if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
continue;
// remove the qualification
std::string fullScope = scope;
while (tok1->strAt(-1) == "::") {
if (fullScope == tok1->strAt(-2)) {
tok1->deletePrevious();
tok1->deletePrevious();
break;
} else {
const std::string::size_type idx = fullScope.rfind(" ");
if (idx == std::string::npos)
break;
if (tok1->strAt(-2) == fullScope.substr(idx + 1)) {
tok1->deletePrevious();
tok1->deletePrevious();
fullScope.resize(idx - 3);
} else
break;
}
}
Token * arrayStart = nullptr;
// parse the type
Token *type = start;
if (type->str() == "::") {
type = type->next();
while (Token::Match(type, "%type% ::"))
type = type->tokAt(2);
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type% ::")) {
do {
type = type->tokAt(2);
} while (Token::Match(type, "%type% ::"));
if (Token::Match(type, "%type%"))
type = type->next();
} else if (Token::Match(type, "%type%")) {
while (Token::Match(type, "const|struct|union|enum %type%") ||
(type->next() && type->next()->isStandardType()))
type = type->next();
type = type->next();
while (Token::Match(type, "%type%") &&
(type->isStandardType() || Token::Match(type, "unsigned|signed"))) {
type = type->next();
}
bool atEnd = false;
while (!atEnd) {
if (type && type->str() == "::") {
type = type->next();
}
if (Token::Match(type, "%type%") &&
type->next() && !Token::Match(type->next(), "[|;|,|(")) {
type = type->next();
} else if (Token::simpleMatch(type, "const (")) {
type = type->next();
atEnd = true;
} else
atEnd = true;
}
} else
syntaxError(type);
// check for invalid input
if (!type)
syntaxError(tok1);
// check for template
if (type->str() == "<") {
type = type->findClosingBracket();
while (type && Token::Match(type->next(), ":: %type%"))
type = type->tokAt(2);
if (!type) {
syntaxError(tok1);
}
while (Token::Match(type->next(), "const|volatile"))
type = type->next();
type = type->next();
}
// check for pointers and references
std::list<std::string> pointers;
while (Token::Match(type, "*|&|&&|const")) {
pointers.push_back(type->str());
type = type->next();
}
// check for array
if (type && type->str() == "[") {
do {
if (!arrayStart)
arrayStart = type;
bool atEnd = false;
while (!atEnd) {
while (type->next() && !Token::Match(type->next(), ";|,")) {
type = type->next();
}
if (!type->next())
syntaxError(type); // invalid input
else if (type->next()->str() == ";")
atEnd = true;
else if (type->str() == "]")
atEnd = true;
else
type = type->next();
}
type = type->next();
} while (type && type->str() == "[");
}
Token* after = tok1->next();
// check if type was parsed
if (type && type == usingEnd) {
// check for array syntax and add type around variable
if (arrayStart) {
if (Token::Match(tok1->next(), "%name%")) {
list.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
list.copyTokens(tok1, start, arrayStart->previous());
tok1->deleteThis();
substitute = true;
}
} else {
// just replace simple type aliases
list.copyTokens(tok1, start, usingEnd->previous());
tok1->deleteThis();
substitute = true;
}
} else {
skip = true;
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
if (!str.empty())
str += ' ';
str += tok3->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &list, Severity::debug, "debug",
"Failed to parse \'" + str + "\'. The checking continues anyway.", false));
}
}
tok1 = after;
} }
if (!skip)
usingList.emplace_back(usingStart, usingEnd);
} }
// delete all used type alias definitions // delete all used type alias definitions

View File

@ -190,6 +190,8 @@ private:
TEST_CASE(templateNamePosition); TEST_CASE(templateNamePosition);
TEST_CASE(findTemplateDeclarationEnd);
TEST_CASE(expandSpecialized1); TEST_CASE(expandSpecialized1);
TEST_CASE(expandSpecialized2); TEST_CASE(expandSpecialized2);
TEST_CASE(expandSpecialized3); // #8671 TEST_CASE(expandSpecialized3); // #8671
@ -3713,6 +3715,35 @@ private:
"template<> unsigned A<int, v<char> >::foo() { return 0; }", 2)); "template<> unsigned A<int, v<char> >::foo() { return 0; }", 2));
} }
// Helper function to unit test TemplateSimplifier::findTemplateDeclarationEnd
bool findTemplateDeclarationEndHelper(const char code[], const char pattern[], unsigned offset = 0) {
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.createTokens(istr, "test.cpp");
tokenizer.createLinks();
tokenizer.mTemplateSimplifier->fixAngleBrackets();
const Token *_tok = tokenizer.tokens();
for (unsigned i = 0 ; i < offset ; ++i)
_tok = _tok->next();
const Token *tok1 = tokenizer.mTemplateSimplifier->findTemplateDeclarationEnd(_tok);
return (tok1 == Token::findsimplematch(tokenizer.list.front(), pattern));
}
void findTemplateDeclarationEnd() {
ASSERT(findTemplateDeclarationEndHelper("template <typename T> class Fred { }; int x;", "; int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <typename T> void Fred() { } int x;", "} int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <typename T> int Fred = 0; int x;", "; int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <typename T> constexpr auto func = [](auto x){ return T(x);}; int x;", "; int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <class, class a> auto b() -> decltype(a{}.template b<void(int, int)>); int x;", "; int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <class, class a> auto b() -> decltype(a{}.template b<void(int, int)>){} int x;", "} int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <typename... f, c<h<e<typename f::d...>>::g>> void i(); int x;", "; int x ;"));
ASSERT(findTemplateDeclarationEndHelper("template <typename... f, c<h<e<typename f::d...>>::g>> void i(){} int x;", "} int x ;"));
}
void expandSpecialized1() { void expandSpecialized1() {
ASSERT_EQUALS("class A<int> { } ;", tok("template<> class A<int> {};")); ASSERT_EQUALS("class A<int> { } ;", tok("template<> class A<int> {};"));
ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};")); ASSERT_EQUALS("class A<int> : public B { } ;", tok("template<> class A<int> : public B {};"));