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