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 <tuple>
20#include <vector>
21
22namespace Fortran::parser {
23
24llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const MessageFixedText &t) {
25 std::size_t n{t.text().size()};
26 for (std::size_t j{0}; j < n; ++j) {
27 o << t.text()[j];
28 }
29 return o;
30}
31
32void MessageFormattedText::Format(const MessageFixedText *text, ...) {
33 const char *p{text->text().begin()};
34 std::string asString;
35 if (*text->text().end() != '\0') {
36 // not NUL-terminated
37 asString = text->text().NULTerminatedToString();
38 p = asString.c_str();
39 }
40 va_list ap;
41 va_start(ap, text);
42#ifdef _MSC_VER
43 // Microsoft has a separate function for "positional arguments", which is
44 // used in some messages.
45 int need{_vsprintf_p(nullptr, 0, p, ap)};
46#else
47 int need{vsnprintf(nullptr, 0, p, ap)};
48#endif
49
50 CHECK(need >= 0);
51 char *buffer{
52 static_cast<char *>(std::malloc(static_cast<std::size_t>(need) + 1))};
53 CHECK(buffer);
54 va_end(ap);
55 va_start(ap, text);
56#ifdef _MSC_VER
57 // Use positional argument variant of printf.
58 int need2{_vsprintf_p(buffer, need + 1, p, ap)};
59#else
60 int need2{vsnprintf(buffer, need + 1, p, ap)};
61#endif
62 CHECK(need2 == need);
63 va_end(ap);
64 string_ = buffer;
65 std::free(buffer);
66 conversions_.clear();
67}
68
69const char *MessageFormattedText::Convert(const std::string &s) {
70 conversions_.emplace_front(s);
71 return conversions_.front().c_str();
72}
73
74const char *MessageFormattedText::Convert(std::string &&s) {
75 conversions_.emplace_front(std::move(s));
76 return conversions_.front().c_str();
77}
78
79const char *MessageFormattedText::Convert(const std::string_view &s) {
80 conversions_.emplace_front(s);
81 return conversions_.front().c_str();
82}
83
84const char *MessageFormattedText::Convert(std::string_view &&s) {
85 conversions_.emplace_front(s);
86 return conversions_.front().c_str();
87}
88
89const char *MessageFormattedText::Convert(CharBlock x) {
90 return Convert(x.ToString());
91}
92
93std::string MessageExpectedText::ToString() const {
94 return common::visit(
95 common::visitors{
96 [](CharBlock cb) {
97 return MessageFormattedText("expected '%s'"_err_en_US, cb)
98 .MoveString();
99 },
100 [](const SetOfChars &set) {
101 SetOfChars expect{set};
102 if (expect.Has('\n')) {
103 expect = expect.Difference('\n');
104 if (expect.empty()) {
105 return "expected end of line"_err_en_US.text().ToString();
106 } else {
107 std::string s{expect.ToString()};
108 if (s.size() == 1) {
109 return MessageFormattedText(
110 "expected end of line or '%s'"_err_en_US, s)
111 .MoveString();
112 } else {
113 return MessageFormattedText(
114 "expected end of line or one of '%s'"_err_en_US, s)
115 .MoveString();
116 }
117 }
118 }
119 std::string s{expect.ToString()};
120 if (s.size() != 1) {
121 return MessageFormattedText("expected one of '%s'"_err_en_US, s)
122 .MoveString();
123 } else {
124 return MessageFormattedText("expected '%s'"_err_en_US, s)
125 .MoveString();
126 }
127 },
128 },
129 u_);
130}
131
132bool MessageExpectedText::Merge(const MessageExpectedText &that) {
133 return common::visit(common::visitors{
134 [](SetOfChars &s1, const SetOfChars &s2) {
135 s1 = s1.Union(s2);
136 return true;
137 },
138 [](const auto &, const auto &) { return false; },
139 },
140 u_, that.u_);
141}
142
143bool Message::SortBefore(const Message &that) const {
144 // Messages from prescanning have ProvenanceRange values for their locations,
145 // while messages from later phases have CharBlock values, since the
146 // conversion of cooked source stream locations to provenances is not
147 // free and needs to be deferred, and many messages created during parsing
148 // are speculative. Messages with ProvenanceRange locations are ordered
149 // before others for sorting.
150 return common::visit(
151 common::visitors{
152 [](CharBlock cb1, CharBlock cb2) {
153 return cb1.begin() < cb2.begin();
154 },
155 [](CharBlock, const ProvenanceRange &) { return false; },
156 [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
157 return pr1.start() < pr2.start();
158 },
159 [](const ProvenanceRange &, CharBlock) { return true; },
160 },
161 location_, that.location_);
162}
163
164bool Message::IsFatal() const {
165 return severity() == Severity::Error || severity() == Severity::Todo;
166}
167
168Severity Message::severity() const {
169 return common::visit(
170 common::visitors{
171 [](const MessageExpectedText &) { return Severity::Error; },
172 [](const MessageFixedText &x) { return x.severity(); },
173 [](const MessageFormattedText &x) { return x.severity(); },
174 },
175 text_);
176}
177
178Message &Message::set_severity(Severity severity) {
179 common::visit(
180 common::visitors{
181 [](const MessageExpectedText &) {},
182 [severity](MessageFixedText &x) { x.set_severity(severity); },
183 [severity](MessageFormattedText &x) { x.set_severity(severity); },
184 },
185 text_);
186 return *this;
187}
188
189std::optional<common::LanguageFeature> Message::languageFeature() const {
190 return languageFeature_;
191}
192
193Message &Message::set_languageFeature(common::LanguageFeature feature) {
194 languageFeature_ = feature;
195 return *this;
196}
197
198std::optional<common::UsageWarning> Message::usageWarning() const {
199 return usageWarning_;
200}
201
202Message &Message::set_usageWarning(common::UsageWarning warning) {
203 usageWarning_ = warning;
204 return *this;
205}
206
207std::string Message::ToString() const {
208 return common::visit(
209 common::visitors{
210 [](const MessageFixedText &t) {
211 return t.text().NULTerminatedToString();
212 },
213 [](const MessageFormattedText &t) { return t.string(); },
214 [](const MessageExpectedText &e) { return e.ToString(); },
215 },
216 text_);
217}
218
219void Message::ResolveProvenances(const AllCookedSources &allCooked) {
220 if (CharBlock * cb{std::get_if<CharBlock>(&location_)}) {
221 if (std::optional<ProvenanceRange> resolved{
222 allCooked.GetProvenanceRange(*cb)}) {
223 location_ = *resolved;
224 }
225 }
226 if (Message * attachment{attachment_.get()}) {
227 attachment->ResolveProvenances(allCooked);
228 }
229}
230
231std::optional<ProvenanceRange> Message::GetProvenanceRange(
232 const AllCookedSources &allCooked) const {
233 return common::visit(
234 common::visitors{
235 [&](CharBlock cb) { return allCooked.GetProvenanceRange(cb); },
236 [](const ProvenanceRange &pr) { return std::make_optional(pr); },
237 },
238 location_);
239}
240
241static std::string Prefix(Severity severity) {
242 switch (severity) {
243 case Severity::Error:
244 return "error: ";
245 case Severity::Warning:
246 return "warning: ";
247 case Severity::Portability:
248 return "portability: ";
249 case Severity::Because:
250 return "because: ";
251 case Severity::Context:
252 return "in the context: ";
253 case Severity::Todo:
254 return "error: not yet implemented: ";
255 case Severity::None:
256 break;
257 }
258 return "";
259}
260
261static llvm::raw_ostream::Colors PrefixColor(Severity severity) {
262 switch (severity) {
263 case Severity::Error:
264 case Severity::Todo:
265 return llvm::raw_ostream::RED;
266 case Severity::Warning:
267 case Severity::Portability:
268 return llvm::raw_ostream::MAGENTA;
269 default:
270 // TODO: Set the color.
271 break;
272 }
273 return llvm::raw_ostream::SAVEDCOLOR;
274}
275
276static constexpr int MAX_CONTEXTS_EMITTED{2};
277static constexpr bool OMIT_SHARED_CONTEXTS{true};
278
279void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
280 bool echoSourceLine) const {
281 std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
282 const AllSources &sources{allCooked.allSources()};
283 sources.EmitMessage(o, provenanceRange, ToString(), Prefix(severity()),
284 PrefixColor(severity()), echoSourceLine);
285 // Refers to whether the attachment in the loop below is a context, but can't
286 // be declared inside the loop because the previous iteration's
287 // attachment->attachmentIsContext_ indicates this.
288 bool isContext{attachmentIsContext_};
289 int contextsEmitted{0};
290 // Emit attachments.
291 for (const Message *attachment{attachment_.get()}; attachment;
292 isContext = attachment->attachmentIsContext_,
293 attachment = attachment->attachment_.get()) {
294 Severity severity = isContext ? Severity::Context : attachment->severity();
295 auto emitAttachment = [&]() {
296 sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),
297 attachment->ToString(), Prefix(severity), PrefixColor(severity),
298 echoSourceLine);
299 };
300
301 if (isContext) {
302 // Truncate the number of contexts emitted.
303 if (contextsEmitted < MAX_CONTEXTS_EMITTED) {
304 emitAttachment();
305 ++contextsEmitted;
306 }
307 if constexpr (OMIT_SHARED_CONTEXTS) {
308 // Skip less specific contexts at the same location.
309 for (const Message *next_attachment{attachment->attachment_.get()};
310 next_attachment && next_attachment->attachmentIsContext_ &&
311 next_attachment->AtSameLocation(*attachment);
312 next_attachment = next_attachment->attachment_.get()) {
313 attachment = next_attachment;
314 }
315 // NB, this loop increments `attachment` one more time after the
316 // previous loop is done advancing it to the last context at the same
317 // location.
318 }
319 } else {
320 emitAttachment();
321 }
322 }
323}
324
325// Messages are equal if they're for the same location and text, and the user
326// visible aspects of their attachments are the same
327bool Message::operator==(const Message &that) const {
328 if (!AtSameLocation(that) || ToString() != that.ToString() ||
329 severity() != that.severity() ||
330 attachmentIsContext_ != that.attachmentIsContext_) {
331 return false;
332 }
333 const Message *thatAttachment{that.attachment_.get()};
334 for (const Message *attachment{attachment_.get()}; attachment;
335 attachment = attachment->attachment_.get()) {
336 if (!thatAttachment || !attachment->AtSameLocation(*thatAttachment) ||
337 attachment->ToString() != thatAttachment->ToString() ||
338 attachment->severity() != thatAttachment->severity()) {
339 return false;
340 }
341 thatAttachment = thatAttachment->attachment_.get();
342 }
343 return !thatAttachment;
344}
345
346bool Message::Merge(const Message &that) {
347 return AtSameLocation(that) &&
348 (!that.attachment_.get() ||
349 attachment_.get() == that.attachment_.get()) &&
350 common::visit(
351 common::visitors{
352 [](MessageExpectedText &e1, const MessageExpectedText &e2) {
353 return e1.Merge(e2);
354 },
355 [](const auto &, const auto &) { return false; },
356 },
357 text_, that.text_);
358}
359
360Message &Message::Attach(Message *m) {
361 if (!attachment_) {
362 attachment_ = m;
363 } else {
364 if (attachment_->references() > 1) {
365 // Don't attach to a shared context attachment; copy it first.
366 attachment_ = new Message{*attachment_};
367 }
368 attachment_->Attach(m);
369 }
370 return *this;
371}
372
373Message &Message::Attach(std::unique_ptr<Message> &&m) {
374 return Attach(m.release());
375}
376
377bool Message::AtSameLocation(const Message &that) const {
378 return common::visit(
379 common::visitors{
380 [](CharBlock cb1, CharBlock cb2) {
381 return cb1.begin() == cb2.begin();
382 },
383 [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
384 return pr1.start() == pr2.start();
385 },
386 [](const auto &, const auto &) { return false; },
387 },
388 location_, that.location_);
389}
390
391bool Messages::Merge(const Message &msg) {
392 if (msg.IsMergeable()) {
393 for (auto &m : messages_) {
394 if (m.Merge(msg)) {
395 return true;
396 }
397 }
398 }
399 return false;
400}
401
402void Messages::Merge(Messages &&that) {
403 if (messages_.empty()) {
404 *this = std::move(that);
405 } else {
406 while (!that.messages_.empty()) {
407 if (Merge(that.messages_.front())) {
408 that.messages_.pop_front();
409 } else {
410 auto next{that.messages_.begin()};
411 ++next;
412 messages_.splice(
413 messages_.end(), that.messages_, that.messages_.begin(), next);
414 }
415 }
416 }
417}
418
419void Messages::Copy(const Messages &that) {
420 for (const Message &m : that.messages_) {
421 Message copy{m};
422 Say(std::move(copy));
423 }
424}
425
426void Messages::ResolveProvenances(const AllCookedSources &allCooked) {
427 for (Message &m : messages_) {
428 m.ResolveProvenances(allCooked);
429 }
430}
431
432void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
433 bool echoSourceLines) const {
434 std::vector<const Message *> sorted;
435 for (const auto &msg : messages_) {
436 sorted.push_back(&msg);
437 }
438 std::stable_sort(sorted.begin(), sorted.end(),
439 [](const Message *x, const Message *y) { return x->SortBefore(*y); });
440 const Message *lastMsg{nullptr};
441 for (const Message *msg : sorted) {
442 if (lastMsg && *msg == *lastMsg) {
443 // Don't emit two identical messages for the same location
444 continue;
445 }
446 msg->Emit(o, allCooked, echoSourceLines);
447 lastMsg = msg;
448 }
449}
450
451void Messages::AttachTo(Message &msg, std::optional<Severity> severity) {
452 for (Message &m : messages_) {
453 Message m2{std::move(m)};
454 if (severity) {
455 m2.set_severity(*severity);
456 }
457 msg.Attach(std::move(m2));
458 }
459 messages_.clear();
460}
461
462bool Messages::AnyFatalError() const {
463 for (const auto &msg : messages_) {
464 if (msg.IsFatal()) {
465 return true;
466 }
467 }
468 return false;
469}
470} // namespace Fortran::parser
471

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of flang/lib/Parser/message.cpp