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, and "..."_en_US
33 // string literals to define the static text and fatality of a message.
34 //
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:
MessageFixedText()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 
text()56   CharBlock text() const { return text_; }
severity()57   Severity severity() const { return severity_; }
set_severity(Severity severity)58   MessageFixedText &set_severity(Severity severity) {
59     severity_ = severity;
60     return *this;
61   }
isFatal()62   bool isFatal() const {
63     return severity_ == Severity::Error || severity_ == Severity::Todo;
64   }
65 
66 private:
67   CharBlock text_;
68   Severity severity_{Severity::None};
69 };
70 
71 inline namespace literals {
72 constexpr MessageFixedText operator""_err_en_US(
73     const char str[], std::size_t n) {
74   return MessageFixedText{str, n, Severity::Error};
75 }
76 constexpr MessageFixedText operator""_warn_en_US(
77     const char str[], std::size_t n) {
78   return MessageFixedText{str, n, Severity::Warning};
79 }
80 constexpr MessageFixedText operator""_port_en_US(
81     const char str[], std::size_t n) {
82   return MessageFixedText{str, n, Severity::Portability};
83 }
84 constexpr MessageFixedText operator""_todo_en_US(
85     const char str[], std::size_t n) {
86   return MessageFixedText{str, n, Severity::Todo};
87 }
88 constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
89   return MessageFixedText{str, n, Severity::None};
90 }
91 } // namespace literals
92 
93 // The construction of a MessageFormattedText uses a MessageFixedText
94 // as a vsnprintf() formatting string that is applied to the
95 // following arguments.  CharBlock and std::string argument
96 // values are also supported; they are automatically converted into
97 // char pointers that are suitable for '%s' formatting.
98 class MessageFormattedText {
99 public:
100   template <typename... A>
MessageFormattedText(const MessageFixedText & text,A &&...x)101   MessageFormattedText(const MessageFixedText &text, A &&...x)
102       : severity_{text.severity()} {
103     Format(&text, Convert(std::forward<A>(x))...);
104   }
105   MessageFormattedText(const MessageFormattedText &) = default;
106   MessageFormattedText(MessageFormattedText &&) = default;
107   MessageFormattedText &operator=(const MessageFormattedText &) = default;
108   MessageFormattedText &operator=(MessageFormattedText &&) = default;
string()109   const std::string &string() const { return string_; }
isFatal()110   bool isFatal() const {
111     return severity_ == Severity::Error || severity_ == Severity::Todo;
112   }
severity()113   Severity severity() const { return severity_; }
set_severity(Severity severity)114   MessageFormattedText &set_severity(Severity severity) {
115     severity_ = severity;
116     return *this;
117   }
MoveString()118   std::string MoveString() { return std::move(string_); }
119 
120 private:
121   void Format(const MessageFixedText *, ...);
122 
Convert(const A & x)123   template <typename A> A Convert(const A &x) {
124     static_assert(!std::is_class_v<std::decay_t<A>>);
125     return x;
126   }
Convert(A & x)127   template <typename A> A Convert(A &x) {
128     static_assert(!std::is_class_v<std::decay_t<A>>);
129     return x;
130   }
Convert(A && x)131   template <typename A> common::IfNoLvalue<A, A> Convert(A &&x) {
132     static_assert(!std::is_class_v<std::decay_t<A>>);
133     return std::move(x);
134   }
Convert(const char * s)135   const char *Convert(const char *s) { return s; }
Convert(char * s)136   const char *Convert(char *s) { return s; }
137   const char *Convert(const std::string &);
138   const char *Convert(std::string &);
139   const char *Convert(std::string &&);
140   const char *Convert(CharBlock);
Convert(std::int64_t x)141   std::intmax_t Convert(std::int64_t x) { return x; }
Convert(std::uint64_t x)142   std::uintmax_t Convert(std::uint64_t x) { return x; }
143 
144   Severity severity_;
145   std::string string_;
146   std::forward_list<std::string> conversions_; // preserves created strings
147 };
148 
149 // Represents a formatted rendition of "expected '%s'"_err_en_US
150 // on a constant text or a set of characters.
151 class MessageExpectedText {
152 public:
MessageExpectedText(const char * s,std::size_t n)153   MessageExpectedText(const char *s, std::size_t n) {
154     if (n == std::string::npos) {
155       n = std::strlen(s);
156     }
157     if (n == 1) {
158       // Treat a one-character string as a singleton set for better merging.
159       u_ = SetOfChars{*s};
160     } else {
161       u_ = CharBlock{s, n};
162     }
163   }
MessageExpectedText(CharBlock cb)164   constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {}
MessageExpectedText(char ch)165   constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {}
MessageExpectedText(SetOfChars set)166   constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {}
167   MessageExpectedText(const MessageExpectedText &) = default;
168   MessageExpectedText(MessageExpectedText &&) = default;
169   MessageExpectedText &operator=(const MessageExpectedText &) = default;
170   MessageExpectedText &operator=(MessageExpectedText &&) = default;
171 
172   std::string ToString() const;
173   bool Merge(const MessageExpectedText &);
174 
175 private:
176   std::variant<CharBlock, SetOfChars> u_;
177 };
178 
179 class Message : public common::ReferenceCounted<Message> {
180 public:
181   using Reference = common::CountedReference<Message>;
182 
183   Message(const Message &) = default;
184   Message(Message &&) = default;
185   Message &operator=(const Message &) = default;
186   Message &operator=(Message &&) = default;
187 
Message(ProvenanceRange pr,const MessageFixedText & t)188   Message(ProvenanceRange pr, const MessageFixedText &t)
189       : location_{pr}, text_{t} {}
Message(ProvenanceRange pr,const MessageFormattedText & s)190   Message(ProvenanceRange pr, const MessageFormattedText &s)
191       : location_{pr}, text_{s} {}
Message(ProvenanceRange pr,MessageFormattedText && s)192   Message(ProvenanceRange pr, MessageFormattedText &&s)
193       : location_{pr}, text_{std::move(s)} {}
Message(ProvenanceRange pr,const MessageExpectedText & t)194   Message(ProvenanceRange pr, const MessageExpectedText &t)
195       : location_{pr}, text_{t} {}
196 
Message(CharBlock csr,const MessageFixedText & t)197   Message(CharBlock csr, const MessageFixedText &t)
198       : location_{csr}, text_{t} {}
Message(CharBlock csr,const MessageFormattedText & s)199   Message(CharBlock csr, const MessageFormattedText &s)
200       : location_{csr}, text_{s} {}
Message(CharBlock csr,MessageFormattedText && s)201   Message(CharBlock csr, MessageFormattedText &&s)
202       : location_{csr}, text_{std::move(s)} {}
Message(CharBlock csr,const MessageExpectedText & t)203   Message(CharBlock csr, const MessageExpectedText &t)
204       : location_{csr}, text_{t} {}
205 
206   template <typename RANGE, typename A, typename... As>
Message(RANGE r,const MessageFixedText & t,A && x,As &&...xs)207   Message(RANGE r, const MessageFixedText &t, A &&x, As &&...xs)
208       : location_{r}, text_{MessageFormattedText{
209                           t, std::forward<A>(x), std::forward<As>(xs)...}} {}
210 
attachment()211   Reference attachment() const { return attachment_; }
212 
SetContext(Message * c)213   void SetContext(Message *c) {
214     attachment_ = c;
215     attachmentIsContext_ = true;
216   }
217   Message &Attach(Message *);
218   Message &Attach(std::unique_ptr<Message> &&);
Attach(A &&...args)219   template <typename... A> Message &Attach(A &&...args) {
220     return Attach(new Message{std::forward<A>(args)...}); // reference-counted
221   }
222 
223   bool SortBefore(const Message &that) const;
224   bool IsFatal() const;
225   Severity severity() const;
226   Message &set_severity(Severity);
227   std::string ToString() const;
228   std::optional<ProvenanceRange> GetProvenanceRange(
229       const AllCookedSources &) const;
230   void Emit(llvm::raw_ostream &, const AllCookedSources &,
231       bool echoSourceLine = true) const;
232 
233   // If this Message or any of its attachments locates itself via a CharBlock,
234   // replace its location with the corresponding ProvenanceRange.
235   void ResolveProvenances(const AllCookedSources &);
236 
IsMergeable()237   bool IsMergeable() const {
238     return std::holds_alternative<MessageExpectedText>(text_);
239   }
240   bool Merge(const Message &);
241   bool operator==(const Message &that) const;
242   bool operator!=(const Message &that) const { return !(*this == that); }
243 
244 private:
245   bool AtSameLocation(const Message &) const;
246   std::variant<ProvenanceRange, CharBlock> location_;
247   std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText>
248       text_;
249   bool attachmentIsContext_{false};
250   Reference attachment_;
251 };
252 
253 class Messages {
254 public:
Messages()255   Messages() {}
Messages(Messages && that)256   Messages(Messages &&that) : messages_{std::move(that.messages_)} {}
257   Messages &operator=(Messages &&that) {
258     messages_ = std::move(that.messages_);
259     return *this;
260   }
261 
messages()262   std::list<Message> &messages() { return messages_; }
empty()263   bool empty() const { return messages_.empty(); }
clear()264   void clear() { messages_.clear(); }
265 
Say(A &&...args)266   template <typename... A> Message &Say(A &&...args) {
267     return messages_.emplace_back(std::forward<A>(args)...);
268   }
269 
Annex(Messages && that)270   void Annex(Messages &&that) {
271     messages_.splice(messages_.end(), that.messages_);
272   }
273 
274   bool Merge(const Message &);
275   void Merge(Messages &&);
276   void Copy(const Messages &);
277   void ResolveProvenances(const AllCookedSources &);
278   void Emit(llvm::raw_ostream &, const AllCookedSources &,
279       bool echoSourceLines = true) const;
280   void AttachTo(Message &, std::optional<Severity> = std::nullopt);
281   bool AnyFatalError() const;
282 
283 private:
284   std::list<Message> messages_;
285 };
286 
287 class ContextualMessages {
288 public:
289   ContextualMessages() = default;
ContextualMessages(CharBlock at,Messages * m)290   ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {}
ContextualMessages(Messages * m)291   explicit ContextualMessages(Messages *m) : messages_{m} {}
ContextualMessages(const ContextualMessages & that)292   ContextualMessages(const ContextualMessages &that)
293       : at_{that.at_}, messages_{that.messages_} {}
294 
at()295   CharBlock at() const { return at_; }
messages()296   Messages *messages() const { return messages_; }
contextMessage()297   Message::Reference contextMessage() const { return contextMessage_; }
empty()298   bool empty() const { return !messages_ || messages_->empty(); }
299 
300   // Set CharBlock for messages; restore when the returned value is deleted
SetLocation(CharBlock at)301   common::Restorer<CharBlock> SetLocation(CharBlock at) {
302     if (at.empty()) {
303       at = at_;
304     }
305     return common::ScopedSet(at_, std::move(at));
306   }
307 
SetContext(Message * m)308   common::Restorer<Message::Reference> SetContext(Message *m) {
309     if (!m) {
310       m = contextMessage_.get();
311     }
312     return common::ScopedSet(contextMessage_, m);
313   }
314 
315   // Diverts messages to another buffer; restored when the returned
316   // value is deleted.
SetMessages(Messages & buffer)317   common::Restorer<Messages *> SetMessages(Messages &buffer) {
318     return common::ScopedSet(messages_, &buffer);
319   }
320   // Discard future messages until the returned value is deleted.
DiscardMessages()321   common::Restorer<Messages *> DiscardMessages() {
322     return common::ScopedSet(messages_, nullptr);
323   }
324 
Say(CharBlock at,A &&...args)325   template <typename... A> Message *Say(CharBlock at, A &&...args) {
326     if (messages_ != nullptr) {
327       auto &msg{messages_->Say(at, std::forward<A>(args)...)};
328       if (contextMessage_) {
329         msg.SetContext(contextMessage_.get());
330       }
331       return &msg;
332     } else {
333       return nullptr;
334     }
335   }
336 
337   template <typename... A>
Say(std::optional<CharBlock> at,A &&...args)338   Message *Say(std::optional<CharBlock> at, A &&...args) {
339     return Say(at.value_or(at_), std::forward<A>(args)...);
340   }
341 
Say(A &&...args)342   template <typename... A> Message *Say(A &&...args) {
343     return Say(at_, std::forward<A>(args)...);
344   }
345 
346 private:
347   CharBlock at_;
348   Messages *messages_{nullptr};
349   Message::Reference contextMessage_;
350 };
351 } // namespace Fortran::parser
352 #endif // FORTRAN_PARSER_MESSAGE_H_
353