Coverage Report

Created: 2026-03-12 06:35

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
416
{
18
416
  auto const it = std::lower_bound(
19
416
    this->begin(), this->end(), name,
20
1.32k
    [](value_type const& elem, cm::string_view k) { return elem.first < k; });
21
416
  return (it != this->end() && it->first == name)
22
416
    ? std::make_pair(it, false)
23
416
    : std::make_pair(this->emplace(it, name, std::move(action)), true);
24
416
}
25
26
auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator
27
0
{
28
0
  auto const it = std::lower_bound(
29
0
    this->begin(), this->end(), name,
30
0
    [](value_type const& elem, cm::string_view k) { return elem.first < k; });
31
0
  return (it != this->end() && it->first == name) ? it : this->end();
32
0
}
33
34
auto PositionActionMap::Emplace(std::size_t pos, PositionAction action)
35
  -> std::pair<iterator, bool>
36
44
{
37
44
  auto const it = std::lower_bound(
38
44
    this->begin(), this->end(), pos,
39
44
    [](value_type const& elem, std::size_t k) { return elem.first < k; });
40
44
  return (it != this->end() && it->first == pos)
41
44
    ? std::make_pair(it, false)
42
44
    : std::make_pair(this->emplace(it, pos, std::move(action)), true);
43
44
}
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
0
{
56
0
  this->GetState().KeywordValueFunc = std::move(f);
57
0
  this->GetState().KeywordValuesExpected = expect.Count;
58
0
}
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
0
{
68
0
  this->Bind(
69
0
    [&val](cm::string_view arg) -> Continue {
70
0
      val = std::string(arg);
71
0
      return Continue::No;
72
0
    },
73
0
    ExpectAtLeast{ 1 });
74
0
}
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
0
{
112
0
  this->Bind(
113
0
    [&val](cm::string_view arg) -> Continue {
114
0
      val.emplace_back(arg);
115
0
      return Continue::Yes;
116
0
    },
117
0
    ExpectAtLeast{ 0 });
118
0
}
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(std::vector<std::vector<std::string>>& multiVal)
131
0
{
132
0
  multiVal.emplace_back();
133
0
  std::vector<std::string>& val = multiVal.back();
134
0
  this->Bind(
135
0
    [&val](cm::string_view arg) -> Continue {
136
0
      val.emplace_back(arg);
137
0
      return Continue::Yes;
138
0
    },
139
0
    ExpectAtLeast{ 0 });
140
0
}
141
142
void Instance::Consume(cm::string_view arg)
143
0
{
144
0
  ParserState& state = this->GetState();
145
146
0
  auto const it = state.Bindings.Keywords.Find(arg);
147
0
  if (it != state.Bindings.Keywords.end()) {
148
0
    this->FinishKeyword();
149
0
    state.Keyword = it->first;
150
0
    state.KeywordValuesSeen = 0;
151
0
    state.DoneWithPositional = true;
152
0
    if (state.Bindings.ParsedKeyword) {
153
0
      state.Bindings.ParsedKeyword(*this, it->first);
154
0
    }
155
0
    it->second(*this);
156
0
    return;
157
0
  }
158
159
0
  if (!state.DoneWithPositional) {
160
0
    auto const pit = state.Bindings.Positions.Find(state.Pos);
161
0
    if (pit != state.Bindings.Positions.end()) {
162
0
      pit->second(*this, state.Pos, arg);
163
0
      return;
164
0
    }
165
166
0
    if (state.Bindings.TrailingArgs) {
167
0
      state.Keyword = ""_s;
168
0
      state.KeywordValuesSeen = 0;
169
0
      state.DoneWithPositional = true;
170
0
      state.Bindings.TrailingArgs(*this);
171
0
      if (!state.KeywordValueFunc) {
172
0
        return;
173
0
      }
174
0
    }
175
0
  }
176
177
0
  if (state.KeywordValueFunc) {
178
0
    switch (state.KeywordValueFunc(arg)) {
179
0
      case Continue::Yes:
180
0
        break;
181
0
      case Continue::No:
182
0
        state.KeywordValueFunc = nullptr;
183
0
        break;
184
0
    }
185
0
    ++state.KeywordValuesSeen;
186
0
    return;
187
0
  }
188
189
0
  if (this->UnparsedArguments) {
190
0
    this->UnparsedArguments->emplace_back(arg);
191
0
  }
192
0
}
193
194
void Instance::FinishKeyword()
195
0
{
196
0
  ParserState const& state = this->GetState();
197
0
  if (!state.DoneWithPositional) {
198
0
    return;
199
0
  }
200
201
0
  if (state.KeywordValuesSeen < state.KeywordValuesExpected) {
202
0
    if (this->ParseResults) {
203
0
      this->ParseResults->AddKeywordError(state.Keyword,
204
0
                                          "  missing required value\n");
205
0
    }
206
0
    if (state.Bindings.KeywordMissingValue) {
207
0
      state.Bindings.KeywordMissingValue(*this, state.Keyword);
208
0
    }
209
0
  }
210
0
}
211
212
bool ParseResult::MaybeReportError(cmMakefile& mf) const
213
0
{
214
0
  if (*this) {
215
0
    return false;
216
0
  }
217
0
  std::string e;
218
0
  for (auto const& kel : this->KeywordErrors) {
219
0
    e = cmStrCat(std::move(e), "Error after keyword \"", kel.first, "\":\n",
220
0
                 cmJoin(kel.second, {}));
221
0
  }
222
0
  mf.IssueMessage(MessageType::FATAL_ERROR, e);
223
0
  return true;
224
0
}
225
226
bool ParseResult::Check(cm::string_view context,
227
                        std::vector<std::string> const* unparsedArguments,
228
                        cmExecutionStatus& status) const
229
0
{
230
0
  if (unparsedArguments && !unparsedArguments->empty()) {
231
0
    status.SetError(cmStrCat(context, " given unknown argument: \""_s,
232
0
                             unparsedArguments->front(), "\"."_s));
233
0
    return false;
234
0
  }
235
236
0
  if (!this->KeywordErrors.empty()) {
237
0
    std::string msg = cmStrCat(
238
0
      context, (context.empty() ? ""_s : " "_s), "given invalid "_s,
239
0
      (this->KeywordErrors.size() > 1 ? "arguments:"_s : "argument:"_s));
240
0
    for (auto const& kel : this->KeywordErrors) {
241
0
      for (auto const& ke : kel.second) {
242
0
        msg =
243
0
          cmStrCat(msg, "\n  "_s, kel.first, ": "_s, cmStripWhitespace(ke));
244
0
      }
245
0
    }
246
0
    status.SetError(msg);
247
0
    return false;
248
0
  }
249
250
0
  return true;
251
0
}
252
253
} // namespace ArgumentParser