Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/liblangutil/Exceptions.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * @author Christian <c@ethdev.com>
20
 * @date 2014
21
 * Solidity exception hierarchy.
22
 */
23
24
#pragma once
25
26
#include <libsolutil/Exceptions.h>
27
#include <libsolutil/Assertions.h>
28
#include <libsolutil/CommonData.h>
29
#include <liblangutil/SourceLocation.h>
30
31
#include <boost/preprocessor/cat.hpp>
32
#include <boost/preprocessor/facilities/empty.hpp>
33
#include <boost/preprocessor/facilities/overload.hpp>
34
#include <boost/algorithm/string/case_conv.hpp>
35
36
#include <optional>
37
#include <string>
38
#include <utility>
39
#include <variant>
40
#include <vector>
41
42
namespace solidity::langutil
43
{
44
class Error;
45
using ErrorList = std::vector<std::shared_ptr<Error const>>;
46
47
struct CompilerError: virtual util::Exception {};
48
struct StackTooDeepError: virtual CompilerError {};
49
struct InternalCompilerError: virtual util::Exception {};
50
struct FatalError: virtual util::Exception {};
51
struct UnimplementedFeatureError: virtual util::Exception {};
52
struct InvalidAstError: virtual util::Exception {};
53
54
55
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
56
#if !BOOST_PP_VARIADICS_MSVC
57
796k
#define solAssert(...) BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__)
58
#else
59
#define solAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
60
#endif
61
62
#define solAssert_1(CONDITION) \
63
6.00G
  solAssert_2((CONDITION), "")
64
65
#define solAssert_2(CONDITION, DESCRIPTION) \
66
6.23G
  assertThrowWithDefaultDescription( \
67
6.23G
    (CONDITION), \
68
6.23G
    ::solidity::langutil::InternalCompilerError, \
69
6.23G
    (DESCRIPTION), \
70
6.23G
    "Solidity assertion failed" \
71
6.23G
  )
72
73
74
/// Assertion that throws an UnimplementedFeatureError containing the given description if it is not met.
75
#if !BOOST_PP_VARIADICS_MSVC
76
412
#define solUnimplementedAssert(...) BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__)
77
#else
78
#define solUnimplementedAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
79
#endif
80
81
#define solUnimplementedAssert_1(CONDITION) \
82
71.3k
  solUnimplementedAssert_2((CONDITION), "")
83
84
#define solUnimplementedAssert_2(CONDITION, DESCRIPTION) \
85
600k
  assertThrowWithDefaultDescription( \
86
1.02M
    (CONDITION), \
87
1.02M
    ::solidity::langutil::UnimplementedFeatureError, \
88
1.02M
    (DESCRIPTION), \
89
1.02M
    "Unimplemented feature" \
90
1.02M
  )
91
92
93
/// Helper that unconditionally reports an unimplemented feature.
94
#define solUnimplemented(DESCRIPTION) \
95
380k
  solUnimplementedAssert(false, DESCRIPTION)
96
97
98
/// Assertion that throws an InvalidAstError containing the given description if it is not met.
99
#if !BOOST_PP_VARIADICS_MSVC
100
0
#define astAssert(...) BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__)
101
#else
102
#define astAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())
103
#endif
104
105
#define astAssert_1(CONDITION) \
106
0
  astAssert_2(CONDITION, "")
107
108
#define astAssert_2(CONDITION, DESCRIPTION) \
109
0
  assertThrowWithDefaultDescription( \
110
0
    (CONDITION), \
111
0
    ::solidity::langutil::InvalidAstError, \
112
0
    (DESCRIPTION), \
113
0
    "AST assertion failed" \
114
0
  )
115
116
117
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
118
119
class SecondarySourceLocation
120
{
121
public:
122
  SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
123
31.6k
  {
124
31.6k
    infos.emplace_back(_errMsg, _sourceLocation);
125
31.6k
    return *this;
126
31.6k
  }
127
  SecondarySourceLocation& append(SecondarySourceLocation&& _other)
128
0
  {
129
0
    infos += std::move(_other.infos);
130
0
    return *this;
131
0
  }
132
133
  /// Limits the number of secondary source locations to 32 and appends a notice to the
134
  /// error message.
135
  void limitSize(std::string& _message)
136
713
  {
137
713
    size_t occurrences = infos.size();
138
713
    if (occurrences > 32)
139
3
    {
140
3
      infos.resize(32);
141
3
      _message += " Truncated from " + std::to_string(occurrences) + " to the first 32 occurrences.";
142
3
    }
143
713
  }
144
145
  std::vector<errorSourceLocationInfo> infos;
146
};
147
148
using errinfo_sourceLocation = boost::error_info<struct tag_sourceLocation, SourceLocation>;
149
using errinfo_secondarySourceLocation = boost::error_info<struct tag_secondarySourceLocation, SecondarySourceLocation>;
150
151
/**
152
 * Unique identifiers are used to tag and track individual error cases.
153
 * They are passed as the first parameter of error reporting functions.
154
 * Suffix _error helps to find them in the sources.
155
 * The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error).
156
 * To create a new ID, one can add 0000_error and then run "python ./scripts/error_codes.py --fix"
157
 * from the root of the repo.
158
 */
159
struct ErrorId
160
{
161
  unsigned long long error = 0;
162
66.5k
  bool operator==(ErrorId const& _rhs) const { return error == _rhs.error; }
163
0
  bool operator!=(ErrorId const& _rhs) const { return !(*this == _rhs); }
164
150k
  bool operator<(ErrorId const& _rhs) const { return error < _rhs.error; }
165
};
166
517k
constexpr ErrorId operator""_error(unsigned long long _error) { return ErrorId{ _error }; }
167
168
class Error: virtual public util::Exception
169
{
170
public:
171
  enum class Type
172
  {
173
    Info,
174
    Warning,
175
    CodeGenerationError,
176
    DeclarationError,
177
    DocstringParsingError,
178
    ParserError,
179
    TypeError,
180
    SyntaxError,
181
    IOError,
182
    FatalError,
183
    JSONError,
184
    InternalCompilerError,
185
    CompilerError,
186
    Exception,
187
    UnimplementedFeatureError,
188
    YulException,
189
    SMTLogicException,
190
  };
191
192
  enum class Severity
193
  {
194
    // NOTE: We rely on these being ordered from least to most severe.
195
    Info,
196
    Warning,
197
    Error,
198
  };
199
200
  Error(
201
    ErrorId _errorId,
202
    Type _type,
203
    std::string const& _description,
204
    SourceLocation const& _location = SourceLocation(),
205
    SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation()
206
  );
207
208
66.5k
  ErrorId errorId() const { return m_errorId; }
209
808k
  Type type() const { return m_type; }
210
0
  Severity severity() const { return errorSeverity(m_type); }
211
212
  SourceLocation const* sourceLocation() const noexcept;
213
  SecondarySourceLocation const* secondarySourceLocation() const noexcept;
214
215
  /// helper functions
216
  static Error const* containsErrorOfType(ErrorList const& _list, Error::Type _type)
217
0
  {
218
0
    for (auto e: _list)
219
0
      if (e->type() == _type)
220
0
        return e.get();
221
0
    return nullptr;
222
0
  }
223
224
  static constexpr Severity errorSeverity(Type _type)
225
808k
  {
226
808k
    switch (_type)
227
808k
    {
228
0
      case Type::Info: return Severity::Info;
229
788k
      case Type::Warning: return Severity::Warning;
230
20.8k
      default: return Severity::Error;
231
808k
    }
232
808k
  }
233
234
  static constexpr Severity errorSeverityOrType(std::variant<Error::Type, Error::Severity> _typeOrSeverity)
235
0
  {
236
0
    if (std::holds_alternative<Error::Type>(_typeOrSeverity))
237
0
      return errorSeverity(std::get<Error::Type>(_typeOrSeverity));
238
0
    return std::get<Error::Severity>(_typeOrSeverity);
239
0
  }
240
241
  static bool isError(Severity _severity)
242
808k
  {
243
808k
    return _severity == Severity::Error;
244
808k
  }
245
246
  static bool isError(Type _type)
247
808k
  {
248
808k
    return isError(errorSeverity(_type));
249
808k
  }
250
251
  static bool containsErrors(ErrorList const& _list)
252
275k
  {
253
275k
    for (auto e: _list)
254
808k
      if (isError(e->type()))
255
20.8k
        return true;
256
255k
    return false;
257
275k
  }
258
259
  static bool hasErrorsWarningsOrInfos(ErrorList const& _list)
260
0
  {
261
0
    return !_list.empty();
262
0
  }
263
264
  static std::string formatErrorSeverity(Severity _severity)
265
0
  {
266
0
    switch (_severity)
267
0
    {
268
0
    case Severity::Info: return "Info";
269
0
    case Severity::Warning: return "Warning";
270
0
    case Severity::Error: return "Error";
271
0
    }
272
0
    util::unreachable();
273
0
  }
274
275
  static std::string formatErrorType(Type _type)
276
0
  {
277
0
    return m_errorTypeNames.at(_type);
278
0
  }
279
280
  static std::optional<Type> parseErrorType(std::string _name)
281
0
  {
282
0
    static std::map<std::string, Error::Type> const m_errorTypesByName = util::invertMap(m_errorTypeNames);
283
0
284
0
    if (m_errorTypesByName.count(_name) == 0)
285
0
      return std::nullopt;
286
0
287
0
    return m_errorTypesByName.at(_name);
288
0
  }
289
290
  static std::string formatTypeOrSeverity(std::variant<Error::Type, Error::Severity> _typeOrSeverity)
291
0
  {
292
0
    if (std::holds_alternative<Error::Type>(_typeOrSeverity))
293
0
      return formatErrorType(std::get<Error::Type>(_typeOrSeverity));
294
0
    return formatErrorSeverity(std::get<Error::Severity>(_typeOrSeverity));
295
0
  }
296
297
  static std::string formatErrorSeverityLowercase(Severity _severity)
298
0
  {
299
0
    std::string severityValue = formatErrorSeverity(_severity);
300
0
    boost::algorithm::to_lower(severityValue);
301
0
    return severityValue;
302
0
  }
303
304
private:
305
  ErrorId m_errorId;
306
  Type m_type;
307
308
  static std::map<Type, std::string> const m_errorTypeNames;
309
};
310
311
}