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 <cstddef> 22 #include <cstring> 23 #include <forward_list> 24 #include <list> 25 #include <optional> 26 #include <string> 27 #include <utility> 28 #include <variant> 29 30 namespace Fortran::parser { 31 32 // Use "..."_err_en_US and "..."_en_US literals to define the static 33 // text and fatality of a message. 34 class MessageFixedText { 35 public: 36 constexpr MessageFixedText( 37 const char str[], std::size_t n, bool isFatal = false) 38 : text_{str, n}, isFatal_{isFatal} {} 39 constexpr MessageFixedText(const MessageFixedText &) = default; 40 constexpr MessageFixedText(MessageFixedText &&) = default; 41 constexpr MessageFixedText &operator=(const MessageFixedText &) = default; 42 constexpr MessageFixedText &operator=(MessageFixedText &&) = default; 43 text()44 CharBlock text() const { return text_; } isFatal()45 bool isFatal() const { return isFatal_; } 46 47 private: 48 CharBlock text_; 49 bool isFatal_{false}; 50 }; 51 52 inline namespace literals { 53 constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) { 54 return MessageFixedText{str, n, false /* not fatal */}; 55 } 56 57 constexpr MessageFixedText operator""_err_en_US( 58 const char str[], std::size_t n) { 59 return MessageFixedText{str, n, true /* fatal */}; 60 } 61 } // namespace literals 62 63 // The construction of a MessageFormattedText uses a MessageFixedText 64 // as a vsnprintf() formatting string that is applied to the 65 // following arguments. CharBlock and std::string argument 66 // values are also supported; they are automatically converted into 67 // char pointers that are suitable for '%s' formatting. 68 class MessageFormattedText { 69 public: 70 template <typename... A> MessageFormattedText(const MessageFixedText & text,A &&...x)71 MessageFormattedText(const MessageFixedText &text, A &&...x) 72 : isFatal_{text.isFatal()} { 73 Format(&text, Convert(std::forward<A>(x))...); 74 } 75 MessageFormattedText(const MessageFormattedText &) = default; 76 MessageFormattedText(MessageFormattedText &&) = default; 77 MessageFormattedText &operator=(const MessageFormattedText &) = default; 78 MessageFormattedText &operator=(MessageFormattedText &&) = default; string()79 const std::string &string() const { return string_; } isFatal()80 bool isFatal() const { return isFatal_; } MoveString()81 std::string MoveString() { return std::move(string_); } 82 83 private: 84 void Format(const MessageFixedText *, ...); 85 Convert(const A & x)86 template <typename A> A Convert(const A &x) { 87 static_assert(!std::is_class_v<std::decay_t<A>>); 88 return x; 89 } Convert(A & x)90 template <typename A> A Convert(A &x) { 91 static_assert(!std::is_class_v<std::decay_t<A>>); 92 return x; 93 } Convert(A && x)94 template <typename A> common::IfNoLvalue<A, A> Convert(A &&x) { 95 static_assert(!std::is_class_v<std::decay_t<A>>); 96 return std::move(x); 97 } Convert(const char * s)98 const char *Convert(const char *s) { return s; } Convert(char * s)99 const char *Convert(char *s) { return s; } 100 const char *Convert(const std::string &); 101 const char *Convert(std::string &); 102 const char *Convert(std::string &&); 103 const char *Convert(CharBlock); Convert(std::int64_t x)104 std::intmax_t Convert(std::int64_t x) { return x; } Convert(std::uint64_t x)105 std::uintmax_t Convert(std::uint64_t x) { return x; } 106 107 bool isFatal_{false}; 108 std::string string_; 109 std::forward_list<std::string> conversions_; // preserves created strings 110 }; 111 112 // Represents a formatted rendition of "expected '%s'"_err_en_US 113 // on a constant text or a set of characters. 114 class MessageExpectedText { 115 public: MessageExpectedText(const char * s,std::size_t n)116 MessageExpectedText(const char *s, std::size_t n) { 117 if (n == std::string::npos) { 118 n = std::strlen(s); 119 } 120 if (n == 1) { 121 // Treat a one-character string as a singleton set for better merging. 122 u_ = SetOfChars{*s}; 123 } else { 124 u_ = CharBlock{s, n}; 125 } 126 } MessageExpectedText(CharBlock cb)127 constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {} MessageExpectedText(char ch)128 constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {} MessageExpectedText(SetOfChars set)129 constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {} 130 MessageExpectedText(const MessageExpectedText &) = default; 131 MessageExpectedText(MessageExpectedText &&) = default; 132 MessageExpectedText &operator=(const MessageExpectedText &) = default; 133 MessageExpectedText &operator=(MessageExpectedText &&) = default; 134 135 std::string ToString() const; 136 bool Merge(const MessageExpectedText &); 137 138 private: 139 std::variant<CharBlock, SetOfChars> u_; 140 }; 141 142 class Message : public common::ReferenceCounted<Message> { 143 public: 144 using Reference = common::CountedReference<Message>; 145 146 Message(const Message &) = default; 147 Message(Message &&) = default; 148 Message &operator=(const Message &) = default; 149 Message &operator=(Message &&) = default; 150 Message(ProvenanceRange pr,const MessageFixedText & t)151 Message(ProvenanceRange pr, const MessageFixedText &t) 152 : location_{pr}, text_{t} {} Message(ProvenanceRange pr,const MessageFormattedText & s)153 Message(ProvenanceRange pr, const MessageFormattedText &s) 154 : location_{pr}, text_{s} {} Message(ProvenanceRange pr,MessageFormattedText && s)155 Message(ProvenanceRange pr, MessageFormattedText &&s) 156 : location_{pr}, text_{std::move(s)} {} Message(ProvenanceRange pr,const MessageExpectedText & t)157 Message(ProvenanceRange pr, const MessageExpectedText &t) 158 : location_{pr}, text_{t} {} 159 Message(CharBlock csr,const MessageFixedText & t)160 Message(CharBlock csr, const MessageFixedText &t) 161 : location_{csr}, text_{t} {} Message(CharBlock csr,const MessageFormattedText & s)162 Message(CharBlock csr, const MessageFormattedText &s) 163 : location_{csr}, text_{s} {} Message(CharBlock csr,MessageFormattedText && s)164 Message(CharBlock csr, MessageFormattedText &&s) 165 : location_{csr}, text_{std::move(s)} {} Message(CharBlock csr,const MessageExpectedText & t)166 Message(CharBlock csr, const MessageExpectedText &t) 167 : location_{csr}, text_{t} {} 168 169 template <typename RANGE, typename A, typename... As> Message(RANGE r,const MessageFixedText & t,A && x,As &&...xs)170 Message(RANGE r, const MessageFixedText &t, A &&x, As &&...xs) 171 : location_{r}, text_{MessageFormattedText{ 172 t, std::forward<A>(x), std::forward<As>(xs)...}} {} 173 attachmentIsContext()174 bool attachmentIsContext() const { return attachmentIsContext_; } attachment()175 Reference attachment() const { return attachment_; } 176 SetContext(Message * c)177 void SetContext(Message *c) { 178 attachment_ = c; 179 attachmentIsContext_ = true; 180 } 181 Message &Attach(Message *); 182 Message &Attach(std::unique_ptr<Message> &&); Attach(A &&...args)183 template <typename... A> Message &Attach(A &&...args) { 184 return Attach(new Message{std::forward<A>(args)...}); // reference-counted 185 } 186 187 bool SortBefore(const Message &that) const; 188 bool IsFatal() const; 189 std::string ToString() const; 190 std::optional<ProvenanceRange> GetProvenanceRange( 191 const AllCookedSources &) const; 192 void Emit(llvm::raw_ostream &, const AllCookedSources &, 193 bool echoSourceLine = true) const; 194 195 // If this Message or any of its attachments locates itself via a CharBlock, 196 // replace its location with the corresponding ProvenanceRange. 197 void ResolveProvenances(const AllCookedSources &); 198 IsMergeable()199 bool IsMergeable() const { 200 return std::holds_alternative<MessageExpectedText>(text_); 201 } 202 bool Merge(const Message &); 203 204 private: 205 bool AtSameLocation(const Message &) const; 206 207 std::variant<ProvenanceRange, CharBlock> location_; 208 std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText> 209 text_; 210 bool attachmentIsContext_{false}; 211 Reference attachment_; 212 }; 213 214 class Messages { 215 public: Messages()216 Messages() {} Messages(Messages && that)217 Messages(Messages &&that) : messages_{std::move(that.messages_)} {} 218 Messages &operator=(Messages &&that) { 219 messages_ = std::move(that.messages_); 220 return *this; 221 } 222 messages()223 std::list<Message> &messages() { return messages_; } empty()224 bool empty() const { return messages_.empty(); } clear()225 void clear() { messages_.clear(); } 226 Say(A &&...args)227 template <typename... A> Message &Say(A &&...args) { 228 return messages_.emplace_back(std::forward<A>(args)...); 229 } 230 Annex(Messages && that)231 void Annex(Messages &&that) { 232 messages_.splice(messages_.end(), that.messages_); 233 } 234 235 bool Merge(const Message &); 236 void Merge(Messages &&); 237 void Copy(const Messages &); 238 void ResolveProvenances(const AllCookedSources &); 239 void Emit(llvm::raw_ostream &, const AllCookedSources &, 240 bool echoSourceLines = true) const; 241 void AttachTo(Message &); 242 bool AnyFatalError() const; 243 244 private: 245 std::list<Message> messages_; 246 }; 247 248 class ContextualMessages { 249 public: 250 ContextualMessages() = default; ContextualMessages(CharBlock at,Messages * m)251 ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {} ContextualMessages(Messages * m)252 explicit ContextualMessages(Messages *m) : messages_{m} {} ContextualMessages(const ContextualMessages & that)253 ContextualMessages(const ContextualMessages &that) 254 : at_{that.at_}, messages_{that.messages_} {} 255 at()256 CharBlock at() const { return at_; } messages()257 Messages *messages() const { return messages_; } contextMessage()258 Message::Reference contextMessage() const { return contextMessage_; } empty()259 bool empty() const { return !messages_ || messages_->empty(); } 260 261 // Set CharBlock for messages; restore when the returned value is deleted SetLocation(CharBlock at)262 common::Restorer<CharBlock> SetLocation(CharBlock at) { 263 if (at.empty()) { 264 at = at_; 265 } 266 return common::ScopedSet(at_, std::move(at)); 267 } 268 SetContext(Message * m)269 common::Restorer<Message::Reference> SetContext(Message *m) { 270 if (!m) { 271 m = contextMessage_.get(); 272 } 273 return common::ScopedSet(contextMessage_, m); 274 } 275 276 // Diverts messages to another buffer; restored when the returned 277 // value is deleted. SetMessages(Messages & buffer)278 common::Restorer<Messages *> SetMessages(Messages &buffer) { 279 return common::ScopedSet(messages_, &buffer); 280 } 281 // Discard future messages until the returned value is deleted. DiscardMessages()282 common::Restorer<Messages *> DiscardMessages() { 283 return common::ScopedSet(messages_, nullptr); 284 } 285 Say(CharBlock at,A &&...args)286 template <typename... A> Message *Say(CharBlock at, A &&...args) { 287 if (messages_ != nullptr) { 288 auto &msg{messages_->Say(at, std::forward<A>(args)...)}; 289 if (contextMessage_) { 290 msg.SetContext(contextMessage_.get()); 291 } 292 return &msg; 293 } else { 294 return nullptr; 295 } 296 } 297 Say(A &&...args)298 template <typename... A> Message *Say(A &&...args) { 299 return Say(at_, std::forward<A>(args)...); 300 } 301 302 private: 303 CharBlock at_; 304 Messages *messages_{nullptr}; 305 Message::Reference contextMessage_; 306 }; 307 } // namespace Fortran::parser 308 #endif // FORTRAN_PARSER_MESSAGE_H_ 309