2018-02-16 20:42:17 +01:00
|
|
|
#ifndef FORTRAN_PARSER_MESSAGE_H_
|
|
|
|
#define FORTRAN_PARSER_MESSAGE_H_
|
2018-01-30 20:53:49 +01:00
|
|
|
|
|
|
|
// Defines a representation for sequences of compiler messages.
|
|
|
|
// Supports nested contextualization.
|
|
|
|
|
|
|
|
#include "idioms.h"
|
2018-02-09 23:04:11 +01:00
|
|
|
#include "provenance.h"
|
2018-03-20 18:59:07 +01:00
|
|
|
#include <cstddef>
|
2018-01-30 20:53:49 +01:00
|
|
|
#include <forward_list>
|
|
|
|
#include <memory>
|
|
|
|
#include <optional>
|
|
|
|
#include <ostream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
namespace Fortran {
|
2018-02-07 21:04:42 +01:00
|
|
|
namespace parser {
|
2018-01-30 20:53:49 +01:00
|
|
|
|
2018-04-03 00:51:04 +02:00
|
|
|
// Use "..."_err_en_US and "..."_en_US literals to define the static
|
|
|
|
// text and fatality of a message.
|
2018-02-20 18:57:30 +01:00
|
|
|
class MessageFixedText {
|
2018-02-17 00:47:30 +01:00
|
|
|
public:
|
2018-02-20 18:57:30 +01:00
|
|
|
MessageFixedText() {}
|
2018-04-03 00:51:04 +02:00
|
|
|
constexpr MessageFixedText(
|
|
|
|
const char str[], std::size_t n, bool isFatal = false)
|
|
|
|
: str_{str}, bytes_{n}, isFatal_{isFatal} {}
|
2018-02-20 18:57:30 +01:00
|
|
|
constexpr MessageFixedText(const MessageFixedText &) = default;
|
|
|
|
MessageFixedText(MessageFixedText &&) = default;
|
|
|
|
constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
|
|
|
|
MessageFixedText &operator=(MessageFixedText &&) = default;
|
2018-02-17 00:47:30 +01:00
|
|
|
|
|
|
|
const char *str() const { return str_; }
|
2018-03-20 18:59:07 +01:00
|
|
|
std::size_t size() const { return bytes_; }
|
2018-02-17 01:57:40 +01:00
|
|
|
bool empty() const { return bytes_ == 0; }
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal() const { return isFatal_; }
|
2018-02-17 00:47:30 +01:00
|
|
|
|
2018-02-21 21:12:52 +01:00
|
|
|
std::string ToString() const;
|
2018-02-17 00:47:30 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
const char *str_{nullptr};
|
2018-03-20 18:59:07 +01:00
|
|
|
std::size_t bytes_{0};
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal_{false};
|
2018-02-17 00:47:30 +01:00
|
|
|
};
|
|
|
|
|
2018-03-20 18:59:07 +01:00
|
|
|
constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
|
2018-04-03 00:51:04 +02:00
|
|
|
return MessageFixedText{str, n, false /* not fatal */};
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr MessageFixedText operator""_err_en_US(
|
|
|
|
const char str[], std::size_t n) {
|
|
|
|
return MessageFixedText{str, n, true /* fatal */};
|
2018-02-17 00:47:30 +01:00
|
|
|
}
|
|
|
|
|
2018-02-20 18:57:30 +01:00
|
|
|
std::ostream &operator<<(std::ostream &, const MessageFixedText &);
|
|
|
|
|
2018-02-21 21:12:52 +01:00
|
|
|
class MessageFormattedText {
|
|
|
|
public:
|
|
|
|
MessageFormattedText(MessageFixedText, ...);
|
|
|
|
std::string MoveString() { return std::move(string_); }
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal() const { return isFatal_; }
|
2018-02-21 21:12:52 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
std::string string_;
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal_{false};
|
2018-02-21 21:12:52 +01:00
|
|
|
};
|
|
|
|
|
2018-04-03 00:51:04 +02:00
|
|
|
// Represents a formatted rendition of "expected '%s'"_err_en_US
|
|
|
|
// on a constant text.
|
2018-02-21 21:12:52 +01:00
|
|
|
class MessageExpectedText {
|
|
|
|
public:
|
2018-03-20 18:59:07 +01:00
|
|
|
MessageExpectedText(const char *s, std::size_t n) : str_{s}, bytes_{n} {}
|
2018-02-21 21:12:52 +01:00
|
|
|
explicit MessageExpectedText(char ch) : singleton_{ch} {}
|
|
|
|
MessageFixedText AsMessageFixedText() const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
const char *str_{nullptr};
|
|
|
|
char singleton_;
|
2018-03-20 18:59:07 +01:00
|
|
|
std::size_t bytes_{1};
|
2018-02-21 21:12:52 +01:00
|
|
|
};
|
|
|
|
|
2018-02-20 18:57:30 +01:00
|
|
|
class Message;
|
|
|
|
using MessageContext = std::shared_ptr<Message>;
|
2018-02-17 00:47:30 +01:00
|
|
|
|
2018-01-30 20:53:49 +01:00
|
|
|
class Message {
|
2018-02-05 21:54:36 +01:00
|
|
|
public:
|
2018-02-03 00:52:43 +01:00
|
|
|
Message() {}
|
|
|
|
Message(const Message &) = default;
|
2018-03-23 23:14:52 +01:00
|
|
|
Message(Message &&) = default;
|
|
|
|
Message &operator=(const Message &that) = default;
|
|
|
|
Message &operator=(Message &&that) = default;
|
|
|
|
|
2018-04-03 01:33:10 +02:00
|
|
|
// TODO: Change these to cover ranges of provenance
|
2018-02-20 18:57:30 +01:00
|
|
|
Message(Provenance p, MessageFixedText t, MessageContext c = nullptr)
|
2018-04-03 00:51:04 +02:00
|
|
|
: provenance_{p}, text_{t}, context_{c}, isFatal_{t.isFatal()} {}
|
2018-02-21 21:12:52 +01:00
|
|
|
Message(Provenance p, MessageFormattedText &&s, MessageContext c = nullptr)
|
2018-04-03 00:51:04 +02:00
|
|
|
: provenance_{p}, string_{s.MoveString()}, context_{c}, isFatal_{
|
|
|
|
s.isFatal()} {}
|
2018-02-21 21:12:52 +01:00
|
|
|
Message(Provenance p, MessageExpectedText t, MessageContext c = nullptr)
|
|
|
|
: provenance_{p}, text_{t.AsMessageFixedText()},
|
2018-04-03 00:51:04 +02:00
|
|
|
isExpectedText_{true}, context_{c}, isFatal_{true} {}
|
2018-03-23 23:14:52 +01:00
|
|
|
|
|
|
|
Message(const char *csl, MessageFixedText t, MessageContext c = nullptr)
|
2018-04-03 00:51:04 +02:00
|
|
|
: cookedSourceLocation_{csl}, text_{t}, context_{c}, isFatal_{t.isFatal()} {
|
|
|
|
}
|
2018-03-23 23:14:52 +01:00
|
|
|
Message(const char *csl, MessageFormattedText &&s, MessageContext c = nullptr)
|
2018-04-03 00:51:04 +02:00
|
|
|
: cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c},
|
|
|
|
isFatal_{s.isFatal()} {}
|
2018-03-23 23:14:52 +01:00
|
|
|
Message(const char *csl, MessageExpectedText t, MessageContext c = nullptr)
|
|
|
|
: cookedSourceLocation_{csl}, text_{t.AsMessageFixedText()},
|
2018-04-03 00:51:04 +02:00
|
|
|
isExpectedText_{true}, context_{c}, isFatal_{true} {}
|
2018-01-30 20:53:49 +01:00
|
|
|
|
2018-02-09 23:04:11 +01:00
|
|
|
bool operator<(const Message &that) const {
|
2018-03-23 23:14:52 +01:00
|
|
|
if (cookedSourceLocation_ != nullptr) {
|
|
|
|
return cookedSourceLocation_ < that.cookedSourceLocation_;
|
|
|
|
} else if (that.cookedSourceLocation_ != nullptr) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return provenance_ < that.provenance_;
|
|
|
|
}
|
2018-02-09 23:04:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Provenance provenance() const { return provenance_; }
|
2018-03-23 23:14:52 +01:00
|
|
|
const char *cookedSourceLocation() const { return cookedSourceLocation_; }
|
2018-01-30 20:53:49 +01:00
|
|
|
MessageContext context() const { return context_; }
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal() const { return isFatal_; }
|
2018-01-30 20:53:49 +01:00
|
|
|
|
2018-02-15 22:13:28 +01:00
|
|
|
Provenance Emit(
|
2018-03-23 23:14:52 +01:00
|
|
|
std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
|
2018-01-30 20:53:49 +01:00
|
|
|
|
2018-02-05 21:54:36 +01:00
|
|
|
private:
|
2018-02-09 23:04:11 +01:00
|
|
|
Provenance provenance_;
|
2018-03-23 23:14:52 +01:00
|
|
|
const char *cookedSourceLocation_{nullptr};
|
2018-02-20 18:57:30 +01:00
|
|
|
MessageFixedText text_;
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isExpectedText_{false}; // implies "expected '%s'"_err_en_US
|
2018-02-21 21:12:52 +01:00
|
|
|
std::string string_;
|
2018-01-30 20:53:49 +01:00
|
|
|
MessageContext context_;
|
2018-04-03 00:51:04 +02:00
|
|
|
bool isFatal_{false};
|
2018-01-30 20:53:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class Messages {
|
|
|
|
using list_type = std::forward_list<Message>;
|
2018-02-05 23:29:26 +01:00
|
|
|
|
2018-02-05 21:54:36 +01:00
|
|
|
public:
|
2018-01-30 20:53:49 +01:00
|
|
|
using iterator = list_type::iterator;
|
|
|
|
using const_iterator = list_type::const_iterator;
|
|
|
|
|
2018-03-23 23:14:52 +01:00
|
|
|
explicit Messages(const CookedSource &cooked) : cooked_{cooked} {}
|
2018-01-30 20:53:49 +01:00
|
|
|
Messages(Messages &&that)
|
2018-03-23 23:14:52 +01:00
|
|
|
: cooked_{that.cooked_}, messages_{std::move(that.messages_)},
|
2018-02-12 21:48:13 +01:00
|
|
|
last_{that.last_} {}
|
2018-01-30 20:53:49 +01:00
|
|
|
Messages &operator=(Messages &&that) {
|
|
|
|
swap(that);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void swap(Messages &that) {
|
|
|
|
messages_.swap(that.messages_);
|
|
|
|
std::swap(last_, that.last_);
|
|
|
|
}
|
|
|
|
|
2018-03-30 01:06:31 +02:00
|
|
|
bool empty() const { return messages_.empty(); }
|
2018-01-30 20:53:49 +01:00
|
|
|
iterator begin() { return messages_.begin(); }
|
|
|
|
iterator end() { return messages_.end(); }
|
|
|
|
const_iterator begin() const { return messages_.cbegin(); }
|
|
|
|
const_iterator end() const { return messages_.cend(); }
|
|
|
|
const_iterator cbegin() const { return messages_.cbegin(); }
|
|
|
|
const_iterator cend() const { return messages_.cend(); }
|
|
|
|
|
2018-03-23 23:14:52 +01:00
|
|
|
const CookedSource &cooked() const { return cooked_; }
|
|
|
|
|
|
|
|
bool IsValidLocation(const Message &m) {
|
|
|
|
if (auto p{m.cookedSourceLocation()}) {
|
|
|
|
return cooked_.IsValid(p);
|
|
|
|
} else {
|
|
|
|
return cooked_.IsValid(m.provenance());
|
|
|
|
}
|
|
|
|
}
|
2018-02-12 21:48:13 +01:00
|
|
|
|
2018-02-17 01:57:40 +01:00
|
|
|
Message &Put(Message &&m) {
|
2018-03-23 23:14:52 +01:00
|
|
|
CHECK(IsValidLocation(m));
|
2018-01-30 20:53:49 +01:00
|
|
|
if (messages_.empty()) {
|
|
|
|
messages_.emplace_front(std::move(m));
|
|
|
|
last_ = messages_.begin();
|
|
|
|
} else {
|
|
|
|
last_ = messages_.emplace_after(last_, std::move(m));
|
|
|
|
}
|
2018-02-17 01:57:40 +01:00
|
|
|
return *last_;
|
2018-01-30 20:53:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Annex(Messages *that) {
|
|
|
|
if (!that->messages_.empty()) {
|
|
|
|
if (messages_.empty()) {
|
|
|
|
messages_ = std::move(that->messages_);
|
|
|
|
} else {
|
|
|
|
messages_.splice_after(last_, that->messages_);
|
|
|
|
}
|
|
|
|
last_ = that->last_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-23 23:14:52 +01:00
|
|
|
void Emit(std::ostream &, const char *prefix = nullptr,
|
|
|
|
bool echoSourceLines = true) const;
|
2018-02-09 23:04:11 +01:00
|
|
|
|
2018-04-03 00:51:04 +02:00
|
|
|
bool AnyFatalError() const;
|
|
|
|
|
2018-02-05 21:54:36 +01:00
|
|
|
private:
|
2018-03-23 23:14:52 +01:00
|
|
|
const CookedSource &cooked_;
|
2018-01-30 20:53:49 +01:00
|
|
|
list_type messages_;
|
|
|
|
iterator last_; // valid iff messages_ nonempty
|
|
|
|
};
|
2018-02-07 21:04:42 +01:00
|
|
|
} // namespace parser
|
2018-01-30 20:53:49 +01:00
|
|
|
} // namespace Fortran
|
2018-02-16 20:42:17 +01:00
|
|
|
#endif // FORTRAN_PARSER_MESSAGE_H_
|