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