Warning: This file is not a C or C++ file. It does not have highlighting.
1 | //===-- include/flang/Parser/message.h --------------------------*- C++ -*-===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef FORTRAN_PARSER_MESSAGE_H_ |
10 | #define FORTRAN_PARSER_MESSAGE_H_ |
11 | |
12 | // Defines a representation for sequences of compiler messages. |
13 | // Supports nested contextualization. |
14 | |
15 | #include "char-block.h" |
16 | #include "char-set.h" |
17 | #include "provenance.h" |
18 | #include "flang/Common/idioms.h" |
19 | #include "flang/Common/reference-counted.h" |
20 | #include "flang/Common/restorer.h" |
21 | #include <cstddef> |
22 | #include <cstring> |
23 | #include <forward_list> |
24 | #include <list> |
25 | #include <optional> |
26 | #include <string> |
27 | #include <utility> |
28 | #include <variant> |
29 | |
30 | namespace Fortran::parser { |
31 | |
32 | // Use "..."_err_en_US, "..."_warn_en_US, "..."_port_en_US, "..."_because_en_US, |
33 | // "..."_todo_en_US, and "..."_en_US string literals to define the static text |
34 | // and severity of a message or attachment. |
35 | enum class Severity { |
36 | Error, // fatal error that prevents code and module file generation |
37 | Warning, // likely problem |
38 | Portability, // nonstandard or obsolete features |
39 | Because, // for AttachTo(), explanatory attachment to support another message |
40 | Context, // (internal): attachment from SetContext() |
41 | Todo, // a feature that's not yet implemented, a fatal error |
42 | None // everything else, common for attachments with source locations |
43 | }; |
44 | |
45 | class MessageFixedText { |
46 | public: |
47 | constexpr MessageFixedText() {} |
48 | constexpr MessageFixedText( |
49 | const char str[], std::size_t n, Severity severity = Severity::None) |
50 | : text_{str, n}, severity_{severity} {} |
51 | constexpr MessageFixedText(const MessageFixedText &) = default; |
52 | constexpr MessageFixedText(MessageFixedText &&) = default; |
53 | constexpr MessageFixedText &operator=(const MessageFixedText &) = default; |
54 | constexpr MessageFixedText &operator=(MessageFixedText &&) = default; |
55 | |
56 | CharBlock text() const { return text_; } |
57 | bool empty() const { return text_.empty(); } |
58 | Severity severity() const { return severity_; } |
59 | MessageFixedText &set_severity(Severity severity) { |
60 | severity_ = severity; |
61 | return *this; |
62 | } |
63 | bool IsFatal() const { |
64 | return severity_ == Severity::Error || severity_ == Severity::Todo; |
65 | } |
66 | |
67 | private: |
68 | CharBlock text_; |
69 | Severity severity_{Severity::None}; |
70 | }; |
71 | |
72 | inline namespace literals { |
73 | constexpr MessageFixedText operator""_err_en_US( |
74 | const char str[], std::size_t n) { |
75 | return MessageFixedText{str, n, Severity::Error}; |
76 | } |
77 | constexpr MessageFixedText operator""_warn_en_US( |
78 | const char str[], std::size_t n) { |
79 | return MessageFixedText{str, n, Severity::Warning}; |
80 | } |
81 | constexpr MessageFixedText operator""_port_en_US( |
82 | const char str[], std::size_t n) { |
83 | return MessageFixedText{str, n, Severity::Portability}; |
84 | } |
85 | constexpr MessageFixedText operator""_because_en_US( |
86 | const char str[], std::size_t n) { |
87 | return MessageFixedText{str, n, Severity::Because}; |
88 | } |
89 | constexpr MessageFixedText operator""_todo_en_US( |
90 | const char str[], std::size_t n) { |
91 | return MessageFixedText{str, n, Severity::Todo}; |
92 | } |
93 | constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) { |
94 | return MessageFixedText{str, n, Severity::None}; |
95 | } |
96 | } // namespace literals |
97 | |
98 | // The construction of a MessageFormattedText uses a MessageFixedText |
99 | // as a vsnprintf() formatting string that is applied to the |
100 | // following arguments. CharBlock, std::string, and std::string_view |
101 | // argument values are also supported; they are automatically converted |
102 | // into char pointers that are suitable for '%s' formatting. |
103 | class MessageFormattedText { |
104 | public: |
105 | template <typename... A> |
106 | MessageFormattedText(const MessageFixedText &text, A &&...x) |
107 | : severity_{text.severity()} { |
108 | Format(&text, Convert(std::forward<A>(x))...); |
109 | } |
110 | MessageFormattedText(const MessageFormattedText &) = default; |
111 | MessageFormattedText(MessageFormattedText &&) = default; |
112 | MessageFormattedText &operator=(const MessageFormattedText &) = default; |
113 | MessageFormattedText &operator=(MessageFormattedText &&) = default; |
114 | const std::string &string() const { return string_; } |
115 | bool IsFatal() const { |
116 | return severity_ == Severity::Error || severity_ == Severity::Todo; |
117 | } |
118 | Severity severity() const { return severity_; } |
119 | MessageFormattedText &set_severity(Severity severity) { |
120 | severity_ = severity; |
121 | return *this; |
122 | } |
123 | std::string MoveString() { return std::move(string_); } |
124 | bool operator==(const MessageFormattedText &that) const { |
125 | return severity_ == that.severity_ && string_ == that.string_; |
126 | } |
127 | bool operator!=(const MessageFormattedText &that) const { |
128 | return !(*this == that); |
129 | } |
130 | |
131 | private: |
132 | void Format(const MessageFixedText *, ...); |
133 | |
134 | template <typename A> A Convert(const A &x) { |
135 | static_assert(!std::is_class_v<std::decay_t<A>>); |
136 | return x; |
137 | } |
138 | template <typename A> common::IfNoLvalue<A, A> Convert(A &&x) { |
139 | static_assert(!std::is_class_v<std::decay_t<A>>); |
140 | return std::move(x); |
141 | } |
142 | const char *Convert(const char *s) { return s; } |
143 | const char *Convert(char *s) { return s; } |
144 | const char *Convert(const std::string &); |
145 | const char *Convert(std::string &&); |
146 | const char *Convert(const std::string_view &); |
147 | const char *Convert(std::string_view &&); |
148 | const char *Convert(CharBlock); |
149 | std::intmax_t Convert(std::int64_t x) { return x; } |
150 | std::uintmax_t Convert(std::uint64_t x) { return x; } |
151 | |
152 | Severity severity_; |
153 | std::string string_; |
154 | std::forward_list<std::string> conversions_; // preserves created strings |
155 | }; |
156 | |
157 | // Represents a formatted rendition of "expected '%s'"_err_en_US |
158 | // on a constant text or a set of characters. |
159 | class MessageExpectedText { |
160 | public: |
161 | MessageExpectedText(const char *s, std::size_t n) { |
162 | if (n == std::string::npos) { |
163 | n = std::strlen(s); |
164 | } |
165 | if (n == 1) { |
166 | // Treat a one-character string as a singleton set for better merging. |
167 | u_ = SetOfChars{*s}; |
168 | } else { |
169 | u_ = CharBlock{s, n}; |
170 | } |
171 | } |
172 | constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {} |
173 | constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {} |
174 | constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {} |
175 | MessageExpectedText(const MessageExpectedText &) = default; |
176 | MessageExpectedText(MessageExpectedText &&) = default; |
177 | MessageExpectedText &operator=(const MessageExpectedText &) = default; |
178 | MessageExpectedText &operator=(MessageExpectedText &&) = default; |
179 | |
180 | std::string ToString() const; |
181 | bool Merge(const MessageExpectedText &); |
182 | |
183 | private: |
184 | std::variant<CharBlock, SetOfChars> u_; |
185 | }; |
186 | |
187 | class Message : public common::ReferenceCounted<Message> { |
188 | public: |
189 | using Reference = common::CountedReference<Message>; |
190 | |
191 | Message(const Message &) = default; |
192 | Message(Message &&) = default; |
193 | Message &operator=(const Message &) = default; |
194 | Message &operator=(Message &&) = default; |
195 | |
196 | Message(ProvenanceRange pr, const MessageFixedText &t) |
197 | : location_{pr}, text_{t} {} |
198 | Message(ProvenanceRange pr, const MessageFormattedText &s) |
199 | : location_{pr}, text_{s} {} |
200 | Message(ProvenanceRange pr, MessageFormattedText &&s) |
201 | : location_{pr}, text_{std::move(s)} {} |
202 | Message(ProvenanceRange pr, const MessageExpectedText &t) |
203 | : location_{pr}, text_{t} {} |
204 | |
205 | Message(CharBlock csr, const MessageFixedText &t) |
206 | : location_{csr}, text_{t} {} |
207 | Message(CharBlock csr, const MessageFormattedText &s) |
208 | : location_{csr}, text_{s} {} |
209 | Message(CharBlock csr, MessageFormattedText &&s) |
210 | : location_{csr}, text_{std::move(s)} {} |
211 | Message(CharBlock csr, const MessageExpectedText &t) |
212 | : location_{csr}, text_{t} {} |
213 | |
214 | template <typename RANGE, typename A, typename... As> |
215 | Message(RANGE r, const MessageFixedText &t, A &&x, As &&...xs) |
216 | : location_{r}, text_{MessageFormattedText{ |
217 | t, std::forward<A>(x), std::forward<As>(xs)...}} {} |
218 | |
219 | Reference attachment() const { return attachment_; } |
220 | |
221 | void SetContext(Message *c) { |
222 | attachment_ = c; |
223 | attachmentIsContext_ = true; |
224 | } |
225 | Message &Attach(Message *); |
226 | Message &Attach(std::unique_ptr<Message> &&); |
227 | template <typename... A> Message &Attach(A &&...args) { |
228 | return Attach(new Message{std::forward<A>(args)...}); // reference-counted |
229 | } |
230 | |
231 | bool SortBefore(const Message &that) const; |
232 | bool IsFatal() const; |
233 | Severity severity() const; |
234 | Message &set_severity(Severity); |
235 | std::string ToString() const; |
236 | std::optional<ProvenanceRange> GetProvenanceRange( |
237 | const AllCookedSources &) const; |
238 | void Emit(llvm::raw_ostream &, const AllCookedSources &, |
239 | bool echoSourceLine = true) const; |
240 | |
241 | // If this Message or any of its attachments locates itself via a CharBlock, |
242 | // replace its location with the corresponding ProvenanceRange. |
243 | void ResolveProvenances(const AllCookedSources &); |
244 | |
245 | bool IsMergeable() const { |
246 | return std::holds_alternative<MessageExpectedText>(text_); |
247 | } |
248 | bool Merge(const Message &); |
249 | bool operator==(const Message &that) const; |
250 | bool operator!=(const Message &that) const { return !(*this == that); } |
251 | |
252 | private: |
253 | bool AtSameLocation(const Message &) const; |
254 | std::variant<ProvenanceRange, CharBlock> location_; |
255 | std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText> |
256 | text_; |
257 | bool attachmentIsContext_{false}; |
258 | Reference attachment_; |
259 | }; |
260 | |
261 | class Messages { |
262 | public: |
263 | Messages() {} |
264 | Messages(Messages &&that) : messages_{std::move(that.messages_)} {} |
265 | Messages &operator=(Messages &&that) { |
266 | messages_ = std::move(that.messages_); |
267 | return *this; |
268 | } |
269 | |
270 | std::list<Message> &messages() { return messages_; } |
271 | bool empty() const { return messages_.empty(); } |
272 | void clear() { messages_.clear(); } |
273 | |
274 | template <typename... A> Message &Say(A &&...args) { |
275 | return messages_.emplace_back(std::forward<A>(args)...); |
276 | } |
277 | |
278 | void Annex(Messages &&that) { |
279 | messages_.splice(messages_.end(), that.messages_); |
280 | } |
281 | |
282 | bool Merge(const Message &); |
283 | void Merge(Messages &&); |
284 | void Copy(const Messages &); |
285 | void ResolveProvenances(const AllCookedSources &); |
286 | void Emit(llvm::raw_ostream &, const AllCookedSources &, |
287 | bool echoSourceLines = true) const; |
288 | void AttachTo(Message &, std::optional<Severity> = std::nullopt); |
289 | bool AnyFatalError() const; |
290 | |
291 | private: |
292 | std::list<Message> messages_; |
293 | }; |
294 | |
295 | class ContextualMessages { |
296 | public: |
297 | ContextualMessages() = default; |
298 | ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {} |
299 | explicit ContextualMessages(Messages *m) : messages_{m} {} |
300 | ContextualMessages(const ContextualMessages &that) |
301 | : at_{that.at_}, messages_{that.messages_} {} |
302 | |
303 | CharBlock at() const { return at_; } |
304 | Messages *messages() const { return messages_; } |
305 | Message::Reference contextMessage() const { return contextMessage_; } |
306 | bool empty() const { return !messages_ || messages_->empty(); } |
307 | |
308 | // Set CharBlock for messages; restore when the returned value is deleted |
309 | common::Restorer<CharBlock> SetLocation(CharBlock at) { |
310 | if (at.empty()) { |
311 | at = at_; |
312 | } |
313 | return common::ScopedSet(at_, std::move(at)); |
314 | } |
315 | |
316 | common::Restorer<Message::Reference> SetContext(Message *m) { |
317 | if (!m) { |
318 | m = contextMessage_.get(); |
319 | } |
320 | return common::ScopedSet(contextMessage_, m); |
321 | } |
322 | |
323 | // Diverts messages to another buffer; restored when the returned |
324 | // value is deleted. |
325 | common::Restorer<Messages *> SetMessages(Messages &buffer) { |
326 | return common::ScopedSet(messages_, &buffer); |
327 | } |
328 | // Discard future messages until the returned value is deleted. |
329 | common::Restorer<Messages *> DiscardMessages() { |
330 | return common::ScopedSet(messages_, nullptr); |
331 | } |
332 | |
333 | template <typename... A> Message *Say(CharBlock at, A &&...args) { |
334 | if (messages_ != nullptr) { |
335 | auto &msg{messages_->Say(at, std::forward<A>(args)...)}; |
336 | if (contextMessage_) { |
337 | msg.SetContext(contextMessage_.get()); |
338 | } |
339 | return &msg; |
340 | } else { |
341 | return nullptr; |
342 | } |
343 | } |
344 | |
345 | template <typename... A> |
346 | Message *Say(std::optional<CharBlock> at, A &&...args) { |
347 | return Say(at.value_or(at_), std::forward<A>(args)...); |
348 | } |
349 | |
350 | template <typename... A> Message *Say(A &&...args) { |
351 | return Say(at_, std::forward<A>(args)...); |
352 | } |
353 | |
354 | Message *Say(Message &&msg) { |
355 | if (messages_ != nullptr) { |
356 | if (contextMessage_) { |
357 | msg.SetContext(contextMessage_.get()); |
358 | } |
359 | return &messages_->Say(std::move(msg)); |
360 | } else { |
361 | return nullptr; |
362 | } |
363 | } |
364 | |
365 | private: |
366 | CharBlock at_; |
367 | Messages *messages_{nullptr}; |
368 | Message::Reference contextMessage_; |
369 | }; |
370 | } // namespace Fortran::parser |
371 | #endif // FORTRAN_PARSER_MESSAGE_H_ |
372 |
Warning: This file is not a C or C++ file. It does not have highlighting.