FE 0.15.0
A C++23 toolkit for writing compiler/interpreter frontends.
Loading...
Searching...
No Matches
error.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <exception>
5#include <format>
6#include <functional>
7#include <iostream>
8#include <string>
9#include <utility>
10#include <vector>
11
12#include "fe/diag.h"
13#include "fe/format.h"
14#include "fe/loc.h"
15#include "fe/term.h"
16
17namespace fe {
18
19struct Driver;
20
21/// Collects diagnostics and hands each to the Diag that lays it out.
22/// Error::ack once you are done: it throws an Error::Bail if anything went wrong.
23class Error {
24public:
25 using Tag = Diag::Tag;
26
27 /// What Error::ack and Error::bail throw.
28 /// It carries the finished text, so nothing in here points into a SrcMap any more and it may
29 /// propagate past the Driver - and the Src%s - that produced it.
30 class Bail : public std::exception {
31 public:
32 Bail(std::string what, size_t num_errors, size_t num_warnings)
33 : what_(std::move(what))
34 , num_errors_(num_errors)
35 , num_warnings_(num_warnings) {}
36
37 const char* what() const noexcept override { return what_.c_str(); }
38 size_t num_errors() const { return num_errors_; }
39 size_t num_warnings() const { return num_warnings_; }
40
41 friend std::ostream& operator<<(std::ostream& os, const Bail& bail) { return os << bail.what_; }
42
43 private:
44 std::string what_;
45 size_t num_errors_;
46 size_t num_warnings_;
47 };
48
49 /// A secondary message elaborating a Msg.
50 /// A Note with a Loc of its own reads as a diagnostic of its own - header line plus snippet;
51 /// one without has nowhere else to point and renders as a `= note:` continuation line.
52 struct Note {
54 std::string str;
55 };
56
57 /// One Tag::Error or Tag::Warn together with the Note%s that belong to it.
58 struct Msg {
61 std::string str;
62 std::vector<Note> notes;
63 };
64
65 /// A sink of your own; Driver::error is the one every frontend building block already reports to.
66 /// @warning A Msg::loc points into @p driver's SrcMap and Error::msg renders through Diag::render,
67 /// so @p driver must outlive this Error.
68 explicit Error(const Driver& driver)
69 : driver_(&driver) {}
70
71 /// @name Getters
72 ///@{
73 const auto& msgs() const { return msgs_; }
74 bool empty() const { return msgs_.empty(); }
75 bool ok() const { return num_errors() == 0; } ///< Nothing recorded that must stop the compilation?
76 size_t num_errors() const { return num_[size_t(Tag::E)]; }
77 size_t num_warnings() const { return num_[size_t(Tag::W)]; }
78 size_t num_notes() const { return num_[size_t(Tag::N)]; }
79 bool truncated() const { return truncated_; } ///< Did Diag::max_errors drop anything?
80 ///@}
81
82 /// @name Add a Message
83 /// Each of these yields the Error again, so a diagnostic, its Note%s, and a closing Error::bail chain.
84 ///@{
85 /// Records the message @p fmt renders; see Diag::render.
86 /// @p tag must be Tag::Error or Tag::Warn - a Tag::Note belongs to Error::note.
87 Error& msg(Loc loc, Tag tag, const std::function<std::string()>& fmt);
88
89 // clang-format off
90 /// The backticks of @p s delimit a `` `citation` ``; those of an argument are data and get escaped - see Cite.
91 /// @note Formats via `std::vformat` because Diag::render may render @p s more than once.
92 template<class... Args> Error& msg(Loc loc, Tag tag, cite_string<Args...> s, Args&&... args) {
93 return msg(loc, tag, [&] { return term::detail::vformat_cite(s.get(), args...); });
94 }
95
96 template<class... Args> Error& e(Loc loc, cite_string<Args...> s, Args&&... args) { return msg(loc, Tag::E, s, std::forward<Args>(args)...); }
97 template<class... Args> Error& w(Loc loc, cite_string<Args...> s, Args&&... args) { return msg(loc, Tag::W, s, std::forward<Args>(args)...); }
98
99 /// A `= note:` continuation of the diagnostic being built; it has no Loc of its own to point at.
100 template<class... Args> Error& n(cite_string<Args...> s, Args&&... args) { return n(Loc(), s, std::forward<Args>(args)...); }
101
102 /// A Note that points *elsewhere*; dropped when @p loc adds nothing.
103 /// A @p loc overlapping the primary one is already covered by its snippet and so points nowhere new.
104 /// An invalid @p loc points *nowhere* and degrades to the `= note:` continuation above.
105 /// The renderer gives @p loc a header line of its own, so phrase the message to stand alone.
106 template<class... Args> Error& n(Loc loc, cite_string<Args...> s, Args&&... args) {
107 if (loc && (loc & primary_loc_())) return *this;
108 note_(loc, [&] { return term::detail::vformat_cite(s.get(), args...); });
109 return *this;
110 }
111 // clang-format on
112 ///@}
113
114 /// @name Handle Errors/Warnings
115 ///@{
116 void clear();
117
118 /// Renders everything collected so far the way it would appear on @p os; @p os only decides the coloring.
119 std::string str(std::ostream& os = std::cerr) const;
120
121 /// Streams everything collected so far to @p os and claims it.
122 /// @returns the number of Tag::Error%s that were reported.
123 size_t report(std::ostream& os = std::cerr);
124
125 /// Claims everything collected so far and throws it as a Bail rendered for @p os.
126 [[noreturn]] void bail(std::ostream& os = std::cerr);
127
128 /// If errors occurred, Error::bail; otherwise Error::report any warnings to @p os.
129 void ack(std::ostream& os = std::cerr);
130 ///@}
131
132 /// Hands every Msg, its Note%s, and the closing summary to Diag.
133 friend std::ostream& operator<<(std::ostream& os, const Error& e);
134
135private:
136 const Diag& diag() const; ///< Driver is incomplete here - it owns an Error of its own.
137
138 /// Loc of the Msg that subsequent Note%s belong to.
139 Loc primary_loc_() const { return msgs_.empty() ? Loc() : msgs_.back().loc; }
140
141 void note_(Loc loc, const std::function<std::string()>& fmt);
142
143 const Driver* driver_;
144 std::vector<Msg> msgs_;
145 std::array<size_t, 3> num_ = {};
146 bool truncated_ = false;
147 bool dropped_ = false; ///< Was the Msg that Note%s would attach to dropped?
148};
149
150} // namespace fe
151
152#ifndef DOXYGEN // clang-format off
153template<> struct std::formatter<fe::Error > : fe::ostream_formatter {};
154template<> struct std::formatter<fe::Error::Bail> : fe::ostream_formatter {};
155template<> struct std::formatter<fe::Diag::Tag > : fe::ostream_formatter {};
156#endif // clang-format on
How a diagnostic lays out - and how much of it an Error keeps.
Definition diag.h:20
Bail(std::string what, size_t num_errors, size_t num_warnings)
Definition error.h:32
size_t num_warnings() const
Definition error.h:39
size_t num_errors() const
Definition error.h:38
friend std::ostream & operator<<(std::ostream &os, const Bail &bail)
Definition error.h:41
const char * what() const noexcept override
Definition error.h:37
Error(const Driver &driver)
A sink of your own; Driver::error is the one every frontend building block already reports to.
Definition error.h:68
size_t num_notes() const
Definition error.h:78
Error & n(cite_string< Args... > s, Args &&... args)
A = note: continuation of the diagnostic being built; it has no Loc of its own to point at.
Definition error.h:100
std::string str(std::ostream &os=std::cerr) const
Renders everything collected so far the way it would appear on os; os only decides the coloring.
Error & msg(Loc loc, Tag tag, const std::function< std::string()> &fmt)
void ack(std::ostream &os=std::cerr)
If errors occurred, Error::bail; otherwise Error::report any warnings to os.
Error & n(Loc loc, cite_string< Args... > s, Args &&... args)
A Note that points elsewhere; dropped when loc adds nothing.
Definition error.h:106
Error & w(Loc loc, cite_string< Args... > s, Args &&... args)
Definition error.h:97
const auto & msgs() const
Definition error.h:73
void clear()
void bail(std::ostream &os=std::cerr)
Claims everything collected so far and throws it as a Bail rendered for os.
bool truncated() const
Did Diag::max_errors drop anything?
Definition error.h:79
friend std::ostream & operator<<(std::ostream &os, const Error &e)
Hands every Msg, its Notes, and the closing summary to Diag.
size_t report(std::ostream &os=std::cerr)
Streams everything collected so far to os and claims it.
size_t num_warnings() const
Definition error.h:77
Diag::Tag Tag
Definition error.h:25
std::string str
Definition error.h:61
size_t num_errors() const
Definition error.h:76
bool empty() const
Definition error.h:74
Error & msg(Loc loc, Tag tag, cite_string< Args... > s, Args &&... args)
The backticks of s delimit a `citation` ; those of an argument are data and get escaped - see Cite.
Definition error.h:92
Error & e(Loc loc, cite_string< Args... > s, Args &&... args)
Definition error.h:96
std::string str
Definition error.h:54
std::vector< Note > notes
Definition error.h:62
bool ok() const
Nothing recorded that must stop the compilation?
Definition error.h:75
One Tag::Error or Tag::Warn together with the Notes that belong to it.
Definition error.h:58
A secondary message elaborating a Msg.
Definition error.h:52
std::format_string< detail::cite_arg_t< Args >... > cite_string
A std::format_string whose backticks delimit a `citation` while those of its arguments are data.
Definition term.h:242
Definition algo.h:17
basic_ostream_formatter< char > ostream_formatter
Definition format.h:61
Definition span.h:150
Use/derive from this class for "global" variables that you need all over the place.
Definition driver.h:22
Location within a Src: the half-open byte range [Loc::begin, Loc::end).
Definition loc.h:42
constexpr Loc() noexcept=default
Creates an invalid Location.