FLANG

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