diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 321fbb4e4..4a93f05b4 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -176,3 +176,48 @@ void CheckExceptionSafety::checkCatchExceptionByValue() 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); + } + } + } + } +} + diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index e9c8f2694..3740930a6 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -61,6 +61,8 @@ public: checkExceptionSafety.deallocThrow(); checkExceptionSafety.checkRethrowCopy(); checkExceptionSafety.checkCatchExceptionByValue(); + checkExceptionSafety.noexceptThrows(); + checkExceptionSafety.nothrowThrows(); } /** Don't throw exceptions in destructors */ @@ -75,6 +77,12 @@ public: /** @brief %Check for exceptions that are caught by value instead of by reference */ void checkCatchExceptionByValue(); + /** @brief %Check for noexcept functions that throw */ + void noexceptThrows(); + + /** @brief %Check for throw() functions that throw */ + void nothrowThrows(); + private: /** Don't throw exceptions in destructors */ void destructorsError(const Token * const tok) { @@ -100,6 +108,16 @@ private: "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) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckExceptionSafety c(0, settings, errorLogger); @@ -107,6 +125,8 @@ private: c.deallocThrowError(0, "p"); c.rethrowCopyError(0, "varname"); c.catchExceptionByValueError(0); + c.noexceptThrowError(0); + c.nothrowThrowError(0); } /** Short description of class (for --doc) */ @@ -120,7 +140,9 @@ private: "* Throwing exceptions in destructors\n" "* Throwing exception during invalid state\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"; } }; /// @} diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index c621ab476..80374846d 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -458,6 +458,72 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti 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 else if (Token::Match(end, ") const| = %any% ;")) { function.isPure = true; @@ -481,6 +547,28 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti function.isInline = 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 '{' while (end && end->str() != "{" && end->str() != ";") end = end->next(); @@ -549,11 +637,13 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti if (isFunction(tok, scope, &funcStart, &argStart)) { bool retFuncPtr = Token::simpleMatch(argStart->link(), ") ) ("); const Token* scopeBegin = argStart->link()->next(); - if (retFuncPtr) scopeBegin = scopeBegin->next()->link()->next(); if (scopeBegin->isName()) { // Jump behind 'const' or unknown Macro scopeBegin = scopeBegin->next(); + if (scopeBegin->str() == "throw") + scopeBegin = scopeBegin->next(); + if (scopeBegin->link() && scopeBegin->str() == "(") // Jump behind unknown macro of type THROW(...) scopeBegin = scopeBegin->link()->next(); } @@ -576,6 +666,29 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti else { Function* function = addGlobalFunction(scope, tok, argStart, funcStart); 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? @@ -598,6 +711,28 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti if (newFunc) { Function* func = addGlobalFunctionDecl(scope, tok, argStart, funcStart); 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; @@ -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 outerScope->isClassOrStruct())) { // or a ctor/dtor 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() && tok2->next()->link()->strAt(1) == "{") || 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; *argStart = tok->next(); 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(), "> (")) { const Token* tok2 = tok->next()->link()->next()->link(); if (Token::Match(tok2, ") const| ;|{|=") || - Token::Match(tok2, ") : ::| %var% (|::|<|{")) { + Token::Match(tok2, ") : ::| %var% (|::|<|{") || + Token::Match(tok->next()->link()->next()->link(), ") const| noexcept {|;|(")) { *funcStart = tok; *argStart = tok2->link(); return true; @@ -1772,8 +1913,12 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " isExplicit: " << (func->isExplicit ? "true" : "false") << std::endl; std::cout << " isDefault: " << (func->isDefault ? "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 << " 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 << " argDef: " << _tokenizer->list.fileLine(func->argDef) << std::endl; if (!func->isConstructor() && !func->isDestructor()) diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 37ebd6f90..6fe814fbe 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -544,8 +544,12 @@ public: isExplicit(false), isDefault(false), isDelete(false), + isNoExcept(false), + isThrow(false), isOperator(false), - retFuncPtr(false) { + retFuncPtr(false), + noexceptArg(nullptr), + throwArg(nullptr) { } const std::string &name() const { @@ -610,8 +614,12 @@ public: bool isExplicit; // is explicit bool isDefault; // is default bool isDelete; // is delete + bool isNoExcept; // is noexcept + bool isThrow; // is throw bool isOperator; // is operator 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); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index b5a23b113..4dfc54442 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -3352,9 +3352,6 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) // Simplify the operator "?:" simplifyConditionOperator(); - // remove exception specifications.. - removeExceptionSpecifications(); - // Collapse operator name tokens into single token // operator = => operator= simplifyOperatorName(); @@ -3680,7 +3677,7 @@ void Tokenizer::removeMacrosInGlobalScope() if (tok->str() == "(") { tok = tok->link(); 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(); } @@ -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 { std::stack linktok; diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 16de5bbd8..aa22aea04 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -42,6 +42,8 @@ private: TEST_CASE(rethrowCopy4); TEST_CASE(rethrowCopy5); TEST_CASE(catchExceptionByValue); + TEST_CASE(noexceptThrow); + TEST_CASE(nothrowThrow); } void check(const char code[], bool inconclusive = false) { @@ -308,6 +310,20 @@ private: "}"); 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) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 19ee4dbc5..8c03986fa 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -217,6 +217,11 @@ private: TEST_CASE(findFunction1); 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 { @@ -1980,6 +1985,104 @@ private: ASSERT_EQUALS(true, callfunc != nullptr); // not 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 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) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index f29726e81..bb339026e 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -424,13 +424,6 @@ private: TEST_CASE(createLinks); 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(simplifyConst); 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() { errout.str(""); Settings settings;