diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 92c9951ed..1fd63948b 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1410,11 +1410,11 @@ bool isConstFunctionCall(const Token* ftok, const Library& library) if (const Function* f = ftok->function()) { if (f->isAttributePure() || f->isAttributeConst()) return true; - if (Function::returnsVoid(f)) - return false; // Any modified arguments if (functionModifiesArguments(f)) return false; + if (Function::returnsVoid(f)) + return false; // Member function call if (Token::simpleMatch(ftok->previous(), ".")) { if (f->isConst()) @@ -1437,8 +1437,7 @@ bool isConstFunctionCall(const Token* ftok, const Library& library) } return false; } else if (f->argumentList.empty()) { - // TODO: Check for constexpr - return false; + return f->isConstexpr(); } } else if (const Library::Function* f = library.getFunction(ftok)) { if (f->ispure) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 2f1222eed..1f2062cc0 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1985,7 +1985,10 @@ void Variable::evaluate(const Settings* settings) setFlag(fIsMutable, true); else if (tok->str() == "const") setFlag(fIsConst, true); - else if (tok->str() == "*") { + else if (tok->str() == "constexpr") { + setFlag(fIsConst, true); + setFlag(fIsStatic, true); + } else if (tok->str() == "*") { setFlag(fIsPointer, !isArray() || Token::Match(tok->previous(), "( * %name% )")); setFlag(fIsConst, false); // Points to const, isn't necessarily const itself } else if (tok->str() == "&") { @@ -2007,7 +2010,7 @@ void Variable::evaluate(const Settings* settings) tok = tok->next(); } - while (Token::Match(mTypeStartToken, "static|const|volatile %any%")) + while (Token::Match(mTypeStartToken, "static|const|constexpr|volatile %any%")) mTypeStartToken = mTypeStartToken->next(); while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile")) mTypeEndToken = mTypeEndToken->previous(); @@ -2300,6 +2303,11 @@ const Token *Function::setFlags(const Token *tok1, const Scope *scope) isFriend(true); } + // constexpr function + else if (tok1->str() == "constexpr") { + isConstexpr(true); + } + // Function template else if (tok1->link() && tok1->str() == ">" && Token::simpleMatch(tok1->link()->previous(), "template <")) { templateDef = tok1->link()->previous(); @@ -4309,7 +4317,7 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, con } // skip const|volatile|static|mutable|extern - while (tok->isKeyword() && Token::Match(tok, "const|volatile|static|mutable|extern")) { + while (tok->isKeyword() && Token::Match(tok, "const|constexpr|volatile|static|mutable|extern")) { tok = tok->next(); } @@ -6126,7 +6134,7 @@ static const Token * parsedecl(const Token *type, ValueType * const valuetype, V type->type() && type->type()->isTypeAlias() && type->type()->typeStart && type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType) parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings); - else if (type->str() == "const") + else if (Token::Match(type, "const|constexpr")) valuetype->constness |= (1 << (valuetype->pointer - pointer0)); else if (settings->clang && type->str().size() > 2 && type->str().find("::") < type->str().find("<")) { TokenList typeTokens(settings); diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 9acdd18a5..c4545411b 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -723,6 +723,7 @@ class CPPCHECKLIB Function { fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type fIsEscapeFunction = (1 << 22), ///< @brief Function throws or exits fIsInlineKeyword = (1 << 23), ///< @brief Function has "inline" keyword + fIsConstexpr = (1 << 24), ///< @brief is constexpr }; /** @@ -889,6 +890,13 @@ public: void isEscapeFunction(bool state) { setFlag(fIsEscapeFunction, state); } + + bool isConstexpr() const { + return getFlag(fIsConstexpr); + } + void isConstexpr(bool state) { + setFlag(fIsConstexpr, state); + } bool isSafe(const Settings *settings) const; const Token *tokenDef; ///< function name token in class definition diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 07dc51631..954a0e804 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -7259,15 +7259,15 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co //check if variable is declared 'const' or 'static' or both while (tok2) { - if (!Token::Match(tok2, "const|static") && Token::Match(tok2, "%type% const|static")) { + if (!Token::Match(tok2, "const|static|constexpr") && Token::Match(tok2, "%type% const|static")) { tok2 = tok2->next(); ++typelen; } - if (tok2->str() == "const") + if (Token::Match(tok2, "const|constexpr")) isconst = true; - else if (tok2->str() == "static") + else if (Token::Match(tok2, "static|constexpr")) isstatic = true; else if (Token::Match(tok2, "%type% :: %type%")) { @@ -11202,11 +11202,6 @@ void Tokenizer::simplifyKeyword() } else if (cpp11) { - if (tok->str() == "constexpr") { - tok->originalName(tok->str()); - tok->str("const"); - } - // final: // 1) struct name final { }; <- struct is final if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) { diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index aa81112ea..74c80f5d6 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -123,6 +123,7 @@ void TokenList::determineCppC() //mKeywords.insert("bool"); // type mKeywords.insert("catch"); mKeywords.insert("class"); + mKeywords.insert("constexpr"); mKeywords.insert("const_cast"); mKeywords.insert("decltype"); mKeywords.insert("delete"); @@ -666,7 +667,7 @@ static bool iscpp11init_impl(const Token * const tok) endtok = nameToken->linkAt(1); else return false; - if (Token::Match(nameToken, "else|try|do|const|override|volatile|&|&&")) + if (Token::Match(nameToken, "else|try|do|const|constexpr|override|volatile|&|&&")) return false; if (Token::simpleMatch(nameToken->previous(), "namespace")) return false; @@ -956,7 +957,7 @@ static void compilePrecedence2(Token *&tok, AST_state& state) if (Token::simpleMatch(squareBracket->link(), "] (")) { Token* const roundBracket = squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); - while (Token::Match(curlyBracket, "mutable|const")) + while (Token::Match(curlyBracket, "mutable|const|constexpr")) curlyBracket = curlyBracket->next(); if (Token::simpleMatch(curlyBracket, "noexcept (")) curlyBracket = curlyBracket->linkAt(1)->next(); diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index ae1a455a6..72a307240 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -2178,9 +2178,8 @@ struct ValueFlowAnalyzer : Analyzer { // bailout: global non-const variables if (isGlobal() && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { - // TODO: Check for constexpr functions if (tok->function()) { - if (!isConstFunctionCall(tok, getSettings()->library)) + if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings()->library)) return Action::Invalid; } else if (getSettings()->library.getFunction(tok)) { // Assume library function doesn't modify user-global variables diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index bbb571f32..954674356 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -2296,10 +2296,10 @@ private: "long f1 = fib<1>;\n" "long f2 = fib<2>;\n" "long f3 = fib<3>;"; - const char exp[] = "const long fib<2> = fib<1> + fib<0> ; " - "const long fib<3> = fib<2> + fib<1> ; " - "const long fib<0> = 0 ; " - "const long fib<1> = 1 ; " + const char exp[] = "constexpr long fib<2> = fib<1> + fib<0> ; " + "constexpr long fib<3> = fib<2> + fib<1> ; " + "constexpr long fib<0> = 0 ; " + "constexpr long fib<1> = 1 ; " "long f0 ; f0 = fib<0> ; " "long f1 ; f1 = fib<1> ; " "long f2 ; f2 = fib<2> ; " @@ -2314,12 +2314,12 @@ private: "template<>\n" " constexpr long fib<1> = 1;\n" "long f5 = fib<5>;\n"; - const char exp[] = "const long fib<5> = fib<4> + fib<3> ; " - "const long fib<4> = fib<3> + fib<2> ; " - "const long fib<3> = fib<2> + fib<1> ; " - "const long fib<2> = fib<1> + fib<0> ; " - "const long fib<0> = 0 ; " - "const long fib<1> = 1 ; " + const char exp[] = "constexpr long fib<5> = fib<4> + fib<3> ; " + "constexpr long fib<4> = fib<3> + fib<2> ; " + "constexpr long fib<3> = fib<2> + fib<1> ; " + "constexpr long fib<2> = fib<1> + fib<0> ; " + "constexpr long fib<0> = 0 ; " + "constexpr long fib<1> = 1 ; " "long f5 ; f5 = fib<5> ;"; ASSERT_EQUALS(exp, tok(code)); } @@ -2933,8 +2933,8 @@ private: "constexpr auto funcBraced = [](auto x){ return T{x};};\n" "double f(int x) { return func(x); }\n" "double fBraced(int x) { return funcBraced(x); }"; - const char exp[] = "const auto func = [ ] ( auto x ) { return double ( x ) ; } ; " - "const auto funcBraced = [ ] ( auto x ) { return int { x } ; } ; " + const char exp[] = "constexpr auto func = [ ] ( auto x ) { return double ( x ) ; } ; " + "constexpr auto funcBraced = [ ] ( auto x ) { return int { x } ; } ; " "double f ( int x ) { return func ( x ) ; } " "double fBraced ( int x ) { return funcBraced ( x ) ; }"; ASSERT_EQUALS(exp, tok(code)); @@ -2946,8 +2946,8 @@ private: " func(x);\n" " func(x);\n" "}"; - const char exp[] = "const auto func = [ ] ( auto x ) { return int ( x ) ; } ; " - "const auto func = [ ] ( auto x ) { return double ( x ) ; } ; " + const char exp[] = "constexpr auto func = [ ] ( auto x ) { return int ( x ) ; } ; " + "constexpr auto func = [ ] ( auto x ) { return double ( x ) ; } ; " "void foo ( ) { " "func ( x ) ; " "func ( x ) ; " @@ -3084,19 +3084,19 @@ private: "a c ; " "template < typename d > " "template < typename b > " - "const decltype ( auto ) a < d > :: operator() ( b && ) const { } " + "constexpr decltype ( auto ) a < d > :: operator() ( b && ) const { } " "struct a { " - "template < typename b > const decltype ( auto ) operator() ( b && ) const ; " + "template < typename b > constexpr decltype ( auto ) operator() ( b && ) const ; " "} ;"; const char act[] = "struct a ; " "a c ; " "template < typename d > " "template < typename b > " - "const decltype ( auto ) a < d > :: operator() ( b && ) const { } " + "constexpr decltype ( auto ) a < d > :: operator() ( b && ) const { } " "struct a { " - "template < typename b > const decltype ( auto ) operator() ( b && ) const ; " + "template < typename b > constexpr decltype ( auto ) operator() ( b && ) const ; " "} ; " - "const decltype ( auto ) a :: operator() ( b && ) const { }"; + "constexpr decltype ( auto ) a :: operator() ( b && ) const { }"; TODO_ASSERT_EQUALS(exp, act, tok(code)); } { @@ -3270,7 +3270,7 @@ private: " return foo();\n" "}"; const char exp[] = "struct TrueFalse { " - "static const bool v ( ) { return true ; } " + "static constexpr bool v ( ) { return true ; } " "} ; " "int global ; " "int foo ( ) ; " @@ -3538,11 +3538,11 @@ private: "static_assert(!e>());\n" "}"; const char exp[] = "namespace a { " - "const bool e> ( ) ; " + "constexpr bool e> ( ) ; " "class f ; " "static_assert ( ! e> ( ) ) ; } " "class a :: f { f ( a :: f < b :: d > ) ; } ; " - "const bool a :: e> ( ) { return false ; }"; + "constexpr bool a :: e> ( ) { return false ; }"; ASSERT_EQUALS(exp, tok(code)); } } @@ -3584,7 +3584,7 @@ private: "using A3 = enum B3 {b = 0;};\n" "A3 a3;"; const char exp[] = "template < int N > " - "using A1 = struct B1 { static const auto value = N ; } ; " + "using A1 = struct B1 { static auto constexpr value = N ; } ; " "A1 < 0 > a1 ; " "template < class T > " "using A2 = struct B2 { void f ( T ) { } } ; " @@ -4807,7 +4807,7 @@ private: //both of these should work but in cppcheck 2.1 only the first option will work (ticket #9843) { - const std::string expected = "template < long Num > const bool foo < bar < Num > > = true ;"; + const std::string expected = "template < long Num > constexpr bool foo < bar < Num > > = true ;"; ASSERT_EQUALS(expected, tok("template \n" "constexpr bool foo > = true;\n")); @@ -5974,28 +5974,28 @@ private: { const char code[] = "template constexpr T pi = T(3.1415926535897932385L);\n" "float x = pi;"; - const char expected[] = "const float pi = float ( 3.1415926535897932385L ) ; " + const char expected[] = "constexpr float pi = float ( 3.1415926535897932385L ) ; " "float x ; x = pi ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr float pi = float(3.1415926535897932385L);\n" "float x = pi;"; - const char expected[] = "const float pi = float ( 3.1415926535897932385L ) ; " + const char expected[] = "constexpr float pi = float ( 3.1415926535897932385L ) ; " "float x ; x = pi ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr T pi = T(3.1415926535897932385L);\n" "float x = pi;"; - const char expected[] = "const float pi = float ( 3.1415926535897932385L ) ; " + const char expected[] = "constexpr float pi = float ( 3.1415926535897932385L ) ; " "float x ; x = pi ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr T pi = T(3.1415926535897932385L);\n" "float x = pi<>;"; - const char expected[] = "const float pi = float ( 3.1415926535897932385L ) ; " + const char expected[] = "constexpr float pi = float ( 3.1415926535897932385L ) ; " "float x ; x = pi ;"; ASSERT_EQUALS(expected, tok(code)); } @@ -6005,35 +6005,35 @@ private: { const char code[] = "template constexpr T foo = T(N*N);\n" "float x = foo;"; - const char expected[] = "const float foo = float ( 49 ) ; " + const char expected[] = "constexpr float foo = float ( 49 ) ; " "float x ; x = foo ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr float foo = float(7);\n" "float x = foo;"; - const char expected[] = "const float foo = float ( 7 ) ; " + const char expected[] = "constexpr float foo = float ( 7 ) ; " "float x ; x = foo ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr T foo = T(7);\n" "double x = foo;"; - const char expected[] = "const double foo = double ( 7 ) ; " + const char expected[] = "constexpr double foo = double ( 7 ) ; " "double x ; x = foo ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr T foo = T(7);\n" "float x = foo<>;"; - const char expected[] = "const float foo = float ( 7 ) ; " + const char expected[] = "constexpr float foo = float ( 7 ) ; " "float x ; x = foo ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template constexpr T foo = T(7);\n" "double x = foo;"; - const char expected[] = "const double foo = double ( 7 ) ; " + const char expected[] = "constexpr double foo = double ( 7 ) ; " "double x ; x = foo ;"; ASSERT_EQUALS(expected, tok(code)); } diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index 00bfdc16e..a1747fd4b 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -4894,7 +4894,7 @@ private: ASSERT_EQUALS("int foo ( ) { }", tok("inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__inline int foo ( ) { }", true)); ASSERT_EQUALS("int foo ( ) { }", tok("__forceinline int foo ( ) { }", true)); - ASSERT_EQUALS("const int foo ( ) { }", tok("constexpr int foo() { }", true)); + ASSERT_EQUALS("constexpr int foo ( ) { }", tok("constexpr int foo() { }", true)); ASSERT_EQUALS("void f ( ) { int final [ 10 ] ; }", tok("void f() { int final[10]; }", true)); ASSERT_EQUALS("int * p ;", tok("int * __restrict p;", "test.c")); ASSERT_EQUALS("int * * p ;", tok("int * __restrict__ * p;", "test.c")); diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 181cce67d..3421781b6 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -2584,7 +2584,7 @@ private: " constexpr const foo &c_str() const noexcept { return _a; }\n" "};"; - const char exp [] = "class c { char _a [ 4 ] ; const const char ( & c_str ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; + const char exp [] = "class c { char _a [ 4 ] ; const constexpr char ( & c_str ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; ASSERT_EQUALS(exp, tok(code, false)); } @@ -2595,7 +2595,7 @@ private: " constexpr operator foo &() const noexcept { return _a; }\n" "};"; - const char actual [] = "class c { char _a [ 4 ] ; const operatorchar ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; + const char actual [] = "class c { char _a [ 4 ] ; constexpr operatorchar ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; const char exp [] = "class c { char _a [ 4 ] ; const operator char ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; TODO_ASSERT_EQUALS(exp, actual, tok(code, false)); } @@ -2607,7 +2607,7 @@ private: " constexpr operator const foo &() const noexcept { return _a; }\n" "};"; - const char actual [] = "class c { char _a [ 4 ] ; const operatorconstchar ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; + const char actual [] = "class c { char _a [ 4 ] ; constexpr operatorconstchar ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; const char exp [] = "class c { char _a [ 4 ] ; const operator const char ( & ( ) const noexcept ) [ 4 ] { return _a ; } } ;"; TODO_ASSERT_EQUALS(exp, actual, tok(code, false)); } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index a5401da65..4c7b2ef40 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -165,6 +165,7 @@ private: TEST_CASE(isVariablePointerToConstVolatilePointer); TEST_CASE(isVariableMultiplePointersAndQualifiers); TEST_CASE(variableVolatile); + TEST_CASE(variableConstexpr); TEST_CASE(isVariableDecltype); TEST_CASE(VariableValueType1); @@ -208,6 +209,7 @@ private: TEST_CASE(functionDeclarationTemplate); TEST_CASE(functionDeclarations); TEST_CASE(functionDeclarations2); + TEST_CASE(constexprFunction); TEST_CASE(constructorInitialization); TEST_CASE(memberFunctionOfUnknownClassMacro1); TEST_CASE(memberFunctionOfUnknownClassMacro2); @@ -1317,6 +1319,20 @@ private: ASSERT(y->variable()->isVolatile()); } + void variableConstexpr() { + GET_SYMBOL_DB("constexpr int x = 16;"); + + const Token *x = Token::findsimplematch(tokenizer.tokens(), "x"); + ASSERT(x); + ASSERT(x->variable()); + ASSERT(x->variable()->isConst()); + ASSERT(x->variable()->isStatic()); + ASSERT(x->valueType()); + ASSERT(x->valueType()->pointer == 0); + ASSERT(x->valueType()->constness == 1); + ASSERT(x->valueType()->reference == Reference::None); + } + void isVariableDecltype() { GET_SYMBOL_DB("int x;\n" "decltype(x) a;\n" @@ -1877,6 +1893,24 @@ private: ASSERT(parenthesis->valueType()->type == ValueType::Type::CONTAINER); } + void constexprFunction() { + GET_SYMBOL_DB_STD("constexpr int foo();"); + + // 1 scopes: Global + ASSERT(db && db->scopeList.size() == 1); + + const Scope *scope = &db->scopeList.front(); + + ASSERT(scope && scope->functionList.size() == 1); + + const Function *foo = &scope->functionList.front(); + + ASSERT(foo); + ASSERT(foo->tokenDef->str() == "foo"); + ASSERT(!foo->hasBody()); + ASSERT(foo->isConstexpr()); + } + void constructorInitialization() { GET_SYMBOL_DB("std::string logfile;\n" "std::ofstream log(logfile.c_str(), std::ios::out);"); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index beddd58cc..73a475da1 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -4852,12 +4852,12 @@ private: void simplifyOperatorName14() { // std::complex operator "" if { const char code[] = "constexpr std::complex operator\"\"if(long double __num);"; - ASSERT_EQUALS("const std :: complex < float > operator\"\"if ( long double __num ) ;", + ASSERT_EQUALS("constexpr std :: complex < float > operator\"\"if ( long double __num ) ;", tokenizeAndStringify(code)); } { const char code[] = "constexpr std::complex operator\"\"if(long double __num) { }"; - ASSERT_EQUALS("const std :: complex < float > operator\"\"if ( long double __num ) { }", + ASSERT_EQUALS("constexpr std :: complex < float > operator\"\"if ( long double __num ) { }", tokenizeAndStringify(code)); } } diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 34231298f..e70835c2a 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -2803,6 +2803,16 @@ private: " x->c();\n" "}\n"; ASSERT_EQUALS(true, testValueOfX(code, 8U, 0)); + + code = "constexpr int f();\n" + "int g() {\n" + " if (f() == 1) {\n" + " int x = f();\n" + " return x;\n" + " }\n" + " return 0;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 1)); } void valueFlowAfterConditionExpr() {