Fixed #5638 (is there any plan to check noexcept correctness?)
This commit is contained in:
parent
4ae204e46b
commit
847d28d283
|
@ -176,3 +176,48 @@ void CheckExceptionSafety::checkCatchExceptionByValue()
|
||||||
catchExceptionByValueError(i->classDef);
|
catchExceptionByValueError(i->classDef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// void func() noexcept { throw x; }
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
void CheckExceptionSafety::noexceptThrows()
|
||||||
|
{
|
||||||
|
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
|
// onlycheck noexcept functions
|
||||||
|
if (scope->function && scope->function->isNoExcept &&
|
||||||
|
(!scope->function->noexceptArg || scope->function->noexceptArg->str() == "true")) {
|
||||||
|
for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
if (tok->str() != "throw") {
|
||||||
|
noexceptThrowError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// void func() throw() { throw x; }
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
void CheckExceptionSafety::nothrowThrows()
|
||||||
|
{
|
||||||
|
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
|
|
||||||
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
|
// onlycheck throw() functions
|
||||||
|
if (scope->function && scope->function->isThrow && !scope->function->throwArg) {
|
||||||
|
for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
||||||
|
if (tok->str() != "throw") {
|
||||||
|
nothrowThrowError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ public:
|
||||||
checkExceptionSafety.deallocThrow();
|
checkExceptionSafety.deallocThrow();
|
||||||
checkExceptionSafety.checkRethrowCopy();
|
checkExceptionSafety.checkRethrowCopy();
|
||||||
checkExceptionSafety.checkCatchExceptionByValue();
|
checkExceptionSafety.checkCatchExceptionByValue();
|
||||||
|
checkExceptionSafety.noexceptThrows();
|
||||||
|
checkExceptionSafety.nothrowThrows();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Don't throw exceptions in destructors */
|
/** Don't throw exceptions in destructors */
|
||||||
|
@ -75,6 +77,12 @@ public:
|
||||||
/** @brief %Check for exceptions that are caught by value instead of by reference */
|
/** @brief %Check for exceptions that are caught by value instead of by reference */
|
||||||
void checkCatchExceptionByValue();
|
void checkCatchExceptionByValue();
|
||||||
|
|
||||||
|
/** @brief %Check for noexcept functions that throw */
|
||||||
|
void noexceptThrows();
|
||||||
|
|
||||||
|
/** @brief %Check for throw() functions that throw */
|
||||||
|
void nothrowThrows();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Don't throw exceptions in destructors */
|
/** Don't throw exceptions in destructors */
|
||||||
void destructorsError(const Token * const tok) {
|
void destructorsError(const Token * const tok) {
|
||||||
|
@ -100,6 +108,16 @@ private:
|
||||||
"as a (const) reference which is usually recommended in C++.");
|
"as a (const) reference which is usually recommended in C++.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Don't throw exceptions in noexcept functions */
|
||||||
|
void noexceptThrowError(const Token * const tok) {
|
||||||
|
reportError(tok, Severity::error, "exceptThrowInNoexecptFunction", "Exception thrown in noexcept function.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Don't throw exceptions in throw() functions */
|
||||||
|
void nothrowThrowError(const Token * const tok) {
|
||||||
|
reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function.");
|
||||||
|
}
|
||||||
|
|
||||||
/** Generate all possible errors (for --errorlist) */
|
/** Generate all possible errors (for --errorlist) */
|
||||||
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
CheckExceptionSafety c(0, settings, errorLogger);
|
CheckExceptionSafety c(0, settings, errorLogger);
|
||||||
|
@ -107,6 +125,8 @@ private:
|
||||||
c.deallocThrowError(0, "p");
|
c.deallocThrowError(0, "p");
|
||||||
c.rethrowCopyError(0, "varname");
|
c.rethrowCopyError(0, "varname");
|
||||||
c.catchExceptionByValueError(0);
|
c.catchExceptionByValueError(0);
|
||||||
|
c.noexceptThrowError(0);
|
||||||
|
c.nothrowThrowError(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Short description of class (for --doc) */
|
/** Short description of class (for --doc) */
|
||||||
|
@ -120,7 +140,9 @@ private:
|
||||||
"* Throwing exceptions in destructors\n"
|
"* Throwing exceptions in destructors\n"
|
||||||
"* Throwing exception during invalid state\n"
|
"* Throwing exception during invalid state\n"
|
||||||
"* Throwing a copy of a caught exception instead of rethrowing the original exception\n"
|
"* Throwing a copy of a caught exception instead of rethrowing the original exception\n"
|
||||||
"* Exception caught by value instead of by reference\n";
|
"* Exception caught by value instead of by reference\n"
|
||||||
|
"* Throwing exception in noexcept function\n"
|
||||||
|
"* Throwing exception in nothrow() function\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -458,6 +458,72 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
scope->functionList.push_back(function);
|
scope->functionList.push_back(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// noexcept;
|
||||||
|
// const noexcept;
|
||||||
|
else if (Token::Match(end, ") const| noexcept ;")) {
|
||||||
|
function.isNoExcept = true;
|
||||||
|
|
||||||
|
if (end->next()->str() == "const")
|
||||||
|
tok = end->tokAt(3);
|
||||||
|
else
|
||||||
|
tok = end->tokAt(2);
|
||||||
|
|
||||||
|
scope->functionList.push_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
// noexcept const;
|
||||||
|
else if (Token::simpleMatch(end, ") noexcept const ;")) {
|
||||||
|
function.isNoExcept = true;
|
||||||
|
|
||||||
|
tok = end->tokAt(3);
|
||||||
|
|
||||||
|
scope->functionList.push_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
// noexcept(...);
|
||||||
|
// noexcept(...) const;
|
||||||
|
else if (Token::simpleMatch(end, ") noexcept (") &&
|
||||||
|
Token::Match(end->linkAt(2), ") const| ;")) {
|
||||||
|
function.isNoExcept = true;
|
||||||
|
|
||||||
|
if (end->linkAt(2)->strAt(1) == "const")
|
||||||
|
tok = end->linkAt(2)->tokAt(2);
|
||||||
|
else
|
||||||
|
tok = end->linkAt(2)->next();
|
||||||
|
|
||||||
|
scope->functionList.push_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const noexcept(...);
|
||||||
|
else if (Token::simpleMatch(end, ") const noexcept (") &&
|
||||||
|
Token::simpleMatch(end->linkAt(3), ") ;")) {
|
||||||
|
function.isNoExcept = true;
|
||||||
|
|
||||||
|
tok = end->linkAt(3)->next();
|
||||||
|
|
||||||
|
scope->functionList.push_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw()
|
||||||
|
// const throw()
|
||||||
|
else if (Token::Match(end, ") const| throw (") &&
|
||||||
|
(end->next()->str() == "const" ? Token::Match(end->linkAt(3), ") ;") :
|
||||||
|
Token::Match(end->linkAt(2), ") ;"))) {
|
||||||
|
function.isThrow = true;
|
||||||
|
|
||||||
|
if (end->next()->str() == "const") {
|
||||||
|
if (end->strAt(4) != ")")
|
||||||
|
function.throwArg = end->tokAt(4);
|
||||||
|
tok = end->linkAt(3)->next();
|
||||||
|
} else {
|
||||||
|
if (end->strAt(3) != ")")
|
||||||
|
function.throwArg = end->tokAt(3);
|
||||||
|
tok = end->linkAt(2)->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope->functionList.push_back(function);
|
||||||
|
}
|
||||||
|
|
||||||
// pure virtual function
|
// pure virtual function
|
||||||
else if (Token::Match(end, ") const| = %any% ;")) {
|
else if (Token::Match(end, ") const| = %any% ;")) {
|
||||||
function.isPure = true;
|
function.isPure = true;
|
||||||
|
@ -481,6 +547,28 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
function.isInline = true;
|
function.isInline = true;
|
||||||
function.hasBody = true;
|
function.hasBody = true;
|
||||||
|
|
||||||
|
if (Token::Match(end, ") const| noexcept")) {
|
||||||
|
int arg = 2;
|
||||||
|
|
||||||
|
if (end->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (end->strAt(arg) == "(")
|
||||||
|
function.noexceptArg = end->tokAt(arg + 1);
|
||||||
|
|
||||||
|
function.isNoExcept = true;
|
||||||
|
} else if (Token::Match(end, ") const| throw (")) {
|
||||||
|
int arg = 3;
|
||||||
|
|
||||||
|
if (end->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (end->strAt(arg) != ")")
|
||||||
|
function.throwArg = end->tokAt(arg);
|
||||||
|
|
||||||
|
function.isThrow = true;
|
||||||
|
}
|
||||||
|
|
||||||
// find start of function '{'
|
// find start of function '{'
|
||||||
while (end && end->str() != "{" && end->str() != ";")
|
while (end && end->str() != "{" && end->str() != ";")
|
||||||
end = end->next();
|
end = end->next();
|
||||||
|
@ -549,11 +637,13 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
if (isFunction(tok, scope, &funcStart, &argStart)) {
|
if (isFunction(tok, scope, &funcStart, &argStart)) {
|
||||||
bool retFuncPtr = Token::simpleMatch(argStart->link(), ") ) (");
|
bool retFuncPtr = Token::simpleMatch(argStart->link(), ") ) (");
|
||||||
const Token* scopeBegin = argStart->link()->next();
|
const Token* scopeBegin = argStart->link()->next();
|
||||||
|
|
||||||
if (retFuncPtr)
|
if (retFuncPtr)
|
||||||
scopeBegin = scopeBegin->next()->link()->next();
|
scopeBegin = scopeBegin->next()->link()->next();
|
||||||
if (scopeBegin->isName()) { // Jump behind 'const' or unknown Macro
|
if (scopeBegin->isName()) { // Jump behind 'const' or unknown Macro
|
||||||
scopeBegin = scopeBegin->next();
|
scopeBegin = scopeBegin->next();
|
||||||
|
if (scopeBegin->str() == "throw")
|
||||||
|
scopeBegin = scopeBegin->next();
|
||||||
|
|
||||||
if (scopeBegin->link() && scopeBegin->str() == "(") // Jump behind unknown macro of type THROW(...)
|
if (scopeBegin->link() && scopeBegin->str() == "(") // Jump behind unknown macro of type THROW(...)
|
||||||
scopeBegin = scopeBegin->link()->next();
|
scopeBegin = scopeBegin->link()->next();
|
||||||
}
|
}
|
||||||
|
@ -576,6 +666,29 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
else {
|
else {
|
||||||
Function* function = addGlobalFunction(scope, tok, argStart, funcStart);
|
Function* function = addGlobalFunction(scope, tok, argStart, funcStart);
|
||||||
function->retFuncPtr = retFuncPtr;
|
function->retFuncPtr = retFuncPtr;
|
||||||
|
|
||||||
|
// global functions can't be const but we have tests that are
|
||||||
|
if (Token::Match(argStart->link(), ") const| noexcept")) {
|
||||||
|
int arg = 2;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(arg) == "(")
|
||||||
|
function->noexceptArg = argStart->link()->tokAt(arg + 1);
|
||||||
|
|
||||||
|
function->isNoExcept = true;
|
||||||
|
} else if (Token::Match(argStart->link(), ") const| throw (")) {
|
||||||
|
int arg = 3;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(arg) != ")")
|
||||||
|
function->throwArg = argStart->link()->tokAt(arg);
|
||||||
|
|
||||||
|
function->isThrow = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// syntax error?
|
// syntax error?
|
||||||
|
@ -598,6 +711,28 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
if (newFunc) {
|
if (newFunc) {
|
||||||
Function* func = addGlobalFunctionDecl(scope, tok, argStart, funcStart);
|
Function* func = addGlobalFunctionDecl(scope, tok, argStart, funcStart);
|
||||||
func->retFuncPtr = retFuncPtr;
|
func->retFuncPtr = retFuncPtr;
|
||||||
|
|
||||||
|
if (Token::Match(argStart->link(), ") const| noexcept")) {
|
||||||
|
int arg = 2;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(arg) == "(")
|
||||||
|
func->noexceptArg = argStart->link()->tokAt(arg + 1);
|
||||||
|
|
||||||
|
func->isNoExcept = true;
|
||||||
|
} else if (Token::Match(argStart->link(), ") const| throw (")) {
|
||||||
|
int arg = 3;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(1) == "const")
|
||||||
|
arg++;
|
||||||
|
|
||||||
|
if (argStart->link()->strAt(arg) != ")")
|
||||||
|
func->throwArg = argStart->link()->tokAt(arg);
|
||||||
|
|
||||||
|
func->isThrow = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tok = scopeBegin;
|
tok = scopeBegin;
|
||||||
|
@ -965,11 +1100,16 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const
|
||||||
tok->strAt(-1) == "::" || tok->strAt(-1) == "~" || // or a scope qualifier in front of tok
|
tok->strAt(-1) == "::" || tok->strAt(-1) == "~" || // or a scope qualifier in front of tok
|
||||||
outerScope->isClassOrStruct())) { // or a ctor/dtor
|
outerScope->isClassOrStruct())) { // or a ctor/dtor
|
||||||
const Token* tok2 = tok->next()->link()->next();
|
const Token* tok2 = tok->next()->link()->next();
|
||||||
if ((Token::Match(tok2, "const| ;|{|=") ||
|
if (tok2 &&
|
||||||
|
(Token::Match(tok2, "const| ;|{|=") ||
|
||||||
(Token::Match(tok2, "%var% ;|{") && tok2->isUpperCaseName()) ||
|
(Token::Match(tok2, "%var% ;|{") && tok2->isUpperCaseName()) ||
|
||||||
(Token::Match(tok2, "%var% (") && tok2->isUpperCaseName() && tok2->next()->link()->strAt(1) == "{") ||
|
(Token::Match(tok2, "%var% (") && tok2->isUpperCaseName() && tok2->next()->link()->strAt(1) == "{") ||
|
||||||
Token::Match(tok2, ": ::| %var% (|::|<|{") ||
|
Token::Match(tok2, ": ::| %var% (|::|<|{") ||
|
||||||
Token::Match(tok2, "= delete|default ;"))) {
|
Token::Match(tok2, "= delete|default ;") ||
|
||||||
|
Token::Match(tok2, "const| noexcept const| {|:|;") ||
|
||||||
|
(Token::Match(tok2, "const| noexcept|throw (") &&
|
||||||
|
tok2->str() == "const" ? (tok2->tokAt(2) && Token::Match(tok2->tokAt(2)->link(), ") const| {|:|;")) :
|
||||||
|
(tok2->next() && Token::Match(tok2->next()->link(), ") const| {|:|;"))))) {
|
||||||
*funcStart = tok;
|
*funcStart = tok;
|
||||||
*argStart = tok->next();
|
*argStart = tok->next();
|
||||||
return true;
|
return true;
|
||||||
|
@ -980,7 +1120,8 @@ bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const
|
||||||
else if (Token::Match(tok, "%var% <") && Token::simpleMatch(tok->next()->link(), "> (")) {
|
else if (Token::Match(tok, "%var% <") && Token::simpleMatch(tok->next()->link(), "> (")) {
|
||||||
const Token* tok2 = tok->next()->link()->next()->link();
|
const Token* tok2 = tok->next()->link()->next()->link();
|
||||||
if (Token::Match(tok2, ") const| ;|{|=") ||
|
if (Token::Match(tok2, ") const| ;|{|=") ||
|
||||||
Token::Match(tok2, ") : ::| %var% (|::|<|{")) {
|
Token::Match(tok2, ") : ::| %var% (|::|<|{") ||
|
||||||
|
Token::Match(tok->next()->link()->next()->link(), ") const| noexcept {|;|(")) {
|
||||||
*funcStart = tok;
|
*funcStart = tok;
|
||||||
*argStart = tok2->link();
|
*argStart = tok2->link();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1772,8 +1913,12 @@ void SymbolDatabase::printOut(const char *title) const
|
||||||
std::cout << " isExplicit: " << (func->isExplicit ? "true" : "false") << std::endl;
|
std::cout << " isExplicit: " << (func->isExplicit ? "true" : "false") << std::endl;
|
||||||
std::cout << " isDefault: " << (func->isDefault ? "true" : "false") << std::endl;
|
std::cout << " isDefault: " << (func->isDefault ? "true" : "false") << std::endl;
|
||||||
std::cout << " isDelete: " << (func->isDelete ? "true" : "false") << std::endl;
|
std::cout << " isDelete: " << (func->isDelete ? "true" : "false") << std::endl;
|
||||||
|
std::cout << " isNoExcept: " << (func->isNoExcept ? "true" : "false") << std::endl;
|
||||||
|
std::cout << " isThrow: " << (func->isThrow ? "true" : "false") << std::endl;
|
||||||
std::cout << " isOperator: " << (func->isOperator ? "true" : "false") << std::endl;
|
std::cout << " isOperator: " << (func->isOperator ? "true" : "false") << std::endl;
|
||||||
std::cout << " retFuncPtr: " << (func->retFuncPtr ? "true" : "false") << std::endl;
|
std::cout << " retFuncPtr: " << (func->retFuncPtr ? "true" : "false") << std::endl;
|
||||||
|
std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl;
|
||||||
|
std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl;
|
||||||
std::cout << " tokenDef: " << func->tokenDef->str() << " " <<_tokenizer->list.fileLine(func->tokenDef) << std::endl;
|
std::cout << " tokenDef: " << func->tokenDef->str() << " " <<_tokenizer->list.fileLine(func->tokenDef) << std::endl;
|
||||||
std::cout << " argDef: " << _tokenizer->list.fileLine(func->argDef) << std::endl;
|
std::cout << " argDef: " << _tokenizer->list.fileLine(func->argDef) << std::endl;
|
||||||
if (!func->isConstructor() && !func->isDestructor())
|
if (!func->isConstructor() && !func->isDestructor())
|
||||||
|
|
|
@ -544,8 +544,12 @@ public:
|
||||||
isExplicit(false),
|
isExplicit(false),
|
||||||
isDefault(false),
|
isDefault(false),
|
||||||
isDelete(false),
|
isDelete(false),
|
||||||
|
isNoExcept(false),
|
||||||
|
isThrow(false),
|
||||||
isOperator(false),
|
isOperator(false),
|
||||||
retFuncPtr(false) {
|
retFuncPtr(false),
|
||||||
|
noexceptArg(nullptr),
|
||||||
|
throwArg(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &name() const {
|
const std::string &name() const {
|
||||||
|
@ -610,8 +614,12 @@ public:
|
||||||
bool isExplicit; // is explicit
|
bool isExplicit; // is explicit
|
||||||
bool isDefault; // is default
|
bool isDefault; // is default
|
||||||
bool isDelete; // is delete
|
bool isDelete; // is delete
|
||||||
|
bool isNoExcept; // is noexcept
|
||||||
|
bool isThrow; // is throw
|
||||||
bool isOperator; // is operator
|
bool isOperator; // is operator
|
||||||
bool retFuncPtr; // returns function pointer
|
bool retFuncPtr; // returns function pointer
|
||||||
|
const Token *noexceptArg;
|
||||||
|
const Token *throwArg;
|
||||||
|
|
||||||
static bool argsMatch(const Scope *info, const Token *first, const Token *second, const std::string &path, unsigned int depth);
|
static bool argsMatch(const Scope *info, const Token *first, const Token *second, const std::string &path, unsigned int depth);
|
||||||
|
|
||||||
|
|
|
@ -3352,9 +3352,6 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
|
||||||
// Simplify the operator "?:"
|
// Simplify the operator "?:"
|
||||||
simplifyConditionOperator();
|
simplifyConditionOperator();
|
||||||
|
|
||||||
// remove exception specifications..
|
|
||||||
removeExceptionSpecifications();
|
|
||||||
|
|
||||||
// Collapse operator name tokens into single token
|
// Collapse operator name tokens into single token
|
||||||
// operator = => operator=
|
// operator = => operator=
|
||||||
simplifyOperatorName();
|
simplifyOperatorName();
|
||||||
|
@ -3680,7 +3677,7 @@ void Tokenizer::removeMacrosInGlobalScope()
|
||||||
if (tok->str() == "(") {
|
if (tok->str() == "(") {
|
||||||
tok = tok->link();
|
tok = tok->link();
|
||||||
if (Token::Match(tok, ") %type% {") &&
|
if (Token::Match(tok, ") %type% {") &&
|
||||||
!Token::Match(tok->next(), "const|namespace|class|struct|union"))
|
!Token::Match(tok->next(), "const|namespace|class|struct|union|noexcept"))
|
||||||
tok->deleteNext();
|
tok->deleteNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8620,30 +8617,6 @@ void Tokenizer::simplifyComma()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Tokenizer::removeExceptionSpecifications()
|
|
||||||
{
|
|
||||||
if (isC())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (Token* tok = list.front(); tok; tok = tok->next()) {
|
|
||||||
if (Token::Match(tok, ") const| throw|noexcept (")) {
|
|
||||||
if (tok->next()->str() == "const") {
|
|
||||||
Token::eraseTokens(tok->next(), tok->linkAt(3));
|
|
||||||
tok = tok->next();
|
|
||||||
} else
|
|
||||||
Token::eraseTokens(tok, tok->linkAt(2));
|
|
||||||
tok->deleteNext();
|
|
||||||
} else if (Token::Match(tok, ") const| noexcept ;|{|const")) {
|
|
||||||
if (tok->next()->str() == "const")
|
|
||||||
tok->next()->deleteNext();
|
|
||||||
else
|
|
||||||
tok->deleteNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Tokenizer::validate() const
|
void Tokenizer::validate() const
|
||||||
{
|
{
|
||||||
std::stack<const Token *> linktok;
|
std::stack<const Token *> linktok;
|
||||||
|
|
|
@ -42,6 +42,8 @@ private:
|
||||||
TEST_CASE(rethrowCopy4);
|
TEST_CASE(rethrowCopy4);
|
||||||
TEST_CASE(rethrowCopy5);
|
TEST_CASE(rethrowCopy5);
|
||||||
TEST_CASE(catchExceptionByValue);
|
TEST_CASE(catchExceptionByValue);
|
||||||
|
TEST_CASE(noexceptThrow);
|
||||||
|
TEST_CASE(nothrowThrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], bool inconclusive = false) {
|
void check(const char code[], bool inconclusive = false) {
|
||||||
|
@ -308,6 +310,20 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void noexceptThrow() {
|
||||||
|
check("void func1() noexcept { throw 1; }\n"
|
||||||
|
"void func2() noexcept(true) { throw 1; }\n"
|
||||||
|
"void func3() noexcept(false) { throw 1; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in noexcept function.\n"
|
||||||
|
"[test.cpp:2]: (error) Exception thrown in noexcept function.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nothrowThrow() {
|
||||||
|
check("void func1() throw() { throw 1; }\n"
|
||||||
|
"void func2() throw(int) { throw 1; }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Exception thrown in throw() function.\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestExceptionSafety)
|
REGISTER_TEST(TestExceptionSafety)
|
||||||
|
|
|
@ -217,6 +217,11 @@ private:
|
||||||
|
|
||||||
TEST_CASE(findFunction1);
|
TEST_CASE(findFunction1);
|
||||||
TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument
|
TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument
|
||||||
|
|
||||||
|
TEST_CASE(noexceptFunction1);
|
||||||
|
TEST_CASE(noexceptFunction2);
|
||||||
|
TEST_CASE(noexceptFunction3);
|
||||||
|
TEST_CASE(noexceptFunction4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void array() const {
|
void array() const {
|
||||||
|
@ -1980,6 +1985,104 @@ private:
|
||||||
ASSERT_EQUALS(true, callfunc != nullptr); // not null
|
ASSERT_EQUALS(true, callfunc != nullptr); // not null
|
||||||
ASSERT_EQUALS(false, (callfunc && callfunc->function())); // callfunc->function() should be null
|
ASSERT_EQUALS(false, (callfunc && callfunc->function())); // callfunc->function() should be null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FUNC(x) const Function *x = findFunctionByName(#x, &db->scopeList.front()); \
|
||||||
|
ASSERT_EQUALS(true, x != nullptr); \
|
||||||
|
if (x) ASSERT_EQUALS(true, x->isNoExcept);
|
||||||
|
|
||||||
|
void noexceptFunction1() {
|
||||||
|
GET_SYMBOL_DB("void func1() noexcept;\n"
|
||||||
|
"void func2() noexcept { }\n"
|
||||||
|
"void func3() noexcept(true);\n"
|
||||||
|
"void func4() noexcept(true) { }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
FUNC(func1);
|
||||||
|
FUNC(func2);
|
||||||
|
FUNC(func3);
|
||||||
|
FUNC(func4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noexceptFunction2() {
|
||||||
|
GET_SYMBOL_DB("template <class T> void self_assign(T& t) noexcept(noexcept(t = t)) {t = t; }\n");
|
||||||
|
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
FUNC(self_assign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLASS_FUNC(x, y) const Function *x = findFunctionByName(#x, y); \
|
||||||
|
ASSERT_EQUALS(true, x != nullptr); \
|
||||||
|
if (x) ASSERT_EQUALS(true, x->isNoExcept);
|
||||||
|
|
||||||
|
void noexceptFunction3() {
|
||||||
|
GET_SYMBOL_DB("struct Fred {\n"
|
||||||
|
" void func1() noexcept;\n"
|
||||||
|
" void func2() noexcept { }\n"
|
||||||
|
" void func3() noexcept(true);\n"
|
||||||
|
" void func4() noexcept(true) { }\n"
|
||||||
|
" void func5() const noexcept;\n"
|
||||||
|
" void func6() const noexcept { }\n"
|
||||||
|
" void func7() const noexcept(true);\n"
|
||||||
|
" void func8() const noexcept(true) { }\n"
|
||||||
|
" void func9() noexcept const;\n"
|
||||||
|
" void func10() noexcept const { }\n"
|
||||||
|
" void func11() noexcept(true) const;\n"
|
||||||
|
" void func12() noexcept(true) const { }\n"
|
||||||
|
"};");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
const Scope *fred = db->findScopeByName("Fred");
|
||||||
|
ASSERT_EQUALS(true, fred != nullptr);
|
||||||
|
if (fred) {
|
||||||
|
CLASS_FUNC(func1, fred);
|
||||||
|
CLASS_FUNC(func2, fred);
|
||||||
|
CLASS_FUNC(func3, fred);
|
||||||
|
CLASS_FUNC(func4, fred);
|
||||||
|
CLASS_FUNC(func5, fred);
|
||||||
|
CLASS_FUNC(func6, fred);
|
||||||
|
CLASS_FUNC(func7, fred);
|
||||||
|
CLASS_FUNC(func8, fred);
|
||||||
|
CLASS_FUNC(func9, fred);
|
||||||
|
CLASS_FUNC(func10, fred);
|
||||||
|
CLASS_FUNC(func11, fred);
|
||||||
|
CLASS_FUNC(func12, fred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noexceptFunction4() {
|
||||||
|
GET_SYMBOL_DB("class A {\n"
|
||||||
|
"public:\n"
|
||||||
|
" A(A&& a) {\n"
|
||||||
|
" throw std::runtime_error(\"err\");\n"
|
||||||
|
" }\n"
|
||||||
|
"};\n"
|
||||||
|
"class B {\n"
|
||||||
|
" A a;\n"
|
||||||
|
" B(B&& b) noexcept\n"
|
||||||
|
" :a(std::move(b.a)) { }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
const Scope *b = db->findScopeByName("B");
|
||||||
|
ASSERT_EQUALS(true, b != nullptr);
|
||||||
|
if (b) {
|
||||||
|
CLASS_FUNC(B, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestSymbolDatabase)
|
REGISTER_TEST(TestSymbolDatabase)
|
||||||
|
|
|
@ -424,13 +424,6 @@ private:
|
||||||
TEST_CASE(createLinks);
|
TEST_CASE(createLinks);
|
||||||
TEST_CASE(signed1);
|
TEST_CASE(signed1);
|
||||||
|
|
||||||
TEST_CASE(removeExceptionSpecification1);
|
|
||||||
TEST_CASE(removeExceptionSpecification2);
|
|
||||||
TEST_CASE(removeExceptionSpecification3);
|
|
||||||
TEST_CASE(removeExceptionSpecification4);
|
|
||||||
TEST_CASE(removeExceptionSpecification5);
|
|
||||||
TEST_CASE(removeExceptionSpecification6); // #4617
|
|
||||||
|
|
||||||
TEST_CASE(simplifyString);
|
TEST_CASE(simplifyString);
|
||||||
TEST_CASE(simplifyConst);
|
TEST_CASE(simplifyConst);
|
||||||
TEST_CASE(switchCase);
|
TEST_CASE(switchCase);
|
||||||
|
@ -6860,133 +6853,6 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeExceptionSpecification1() {
|
|
||||||
const char code[] = "class A\n"
|
|
||||||
"{\n"
|
|
||||||
"private:\n"
|
|
||||||
" void f() throw (std::runtime_error);\n"
|
|
||||||
"};\n"
|
|
||||||
"void A::f() throw (std::runtime_error)\n"
|
|
||||||
"{ }";
|
|
||||||
|
|
||||||
const char expected[] = "class A\n"
|
|
||||||
"{\n"
|
|
||||||
"private:\n"
|
|
||||||
"void f ( ) ;\n"
|
|
||||||
"} ;\n"
|
|
||||||
"void A :: f ( )\n"
|
|
||||||
"{ }";
|
|
||||||
|
|
||||||
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeExceptionSpecification2() {
|
|
||||||
const char code[] = "class A\n"
|
|
||||||
"{\n"
|
|
||||||
"private:\n"
|
|
||||||
" int value;\n"
|
|
||||||
"public:\n"
|
|
||||||
" A::A() throw ()\n"
|
|
||||||
" : value(0)\n"
|
|
||||||
" { }\n"
|
|
||||||
"};\n";
|
|
||||||
|
|
||||||
const char expected[] = "class A\n"
|
|
||||||
"{\n"
|
|
||||||
"private:\n"
|
|
||||||
"int value ;\n"
|
|
||||||
"public:\n"
|
|
||||||
"A :: A ( )\n"
|
|
||||||
": value ( 0 )\n"
|
|
||||||
"{ }\n"
|
|
||||||
"} ;";
|
|
||||||
|
|
||||||
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeExceptionSpecification3() {
|
|
||||||
const char code[] = "namespace A {\n"
|
|
||||||
" struct B {\n"
|
|
||||||
" B() throw ()\n"
|
|
||||||
" { }\n"
|
|
||||||
" };\n"
|
|
||||||
"};\n";
|
|
||||||
|
|
||||||
const char expected[] = "namespace A {\n"
|
|
||||||
"struct B {\n"
|
|
||||||
"B ( )\n"
|
|
||||||
"{ }\n"
|
|
||||||
"} ;\n"
|
|
||||||
"} ;";
|
|
||||||
|
|
||||||
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeExceptionSpecification4() {
|
|
||||||
const char code[] = "namespace {\n"
|
|
||||||
" void B() throw ();\n"
|
|
||||||
"};";
|
|
||||||
|
|
||||||
const char expected[] = "namespace {\n"
|
|
||||||
"void B ( ) ;\n"
|
|
||||||
"} ;";
|
|
||||||
|
|
||||||
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeExceptionSpecification5() {
|
|
||||||
ASSERT_EQUALS("void foo ( struct S ) ;",
|
|
||||||
tokenizeAndStringify("void foo (struct S) throw();"));
|
|
||||||
ASSERT_EQUALS("void foo ( struct S , int ) ;",
|
|
||||||
tokenizeAndStringify("void foo (struct S, int) throw();"));
|
|
||||||
ASSERT_EQUALS("void foo ( int , struct S ) ;",
|
|
||||||
tokenizeAndStringify("void foo (int, struct S) throw();"));
|
|
||||||
ASSERT_EQUALS("void foo ( struct S1 , struct S2 ) ;",
|
|
||||||
tokenizeAndStringify("void foo (struct S1, struct S2) throw();"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeExceptionSpecification6() { // #4617
|
|
||||||
ASSERT_EQUALS("void foo ( ) ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept;"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(true);"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(true) { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(noexcept(true));"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(noexcept(true)) { }"));
|
|
||||||
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept;"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept(true);"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept(true) { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept(noexcept(true));"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () const noexcept(noexcept(true)) { }"));
|
|
||||||
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept const;"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept const { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(true) const;"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(true) const { }"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const ;",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(noexcept(true)) const;"));
|
|
||||||
ASSERT_EQUALS("void foo ( ) const { }",
|
|
||||||
tokenizeAndStringify("void foo () noexcept(noexcept(true)) const { }"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void simplifyString() {
|
void simplifyString() {
|
||||||
errout.str("");
|
errout.str("");
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
Loading…
Reference in New Issue