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