Warning: This file is not a C or C++ file. It does not have highlighting.

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

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of flang/include/flang/Parser/message.h