Moved simplifyUsing from TemplateSimplifier to Tokenizer.
This commit is contained in:
parent
49e2f9d551
commit
5b72e1f568
|
@ -2982,415 +2982,6 @@ void TemplateSimplifier::printOut(const std::string & text) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token *findSemicolon(Token *tok)
|
|
||||||
{
|
|
||||||
unsigned int level = 0;
|
|
||||||
|
|
||||||
for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) {
|
|
||||||
if (tok->str() == "{")
|
|
||||||
++level;
|
|
||||||
else if (level > 0 && tok->str() == "}")
|
|
||||||
--level;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool usingMatch(
|
|
||||||
const Token *nameToken,
|
|
||||||
const std::string &scope,
|
|
||||||
Token **tok,
|
|
||||||
const std::string &scope1,
|
|
||||||
const std::list<ScopeInfo2> &scopeList1)
|
|
||||||
{
|
|
||||||
Token *tok1 = *tok;
|
|
||||||
|
|
||||||
if (tok1 && tok1->str() != nameToken->str())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// skip this using
|
|
||||||
if (tok1 == nameToken) {
|
|
||||||
*tok = findSemicolon(tok1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip other using with this name
|
|
||||||
if (tok1->strAt(-1) == "using") {
|
|
||||||
// fixme: this is wrong
|
|
||||||
// skip to end of scope
|
|
||||||
if (scopeList1.back().bodyEnd)
|
|
||||||
*tok = scopeList1.back().bodyEnd->previous();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) {
|
|
||||||
// fixme
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get qualification
|
|
||||||
std::string qualification;
|
|
||||||
const Token* tok2 = tok1;
|
|
||||||
std::string::size_type index = scope.size();
|
|
||||||
std::string::size_type new_index = std::string::npos;
|
|
||||||
bool match = true;
|
|
||||||
while (tok2->strAt(-1) == "::") {
|
|
||||||
std::string last;
|
|
||||||
if (match && !scope1.empty()) {
|
|
||||||
new_index = scope1.rfind(' ', index - 1);
|
|
||||||
if (new_index != std::string::npos)
|
|
||||||
last = scope1.substr(new_index, index - new_index);
|
|
||||||
else if (!qualification.empty())
|
|
||||||
last.clear();
|
|
||||||
else
|
|
||||||
last = scope1;
|
|
||||||
} else
|
|
||||||
match = false;
|
|
||||||
if (match && tok2->strAt(-2) == last)
|
|
||||||
index = new_index;
|
|
||||||
else {
|
|
||||||
if (!qualification.empty())
|
|
||||||
qualification = " :: " + qualification;
|
|
||||||
qualification = tok2->strAt(-2) + qualification;
|
|
||||||
}
|
|
||||||
tok2 = tok2->tokAt(-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: check using namespace
|
|
||||||
std::string fullScope1 = scope1;
|
|
||||||
if (!scope1.empty() && !qualification.empty())
|
|
||||||
fullScope1 += " :: ";
|
|
||||||
fullScope1 += qualification;
|
|
||||||
|
|
||||||
if (scope == fullScope1)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
std::string newScope1 = scope1;
|
|
||||||
|
|
||||||
// scopes didn't match so try higher scopes
|
|
||||||
while (!newScope1.empty()) {
|
|
||||||
std::string::size_type separator = newScope1.rfind(" :: ", index - 1);
|
|
||||||
if (separator != std::string::npos)
|
|
||||||
newScope1 = newScope1.substr(0, separator);
|
|
||||||
else
|
|
||||||
newScope1.clear();
|
|
||||||
|
|
||||||
std::string newFullScope1 = newScope1;
|
|
||||||
if (!newScope1.empty() && !qualification.empty())
|
|
||||||
newFullScope1 += " :: ";
|
|
||||||
newFullScope1 += qualification;
|
|
||||||
|
|
||||||
if (scope == newFullScope1)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TemplateSimplifier::simplifyUsing()
|
|
||||||
{
|
|
||||||
bool substitute = false;
|
|
||||||
std::list<ScopeInfo2> scopeList;
|
|
||||||
bool inTemplateDefinition = false;
|
|
||||||
const Token *endOfTemplateDefinition = nullptr;
|
|
||||||
bool isVariable = false;
|
|
||||||
struct Using {
|
|
||||||
Using(Token *start, Token *end) : startTok(start), endTok(end) { }
|
|
||||||
Token *startTok;
|
|
||||||
Token *endTok;
|
|
||||||
};
|
|
||||||
std::list<Using> usingList;
|
|
||||||
|
|
||||||
scopeList.emplace_back("", nullptr);
|
|
||||||
|
|
||||||
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
|
|
||||||
if (mErrorLogger && !mTokenList.getFiles().empty())
|
|
||||||
mErrorLogger->reportProgress(mTokenList.getFiles()[0], "Tokenize (using)", tok->progressValue());
|
|
||||||
|
|
||||||
if (mSettings->terminated())
|
|
||||||
return substitute;
|
|
||||||
|
|
||||||
if (Token::Match(tok, "{|}|namespace|class|struct|union") ||
|
|
||||||
Token::Match(tok, "using namespace %name% ;|::")) {
|
|
||||||
setScopeInfo(tok, &scopeList);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inTemplateDefinition) {
|
|
||||||
if (!endOfTemplateDefinition) {
|
|
||||||
if (isVariable)
|
|
||||||
endOfTemplateDefinition = findSemicolon(tok);
|
|
||||||
else if (tok->str() == "{")
|
|
||||||
endOfTemplateDefinition = tok->link();
|
|
||||||
}
|
|
||||||
if (tok == endOfTemplateDefinition) {
|
|
||||||
inTemplateDefinition = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok->str()=="template") {
|
|
||||||
if (Token::Match(tok->next(), "< !!>"))
|
|
||||||
inTemplateDefinition = true;
|
|
||||||
else
|
|
||||||
inTemplateDefinition = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ScopeInfo2> 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(";", "");
|
|
||||||
mTokenList.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(mTokenizer->mUnnamedCount++);
|
|
||||||
mTokenList.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = mTokenList.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// remove the qualification
|
|
||||||
while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) {
|
|
||||||
tok1->deletePrevious();
|
|
||||||
tok1->deletePrevious();
|
|
||||||
}
|
|
||||||
|
|
||||||
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%")) {
|
|
||||||
mTokenList.copyTokens(tok1->next(), arrayStart, usingEnd->previous());
|
|
||||||
mTokenList.copyTokens(tok1, start, arrayStart->previous());
|
|
||||||
tok1->deleteThis();
|
|
||||||
substitute = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// just replace simple type aliases
|
|
||||||
mTokenList.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, &mTokenList, 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
|
|
||||||
for (std::list<Using>::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) {
|
|
||||||
Token *usingStart = it->startTok;
|
|
||||||
Token *usingEnd = it->endTok;
|
|
||||||
if (usingStart->previous()) {
|
|
||||||
if (usingEnd->next())
|
|
||||||
Token::eraseTokens(usingStart->previous(), usingEnd->next());
|
|
||||||
else {
|
|
||||||
Token::eraseTokens(usingStart, usingEnd);
|
|
||||||
usingEnd->deleteThis();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (usingEnd->next()) {
|
|
||||||
Token::eraseTokens(usingStart, usingEnd->next());
|
|
||||||
usingStart->deleteThis();
|
|
||||||
} else {
|
|
||||||
// this is the only code being checked so leave ';'
|
|
||||||
Token::eraseTokens(usingStart, usingEnd);
|
|
||||||
usingStart->deleteThis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return substitute;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TemplateSimplifier::simplifyTemplates(
|
void TemplateSimplifier::simplifyTemplates(
|
||||||
const std::time_t maxtime,
|
const std::time_t maxtime,
|
||||||
bool &codeWithTemplates)
|
bool &codeWithTemplates)
|
||||||
|
@ -3417,11 +3008,11 @@ void TemplateSimplifier::simplifyTemplates(
|
||||||
// is fixed.
|
// is fixed.
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
if (i) {
|
||||||
// it may take more than one pass to simplify type aliases
|
// it may take more than one pass to simplify type aliases
|
||||||
while (simplifyUsing())
|
while (mTokenizer->simplifyUsing())
|
||||||
;
|
;
|
||||||
|
|
||||||
if (i) {
|
|
||||||
mTemplateDeclarations.clear();
|
mTemplateDeclarations.clear();
|
||||||
mTemplateForwardDeclarations.clear();
|
mTemplateForwardDeclarations.clear();
|
||||||
mTemplateForwardDeclarationsMap.clear();
|
mTemplateForwardDeclarationsMap.clear();
|
||||||
|
|
|
@ -396,8 +396,6 @@ private:
|
||||||
const std::string &indent = " ") const;
|
const std::string &indent = " ") const;
|
||||||
void printOut(const std::string &text = "") const;
|
void printOut(const std::string &text = "") const;
|
||||||
|
|
||||||
bool simplifyUsing();
|
|
||||||
|
|
||||||
Tokenizer *mTokenizer;
|
Tokenizer *mTokenizer;
|
||||||
TokenList &mTokenList;
|
TokenList &mTokenList;
|
||||||
const Settings *mSettings;
|
const Settings *mSettings;
|
||||||
|
|
503
lib/tokenize.cpp
503
lib/tokenize.cpp
|
@ -1666,6 +1666,505 @@ void Tokenizer::simplifyTypedef()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct ScopeInfo3 {
|
||||||
|
ScopeInfo3(const std::string &name_, const Token *bodyEnd_) : name(name_), bodyEnd(bodyEnd_) {}
|
||||||
|
const std::string name;
|
||||||
|
const Token * const bodyEnd;
|
||||||
|
std::set<std::string> usingNamespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string getScopeName(const std::list<ScopeInfo3> &scopeInfo)
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
for (const ScopeInfo3 &i : scopeInfo) {
|
||||||
|
if (!i.name.empty())
|
||||||
|
ret += (ret.empty() ? "" : " :: ") + i.name;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScopeInfo(Token *tok, std::list<ScopeInfo3> *scopeInfo, bool all = false)
|
||||||
|
{
|
||||||
|
while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd)
|
||||||
|
scopeInfo->pop_back();
|
||||||
|
if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::")) {
|
||||||
|
// check for using namespace
|
||||||
|
if (Token::Match(tok, "using namespace %name% ;|::")) {
|
||||||
|
const Token * tok1 = tok->tokAt(2);
|
||||||
|
std::string nameSpace;
|
||||||
|
while (tok1 && tok1->str() != ";") {
|
||||||
|
if (!nameSpace.empty())
|
||||||
|
nameSpace += " ";
|
||||||
|
nameSpace += tok1->str();
|
||||||
|
tok1 = tok1->next();
|
||||||
|
}
|
||||||
|
scopeInfo->back().usingNamespaces.insert(nameSpace);
|
||||||
|
}
|
||||||
|
// check for member function
|
||||||
|
else if (tok->str() == "{") {
|
||||||
|
bool added = false;
|
||||||
|
Token *tok1 = tok;
|
||||||
|
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
|
||||||
|
tok1 = tok1->previous();
|
||||||
|
if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") {
|
||||||
|
tok1 = tok1->linkAt(-1);
|
||||||
|
if (Token::Match(tok1->previous(), "throw|noexcept")) {
|
||||||
|
tok1 = tok1->previous();
|
||||||
|
while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept"))
|
||||||
|
tok1 = tok1->previous();
|
||||||
|
if (tok1->strAt(-1) != ")")
|
||||||
|
return;
|
||||||
|
} else if (Token::Match(tok->tokAt(-2), ":|, %name%")) {
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
if (tok1->strAt(-1) != ")")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tok1->strAt(-1) == ">")
|
||||||
|
tok1 = tok1->previous()->findOpeningBracket();
|
||||||
|
if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) {
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
std::string scope = tok1->strAt(-1);
|
||||||
|
while (Token::Match(tok1->tokAt(-2), ":: %name%")) {
|
||||||
|
scope = tok1->strAt(-3) + " :: " + scope;
|
||||||
|
tok1 = tok1->tokAt(-2);
|
||||||
|
}
|
||||||
|
scopeInfo->emplace_back(scope, tok->link());
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all && !added)
|
||||||
|
scopeInfo->emplace_back("", tok->link());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = tok->next();
|
||||||
|
std::string classname = tok->str();
|
||||||
|
while (Token::Match(tok, "%name% :: %name%")) {
|
||||||
|
tok = tok->tokAt(2);
|
||||||
|
classname += " :: " + tok->str();
|
||||||
|
}
|
||||||
|
tok = tok->next();
|
||||||
|
if (tok && tok->str() == ":") {
|
||||||
|
while (tok && !Token::Match(tok, ";|{"))
|
||||||
|
tok = tok->next();
|
||||||
|
}
|
||||||
|
if (tok && tok->str() == "{") {
|
||||||
|
scopeInfo->emplace_back(classname,tok->link());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token *findSemicolon(Token *tok)
|
||||||
|
{
|
||||||
|
unsigned int level = 0;
|
||||||
|
|
||||||
|
for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) {
|
||||||
|
if (tok->str() == "{")
|
||||||
|
++level;
|
||||||
|
else if (level > 0 && tok->str() == "}")
|
||||||
|
--level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usingMatch(
|
||||||
|
const Token *nameToken,
|
||||||
|
const std::string &scope,
|
||||||
|
Token **tok,
|
||||||
|
const std::string &scope1,
|
||||||
|
const std::list<ScopeInfo3> &scopeList1)
|
||||||
|
{
|
||||||
|
Token *tok1 = *tok;
|
||||||
|
|
||||||
|
if (tok1 && tok1->str() != nameToken->str())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// skip this using
|
||||||
|
if (tok1 == nameToken) {
|
||||||
|
*tok = findSemicolon(tok1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip other using with this name
|
||||||
|
if (tok1->strAt(-1) == "using") {
|
||||||
|
// fixme: this is wrong
|
||||||
|
// skip to end of scope
|
||||||
|
if (scopeList1.back().bodyEnd)
|
||||||
|
*tok = scopeList1.back().bodyEnd->previous();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Token::Match(tok1->tokAt(-1), "struct|union|enum")) {
|
||||||
|
// fixme
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get qualification
|
||||||
|
std::string qualification;
|
||||||
|
const Token* tok2 = tok1;
|
||||||
|
std::string::size_type index = scope.size();
|
||||||
|
std::string::size_type new_index = std::string::npos;
|
||||||
|
bool match = true;
|
||||||
|
while (tok2->strAt(-1) == "::") {
|
||||||
|
std::string last;
|
||||||
|
if (match && !scope1.empty()) {
|
||||||
|
new_index = scope1.rfind(' ', index - 1);
|
||||||
|
if (new_index != std::string::npos)
|
||||||
|
last = scope1.substr(new_index, index - new_index);
|
||||||
|
else if (!qualification.empty())
|
||||||
|
last.clear();
|
||||||
|
else
|
||||||
|
last = scope1;
|
||||||
|
} else
|
||||||
|
match = false;
|
||||||
|
if (match && tok2->strAt(-2) == last)
|
||||||
|
index = new_index;
|
||||||
|
else {
|
||||||
|
if (!qualification.empty())
|
||||||
|
qualification = " :: " + qualification;
|
||||||
|
qualification = tok2->strAt(-2) + qualification;
|
||||||
|
}
|
||||||
|
tok2 = tok2->tokAt(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check using namespace
|
||||||
|
std::string fullScope1 = scope1;
|
||||||
|
if (!scope1.empty() && !qualification.empty())
|
||||||
|
fullScope1 += " :: ";
|
||||||
|
fullScope1 += qualification;
|
||||||
|
|
||||||
|
if (scope == fullScope1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::string newScope1 = scope1;
|
||||||
|
|
||||||
|
// scopes didn't match so try higher scopes
|
||||||
|
while (!newScope1.empty()) {
|
||||||
|
std::string::size_type separator = newScope1.rfind(" :: ", index - 1);
|
||||||
|
if (separator != std::string::npos)
|
||||||
|
newScope1 = newScope1.substr(0, separator);
|
||||||
|
else
|
||||||
|
newScope1.clear();
|
||||||
|
|
||||||
|
std::string newFullScope1 = newScope1;
|
||||||
|
if (!newScope1.empty() && !qualification.empty())
|
||||||
|
newFullScope1 += " :: ";
|
||||||
|
newFullScope1 += qualification;
|
||||||
|
|
||||||
|
if (scope == newFullScope1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool Tokenizer::simplifyUsing()
|
||||||
|
{
|
||||||
|
bool substitute = false;
|
||||||
|
std::list<ScopeInfo3> scopeList;
|
||||||
|
bool inTemplateDefinition = false;
|
||||||
|
const Token *endOfTemplateDefinition = nullptr;
|
||||||
|
bool isVariable = false;
|
||||||
|
struct Using {
|
||||||
|
Using(Token *start, Token *end) : startTok(start), endTok(end) { }
|
||||||
|
Token *startTok;
|
||||||
|
Token *endTok;
|
||||||
|
};
|
||||||
|
std::list<Using> usingList;
|
||||||
|
|
||||||
|
scopeList.emplace_back("", nullptr);
|
||||||
|
|
||||||
|
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||||
|
if (mErrorLogger && !list.getFiles().empty())
|
||||||
|
mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue());
|
||||||
|
|
||||||
|
if (mSettings->terminated())
|
||||||
|
return substitute;
|
||||||
|
|
||||||
|
if (Token::Match(tok, "{|}|namespace|class|struct|union") ||
|
||||||
|
Token::Match(tok, "using namespace %name% ;|::")) {
|
||||||
|
setScopeInfo(tok, &scopeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inTemplateDefinition) {
|
||||||
|
if (!endOfTemplateDefinition) {
|
||||||
|
if (isVariable)
|
||||||
|
endOfTemplateDefinition = findSemicolon(tok);
|
||||||
|
else if (tok->str() == "{")
|
||||||
|
endOfTemplateDefinition = tok->link();
|
||||||
|
}
|
||||||
|
if (tok == endOfTemplateDefinition) {
|
||||||
|
inTemplateDefinition = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok->str()=="template") {
|
||||||
|
if (Token::Match(tok->next(), "< !!>"))
|
||||||
|
inTemplateDefinition = true;
|
||||||
|
else
|
||||||
|
inTemplateDefinition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// remove the qualification
|
||||||
|
while (tok1->strAt(-1) == "::" && tok1->strAt(-2) == scope) {
|
||||||
|
tok1->deletePrevious();
|
||||||
|
tok1->deletePrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
for (std::list<Using>::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) {
|
||||||
|
Token *usingStart = it->startTok;
|
||||||
|
Token *usingEnd = it->endTok;
|
||||||
|
if (usingStart->previous()) {
|
||||||
|
if (usingEnd->next())
|
||||||
|
Token::eraseTokens(usingStart->previous(), usingEnd->next());
|
||||||
|
else {
|
||||||
|
Token::eraseTokens(usingStart, usingEnd);
|
||||||
|
usingEnd->deleteThis();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (usingEnd->next()) {
|
||||||
|
Token::eraseTokens(usingStart, usingEnd->next());
|
||||||
|
usingStart->deleteThis();
|
||||||
|
} else {
|
||||||
|
// this is the only code being checked so leave ';'
|
||||||
|
Token::eraseTokens(usingStart, usingEnd);
|
||||||
|
usingStart->deleteThis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return substitute;
|
||||||
|
}
|
||||||
void Tokenizer::simplifyMulAndParens()
|
void Tokenizer::simplifyMulAndParens()
|
||||||
{
|
{
|
||||||
if (!list.front())
|
if (!list.front())
|
||||||
|
@ -3813,6 +4312,10 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
|
||||||
simplifyTypedef();
|
simplifyTypedef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// using A = B;
|
||||||
|
while (simplifyUsing())
|
||||||
|
;
|
||||||
|
|
||||||
// Add parentheses to ternary operator where necessary
|
// Add parentheses to ternary operator where necessary
|
||||||
// TODO: this is only necessary if one typedef simplification had a comma and was used within ?:
|
// TODO: this is only necessary if one typedef simplification had a comma and was used within ?:
|
||||||
// If typedef handling is refactored and moved to symboldatabase someday we can remove this
|
// If typedef handling is refactored and moved to symboldatabase someday we can remove this
|
||||||
|
|
|
@ -376,6 +376,10 @@ public:
|
||||||
*/
|
*/
|
||||||
void simplifyTypedef();
|
void simplifyTypedef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
bool simplifyUsing();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplify casts
|
* Simplify casts
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -5004,6 +5004,16 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'value' is less than zero.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'value' is less than zero.\n", errout.str());
|
||||||
|
|
||||||
|
// #9040
|
||||||
|
Settings settings1;
|
||||||
|
settings1.platform(Settings::Win64);
|
||||||
|
check("using BOOL = unsigned;\n"
|
||||||
|
"int i;\n"
|
||||||
|
"bool f() {\n"
|
||||||
|
" return i >= 0;\n"
|
||||||
|
"}\n", &settings1);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkSignOfPointer() {
|
void checkSignOfPointer() {
|
||||||
|
|
|
@ -62,6 +62,7 @@ private:
|
||||||
TEST_CASE(simplifyUsing8970);
|
TEST_CASE(simplifyUsing8970);
|
||||||
TEST_CASE(simplifyUsing8971);
|
TEST_CASE(simplifyUsing8971);
|
||||||
TEST_CASE(simplifyUsing8976);
|
TEST_CASE(simplifyUsing8976);
|
||||||
|
TEST_CASE(simplifyUsing9040);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) {
|
std::string tok(const char code[], bool simplify = true, Settings::PlatformType type = Settings::Native, bool debugwarnings = true) {
|
||||||
|
@ -461,6 +462,18 @@ private:
|
||||||
ASSERT_EQUALS(exp, tok(code));
|
ASSERT_EQUALS(exp, tok(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void simplifyUsing9040() {
|
||||||
|
const char code[] = "using BOOL = unsigned; int i;";
|
||||||
|
|
||||||
|
const char exp[] = "int i ;";
|
||||||
|
|
||||||
|
ASSERT_EQUALS(exp, tok(code, true, Settings::Unix32));
|
||||||
|
ASSERT_EQUALS(exp, tok(code, true, Settings::Unix64));
|
||||||
|
ASSERT_EQUALS(exp, tok(code, true, Settings::Win32A));
|
||||||
|
ASSERT_EQUALS(exp, tok(code, true, Settings::Win32W));
|
||||||
|
ASSERT_EQUALS(exp, tok(code, true, Settings::Win64));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestSimplifyUsing)
|
REGISTER_TEST(TestSimplifyUsing)
|
||||||
|
|
Loading…
Reference in New Issue