Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCMakePresetsErrors.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 "cmConfigure.h" // IWYU pragma: keep
4
5
#include "cmCMakePresetsErrors.h"
6
7
#include <functional>
8
#include <utility>
9
#include <vector>
10
11
#include <cm3p/json/value.h>
12
13
#include "cmJSONHelpers.h"
14
#include "cmJSONState.h"
15
#include "cmStringAlgorithms.h"
16
17
namespace cmCMakePresetsErrors {
18
namespace {
19
std::string CapitalizePresetKind(std::string kind)
20
1.05k
{
21
1.05k
  if (!kind.empty() && kind.front() >= 'a' && kind.front() <= 'z') {
22
1.05k
    kind.front() = static_cast<char>(kind.front() - 'a' + 'A');
23
1.05k
  }
24
1.05k
  return kind;
25
1.05k
}
26
}
27
28
Json::Value const* getPreset(cmJSONState* state)
29
208k
{
30
208k
  if (state->parseStack.size() < 2) {
31
0
    return nullptr;
32
0
  }
33
208k
  std::string firstKey = state->parseStack[0].first;
34
208k
  if (firstKey == "configurePresets" || firstKey == "packagePresets" ||
35
208k
      firstKey == "buildPresets" || firstKey == "testPresets") {
36
208k
    return state->parseStack[1].second;
37
208k
  }
38
0
  return nullptr;
39
208k
}
40
41
std::string getPresetName(cmJSONState* state)
42
208k
{
43
208k
  Json::Value const* preset = getPreset(state);
44
208k
  if (preset && preset->isMember("name")) {
45
154k
    Json::Value const& name = preset->operator[]("name");
46
154k
    if (name.isConvertibleTo(Json::ValueType::stringValue)) {
47
152k
      return name.asString();
48
152k
    }
49
154k
  }
50
55.5k
  return "";
51
208k
}
52
53
std::string getVariableName(cmJSONState* state)
54
29.5k
{
55
29.5k
  std::string var = state->key_after("cacheVariables");
56
29.5k
  if (var.empty()) {
57
21.7k
    var = state->key_after("variables");
58
21.7k
  }
59
29.5k
  std::string errMsg =
60
29.5k
    var.empty() ? "variable" : cmStrCat("variable \"", var, '"');
61
29.5k
  errMsg =
62
29.5k
    cmStrCat(std::move(errMsg), " for preset \"", getPresetName(state), '"');
63
29.5k
  return errMsg;
64
29.5k
}
65
66
void FILE_NOT_FOUND(std::string const& filename, cmJSONState* state)
67
0
{
68
0
  state->AddError(cmStrCat("File not found: ", filename));
69
0
}
70
71
void INVALID_ROOT(Json::Value const* value, cmJSONState* state)
72
3
{
73
3
  state->AddErrorAtValue("Invalid root object", value);
74
3
}
75
76
void NO_VERSION(Json::Value const* value, cmJSONState* state)
77
261
{
78
261
  state->AddErrorAtValue("No \"version\" field", value);
79
261
}
80
81
void INVALID_VERSION(Json::Value const* value, cmJSONState* state)
82
1.01k
{
83
1.01k
  state->AddErrorAtValue("Invalid \"version\" field", value);
84
1.01k
}
85
86
void UNRECOGNIZED_VERSION(Json::Value const* value, cmJSONState* state)
87
0
{
88
0
  state->AddErrorAtValue("Unrecognized \"version\" field", value);
89
0
}
90
91
void INVALID_PRESETS(Json::Value const* value, cmJSONState* state)
92
48
{
93
48
  state->AddErrorAtValue(cmStrCat("Invalid \"", state->key(), "\" field"),
94
48
                         value);
95
48
}
96
97
void INVALID_PRESET(Json::Value const* value, cmJSONState* state)
98
19.9k
{
99
19.9k
  state->AddErrorAtValue("Invalid preset", value);
100
19.9k
}
101
102
void INVALID_PRESET_NAMED(std::string const& presetName,
103
                          std::string const& kind, cmJSONState* state,
104
                          std::string const& detail)
105
5.66k
{
106
5.66k
  std::string err_msg =
107
5.66k
    cmStrCat("Invalid ", kind, " preset: \"", presetName, '"');
108
5.66k
  if (!detail.empty()) {
109
5.12k
    err_msg = cmStrCat(std::move(err_msg), ": ", detail);
110
5.12k
  }
111
5.66k
  state->AddError(err_msg);
112
5.66k
}
113
114
void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state)
115
23.1k
{
116
23.1k
  std::string var = cmCMakePresetsErrors::getVariableName(state);
117
23.1k
  state->AddErrorAtValue(cmStrCat("Invalid CMake ", var), value);
118
23.1k
}
119
120
void DUPLICATE_PRESETS(std::string const& presetName, cmJSONState* state)
121
456
{
122
456
  state->AddError(cmStrCat("Duplicate preset: \"", presetName, '"'));
123
456
}
124
125
void CYCLIC_PRESET_INHERITANCE(std::string const& presetName,
126
                               std::string const& kind, cmJSONState* state)
127
128
30
{
129
30
  state->AddError(cmStrCat("Cyclic preset inheritance for ", kind,
130
30
                           " preset \"", presetName, '"'));
131
30
}
132
133
void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName,
134
                                            std::string const& inheritedPreset,
135
                                            std::string const& kind,
136
                                            cmJSONState* state)
137
0
{
138
0
  state->AddError(cmStrCat(CapitalizePresetKind(kind), " preset \"",
139
0
                           presetName, "\" inherits preset \"",
140
0
                           inheritedPreset, "\", but \"", inheritedPreset,
141
0
                           "\" is not reachable from the preset's file"));
142
0
}
143
144
void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName,
145
                                            std::string const& kind,
146
                                            std::string const& configurePreset,
147
                                            cmJSONState* state)
148
0
{
149
0
  state->AddError(cmStrCat(CapitalizePresetKind(kind), " preset \"",
150
0
                           presetName, "\" references configure preset \"",
151
0
                           configurePreset, "\", but \"", configurePreset,
152
0
                           "\" is not reachable from the preset's file"));
153
0
}
154
155
void INVALID_MACRO_EXPANSION(std::string const& presetName, cmJSONState* state)
156
3.25k
{
157
3.25k
  state->AddError(cmStrCat("Invalid macro expansion in \"", presetName, '"'));
158
3.25k
}
159
160
void BUILD_TEST_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state)
161
6
{
162
6
  state->AddError("File version must be 2 or higher for build and test preset "
163
6
                  "support");
164
6
}
165
166
void PACKAGE_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state)
167
3
{
168
3
  state->AddError(
169
3
    "File version must be 6 or higher for package preset support");
170
3
}
171
172
void WORKFLOW_PRESETS_UNSUPPORTED(Json::Value const*, cmJSONState* state)
173
3
{
174
3
  state->AddError(
175
3
    "File version must be 6 or higher for workflow preset support");
176
3
}
177
178
void INCLUDE_UNSUPPORTED(Json::Value const*, cmJSONState* state)
179
3
{
180
3
  state->AddError("File version must be 4 or higher for include support");
181
3
}
182
183
void INVALID_INCLUDE(Json::Value const* value, cmJSONState* state)
184
2.04k
{
185
2.04k
  state->AddErrorAtValue("Invalid \"include\" field", value);
186
2.04k
}
187
188
void CONFIGURE_PRESET_NOT_FOUND(std::string const& presetName,
189
                                std::string const& kind,
190
                                std::string const& configurePreset,
191
                                cmJSONState* state)
192
1.04k
{
193
1.04k
  state->AddError(cmStrCat(
194
1.04k
    CapitalizePresetKind(kind), " preset \"", presetName,
195
1.04k
    "\" references unknown configure preset \"", configurePreset, '"'));
196
1.04k
}
197
198
void INSTALL_PREFIX_UNSUPPORTED(Json::Value const* value, cmJSONState* state)
199
24
{
200
24
  state->AddErrorAtValue(
201
24
    "File version must be 3 or higher for installDir preset "
202
24
    "support",
203
24
    value);
204
24
}
205
206
void CONDITION_UNSUPPORTED(cmJSONState* state)
207
57
{
208
57
  state->AddError("File version must be 3 or higher for condition support");
209
57
}
210
211
void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state)
212
30
{
213
30
  state->AddError("File version must be 3 or higher for toolchainFile preset "
214
30
                  "support");
215
30
}
216
217
void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state)
218
39
{
219
39
  state->AddError(
220
39
    "File version must be 10 or higher for graphviz preset support");
221
39
}
222
223
void JOBS_PROC_UNSUPPORTED(cmJSONState* state)
224
0
{
225
0
  state->AddError("File version must be 11 or higher for "
226
0
                  "processor-count-based jobs preset support");
227
0
}
228
229
void PASSTHROUGH_ARGS_UNSUPPORTED(cmJSONState* state)
230
0
{
231
0
  state->AddError("File version must be 12 or higher for "
232
0
                  "testPassthroughArguments preset support");
233
0
}
234
235
void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state)
236
1.94k
{
237
1.94k
  state->AddError(cmStrCat("Cyclic include among presets files: ", file));
238
1.94k
}
239
240
void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state)
241
0
{
242
0
  state->AddError("File version must be 5 or higher for testOutputTruncation "
243
0
                  "preset support");
244
0
}
245
246
namespace {
247
std::string WorkflowStepLabel(cm::string_view stepType,
248
                              std::string const& stepName)
249
996
{
250
996
  return cmStrCat("of type \"", stepType, "\" named \"", stepName, '"');
251
996
}
252
}
253
254
void INVALID_WORKFLOW_STEPS(cm::string_view stepType,
255
                            std::string const& stepName, cmJSONState* state)
256
849
{
257
849
  state->AddError(
258
849
    cmStrCat("Invalid workflow step ", WorkflowStepLabel(stepType, stepName)));
259
849
}
260
261
void WORKFLOW_STEP_CONFIGURE_PRESET_MISMATCH(
262
  std::string const& kind, std::string const& workflowStep,
263
  std::string const& stepConfigurePreset,
264
  std::string const& workflowConfigurePreset, cmJSONState* state)
265
6
{
266
6
  state->AddError(cmStrCat(
267
6
    CapitalizePresetKind(kind), " workflow step \"", workflowStep,
268
6
    "\" uses configure preset \"", stepConfigurePreset,
269
6
    "\", but workflow configure preset is \"", workflowConfigurePreset, '"'));
270
6
}
271
272
void INVALID_TEST_FILTER_INCLUDE_INDEX(Json::Value const* value,
273
                                       cmJSONState* state)
274
0
{
275
0
  state->AddErrorAtValue(
276
0
    "Invalid \"filter.include.index\" field: expected a string or object",
277
0
    value);
278
0
}
279
280
void NO_WORKFLOW_STEPS(std::string const& presetName, cmJSONState* state)
281
12
{
282
12
  state->AddError(
283
12
    cmStrCat("No workflow steps specified for \"", presetName, '"'));
284
12
}
285
286
void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(cm::string_view stepType,
287
                                       std::string const& stepName,
288
                                       cmJSONState* state)
289
138
{
290
138
  state->AddError(cmStrCat("First workflow step ",
291
138
                           WorkflowStepLabel(stepType, stepName),
292
138
                           " must be a configure step"));
293
138
}
294
295
void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(cm::string_view stepType,
296
                                       std::string const& stepName,
297
                                       cmJSONState* state)
298
9
{
299
9
  state->AddError(cmStrCat("Workflow step ",
300
9
                           WorkflowStepLabel(stepType, stepName),
301
9
                           " must be the first step"));
302
9
}
303
304
void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(cm::string_view stepType,
305
                                         std::string const& stepName,
306
                                         cmJSONState* state)
307
0
{
308
0
  state->AddError(cmStrCat("Workflow step ",
309
0
                           WorkflowStepLabel(stepType, stepName),
310
0
                           " is unreachable from preset's file"));
311
0
}
312
313
void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state)
314
0
{
315
0
  state->AddError(
316
0
    "File version must be 6 or higher for CTest JUnit output support");
317
0
}
318
319
void TRACE_UNSUPPORTED(cmJSONState* state)
320
0
{
321
0
  state->AddError("File version must be 7 or higher for trace preset support");
322
0
}
323
324
JsonErrors::ErrorGenerator UNRECOGNIZED_VERSION_RANGE(int min, int max)
325
12
{
326
423
  return [min, max](Json::Value const* value, cmJSONState* state) -> void {
327
423
    state->AddErrorAtValue(cmStrCat("Unrecognized \"version\" ",
328
423
                                    value->asString(), ": must be >=", min,
329
423
                                    " and <=", max),
330
423
                           value);
331
423
  };
332
12
}
333
334
JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION(
335
  std::string const& version, int current, int required)
336
255
{
337
255
  return [version, current, required](Json::Value const* value,
338
255
                                      cmJSONState* state) -> void {
339
255
    state->AddErrorAtValue(cmStrCat("\"cmakeMinimumRequired\" ", version,
340
255
                                    " version ", required,
341
255
                                    " must be less than ", current),
342
255
                           value);
343
255
  };
344
255
}
345
346
void INVALID_PRESET_NAME(Json::Value const* value, cmJSONState* state)
347
7.43k
{
348
7.43k
  std::string errMsg = "Invalid Preset Name";
349
7.43k
  if (value && value->isConvertibleTo(Json::ValueType::stringValue) &&
350
7.06k
      !value->asString().empty()) {
351
5.76k
    errMsg = cmStrCat(std::move(errMsg), ": ", value->asString());
352
5.76k
  }
353
7.43k
  state->AddErrorAtValue(errMsg, value);
354
7.43k
}
355
356
void INVALID_CONDITION(Json::Value const* value, cmJSONState* state)
357
97.7k
{
358
97.7k
  state->AddErrorAtValue(
359
97.7k
    cmStrCat("Invalid condition for preset \"", getPresetName(state), '"'),
360
97.7k
    value);
361
97.7k
}
362
363
JsonErrors::ErrorGenerator INVALID_CONDITION_OBJECT(
364
  JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields)
365
80.8k
{
366
80.8k
  return JsonErrors::INVALID_NAMED_OBJECT(
367
80.8k
    [](Json::Value const*, cmJSONState* state) -> std::string {
368
80.8k
      return cmStrCat(" condition for preset \"", getPresetName(state), '"');
369
80.8k
    })(errorType, extraFields);
370
80.8k
}
371
372
JsonErrors::ErrorGenerator INVALID_VARIABLE_OBJECT(
373
  JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields)
374
6.43k
{
375
6.43k
  return JsonErrors::INVALID_NAMED_OBJECT(
376
6.43k
    [](Json::Value const*, cmJSONState* state) -> std::string {
377
6.43k
      return getVariableName(state);
378
6.43k
    })(errorType, extraFields);
379
6.43k
}
380
381
JsonErrors::ErrorGenerator INVALID_PRESET_OBJECT(
382
  JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields)
383
383k
{
384
383k
  return JsonErrors::INVALID_NAMED_OBJECT(
385
383k
    [](Json::Value const*, cmJSONState*) -> std::string { return "Preset"; })(
386
383k
    errorType, extraFields);
387
383k
}
388
389
JsonErrors::ErrorGenerator INVALID_ROOT_OBJECT(
390
  JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields)
391
2.68k
{
392
2.68k
  return JsonErrors::INVALID_NAMED_OBJECT(
393
2.68k
    [](Json::Value const*, cmJSONState*) -> std::string {
394
2.68k
      return "root object";
395
2.68k
    })(errorType, extraFields);
396
2.68k
}
397
398
void PRESET_MISSING_FIELD(std::string const& presetName,
399
                          std::string const& missingField, cmJSONState* state)
400
546
{
401
546
  state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"",
402
546
                           missingField, '"'));
403
546
}
404
405
void SCHEMA_UNSUPPORTED(cmJSONState* state)
406
3
{
407
3
  state->AddError("File version must be 8 or higher for $schema support");
408
3
}
409
410
void DIAGNOSTIC_UNSUPPORTED(cm::string_view name, cm::string_view context,
411
                            int version, cmJSONState* state)
412
0
{
413
0
  state->AddError(cmStrCat("File version must be ", std::to_string(version),
414
0
                           " or higher for ", context, '.', name, " support"));
415
0
}
416
417
void DIAGNOSTIC_REMOVED(cm::string_view name, cm::string_view context,
418
                        int version, cmJSONState* state)
419
0
{
420
0
  state->AddError(cmStrCat("File version must be ", std::to_string(version),
421
0
                           " or lower for ", context, '.', name, " support"));
422
0
}
423
}