Coverage Report

Created: 2026-10-28 06:38

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