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::optional
<common::LanguageFeature
> Message::languageFeature() const {
189 return languageFeature_
;
192 Message
&Message::set_languageFeature(common::LanguageFeature feature
) {
193 languageFeature_
= feature
;
197 std::optional
<common::UsageWarning
> Message::usageWarning() const {
198 return usageWarning_
;
201 Message
&Message::set_usageWarning(common::UsageWarning warning
) {
202 usageWarning_
= warning
;
206 std::string
Message::ToString() const {
207 return common::visit(
209 [](const MessageFixedText
&t
) {
210 return t
.text().NULTerminatedToString();
212 [](const MessageFormattedText
&t
) { return t
.string(); },
213 [](const MessageExpectedText
&e
) { return e
.ToString(); },
218 void Message::ResolveProvenances(const AllCookedSources
&allCooked
) {
219 if (CharBlock
* cb
{std::get_if
<CharBlock
>(&location_
)}) {
220 if (std::optional
<ProvenanceRange
> resolved
{
221 allCooked
.GetProvenanceRange(*cb
)}) {
222 location_
= *resolved
;
225 if (Message
* attachment
{attachment_
.get()}) {
226 attachment
->ResolveProvenances(allCooked
);
230 std::optional
<ProvenanceRange
> Message::GetProvenanceRange(
231 const AllCookedSources
&allCooked
) const {
232 return common::visit(
234 [&](CharBlock cb
) { return allCooked
.GetProvenanceRange(cb
); },
235 [](const ProvenanceRange
&pr
) { return std::make_optional(pr
); },
240 static std::string
Prefix(Severity severity
) {
242 case Severity::Error
:
244 case Severity::Warning
:
246 case Severity::Portability
:
247 return "portability: ";
248 case Severity::Because
:
250 case Severity::Context
:
251 return "in the context: ";
253 return "error: not yet implemented: ";
260 static llvm::raw_ostream::Colors
PrefixColor(Severity severity
) {
262 case Severity::Error
:
264 return llvm::raw_ostream::RED
;
265 case Severity::Warning
:
266 case Severity::Portability
:
267 return llvm::raw_ostream::MAGENTA
;
269 // TODO: Set the color.
272 return llvm::raw_ostream::SAVEDCOLOR
;
275 void Message::Emit(llvm::raw_ostream
&o
, const AllCookedSources
&allCooked
,
276 bool echoSourceLine
) const {
277 std::optional
<ProvenanceRange
> provenanceRange
{GetProvenanceRange(allCooked
)};
278 const AllSources
&sources
{allCooked
.allSources()};
279 sources
.EmitMessage(o
, provenanceRange
, ToString(), Prefix(severity()),
280 PrefixColor(severity()), echoSourceLine
);
281 bool isContext
{attachmentIsContext_
};
282 for (const Message
*attachment
{attachment_
.get()}; attachment
;
283 attachment
= attachment
->attachment_
.get()) {
284 Severity severity
= isContext
? Severity::Context
: attachment
->severity();
285 sources
.EmitMessage(o
, attachment
->GetProvenanceRange(allCooked
),
286 attachment
->ToString(), Prefix(severity
), PrefixColor(severity
),
291 // Messages are equal if they're for the same location and text, and the user
292 // visible aspects of their attachments are the same
293 bool Message::operator==(const Message
&that
) const {
294 if (!AtSameLocation(that
) || ToString() != that
.ToString() ||
295 severity() != that
.severity() ||
296 attachmentIsContext_
!= that
.attachmentIsContext_
) {
299 const Message
*thatAttachment
{that
.attachment_
.get()};
300 for (const Message
*attachment
{attachment_
.get()}; attachment
;
301 attachment
= attachment
->attachment_
.get()) {
302 if (!thatAttachment
|| !attachment
->AtSameLocation(*thatAttachment
) ||
303 attachment
->ToString() != thatAttachment
->ToString() ||
304 attachment
->severity() != thatAttachment
->severity()) {
307 thatAttachment
= thatAttachment
->attachment_
.get();
309 return !thatAttachment
;
312 bool Message::Merge(const Message
&that
) {
313 return AtSameLocation(that
) &&
314 (!that
.attachment_
.get() ||
315 attachment_
.get() == that
.attachment_
.get()) &&
318 [](MessageExpectedText
&e1
, const MessageExpectedText
&e2
) {
321 [](const auto &, const auto &) { return false; },
326 Message
&Message::Attach(Message
*m
) {
330 if (attachment_
->references() > 1) {
331 // Don't attach to a shared context attachment; copy it first.
332 attachment_
= new Message
{*attachment_
};
334 attachment_
->Attach(m
);
339 Message
&Message::Attach(std::unique_ptr
<Message
> &&m
) {
340 return Attach(m
.release());
343 bool Message::AtSameLocation(const Message
&that
) const {
344 return common::visit(
346 [](CharBlock cb1
, CharBlock cb2
) {
347 return cb1
.begin() == cb2
.begin();
349 [](const ProvenanceRange
&pr1
, const ProvenanceRange
&pr2
) {
350 return pr1
.start() == pr2
.start();
352 [](const auto &, const auto &) { return false; },
354 location_
, that
.location_
);
357 bool Messages::Merge(const Message
&msg
) {
358 if (msg
.IsMergeable()) {
359 for (auto &m
: messages_
) {
368 void Messages::Merge(Messages
&&that
) {
369 if (messages_
.empty()) {
370 *this = std::move(that
);
372 while (!that
.messages_
.empty()) {
373 if (Merge(that
.messages_
.front())) {
374 that
.messages_
.pop_front();
376 auto next
{that
.messages_
.begin()};
379 messages_
.end(), that
.messages_
, that
.messages_
.begin(), next
);
385 void Messages::Copy(const Messages
&that
) {
386 for (const Message
&m
: that
.messages_
) {
388 Say(std::move(copy
));
392 void Messages::ResolveProvenances(const AllCookedSources
&allCooked
) {
393 for (Message
&m
: messages_
) {
394 m
.ResolveProvenances(allCooked
);
398 void Messages::Emit(llvm::raw_ostream
&o
, const AllCookedSources
&allCooked
,
399 bool echoSourceLines
) const {
400 std::vector
<const Message
*> sorted
;
401 for (const auto &msg
: messages_
) {
402 sorted
.push_back(&msg
);
404 std::stable_sort(sorted
.begin(), sorted
.end(),
405 [](const Message
*x
, const Message
*y
) { return x
->SortBefore(*y
); });
406 const Message
*lastMsg
{nullptr};
407 for (const Message
*msg
: sorted
) {
408 if (lastMsg
&& *msg
== *lastMsg
) {
409 // Don't emit two identical messages for the same location
412 msg
->Emit(o
, allCooked
, echoSourceLines
);
417 void Messages::AttachTo(Message
&msg
, std::optional
<Severity
> severity
) {
418 for (Message
&m
: messages_
) {
419 Message m2
{std::move(m
)};
421 m2
.set_severity(*severity
);
423 msg
.Attach(std::move(m2
));
428 bool Messages::AnyFatalError() const {
429 for (const auto &msg
: messages_
) {
436 } // namespace Fortran::parser