Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmArgumentParser.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 "cmArgumentParser.h"
4
5
#include <algorithm>
6
7
#include "cmArgumentParserTypes.h"
8
#include "cmExecutionStatus.h"
9
#include "cmMakefile.h"
10
#include "cmMessageType.h"
11
#include "cmStringAlgorithms.h"
12
13
namespace ArgumentParser {
14
15
auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action)
16
  -> std::pair<iterator, bool>
17
1.07k
{
18
1.07k
  auto const it = std::lower_bound(
19
1.07k
    this->begin(), this->end(), name,
20
3.47k
    [](value_type const& elem, cm::string_view k) { return elem.first < k; });
21
1.07k
  return (it != this->end() && it->first == name)
22
1.07k
    ? std::make_pair(it, false)
23
1.07k
    : std::make_pair(this->emplace(it, name, std::move(action)), true);
24
1.07k
}
25
26
auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator
27
142k
{
28
142k
  auto const it = std::lower_bound(
29
142k
    this->begin(), this->end(), name,
30
260k
    [](value_type const& elem, cm::string_view k) { return elem.first < k; });
31
142k
  return (it != this->end() && it->first == name) ? it : this->end();
32
142k
}
33
34
auto PositionActionMap::Emplace(std::size_t pos, PositionAction action)
35
  -> std::pair<iterator, bool>
36
110
{
37
110
  auto const it = std::lower_bound(
38
110
    this->begin(), this->end(), pos,
39
110
    [](value_type const& elem, std::size_t k) { return elem.first < k; });
40
110
  return (it != this->end() && it->first == pos)
41
110
    ? std::make_pair(it, false)
42
110
    : std::make_pair(this->emplace(it, pos, std::move(action)), true);
43
110
}
44
45
auto PositionActionMap::Find(std::size_t pos) const -> const_iterator
46
0
{
47
0
  auto const it = std::lower_bound(
48
0
    this->begin(), this->end(), pos,
49
0
    [](value_type const& elem, std::size_t k) { return elem.first < k; });
50
0
  return (it != this->end() && it->first == pos) ? it : this->end();
51
0
}
52
53
void Instance::Bind(std::function<Continue(cm::string_view)> f,
54
                    ExpectAtLeast expect)
55
23.7k
{
56
23.7k
  this->GetState().KeywordValueFunc = std::move(f);
57
23.7k
  this->GetState().KeywordValuesExpected = expect.Count;
58
23.7k
}
59
60
void Instance::Bind(bool& val)
61
0
{
62
0
  val = true;
63
0
  this->Bind(nullptr, ExpectAtLeast{ 0 });
64
0
}
65
66
void Instance::Bind(std::string& val)
67
5.92k
{
68
5.92k
  this->Bind(
69
5.92k
    [&val](cm::string_view arg) -> Continue {
70
5.92k
      val = std::string(arg);
71
5.92k
      return Continue::No;
72
5.92k
    },
73
5.92k
    ExpectAtLeast{ 1 });
74
5.92k
}
75
76
void Instance::Bind(MaybeEmpty<std::string>& val)
77
0
{
78
0
  this->Bind(
79
0
    [&val](cm::string_view arg) -> Continue {
80
0
      val = std::string(arg);
81
0
      return Continue::No;
82
0
    },
83
0
    ExpectAtLeast{ 1 });
84
0
}
85
86
void Instance::Bind(NonEmpty<std::string>& val)
87
0
{
88
0
  this->Bind(
89
0
    [this, &val](cm::string_view arg) -> Continue {
90
0
      if (arg.empty() && this->ParseResults) {
91
0
        this->ParseResults->AddKeywordError(this->GetState().Keyword,
92
0
                                            "  empty string not allowed\n");
93
0
      }
94
0
      val = std::string(arg);
95
0
      return Continue::No;
96
0
    },
97
0
    ExpectAtLeast{ 1 });
98
0
}
99
100
void Instance::Bind(Maybe<std::string>& val)
101
0
{
102
0
  this->Bind(
103
0
    [&val](cm::string_view arg) -> Continue {
104
0
      val = std::string(arg);
105
0
      return Continue::No;
106
0
    },
107
0
    ExpectAtLeast{ 0 });
108
0
}
109
110
void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val)
111
11.8k
{
112
11.8k
  this->Bind(
113
11.8k
    [&val](cm::string_view arg) -> Continue {
114
11.8k
      val.emplace_back(arg);
115
11.8k
      return Continue::Yes;
116
11.8k
    },
117
11.8k
    ExpectAtLeast{ 0 });
118
11.8k
}
119
120
void Instance::Bind(NonEmpty<std::vector<std::string>>& val)
121
0
{
122
0
  this->Bind(
123
0
    [&val](cm::string_view arg) -> Continue {
124
0
      val.emplace_back(arg);
125
0
      return Continue::Yes;
126
0
    },
127
0
    ExpectAtLeast{ 1 });
128
0
}
129
130
void Instance::Bind(
131
  MaybeEmpty<std::vector<std::vector<std::string>>>& multiVal)
132
5.92k
{
133
5.92k
  multiVal.emplace_back();
134
5.92k
  std::vector<std::string>& val = multiVal.back();
135
5.92k
  this->Bind(
136
29.6k
    [&val](cm::string_view arg) -> Continue {
137
29.6k
      val.emplace_back(arg);
138
29.6k
      return Continue::Yes;
139
29.6k
    },
140
5.92k
    ExpectAtLeast{ 0 });
141
5.92k
}
142
143
void Instance::Bind(NonEmpty<std::vector<std::vector<std::string>>>& multiVal)
144
0
{
145
0
  multiVal.emplace_back();
146
0
  std::vector<std::string>& val = multiVal.back();
147
0
  this->Bind(
148
0
    [&val](cm::string_view arg) -> Continue {
149
0
      val.emplace_back(arg);
150
0
      return Continue::Yes;
151
0
    },
152
0
    ExpectAtLeast{ 1 });
153
0
}
154
155
void Instance::Consume(cm::string_view arg)
156
71.1k
{
157
71.1k
  ParserState& state = this->GetState();
158
159
71.1k
  auto const it = state.Bindings.Keywords.Find(arg);
160
71.1k
  if (it != state.Bindings.Keywords.end()) {
161
23.7k
    this->FinishKeyword();
162
23.7k
    state.Keyword = it->first;
163
23.7k
    state.KeywordValuesSeen = 0;
164
23.7k
    state.DoneWithPositional = true;
165
23.7k
    if (state.Bindings.ParsedKeyword) {
166
0
      state.Bindings.ParsedKeyword(*this, it->first);
167
0
    }
168
23.7k
    it->second(*this);
169
23.7k
    return;
170
23.7k
  }
171
172
47.4k
  if (!state.DoneWithPositional) {
173
0
    auto const pit = state.Bindings.Positions.Find(state.Pos);
174
0
    if (pit != state.Bindings.Positions.end()) {
175
0
      pit->second(*this, state.Pos, arg);
176
0
      return;
177
0
    }
178
179
0
    if (state.Bindings.TrailingArgs) {
180
0
      state.Keyword = ""_s;
181
0
      state.KeywordValuesSeen = 0;
182
0
      state.DoneWithPositional = true;
183
0
      state.Bindings.TrailingArgs(*this);
184
0
      if (!state.KeywordValueFunc) {
185
0
        return;
186
0
      }
187
0
    }
188
0
  }
189
190
47.4k
  if (state.KeywordValueFunc) {
191
47.4k
    switch (state.KeywordValueFunc(arg)) {
192
41.4k
      case Continue::Yes:
193
41.4k
        break;
194
5.92k
      case Continue::No:
195
5.92k
        state.KeywordValueFunc = nullptr;
196
5.92k
        break;
197
47.4k
    }
198
47.4k
    ++state.KeywordValuesSeen;
199
47.4k
    return;
200
47.4k
  }
201
202
0
  if (this->UnparsedArguments) {
203
0
    this->UnparsedArguments->emplace_back(arg);
204
0
  }
205
0
}
206
207
void Instance::FinishKeyword()
208
35.5k
{
209
35.5k
  ParserState const& state = this->GetState();
210
35.5k
  if (!state.DoneWithPositional) {
211
11.8k
    return;
212
11.8k
  }
213
214
23.7k
  if (state.KeywordValuesSeen < state.KeywordValuesExpected) {
215
0
    if (this->ParseResults) {
216
0
      this->ParseResults->AddKeywordError(state.Keyword,
217
0
                                          "  missing required value\n");
218
0
    }
219
0
    if (state.Bindings.KeywordMissingValue) {
220
0
      state.Bindings.KeywordMissingValue(*this, state.Keyword);
221
0
    }
222
0
  }
223
23.7k
}
224
225
bool ParseResult::MaybeReportError(cmMakefile& mf) const
226
0
{
227
0
  if (*this) {
228
0
    return false;
229
0
  }
230
0
  std::string e;
231
0
  for (auto const& kel : this->KeywordErrors) {
232
0
    e = cmStrCat(std::move(e), "Error after keyword \"", kel.first, "\":\n",
233
0
                 cmJoin(kel.second, {}));
234
0
  }
235
0
  mf.IssueMessage(MessageType::FATAL_ERROR, e);
236
0
  return true;
237
0
}
238
239
bool ParseResult::Check(cm::string_view context,
240
                        std::vector<std::string> const* unparsedArguments,
241
                        cmExecutionStatus& status) const
242
0
{
243
0
  if (unparsedArguments && !unparsedArguments->empty()) {
244
0
    status.SetError(cmStrCat(context, " given unknown argument: \""_s,
245
0
                             unparsedArguments->front(), "\"."_s));
246
0
    return false;
247
0
  }
248
249
0
  if (!this->KeywordErrors.empty()) {
250
0
    std::string msg = cmStrCat(
251
0
      context, (context.empty() ? ""_s : " "_s), "given invalid "_s,
252
0
      (this->KeywordErrors.size() > 1 ? "arguments:"_s : "argument:"_s));
253
0
    for (auto const& kel : this->KeywordErrors) {
254
0
      for (auto const& ke : kel.second) {
255
0
        msg = cmStrCat(std::move(msg), "\n  "_s, kel.first, ": "_s,
256
0
                       cmStripWhitespace(ke));
257
0
      }
258
0
    }
259
0
    status.SetError(msg);
260
0
    return false;
261
0
  }
262
263
0
  return true;
264
0
}
265
266
} // namespace ArgumentParser