1 // Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef FORTRAN_PARSER_MESSAGE_H_
16 #define FORTRAN_PARSER_MESSAGE_H_
17 
18 // Defines a representation for sequences of compiler messages.
19 // Supports nested contextualization.
20 
21 #include "char-block.h"
22 #include "char-set.h"
23 #include "provenance.h"
24 #include "../common/idioms.h"
25 #include "../common/reference-counted.h"
26 #include "../common/restorer.h"
27 #include <cstddef>
28 #include <cstring>
29 #include <forward_list>
30 #include <optional>
31 #include <ostream>
32 #include <string>
33 #include <utility>
34 #include <variant>
35 
36 namespace Fortran::parser {
37 
38 // Use "..."_err_en_US and "..."_en_US literals to define the static
39 // text and fatality of a message.
40 class MessageFixedText {
41 public:
42   constexpr MessageFixedText(
43       const char str[], std::size_t n, bool isFatal = false)
44     : text_{str, n}, isFatal_{isFatal} {}
45   constexpr MessageFixedText(const MessageFixedText &) = default;
46   constexpr MessageFixedText(MessageFixedText &&) = default;
47   constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
48   constexpr MessageFixedText &operator=(MessageFixedText &&) = default;
49 
text()50   const CharBlock &text() const { return text_; }
isFatal()51   bool isFatal() const { return isFatal_; }
52 
53 private:
54   CharBlock text_;
55   bool isFatal_{false};
56 };
57 
58 inline namespace literals {
59 constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
60   return MessageFixedText{str, n, false /* not fatal */};
61 }
62 
63 constexpr MessageFixedText operator""_err_en_US(
64     const char str[], std::size_t n) {
65   return MessageFixedText{str, n, true /* fatal */};
66 }
67 }
68 
69 // The construction of a MessageFormattedText uses a MessageFixedText
70 // as a vsnprintf() formatting string that is applied to the
71 // following arguments.  CharBlock and std::string argument
72 // values are also supported; they are automatically converted into
73 // char pointers that are suitable for '%s' formatting.
74 class MessageFormattedText {
75 public:
76   template<typename... A>
MessageFormattedText(const MessageFixedText & text,A &&...x)77   MessageFormattedText(const MessageFixedText &text, A &&... x)
78     : isFatal_{text.isFatal()} {
79     Format(&text, Convert(std::forward<A>(x))...);
80   }
81   MessageFormattedText(const MessageFormattedText &) = default;
82   MessageFormattedText(MessageFormattedText &&) = default;
83   MessageFormattedText &operator=(const MessageFormattedText &) = default;
84   MessageFormattedText &operator=(MessageFormattedText &&) = default;
string()85   const std::string &string() const { return string_; }
isFatal()86   bool isFatal() const { return isFatal_; }
MoveString()87   std::string MoveString() { return std::move(string_); }
88 
89 private:
90   void Format(const MessageFixedText *text, ...);
91 
Convert(const A & x)92   template<typename A> A Convert(const A &x) {
93     static_assert(!std::is_class_v<std::decay_t<A>>);
94     return x;
95   }
Convert(A & x)96   template<typename A> A Convert(A &x) {
97     static_assert(!std::is_class_v<std::decay_t<A>>);
98     return x;
99   }
Convert(A && x)100   template<typename A> common::IfNoLvalue<A, A> Convert(A &&x) {
101     static_assert(!std::is_class_v<std::decay_t<A>>);
102     return std::move(x);
103   }
Convert(const char * s)104   const char *Convert(const char *s) { return s; }
Convert(char * s)105   const char *Convert(char *s) { return s; }
106   const char *Convert(const std::string &);
107   const char *Convert(std::string &);
108   const char *Convert(std::string &&);
109   const char *Convert(const CharBlock &);
110   const char *Convert(CharBlock &);
111   const char *Convert(CharBlock &&);
112 
113   bool isFatal_{false};
114   std::string string_;
115   std::forward_list<std::string> conversions_;  // preserves created strings
116 };
117 
118 // Represents a formatted rendition of "expected '%s'"_err_en_US
119 // on a constant text or a set of characters.
120 class MessageExpectedText {
121 public:
MessageExpectedText(const char * s,std::size_t n)122   MessageExpectedText(const char *s, std::size_t n) {
123     if (n == std::string::npos) {
124       n = std::strlen(s);
125     }
126     if (n == 1) {
127       // Treat a one-character string as a singleton set for better merging.
128       u_ = SetOfChars{*s};
129     } else {
130       u_ = CharBlock{s, n};
131     }
132   }
MessageExpectedText(CharBlock cb)133   constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {}
MessageExpectedText(char ch)134   constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {}
MessageExpectedText(SetOfChars set)135   constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {}
136   MessageExpectedText(const MessageExpectedText &) = default;
137   MessageExpectedText(MessageExpectedText &&) = default;
138   MessageExpectedText &operator=(const MessageExpectedText &) = default;
139   MessageExpectedText &operator=(MessageExpectedText &&) = default;
140 
141   std::string ToString() const;
142   bool Merge(const MessageExpectedText &);
143 
144 private:
145   std::variant<CharBlock, SetOfChars> u_;
146 };
147 
148 class Message : public common::ReferenceCounted<Message> {
149 public:
150   using Reference = common::CountedReference<Message>;
151 
152   Message(const Message &) = default;
153   Message(Message &&) = default;
154   Message &operator=(const Message &) = default;
155   Message &operator=(Message &&) = default;
156 
Message(ProvenanceRange pr,const MessageFixedText & t)157   Message(ProvenanceRange pr, const MessageFixedText &t)
158     : location_{pr}, text_{t} {}
Message(ProvenanceRange pr,const MessageFormattedText & s)159   Message(ProvenanceRange pr, const MessageFormattedText &s)
160     : location_{pr}, text_{s} {}
Message(ProvenanceRange pr,MessageFormattedText && s)161   Message(ProvenanceRange pr, MessageFormattedText &&s)
162     : location_{pr}, text_{std::move(s)} {}
Message(ProvenanceRange pr,const MessageExpectedText & t)163   Message(ProvenanceRange pr, const MessageExpectedText &t)
164     : location_{pr}, text_{t} {}
165 
Message(CharBlock csr,const MessageFixedText & t)166   Message(CharBlock csr, const MessageFixedText &t)
167     : location_{csr}, text_{t} {}
Message(CharBlock csr,const MessageFormattedText & s)168   Message(CharBlock csr, const MessageFormattedText &s)
169     : location_{csr}, text_{s} {}
Message(CharBlock csr,MessageFormattedText && s)170   Message(CharBlock csr, MessageFormattedText &&s)
171     : location_{csr}, text_{std::move(s)} {}
Message(CharBlock csr,const MessageExpectedText & t)172   Message(CharBlock csr, const MessageExpectedText &t)
173     : location_{csr}, text_{t} {}
174 
175   template<typename RANGE, typename A, typename... As>
Message(RANGE r,const MessageFixedText & t,A && x,As &&...xs)176   Message(RANGE r, const MessageFixedText &t, A &&x, As &&... xs)
177     : location_{r}, text_{MessageFormattedText{
178                         t, std::forward<A>(x), std::forward<As>(xs)...}} {}
179 
attachmentIsContext()180   bool attachmentIsContext() const { return attachmentIsContext_; }
attachment()181   Reference attachment() const { return attachment_; }
182 
SetContext(Message * c)183   void SetContext(Message *c) {
184     attachment_ = c;
185     attachmentIsContext_ = true;
186   }
187   Message &Attach(Message *);
Attach(A &&...args)188   template<typename... A> Message &Attach(A &&... args) {
189     return Attach(new Message{std::forward<A>(args)...});  // reference-counted
190   }
191 
192   bool SortBefore(const Message &that) const;
193   bool IsFatal() const;
194   std::string ToString() const;
195   std::optional<ProvenanceRange> GetProvenanceRange(const CookedSource &) const;
196   void Emit(
197       std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
198 
199   // If this Message or any of its attachments locates itself via a CharBlock
200   // within a particular CookedSource, replace its location with the
201   // corresponding ProvenanceRange.
202   void ResolveProvenances(const CookedSource &);
203 
IsMergeable()204   bool IsMergeable() const {
205     return std::holds_alternative<MessageExpectedText>(text_);
206   }
207   bool Merge(const Message &);
208 
209 private:
210   bool AtSameLocation(const Message &) const;
211 
212   std::variant<ProvenanceRange, CharBlock> location_;
213   std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText>
214       text_;
215   bool attachmentIsContext_{false};
216   Reference attachment_;
217 };
218 
219 class Messages {
220 public:
Messages()221   Messages() {}
Messages(Messages && that)222   Messages(Messages &&that) : messages_{std::move(that.messages_)} {
223     if (!messages_.empty()) {
224       last_ = that.last_;
225       that.ResetLastPointer();
226     }
227   }
228   Messages &operator=(Messages &&that) {
229     messages_ = std::move(that.messages_);
230     if (messages_.empty()) {
231       ResetLastPointer();
232     } else {
233       last_ = that.last_;
234       that.ResetLastPointer();
235     }
236     return *this;
237   }
238 
messages()239   std::forward_list<Message> &messages() { return messages_; }
empty()240   bool empty() const { return messages_.empty(); }
241   void clear();
242 
Say(A &&...args)243   template<typename... A> Message &Say(A &&... args) {
244     last_ = messages_.emplace_after(last_, std::forward<A>(args)...);
245     return *last_;
246   }
247 
Annex(Messages && that)248   void Annex(Messages &&that) {
249     if (!that.messages_.empty()) {
250       messages_.splice_after(last_, that.messages_);
251       last_ = that.last_;
252       that.ResetLastPointer();
253     }
254   }
255 
Restore(Messages && that)256   void Restore(Messages &&that) {
257     that.Annex(std::move(*this));
258     *this = std::move(that);
259   }
260 
261   bool Merge(const Message &);
262   void Merge(Messages &&);
263   void Copy(const Messages &);
264   void ResolveProvenances(const CookedSource &);
265   void Emit(std::ostream &, const CookedSource &cooked,
266       bool echoSourceLines = true) const;
267   void AttachTo(Message &);
268   bool AnyFatalError() const;
269 
270 private:
ResetLastPointer()271   void ResetLastPointer() { last_ = messages_.before_begin(); }
272 
273   std::forward_list<Message> messages_;
274   std::forward_list<Message>::iterator last_{messages_.before_begin()};
275 };
276 
277 class ContextualMessages {
278 public:
279   ContextualMessages() = default;
ContextualMessages(CharBlock at,Messages * m)280   ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {}
ContextualMessages(Messages * m)281   explicit ContextualMessages(Messages *m) : messages_{m} {}
ContextualMessages(const ContextualMessages & that)282   ContextualMessages(const ContextualMessages &that)
283     : at_{that.at_}, messages_{that.messages_} {}
284 
at()285   CharBlock at() const { return at_; }
messages()286   Messages *messages() const { return messages_; }
287 
288   // Set CharBlock for messages; restore when the returned value is deleted
SetLocation(CharBlock at)289   common::Restorer<CharBlock> SetLocation(CharBlock at) {
290     if (at.empty()) {
291       at = at_;
292     }
293     return common::ScopedSet(at_, std::move(at));
294   }
295 
Say(CharBlock at,A &&...args)296   template<typename... A> Message *Say(CharBlock at, A &&... args) {
297     if (messages_ != nullptr) {
298       return &messages_->Say(at, std::forward<A>(args)...);
299     } else {
300       return nullptr;
301     }
302   }
303 
Say(A &&...args)304   template<typename... A> Message *Say(A &&... args) {
305     return Say(at_, std::forward<A>(args)...);
306   }
307 
308 private:
309   CharBlock at_;
310   Messages *messages_{nullptr};
311 };
312 }
313 #endif  // FORTRAN_PARSER_MESSAGE_H_
314