TemplateSimplifier; Better handling of c++17 fold expressions and c++20 concepts.

c++17 fold expressions are simplified to a __cppcheck_uninstantiated_fold__ if they are not instantiated.

c++20 concepts are skipped/removed by Cppcheck and these will be enforced by the compiler.
This commit is contained in:
Daniel Marjamäki 2021-04-20 15:39:04 +02:00
parent f371a92501
commit 04e9c13bc6
3 changed files with 145 additions and 8 deletions

View File

@ -32,6 +32,32 @@
#include <stack>
#include <utility>
static Token *skipRequires(Token *tok)
{
if (!Token::simpleMatch(tok, "requires"))
return tok;
while (Token::Match(tok, "%oror%|&&|requires %name%|(")) {
Token *after = tok->next();
if (after->str() == "(") {
tok = after->link()->next();
continue;
}
if (Token::Match(after, "requires (") && Token::simpleMatch(after->linkAt(1), ") {")) {
tok = after->linkAt(1)->linkAt(1)->next();
continue;
}
while (Token::Match(after, "%name% :: %name%"))
after = after->tokAt(2);
if (Token::Match(after, "%name% <")) {
after = after->next()->findClosingBracket();
tok = after ? after->next() : nullptr;
} else
break;
}
return tok;
}
namespace {
class FindToken {
public:
@ -616,13 +642,8 @@ bool TemplateSimplifier::getTemplateDeclarations()
break;
tok1 = closing->next();
}
if (!tok1)
if (!Token::Match(tok, "%any% %any%"))
syntaxError(tok);
if (!tok1->next())
syntaxError(tok);
// Some syntax checks, see #6865
if (!tok->tokAt(2))
syntaxError(tok->next());
if (tok->strAt(2)=="typename" &&
!Token::Match(tok->tokAt(3), "%name%|...|,|=|>"))
syntaxError(tok->next());
@ -3662,6 +3683,18 @@ void TemplateSimplifier::simplifyTemplates(
}
}
if (mSettings->standards.cpp >= Standards::CPP20) {
// Remove concepts/requires
// TODO concepts are not removed yet
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (!Token::Match(tok, ")|>|>> requires %name%|("))
continue;
Token *end = skipRequires(tok->next());
if (end)
Token::eraseTokens(tok, end);
}
}
mTokenizer->calculateScopes();
unsigned int passCount = 0;
@ -3820,6 +3853,26 @@ void TemplateSimplifier::simplifyTemplates(
mErrorLogger->reportErr(errmsg);
}
}
// Tweak uninstantiated C++17 fold expressions (... && args)
if (mSettings->standards.cpp >= Standards::CPP17) {
bool simplify = false;
for (Token *tok = mTokenList.front(); tok; tok = tok->next()) {
if (tok->str() == "template")
simplify = false;
if (tok->str() == "{")
simplify = true;
if (!simplify || tok->str() != "(")
continue;
if (Token::Match(tok, "( ... %op%") ||
Token::Match(tok, "( %name% %op% ...") ||
Token::Match(tok->link()->tokAt(-3), "%op% ... )") ||
Token::Match(tok->link()->tokAt(-3), "... %op% %name% )")) {
Token::eraseTokens(tok, tok->link());
tok->insertToken("__cppcheck_uninstantiated_fold__");
}
}
}
}
void TemplateSimplifier::syntaxError(const Token *tok)

View File

@ -6216,8 +6216,8 @@ static void valueFlowIteratorInfer(TokenList *tokenlist, const Settings *setting
}
static std::vector<ValueFlow::Value> getInitListSize(const Token* tok,
const Library::Container* container,
bool known = true)
const Library::Container* container,
bool known = true)
{
std::vector<const Token*> args = getArguments(tok);
if ((args.size() == 1 && astIsContainer(args[0]) && args[0]->valueType()->container == container) ||

View File

@ -282,6 +282,18 @@ private:
TEST_CASE(simplifyDecltype);
TEST_CASE(castInExpansion);
TEST_CASE(fold_expression_1);
TEST_CASE(fold_expression_2);
TEST_CASE(fold_expression_3);
TEST_CASE(fold_expression_4);
TEST_CASE(concepts1);
TEST_CASE(requires1);
TEST_CASE(requires2);
TEST_CASE(requires3);
TEST_CASE(requires4);
TEST_CASE(requires5);
}
std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) {
@ -6051,6 +6063,78 @@ private:
"class Base<C<static_cast<int>-1>> { } ;";
ASSERT_EQUALS(expected, tok(code));
}
void fold_expression_1() {
const char code[] = "template<typename... Args> bool all(Args... args) { return (... && args); }\n"
"x=all(true,false,true,true);";
const char expected[] = "template < typename ... Args > bool all ( Args ... args ) { return ( __cppcheck_uninstantiated_fold__ ) ; } x = all ( true , false , true , true ) ;";
ASSERT_EQUALS(expected, tok(code));
}
void fold_expression_2() {
const char code[] = "template<typename... Args> bool all(Args... args) { return (args && ...); }\n"
"x=all(true,false,true,true);";
const char expected[] = "template < typename ... Args > bool all ( Args ... args ) { return ( __cppcheck_uninstantiated_fold__ ) ; } x = all ( true , false , true , true ) ;";
ASSERT_EQUALS(expected, tok(code));
}
void fold_expression_3() {
const char code[] = "template<typename... Args> int foo(Args... args) { return (12 * ... * args); }\n"
"x=foo(1,2);";
const char expected[] = "template < typename ... Args > int foo ( Args ... args ) { return ( __cppcheck_uninstantiated_fold__ ) ; } x = foo ( 1 , 2 ) ;";
ASSERT_EQUALS(expected, tok(code));
}
void fold_expression_4() {
const char code[] = "template<typename... Args> int foo(Args... args) { return (args * ... * 123); }\n"
"x=foo(1,2);";
const char expected[] = "template < typename ... Args > int foo ( Args ... args ) { return ( __cppcheck_uninstantiated_fold__ ) ; } x = foo ( 1 , 2 ) ;";
ASSERT_EQUALS(expected, tok(code));
}
void concepts1() {
const char code[] = "template <my_concept T> void f(T v) {}\n"
"f<int>(123);";
const char expected[] = "void f<int> ( int v ) ; f<int> ( 123 ) ; void f<int> ( int v ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void requires1() {
const char code[] = "template <class T> requires my_concept<T> void f(T v) {}\n"
"f<int>(123);";
const char expected[] = "void f<int> ( int v ) ; f<int> ( 123 ) ; void f<int> ( int v ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void requires2() {
const char code[] = "template<class T> requires (sizeof(T) > 1 && get_value<T>()) void f(T v){}\n"
"f<int>(123);";
const char expected[] = "void f<int> ( int v ) ; f<int> ( 123 ) ; void f<int> ( int v ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void requires3() {
const char code[] = "template<class T> requires c1<T> && c2<T> void f(T v){}\n"
"f<int>(123);";
const char expected[] = "void f<int> ( int v ) ; f<int> ( 123 ) ; void f<int> ( int v ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void requires4() {
const char code[] = "template <class T> void f(T v) requires my_concept<T> {}\n"
"f<int>(123);";
const char expected[] = "void f<int> ( int v ) ; f<int> ( 123 ) ; void f<int> ( int v ) { }";
ASSERT_EQUALS(expected, tok(code));
}
void requires5() {
const char code[] = "template <class T>\n"
" requires requires (T x) { x + x; }\n"
" T add(T a, T b) { return a + b; }\n"
"add<int>(123,456);";
const char expected[] = "int add<int> ( int a , int b ) ; add<int> ( 123 , 456 ) ; int add<int> ( int a , int b ) { return a + b ; }";
ASSERT_EQUALS(expected, tok(code));
}
};
REGISTER_TEST(TestSimplifyTemplate)