Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorExpressionParser.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmGeneratorExpressionParser.h"
4
5
#include <cassert>
6
#include <cstddef>
7
#include <utility>
8
9
#include <cm/memory>
10
#include <cmext/algorithm>
11
#include <cmext/memory>
12
13
#include "cmGeneratorExpressionEvaluator.h"
14
15
cmGeneratorExpressionParser::cmGeneratorExpressionParser(
16
  std::vector<cmGeneratorExpressionToken> tokens)
17
0
  : Tokens(std::move(tokens))
18
0
{
19
0
}
20
21
void cmGeneratorExpressionParser::Parse(
22
  cmGeneratorExpressionEvaluatorVector& result)
23
0
{
24
0
  this->it = this->Tokens.begin();
25
26
0
  while (this->it != this->Tokens.end()) {
27
0
    this->ParseContent(result);
28
0
  }
29
0
}
30
31
static void extendText(
32
  cmGeneratorExpressionEvaluatorVector& result,
33
  std::vector<cmGeneratorExpressionToken>::const_iterator it)
34
0
{
35
0
  if (!result.empty() &&
36
0
      (*(result.end() - 1))->GetType() ==
37
0
        cmGeneratorExpressionEvaluator::Text) {
38
0
    cm::static_reference_cast<TextContent>(*(result.end() - 1))
39
0
      .Extend(it->Length);
40
0
  } else {
41
0
    auto textContent = cm::make_unique<TextContent>(it->Content, it->Length);
42
0
    result.push_back(std::move(textContent));
43
0
  }
44
0
}
45
46
static void extendResult(
47
  cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector& result,
48
  cmGeneratorExpressionParser::cmGeneratorExpressionEvaluatorVector&& contents)
49
0
{
50
0
  if (!result.empty() &&
51
0
      (*(result.end() - 1))->GetType() ==
52
0
        cmGeneratorExpressionEvaluator::Text &&
53
0
      contents.front()->GetType() == cmGeneratorExpressionEvaluator::Text) {
54
0
    cm::static_reference_cast<TextContent>(*(result.end() - 1))
55
0
      .Extend(
56
0
        cm::static_reference_cast<TextContent>(contents.front()).GetLength());
57
0
    contents.erase(contents.begin());
58
0
  }
59
0
  cm::append(result, std::move(contents));
60
0
}
61
62
void cmGeneratorExpressionParser::ParseGeneratorExpression(
63
  cmGeneratorExpressionEvaluatorVector& result)
64
0
{
65
0
  assert(this->it != this->Tokens.end());
66
0
  unsigned int nestedLevel = this->NestingLevel;
67
0
  ++this->NestingLevel;
68
69
0
  auto startToken = this->it - 1;
70
71
0
  cmGeneratorExpressionEvaluatorVector identifier;
72
0
  while (this->it->TokenType != cmGeneratorExpressionToken::EndExpression &&
73
0
         this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator) {
74
0
    if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) {
75
0
      extendText(identifier, this->it);
76
0
      ++this->it;
77
0
    } else {
78
0
      this->ParseContent(identifier);
79
0
    }
80
0
    if (this->it == this->Tokens.end()) {
81
0
      break;
82
0
    }
83
0
  }
84
0
  if (identifier.empty()) {
85
    // ERROR
86
0
  }
87
88
0
  if (this->it != this->Tokens.end() &&
89
0
      this->it->TokenType == cmGeneratorExpressionToken::EndExpression) {
90
0
    auto content = cm::make_unique<GeneratorExpressionContent>(
91
0
      startToken->Content,
92
0
      this->it->Content - startToken->Content + this->it->Length);
93
0
    assert(this->it != this->Tokens.end());
94
0
    ++this->it;
95
0
    --this->NestingLevel;
96
0
    content->SetIdentifier(std::move(identifier));
97
0
    result.push_back(std::move(content));
98
0
    return;
99
0
  }
100
101
0
  std::vector<cmGeneratorExpressionEvaluatorVector> parameters;
102
0
  std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator>
103
0
    commaTokens;
104
0
  std::vector<cmGeneratorExpressionToken>::const_iterator colonToken;
105
106
0
  bool emptyParamTermination = false;
107
108
0
  if (this->it != this->Tokens.end() &&
109
0
      this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator) {
110
0
    colonToken = this->it;
111
0
    parameters.resize(parameters.size() + 1);
112
0
    assert(this->it != this->Tokens.end());
113
0
    ++this->it;
114
0
    if (this->it == this->Tokens.end()) {
115
0
      emptyParamTermination = true;
116
0
    }
117
118
0
    auto handleCommaOrColon = [this, &commaTokens, &parameters,
119
0
                               &emptyParamTermination]() -> void {
120
0
      if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator) {
121
0
        commaTokens.push_back(this->it);
122
0
        parameters.resize(parameters.size() + 1);
123
0
        assert(this->it != this->Tokens.end());
124
0
        ++this->it;
125
0
        if (this->it == this->Tokens.end()) {
126
0
          emptyParamTermination = true;
127
0
        }
128
0
      } else if (this->it->TokenType ==
129
0
                 cmGeneratorExpressionToken::ColonSeparator) {
130
0
        extendText(*(parameters.end() - 1), this->it);
131
0
        assert(this->it != this->Tokens.end());
132
0
        ++this->it;
133
0
      }
134
0
    };
135
136
0
    while (
137
0
      this->it != this->Tokens.end() &&
138
0
      (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator ||
139
0
       this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) {
140
0
      handleCommaOrColon();
141
0
    }
142
0
    while (this->it != this->Tokens.end() &&
143
0
           this->it->TokenType != cmGeneratorExpressionToken::EndExpression) {
144
0
      this->ParseContent(*(parameters.end() - 1));
145
0
      if (this->it == this->Tokens.end()) {
146
0
        break;
147
0
      }
148
0
      while (
149
0
        this->it != this->Tokens.end() &&
150
0
        (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator ||
151
0
         this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)) {
152
0
        handleCommaOrColon();
153
0
      }
154
0
    }
155
0
    if (this->it != this->Tokens.end() &&
156
0
        this->it->TokenType == cmGeneratorExpressionToken::EndExpression) {
157
0
      --this->NestingLevel;
158
0
      assert(this->it != this->Tokens.end());
159
0
      ++this->it;
160
0
    }
161
0
  }
162
163
0
  if (nestedLevel != this->NestingLevel) {
164
    // There was a '$<' in the text, but no corresponding '>'. Rebuild to
165
    // treat the '$<' as having been plain text, along with the
166
    // corresponding : and , tokens that might have been found.
167
0
    extendText(result, startToken);
168
0
    extendResult(result, std::move(identifier));
169
0
    if (!parameters.empty()) {
170
0
      extendText(result, colonToken);
171
172
0
      auto pit = parameters.begin();
173
0
      auto const pend = parameters.end();
174
0
      auto commaIt = commaTokens.begin();
175
0
      assert(parameters.size() > commaTokens.size());
176
0
      for (; pit != pend; ++pit, ++commaIt) {
177
0
        if (!pit->empty() && !emptyParamTermination) {
178
0
          extendResult(result, std::move(*pit));
179
0
        }
180
0
        if (commaIt != commaTokens.end()) {
181
0
          extendText(result, *commaIt);
182
0
        } else {
183
0
          break;
184
0
        }
185
0
      }
186
0
    }
187
0
    return;
188
0
  }
189
190
0
  size_t contentLength =
191
0
    ((this->it - 1)->Content - startToken->Content) + (this->it - 1)->Length;
192
0
  auto content = cm::make_unique<GeneratorExpressionContent>(
193
0
    startToken->Content, contentLength);
194
0
  content->SetIdentifier(std::move(identifier));
195
0
  content->SetParameters(std::move(parameters));
196
0
  result.push_back(std::move(content));
197
0
}
198
199
void cmGeneratorExpressionParser::ParseContent(
200
  cmGeneratorExpressionEvaluatorVector& result)
201
0
{
202
0
  assert(this->it != this->Tokens.end());
203
0
  switch (this->it->TokenType) {
204
0
    case cmGeneratorExpressionToken::Text: {
205
0
      if (this->NestingLevel == 0) {
206
0
        if (!result.empty() &&
207
0
            (*(result.end() - 1))->GetType() ==
208
0
              cmGeneratorExpressionEvaluator::Text) {
209
          // A comma in 'plain text' could have split text that should
210
          // otherwise be continuous. Extend the last text content instead of
211
          // creating a new one.
212
0
          cm::static_reference_cast<TextContent>(*(result.end() - 1))
213
0
            .Extend(this->it->Length);
214
0
          assert(this->it != this->Tokens.end());
215
0
          ++this->it;
216
0
          return;
217
0
        }
218
0
      }
219
0
      auto n =
220
0
        cm::make_unique<TextContent>(this->it->Content, this->it->Length);
221
0
      result.push_back(std::move(n));
222
0
      assert(this->it != this->Tokens.end());
223
0
      ++this->it;
224
0
      return;
225
0
    }
226
0
    case cmGeneratorExpressionToken::BeginExpression:
227
0
      assert(this->it != this->Tokens.end());
228
0
      ++this->it;
229
0
      this->ParseGeneratorExpression(result);
230
0
      return;
231
0
    case cmGeneratorExpressionToken::EndExpression:
232
0
    case cmGeneratorExpressionToken::ColonSeparator:
233
0
    case cmGeneratorExpressionToken::CommaSeparator:
234
0
      if (this->NestingLevel == 0) {
235
0
        extendText(result, this->it);
236
0
      } else {
237
0
        assert(false && "Got unexpected syntax token.");
238
0
      }
239
0
      assert(this->it != this->Tokens.end());
240
0
      ++this->it;
241
0
      return;
242
0
  }
243
0
  assert(false && "Unhandled token in generator expression.");
244
0
}