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