Fixed #5756 (declspec(nothrow) not supported)

This commit is contained in:
Robert Reif 2014-05-04 20:47:20 +02:00 committed by Daniel Marjamäki
parent 7d583e639c
commit ae96491d6c
10 changed files with 74 additions and 9 deletions

View File

@ -253,6 +253,13 @@ void CheckExceptionSafety::nothrowThrows()
if (throws) if (throws)
nothrowAttributeThrowError(throws); nothrowAttributeThrowError(throws);
} }
// check __declspec(nothrow) functions
else if (scope->function && scope->function->isDeclspecNothrow()) {
const Token *throws = functionThrows(scope->function);
if (throws)
nothrowDeclspecThrowError(throws);
}
} }
} }

View File

@ -123,6 +123,11 @@ private:
reportError(tok, Severity::error, "exceptThrowInAttributeNoThrowFunction", "Exception thrown in __attribute__((nothrow)) function."); 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 */ /** 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");
@ -145,6 +150,7 @@ private:
c.noexceptThrowError(0); c.noexceptThrowError(0);
c.nothrowThrowError(0); c.nothrowThrowError(0);
c.nothrowAttributeThrowError(0); c.nothrowAttributeThrowError(0);
c.nothrowDeclspecThrowError(0);
c.unhandledExceptionSpecificationError(0, 0, "funcname"); c.unhandledExceptionSpecificationError(0, 0, "funcname");
} }
@ -163,6 +169,7 @@ private:
"* 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" "* Throwing exception in __attribute__((nothrow)) function\n"
"* Throwing exception in __declspec(nothrow) function\n"
"* Unhandled exception specification when calling function foo()\n"; "* Unhandled exception specification when calling function foo()\n";
} }
}; };

View File

@ -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)->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()); const_cast<Token *>(tok)->isAttributeNothrow(i->tokenDef->isAttributeNothrow());
const_cast<Token *>(tok)->isDeclspecNothrow(i->tokenDef->isDeclspecNothrow());
break; break;
} }
} }
@ -1916,6 +1917,7 @@ void SymbolDatabase::printOut(const char *title) const
std::cerr << " isAttributeConst: " << (func->isAttributeConst() ? "true" : "false") << std::endl; std::cerr << " isAttributeConst: " << (func->isAttributeConst() ? "true" : "false") << std::endl;
std::cerr << " isAttributePure: " << (func->isAttributePure() ? "true" : "false") << std::endl; std::cerr << " isAttributePure: " << (func->isAttributePure() ? "true" : "false") << std::endl;
std::cerr << " isAttributeNothrow: " << (func->isAttributeNothrow() ? "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 << " 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;

View File

@ -593,6 +593,9 @@ public:
bool isAttributeNothrow() const { bool isAttributeNothrow() const {
return tokenDef->isAttributeNothrow(); return tokenDef->isAttributeNothrow();
} }
bool isDeclspecNothrow() const {
return tokenDef->isDeclspecNothrow();
}
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

View File

@ -57,6 +57,7 @@ Token::Token(Token **t) :
_isAttributePure(false), _isAttributePure(false),
_isAttributeConst(false), _isAttributeConst(false),
_isAttributeNothrow(false), _isAttributeNothrow(false),
_isDeclspecNothrow(false),
_astOperand1(nullptr), _astOperand1(nullptr),
_astOperand2(nullptr), _astOperand2(nullptr),
_astParent(nullptr) _astParent(nullptr)
@ -189,6 +190,7 @@ void Token::deleteThis()
_isAttributePure = _next->_isAttributePure; _isAttributePure = _next->_isAttributePure;
_isAttributeConst = _next->_isAttributeConst; _isAttributeConst = _next->_isAttributeConst;
_isAttributeNothrow = _next->_isAttributeNothrow; _isAttributeNothrow = _next->_isAttributeNothrow;
_isDeclspecNothrow = _next->_isDeclspecNothrow;
_varId = _next->_varId; _varId = _next->_varId;
_fileIndex = _next->_fileIndex; _fileIndex = _next->_fileIndex;
_linenr = _next->_linenr; _linenr = _next->_linenr;
@ -217,6 +219,7 @@ void Token::deleteThis()
_isAttributePure = _previous->_isAttributePure; _isAttributePure = _previous->_isAttributePure;
_isAttributeConst = _previous->_isAttributeConst; _isAttributeConst = _previous->_isAttributeConst;
_isAttributeNothrow = _previous->_isAttributeNothrow; _isAttributeNothrow = _previous->_isAttributeNothrow;
_isDeclspecNothrow = _previous->_isDeclspecNothrow;
_varId = _previous->_varId; _varId = _previous->_varId;
_fileIndex = _previous->_fileIndex; _fileIndex = _previous->_fileIndex;
_linenr = _previous->_linenr; _linenr = _previous->_linenr;

View File

@ -322,6 +322,12 @@ public:
void isAttributeNothrow(bool value) { void isAttributeNothrow(bool value) {
_isAttributeNothrow = 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[]);
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);
@ -706,6 +712,7 @@ private:
bool _isAttributePure; // __attribute__((pure)) bool _isAttributePure; // __attribute__((pure))
bool _isAttributeConst; // __attribute__((const)) bool _isAttributeConst; // __attribute__((const))
bool _isAttributeNothrow; // __attribute__((nothrow)) bool _isAttributeNothrow; // __attribute__((nothrow))
bool _isDeclspecNothrow; // __declspec(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. */

View File

@ -121,6 +121,7 @@ Token *Tokenizer::copyTokens(Token *dest, const Token *first, const Token *last,
tok2->isAttributePure(tok->isAttributePure()); tok2->isAttributePure(tok->isAttributePure());
tok2->isAttributeConst(tok->isAttributeConst()); tok2->isAttributeConst(tok->isAttributeConst());
tok2->isAttributeNothrow(tok->isAttributeNothrow()); tok2->isAttributeNothrow(tok->isAttributeNothrow());
tok2->isDeclspecNothrow(tok->isDeclspecNothrow());
tok2->varId(tok->varId()); tok2->varId(tok->varId());
// Check for links and fix them up // Check for links and fix them up
@ -3215,15 +3216,15 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
if (_settings->terminated()) if (_settings->terminated())
return false; return false;
// remove calling conventions __cdecl, __stdcall..
simplifyCallingConvention();
// Remove __declspec() // Remove __declspec()
simplifyDeclspec(); simplifyDeclspec();
// remove some unhandled macros in global scope // remove some unhandled macros in global scope
removeMacrosInGlobalScope(); removeMacrosInGlobalScope();
// remove calling conventions __cdecl, __stdcall..
simplifyCallingConvention();
// remove __attribute__((?)) // remove __attribute__((?))
simplifyAttribute(); simplifyAttribute();
@ -9147,6 +9148,15 @@ void Tokenizer::simplifyDeclspec()
{ {
for (Token *tok = list.front(); tok; tok = tok->next()) { for (Token *tok = list.front(); tok; tok = tok->next()) {
while (Token::simpleMatch(tok, "__declspec (") && tok->next()->link() && tok->next()->link()->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()); Token::eraseTokens(tok, tok->next()->link()->next());
tok->deleteThis(); tok->deleteThis();
} }

View File

@ -154,6 +154,7 @@ void TokenList::addtoken(const Token * tok, const unsigned int lineno, const uns
_back->isAttributePure(tok->isAttributePure()); _back->isAttributePure(tok->isAttributePure());
_back->isAttributeConst(tok->isAttributeConst()); _back->isAttributeConst(tok->isAttributeConst());
_back->isAttributeNothrow(tok->isAttributeNothrow()); _back->isAttributeNothrow(tok->isAttributeNothrow());
_back->isDeclspecNothrow(tok->isDeclspecNothrow());
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// InsertTokens - Copy and insert tokens // 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->isAttributePure(src->isAttributePure());
dest->isAttributeConst(src->isAttributeConst()); dest->isAttributeConst(src->isAttributeConst());
dest->isAttributeNothrow(src->isAttributeNothrow()); dest->isAttributeNothrow(src->isAttributeNothrow());
dest->isDeclspecNothrow(src->isDeclspecNothrow());
src = src->next(); src = src->next();
--n; --n;
} }

View File

@ -48,6 +48,7 @@ private:
TEST_CASE(unhandledExceptionSpecification2); TEST_CASE(unhandledExceptionSpecification2);
TEST_CASE(nothrowAttributeThrow); TEST_CASE(nothrowAttributeThrow);
TEST_CASE(nothrowAttributeThrow2); // #5703 TEST_CASE(nothrowAttributeThrow2); // #5703
TEST_CASE(nothrowDeclspecThrow);
} }
void check(const char code[], bool inconclusive = false) { void check(const char code[], bool inconclusive = false) {
@ -371,12 +372,10 @@ private:
void nothrowAttributeThrow() { void nothrowAttributeThrow() {
check("void func1() throw(int) { throw 1; }\n" check("void func1() throw(int) { throw 1; }\n"
"void func2() __attribute((nothrow)); void func1() { throw 1; }\n" "void func2() __attribute((nothrow)); void func2() { throw 1; }\n"
"void func3() __attribute((nothrow)); void func1() { func1(); }\n"); "void func3() __attribute((nothrow)); void func3() { func1(); }\n");
TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in __attribute__((nothrow)) function.\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", "[test.cpp:3]: (error) Exception thrown in __attribute__((nothrow)) function.\n", errout.str());
"",
errout.str());
// avoid false positives // avoid false positives
check("const char *func() __attribute((nothrow)); void func1() { return 0; }\n"); check("const char *func() __attribute((nothrow)); void func1() { return 0; }\n");
@ -392,6 +391,17 @@ private:
ASSERT_EQUALS("", errout.str()); 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) REGISTER_TEST(TestExceptionSafety)

View File

@ -236,6 +236,7 @@ private:
TEST_CASE(noexceptFunction4); TEST_CASE(noexceptFunction4);
TEST_CASE(nothrowAttributeFunction); TEST_CASE(nothrowAttributeFunction);
TEST_CASE(nothrowDeclspecFunction);
} }
void array() const { 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) REGISTER_TEST(TestSymbolDatabase)