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