Tokenizer: replace ') const| {' pattern to detect function start
This commit is contained in:
parent
cd6332d960
commit
ca9386859b
|
@ -20,6 +20,7 @@
|
|||
#include "mathlib.h"
|
||||
#include "token.h"
|
||||
#include "tokenlist.h"
|
||||
#include "tokenize.h"
|
||||
#include "errorlogger.h"
|
||||
#include "settings.h"
|
||||
#include <algorithm>
|
||||
|
@ -126,10 +127,9 @@ const Token* TemplateSimplifier::hasComplicatedSyntaxErrorsInTemplates(Token *to
|
|||
tok = tok->link();
|
||||
|
||||
// skip executing scopes..
|
||||
if (Token::simpleMatch(tok, ") {") || Token::Match(tok, ") %var% {") || Token::Match(tok, "[;,=] {")) {
|
||||
while (tok->str() != "{")
|
||||
tok = tok->next();
|
||||
tok = tok->link();
|
||||
const Token *start = Tokenizer::startOfExecutableScope(tok);
|
||||
if (start) {
|
||||
tok = start->link();
|
||||
}
|
||||
|
||||
// skip executing scopes (ticket #1985)..
|
||||
|
@ -342,7 +342,8 @@ bool TemplateSimplifier::removeTemplate(Token *tok)
|
|||
|
||||
// don't remove constructor
|
||||
if (tok2->str() == "explicit" ||
|
||||
(countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && Token::simpleMatch(tok2->next()->link(), ") {"))) {
|
||||
(countgt == 1 && Token::Match(tok2->previous(), "> %type% (") &&
|
||||
Tokenizer::startOfExecutableScope(const_cast<const Token *>(tok2->next()->link())))) {
|
||||
Token::eraseTokens(tok, tok2);
|
||||
tok->deleteThis();
|
||||
return true;
|
||||
|
|
112
lib/tokenize.cpp
112
lib/tokenize.cpp
|
@ -2086,6 +2086,82 @@ static Token *skipTernaryOp(Token *tok)
|
|||
return tok;
|
||||
}
|
||||
|
||||
Token * Tokenizer::startOfFunction(Token * tok)
|
||||
{
|
||||
if (tok && tok->str() == ")") {
|
||||
tok = tok->next();
|
||||
while (tok && tok->str() != "{") {
|
||||
if (tok->str() == "const" || tok->str() == "volatile") {
|
||||
tok = tok->next();
|
||||
} else if (tok->str() == "noexcept") {
|
||||
tok = tok->next();
|
||||
if (tok && tok->str() == "(") {
|
||||
tok = tok->link()->next();
|
||||
}
|
||||
} else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") {
|
||||
tok = tok->next()->link()->next();
|
||||
}
|
||||
// unknown macros ") MACRO {" and ") MACRO(...) {"
|
||||
else if (tok->isUpperCaseName()) {
|
||||
tok = tok->next();
|
||||
if (tok && tok->str() == "(") {
|
||||
tok = tok->link()->next();
|
||||
}
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Token * Tokenizer::startOfExecutableScope(const Token * tok)
|
||||
{
|
||||
if (tok && tok->str() == ")") {
|
||||
tok = tok->next();
|
||||
bool inInit = false;
|
||||
while (tok && tok->str() != "{") {
|
||||
if (!inInit) {
|
||||
if (tok->str() == "const" || tok->str() == "volatile") {
|
||||
tok = tok->next();
|
||||
} else if (tok->str() == "noexcept") {
|
||||
tok = tok->next();
|
||||
if (tok && tok->str() == "(") {
|
||||
tok = tok->link()->next();
|
||||
}
|
||||
} else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") {
|
||||
tok = tok->next()->link()->next();
|
||||
} else if (tok->str() == ":") {
|
||||
inInit = true;
|
||||
tok = tok->next();
|
||||
}
|
||||
// unknown macros ") MACRO {" and ") MACRO(...) {"
|
||||
else if (tok->isUpperCaseName()) {
|
||||
tok = tok->next();
|
||||
if (tok && tok->str() == "(") {
|
||||
tok = tok->link()->next();
|
||||
}
|
||||
} else
|
||||
return nullptr;
|
||||
} else {
|
||||
if (tok->isName() && tok->next() && tok->next()->str() == "(") {
|
||||
tok = tok->next()->link()->next();
|
||||
} else if (tok->str() == ",") {
|
||||
tok = tok->next();
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/** simplify labels and case|default in the code: add a ";" if not already in.*/
|
||||
|
||||
void Tokenizer::simplifyLabelsCaseDefault()
|
||||
|
@ -2094,10 +2170,9 @@ void Tokenizer::simplifyLabelsCaseDefault()
|
|||
unsigned int indentlevel = 0;
|
||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||
// Simplify labels in the executable scope..
|
||||
if (Token::Match(tok, ") const| {")) {
|
||||
tok = tok->next();
|
||||
if (tok->str() != "{")
|
||||
tok = tok->next();
|
||||
Token *start = startOfExecutableScope(tok);
|
||||
if (start) {
|
||||
tok = start;
|
||||
executablescope = true;
|
||||
}
|
||||
|
||||
|
@ -2607,10 +2682,9 @@ void Tokenizer::setVarId()
|
|||
break;
|
||||
|
||||
// If this is a function implementation.. add it to funclist
|
||||
if (Token::Match(tok2, ") const|volatile| {")) {
|
||||
while (tok2->str() != "{")
|
||||
tok2 = tok2->next();
|
||||
setVarIdClassFunction(classname, tok2, tok2->link(), varlist, &structMembers, &_varId);
|
||||
Token * start = startOfFunction(tok2);
|
||||
if (start) {
|
||||
setVarIdClassFunction(classname, start, start->link(), varlist, &structMembers, &_varId);
|
||||
}
|
||||
|
||||
// constructor with initializer list
|
||||
|
@ -3764,11 +3838,11 @@ void Tokenizer::removeRedundantAssignment()
|
|||
if (tok->str() == "{")
|
||||
tok = tok->link();
|
||||
|
||||
if (Token::Match(tok, ") const| {")) {
|
||||
Token * start = startOfExecutableScope(tok);
|
||||
if (start) {
|
||||
tok = start->previous();
|
||||
// parse in this function..
|
||||
std::set<unsigned int> localvars;
|
||||
if (tok->next()->str() == "const")
|
||||
tok = tok->next();
|
||||
const Token * const end = tok->next()->link();
|
||||
for (Token *tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) {
|
||||
// skip local class or struct
|
||||
|
@ -6040,9 +6114,9 @@ bool Tokenizer::simplifyCAlternativeTokens()
|
|||
{
|
||||
bool ret = false;
|
||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||
if (Token::Match(tok, ") const| {")) { // Check for executable scope
|
||||
while (tok->str() != "{")
|
||||
tok = tok->next();
|
||||
Token *start = startOfExecutableScope(tok);
|
||||
if (start) { // Check for executable scope
|
||||
tok = start;
|
||||
Token * const end = tok->link();
|
||||
for (Token *tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) {
|
||||
if (Token::Match(tok2, "%var%|%num%|)|] %any% %var%|%num%|(")) {
|
||||
|
@ -6244,9 +6318,11 @@ bool Tokenizer::simplifyKnownVariables()
|
|||
// auto variables..
|
||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||
// Search for a block of code
|
||||
if (! Token::Match(tok, ") const| {"))
|
||||
Token *start = startOfExecutableScope(tok);
|
||||
if (!start)
|
||||
continue;
|
||||
|
||||
tok = start;
|
||||
// parse the block of code..
|
||||
int indentlevel = 0;
|
||||
Token *tok2 = tok;
|
||||
|
@ -7061,10 +7137,10 @@ void Tokenizer::simplifyReference()
|
|||
|
||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||
// starting executable scope..
|
||||
if (Token::Match(tok, ") const| {")) {
|
||||
Token *start = startOfExecutableScope(tok);
|
||||
if (start) {
|
||||
tok = start;
|
||||
// replace references in this scope..
|
||||
if (tok->next()->str() != "{")
|
||||
tok = tok->next();
|
||||
Token * const end = tok->next()->link();
|
||||
for (Token *tok2 = tok; tok2 && tok2 != end; tok2 = tok2->next()) {
|
||||
// found a reference..
|
||||
|
|
|
@ -776,6 +776,14 @@ public:
|
|||
*/
|
||||
static bool isTwoNumber(const std::string &s);
|
||||
|
||||
/**
|
||||
* Helper function to check for start of function execution scope.
|
||||
* Do not use this in checks. Use the symbol database.
|
||||
* @param tok --> pointer to end parentheses of parameter list
|
||||
* @return pointer to start brace of function scope or nullptr if not start.
|
||||
*/
|
||||
static const Token * startOfExecutableScope(const Token * tok);
|
||||
|
||||
private:
|
||||
/** Disable copy constructor, no implementation */
|
||||
Tokenizer(const Tokenizer &);
|
||||
|
@ -783,6 +791,11 @@ private:
|
|||
/** Disable assignment operator, no implementation */
|
||||
Tokenizer &operator=(const Tokenizer &);
|
||||
|
||||
static Token * startOfFunction(Token * tok);
|
||||
static Token * startOfExecutableScope(Token * tok) {
|
||||
return const_cast<Token*>(startOfExecutableScope(const_cast<const Token *>(tok)));
|
||||
}
|
||||
|
||||
/** settings */
|
||||
const Settings * _settings;
|
||||
|
||||
|
|
|
@ -575,6 +575,8 @@ private:
|
|||
TEST_CASE(astunaryop);
|
||||
TEST_CASE(astfunction);
|
||||
TEST_CASE(asttemplate);
|
||||
|
||||
TEST_CASE(startOfExecutableScope);
|
||||
}
|
||||
|
||||
std::string tokenizeAndStringify(const char code[], bool simplify = false, bool expand = true, Settings::PlatformType platform = Settings::Unspecified, const char* filename = "test.cpp", bool cpp11 = true) {
|
||||
|
@ -739,11 +741,11 @@ private:
|
|||
|
||||
// ticket #2118 - invalid syntax error
|
||||
void tokenize12() {
|
||||
tokenizeAndStringify("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n"
|
||||
" for (unsigned i = 0 ; i < count; i++) {\n"
|
||||
" }\n"
|
||||
"});");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
const char code[] = "Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n"
|
||||
" for (unsigned i = 0 ; i < count; i++) {\n"
|
||||
" }\n"
|
||||
"});";
|
||||
ASSERT_THROW(tokenizeAndStringify(code), InternalError);
|
||||
}
|
||||
|
||||
// bailout if there is "@" - it is not handled well
|
||||
|
@ -10463,6 +10465,35 @@ private:
|
|||
tokenizeAndStringify(code.c_str()); // just survive...
|
||||
}
|
||||
|
||||
bool isStartOfExecutableScope(int offset, const char code[]) {
|
||||
const Settings settings;
|
||||
Tokenizer tokenizer(&settings, this);
|
||||
std::istringstream istr(code);
|
||||
tokenizer.tokenize(istr, "test.cpp");
|
||||
|
||||
return Tokenizer::startOfExecutableScope(tokenizer.tokens()->tokAt(offset)) != nullptr;
|
||||
}
|
||||
|
||||
void startOfExecutableScope() {
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() const { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() volatile { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() noexcept { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() NOEXCEPT { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() CONST NOEXCEPT { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() noexcept(true) { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() const noexcept(true) { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() throw() { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() THROW() { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() CONST THROW() { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() const throw() { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() throw(int) { }"));
|
||||
ASSERT(isStartOfExecutableScope(3, "void foo() const throw(int) { }"));
|
||||
ASSERT(isStartOfExecutableScope(2, "foo() : a(1) { }"));
|
||||
ASSERT(isStartOfExecutableScope(2, "foo() : a(1), b(2) { }"));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestTokenizer)
|
||||
|
|
Loading…
Reference in New Issue