From de26c9b513a59860c666ea76410410dce96997fa Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Sun, 22 Mar 2026 11:25:08 +0100 Subject: [PATCH 1/4] Fix #14613 Crash in compilePrecedence2() (function called requires) --- lib/tokenlist.cpp | 8 +++++--- test/testtokenize.cpp | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) 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..768b0c766c4 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -8328,6 +8328,19 @@ class TestTokenizer : public TestFixture { void cppKeywordInCSource() { ASSERT_NO_THROW(tokenizeAndStringify("int throw() {}", dinit(TokenizeOptions, $.cpp = false))); + + ASSERT_NO_THROW(tokenizeAndStringify("void requires(const char*);\n" // #14613 + "void f() { requires(\"abc\"); }\n", + dinit(TokenizeOptions, $.cpp = false))); + + ASSERT_NO_THROW(tokenizeAndStringify("void requires(const char*);\n" + "void f() { requires(\"abc\"); }\n", + dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP17))); + + ASSERT_THROW_INTERNAL(tokenizeAndStringify("void requires(const char*);\n" + "void f() { requires(\"abc\"); }\n", + dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP20)), + AST); } void cppcast() { From 05ecbcef9ea5ce12c1ae97f606e024c4ac91c9ac Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Sun, 22 Mar 2026 11:27:38 +0100 Subject: [PATCH 2/4] Format --- test/testtokenize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 768b0c766c4..381cc4ec91c 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -8340,7 +8340,7 @@ class TestTokenizer : public TestFixture { ASSERT_THROW_INTERNAL(tokenizeAndStringify("void requires(const char*);\n" "void f() { requires(\"abc\"); }\n", dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP20)), - AST); + AST); } void cppcast() { From b598e7aa802ed77747ee8100b9e094de4615af3a Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:15:43 +0100 Subject: [PATCH 3/4] Update testtokenize.cpp --- test/testtokenize.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 381cc4ec91c..0423f726a13 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -8329,18 +8329,11 @@ class TestTokenizer : public TestFixture { void cppKeywordInCSource() { ASSERT_NO_THROW(tokenizeAndStringify("int throw() {}", dinit(TokenizeOptions, $.cpp = false))); - ASSERT_NO_THROW(tokenizeAndStringify("void requires(const char*);\n" // #14613 - "void f() { requires(\"abc\"); }\n", - dinit(TokenizeOptions, $.cpp = false))); - - ASSERT_NO_THROW(tokenizeAndStringify("void requires(const char*);\n" - "void f() { requires(\"abc\"); }\n", - dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP17))); - - ASSERT_THROW_INTERNAL(tokenizeAndStringify("void requires(const char*);\n" - "void f() { requires(\"abc\"); }\n", - dinit(TokenizeOptions, $.cpp = true, $.cppstd = Standards::CPP20)), - AST); + 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() { From 4041204252833ccfc0422e8afe2c5211e0cfc015 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:57:13 +0100 Subject: [PATCH 4/4] Update testtokenize.cpp --- test/testtokenize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 0423f726a13..a10ccbd433f 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -8329,8 +8329,8 @@ 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"; + 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);