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/Fortran-features.h" 19 #include "flang/Common/idioms.h" 20 #include "flang/Common/reference-counted.h" 21 #include "flang/Common/restorer.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 31 namespace 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. 36 enum 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 46 class MessageFixedText { 47 public: 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 68 private: 69 CharBlock text_; 70 Severity severity_{Severity::None}; 71 }; 72 73 inline namespace literals { 74 constexpr MessageFixedText operator""_err_en_US( 75 const char str[], std::size_t n) { 76 return MessageFixedText{str, n, Severity::Error}; 77 } 78 constexpr MessageFixedText operator""_warn_en_US( 79 const char str[], std::size_t n) { 80 return MessageFixedText{str, n, Severity::Warning}; 81 } 82 constexpr MessageFixedText operator""_port_en_US( 83 const char str[], std::size_t n) { 84 return MessageFixedText{str, n, Severity::Portability}; 85 } 86 constexpr MessageFixedText operator""_because_en_US( 87 const char str[], std::size_t n) { 88 return MessageFixedText{str, n, Severity::Because}; 89 } 90 constexpr MessageFixedText operator""_todo_en_US( 91 const char str[], std::size_t n) { 92 return MessageFixedText{str, n, Severity::Todo}; 93 } 94 constexpr 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. 104 class MessageFormattedText { 105 public: 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 132 private: 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. 160 class MessageExpectedText { 161 public: 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 184 private: 185 std::variant<CharBlock, SetOfChars> u_; 186 }; 187 188 class Message : public common::ReferenceCounted<Message> { 189 public: 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, 213 MessageFormattedText &&s) 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, 223 MessageFormattedText &&s) 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) const; 296 297 // If this Message or any of its attachments locates itself via a CharBlock, 298 // replace its location with the corresponding ProvenanceRange. 299 void ResolveProvenances(const AllCookedSources &); 300 301 bool IsMergeable() const { 302 return std::holds_alternative<MessageExpectedText>(text_); 303 } 304 bool Merge(const Message &); 305 bool operator==(const Message &that) const; 306 bool operator!=(const Message &that) const { return !(*this == that); } 307 308 private: 309 bool AtSameLocation(const Message &) const; 310 std::variant<ProvenanceRange, CharBlock> location_; 311 std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText> 312 text_; 313 bool attachmentIsContext_{false}; 314 Reference attachment_; 315 std::optional<common::LanguageFeature> languageFeature_; 316 std::optional<common::UsageWarning> usageWarning_; 317 }; 318 319 class Messages { 320 public: 321 Messages() {} 322 Messages(Messages &&that) : messages_{std::move(that.messages_)} {} 323 Messages &operator=(Messages &&that) { 324 messages_ = std::move(that.messages_); 325 return *this; 326 } 327 328 std::list<Message> &messages() { return messages_; } 329 bool empty() const { return messages_.empty(); } 330 void clear() { messages_.clear(); } 331 332 template <typename... A> Message &Say(A &&...args) { 333 return messages_.emplace_back(std::forward<A>(args)...); 334 } 335 336 template <typename... A> 337 Message &Say(common::LanguageFeature feature, A &&...args) { 338 return Say(std::forward<A>(args)...).set_languageFeature(feature); 339 } 340 341 template <typename... A> 342 Message &Say(common::UsageWarning warning, A &&...args) { 343 return Say(std::forward<A>(args)...).set_usageWarning(warning); 344 } 345 346 void Annex(Messages &&that) { 347 messages_.splice(messages_.end(), that.messages_); 348 } 349 350 bool Merge(const Message &); 351 void Merge(Messages &&); 352 void Copy(const Messages &); 353 void ResolveProvenances(const AllCookedSources &); 354 void Emit(llvm::raw_ostream &, const AllCookedSources &, 355 bool echoSourceLines = true) const; 356 void AttachTo(Message &, std::optional<Severity> = std::nullopt); 357 bool AnyFatalError() const; 358 359 private: 360 std::list<Message> messages_; 361 }; 362 363 class ContextualMessages { 364 public: 365 ContextualMessages() = default; 366 ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {} 367 explicit ContextualMessages(Messages *m) : messages_{m} {} 368 ContextualMessages(const ContextualMessages &that) 369 : at_{that.at_}, messages_{that.messages_} {} 370 371 CharBlock at() const { return at_; } 372 Messages *messages() const { return messages_; } 373 Message::Reference contextMessage() const { return contextMessage_; } 374 bool empty() const { return !messages_ || messages_->empty(); } 375 376 // Set CharBlock for messages; restore when the returned value is deleted 377 common::Restorer<CharBlock> SetLocation(CharBlock at) { 378 if (at.empty()) { 379 at = at_; 380 } 381 return common::ScopedSet(at_, std::move(at)); 382 } 383 384 common::Restorer<Message::Reference> SetContext(Message *m) { 385 if (!m) { 386 m = contextMessage_.get(); 387 } 388 return common::ScopedSet(contextMessage_, m); 389 } 390 391 // Diverts messages to another buffer; restored when the returned 392 // value is deleted. 393 common::Restorer<Messages *> SetMessages(Messages &buffer) { 394 return common::ScopedSet(messages_, &buffer); 395 } 396 // Discard future messages until the returned value is deleted. 397 common::Restorer<Messages *> DiscardMessages() { 398 return common::ScopedSet(messages_, nullptr); 399 } 400 401 template <typename... A> Message *Say(A &&...args) { 402 return Say(at_, std::forward<A>(args)...); 403 } 404 405 template <typename... A> Message *Say(CharBlock at, A &&...args) { 406 if (messages_ != nullptr) { 407 auto &msg{messages_->Say(at, std::forward<A>(args)...)}; 408 if (contextMessage_) { 409 msg.SetContext(contextMessage_.get()); 410 } 411 return &msg; 412 } else { 413 return nullptr; 414 } 415 } 416 417 template <typename... A> 418 Message *Say(std::optional<CharBlock> at, A &&...args) { 419 return Say(at.value_or(at_), std::forward<A>(args)...); 420 } 421 422 template <typename... A> 423 Message *Say(common::LanguageFeature feature, A &&...args) { 424 Message *msg{Say(std::forward<A>(args)...)}; 425 if (msg) { 426 msg->set_languageFeature(feature); 427 } 428 return msg; 429 } 430 431 template <typename... A> 432 Message *Say(common::UsageWarning warning, A &&...args) { 433 Message *msg{Say(std::forward<A>(args)...)}; 434 if (msg) { 435 msg->set_usageWarning(warning); 436 } 437 return msg; 438 } 439 440 Message *Say(Message &&msg) { 441 if (messages_ != nullptr) { 442 if (contextMessage_) { 443 msg.SetContext(contextMessage_.get()); 444 } 445 return &messages_->Say(std::move(msg)); 446 } else { 447 return nullptr; 448 } 449 } 450 451 private: 452 CharBlock at_; 453 Messages *messages_{nullptr}; 454 Message::Reference contextMessage_; 455 }; 456 } // namespace Fortran::parser 457 #endif // FORTRAN_PARSER_MESSAGE_H_ 458