Fixed #5756 (declspec(nothrow) not supported)
This commit is contained in:
parent
7d583e639c
commit
ae96491d6c
|
@ -253,6 +253,13 @@ void CheckExceptionSafety::nothrowThrows()
|
|||
if (throws)
|
||||
nothrowAttributeThrowError(throws);
|
||||
}
|
||||
|
||||
// check __declspec(nothrow) functions
|
||||
else if (scope->function && scope->function->isDeclspecNothrow()) {
|
||||
const Token *throws = functionThrows(scope->function);
|
||||
if (throws)
|
||||
nothrowDeclspecThrowError(throws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,11 @@ private:
|
|||
reportError(tok, Severity::error, "exceptThrowInAttributeNoThrowFunction", "Exception thrown in __attribute__((nothrow)) function.");
|
||||
}
|
||||
|
||||
/** Don't throw exceptions in __declspec(nothrow) functions */
|
||||
void nothrowDeclspecThrowError(const Token * const tok) {
|
||||
reportError(tok, Severity::error, "exceptThrowInDeclspecNoThrowFunction", "Exception thrown in __declspec(nothrow) function.");
|
||||
}
|
||||
|
||||
/** Missing exception specification */
|
||||
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
|
||||
std::string str1(tok1 ? tok1->str() : "foo");
|
||||
|
@ -145,6 +150,7 @@ private:
|
|||
c.noexceptThrowError(0);
|
||||
c.nothrowThrowError(0);
|
||||
c.nothrowAttributeThrowError(0);
|
||||
c.nothrowDeclspecThrowError(0);
|
||||
c.unhandledExceptionSpecificationError(0, 0, "funcname");
|
||||
}
|
||||
|
||||
|
@ -163,6 +169,7 @@ private:
|
|||
"* Throwing exception in noexcept function\n"
|
||||
"* Throwing exception in nothrow() function\n"
|
||||
"* Throwing exception in __attribute__((nothrow)) function\n"
|
||||
"* Throwing exception in __declspec(nothrow) function\n"
|
||||
"* Unhandled exception specification when calling function foo()\n";
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1365,6 +1365,7 @@ Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, co
|
|||
const_cast<Token *>(tok)->isAttributePure(i->tokenDef->isAttributePure());
|
||||
const_cast<Token *>(tok)->isAttributeConst(i->tokenDef->isAttributeConst());
|
||||
const_cast<Token *>(tok)->isAttributeNothrow(i->tokenDef->isAttributeNothrow());
|
||||
const_cast<Token *>(tok)->isDeclspecNothrow(i->tokenDef->isDeclspecNothrow());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1916,6 +1917,7 @@ void SymbolDatabase::printOut(const char *title) const
|
|||
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::cerr << " isDeclspecNothrow: " << (func->isDeclspecNothrow() ? "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;
|
||||
|
|
|
@ -593,6 +593,9 @@ public:
|
|||
bool isAttributeNothrow() const {
|
||||
return tokenDef->isAttributeNothrow();
|
||||
}
|
||||
bool isDeclspecNothrow() const {
|
||||
return tokenDef->isDeclspecNothrow();
|
||||
}
|
||||
|
||||
const Token *tokenDef; // function name token in class definition
|
||||
const Token *argDef; // function argument start '(' in class definition
|
||||
|
|
|
@ -57,6 +57,7 @@ Token::Token(Token **t) :
|
|||
_isAttributePure(false),
|
||||
_isAttributeConst(false),
|
||||
_isAttributeNothrow(false),
|
||||
_isDeclspecNothrow(false),
|
||||
_astOperand1(nullptr),
|
||||
_astOperand2(nullptr),
|
||||
_astParent(nullptr)
|
||||
|
@ -189,6 +190,7 @@ void Token::deleteThis()
|
|||
_isAttributePure = _next->_isAttributePure;
|
||||
_isAttributeConst = _next->_isAttributeConst;
|
||||
_isAttributeNothrow = _next->_isAttributeNothrow;
|
||||
_isDeclspecNothrow = _next->_isDeclspecNothrow;
|
||||
_varId = _next->_varId;
|
||||
_fileIndex = _next->_fileIndex;
|
||||
_linenr = _next->_linenr;
|
||||
|
@ -217,6 +219,7 @@ void Token::deleteThis()
|
|||
_isAttributePure = _previous->_isAttributePure;
|
||||
_isAttributeConst = _previous->_isAttributeConst;
|
||||
_isAttributeNothrow = _previous->_isAttributeNothrow;
|
||||
_isDeclspecNothrow = _previous->_isDeclspecNothrow;
|
||||
_varId = _previous->_varId;
|
||||
_fileIndex = _previous->_fileIndex;
|
||||
_linenr = _previous->_linenr;
|
||||
|
|
|
@ -322,6 +322,12 @@ public:
|
|||
void isAttributeNothrow(bool value) {
|
||||
_isAttributeNothrow = value;
|
||||
}
|
||||
bool isDeclspecNothrow() const {
|
||||
return _isDeclspecNothrow;
|
||||
}
|
||||
void isDeclspecNothrow(bool value) {
|
||||
_isDeclspecNothrow = value;
|
||||
}
|
||||
|
||||
static const Token *findsimplematch(const Token *tok, const char pattern[]);
|
||||
static const Token *findsimplematch(const Token *tok, const char pattern[], const Token *end);
|
||||
|
@ -706,6 +712,7 @@ private:
|
|||
bool _isAttributePure; // __attribute__((pure))
|
||||
bool _isAttributeConst; // __attribute__((const))
|
||||
bool _isAttributeNothrow; // __attribute__((nothrow))
|
||||
bool _isDeclspecNothrow; // __declspec(nothrow)
|
||||
|
||||
/** Updates internal property cache like _isName or _isBoolean.
|
||||
Called after any _str() modification. */
|
||||
|
|
|
@ -121,6 +121,7 @@ Token *Tokenizer::copyTokens(Token *dest, const Token *first, const Token *last,
|
|||
tok2->isAttributePure(tok->isAttributePure());
|
||||
tok2->isAttributeConst(tok->isAttributeConst());
|
||||
tok2->isAttributeNothrow(tok->isAttributeNothrow());
|
||||
tok2->isDeclspecNothrow(tok->isDeclspecNothrow());
|
||||
tok2->varId(tok->varId());
|
||||
|
||||
// Check for links and fix them up
|
||||
|
@ -3215,15 +3216,15 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
|
|||
if (_settings->terminated())
|
||||
return false;
|
||||
|
||||
// remove calling conventions __cdecl, __stdcall..
|
||||
simplifyCallingConvention();
|
||||
|
||||
// Remove __declspec()
|
||||
simplifyDeclspec();
|
||||
|
||||
// remove some unhandled macros in global scope
|
||||
removeMacrosInGlobalScope();
|
||||
|
||||
// remove calling conventions __cdecl, __stdcall..
|
||||
simplifyCallingConvention();
|
||||
|
||||
// remove __attribute__((?))
|
||||
simplifyAttribute();
|
||||
|
||||
|
@ -9147,6 +9148,15 @@ void Tokenizer::simplifyDeclspec()
|
|||
{
|
||||
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||
while (Token::simpleMatch(tok, "__declspec (") && tok->next()->link() && tok->next()->link()->next()) {
|
||||
if (tok->strAt(2) == "nothrow") {
|
||||
Token *tok1 = tok->next()->link()->next();
|
||||
while (tok1 && !Token::Match(tok1, "%var%")) {
|
||||
tok1 = tok1->next();
|
||||
}
|
||||
if (tok1) {
|
||||
tok1->isDeclspecNothrow(true);
|
||||
}
|
||||
}
|
||||
Token::eraseTokens(tok, tok->next()->link()->next());
|
||||
tok->deleteThis();
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ void TokenList::addtoken(const Token * tok, const unsigned int lineno, const uns
|
|||
_back->isAttributePure(tok->isAttributePure());
|
||||
_back->isAttributeConst(tok->isAttributeConst());
|
||||
_back->isAttributeNothrow(tok->isAttributeNothrow());
|
||||
_back->isDeclspecNothrow(tok->isDeclspecNothrow());
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
// InsertTokens - Copy and insert tokens
|
||||
|
@ -189,6 +190,7 @@ void TokenList::insertTokens(Token *dest, const Token *src, unsigned int n)
|
|||
dest->isAttributePure(src->isAttributePure());
|
||||
dest->isAttributeConst(src->isAttributeConst());
|
||||
dest->isAttributeNothrow(src->isAttributeNothrow());
|
||||
dest->isDeclspecNothrow(src->isDeclspecNothrow());
|
||||
src = src->next();
|
||||
--n;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ private:
|
|||
TEST_CASE(unhandledExceptionSpecification2);
|
||||
TEST_CASE(nothrowAttributeThrow);
|
||||
TEST_CASE(nothrowAttributeThrow2); // #5703
|
||||
TEST_CASE(nothrowDeclspecThrow);
|
||||
}
|
||||
|
||||
void check(const char code[], bool inconclusive = false) {
|
||||
|
@ -371,12 +372,10 @@ private:
|
|||
|
||||
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");
|
||||
TODO_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());
|
||||
"void func2() __attribute((nothrow)); void func2() { throw 1; }\n"
|
||||
"void func3() __attribute((nothrow)); void func3() { 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");
|
||||
|
@ -392,6 +391,17 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void nothrowDeclspecThrow() {
|
||||
check("void func1() throw(int) { throw 1; }\n"
|
||||
"void __declspec(nothrow) func2() { throw 1; }\n"
|
||||
"void __declspec(nothrow) func3() { func1(); }\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in __declspec(nothrow) function.\n"
|
||||
"[test.cpp:3]: (error) Exception thrown in __declspec(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)
|
||||
|
|
|
@ -236,6 +236,7 @@ private:
|
|||
TEST_CASE(noexceptFunction4);
|
||||
|
||||
TEST_CASE(nothrowAttributeFunction);
|
||||
TEST_CASE(nothrowDeclspecFunction);
|
||||
}
|
||||
|
||||
void array() const {
|
||||
|
@ -2160,6 +2161,19 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void nothrowDeclspecFunction() {
|
||||
GET_SYMBOL_DB("void __declspec(nothrow) 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->isDeclspecNothrow());
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestSymbolDatabase)
|
||||
|
|
Loading…
Reference in New Issue