diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 7f7ca25aa5c..55ca98c628c 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -1058,10 +1058,12 @@ static void compilePrecedence2(Token *&tok, AST_state& state) else compileUnaryOp(tok, state, compileExpression); tok = tok2->link()->next(); - } else if (Token::simpleMatch(tok->previous(), "requires {") - || (Token::simpleMatch(tok->previous(), ")") + } else if ((Token::simpleMatch(tok->tokAt(-1), "requires {") && tok->tokAt(-1)->isKeyword()) + || (Token::simpleMatch(tok->tokAt(-1), ")") && tok->linkAt(-1) - && Token::simpleMatch(tok->linkAt(-1)->previous(), "requires ("))) { + && Token::simpleMatch(tok->linkAt(-1)->tokAt(-1), "requires (") && tok->linkAt(-1)->tokAt(-1)->isKeyword())) { + if (!tok->link()) + throw InternalError(tok, "Syntax error, token has no link.", InternalError::AST); tok->astOperand1(state.op.top()); state.op.pop(); state.op.push(tok); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 16c9335dce7..a10ccbd433f 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -8328,6 +8328,12 @@ class TestTokenizer : public TestFixture { void cppKeywordInCSource() { ASSERT_NO_THROW(tokenizeAndStringify("int throw() {}", dinit(TokenizeOptions, $.cpp = false))); + + const char code[] = "void requires(const char*);\n" // #14613 + "void f() { requires(\"abc\"); }\n"; + ASSERT_NO_THROW(tokenizeAndStringify(code, dinit(TokenizeOptions, $.cpp = false))); + ASSERT_NO_THROW(tokenizeAndStringify(code, dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP17))); + ASSERT_THROW_INTERNAL(tokenizeAndStringify(code, dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP20)), AST); } void cppcast() {