Fixed #5697 (Check for throw in __attribute__((nothrow)) function)
This commit is contained in:
parent
01d14d0388
commit
6ff5de2118
|
@ -178,44 +178,48 @@ void CheckExceptionSafety::checkCatchExceptionByValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
static const Token * functionThrowsRecursive(const Function * function, std::set<const Function *> & recursive)
|
||||||
// void func() noexcept { throw x; }
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
void CheckExceptionSafety::noexceptThrows()
|
|
||||||
{
|
{
|
||||||
const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
|
// check for recursion and bail if found
|
||||||
|
if (recursive.find(function) != recursive.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
for (const Token *tok = function->functionScope->classStart->next();
|
||||||
for (std::size_t i = 0; i < functions; ++i) {
|
tok != function->functionScope->classEnd; tok = tok->next()) {
|
||||||
const Scope * scope = symbolDatabase->functionScopes[i];
|
if (tok->str() == "try") {
|
||||||
// only check noexcept functions
|
// just bail for now
|
||||||
if (scope->function && scope->function->isNoExcept &&
|
break;
|
||||||
(!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") {
|
||||||
if (tok->str() == "try") {
|
return tok;
|
||||||
break;
|
} else if (tok->function()) {
|
||||||
} else if (tok->str() == "throw") {
|
const Function * called = tok->function();
|
||||||
noexceptThrowError(tok);
|
// check if called function has an exception specification
|
||||||
break;
|
if (called->isThrow && called->throwArg) {
|
||||||
} else if (tok->function()) {
|
return tok;
|
||||||
const Function * called = tok->function();
|
} else if (called->isNoExcept && called->noexceptArg &&
|
||||||
// check if called function has an exception specification
|
called->noexceptArg->str() != "true") {
|
||||||
if (called->isThrow && called->throwArg) {
|
return tok;
|
||||||
noexceptThrowError(tok);
|
} else if (functionThrowsRecursive(called, recursive)) {
|
||||||
break;
|
return tok;
|
||||||
} else if (called->isNoExcept && called->noexceptArg &&
|
|
||||||
called->noexceptArg->str() != "true") {
|
|
||||||
noexceptThrowError(tok);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Token * functionThrows(const Function * function)
|
||||||
|
{
|
||||||
|
std::set<const Function *> recursive;
|
||||||
|
|
||||||
|
return functionThrowsRecursive(function, recursive);
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
// void func() noexcept { throw x; }
|
||||||
// void func() throw() { throw x; }
|
// void func() throw() { throw x; }
|
||||||
|
// void func() __attribute__((nothrow)); void func() { throw x; }
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
void CheckExceptionSafety::nothrowThrows()
|
void CheckExceptionSafety::nothrowThrows()
|
||||||
{
|
{
|
||||||
|
@ -224,27 +228,27 @@ void CheckExceptionSafety::nothrowThrows()
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
for (std::size_t i = 0; i < functions; ++i) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
const Scope * scope = symbolDatabase->functionScopes[i];
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
||||||
// only check throw() functions
|
|
||||||
if (scope->function && scope->function->isThrow && !scope->function->throwArg) {
|
// check noexcept functions
|
||||||
for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
|
if (scope->function && scope->function->isNoExcept &&
|
||||||
if (tok->str() == "try") {
|
(!scope->function->noexceptArg || scope->function->noexceptArg->str() == "true")) {
|
||||||
break;
|
const Token *throws = functionThrows(scope->function);
|
||||||
} else if (tok->str() == "throw") {
|
if (throws)
|
||||||
nothrowThrowError(tok);
|
noexceptThrowError(throws);
|
||||||
break;
|
}
|
||||||
} else if (tok->function()) {
|
|
||||||
const Function * called = tok->function();
|
// check throw() functions
|
||||||
// check if called function has an exception specification
|
else if (scope->function && scope->function->isThrow && !scope->function->throwArg) {
|
||||||
if (called->isThrow && called->throwArg) {
|
const Token *throws = functionThrows(scope->function);
|
||||||
nothrowThrowError(tok);
|
if (throws)
|
||||||
break;
|
nothrowThrowError(throws);
|
||||||
} else if (called->isNoExcept && called->noexceptArg &&
|
}
|
||||||
called->noexceptArg->str() != "true") {
|
|
||||||
nothrowThrowError(tok);
|
// check __attribute__((nothrow)) functions
|
||||||
break;
|
else if (scope->function && scope->function->isAttributeNothrow()) {
|
||||||
}
|
const Token *throws = functionThrows(scope->function);
|
||||||
}
|
if (throws)
|
||||||
}
|
nothrowAttributeThrowError(throws);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,6 @@ public:
|
||||||
checkExceptionSafety.deallocThrow();
|
checkExceptionSafety.deallocThrow();
|
||||||
checkExceptionSafety.checkRethrowCopy();
|
checkExceptionSafety.checkRethrowCopy();
|
||||||
checkExceptionSafety.checkCatchExceptionByValue();
|
checkExceptionSafety.checkCatchExceptionByValue();
|
||||||
checkExceptionSafety.noexceptThrows();
|
|
||||||
checkExceptionSafety.nothrowThrows();
|
checkExceptionSafety.nothrowThrows();
|
||||||
checkExceptionSafety.unhandledExceptionSpecification();
|
checkExceptionSafety.unhandledExceptionSpecification();
|
||||||
}
|
}
|
||||||
|
@ -78,10 +77,7 @@ 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 */
|
/** @brief %Check for functions that throw that shouldn't */
|
||||||
void noexceptThrows();
|
|
||||||
|
|
||||||
/** @brief %Check for throw() functions that throw */
|
|
||||||
void nothrowThrows();
|
void nothrowThrows();
|
||||||
|
|
||||||
/** @brief %Check for unhandled exception specification */
|
/** @brief %Check for unhandled exception specification */
|
||||||
|
@ -122,6 +118,11 @@ private:
|
||||||
reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function.");
|
reportError(tok, Severity::error, "exceptThrowInNoThrowFunction", "Exception thrown in throw() function.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Don't throw exceptions in __attribute__((nothrow)) functions */
|
||||||
|
void nothrowAttributeThrowError(const Token * const tok) {
|
||||||
|
reportError(tok, Severity::error, "exceptThrowInAttributeNoThrowFunction", "Exception thrown in __attribute__((nothrow)) function.");
|
||||||
|
}
|
||||||
|
|
||||||
/** Missing exception specification */
|
/** Missing exception specification */
|
||||||
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
|
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
|
||||||
std::string str1(tok1 ? tok1->str() : "foo");
|
std::string str1(tok1 ? tok1->str() : "foo");
|
||||||
|
@ -143,6 +144,7 @@ private:
|
||||||
c.catchExceptionByValueError(0);
|
c.catchExceptionByValueError(0);
|
||||||
c.noexceptThrowError(0);
|
c.noexceptThrowError(0);
|
||||||
c.nothrowThrowError(0);
|
c.nothrowThrowError(0);
|
||||||
|
c.nothrowAttributeThrowError(0);
|
||||||
c.unhandledExceptionSpecificationError(0, 0, "funcname");
|
c.unhandledExceptionSpecificationError(0, 0, "funcname");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +162,7 @@ private:
|
||||||
"* 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 noexcept function\n"
|
||||||
"* Throwing exception in nothrow() function\n"
|
"* Throwing exception in nothrow() function\n"
|
||||||
|
"* Throwing exception in __attribute__((nothrow)) function\n"
|
||||||
"* Unhandled exception specification when calling function foo()\n";
|
"* Unhandled exception specification when calling function foo()\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -657,6 +657,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
|
||||||
// regular function
|
// regular function
|
||||||
else {
|
else {
|
||||||
Function* function = addGlobalFunction(scope, tok, argStart, funcStart);
|
Function* function = addGlobalFunction(scope, tok, argStart, funcStart);
|
||||||
|
|
||||||
if (!function)
|
if (!function)
|
||||||
_tokenizer->syntaxError(tok);
|
_tokenizer->syntaxError(tok);
|
||||||
|
|
||||||
|
@ -1363,6 +1364,7 @@ Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, co
|
||||||
const_cast<Token *>(tok)->isAttributeDestructor(i->tokenDef->isAttributeDestructor());
|
const_cast<Token *>(tok)->isAttributeDestructor(i->tokenDef->isAttributeDestructor());
|
||||||
const_cast<Token *>(tok)->isAttributePure(i->tokenDef->isAttributePure());
|
const_cast<Token *>(tok)->isAttributePure(i->tokenDef->isAttributePure());
|
||||||
const_cast<Token *>(tok)->isAttributeConst(i->tokenDef->isAttributeConst());
|
const_cast<Token *>(tok)->isAttributeConst(i->tokenDef->isAttributeConst());
|
||||||
|
const_cast<Token *>(tok)->isAttributeNothrow(i->tokenDef->isAttributeNothrow());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1911,6 +1913,9 @@ void SymbolDatabase::printOut(const char *title) const
|
||||||
std::cout << " isNoExcept: " << (func->isNoExcept ? "true" : "false") << std::endl;
|
std::cout << " isNoExcept: " << (func->isNoExcept ? "true" : "false") << std::endl;
|
||||||
std::cout << " isThrow: " << (func->isThrow ? "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::cerr << " isAttributeConst: " << (func->isAttributeConst() ? "true" : "false") << std::endl;
|
||||||
|
std::cerr << " isAttributePure: " << (func->isAttributePure() ? "true" : "false") << std::endl;
|
||||||
|
std::cerr << " isAttributeNothrow: " << (func->isAttributeNothrow() ? "true" : "false") << std::endl;
|
||||||
std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << 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 << " 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;
|
||||||
|
|
|
@ -590,6 +590,9 @@ public:
|
||||||
bool isAttributeConst() const {
|
bool isAttributeConst() const {
|
||||||
return tokenDef->isAttributeConst();
|
return tokenDef->isAttributeConst();
|
||||||
}
|
}
|
||||||
|
bool isAttributeNothrow() const {
|
||||||
|
return tokenDef->isAttributeNothrow();
|
||||||
|
}
|
||||||
|
|
||||||
const Token *tokenDef; // function name token in class definition
|
const Token *tokenDef; // function name token in class definition
|
||||||
const Token *argDef; // function argument start '(' in class definition
|
const Token *argDef; // function argument start '(' in class definition
|
||||||
|
|
|
@ -56,6 +56,7 @@ Token::Token(Token **t) :
|
||||||
_isAttributeUnused(false),
|
_isAttributeUnused(false),
|
||||||
_isAttributePure(false),
|
_isAttributePure(false),
|
||||||
_isAttributeConst(false),
|
_isAttributeConst(false),
|
||||||
|
_isAttributeNothrow(false),
|
||||||
_astOperand1(nullptr),
|
_astOperand1(nullptr),
|
||||||
_astOperand2(nullptr),
|
_astOperand2(nullptr),
|
||||||
_astParent(nullptr)
|
_astParent(nullptr)
|
||||||
|
@ -196,6 +197,7 @@ void Token::deleteThis()
|
||||||
_isAttributeUnused = _next->_isAttributeUnused;
|
_isAttributeUnused = _next->_isAttributeUnused;
|
||||||
_isAttributePure = _next->_isAttributePure;
|
_isAttributePure = _next->_isAttributePure;
|
||||||
_isAttributeConst = _next->_isAttributeConst;
|
_isAttributeConst = _next->_isAttributeConst;
|
||||||
|
_isAttributeNothrow = _next->_isAttributeNothrow;
|
||||||
_varId = _next->_varId;
|
_varId = _next->_varId;
|
||||||
_fileIndex = _next->_fileIndex;
|
_fileIndex = _next->_fileIndex;
|
||||||
_linenr = _next->_linenr;
|
_linenr = _next->_linenr;
|
||||||
|
@ -223,6 +225,7 @@ void Token::deleteThis()
|
||||||
_isAttributeUnused = _previous->_isAttributeUnused;
|
_isAttributeUnused = _previous->_isAttributeUnused;
|
||||||
_isAttributePure = _previous->_isAttributePure;
|
_isAttributePure = _previous->_isAttributePure;
|
||||||
_isAttributeConst = _previous->_isAttributeConst;
|
_isAttributeConst = _previous->_isAttributeConst;
|
||||||
|
_isAttributeNothrow = _previous->_isAttributeNothrow;
|
||||||
_varId = _previous->_varId;
|
_varId = _previous->_varId;
|
||||||
_fileIndex = _previous->_fileIndex;
|
_fileIndex = _previous->_fileIndex;
|
||||||
_linenr = _previous->_linenr;
|
_linenr = _previous->_linenr;
|
||||||
|
|
|
@ -312,6 +312,12 @@ public:
|
||||||
void isAttributeConst(bool value) {
|
void isAttributeConst(bool value) {
|
||||||
_isAttributeConst = value;
|
_isAttributeConst = value;
|
||||||
}
|
}
|
||||||
|
bool isAttributeNothrow() const {
|
||||||
|
return _isAttributeNothrow;
|
||||||
|
}
|
||||||
|
void isAttributeNothrow(bool value) {
|
||||||
|
_isAttributeNothrow = value;
|
||||||
|
}
|
||||||
|
|
||||||
static const Token *findsimplematch(const Token *tok, const char pattern[]);
|
static const Token *findsimplematch(const Token *tok, const char pattern[]);
|
||||||
static const Token *findsimplematch(const Token *tok, const char pattern[], const Token *end);
|
static const Token *findsimplematch(const Token *tok, const char pattern[], const Token *end);
|
||||||
|
@ -695,6 +701,7 @@ private:
|
||||||
bool _isAttributeUnused; // __attribute__((unused))
|
bool _isAttributeUnused; // __attribute__((unused))
|
||||||
bool _isAttributePure; // __attribute__((pure))
|
bool _isAttributePure; // __attribute__((pure))
|
||||||
bool _isAttributeConst; // __attribute__((const))
|
bool _isAttributeConst; // __attribute__((const))
|
||||||
|
bool _isAttributeNothrow; // __attribute__((nothrow))
|
||||||
|
|
||||||
/** Updates internal property cache like _isName or _isBoolean.
|
/** Updates internal property cache like _isName or _isBoolean.
|
||||||
Called after any _str() modification. */
|
Called after any _str() modification. */
|
||||||
|
|
|
@ -120,6 +120,7 @@ Token *Tokenizer::copyTokens(Token *dest, const Token *first, const Token *last,
|
||||||
tok2->isAttributeUnused(tok->isAttributeUnused());
|
tok2->isAttributeUnused(tok->isAttributeUnused());
|
||||||
tok2->isAttributePure(tok->isAttributePure());
|
tok2->isAttributePure(tok->isAttributePure());
|
||||||
tok2->isAttributeConst(tok->isAttributeConst());
|
tok2->isAttributeConst(tok->isAttributeConst());
|
||||||
|
tok2->isAttributeNothrow(tok->isAttributeNothrow());
|
||||||
tok2->varId(tok->varId());
|
tok2->varId(tok->varId());
|
||||||
|
|
||||||
// Check for links and fix them up
|
// Check for links and fix them up
|
||||||
|
@ -9057,7 +9058,7 @@ void Tokenizer::simplifyAttribute()
|
||||||
{
|
{
|
||||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||||
while (Token::Match(tok, "__attribute__|__attribute (") && tok->next()->link() && tok->next()->link()->next()) {
|
while (Token::Match(tok, "__attribute__|__attribute (") && tok->next()->link() && tok->next()->link()->next()) {
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "( constructor")) {
|
if (Token::Match(tok->tokAt(2), "( constructor|__constructor__")) {
|
||||||
// prototype for constructor is: void func(void);
|
// prototype for constructor is: void func(void);
|
||||||
if (tok->next()->link()->next()->str() == "void") // __attribute__((constructor)) void func() {}
|
if (tok->next()->link()->next()->str() == "void") // __attribute__((constructor)) void func() {}
|
||||||
tok->next()->link()->next()->next()->isAttributeConstructor(true);
|
tok->next()->link()->next()->next()->isAttributeConstructor(true);
|
||||||
|
@ -9067,7 +9068,7 @@ void Tokenizer::simplifyAttribute()
|
||||||
tok->next()->link()->next()->isAttributeConstructor(true);
|
tok->next()->link()->next()->isAttributeConstructor(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "( destructor")) {
|
else if (Token::Match(tok->tokAt(2), "( destructor|__destructor__")) {
|
||||||
// prototype for destructor is: void func(void);
|
// prototype for destructor is: void func(void);
|
||||||
if (tok->next()->link()->next()->str() == "void") // __attribute__((destructor)) void func() {}
|
if (tok->next()->link()->next()->str() == "void") // __attribute__((destructor)) void func() {}
|
||||||
tok->next()->link()->next()->next()->isAttributeDestructor(true);
|
tok->next()->link()->next()->next()->isAttributeDestructor(true);
|
||||||
|
@ -9077,7 +9078,7 @@ void Tokenizer::simplifyAttribute()
|
||||||
tok->next()->link()->next()->isAttributeDestructor(true);
|
tok->next()->link()->next()->isAttributeDestructor(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "( unused )")) {
|
else if (Token::Match(tok->tokAt(2), "( unused|__unused__ )")) {
|
||||||
// check if after variable name
|
// check if after variable name
|
||||||
if (Token::Match(tok->next()->link()->next(), ";|=")) {
|
if (Token::Match(tok->next()->link()->next(), ";|=")) {
|
||||||
if (Token::Match(tok->previous(), "%type%"))
|
if (Token::Match(tok->previous(), "%type%"))
|
||||||
|
@ -9089,26 +9090,57 @@ void Tokenizer::simplifyAttribute()
|
||||||
tok->next()->link()->next()->isAttributeUnused(true);
|
tok->next()->link()->next()->isAttributeUnused(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "( pure )")) {
|
else if (Token::Match(tok->tokAt(2), "( pure|__pure__ )")) {
|
||||||
// type func(...) __attribute__((pure));
|
// type func(...) __attribute__((pure));
|
||||||
if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% ("))
|
if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% ("))
|
||||||
tok->previous()->link()->previous()->isAttributePure(true);
|
tok->previous()->link()->previous()->isAttributePure(true);
|
||||||
|
|
||||||
// type __attribute__((pure)) func() { }
|
// type __attribute__((pure)) func() { }
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributePure(true);
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->next()->isAttributePure(true);
|
||||||
else if (Token::Match(tok->next()->link(), ") %var% ("))
|
else if (Token::Match(tok->next()->link(), ") %var% ("))
|
||||||
tok->next()->link()->next()->isAttributePure(true);
|
tok->next()->link()->next()->isAttributePure(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::simpleMatch(tok->tokAt(2), "( const )")) {
|
else if (Token::Match(tok->tokAt(2), "( const|__const__ )")) {
|
||||||
// type func(...) __attribute__((const));
|
// type func(...) __attribute__((const));
|
||||||
if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% ("))
|
if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% ("))
|
||||||
tok->previous()->link()->previous()->isAttributeConst(true);
|
tok->previous()->link()->previous()->isAttributeConst(true);
|
||||||
|
|
||||||
// type __attribute__((const)) func() { }
|
// type __attribute__((const)) func() { }
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributeConst(true);
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->next()->isAttributeConst(true);
|
||||||
else if (Token::Match(tok->next()->link(), ") %var% ("))
|
else if (Token::Match(tok->next()->link(), ") %var% ("))
|
||||||
tok->next()->link()->next()->isAttributeConst(true);
|
tok->next()->link()->next()->isAttributeConst(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (Token::Match(tok->tokAt(2), "( nothrow|__nothrow__")) {
|
||||||
|
// type func(...) __attribute__((nothrow));
|
||||||
|
if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%var% ("))
|
||||||
|
tok->previous()->link()->previous()->isAttributeNothrow(true);
|
||||||
|
|
||||||
|
// type __attribute__((nothrow)) func() { }
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2)->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->linkAt(2)->next()->isAttributeNothrow(true);
|
||||||
|
else if (Token::Match(tok->next()->link(), ") __attribute__|__attribute (") &&
|
||||||
|
Token::Match(tok->next()->link()->linkAt(2), ") %var% ("))
|
||||||
|
tok->next()->link()->linkAt(2)->next()->isAttributeNothrow(true);
|
||||||
|
else if (Token::Match(tok->next()->link(), ") %var% ("))
|
||||||
|
tok->next()->link()->next()->isAttributeNothrow(true);
|
||||||
|
}
|
||||||
|
|
||||||
Token::eraseTokens(tok, tok->next()->link()->next());
|
Token::eraseTokens(tok, tok->next()->link()->next());
|
||||||
tok->deleteThis();
|
tok->deleteThis();
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ void TokenList::addtoken(const Token * tok, const unsigned int lineno, const uns
|
||||||
_back->isAttributeUnused(tok->isAttributeUnused());
|
_back->isAttributeUnused(tok->isAttributeUnused());
|
||||||
_back->isAttributePure(tok->isAttributePure());
|
_back->isAttributePure(tok->isAttributePure());
|
||||||
_back->isAttributeConst(tok->isAttributeConst());
|
_back->isAttributeConst(tok->isAttributeConst());
|
||||||
|
_back->isAttributeNothrow(tok->isAttributeNothrow());
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// InsertTokens - Copy and insert tokens
|
// InsertTokens - Copy and insert tokens
|
||||||
|
@ -187,6 +188,7 @@ void TokenList::insertTokens(Token *dest, const Token *src, unsigned int n)
|
||||||
dest->isAttributeUnused(src->isAttributeUnused());
|
dest->isAttributeUnused(src->isAttributeUnused());
|
||||||
dest->isAttributePure(src->isAttributePure());
|
dest->isAttributePure(src->isAttributePure());
|
||||||
dest->isAttributeConst(src->isAttributeConst());
|
dest->isAttributeConst(src->isAttributeConst());
|
||||||
|
dest->isAttributeNothrow(src->isAttributeNothrow());
|
||||||
src = src->next();
|
src = src->next();
|
||||||
--n;
|
--n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,6 +356,19 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (warning) Unhandled exception specification when calling function myThrowingFoo().\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (warning) Unhandled exception specification when calling function myThrowingFoo().\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nothrowAttributeThrow() {
|
||||||
|
check("void func1() throw(int) { throw 1; }\n"
|
||||||
|
"void func2() __attribute((nothrow)); void func1() { throw 1; }\n"
|
||||||
|
"void func3() __attribute((nothrow)); void func1() { func1(); }\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in __attribute__((nothrow)) function.\n"
|
||||||
|
"[test.cpp:3]: (error) Exception thrown in __attribute__((nothrow)) function.\n", errout.str());
|
||||||
|
|
||||||
|
// avoid false positives
|
||||||
|
check("const char *func() __attribute((nothrow)); void func1() { return 0; }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestExceptionSafety)
|
REGISTER_TEST(TestExceptionSafety)
|
||||||
|
|
|
@ -234,6 +234,8 @@ private:
|
||||||
TEST_CASE(noexceptFunction2);
|
TEST_CASE(noexceptFunction2);
|
||||||
TEST_CASE(noexceptFunction3);
|
TEST_CASE(noexceptFunction3);
|
||||||
TEST_CASE(noexceptFunction4);
|
TEST_CASE(noexceptFunction4);
|
||||||
|
|
||||||
|
TEST_CASE(nothrowAttributeFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void array() const {
|
void array() const {
|
||||||
|
@ -2144,6 +2146,20 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nothrowAttributeFunction() {
|
||||||
|
GET_SYMBOL_DB("void func() __attribute__((nothrow));\n"
|
||||||
|
"void func() { }\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
const Function *func = findFunctionByName("func", &db->scopeList.front());
|
||||||
|
ASSERT_EQUALS(true, func != nullptr);
|
||||||
|
if (func)
|
||||||
|
ASSERT_EQUALS(true, func->isAttributeNothrow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestSymbolDatabase)
|
REGISTER_TEST(TestSymbolDatabase)
|
||||||
|
|
|
@ -444,6 +444,8 @@ private:
|
||||||
|
|
||||||
TEST_CASE(removedeclspec);
|
TEST_CASE(removedeclspec);
|
||||||
TEST_CASE(removeattribute);
|
TEST_CASE(removeattribute);
|
||||||
|
TEST_CASE(functionAttributeBefore);
|
||||||
|
TEST_CASE(functionAttributeAfter);
|
||||||
TEST_CASE(cpp0xtemplate1);
|
TEST_CASE(cpp0xtemplate1);
|
||||||
TEST_CASE(cpp0xtemplate2);
|
TEST_CASE(cpp0xtemplate2);
|
||||||
TEST_CASE(cpp0xtemplate3);
|
TEST_CASE(cpp0xtemplate3);
|
||||||
|
@ -7089,6 +7091,66 @@ private:
|
||||||
ASSERT_EQUALS("int vecint ;", tokenizeAndStringify("int __attribute((mode(SI))) __attribute((vector_size (16))) vecint;"));
|
ASSERT_EQUALS("int vecint ;", tokenizeAndStringify("int __attribute((mode(SI))) __attribute((vector_size (16))) vecint;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void functionAttributeBefore() {
|
||||||
|
const char code[] = "void __attribute__((pure)) __attribute__((nothrow)) __attribute__((const)) func1();\n"
|
||||||
|
"void __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__)) func2();\n"
|
||||||
|
"void __attribute__((nothrow)) __attribute__((pure)) __attribute__((const)) func3();\n"
|
||||||
|
"void __attribute__((__nothrow__)) __attribute__((__pure__)) __attribute__((__const__)) func4();";
|
||||||
|
const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ;";
|
||||||
|
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
// tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
// Expected result..
|
||||||
|
ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false));
|
||||||
|
|
||||||
|
const Token * func1 = Token::findsimplematch(tokenizer.tokens(), "func1");
|
||||||
|
const Token * func2 = Token::findsimplematch(tokenizer.tokens(), "func2");
|
||||||
|
const Token * func3 = Token::findsimplematch(tokenizer.tokens(), "func3");
|
||||||
|
const Token * func4 = Token::findsimplematch(tokenizer.tokens(), "func4");
|
||||||
|
|
||||||
|
ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst());
|
||||||
|
ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst());
|
||||||
|
ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst());
|
||||||
|
ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst());
|
||||||
|
}
|
||||||
|
|
||||||
|
void functionAttributeAfter() {
|
||||||
|
const char code[] = "void func1() __attribute__((pure)) __attribute__((nothrow)) __attribute__((const));\n"
|
||||||
|
"void func2() __attribute__((__pure__)) __attribute__((__nothrow__)) __attribute__((__const__));\n"
|
||||||
|
"void func3() __attribute__((nothrow)) __attribute__((pure)) __attribute__((const));\n"
|
||||||
|
"void func4() __attribute__((__nothrow__)) __attribute__((__pure__)) __attribute__((__const__));";
|
||||||
|
const char expected[] = "void func1 ( ) ; void func2 ( ) ; void func3 ( ) ; void func4 ( ) ;";
|
||||||
|
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
// tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
// Expected result..
|
||||||
|
ASSERT_EQUALS(expected, tokenizer.tokens()->stringifyList(0, false));
|
||||||
|
|
||||||
|
const Token * func1 = Token::findsimplematch(tokenizer.tokens(), "func1");
|
||||||
|
const Token * func2 = Token::findsimplematch(tokenizer.tokens(), "func2");
|
||||||
|
const Token * func3 = Token::findsimplematch(tokenizer.tokens(), "func3");
|
||||||
|
const Token * func4 = Token::findsimplematch(tokenizer.tokens(), "func4");
|
||||||
|
|
||||||
|
ASSERT(func1 && func1->isAttributePure() && func1->isAttributeNothrow() && func1->isAttributeConst());
|
||||||
|
ASSERT(func2 && func2->isAttributePure() && func2->isAttributeNothrow() && func2->isAttributeConst());
|
||||||
|
ASSERT(func3 && func3->isAttributePure() && func3->isAttributeNothrow() && func3->isAttributeConst());
|
||||||
|
ASSERT(func4 && func4->isAttributePure() && func4->isAttributeNothrow() && func4->isAttributeConst());
|
||||||
|
}
|
||||||
|
|
||||||
void cpp0xtemplate1() {
|
void cpp0xtemplate1() {
|
||||||
const char *code = "template <class T>\n"
|
const char *code = "template <class T>\n"
|
||||||
"void fn2 (T t = []{return 1;}())\n"
|
"void fn2 (T t = []{return 1;}())\n"
|
||||||
|
|
Loading…
Reference in New Issue