class CharBlock {
public:
- CharBlock() {}
- CharBlock(const char *x, std::size_t n = 1) : interval_{x, n} {}
- CharBlock(const char *b, const char *ep1)
+ constexpr CharBlock() {}
+ constexpr CharBlock(const char *x, std::size_t n = 1) : interval_{x, n} {}
+ constexpr CharBlock(const char *b, const char *ep1)
: interval_{b, static_cast<std::size_t>(ep1 - b)} {}
CharBlock(const std::string &s) : interval_{s.data(), s.size()} {}
- CharBlock(const CharBlock &) = default;
- CharBlock(CharBlock &&) = default;
- CharBlock &operator=(const CharBlock &) = default;
- CharBlock &operator=(CharBlock &&) = default;
+ constexpr CharBlock(const CharBlock &) = default;
+ constexpr CharBlock(CharBlock &&) = default;
+ constexpr CharBlock &operator=(const CharBlock &) = default;
+ constexpr CharBlock &operator=(CharBlock &&) = default;
- bool empty() const { return interval_.empty(); }
- std::size_t size() const { return interval_.size(); }
- const char *begin() const { return interval_.start(); }
- const char *end() const { return interval_.start() + interval_.size(); }
- const char &operator[](std::size_t j) const { return interval_.start()[j]; }
+ constexpr bool empty() const { return interval_.empty(); }
+ constexpr std::size_t size() const { return interval_.size(); }
+ constexpr const char *begin() const { return interval_.start(); }
+ constexpr const char *end() const {
+ return interval_.start() + interval_.size();
+ }
+ constexpr const char &operator[](std::size_t j) const {
+ return interval_.start()[j];
+ }
bool IsBlank() const {
for (char ch : *this) {
return std::string{interval_.start(), interval_.size()};
}
+ // Convert to string, stopping early at any embedded '\0'.
+ std::string NULTerminatedToString() const {
+ return std::string{interval_.start(),
+ /*not in std::*/ strnlen(interval_.start(), interval_.size())};
+ }
+
bool operator<(const CharBlock &that) const { return Compare(that) < 0; }
bool operator<=(const CharBlock &that) const { return Compare(that) <= 0; }
bool operator==(const CharBlock &that) const { return Compare(that) == 0; }
// In the logs, just use the addresses of the message texts to sort the
// map keys.
bool operator<(const MessageFixedText &x, const MessageFixedText &y) {
- return x.str() < y.str();
+ return x.text().begin() < y.text().begin();
}
bool ParsingLog::Fails(
template<typename A> class Interval {
public:
using type = A;
- Interval() {}
- Interval(const A &s, std::size_t n = 1) : start_{s}, size_{n} {}
- Interval(A &&s, std::size_t n = 1) : start_{std::move(s)}, size_{n} {}
- Interval(const Interval &) = default;
- Interval(Interval &&) = default;
- Interval &operator=(const Interval &) = default;
- Interval &operator=(Interval &&) = default;
+ constexpr Interval() {}
+ constexpr Interval(const A &s, std::size_t n = 1) : start_{s}, size_{n} {}
+ constexpr Interval(A &&s, std::size_t n = 1)
+ : start_{std::move(s)}, size_{n} {}
+ constexpr Interval(const Interval &) = default;
+ constexpr Interval(Interval &&) = default;
+ constexpr Interval &operator=(const Interval &) = default;
+ constexpr Interval &operator=(Interval &&) = default;
- bool operator==(const Interval &that) const {
+ constexpr bool operator==(const Interval &that) const {
return start_ == that.start_ && size_ == that.size_;
}
- bool operator!=(const Interval &that) const {
+ constexpr bool operator!=(const Interval &that) const {
return start_ != that.start_ || size_ != that.size_;
}
- const A &start() const { return start_; }
- std::size_t size() const { return size_; }
- bool empty() const { return size_ == 0; }
+ constexpr const A &start() const { return start_; }
+ constexpr std::size_t size() const { return size_; }
+ constexpr bool empty() const { return size_ == 0; }
- bool Contains(const A &x) const { return start_ <= x && x < start_ + size_; }
- bool Contains(const Interval &that) const {
+ constexpr bool Contains(const A &x) const {
+ return start_ <= x && x < start_ + size_;
+ }
+ constexpr bool Contains(const Interval &that) const {
return Contains(that.start_) && Contains(that.start_ + (that.size_ - 1));
}
- bool ImmediatelyPrecedes(const Interval &that) const {
+ constexpr bool ImmediatelyPrecedes(const Interval &that) const {
return NextAfter() == that.start_;
}
void Annex(const Interval &that) {
return start_ + n;
}
- A Last() const { return start_ + (size_ - 1); }
- A NextAfter() const { return start_ + size_; }
- Interval Prefix(std::size_t n) const { return {start_, std::min(size_, n)}; }
+ constexpr A Last() const { return start_ + (size_ - 1); }
+ constexpr A NextAfter() const { return start_ + size_; }
+ constexpr Interval Prefix(std::size_t n) const {
+ return {start_, std::min(size_, n)};
+ }
Interval Suffix(std::size_t n) const {
CHECK(n <= size_);
return {start_ + n, size_ - n};
#include "message.h"
#include "char-set.h"
+#include "idioms.h"
#include <algorithm>
#include <cstdarg>
#include <cstddef>
namespace Fortran::parser {
std::ostream &operator<<(std::ostream &o, const MessageFixedText &t) {
- for (std::size_t j{0}; j < t.size(); ++j) {
- o << t.str()[j];
+ std::size_t n{t.text().size()};
+ for (std::size_t j{0}; j < n; ++j) {
+ o << t.text()[j];
}
return o;
}
-std::string MessageFixedText::ToString() const {
- return std::string{str_, /*not in std::*/ strnlen(str_, bytes_)};
-}
-
MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
: isFatal_{text.isFatal()} {
- const char *p{text.str()};
+ const char *p{text.text().begin()};
std::string asString;
- if (p[text.size()] != '\0') {
+ if (*text.text().end() != '\0') {
// not NUL-terminated
- asString = text.ToString();
+ asString = text.text().NULTerminatedToString();
p = asString.data();
}
char buffer[256];
string_ = buffer;
}
+std::string MessageExpectedText::ToString() const {
+ return std::visit(
+ visitors{[](const CharBlock &cb) {
+ return MessageFormattedText("expected '%s'"_err_en_US,
+ cb.NULTerminatedToString().data())
+ .MoveString();
+ },
+ [](const SetOfChars &set) {
+ SetOfChars expect{set};
+ if (expect.Has('\n')) {
+ expect = expect.Difference('\n');
+ if (expect.empty()) {
+ return "expected end of line"_err_en_US.text().ToString();
+ } else {
+ std::string s{expect.ToString()};
+ if (s.size() == 1) {
+ return MessageFormattedText(
+ "expected end of line or '%s'"_err_en_US, s.data())
+ .MoveString();
+ } else {
+ return MessageFormattedText(
+ "expected end of line or one of '%s'"_err_en_US, s.data())
+ .MoveString();
+ }
+ }
+ }
+ std::string s{expect.ToString()};
+ if (s.size() != 1) {
+ return MessageFormattedText(
+ "expected one of '%s'"_err_en_US, s.data())
+ .MoveString();
+ } else {
+ return MessageFormattedText("expected '%s'"_err_en_US, s.data())
+ .MoveString();
+ }
+ }},
+ u_);
+}
+
+void MessageExpectedText::Incorporate(const MessageExpectedText &that) {
+ std::visit(
+ visitors{[&](SetOfChars &s1, const SetOfChars &s2) { s1.Union(s2); },
+ [](const auto &, const auto &) {}},
+ u_, that.u_);
+}
+
void Message::Incorporate(Message &that) {
- if (provenanceRange_.start() == that.provenanceRange_.start() &&
- cookedSourceRange_.begin() == that.cookedSourceRange_.begin() &&
- !expected_.empty()) {
- expected_ = expected_.Union(that.expected_);
- }
+ std::visit(
+ visitors{[&](MessageExpectedText &e1, const MessageExpectedText &e2) {
+ e1.Incorporate(e2);
+ },
+ [](const auto &, const auto &) {}},
+ text_, that.text_);
}
std::string Message::ToString() const {
- std::string s{string_};
- bool isExpected{isExpected_};
- if (string_.empty()) {
- if (fixedText_ != nullptr) {
- if (fixedBytes_ > 0 && fixedBytes_ < std::string::npos) {
- s = std::string(fixedText_, fixedBytes_);
- } else {
- s = std::string{fixedText_}; // NUL-terminated
- }
- } else {
- SetOfChars expect{expected_};
- if (expect.Has('\n')) {
- expect = expect.Difference('\n');
- if (expect.empty()) {
- return "expected end of line"_err_en_US.ToString();
- } else {
- s = expect.ToString();
- if (s.size() == 1) {
- return MessageFormattedText(
- "expected end of line or '%s'"_err_en_US, s.data())
- .MoveString();
- } else {
- return MessageFormattedText(
- "expected end of line or one of '%s'"_err_en_US, s.data())
- .MoveString();
- }
- }
- }
- s = expect.ToString();
- if (s.size() != 1) {
- return MessageFormattedText("expected one of '%s'"_err_en_US, s.data())
- .MoveString();
- }
- isExpected = true;
- }
- }
- if (isExpected) {
- return MessageFormattedText("expected '%s'"_err_en_US, s.data())
- .MoveString();
- }
- return s;
+ return std::visit(
+ visitors{[](const CharBlock &cb) { return cb.NULTerminatedToString(); },
+ [](const std::string &s) { return s; },
+ [](const MessageExpectedText &e) { return e.ToString(); }},
+ text_);
}
ProvenanceRange Message::GetProvenanceRange(const CookedSource &cooked) const {
- if (cookedSourceRange_.begin() != nullptr) {
- return cooked.GetProvenanceRange(cookedSourceRange_);
- }
- return provenanceRange_;
+ return std::visit(visitors{[&](const CharBlock &cb) {
+ return cooked.GetProvenanceRange(cb);
+ },
+ [](const ProvenanceRange &pr) { return pr; }},
+ location_);
}
void Message::Emit(
}
}
+bool Message::AtSameLocation(const Message &that) const {
+ return std::visit(
+ visitors{[](const CharBlock &cb1, const CharBlock &cb2) {
+ return cb1.begin() == cb2.begin();
+ },
+ [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
+ return pr1.start() == pr2.start();
+ },
+ [](const auto &, const auto &) { return false; }},
+ location_, that.location_);
+}
+
+bool Message::operator<(const Message &that) const {
+ // Messages from prescanning have ProvenanceRange values for their locations,
+ // while messages from later phases have CharBlock values, since the
+ // conversion of cooked source stream locations to provenances is not
+ // free and needs to be deferred, since many messages created during parsing
+ // are speculative. Messages with ProvenanceRange locations are ordered
+ // before others for sorting.
+ return std::visit(
+ visitors{[](const CharBlock &cb1, const CharBlock &cb2) {
+ return cb1.begin() < cb2.begin();
+ },
+ [](const CharBlock &, const ProvenanceRange &) { return false; },
+ [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {
+ return pr1.start() < pr2.start();
+ },
+ [](const ProvenanceRange &, const CharBlock &) { return true; }},
+ location_, that.location_);
+}
+
void Messages::Incorporate(Messages &that) {
if (messages_.empty()) {
*this = std::move(that);
// Defines a representation for sequences of compiler messages.
// Supports nested contextualization.
+#include "char-block.h"
#include "char-set.h"
#include "idioms.h"
#include "provenance.h"
#include <ostream>
#include <string>
#include <utility>
+#include <variant>
namespace Fortran::parser {
MessageFixedText() {}
constexpr MessageFixedText(
const char str[], std::size_t n, bool isFatal = false)
- : str_{str}, bytes_{n}, isFatal_{isFatal} {}
+ : text_{str, n}, isFatal_{isFatal} {}
constexpr MessageFixedText(const MessageFixedText &) = default;
MessageFixedText(MessageFixedText &&) = default;
constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
MessageFixedText &operator=(MessageFixedText &&) = default;
- const char *str() const { return str_; }
- std::size_t size() const { return bytes_; }
- bool empty() const { return bytes_ == 0; }
+ const CharBlock &text() const { return text_; }
bool isFatal() const { return isFatal_; }
- std::string ToString() const;
-
private:
- const char *str_{nullptr};
- std::size_t bytes_{0};
+ CharBlock text_;
bool isFatal_{false};
};
};
// Represents a formatted rendition of "expected '%s'"_err_en_US
-// on a constant text.
+// on a constant text or a set of characters.
class MessageExpectedText {
public:
- MessageExpectedText(const char *s, std::size_t n) : str_{s}, bytes_{n} {
- if (n == std::string::npos) {
- bytes_ = std::strlen(s);
- }
- }
- MessageExpectedText(MessageExpectedText &&) = default;
- explicit MessageExpectedText(char ch) : set_{ch} {}
- explicit MessageExpectedText(SetOfChars set) : set_{set} {}
+ MessageExpectedText(const char *s, std::size_t n)
+ : u_{CharBlock{s, n == std::string::npos ? std::strlen(s) : n}} {}
+ constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {}
+ constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {}
+ constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {}
+ MessageExpectedText(const MessageExpectedText &) = default;
- const char *str() const { return str_; }
- std::size_t size() const { return bytes_; }
- SetOfChars set() const { return set_; }
+ std::string ToString() const;
+ void Incorporate(const MessageExpectedText &);
private:
- const char *str_{nullptr};
- std::size_t bytes_{0};
- SetOfChars set_;
+ std::variant<CharBlock, SetOfChars> u_;
};
class Message : public ReferenceCounted<Message> {
public:
using Context = CountedReference<Message>;
- Message() {}
Message(const Message &) = default;
Message(Message &&) = default;
Message &operator=(const Message &that) = default;
Message &operator=(Message &&that) = default;
Message(ProvenanceRange pr, MessageFixedText t)
- : provenanceRange_{pr}, fixedText_{t.str()},
- fixedBytes_{t.size()}, isFatal_{t.isFatal()} {}
+ : location_{pr}, text_{t.text()}, isFatal_{t.isFatal()} {}
Message(ProvenanceRange pr, MessageFormattedText &&s)
- : provenanceRange_{pr}, string_{s.MoveString()}, isFatal_{s.isFatal()} {}
+ : location_{pr}, text_{s.MoveString()}, isFatal_{s.isFatal()} {}
Message(ProvenanceRange pr, MessageExpectedText t)
- : provenanceRange_{pr}, fixedText_{t.str()}, fixedBytes_{t.size()},
- isExpected_{true}, expected_{t.set()}, isFatal_{true} {}
+ : location_{pr}, text_{t}, isFatal_{true} {}
Message(CharBlock csr, MessageFixedText t)
- : cookedSourceRange_{csr}, fixedText_{t.str()},
- fixedBytes_{t.size()}, isFatal_{t.isFatal()} {}
+ : location_{csr}, text_{t.text()}, isFatal_{t.isFatal()} {}
Message(CharBlock csr, MessageFormattedText &&s)
- : cookedSourceRange_{csr}, string_{s.MoveString()}, isFatal_{s.isFatal()} {}
+ : location_{csr}, text_{s.MoveString()}, isFatal_{s.isFatal()} {}
Message(CharBlock csr, MessageExpectedText t)
- : cookedSourceRange_{csr}, fixedText_{t.str()}, fixedBytes_{t.size()},
- isExpected_{true}, expected_{t.set()}, isFatal_{true} {}
-
- bool operator<(const Message &that) const {
- if (cookedSourceRange_.begin() != nullptr) {
- return cookedSourceRange_.begin() < that.cookedSourceRange_.begin();
- } else if (that.cookedSourceRange_.begin() != nullptr) {
- return false;
- } else {
- return provenanceRange_.start() < that.provenanceRange_.start();
- }
- }
+ : location_{csr}, text_{t}, isFatal_{true} {}
Context context() const { return context_; }
Message &set_context(Message *c) {
}
bool isFatal() const { return isFatal_; }
+ bool operator<(const Message &that) const;
void Incorporate(Message &);
std::string ToString() const;
ProvenanceRange GetProvenanceRange(const CookedSource &) const;
std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
private:
- ProvenanceRange provenanceRange_;
- CharBlock cookedSourceRange_;
- const char *fixedText_{nullptr};
- std::size_t fixedBytes_{0};
- bool isExpected_{false};
- std::string string_;
- SetOfChars expected_;
+ bool AtSameLocation(const Message &) const;
+
+ std::variant<ProvenanceRange, CharBlock> location_;
+ std::variant<CharBlock, MessageExpectedText, std::string> text_;
Context context_;
bool isFatal_{false};
};