From 28a7bb63ec5ae59ddbd0c22fea3bba86f760fbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 25 Apr 2021 14:37:27 +0200 Subject: [PATCH] Parser; simplify (break out) init expression from if/switch/range-for --- lib/tokenize.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++ lib/tokenize.h | 3 +++ test/testtokenize.cpp | 41 ++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index f7a823900..3c97405ae 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -5143,6 +5143,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) elseif(); + simplifyIfSwitchForInit(); + simplifyOverloadedOperators(); validate(); @@ -8593,6 +8595,66 @@ void Tokenizer::elseif() } +void Tokenizer::simplifyIfSwitchForInit() +{ + if (!isCPP() || mSettings->standards.cpp < Standards::CPP17) + return; + + const bool forInit = (mSettings->standards.cpp >= Standards::CPP20); + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "if|switch|for (")) + continue; + + Token *semicolon = tok->tokAt(2); + while (!Token::Match(semicolon, "[;)]")) { + if (semicolon->str() == "(") + semicolon = semicolon->link(); + semicolon = semicolon->next(); + } + if (semicolon->str() != ";") + continue; + + if (tok->str() == "for") { + if (!forInit) + continue; + + // Is it a for range.. + const Token *tok2 = semicolon->next(); + bool rangeFor = false; + while (!Token::Match(tok2, "[;)]")) { + if (tok2->str() == "(") + tok2 = tok2->link(); + else if (!rangeFor && tok2->str() == "?") + break; + else if (tok2->str() == ":") + rangeFor = true; + tok2 = tok2->next(); + } + if (!rangeFor || tok2->str() != ")") + continue; + } + + Token *endpar = tok->linkAt(1); + if (!Token::simpleMatch(endpar, ") {")) + continue; + + Token *endscope = endpar->linkAt(1); + if (Token::simpleMatch(endscope, "} else {")) + endscope = endscope->linkAt(2); + + // Simplify, the initialization expression is broken out.. + semicolon->insertToken(tok->str()); + semicolon->next()->insertToken("("); + Token::createMutualLinks(semicolon->next()->next(), endpar); + tok->deleteNext(); + tok->str("{"); + endscope->insertToken("}"); + Token::createMutualLinks(tok, endscope->next()); + } +} + + bool Tokenizer::simplifyRedundantParentheses() { bool ret = false; diff --git a/lib/tokenize.h b/lib/tokenize.h index a11e113c7..da4861b1b 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -449,6 +449,9 @@ public: /** Simplify "if else" */ void elseif(); + /** Simplify C++17/C++20 if/switch/for initialization expression */ + void simplifyIfSwitchForInit(); + /** Simplify conditions * @return true if something is modified * false if nothing is done. diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index db8519f62..e7d8dc325 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -281,7 +281,6 @@ private: TEST_CASE(bitfields14); // ticket #4561 (segfault for 'class a { signals: };') TEST_CASE(bitfields15); // ticket #7747 (enum Foo {A,B}:4;) TEST_CASE(bitfields16); // Save bitfield bit count - TEST_CASE(bitfields17); // for (A; b:c); TEST_CASE(simplifyNamespaceStd); @@ -422,6 +421,11 @@ private: TEST_CASE(simplifyCoroutines); TEST_CASE(simplifySpaceshipOperator); + + TEST_CASE(simplifyIfSwitchForInit1); + TEST_CASE(simplifyIfSwitchForInit2); + TEST_CASE(simplifyIfSwitchForInit3); + TEST_CASE(simplifyIfSwitchForInit4); } std::string tokenizeAndStringify(const char code[], bool expand = true, Settings::PlatformType platform = Settings::Native, const char* filename = "test.cpp", bool cpp11 = true) { @@ -3953,13 +3957,6 @@ private: ASSERT_EQUALS(1, x->bits()); } - void bitfields17() { - Settings settings; - settings.standards.cpp = Standards::CPP20; - const char code[] = "void f() { for (a;b:c) {} }"; - ASSERT_EQUALS("void f ( ) { for ( a ; b : c ) { } }", tokenizeAndStringify(code, settings)); - } - void simplifyNamespaceStd() { const char *code, *expected; @@ -6556,6 +6553,34 @@ private: ASSERT_EQUALS("; x <=> y ;", tokenizeAndStringify(";x<=>y;", settings)); } + + void simplifyIfSwitchForInit1() { + Settings settings; + settings.standards.cpp = Standards::CPP17; + const char code[] = "void f() { if (a;b) {} }"; + ASSERT_EQUALS("void f ( ) { { a ; if ( b ) { } } }", tokenizeAndStringify(code, settings)); + } + + void simplifyIfSwitchForInit2() { + Settings settings; + settings.standards.cpp = Standards::CPP20; + const char code[] = "void f() { if (a;b) {} else {} }"; + ASSERT_EQUALS("void f ( ) { { a ; if ( b ) { } else { } } }", tokenizeAndStringify(code, settings)); + } + + void simplifyIfSwitchForInit3() { + Settings settings; + settings.standards.cpp = Standards::CPP20; + const char code[] = "void f() { switch (a;b) {} }"; + ASSERT_EQUALS("void f ( ) { { a ; switch ( b ) { } } }", tokenizeAndStringify(code, settings)); + } + + void simplifyIfSwitchForInit4() { + Settings settings; + settings.standards.cpp = Standards::CPP20; + const char code[] = "void f() { for (a;b:c) {} }"; + ASSERT_EQUALS("void f ( ) { { a ; for ( b : c ) { } } }", tokenizeAndStringify(code, settings)); + } }; REGISTER_TEST(TestTokenizer)