1 // Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef FORTRAN_PARSER_MESSAGE_H_ 16 #define FORTRAN_PARSER_MESSAGE_H_ 17 18 // Defines a representation for sequences of compiler messages. 19 // Supports nested contextualization. 20 21 #include "char-block.h" 22 #include "char-set.h" 23 #include "provenance.h" 24 #include "../common/idioms.h" 25 #include "../common/reference-counted.h" 26 #include "../common/restorer.h" 27 #include <cstddef> 28 #include <cstring> 29 #include <forward_list> 30 #include <optional> 31 #include <ostream> 32 #include <string> 33 #include <utility> 34 #include <variant> 35 36 namespace Fortran::parser { 37 38 // Use "..."_err_en_US and "..."_en_US literals to define the static 39 // text and fatality of a message. 40 class MessageFixedText { 41 public: 42 constexpr MessageFixedText( 43 const char str[], std::size_t n, bool isFatal = false) 44 : text_{str, n}, isFatal_{isFatal} {} 45 constexpr MessageFixedText(const MessageFixedText &) = default; 46 constexpr MessageFixedText(MessageFixedText &&) = default; 47 constexpr MessageFixedText &operator=(const MessageFixedText &) = default; 48 constexpr MessageFixedText &operator=(MessageFixedText &&) = default; 49 text()50 const CharBlock &text() const { return text_; } isFatal()51 bool isFatal() const { return isFatal_; } 52 53 private: 54 CharBlock text_; 55 bool isFatal_{false}; 56 }; 57 58 inline namespace literals { 59 constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) { 60 return MessageFixedText{str, n, false /* not fatal */}; 61 } 62 63 constexpr MessageFixedText operator""_err_en_US( 64 const char str[], std::size_t n) { 65 return MessageFixedText{str, n, true /* fatal */}; 66 } 67 } 68 69 // The construction of a MessageFormattedText uses a MessageFixedText 70 // as a vsnprintf() formatting string that is applied to the 71 // following arguments. CharBlock and std::string argument 72 // values are also supported; they are automatically converted into 73 // char pointers that are suitable for '%s' formatting. 74 class MessageFormattedText { 75 public: 76 template<typename... A> MessageFormattedText(const MessageFixedText & text,A &&...x)77 MessageFormattedText(const MessageFixedText &text, A &&... x) 78 : isFatal_{text.isFatal()} { 79 Format(&text, Convert(std::forward<A>(x))...); 80 } 81 MessageFormattedText(const MessageFormattedText &) = default; 82 MessageFormattedText(MessageFormattedText &&) = default; 83 MessageFormattedText &operator=(const MessageFormattedText &) = default; 84 MessageFormattedText &operator=(MessageFormattedText &&) = default; string()85 const std::string &string() const { return string_; } isFatal()86 bool isFatal() const { return isFatal_; } MoveString()87 std::string MoveString() { return std::move(string_); } 88 89 private: 90 void Format(const MessageFixedText *text, ...); 91 Convert(const A & x)92 template<typename A> A Convert(const A &x) { 93 static_assert(!std::is_class_v<std::decay_t<A>>); 94 return x; 95 } Convert(A & x)96 template<typename A> A Convert(A &x) { 97 static_assert(!std::is_class_v<std::decay_t<A>>); 98 return x; 99 } Convert(A && x)100 template<typename A> common::IfNoLvalue<A, A> Convert(A &&x) { 101 static_assert(!std::is_class_v<std::decay_t<A>>); 102 return std::move(x); 103 } Convert(const char * s)104 const char *Convert(const char *s) { return s; } Convert(char * s)105 const char *Convert(char *s) { return s; } 106 const char *Convert(const std::string &); 107 const char *Convert(std::string &); 108 const char *Convert(std::string &&); 109 const char *Convert(const CharBlock &); 110 const char *Convert(CharBlock &); 111 const char *Convert(CharBlock &&); 112 113 bool isFatal_{false}; 114 std::string string_; 115 std::forward_list<std::string> conversions_; // preserves created strings 116 }; 117 118 // Represents a formatted rendition of "expected '%s'"_err_en_US 119 // on a constant text or a set of characters. 120 class MessageExpectedText { 121 public: MessageExpectedText(const char * s,std::size_t n)122 MessageExpectedText(const char *s, std::size_t n) { 123 if (n == std::string::npos) { 124 n = std::strlen(s); 125 } 126 if (n == 1) { 127 // Treat a one-character string as a singleton set for better merging. 128 u_ = SetOfChars{*s}; 129 } else { 130 u_ = CharBlock{s, n}; 131 } 132 } MessageExpectedText(CharBlock cb)133 constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {} MessageExpectedText(char ch)134 constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {} MessageExpectedText(SetOfChars set)135 constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {} 136 MessageExpectedText(const MessageExpectedText &) = default; 137 MessageExpectedText(MessageExpectedText &&) = default; 138 MessageExpectedText &operator=(const MessageExpectedText &) = default; 139 MessageExpectedText &operator=(MessageExpectedText &&) = default; 140 141 std::string ToString() const; 142 bool Merge(const MessageExpectedText &); 143 144 private: 145 std::variant<CharBlock, SetOfChars> u_; 146 }; 147 148 class Message : public common::ReferenceCounted<Message> { 149 public: 150 using Reference = common::CountedReference<Message>; 151 152 Message(const Message &) = default; 153 Message(Message &&) = default; 154 Message &operator=(const Message &) = default; 155 Message &operator=(Message &&) = default; 156 Message(ProvenanceRange pr,const MessageFixedText & t)157 Message(ProvenanceRange pr, const MessageFixedText &t) 158 : location_{pr}, text_{t} {} Message(ProvenanceRange pr,const MessageFormattedText & s)159 Message(ProvenanceRange pr, const MessageFormattedText &s) 160 : location_{pr}, text_{s} {} Message(ProvenanceRange pr,MessageFormattedText && s)161 Message(ProvenanceRange pr, MessageFormattedText &&s) 162 : location_{pr}, text_{std::move(s)} {} Message(ProvenanceRange pr,const MessageExpectedText & t)163 Message(ProvenanceRange pr, const MessageExpectedText &t) 164 : location_{pr}, text_{t} {} 165 Message(CharBlock csr,const MessageFixedText & t)166 Message(CharBlock csr, const MessageFixedText &t) 167 : location_{csr}, text_{t} {} Message(CharBlock csr,const MessageFormattedText & s)168 Message(CharBlock csr, const MessageFormattedText &s) 169 : location_{csr}, text_{s} {} Message(CharBlock csr,MessageFormattedText && s)170 Message(CharBlock csr, MessageFormattedText &&s) 171 : location_{csr}, text_{std::move(s)} {} Message(CharBlock csr,const MessageExpectedText & t)172 Message(CharBlock csr, const MessageExpectedText &t) 173 : location_{csr}, text_{t} {} 174 175 template<typename RANGE, typename A, typename... As> Message(RANGE r,const MessageFixedText & t,A && x,As &&...xs)176 Message(RANGE r, const MessageFixedText &t, A &&x, As &&... xs) 177 : location_{r}, text_{MessageFormattedText{ 178 t, std::forward<A>(x), std::forward<As>(xs)...}} {} 179 attachmentIsContext()180 bool attachmentIsContext() const { return attachmentIsContext_; } attachment()181 Reference attachment() const { return attachment_; } 182 SetContext(Message * c)183 void SetContext(Message *c) { 184 attachment_ = c; 185 attachmentIsContext_ = true; 186 } 187 Message &Attach(Message *); Attach(A &&...args)188 template<typename... A> Message &Attach(A &&... args) { 189 return Attach(new Message{std::forward<A>(args)...}); // reference-counted 190 } 191 192 bool SortBefore(const Message &that) const; 193 bool IsFatal() const; 194 std::string ToString() const; 195 std::optional<ProvenanceRange> GetProvenanceRange(const CookedSource &) const; 196 void Emit( 197 std::ostream &, const CookedSource &, bool echoSourceLine = true) const; 198 199 // If this Message or any of its attachments locates itself via a CharBlock 200 // within a particular CookedSource, replace its location with the 201 // corresponding ProvenanceRange. 202 void ResolveProvenances(const CookedSource &); 203 IsMergeable()204 bool IsMergeable() const { 205 return std::holds_alternative<MessageExpectedText>(text_); 206 } 207 bool Merge(const Message &); 208 209 private: 210 bool AtSameLocation(const Message &) const; 211 212 std::variant<ProvenanceRange, CharBlock> location_; 213 std::variant<MessageFixedText, MessageFormattedText, MessageExpectedText> 214 text_; 215 bool attachmentIsContext_{false}; 216 Reference attachment_; 217 }; 218 219 class Messages { 220 public: Messages()221 Messages() {} Messages(Messages && that)222 Messages(Messages &&that) : messages_{std::move(that.messages_)} { 223 if (!messages_.empty()) { 224 last_ = that.last_; 225 that.ResetLastPointer(); 226 } 227 } 228 Messages &operator=(Messages &&that) { 229 messages_ = std::move(that.messages_); 230 if (messages_.empty()) { 231 ResetLastPointer(); 232 } else { 233 last_ = that.last_; 234 that.ResetLastPointer(); 235 } 236 return *this; 237 } 238 messages()239 std::forward_list<Message> &messages() { return messages_; } empty()240 bool empty() const { return messages_.empty(); } 241 void clear(); 242 Say(A &&...args)243 template<typename... A> Message &Say(A &&... args) { 244 last_ = messages_.emplace_after(last_, std::forward<A>(args)...); 245 return *last_; 246 } 247 Annex(Messages && that)248 void Annex(Messages &&that) { 249 if (!that.messages_.empty()) { 250 messages_.splice_after(last_, that.messages_); 251 last_ = that.last_; 252 that.ResetLastPointer(); 253 } 254 } 255 Restore(Messages && that)256 void Restore(Messages &&that) { 257 that.Annex(std::move(*this)); 258 *this = std::move(that); 259 } 260 261 bool Merge(const Message &); 262 void Merge(Messages &&); 263 void Copy(const Messages &); 264 void ResolveProvenances(const CookedSource &); 265 void Emit(std::ostream &, const CookedSource &cooked, 266 bool echoSourceLines = true) const; 267 void AttachTo(Message &); 268 bool AnyFatalError() const; 269 270 private: ResetLastPointer()271 void ResetLastPointer() { last_ = messages_.before_begin(); } 272 273 std::forward_list<Message> messages_; 274 std::forward_list<Message>::iterator last_{messages_.before_begin()}; 275 }; 276 277 class ContextualMessages { 278 public: 279 ContextualMessages() = default; ContextualMessages(CharBlock at,Messages * m)280 ContextualMessages(CharBlock at, Messages *m) : at_{at}, messages_{m} {} ContextualMessages(Messages * m)281 explicit ContextualMessages(Messages *m) : messages_{m} {} ContextualMessages(const ContextualMessages & that)282 ContextualMessages(const ContextualMessages &that) 283 : at_{that.at_}, messages_{that.messages_} {} 284 at()285 CharBlock at() const { return at_; } messages()286 Messages *messages() const { return messages_; } 287 288 // Set CharBlock for messages; restore when the returned value is deleted SetLocation(CharBlock at)289 common::Restorer<CharBlock> SetLocation(CharBlock at) { 290 if (at.empty()) { 291 at = at_; 292 } 293 return common::ScopedSet(at_, std::move(at)); 294 } 295 Say(CharBlock at,A &&...args)296 template<typename... A> Message *Say(CharBlock at, A &&... args) { 297 if (messages_ != nullptr) { 298 return &messages_->Say(at, std::forward<A>(args)...); 299 } else { 300 return nullptr; 301 } 302 } 303 Say(A &&...args)304 template<typename... A> Message *Say(A &&... args) { 305 return Say(at_, std::forward<A>(args)...); 306 } 307 308 private: 309 CharBlock at_; 310 Messages *messages_{nullptr}; 311 }; 312 } 313 #endif // FORTRAN_PARSER_MESSAGE_H_ 314