1use std::fmt::Display;
2
3use crate::{
4 DebugEvent, DebugEventPayload,
5 span::{AbsoluteSpan, Span},
6};
7use dada_util::debug;
8use salsa::{Accumulator, Update};
9use serde::Serialize;
10
11mod render;
12
13#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Update, Debug, Serialize)]
15pub struct Reported(pub AbsoluteSpan);
16
17impl Reported {
18 pub fn span(self, db: &dyn crate::Db) -> Span<'_> {
19 self.0.into_span(db)
20 }
21}
22
23pub type Errors<T> = Result<T, Reported>;
26
27#[salsa::accumulator]
29#[derive(PartialEq, Eq, Hash, Serialize, Clone, Debug)]
30#[must_use]
31pub struct Diagnostic {
32 pub level: Level,
34
35 pub span: AbsoluteSpan,
37
38 pub message: String,
40
41 pub labels: Vec<DiagnosticLabel>,
44
45 pub children: Vec<Diagnostic>,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
50pub enum Level {
51 Note,
52 Help,
53 Info,
54 Warning,
55 Error,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
60pub struct DiagnosticLabel {
61 pub level: Level,
63
64 pub span: AbsoluteSpan,
67
68 pub message: String,
70}
71
72#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash, Serialize)]
73pub struct RenderOptions {
74 pub no_color: bool,
75}
76
77impl Diagnostic {
78 pub fn error<'db>(db: &'db dyn crate::Db, span: Span<'db>, message: impl Display) -> Self {
79 Self::new(db, Level::Error, span, message)
80 }
81
82 pub fn help<'db>(db: &'db dyn crate::Db, span: Span<'db>, message: impl Display) -> Self {
83 Self::new(db, Level::Help, span, message)
84 }
85
86 pub fn info<'db>(db: &'db dyn crate::Db, span: Span<'db>, message: impl Display) -> Self {
87 Self::new(db, Level::Info, span, message)
88 }
89
90 pub fn new<'db>(
91 db: &'db dyn crate::Db,
92 level: Level,
93 span: Span<'db>,
94 message: impl Display,
95 ) -> Self {
96 let message = message.to_string();
97 Diagnostic {
98 span: span.absolute_span(db),
99 level,
100 children: vec![],
101 message,
102 labels: vec![],
103 }
104 }
105
106 pub fn report(self, db: &dyn crate::Db) -> Reported {
107 debug!("reporting diagnostic", self);
108 let span = self.span;
109
110 if let Some(debug_tx) = db.debug_tx() {
111 debug_tx
112 .send(DebugEvent {
113 url: span.source_file.url(db).clone(),
114 start: span.start,
115 end: span.end,
116 payload: DebugEventPayload::Diagnostic(self.clone()),
117 })
118 .unwrap();
119 }
120
121 self.accumulate(db);
122
123 Reported(span)
124 }
125
126 pub fn label(
127 mut self,
128 db: &dyn crate::Db,
129 level: Level,
130 span: Span,
131 message: impl Display,
132 ) -> Self {
133 let span = span.absolute_span(db);
134 assert_eq!(self.span.source_file, span.source_file);
135 self.labels.push(DiagnosticLabel {
136 level,
137 span,
138 message: message.to_string(),
139 });
140 self
141 }
142
143 pub fn child(mut self, child: Diagnostic) -> Self {
144 self.children.push(child);
145 self
146 }
147
148 pub fn render(&self, db: &dyn crate::Db, opts: &RenderOptions) -> String {
149 render::render(db, opts, self)
150 }
151}
152
153pub fn report_all(db: &dyn crate::Db, diagnostics: Vec<Diagnostic>) {
154 for diagnostic in diagnostics {
155 diagnostic.report(db);
156 }
157}
158
159pub fn ordinal(n: usize) -> impl std::fmt::Display {
160 match n % 10 {
161 1 => format!("{n}st"),
162 2 => format!("{n}nd"),
163 3 => format!("{n}rd"),
164 _ => format!("{n}th"),
165 }
166}
167
168pub trait Err<'db> {
170 fn err(db: &'db dyn crate::Db, reported: Reported) -> Self;
171}
172
173impl<'db, T> Err<'db> for Errors<T> {
174 fn err(_db: &'db dyn crate::Db, reported: Reported) -> Self {
175 Err(reported)
176 }
177}
178
179impl<'db> Err<'db> for Reported {
180 fn err(_db: &'db dyn crate::Db, reported: Reported) -> Self {
181 reported
182 }
183}