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