From ae96491d6c58cdf7f682cd1fb87e0066ef66dad5 Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Sun, 4 May 2014 20:47:20 +0200 Subject: [PATCH] Fixed #5756 (declspec(nothrow) not supported) --- lib/checkexceptionsafety.cpp | 7 +++++++ lib/checkexceptionsafety.h | 7 +++++++ lib/symboldatabase.cpp | 2 ++ lib/symboldatabase.h | 3 +++ lib/token.cpp | 3 +++ lib/token.h | 7 +++++++ lib/tokenize.cpp | 16 +++++++++++++--- lib/tokenlist.cpp | 2 ++ test/testexceptionsafety.cpp | 22 ++++++++++++++++------ test/testsymboldatabase.cpp | 14 ++++++++++++++ 10 files changed, 74 insertions(+), 9 deletions(-) diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 3e16e390f..9d8c9008a 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -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); + } } } diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index 895283207..c42029f52 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -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"; } }; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 53e5ce425..feed04699 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1365,6 +1365,7 @@ Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, co const_cast(tok)->isAttributePure(i->tokenDef->isAttributePure()); const_cast(tok)->isAttributeConst(i->tokenDef->isAttributeConst()); const_cast(tok)->isAttributeNothrow(i->tokenDef->isAttributeNothrow()); + const_cast(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; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index bed760c6d..3d3e128a9 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -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 diff --git a/lib/token.cpp b/lib/token.cpp index 9eebbd91e..741ade61f 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -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; diff --git a/lib/token.h b/lib/token.h index 8b7792595..8db24ebb1 100644 --- a/lib/token.h +++ b/lib/token.h @@ -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. */ diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 50da72b17..50dc81370 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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(); } diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 58ee1af88..07413d588 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -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; } diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 9a22d4511..0246a358e 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -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) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 2df61ea5a..000898dd1 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -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)