Tokenizer: replace ') const| {' pattern to detect function start

This commit is contained in:
Robert Reif 2014-04-26 18:31:56 +02:00 committed by Daniel Marjamäki
parent cd6332d960
commit ca9386859b
4 changed files with 149 additions and 28 deletions

View File

@ -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;

View File

@ -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..

View File

@ -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;

View File

@ -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)