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