//===-- include/flang/Parser/message.h --------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef FORTRAN_PARSER_MESSAGE_H_ #define FORTRAN_PARSER_MESSAGE_H_ // Defines a representation for sequences of compiler messages. // Supports nested contextualization. #include "char-block.h" #include "char-set.h" #include "provenance.h" #include "flang/Common/idioms.h" #include "flang/Common/reference-counted.h" #include "flang/Common/restorer.h" #include #include #include #include #include #include #include #include namespace Fortran::parser { // Use "..."_err_en_US, "..."_warn_en_US, "..."_port_en_US, "..."_because_en_US, // "..."_todo_en_US, and "..."_en_US string literals to define the static text // and severity of a message or attachment. enum class Severity { Error, // fatal error that prevents code and module file generation Warning, // likely problem Portability, // nonstandard or obsolete features Because, // for AttachTo(), explanatory attachment to support another message Context, // (internal): attachment from SetContext() Todo, // a feature that's not yet implemented, a fatal error None // everything else, common for attachments with source locations }; class MessageFixedText { public: constexpr MessageFixedText() {} constexpr MessageFixedText( const char str[], std::size_t n, Severity severity = Severity::None) : text_{str, n}, severity_{severity} {} constexpr MessageFixedText(const MessageFixedText &) = default; constexpr MessageFixedText(MessageFixedText &&) = default; constexpr MessageFixedText &operator=(const MessageFixedText &) = default; constexpr MessageFixedText &operator=(MessageFixedText &&) = default; CharBlock text() const { return text_; } bool empty() const { return text_.empty(); } Severity severity() const { return severity_; } MessageFixedText &set_severity(Severity severity) { severity_ = severity; return *this; } bool IsFatal() const { return severity_ == Severity::Error || severity_ == Severity::Todo; } private: CharBlock text_; Severity severity_{Severity::None}; }; inline namespace literals { constexpr MessageFixedText operator""_err_en_US( const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::Error}; } constexpr MessageFixedText operator""_warn_en_US( const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::Warning}; } constexpr MessageFixedText operator""_port_en_US( const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::Portability}; } constexpr MessageFixedText operator""_because_en_US( const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::Because}; } constexpr MessageFixedText operator""_todo_en_US( const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::Todo}; } constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) { return MessageFixedText{str, n, Severity::None}; } } // namespace literals // The construction of a MessageFormattedText uses a MessageFixedText // as a vsnprintf() formatting string that is applied to the // following arguments. CharBlock, std::string, and std::string_view // argument values are also supported; they are automatically converted // into char pointers that are suitable for '%s' formatting. class MessageFormattedText { public: template MessageFormattedText(const MessageFixedText &text, A &&...x) : severity_{text.severity()} { Format(&text, Convert(std::forward(x))...); } MessageFormattedText(const MessageFormattedText &) = default; MessageFormattedText(MessageFormattedText &&) = default; MessageFormattedText &operator=(const MessageFormattedText &) = default; MessageFormattedText &operator=(MessageFormattedText &&) = default; const std::string &string() const { return string_; } bool IsFatal() const { return severity_ == Severity::Error || severity_ == Severity::Todo; } Severity severity() const { return severity_; } MessageFormattedText &set_severity(Severity severity) { severity_ = severity; return *this; } std::string MoveString() { return std::move(string_); } bool operator==(const MessageFormattedText &that) const { return severity_ == that.severity_ && string_ == that.string_; } bool operator!=(const MessageFormattedText &that) const { return !(*this == that); } private: void Format(const MessageFixedText *, ...); template A Convert(const A &x) { static_assert(!std::is_class_v>); return x; } template common::IfNoLvalue Convert(A &&x) { static_assert(!std::is_class_v>); return std::move(x); } const char *Convert(const char *s) { return s; } const char *Convert(char *s) { return s; } const char *Convert(const std::string &); const char *Convert(std::string &&); const char *Convert(const std::string_view &); const char *Convert(std::string_view &&); const char *Convert(CharBlock); std::intmax_t Convert(std::int64_t x) { return x; } std::uintmax_t Convert(std::uint64_t x) { return x; } Severity severity_; std::string string_; std::forward_list conversions_; // preserves created strings }; // Represents a formatted rendition of "expected '%s'"_err_en_US // on a constant text or a set of characters. class MessageExpectedText { public: MessageExpectedText(const char *s, std::size_t n) { if (n == std::string::npos) { n = std::strlen(s); } if (n == 1) { // Treat a one-character string as a singleton set for better merging. u_ = SetOfChars{*s}; } else { u_ = CharBlock{s, n}; } } constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {} constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {} constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {} MessageExpectedText(const MessageExpectedText &) = default; MessageExpectedText(MessageExpectedText &&) = default; MessageExpectedText &operator=(const MessageExpectedText &) = default; MessageExpectedText &operator=(MessageExpectedText &&) = default; std::string ToString() const; bool Merge(const MessageExpectedText &); private: std::variant u_; }; class Message : public common::ReferenceCounted { public: using Reference = common::CountedReference; Message(const Message &) = default; Message(Message &&) = default; Message &operator=(const Message &) = default; Message &operator=(Message &&) = default; Message(ProvenanceRange pr, const MessageFixedText &t) : location_{pr}, text_{t} {} Message(ProvenanceRange pr, const MessageFormattedText &s) : location_{pr}, text_{s} {} Message(ProvenanceRange pr, MessageFormattedText &&s) : location_{pr}, text_{std::move(s)} {} Message(ProvenanceRange pr, const MessageExpectedText &t) : location_{pr}, text_{t} {} Message(CharBlock csr, const MessageFixedText &t) : location_{csr}, text_{t} {} Message(CharBlock csr, const MessageFormattedText &s) : location_{csr}, text_{s} {} Message(CharBlock csr, MessageFormattedText &&s) : location_{csr}, text_{std::move(s)} {} Message(CharBlock csr, const MessageExpectedText &t) : location_{csr}, text_{t} {} template Message(RANGE r, const MessageFixedText &t, A &&x, As &&...xs) : location_{r}, text_{MessageFormattedText{ t, std::forward(x), std::forward(xs)...}} {} Reference attachment() const { return attachment_; } void SetContext(Message *c) { attachment_ = c; attachmentIsContext_ = true; } Message &Attach(Message *); Message &Attach(std::unique_ptr &&); template Message &Attach(A &&...args) { return Attach(new Message{std::forward(args)...}); // reference-counted } bool SortBefore(const Message &that) const; bool IsFatal() const; Severity severity() const; Message &set_severity(Severity); std::string ToString() const; std::optional GetProvenanceRange( const AllCookedSources &) const; void Emit(llvm::raw_ostream &, const AllCookedSources &, bool echoSourceLine = true) const; // If this Message or any of its attachments locates itself via a CharBlock, // replace its location with the corresponding ProvenanceRange. void ResolveProvenances(const AllCookedSources &); bool IsMergeable() const { return std::holds_alternative(text_); } bool Merge(const Message &); bool operator==(const Message &that) const; bool operator!=(const Message &that) const { return !(*this == that); } private: bool AtSameLocation(const Message &) const; std::variant location_; std::variant text_; bool attachmentIsContext_{false}; Reference attachment_; }; class Messages { public: Messages() {} Messages(Messages &&that) : messages_{std::move(that.messages_)} {} Messages &operator=(Messages &&that) { messages_ = std::move(that.messages_); return *this; } std::list &messages() { return messages_; } bool empty() const { return messages_.empty(); } void clear() { messages_.clear(); } template Message &Say(A &&...args) { return messages_.emplace_back(std::forward(args)...); } void Annex(Messages &&that) { messages_.splice(messages_.end(), that.messages_); } bool Merge(const Message &); void Merge(Messages &&); void Copy(const Messages &); void ResolveProvenances(const AllCookedSources &); void Emit(llvm::raw_ostream &, const AllCookedSources &, bool echoSourceLines = true) const; void AttachTo(Message &, std::optional = std::nullopt); bool AnyFatalError() const; private: std::list messages_; }; class ContextualMessages { public: ContextualMessages() = default; ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {} explicit ContextualMessages(Messages *m) : messages_{m} {} ContextualMessages(const ContextualMessages &that) : at_{that.at_}, messages_{that.messages_} {} CharBlock at() const { return at_; } Messages *messages() const { return messages_; } Message::Reference contextMessage() const { return contextMessage_; } bool empty() const { return !messages_ || messages_->empty(); } // Set CharBlock for messages; restore when the returned value is deleted common::Restorer SetLocation(CharBlock at) { if (at.empty()) { at = at_; } return common::ScopedSet(at_, std::move(at)); } common::Restorer SetContext(Message *m) { if (!m) { m = contextMessage_.get(); } return common::ScopedSet(contextMessage_, m); } // Diverts messages to another buffer; restored when the returned // value is deleted. common::Restorer SetMessages(Messages &buffer) { return common::ScopedSet(messages_, &buffer); } // Discard future messages until the returned value is deleted. common::Restorer DiscardMessages() { return common::ScopedSet(messages_, nullptr); } template Message *Say(CharBlock at, A &&...args) { if (messages_ != nullptr) { auto &msg{messages_->Say(at, std::forward(args)...)}; if (contextMessage_) { msg.SetContext(contextMessage_.get()); } return &msg; } else { return nullptr; } } template Message *Say(std::optional at, A &&...args) { return Say(at.value_or(at_), std::forward(args)...); } template Message *Say(A &&...args) { return Say(at_, std::forward(args)...); } Message *Say(Message &&msg) { if (messages_ != nullptr) { if (contextMessage_) { msg.SetContext(contextMessage_.get()); } return &messages_->Say(std::move(msg)); } else { return nullptr; } } private: CharBlock at_; Messages *messages_{nullptr}; Message::Reference contextMessage_; }; } // namespace Fortran::parser #endif // FORTRAN_PARSER_MESSAGE_H_