diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 97d6f2ef095..98113b9654b 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -25,6 +25,7 @@ #include "symboldatabase.h" #include "token.h" +#include #include #include #include @@ -239,6 +240,13 @@ void CheckExceptionSafety::catchExceptionByValueError(const Token *tok) "as a (const) reference which is usually recommended in C++.", CWE398, Certainty::normal); } +static bool isPossibleBool(const Token *tok, bool b) +{ + const std::list &values = tok->values(); + return std::any_of(values.cbegin(), values.cend(), [&](const ValueFlow::Value &value) { + return value.isIntValue() && value.isPossible() && value.intvalue == static_cast(b); + }); +} static const Token * functionThrowsRecursive(const Function * function, std::set & recursive) { @@ -251,6 +259,19 @@ static const Token * functionThrowsRecursive(const Function * function, std::set for (const Token *tok = function->functionScope->bodyStart->next(); tok != function->functionScope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "if")) { + tok = tok->astParent(); + const Token *condTok = tok->astOperand2(); + // check if condition is possibly false, since template arguments are not set to known + if (isPossibleBool(condTok, false)) { + tok = tok->link()->next(); + if (Token::simpleMatch(tok, "{")) + tok = tok->link(); + else while (tok && !Token::simpleMatch(tok, ";")) + tok = tok->next(); + continue; + } + } if (Token::simpleMatch(tok, "try {")) tok = tok->linkAt(1); // skip till start of catch clauses if (tok->str() == "throw") diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 0c43cb6c689..2772fbb80dc 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -361,6 +361,27 @@ class TestExceptionSafety : public TestFixture { // avoid false positives check("const char *func() throw() { return 0; }"); ASSERT_EQUALS("", errout_str()); + + + // #11691: FP throwInNoexceptFunction with if constexpr in template + check("template\n" + "static void* MyNew(size_t Size) noexcept(IsNoThrow) {\n" + " void* Memory = std::malloc(Size);\n" + " if (!Memory) {\n" + " if constexpr (!IsNoThrow) {\n" + " throw std::bad_alloc();\n" + " }\n" + " }\n" + " return Memory;\n" + "}\n" + "void* AllocateMemory(size_t Size, bool UseNoThrow) {\n" + " if (UseNoThrow) {\n" + " return MyNew(Size);\n" + " } else {\n" + " return MyNew(Size);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void unhandledExceptionSpecification1() { // #4800