/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 | | } |