Remove constexpr -> const simplification (#3346)

This commit is contained in:
Paul Fultz II 2021-07-22 00:22:26 -05:00 committed by GitHub
parent 561e9174fa
commit 00eb71fd49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 113 additions and 59 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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 [:{]")) {

View File

@ -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();

View File

@ -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

View File

@ -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<double>(x); }\n"
"double fBraced(int x) { return funcBraced<int>(x); }";
const char exp[] = "const auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
"const auto funcBraced<int> = [ ] ( auto x ) { return int { x } ; } ; "
const char exp[] = "constexpr auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
"constexpr auto funcBraced<int> = [ ] ( auto x ) { return int { x } ; } ; "
"double f ( int x ) { return func<double> ( x ) ; } "
"double fBraced ( int x ) { return funcBraced<int> ( x ) ; }";
ASSERT_EQUALS(exp, tok(code));
@ -2946,8 +2946,8 @@ private:
" func<int>(x);\n"
" func<double>(x);\n"
"}";
const char exp[] = "const auto func<int> = [ ] ( auto x ) { return int ( x ) ; } ; "
"const auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
const char exp[] = "constexpr auto func<int> = [ ] ( auto x ) { return int ( x ) ; } ; "
"constexpr auto func<double> = [ ] ( auto x ) { return double ( x ) ; } ; "
"void foo ( ) { "
"func<int> ( x ) ; "
"func<double> ( x ) ; "
@ -3084,19 +3084,19 @@ private:
"a<int> 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<int> { "
"template < typename b > const decltype ( auto ) operator() ( b && ) const ; "
"template < typename b > constexpr decltype ( auto ) operator() ( b && ) const ; "
"} ;";
const char act[] = "struct a<int> ; "
"a<int> 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<int> { "
"template < typename b > const decltype ( auto ) operator() ( b && ) const ; "
"template < typename b > constexpr decltype ( auto ) operator() ( b && ) const ; "
"} ; "
"const decltype ( auto ) a<int> :: operator() ( b && ) const { }";
"constexpr decltype ( auto ) a<int> :: operator() ( b && ) const { }";
TODO_ASSERT_EQUALS(exp, act, tok(code));
}
{
@ -3270,7 +3270,7 @@ private:
" return foo<TrueFalse>();\n"
"}";
const char exp[] = "struct TrueFalse { "
"static const bool v ( ) { return true ; } "
"static constexpr bool v ( ) { return true ; } "
"} ; "
"int global ; "
"int foo<TrueFalse> ( ) ; "
@ -3538,11 +3538,11 @@ private:
"static_assert(!e<f<char>>());\n"
"}";
const char exp[] = "namespace a { "
"const bool e<f<char>> ( ) ; "
"constexpr bool e<f<char>> ( ) ; "
"class f<char> ; "
"static_assert ( ! e<f<char>> ( ) ) ; } "
"class a :: f<char> { f<char> ( a :: f < b :: d > ) ; } ; "
"const bool a :: e<f<char>> ( ) { return false ; }";
"constexpr bool a :: e<f<char>> ( ) { return false ; }";
ASSERT_EQUALS(exp, tok(code));
}
}
@ -3584,7 +3584,7 @@ private:
"using A3 = enum B3 {b = 0;};\n"
"A3<int> 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 <long Num>\n"
"constexpr bool foo<bar<Num> > = true;\n"));
@ -5974,28 +5974,28 @@ private:
{
const char code[] = "template<class T> constexpr T pi = T(3.1415926535897932385L);\n"
"float x = pi<float>;";
const char expected[] = "const float pi<float> = float ( 3.1415926535897932385L ) ; "
const char expected[] = "constexpr float pi<float> = float ( 3.1415926535897932385L ) ; "
"float x ; x = pi<float> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class> constexpr float pi = float(3.1415926535897932385L);\n"
"float x = pi<float>;";
const char expected[] = "const float pi<float> = float ( 3.1415926535897932385L ) ; "
const char expected[] = "constexpr float pi<float> = float ( 3.1415926535897932385L ) ; "
"float x ; x = pi<float> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class T = float> constexpr T pi = T(3.1415926535897932385L);\n"
"float x = pi<float>;";
const char expected[] = "const float pi<float> = float ( 3.1415926535897932385L ) ; "
const char expected[] = "constexpr float pi<float> = float ( 3.1415926535897932385L ) ; "
"float x ; x = pi<float> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class T = float> constexpr T pi = T(3.1415926535897932385L);\n"
"float x = pi<>;";
const char expected[] = "const float pi<float> = float ( 3.1415926535897932385L ) ; "
const char expected[] = "constexpr float pi<float> = float ( 3.1415926535897932385L ) ; "
"float x ; x = pi<float> ;";
ASSERT_EQUALS(expected, tok(code));
}
@ -6005,35 +6005,35 @@ private:
{
const char code[] = "template<class T, int N> constexpr T foo = T(N*N);\n"
"float x = foo<float,7>;";
const char expected[] = "const float foo<float,7> = float ( 49 ) ; "
const char expected[] = "constexpr float foo<float,7> = float ( 49 ) ; "
"float x ; x = foo<float,7> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class,int> constexpr float foo = float(7);\n"
"float x = foo<float,7>;";
const char expected[] = "const float foo<float,7> = float ( 7 ) ; "
const char expected[] = "constexpr float foo<float,7> = float ( 7 ) ; "
"float x ; x = foo<float,7> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class T = float, int N = 7> constexpr T foo = T(7);\n"
"double x = foo<double, 14>;";
const char expected[] = "const double foo<double,14> = double ( 7 ) ; "
const char expected[] = "constexpr double foo<double,14> = double ( 7 ) ; "
"double x ; x = foo<double,14> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class T = float, int N = 7> constexpr T foo = T(7);\n"
"float x = foo<>;";
const char expected[] = "const float foo<float,7> = float ( 7 ) ; "
const char expected[] = "constexpr float foo<float,7> = float ( 7 ) ; "
"float x ; x = foo<float,7> ;";
ASSERT_EQUALS(expected, tok(code));
}
{
const char code[] = "template<class T = float, int N = 7> constexpr T foo = T(7);\n"
"double x = foo<double>;";
const char expected[] = "const double foo<double,7> = double ( 7 ) ; "
const char expected[] = "constexpr double foo<double,7> = double ( 7 ) ; "
"double x ; x = foo<double,7> ;";
ASSERT_EQUALS(expected, tok(code));
}

View File

@ -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"));

View File

@ -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));
}

View File

@ -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);");

View File

@ -4852,12 +4852,12 @@ private:
void simplifyOperatorName14() { // std::complex operator "" if
{
const char code[] = "constexpr std::complex<float> 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<float> 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));
}
}

View File

@ -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() {