1 //===-- lib/Parser/message.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 namespace Fortran::parser
{
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
) {
31 void MessageFormattedText::Format(const MessageFixedText
*text
, ...) {
32 const char *p
{text
->text().begin()};
34 if (*text
->text().end() != '\0') {
36 asString
= text
->text().NULTerminatedToString();
42 // Microsoft has a separate function for "positional arguments", which is
43 // used in some messages.
44 int need
{_vsprintf_p(nullptr, 0, p
, ap
)};
46 int need
{vsnprintf(nullptr, 0, p
, ap
)};
51 static_cast<char *>(std::malloc(static_cast<std::size_t>(need
) + 1))};
56 // Use positional argument variant of printf.
57 int need2
{_vsprintf_p(buffer
, need
+ 1, p
, ap
)};
59 int need2
{vsnprintf(buffer
, need
+ 1, p
, ap
)};
68 const char *MessageFormattedText::Convert(const std::string
&s
) {
69 conversions_
.emplace_front(s
);
70 return conversions_
.front().c_str();
73 const char *MessageFormattedText::Convert(std::string
&&s
) {
74 conversions_
.emplace_front(std::move(s
));
75 return conversions_
.front().c_str();
78 const char *MessageFormattedText::Convert(const std::string_view
&s
) {
79 conversions_
.emplace_front(s
);
80 return conversions_
.front().c_str();
83 const char *MessageFormattedText::Convert(std::string_view
&&s
) {
84 conversions_
.emplace_front(s
);
85 return conversions_
.front().c_str();
88 const char *MessageFormattedText::Convert(CharBlock x
) {
89 return Convert(x
.ToString());
92 std::string
MessageExpectedText::ToString() const {
96 return MessageFormattedText("expected '%s'"_err_en_US
, cb
)
99 [](const SetOfChars
&set
) {
100 SetOfChars expect
{set
};
101 if (expect
.Has('\n')) {
102 expect
= expect
.Difference('\n');
103 if (expect
.empty()) {
104 return "expected end of line"_err_en_US
.text().ToString();
106 std::string s
{expect
.ToString()};
108 return MessageFormattedText(
109 "expected end of line or '%s'"_err_en_US
, s
)
112 return MessageFormattedText(
113 "expected end of line or one of '%s'"_err_en_US
, s
)
118 std::string s
{expect
.ToString()};
120 return MessageFormattedText("expected one of '%s'"_err_en_US
, s
)
123 return MessageFormattedText("expected '%s'"_err_en_US
, s
)
131 bool MessageExpectedText::Merge(const MessageExpectedText
&that
) {
132 return common::visit(common::visitors
{
133 [](SetOfChars
&s1
, const SetOfChars
&s2
) {
137 [](const auto &, const auto &) { return false; },
142 bool Message::SortBefore(const Message
&that
) const {
143 // Messages from prescanning have ProvenanceRange values for their locations,
144 // while messages from later phases have CharBlock values, since the
145 // conversion of cooked source stream locations to provenances is not
146 // free and needs to be deferred, and many messages created during parsing
147 // are speculative. Messages with ProvenanceRange locations are ordered
148 // before others for sorting.
149 return common::visit(
151 [](CharBlock cb1
, CharBlock cb2
) {
152 return cb1
.begin() < cb2
.begin();
154 [](CharBlock
, const ProvenanceRange
&) { return false; },
155 [](const ProvenanceRange
&pr1
, const ProvenanceRange
&pr2
) {
156 return pr1
.start() < pr2
.start();
158 [](const ProvenanceRange
&, CharBlock
) { return true; },
160 location_
, that
.location_
);
163 bool Message::IsFatal() const {
164 return severity() == Severity::Error
|| severity() == Severity::Todo
;
167 Severity
Message::severity() const {
168 return common::visit(
170 [](const MessageExpectedText
&) { return Severity::Error
; },
171 [](const MessageFixedText
&x
) { return x
.severity(); },
172 [](const MessageFormattedText
&x
) { return x
.severity(); },
177 Message
&Message::set_severity(Severity severity
) {
180 [](const MessageExpectedText
&) {},
181 [severity
](MessageFixedText
&x
) { x
.set_severity(severity
); },
182 [severity
](MessageFormattedText
&x
) { x
.set_severity(severity
); },
188 std::string
Message::ToString() const {
189 return common::visit(
191 [](const MessageFixedText
&t
) {
192 return t
.text().NULTerminatedToString();
194 [](const MessageFormattedText
&t
) { return t
.string(); },
195 [](const MessageExpectedText
&e
) { return e
.ToString(); },
200 void Message::ResolveProvenances(const AllCookedSources
&allCooked
) {
201 if (CharBlock
* cb
{std::get_if
<CharBlock
>(&location_
)}) {
202 if (std::optional
<ProvenanceRange
> resolved
{
203 allCooked
.GetProvenanceRange(*cb
)}) {
204 location_
= *resolved
;
207 if (Message
* attachment
{attachment_
.get()}) {
208 attachment
->ResolveProvenances(allCooked
);
212 std::optional
<ProvenanceRange
> Message::GetProvenanceRange(
213 const AllCookedSources
&allCooked
) const {
214 return common::visit(
216 [&](CharBlock cb
) { return allCooked
.GetProvenanceRange(cb
); },
217 [](const ProvenanceRange
&pr
) { return std::make_optional(pr
); },
222 static std::string
Prefix(Severity severity
) {
224 case Severity::Error
:
226 case Severity::Warning
:
228 case Severity::Portability
:
229 return "portability: ";
230 case Severity::Because
:
232 case Severity::Context
:
233 return "in the context: ";
235 return "error: not yet implemented: ";
242 static llvm::raw_ostream::Colors
PrefixColor(Severity severity
) {
244 case Severity::Error
:
246 return llvm::raw_ostream::RED
;
247 case Severity::Warning
:
248 case Severity::Portability
:
249 return llvm::raw_ostream::MAGENTA
;
251 // TODO: Set the color.
254 return llvm::raw_ostream::SAVEDCOLOR
;
257 void Message::Emit(llvm::raw_ostream
&o
, const AllCookedSources
&allCooked
,
258 bool echoSourceLine
) const {
259 std::optional
<ProvenanceRange
> provenanceRange
{GetProvenanceRange(allCooked
)};
260 const AllSources
&sources
{allCooked
.allSources()};
261 sources
.EmitMessage(o
, provenanceRange
, ToString(), Prefix(severity()),
262 PrefixColor(severity()), echoSourceLine
);
263 bool isContext
{attachmentIsContext_
};
264 for (const Message
*attachment
{attachment_
.get()}; attachment
;
265 attachment
= attachment
->attachment_
.get()) {
266 Severity severity
= isContext
? Severity::Context
: attachment
->severity();
267 sources
.EmitMessage(o
, attachment
->GetProvenanceRange(allCooked
),
268 attachment
->ToString(), Prefix(severity
), PrefixColor(severity
),
273 // Messages are equal if they're for the same location and text, and the user
274 // visible aspects of their attachments are the same
275 bool Message::operator==(const Message
&that
) const {
276 if (!AtSameLocation(that
) || ToString() != that
.ToString() ||
277 severity() != that
.severity() ||
278 attachmentIsContext_
!= that
.attachmentIsContext_
) {
281 const Message
*thatAttachment
{that
.attachment_
.get()};
282 for (const Message
*attachment
{attachment_
.get()}; attachment
;
283 attachment
= attachment
->attachment_
.get()) {
284 if (!thatAttachment
|| !attachment
->AtSameLocation(*thatAttachment
) ||
285 attachment
->ToString() != thatAttachment
->ToString() ||
286 attachment
->severity() != thatAttachment
->severity()) {
289 thatAttachment
= thatAttachment
->attachment_
.get();
291 return !thatAttachment
;
294 bool Message::Merge(const Message
&that
) {
295 return AtSameLocation(that
) &&
296 (!that
.attachment_
.get() ||
297 attachment_
.get() == that
.attachment_
.get()) &&
300 [](MessageExpectedText
&e1
, const MessageExpectedText
&e2
) {
303 [](const auto &, const auto &) { return false; },
308 Message
&Message::Attach(Message
*m
) {
312 if (attachment_
->references() > 1) {
313 // Don't attach to a shared context attachment; copy it first.
314 attachment_
= new Message
{*attachment_
};
316 attachment_
->Attach(m
);
321 Message
&Message::Attach(std::unique_ptr
<Message
> &&m
) {
322 return Attach(m
.release());
325 bool Message::AtSameLocation(const Message
&that
) const {
326 return common::visit(
328 [](CharBlock cb1
, CharBlock cb2
) {
329 return cb1
.begin() == cb2
.begin();
331 [](const ProvenanceRange
&pr1
, const ProvenanceRange
&pr2
) {
332 return pr1
.start() == pr2
.start();
334 [](const auto &, const auto &) { return false; },
336 location_
, that
.location_
);
339 bool Messages::Merge(const Message
&msg
) {
340 if (msg
.IsMergeable()) {
341 for (auto &m
: messages_
) {
350 void Messages::Merge(Messages
&&that
) {
351 if (messages_
.empty()) {
352 *this = std::move(that
);
354 while (!that
.messages_
.empty()) {
355 if (Merge(that
.messages_
.front())) {
356 that
.messages_
.pop_front();
358 auto next
{that
.messages_
.begin()};
361 messages_
.end(), that
.messages_
, that
.messages_
.begin(), next
);
367 void Messages::Copy(const Messages
&that
) {
368 for (const Message
&m
: that
.messages_
) {
370 Say(std::move(copy
));
374 void Messages::ResolveProvenances(const AllCookedSources
&allCooked
) {
375 for (Message
&m
: messages_
) {
376 m
.ResolveProvenances(allCooked
);
380 void Messages::Emit(llvm::raw_ostream
&o
, const AllCookedSources
&allCooked
,
381 bool echoSourceLines
) const {
382 std::vector
<const Message
*> sorted
;
383 for (const auto &msg
: messages_
) {
384 sorted
.push_back(&msg
);
386 std::stable_sort(sorted
.begin(), sorted
.end(),
387 [](const Message
*x
, const Message
*y
) { return x
->SortBefore(*y
); });
388 const Message
*lastMsg
{nullptr};
389 for (const Message
*msg
: sorted
) {
390 if (lastMsg
&& *msg
== *lastMsg
) {
391 // Don't emit two identical messages for the same location
394 msg
->Emit(o
, allCooked
, echoSourceLines
);
399 void Messages::AttachTo(Message
&msg
, std::optional
<Severity
> severity
) {
400 for (Message
&m
: messages_
) {
401 Message m2
{std::move(m
)};
403 m2
.set_severity(*severity
);
405 msg
.Attach(std::move(m2
));
410 bool Messages::AnyFatalError() const {
411 for (const auto &msg
: messages_
) {
418 } // namespace Fortran::parser