Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmMathCommand.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 "cmMathCommand.h"
4
5
#include <cstdio>
6
#include <limits>
7
#include <stdexcept>
8
9
#include <cm/string_view>
10
#include <cmext/string_view>
11
12
#include <cm3p/kwiml/int.h>
13
14
#include "cmDiagnostics.h"
15
#include "cmExecutionStatus.h"
16
#include "cmExprParserHelper.h"
17
#include "cmMakefile.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmValue.h"
20
21
namespace {
22
bool HandleExprCommand(std::vector<std::string> const& args,
23
                       cmExecutionStatus& status);
24
bool HandleIncDecCommand(std::vector<std::string> const& args,
25
                         cmExecutionStatus& status, int amount,
26
                         long long overflowFrom, long long overflowTo,
27
                         cm::string_view verbing);
28
}
29
30
bool cmMathCommand(std::vector<std::string> const& args,
31
                   cmExecutionStatus& status)
32
0
{
33
0
  if (args.empty()) {
34
0
    status.SetError("must be called with at least one argument.");
35
0
    return false;
36
0
  }
37
0
  std::string const& subCommand = args[0];
38
0
  if (subCommand == "EXPR") {
39
0
    return HandleExprCommand(args, status);
40
0
  }
41
0
  if (subCommand == "INCREMENT") {
42
0
    return HandleIncDecCommand(
43
0
      args, status, 1, std::numeric_limits<long long>::max(),
44
0
      std::numeric_limits<long long>::min(), "incrementing"_s);
45
0
  }
46
0
  if (subCommand == "DECREMENT") {
47
0
    return HandleIncDecCommand(
48
0
      args, status, -1, std::numeric_limits<long long>::min(),
49
0
      std::numeric_limits<long long>::max(), "decrementing"_s);
50
0
  }
51
0
  status.SetError(cmStrCat("does not recognize sub-command ", subCommand));
52
0
  return false;
53
0
}
54
55
namespace {
56
bool HandleExprCommand(std::vector<std::string> const& args,
57
                       cmExecutionStatus& status)
58
0
{
59
0
  if ((args.size() != 3) && (args.size() != 5)) {
60
0
    status.SetError("EXPR called with incorrect arguments.");
61
0
    return false;
62
0
  }
63
64
0
  enum class NumericFormat
65
0
  {
66
0
    UNINITIALIZED,
67
0
    DECIMAL,
68
0
    HEXADECIMAL,
69
0
  };
70
71
0
  std::string const& outputVariable = args[1];
72
0
  std::string const& expression = args[2];
73
0
  size_t argumentIndex = 3;
74
0
  NumericFormat outputFormat = NumericFormat::UNINITIALIZED;
75
76
0
  status.GetMakefile().AddDefinition(outputVariable, "ERROR");
77
78
0
  if (argumentIndex < args.size()) {
79
0
    std::string const messageHint = "sub-command EXPR ";
80
0
    std::string const& option = args[argumentIndex++];
81
0
    if (option == "OUTPUT_FORMAT") {
82
0
      if (argumentIndex < args.size()) {
83
0
        std::string const& argument = args[argumentIndex++];
84
0
        if (argument == "DECIMAL") {
85
0
          outputFormat = NumericFormat::DECIMAL;
86
0
        } else if (argument == "HEXADECIMAL") {
87
0
          outputFormat = NumericFormat::HEXADECIMAL;
88
0
        } else {
89
0
          status.SetError(cmStrCat(messageHint, "value \"", argument,
90
0
                                   "\" for option \"", option,
91
0
                                   "\" is invalid."));
92
0
          return false;
93
0
        }
94
0
      } else {
95
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
96
0
                                 option, "\"."));
97
0
        return false;
98
0
      }
99
0
    } else {
100
0
      status.SetError(
101
0
        cmStrCat(messageHint, "option \"", option, "\" is unknown."));
102
0
      return false;
103
0
    }
104
0
  }
105
106
0
  if (outputFormat == NumericFormat::UNINITIALIZED) {
107
0
    outputFormat = NumericFormat::DECIMAL;
108
0
  }
109
110
0
  cmExprParserHelper helper;
111
0
  if (!helper.ParseString(expression.c_str(), 0)) {
112
0
    status.SetError(helper.GetError());
113
0
    return false;
114
0
  }
115
116
0
  char buffer[1024];
117
0
  char const* fmt;
118
0
  switch (outputFormat) {
119
0
    case NumericFormat::HEXADECIMAL:
120
0
      fmt = "0x%" KWIML_INT_PRIx64;
121
0
      break;
122
0
    case NumericFormat::DECIMAL:
123
0
      CM_FALLTHROUGH;
124
0
    default:
125
0
      fmt = "%" KWIML_INT_PRId64;
126
0
      break;
127
0
  }
128
0
  snprintf(buffer, sizeof(buffer), fmt, helper.GetResult());
129
130
0
  std::string const& w = helper.GetWarning();
131
0
  if (!w.empty()) {
132
0
    status.GetMakefile().IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, w);
133
0
  }
134
135
0
  status.GetMakefile().AddDefinition(outputVariable, buffer);
136
0
  return true;
137
0
}
138
139
bool HandleIncDecCommand(std::vector<std::string> const& args,
140
                         cmExecutionStatus& status, int amount,
141
                         long long overflowFrom, long long overflowTo,
142
                         cm::string_view verbing)
143
0
{
144
0
  std::string const messageHint = cmStrCat("sub-command ", args[0], ' ');
145
0
  if (args.size() != 2) {
146
0
    status.SetError(cmStrCat(messageHint, "wrong number of arguments"));
147
0
    return false;
148
0
  }
149
0
  auto value = status.GetMakefile().GetDefinition(args[1]);
150
0
  if (!value) {
151
0
    status.SetError(
152
0
      cmStrCat(messageHint, "variable \"", args[1], "\" is not defined"));
153
0
    return false;
154
0
  }
155
0
  if (value->empty()) {
156
0
    status.SetError(
157
0
      cmStrCat(messageHint, "value \"\" is not a valid integer"));
158
0
    return false;
159
0
  }
160
0
  std::size_t pos = 0;
161
0
  long long intValue = 0;
162
0
  try {
163
0
    intValue = std::stoll(*value, &pos, 10);
164
0
  } catch (std::invalid_argument&) {
165
    // Do nothing, leave pos as is, which will trigger the error
166
0
  }
167
0
  if (pos != value->length()) {
168
0
    status.SetError(
169
0
      cmStrCat(messageHint, "value \"", *value, "\" is not a valid integer"));
170
0
    return false;
171
0
  }
172
0
  auto newValue = intValue + amount;
173
0
  if (intValue == overflowFrom) {
174
0
    status.GetMakefile().IssueDiagnostic(
175
0
      cmDiagnosticCategory::CMD_AUTHOR,
176
0
      cmStrCat("signed integer overflow while ", verbing, ":\n  ", intValue,
177
0
               '\n'));
178
    // Overflow is undefined behavior in C++, so define it manually
179
0
    newValue = overflowTo;
180
0
  }
181
0
  status.GetMakefile().AddDefinition(args[1], std::to_string(newValue));
182
0
  return true;
183
0
}
184
}