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 "flang/Support/Fortran-features.h"
22#include <cstddef>
23#include <cstring>
24#include <forward_list>
25#include <list>
26#include <optional>
27#include <string>
28#include <utility>
29#include <variant>
30
31namespace Fortran::parser {
32
33// Use "..."_err_en_US, "..."_warn_en_US, "..."_port_en_US, "..."_because_en_US,
34// "..."_todo_en_US, and "..."_en_US string literals to define the static text
35// and severity of a message or attachment.
36enum class Severity {
37 Error, // fatal error that prevents code and module file generation
38 Warning, // likely problem
39 Portability, // nonstandard or obsolete features
40 Because, // for AttachTo(), explanatory attachment to support another message
41 Context, // (internal): attachment from SetContext()
42 Todo, // a feature that's not yet implemented, a fatal error
43 None // everything else, common for attachments with source locations
44};
45
46class MessageFixedText {
47public:
48 constexpr MessageFixedText() {}
49 constexpr MessageFixedText(
50 const char str[], std::size_t n, Severity severity = Severity::None)
51 : text_{str, n}, severity_{severity} {}
52 constexpr MessageFixedText(const MessageFixedText &) = default;
53 constexpr MessageFixedText(MessageFixedText &&) = default;
54 constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
55 constexpr MessageFixedText &operator=(MessageFixedText &&) = default;
56
57 CharBlock text() const { return text_; }
58 bool empty() const { return text_.empty(); }
59 Severity severity() const { return severity_; }
60 MessageFixedText &set_severity(Severity severity) {
61 severity_ = severity;
62 return *this;
63 }
64 bool IsFatal() const {
65 return severity_ == Severity::Error || severity_ == Severity::Todo;
66 }
67
68 static const MessageFixedText endOfFileMessage; // "end of file"_err_en_US
69
70private:
71 CharBlock text_;
72 Severity severity_{Severity::None};
73};
74
75inline namespace literals {
76constexpr MessageFixedText operator""_err_en_US(
77 const char str[], std::size_t n) {
78 return MessageFixedText{str, n, Severity::Error};
79}
80constexpr MessageFixedText operator""_warn_en_US(
81 const char str[], std::size_t n) {
82 return MessageFixedText{str, n, Severity::Warning};
83}
84constexpr MessageFixedText operator""_port_en_US(
85 const char str[], std::size_t n) {
86 return MessageFixedText{str, n, Severity::Portability};
87}
88constexpr MessageFixedText operator""_because_en_US(
89 const char str[], std::size_t n) {
90 return MessageFixedText{str, n, Severity::Because};
91}
92constexpr MessageFixedText operator""_todo_en_US(
93 const char str[], std::size_t n) {
94 return MessageFixedText{str, n, Severity::Todo};
95}
96constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
97 return MessageFixedText{str, n, Severity::None};
98}
99} // namespace literals
100
101// The construction of a MessageFormattedText uses a MessageFixedText
102// as a vsnprintf() formatting string that is applied to the
103// following arguments. CharBlock, std::string, and std::string_view
104// argument values are also supported; they are automatically converted
105// into char pointers that are suitable for '%s' formatting.
106class MessageFormattedText {
107public:
108 template <typename... A>
109 MessageFormattedText(const MessageFixedText &text, A &&...x)
110 : severity_{text.severity()} {
111 Format(&text, Convert(std::forward<A>(x))...);
112 }
113 MessageFormattedText(const MessageFormattedText &) = default;
114 MessageFormattedText(MessageFormattedText &&) = default;
115 MessageFormattedText &operator=(const MessageFormattedText &) = default;
116 MessageFormattedText &operator=(MessageFormattedText &&) = default;
117 const std::string &string() const { return string_; }
118 bool IsFatal() const {
119 return severity_ == Severity::Error || severity_ == Severity::Todo;
120 }
121 Severity severity() const { return severity_; }
122 MessageFormattedText &set_severity(Severity severity) {
123 severity_ = severity;
124 return *this;
125 }
126 std::string MoveString() { return std::move(string_); }
127 bool operator==(const MessageFormattedText &that) const {
128 return severity_ == that.severity_ && string_ == that.string_;
129 }
130 bool operator!=(const MessageFormattedText &that) const {
131 return !(*this == that);
132 }
133
134private:
135 void Format(const MessageFixedText *, ...);
136
137 template <typename A> A Convert(const A &x) {
138 static_assert(!std::is_class_v<std::decay_t<A>>);
139 return x;
140 }
141 template <typename A> common::IfNoLvalue<A, A> Convert(A &&x) {
142 static_assert(!std::is_class_v<std::decay_t<A>>);
143 return std::move(x);
144 }
145 const char *Convert(const char *s) { return s; }
146 const char *Convert(char *s) { return s; }
147 const char *Convert(const std::string &);
148 const char *Convert(std::string &&);
149 const char *Convert(const std::string_view &);
150 const char *Convert(std::string_view &&);
151 const char *Convert(CharBlock);
152 std::intmax_t Convert(std::int64_t x) { return x; }
153 std::uintmax_t Convert(std::uint64_t x) { return x; }
154
155 Severity severity_;
156 std::string string_;
157 std::forward_list<std::string> conversions_; // preserves created strings
158};
159
160// Represents a formatted rendition of "expected '%s'"_err_en_US
161// on a constant text or a set of characters.
162class MessageExpectedText {
163public:
164 MessageExpectedText(const char *s, std::size_t n) {
165 if (n == std::string::npos) {
166 n = std::strlen(s);
167 }
168 if (n == 1) {
169 // Treat a one-character string as a singleton set for better merging.
170 u_ = SetOfChars{*s};
171 } else {
172 u_ = CharBlock{s, n};
173 }
174 }
175 constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {}
176 constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {}
177 constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {}
178 MessageExpectedText(const MessageExpectedText &) = default;
179 MessageExpectedText(MessageExpectedText &&) = default;
180 MessageExpectedText &operator=(const MessageExpectedText &) = default;
181 MessageExpectedText &operator=(MessageExpectedText &&) = default;
182
183 std::string ToString() const;
184 bool Merge(const MessageExpectedText &);
185
186private:
187 std::variant<CharBlock, SetOfChars> u_;
188};
189
190class Message : public common::ReferenceCounted<Message> {
191public:
192 using Reference = common::CountedReference<Message>;
193
194 Message(const Message &) = default;
195 Message(Message &&) = default;
196 Message &operator=(const Message &) = default;
197 Message &operator=(Message &&) = default;
198
199 Message(ProvenanceRange pr, const MessageFixedText &t)
200 : location_{pr}, text_{t} {}
201 Message(ProvenanceRange pr, const MessageFormattedText &s)
202 : location_{pr}, text_{s} {}
203 Message(ProvenanceRange pr, MessageFormattedText &&s)
204 : location_{pr}, text_{std::move(s)} {}
205 Message(ProvenanceRange pr, const MessageExpectedText &t)
206 : location_{pr}, text_{t} {}
207
208 Message(common::LanguageFeature feature, ProvenanceRange pr,
209 const MessageFixedText &t)
210 : location_{pr}, text_{t}, languageFeature_{feature} {}
211 Message(common::LanguageFeature feature, ProvenanceRange pr,
212 const MessageFormattedText &s)
213 : location_{pr}, text_{s}, languageFeature_{feature} {}
214 Message(common::LanguageFeature feature, ProvenanceRange pr,
216 : location_{pr}, text_{std::move(s)}, languageFeature_{feature} {}
217
218 Message(common::UsageWarning warning, ProvenanceRange pr,
219 const MessageFixedText &t)
220 : location_{pr}, text_{t}, usageWarning_{warning} {}
221 Message(common::UsageWarning warning, ProvenanceRange pr,
222 const MessageFormattedText &s)
223 : location_{pr}, text_{s}, usageWarning_{warning} {}
224 Message(common::UsageWarning warning, ProvenanceRange pr,
226 : location_{pr}, text_{std::move(s)}, usageWarning_{warning} {}
227
228 Message(CharBlock csr, const MessageFixedText &t)
229 : location_{csr}, text_{t} {}
230 Message(CharBlock csr, const MessageFormattedText &s)
231 : location_{csr}, text_{s} {}
232 Message(CharBlock csr, MessageFormattedText &&s)
233 : location_{csr}, text_{std::move(s)} {}
234 Message(CharBlock csr, const MessageExpectedText &t)
235 : location_{csr}, text_{t} {}
236
237 Message(
238 common::LanguageFeature feature, CharBlock csr, const MessageFixedText &t)
239 : location_{csr}, text_{t}, languageFeature_{feature} {}
240 Message(common::LanguageFeature feature, CharBlock csr,
241 const MessageFormattedText &s)
242 : location_{csr}, text_{s}, languageFeature_{feature} {}
243 Message(
244 common::LanguageFeature feature, CharBlock csr, MessageFormattedText &&s)
245 : location_{csr}, text_{std::move(s)}, languageFeature_{feature} {}
246
247 Message(
248 common::UsageWarning warning, CharBlock csr, const MessageFixedText &t)
249 : location_{csr}, text_{t}, usageWarning_{warning} {}
250 Message(common::UsageWarning warning, CharBlock csr,
251 const MessageFormattedText &s)
252 : location_{csr}, text_{s}, usageWarning_{warning} {}
253 Message(common::UsageWarning warning, CharBlock csr, MessageFormattedText &&s)
254 : location_{csr}, text_{std::move(s)}, usageWarning_{warning} {}
255
256 template <typename RANGE, typename A, typename... As>
257 Message(RANGE r, const MessageFixedText &t, A &&x, As &&...xs)
258 : location_{r}, text_{MessageFormattedText{
259 t, std::forward<A>(x), std::forward<As>(xs)...}} {}
260 template <typename RANGE, typename A, typename... As>
261 Message(common::LanguageFeature feature, RANGE r, const MessageFixedText &t,
262 A &&x, As &&...xs)
263 : location_{r}, text_{MessageFormattedText{
264 t, std::forward<A>(x), std::forward<As>(xs)...}},
265 languageFeature_{feature} {}
266 template <typename RANGE, typename A, typename... As>
267 Message(common::UsageWarning warning, RANGE r, const MessageFixedText &t,
268 A &&x, As &&...xs)
269 : location_{r}, text_{MessageFormattedText{
270 t, std::forward<A>(x), std::forward<As>(xs)...}},
271 usageWarning_{warning} {}
272
273 Reference attachment() const { return attachment_; }
274
275 void SetContext(Message *c) {
276 attachment_ = c;
277 attachmentIsContext_ = true;
278 }
279 Message &Attach(Message *);
280 Message &Attach(std::unique_ptr<Message> &&);
281 template <typename... A> Message &Attach(A &&...args) {
282 return Attach(new Message{std::forward<A>(args)...}); // reference-counted
283 }
284
285 bool SortBefore(const Message &that) const;
286 bool IsFatal() const;
287 Severity severity() const;
288 Message &set_severity(Severity);
289 std::optional<common::LanguageFeature> languageFeature() const;
290 Message &set_languageFeature(common::LanguageFeature);
291 std::optional<common::UsageWarning> usageWarning() const;
292 Message &set_usageWarning(common::UsageWarning);
293 std::string ToString() const;
294 std::optional<ProvenanceRange> GetProvenanceRange(
295 const AllCookedSources &) const;
296 void Emit(llvm::raw_ostream &, const AllCookedSources &,
297 bool echoSourceLine = true,
298 const common::LanguageFeatureControl *hintFlags = nullptr) const;
299
300 // If this Message or any of its attachments locates itself via a CharBlock,
301 // replace its location with the corresponding ProvenanceRange.
302 void ResolveProvenances(const AllCookedSources &);
303
304 bool IsMergeable() const {
305 return std::holds_alternative<MessageExpectedText>(text_);
306 }
307 bool Merge(const Message &);
308 bool operator==(const Message &that) const;
309 bool operator!=(const Message &that) const { return !(*this == that); }
310 bool AtSameLocation(const Message &) const;
311
312private:
313 std::variant<ProvenanceRange, CharBlock> location_;
314 std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText>
315 text_;
316 bool attachmentIsContext_{false};
317 Reference attachment_;
318 std::optional<common::LanguageFeature> languageFeature_;
319 std::optional<common::UsageWarning> usageWarning_;
320};
321
322class Messages {
323public:
324 Messages() {}
325 Messages(Messages &&that) : messages_{std::move(that.messages_)} {}
326 Messages &operator=(Messages &&that) {
327 messages_ = std::move(that.messages_);
328 return *this;
329 }
330
331 std::list<Message> &messages() { return messages_; }
332 bool empty() const { return messages_.empty(); }
333 void clear() { messages_.clear(); }
334
335 template <typename... A> Message &Say(A &&...args) {
336 return messages_.emplace_back(std::forward<A>(args)...);
337 }
338
339 template <typename... A>
340 Message *Warn(bool isInModuleFile,
341 const common::LanguageFeatureControl &control,
342 common::LanguageFeature feature, A &&...args) {
343 if (!isInModuleFile && control.ShouldWarn(feature)) {
344 return &AddWarning(feature, std::forward<A>(args)...);
345 }
346 return nullptr;
347 }
348
349 template <typename... A>
350 Message *Warn(bool isInModuleFile,
351 const common::LanguageFeatureControl &control,
352 common::UsageWarning warning, A &&...args) {
353 if (!isInModuleFile && control.ShouldWarn(warning)) {
354 return &AddWarning(warning, std::forward<A>(args)...);
355 }
356 return nullptr;
357 }
358
359 void Annex(Messages &&that) {
360 messages_.splice(messages_.end(), that.messages_);
361 }
362
363 bool Merge(const Message &);
364 void Merge(Messages &&);
365 void Copy(const Messages &);
366 void ResolveProvenances(const AllCookedSources &);
367 void Emit(llvm::raw_ostream &, const AllCookedSources &,
368 bool echoSourceLines = true,
369 const common::LanguageFeatureControl *hintFlags = nullptr,
370 std::size_t maxErrorsToEmit = 0, bool warningsAreErrors = false) const;
371 void AttachTo(Message &, std::optional<Severity> = std::nullopt);
372 bool AnyFatalError(bool warningsAreErrors = false) const;
373
374private:
375 template <typename... A>
376 Message &AddWarning(common::UsageWarning warning, A &&...args) {
377 return messages_.emplace_back(warning, std::forward<A>(args)...);
378 }
379 template <typename... A>
380 Message &AddWarning(common::LanguageFeature feature, A &&...args) {
381 return messages_.emplace_back(feature, std::forward<A>(args)...);
382 }
383 std::list<Message> messages_;
384};
385
386class ContextualMessages {
387public:
388 ContextualMessages() = default;
389 ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {}
390 explicit ContextualMessages(Messages *m) : messages_{m} {}
391 ContextualMessages(const ContextualMessages &that)
392 : at_{that.at_}, messages_{that.messages_} {}
393
394 CharBlock at() const { return at_; }
395 Messages *messages() const { return messages_; }
396 Message::Reference contextMessage() const { return contextMessage_; }
397 bool empty() const { return !messages_ || messages_->empty(); }
398
399 // Set CharBlock for messages; restore when the returned value is deleted
400 common::Restorer<CharBlock> SetLocation(CharBlock at) {
401 if (at.empty()) {
402 at = at_;
403 }
404 return common::ScopedSet(at_, std::move(at));
405 }
406
408 if (!m) {
409 m = contextMessage_.get();
410 }
411 return common::ScopedSet(contextMessage_, m);
412 }
413
414 // Diverts messages to another buffer; restored when the returned
415 // value is deleted.
416 common::Restorer<Messages *> SetMessages(Messages &buffer) {
417 return common::ScopedSet(messages_, &buffer);
418 }
419 // Discard future messages until the returned value is deleted.
420 common::Restorer<Messages *> DiscardMessages() {
421 return common::ScopedSet(messages_, nullptr);
422 }
423
424 template <typename... A> Message *Say(A &&...args) {
425 return Say(at_, std::forward<A>(args)...);
426 }
427
428 template <typename... A> Message *Say(CharBlock at, A &&...args) {
429 if (messages_ != nullptr) {
430 auto &msg{messages_->Say(at, std::forward<A>(args)...)};
431 if (contextMessage_) {
432 msg.SetContext(contextMessage_.get());
433 }
434 return &msg;
435 } else {
436 return nullptr;
437 }
438 }
439
440 template <typename... A>
441 Message *Say(std::optional<CharBlock> at, A &&...args) {
442 return Say(at.value_or(at_), std::forward<A>(args)...);
443 }
444
445 Message *Say(Message &&msg) {
446 if (messages_ != nullptr) {
447 if (contextMessage_) {
448 msg.SetContext(contextMessage_.get());
449 }
450 return &messages_->Say(std::move(msg));
451 } else {
452 return nullptr;
453 }
454 }
455
456 template <typename FeatureOrUsageWarning, typename... A>
457 Message *Warn(bool isInModuleFile,
458 const common::LanguageFeatureControl &control,
459 FeatureOrUsageWarning feature, CharBlock at, A &&...args) {
460 if (messages_ != nullptr) {
461 if (Message *
462 msg{messages_->Warn(isInModuleFile, control, feature, at,
463 std::forward<A>(args)...)}) {
464 if (contextMessage_) {
465 msg->SetContext(contextMessage_.get());
466 }
467 return msg;
468 }
469 }
470 return nullptr;
471 }
472
473 template <typename FeatureOrUsageWarning, typename... A>
474 Message *Warn(bool isInModuleFile,
475 const common::LanguageFeatureControl &control,
476 FeatureOrUsageWarning feature, A &&...args) {
477 return Warn(
478 isInModuleFile, control, feature, at_, std::forward<A>(args)...);
479 }
480
481 template <typename FeatureOrUsageWarning, typename... A>
482 Message *Warn(bool isInModuleFile,
483 const common::LanguageFeatureControl &control,
484 FeatureOrUsageWarning feature, std::optional<CharBlock> at, A &&...args) {
485 return Warn(isInModuleFile, control, feature, at.value_or(at_),
486 std::forward<A>(args)...);
487 }
488
489private:
490 CharBlock at_;
491 Messages *messages_{nullptr};
492 Message::Reference contextMessage_;
493};
494} // namespace Fortran::parser
495#endif // FORTRAN_PARSER_MESSAGE_H_
Definition reference-counted.h:35
Definition Fortran-features.h:90
Definition reference-counted.h:19
Definition restorer.h:24
Definition provenance.h:281
Definition char-block.h:28
Definition message.h:162
Definition message.h:46
Definition message.h:190
Definition message.h:322
Definition check-expression.h:19
Definition char-set.h:23