xref: /llvm-project/flang/lib/Parser/message.cpp (revision 8670e49901d116b2094aeb0a5117edef9e69cdcf)
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 
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 
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 
56 const char *MessageFormattedText::Convert(const std::string &s) {
57   conversions_.emplace_front(s);
58   return conversions_.front().c_str();
59 }
60 
61 const char *MessageFormattedText::Convert(std::string &s) {
62   conversions_.emplace_front(s);
63   return conversions_.front().c_str();
64 }
65 
66 const char *MessageFormattedText::Convert(std::string &&s) {
67   conversions_.emplace_front(std::move(s));
68   return conversions_.front().c_str();
69 }
70 
71 const char *MessageFormattedText::Convert(CharBlock x) {
72   return Convert(x.ToString());
73 }
74 
75 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 
114 bool MessageExpectedText::Merge(const MessageExpectedText &that) {
115   return std::visit(
116       common::visitors{
117           [](SetOfChars &s1, const SetOfChars &s2) {
118             s1 = s1.Union(s2);
119             return true;
120           },
121           [](const auto &, const auto &) { return false; },
122       },
123       u_, that.u_);
124 }
125 
126 bool Message::SortBefore(const Message &that) const {
127   // Messages from prescanning have ProvenanceRange values for their locations,
128   // while messages from later phases have CharBlock values, since the
129   // conversion of cooked source stream locations to provenances is not
130   // free and needs to be deferred, and many messages created during parsing
131   // are speculative.  Messages with ProvenanceRange locations are ordered
132   // before others for sorting.
133   return std::visit(
134       common::visitors{
135           [](CharBlock cb1, CharBlock cb2) {
136             return cb1.begin() < cb2.begin();
137           },
138           [](CharBlock, const ProvenanceRange &) { return false; },
139           [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
140             return pr1.start() < pr2.start();
141           },
142           [](const ProvenanceRange &, CharBlock) { return true; },
143       },
144       location_, that.location_);
145 }
146 
147 bool Message::IsFatal() const {
148   return std::visit(
149       common::visitors{
150           [](const MessageExpectedText &) { return true; },
151           [](const MessageFixedText &x) { return x.isFatal(); },
152           [](const MessageFormattedText &x) { return x.isFatal(); },
153       },
154       text_);
155 }
156 
157 std::string Message::ToString() const {
158   return std::visit(
159       common::visitors{
160           [](const MessageFixedText &t) {
161             return t.text().NULTerminatedToString();
162           },
163           [](const MessageFormattedText &t) { return t.string(); },
164           [](const MessageExpectedText &e) { return e.ToString(); },
165       },
166       text_);
167 }
168 
169 void Message::ResolveProvenances(const CookedSource &cooked) {
170   if (CharBlock * cb{std::get_if<CharBlock>(&location_)}) {
171     if (std::optional<ProvenanceRange> resolved{
172             cooked.GetProvenanceRange(*cb)}) {
173       location_ = *resolved;
174     }
175   }
176   if (Message * attachment{attachment_.get()}) {
177     attachment->ResolveProvenances(cooked);
178   }
179 }
180 
181 std::optional<ProvenanceRange> Message::GetProvenanceRange(
182     const CookedSource &cooked) const {
183   return std::visit(
184       common::visitors{
185           [&](CharBlock cb) { return cooked.GetProvenanceRange(cb); },
186           [](const ProvenanceRange &pr) { return std::make_optional(pr); },
187       },
188       location_);
189 }
190 
191 void Message::Emit(llvm::raw_ostream &o, const CookedSource &cooked,
192     bool echoSourceLine) const {
193   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(cooked)};
194   std::string text;
195   if (IsFatal()) {
196     text += "error: ";
197   }
198   text += ToString();
199   const AllSources &sources{cooked.allSources()};
200   sources.EmitMessage(o, provenanceRange, text, echoSourceLine);
201   if (attachmentIsContext_) {
202     for (const Message *context{attachment_.get()}; context;
203          context = context->attachment_.get()) {
204       std::optional<ProvenanceRange> contextProvenance{
205           context->GetProvenanceRange(cooked)};
206       text = "in the context: ";
207       text += context->ToString();
208       // TODO: don't echo the source lines of a context when it's the
209       // same line (or maybe just never echo source for context)
210       sources.EmitMessage(o, contextProvenance, text,
211           echoSourceLine && contextProvenance != provenanceRange);
212       provenanceRange = contextProvenance;
213     }
214   } else {
215     for (const Message *attachment{attachment_.get()}; attachment;
216          attachment = attachment->attachment_.get()) {
217       sources.EmitMessage(o, attachment->GetProvenanceRange(cooked),
218           attachment->ToString(), echoSourceLine);
219     }
220   }
221 }
222 
223 bool Message::Merge(const Message &that) {
224   return AtSameLocation(that) &&
225       (!that.attachment_.get() ||
226           attachment_.get() == that.attachment_.get()) &&
227       std::visit(
228           common::visitors{
229               [](MessageExpectedText &e1, const MessageExpectedText &e2) {
230                 return e1.Merge(e2);
231               },
232               [](const auto &, const auto &) { return false; },
233           },
234           text_, that.text_);
235 }
236 
237 Message &Message::Attach(Message *m) {
238   if (!attachment_) {
239     attachment_ = m;
240   } else {
241     attachment_->Attach(m);
242   }
243   return *this;
244 }
245 
246 Message &Message::Attach(std::unique_ptr<Message> &&m) {
247   return Attach(m.release());
248 }
249 
250 bool Message::AtSameLocation(const Message &that) const {
251   return std::visit(
252       common::visitors{
253           [](CharBlock cb1, CharBlock cb2) {
254             return cb1.begin() == cb2.begin();
255           },
256           [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
257             return pr1.start() == pr2.start();
258           },
259           [](const auto &, const auto &) { return false; },
260       },
261       location_, that.location_);
262 }
263 
264 void Messages::clear() {
265   messages_.clear();
266   ResetLastPointer();
267 }
268 
269 bool Messages::Merge(const Message &msg) {
270   if (msg.IsMergeable()) {
271     for (auto &m : messages_) {
272       if (m.Merge(msg)) {
273         return true;
274       }
275     }
276   }
277   return false;
278 }
279 
280 void Messages::Merge(Messages &&that) {
281   if (messages_.empty()) {
282     *this = std::move(that);
283   } else {
284     while (!that.messages_.empty()) {
285       if (Merge(that.messages_.front())) {
286         that.messages_.pop_front();
287       } else {
288         messages_.splice_after(
289             last_, that.messages_, that.messages_.before_begin());
290         ++last_;
291       }
292     }
293     that.ResetLastPointer();
294   }
295 }
296 
297 void Messages::Copy(const Messages &that) {
298   for (const Message &m : that.messages_) {
299     Message copy{m};
300     Say(std::move(copy));
301   }
302 }
303 
304 void Messages::ResolveProvenances(const CookedSource &cooked) {
305   for (Message &m : messages_) {
306     m.ResolveProvenances(cooked);
307   }
308 }
309 
310 void Messages::Emit(llvm::raw_ostream &o, const CookedSource &cooked,
311     bool echoSourceLines) const {
312   std::vector<const Message *> sorted;
313   for (const auto &msg : messages_) {
314     sorted.push_back(&msg);
315   }
316   std::stable_sort(sorted.begin(), sorted.end(),
317       [](const Message *x, const Message *y) { return x->SortBefore(*y); });
318   for (const Message *msg : sorted) {
319     msg->Emit(o, cooked, echoSourceLines);
320   }
321 }
322 
323 void Messages::AttachTo(Message &msg) {
324   for (Message &m : messages_) {
325     msg.Attach(std::move(m));
326   }
327   messages_.clear();
328 }
329 
330 bool Messages::AnyFatalError() const {
331   for (const auto &msg : messages_) {
332     if (msg.IsFatal()) {
333       return true;
334     }
335   }
336   return false;
337 }
338 }
339