From 41c69f8c876b16e4045f1a1397794b41935321dc Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Tue, 2 Dec 2025 11:53:11 -0500 Subject: [PATCH] cmGeneratorExpression: Fix parser for adjacent colon and comma Update ParseGeneratorExpression to allow correct parsing of adjacent colon and comma separators in either order. Fixes: #27324 --- Source/cmGeneratorExpressionParser.cxx | 54 +++++++++---------- .../LIST-edgecases-check.cmake | 6 +++ .../GeneratorExpression/LIST-edgecases.cmake | 3 ++ .../GeneratorExpression/RunCMakeTest.cmake | 1 + 4 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 Tests/RunCMake/GeneratorExpression/LIST-edgecases-check.cmake create mode 100644 Tests/RunCMake/GeneratorExpression/LIST-edgecases.cmake diff --git a/Source/cmGeneratorExpressionParser.cxx b/Source/cmGeneratorExpressionParser.cxx index c47de3b477..6975642caf 100644 --- a/Source/cmGeneratorExpressionParser.cxx +++ b/Source/cmGeneratorExpressionParser.cxx @@ -115,31 +115,9 @@ void cmGeneratorExpressionParser::ParseGeneratorExpression( emptyParamTermination = true; } - while (this->it != this->Tokens.end() && - this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { - commaTokens.push_back(this->it); - parameters.resize(parameters.size() + 1); - assert(this->it != this->Tokens.end()); - ++this->it; - if (this->it == this->Tokens.end()) { - emptyParamTermination = true; - } - } - while (this->it != this->Tokens.end() && - this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) { - extendText(*(parameters.end() - 1), this->it); - assert(this->it != this->Tokens.end()); - ++this->it; - } - while (this->it != this->Tokens.end() && - this->it->TokenType != cmGeneratorExpressionToken::EndExpression) { - this->ParseContent(*(parameters.end() - 1)); - if (this->it == this->Tokens.end()) { - break; - } - while (this->it != this->Tokens.end() && - this->it->TokenType == - cmGeneratorExpressionToken::CommaSeparator) { + auto handleCommaOrColon = [this, &commaTokens, ¶meters, + &emptyParamTermination]() -> void { + if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) { commaTokens.push_back(this->it); parameters.resize(parameters.size() + 1); assert(this->it != this->Tokens.end()); @@ -147,14 +125,32 @@ void cmGeneratorExpressionParser::ParseGeneratorExpression( if (this->it == this->Tokens.end()) { emptyParamTermination = true; } - } - while (this->it != this->Tokens.end() && - this->it->TokenType == - cmGeneratorExpressionToken::ColonSeparator) { + } else if (this->it->TokenType == + cmGeneratorExpressionToken::ColonSeparator) { extendText(*(parameters.end() - 1), this->it); assert(this->it != this->Tokens.end()); ++this->it; } + }; + + while ( + this->it != this->Tokens.end() && + (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || + this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { + handleCommaOrColon(); + } + while (this->it != this->Tokens.end() && + this->it->TokenType != cmGeneratorExpressionToken::EndExpression) { + this->ParseContent(*(parameters.end() - 1)); + if (this->it == this->Tokens.end()) { + break; + } + while ( + this->it != this->Tokens.end() && + (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator || + this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) { + handleCommaOrColon(); + } } if (this->it != this->Tokens.end() && this->it->TokenType == cmGeneratorExpressionToken::EndExpression) { diff --git a/Tests/RunCMake/GeneratorExpression/LIST-edgecases-check.cmake b/Tests/RunCMake/GeneratorExpression/LIST-edgecases-check.cmake new file mode 100644 index 0000000000..07205f1cae --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/LIST-edgecases-check.cmake @@ -0,0 +1,6 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/LIST-edgecases.txt" content) + +set(expected "a;b;c:;d\na;b;c;:d") +if(NOT content STREQUAL expected) + set(RunCMake_TEST_FAILED "actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]") +endif() diff --git a/Tests/RunCMake/GeneratorExpression/LIST-edgecases.cmake b/Tests/RunCMake/GeneratorExpression/LIST-edgecases.cmake new file mode 100644 index 0000000000..42b1f7a64b --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/LIST-edgecases.cmake @@ -0,0 +1,3 @@ +set(ls1 "$") +set(ls2 "$") +file(GENERATE OUTPUT LIST-edgecases.txt CONTENT ${ls1}\n${ls2}) diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index a53cf87a9f..e75b53b88b 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -56,6 +56,7 @@ run_cmake(FILTER-empty) run_cmake(FILTER-InvalidOperator) run_cmake(FILTER-Exclude) run_cmake(FILTER-Include) +run_cmake(LIST-edgecases) function(run_cmake_build test) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)