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