• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- lib/Parser/message.cpp --------------------------------------------===//
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 #include "flang/Parser/message.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Parser/char-set.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include <algorithm>
14 #include <cstdarg>
15 #include <cstddef>
16 #include <cstdio>
17 #include <cstring>
18 #include <string>
19 #include <vector>
20 
21 namespace Fortran::parser {
22 
operator <<(llvm::raw_ostream & o,const MessageFixedText & t)23 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const MessageFixedText &t) {
24   std::size_t n{t.text().size()};
25   for (std::size_t j{0}; j < n; ++j) {
26     o << t.text()[j];
27   }
28   return o;
29 }
30 
Format(const MessageFixedText * text,...)31 void MessageFormattedText::Format(const MessageFixedText *text, ...) {
32   const char *p{text->text().begin()};
33   std::string asString;
34   if (*text->text().end() != '\0') {
35     // not NUL-terminated
36     asString = text->text().NULTerminatedToString();
37     p = asString.c_str();
38   }
39   va_list ap;
40   va_start(ap, text);
41   int need{vsnprintf(nullptr, 0, p, ap)};
42   CHECK(need >= 0);
43   char *buffer{
44       static_cast<char *>(std::malloc(static_cast<std::size_t>(need) + 1))};
45   CHECK(buffer);
46   va_end(ap);
47   va_start(ap, text);
48   int need2{vsnprintf(buffer, need + 1, p, ap)};
49   CHECK(need2 == need);
50   va_end(ap);
51   string_ = buffer;
52   std::free(buffer);
53   conversions_.clear();
54 }
55 
Convert(const std::string & s)56 const char *MessageFormattedText::Convert(const std::string &s) {
57   conversions_.emplace_front(s);
58   return conversions_.front().c_str();
59 }
60 
Convert(std::string & s)61 const char *MessageFormattedText::Convert(std::string &s) {
62   conversions_.emplace_front(s);
63   return conversions_.front().c_str();
64 }
65 
Convert(std::string && s)66 const char *MessageFormattedText::Convert(std::string &&s) {
67   conversions_.emplace_front(std::move(s));
68   return conversions_.front().c_str();
69 }
70 
Convert(CharBlock x)71 const char *MessageFormattedText::Convert(CharBlock x) {
72   return Convert(x.ToString());
73 }
74 
ToString() const75 std::string MessageExpectedText::ToString() const {
76   return std::visit(
77       common::visitors{
78           [](CharBlock cb) {
79             return MessageFormattedText("expected '%s'"_err_en_US, cb)
80                 .MoveString();
81           },
82           [](const SetOfChars &set) {
83             SetOfChars expect{set};
84             if (expect.Has('\n')) {
85               expect = expect.Difference('\n');
86               if (expect.empty()) {
87                 return "expected end of line"_err_en_US.text().ToString();
88               } else {
89                 std::string s{expect.ToString()};
90                 if (s.size() == 1) {
91                   return MessageFormattedText(
92                       "expected end of line or '%s'"_err_en_US, s)
93                       .MoveString();
94                 } else {
95                   return MessageFormattedText(
96                       "expected end of line or one of '%s'"_err_en_US, s)
97                       .MoveString();
98                 }
99               }
100             }
101             std::string s{expect.ToString()};
102             if (s.size() != 1) {
103               return MessageFormattedText("expected one of '%s'"_err_en_US, s)
104                   .MoveString();
105             } else {
106               return MessageFormattedText("expected '%s'"_err_en_US, s)
107                   .MoveString();
108             }
109           },
110       },
111       u_);
112 }
113 
Merge(const MessageExpectedText & that)114 bool MessageExpectedText::Merge(const MessageExpectedText &that) {
115   return std::visit(common::visitors{
116                         [](SetOfChars &s1, const SetOfChars &s2) {
117                           s1 = s1.Union(s2);
118                           return true;
119                         },
120                         [](const auto &, const auto &) { return false; },
121                     },
122       u_, that.u_);
123 }
124 
SortBefore(const Message & that) const125 bool Message::SortBefore(const Message &that) const {
126   // Messages from prescanning have ProvenanceRange values for their locations,
127   // while messages from later phases have CharBlock values, since the
128   // conversion of cooked source stream locations to provenances is not
129   // free and needs to be deferred, and many messages created during parsing
130   // are speculative.  Messages with ProvenanceRange locations are ordered
131   // before others for sorting.
132   return std::visit(
133       common::visitors{
134           [](CharBlock cb1, CharBlock cb2) {
135             return cb1.begin() < cb2.begin();
136           },
137           [](CharBlock, const ProvenanceRange &) { return false; },
138           [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
139             return pr1.start() < pr2.start();
140           },
141           [](const ProvenanceRange &, CharBlock) { return true; },
142       },
143       location_, that.location_);
144 }
145 
IsFatal() const146 bool Message::IsFatal() const {
147   return std::visit(
148       common::visitors{
149           [](const MessageExpectedText &) { return true; },
150           [](const MessageFixedText &x) { return x.isFatal(); },
151           [](const MessageFormattedText &x) { return x.isFatal(); },
152       },
153       text_);
154 }
155 
ToString() const156 std::string Message::ToString() const {
157   return std::visit(
158       common::visitors{
159           [](const MessageFixedText &t) {
160             return t.text().NULTerminatedToString();
161           },
162           [](const MessageFormattedText &t) { return t.string(); },
163           [](const MessageExpectedText &e) { return e.ToString(); },
164       },
165       text_);
166 }
167 
ResolveProvenances(const AllCookedSources & allCooked)168 void Message::ResolveProvenances(const AllCookedSources &allCooked) {
169   if (CharBlock * cb{std::get_if<CharBlock>(&location_)}) {
170     if (std::optional<ProvenanceRange> resolved{
171             allCooked.GetProvenanceRange(*cb)}) {
172       location_ = *resolved;
173     }
174   }
175   if (Message * attachment{attachment_.get()}) {
176     attachment->ResolveProvenances(allCooked);
177   }
178 }
179 
GetProvenanceRange(const AllCookedSources & allCooked) const180 std::optional<ProvenanceRange> Message::GetProvenanceRange(
181     const AllCookedSources &allCooked) const {
182   return std::visit(
183       common::visitors{
184           [&](CharBlock cb) { return allCooked.GetProvenanceRange(cb); },
185           [](const ProvenanceRange &pr) { return std::make_optional(pr); },
186       },
187       location_);
188 }
189 
Emit(llvm::raw_ostream & o,const AllCookedSources & allCooked,bool echoSourceLine) const190 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
191     bool echoSourceLine) const {
192   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
193   std::string text;
194   if (IsFatal()) {
195     text += "error: ";
196   }
197   text += ToString();
198   const AllSources &sources{allCooked.allSources()};
199   sources.EmitMessage(o, provenanceRange, text, echoSourceLine);
200   bool isContext{attachmentIsContext_};
201   for (const Message *attachment{attachment_.get()}; attachment;
202        attachment = attachment->attachment_.get()) {
203     text.clear();
204     if (isContext) {
205       text = "in the context: ";
206     }
207     text += attachment->ToString();
208     sources.EmitMessage(
209         o, attachment->GetProvenanceRange(allCooked), text, echoSourceLine);
210     isContext = attachment->attachmentIsContext_;
211   }
212 }
213 
Merge(const Message & that)214 bool Message::Merge(const Message &that) {
215   return AtSameLocation(that) &&
216       (!that.attachment_.get() ||
217           attachment_.get() == that.attachment_.get()) &&
218       std::visit(
219           common::visitors{
220               [](MessageExpectedText &e1, const MessageExpectedText &e2) {
221                 return e1.Merge(e2);
222               },
223               [](const auto &, const auto &) { return false; },
224           },
225           text_, that.text_);
226 }
227 
Attach(Message * m)228 Message &Message::Attach(Message *m) {
229   if (!attachment_) {
230     attachment_ = m;
231   } else {
232     if (attachment_->references() > 1) {
233       // Don't attach to a shared context attachment; copy it first.
234       attachment_ = new Message{*attachment_};
235     }
236     attachment_->Attach(m);
237   }
238   return *this;
239 }
240 
Attach(std::unique_ptr<Message> && m)241 Message &Message::Attach(std::unique_ptr<Message> &&m) {
242   return Attach(m.release());
243 }
244 
AtSameLocation(const Message & that) const245 bool Message::AtSameLocation(const Message &that) const {
246   return std::visit(
247       common::visitors{
248           [](CharBlock cb1, CharBlock cb2) {
249             return cb1.begin() == cb2.begin();
250           },
251           [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
252             return pr1.start() == pr2.start();
253           },
254           [](const auto &, const auto &) { return false; },
255       },
256       location_, that.location_);
257 }
258 
Merge(const Message & msg)259 bool Messages::Merge(const Message &msg) {
260   if (msg.IsMergeable()) {
261     for (auto &m : messages_) {
262       if (m.Merge(msg)) {
263         return true;
264       }
265     }
266   }
267   return false;
268 }
269 
Merge(Messages && that)270 void Messages::Merge(Messages &&that) {
271   if (messages_.empty()) {
272     *this = std::move(that);
273   } else {
274     while (!that.messages_.empty()) {
275       if (Merge(that.messages_.front())) {
276         that.messages_.pop_front();
277       } else {
278         auto next{that.messages_.begin()};
279         ++next;
280         messages_.splice(
281             messages_.end(), that.messages_, that.messages_.begin(), next);
282       }
283     }
284   }
285 }
286 
Copy(const Messages & that)287 void Messages::Copy(const Messages &that) {
288   for (const Message &m : that.messages_) {
289     Message copy{m};
290     Say(std::move(copy));
291   }
292 }
293 
ResolveProvenances(const AllCookedSources & allCooked)294 void Messages::ResolveProvenances(const AllCookedSources &allCooked) {
295   for (Message &m : messages_) {
296     m.ResolveProvenances(allCooked);
297   }
298 }
299 
Emit(llvm::raw_ostream & o,const AllCookedSources & allCooked,bool echoSourceLines) const300 void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
301     bool echoSourceLines) const {
302   std::vector<const Message *> sorted;
303   for (const auto &msg : messages_) {
304     sorted.push_back(&msg);
305   }
306   std::stable_sort(sorted.begin(), sorted.end(),
307       [](const Message *x, const Message *y) { return x->SortBefore(*y); });
308   for (const Message *msg : sorted) {
309     msg->Emit(o, allCooked, echoSourceLines);
310   }
311 }
312 
AttachTo(Message & msg)313 void Messages::AttachTo(Message &msg) {
314   for (Message &m : messages_) {
315     msg.Attach(std::move(m));
316   }
317   messages_.clear();
318 }
319 
AnyFatalError() const320 bool Messages::AnyFatalError() const {
321   for (const auto &msg : messages_) {
322     if (msg.IsFatal()) {
323       return true;
324     }
325   }
326   return false;
327 }
328 } // namespace Fortran::parser
329