Coverage Report

Created: 2026-09-14 06:43

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