std::optional<Success> DebugParser::Parse(ParseState &state) const {
if (auto ustate = state.userState()) {
if (auto out = ustate->debugOutput()) {
- const CookedSource &cooked{ustate->cooked()};
- if (auto context = state.context()) {
- context->Emit(*out, cooked);
+ std::string note{str_, length_};
+ Message message{state.GetLocation(),
+ MessageFormattedText{"parser debug: %s"_en_US, note.data()}};
+ if (Message * context{state.context().get()}) {
+ message.set_context(context);
}
- Provenance p{cooked.GetProvenance(state.GetLocation()).start()};
- cooked.allSources().Identify(*out, p, "", true);
- *out << " parser debug: " << std::string{str_, length_} << "\n\n";
+ message.Emit(*out, ustate->cooked(), true);
}
}
return {Success{}};
#include "message.h"
#include "char-set.h"
+#include <algorithm>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <string>
+#include <vector>
namespace Fortran::parser {
void Message::Emit(
std::ostream &o, const CookedSource &cooked, bool echoSourceLine) const {
ProvenanceRange provenanceRange{GetProvenance(cooked)};
- bool doIdentify{true};
- if (context_) {
- bool sameProvenance{provenanceRange == context_->GetProvenance(cooked)};
- context_->Emit(o, cooked, echoSourceLine && sameProvenance);
- doIdentify = !sameProvenance;
- }
- if (doIdentify) {
- cooked.allSources().Identify(o, provenanceRange, "", echoSourceLine);
- }
- o << " ";
+ std::string text;
if (isFatal_) {
- o << "ERROR: ";
+ text += "ERROR: ";
+ }
+ text += ToString();
+ cooked.allSources().EmitMessage(o, provenanceRange, text, echoSourceLine);
+ for (const Message *context{context_.get()}; context != nullptr;
+ context = context->context_.get()) {
+ ProvenanceRange contextProvenance{context->GetProvenance(cooked)};
+ text = "in the context: ";
+ text += context->ToString();
+ cooked.allSources().EmitMessage(o, contextProvenance, text,
+ echoSourceLine && contextProvenance != provenanceRange);
+ provenanceRange = contextProvenance;
}
- o << ToString() << '\n';
}
void Messages::Incorporate(Messages &that) {
}
}
-void Messages::Emit(std::ostream &o, const CookedSource &cooked,
- const char *prefix, bool echoSourceLines) const {
+void Messages::Emit(
+ std::ostream &o, const CookedSource &cooked, bool echoSourceLines) const {
+ std::vector<const Message *> sorted;
for (const auto &msg : messages_) {
- if (prefix) {
- o << prefix;
- }
- if (msg.context()) {
- o << "In the context ";
- }
- msg.Emit(o, cooked, echoSourceLines);
+ sorted.push_back(&msg);
+ }
+ std::sort(sorted.begin(), sorted.end(),
+ [](const Message *x, const Message *y) { return *x < *y; });
+ for (const Message *msg : sorted) {
+ msg->Emit(o, cooked, echoSourceLines);
}
}
void Incorporate(Messages &);
void Copy(const Messages &);
-
void Emit(std::ostream &, const CookedSource &cooked,
- const char *prefix = nullptr, bool echoSourceLines = true) const;
+ bool echoSourceLines = true) const;
bool AnyFatalError() const;
Parse();
messages_.Emit(err, cooked_);
if (!consumedWholeFile_) {
- err << "f18 parser FAIL; final position: ";
- Identify(err, finalRestingPlace_, " ");
+ EmitMessage(err, finalRestingPlace_, "parser FAIL; final position");
return false;
}
if (messages_.AnyFatalError() || !parseTree_.has_value()) {
void Parse(std::ostream *debugOutput = nullptr);
void ClearLog();
- void Identify(std::ostream &o, const char *at, const std::string &prefix,
+ void EmitMessage(std::ostream &o, const char *at, const std::string &message,
bool echoSourceLine = false) const {
- allSources_.Identify(
- o, cooked_.GetProvenance(at).start(), prefix, echoSourceLine);
+ allSources_.EmitMessage(
+ o, cooked_.GetProvenance(at).start(), message, echoSourceLine);
}
bool ForTesting(std::string path, std::ostream &);
}
AllSources::AllSources() : range_{1, 1} {
- // Start the origin_ array with a dummy that has a forced provenance,
+ // Start the origin_ array with a dummy entry that has a forced provenance,
// so that provenance offset 0 remains reserved as an uninitialized
// value.
origin_.emplace_back(range_, std::string{'?'});
return covers;
}
-void AllSources::Identify(std::ostream &o, ProvenanceRange range,
- const std::string &prefix, bool echoSourceLine) const {
+void AllSources::EmitMessage(std::ostream &o, ProvenanceRange range,
+ const std::string &message, bool echoSourceLine) const {
CHECK(IsValid(range));
- static const std::string indented{prefix + " "};
const Origin &origin{MapToOrigin(range.start())};
std::visit(
visitors{
[&](const Inclusion &inc) {
+ o << inc.source.path();
std::size_t offset{origin.covers.MemberOffset(range.start())};
std::pair<int, int> pos{inc.source.FindOffsetLineAndColumn(offset)};
- o << prefix << "at line " << pos.first << ", column " << pos.second;
+ o << ':' << pos.first << ':' << pos.second;
+ o << ": " << message << '\n';
if (echoSourceLine) {
- o << ":\n" << indented << " ";
const char *text{inc.source.content() +
inc.source.GetLineStartOffset(pos.first)};
+ o << " ";
for (const char *p{text}; *p != '\n'; ++p) {
o << *p;
}
- o << '\n' << indented << " ";
+ o << "\n ";
for (int j{1}; j < pos.second; ++j) {
char ch{text[j - 1]};
o << (ch == '\t' ? '\t' : ' ');
}
}
}
- o << '\n' << prefix;
- } else {
- o << ' ';
+ o << '\n';
}
- o << "in the " << (inc.isModule ? "module " : "file ")
- << inc.source.path();
if (IsValid(origin.replaces)) {
- o << (inc.isModule ? " used\n" : " included\n");
- Identify(o, origin.replaces.start(), indented);
- } else {
- o << '\n';
+ EmitMessage(o, origin.replaces,
+ inc.isModule ? "used here"s : "included here"s,
+ echoSourceLine);
}
},
[&](const Macro &mac) {
- o << prefix << "in the expansion of a macro that was defined\n";
- Identify(o, mac.definition.start(), indented, echoSourceLine);
- o << prefix << "and called\n";
- Identify(o, origin.replaces.start(), indented, echoSourceLine);
+ EmitMessage(o, origin.replaces, message, echoSourceLine);
+ EmitMessage(
+ o, mac.definition, "in a macro defined here", echoSourceLine);
if (echoSourceLine) {
- o << prefix << "and expanded to\n"
- << indented << " " << mac.expansion << '\n'
- << indented << " ";
+ o << "that expanded to:\n " << mac.expansion << "\n ";
for (std::size_t j{0};
origin.covers.OffsetMember(j) < range.start(); ++j) {
o << (mac.expansion[j] == '\t' ? '\t' : ' ');
o << "^\n";
}
},
- [&](const CompilerInsertion &ins) {
- o << prefix << ins.text << '\n';
- }},
+ [&](const CompilerInsertion &ins) { o << message << '\n'; }},
origin.u);
}
}
}},
m.u);
+ if (IsValid(m.replaces)) {
+ o << " replaces ";
+ DumpRange(o, m.replaces);
+ }
o << '\n';
}
}
bool IsValid(ProvenanceRange range) const {
return range.size() > 0 && range_.Contains(range);
}
- void Identify(std::ostream &, ProvenanceRange, const std::string &prefix,
+ void EmitMessage(std::ostream &, ProvenanceRange, const std::string &message,
bool echoSourceLine = false) const;
const SourceFile *GetSourceFile(
Provenance, std::size_t *offset = nullptr) const;
if (!parsing.messages().empty() &&
(driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
std::cerr << driver.prefix << "could not scan " << path << '\n';
- parsing.messages().Emit(std::cerr, parsing.cooked(), driver.prefix);
+ parsing.messages().Emit(std::cerr, parsing.cooked());
exitStatus = EXIT_FAILURE;
return {};
}
return {};
}
parsing.ClearLog();
- parsing.messages().Emit(std::cerr, parsing.cooked(), driver.prefix);
+ parsing.messages().Emit(std::cerr, parsing.cooked());
if (!parsing.consumedWholeFile()) {
- std::cerr << "f18 parser FAIL; final position: ";
- parsing.Identify(std::cerr, parsing.finalRestingPlace(), " ");
+ parsing.EmitMessage(std::cerr, parsing.finalRestingPlace(),
+ "parser FAIL (final position)");
exitStatus = EXIT_FAILURE;
return {};
}