Coverage Report

Created: 2026-09-14 06:43

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