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