From ac505afe692e0410fd6231e758f0e35f056c51c1 Mon Sep 17 00:00:00 2001 From: dummyunit Date: Sun, 2 May 2021 22:34:28 +0300 Subject: [PATCH] Fixed #9729 (AST broken: lambda with noexcept keyword) (#3243) The previous fix for the issue (43b58dbc9e) didn't seem to actually fix it because it added a check for noexcept without a condition, but when AST is created noexcept always has a condition due to simplification from "noexcept" to "noexcept(true)" in Tokenizer::simplifyKeyword(). The issue from the ticket couldn't be reproduced neither on 43b58dbc9e nor on the previous commit, so it is hard to tell whether the fix was effective or not. The issue appeared again after a refactoring of AST code in ac67049661. Test added with the original fix was unable to catch that because it used testAst() helper function which skips most simplification steps. To fix the issue we now check for noexcept with a condition and add a proper regression test that: 1. Uses tokenizeAndStringify() to ensure that all simplifications are performed before AST is created. 2. Parses the code snippet from the ticket, as having "if (cond)" is crucial to reproducing the original issue (internalAstError). Also fix AST creation for lambdas that have both constexpr and mutable keywords. --- lib/tokenlist.cpp | 4 +++- test/testtokenize.cpp | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index f5ad39367..4875e6c01 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -951,8 +951,10 @@ static void compilePrecedence2(Token *&tok, AST_state& state) if (Token::simpleMatch(squareBracket->link(), "] (")) { Token* const roundBracket = squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); - if (Token::Match(curlyBracket, "mutable|const|noexcept")) + while (Token::Match(curlyBracket, "mutable|const")) curlyBracket = curlyBracket->next(); + if (Token::simpleMatch(curlyBracket, "noexcept (")) + curlyBracket = curlyBracket->linkAt(1)->next(); if (curlyBracket && curlyBracket->originalName() == "->") curlyBracket = findTypeEnd(curlyBracket->next()); if (curlyBracket && curlyBracket->str() == "{") { diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 24eadcca9..041f6e708 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -5932,8 +5932,16 @@ private: ASSERT_EQUALS("x{(a&[( ai=", testAst("x([&a](int i){a=i;});")); ASSERT_EQUALS("{([(return 0return", testAst("return [](){ return 0; }();")); - // noexcept - ASSERT_EQUALS("x{([( ai=", testAst("x([](int i)noexcept{a=i;});")); + // noexcept (which if simplified to always have a condition by the time AST is created) + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) noexcept(true) { a=i; });")); + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) mutable noexcept(true) { a=i; });")); + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) const noexcept(true) { a=i; });")); + + // both mutable and constexpr (which is simplified to 'const' by the time AST is created) + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) const mutable { a=i; });")); + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) mutable const { a=i; });")); + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) const mutable noexcept(true) { a=i; });")); + ASSERT_EQUALS("x{([( ai=", testAst("x([](int i) mutable const noexcept(true) { a=i; });")); // -> ASSERT_EQUALS("{([(return 0return", testAst("return []() -> int { return 0; }();")); @@ -5989,6 +5997,9 @@ private: // Lambda capture expression (C++14) ASSERT_EQUALS("a{b1=[= c2=", testAst("a = [b=1]{c=2;};")); + + // #9729 + ASSERT_NO_THROW(tokenizeAndStringify("void foo() { bar([]() noexcept { if (0) {} }); }")); } void astcase() {