Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.1 kB
156 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2020 The Gleam contributors
3
4use std::collections::HashMap;
5
6use camino::Utf8PathBuf;
7
8pub use codespan_reporting::diagnostic::{LabelStyle, Severity};
9use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFiles};
10use ecow::EcoString;
11use termcolor::Buffer;
12
13use crate::{ast::SrcSpan, error::wrap};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Level {
17 Error,
18 Warning,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct Label {
23 pub text: Option<String>,
24 pub span: SrcSpan,
25}
26
27impl Label {
28 fn to_codespan_label(&self, fileid: usize, style: LabelStyle) -> CodespanLabel<usize> {
29 let label = CodespanLabel::new(
30 style,
31 fileid,
32 (self.span.start as usize)..(self.span.end as usize),
33 );
34 match &self.text {
35 None => label,
36 Some(text) => label.with_message(text.clone()),
37 }
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct ExtraLabel {
43 pub src_info: Option<(EcoString, Utf8PathBuf)>,
44 pub label: Label,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct Location {
49 pub src: EcoString,
50 pub path: Utf8PathBuf,
51 pub label: Label,
52 pub extra_labels: Vec<ExtraLabel>,
53}
54
55// TODO: split this into locationed diagnostics and locationless diagnostics
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct Diagnostic {
58 pub title: String,
59 pub text: String,
60 pub level: Level,
61 pub location: Option<Location>,
62 pub hint: Option<String>,
63}
64
65impl Diagnostic {
66 pub fn write(&self, buffer: &mut Buffer) {
67 use std::io::Write;
68 match &self.location {
69 Some(location) => self.write_span(location, buffer),
70 None => self.write_title(buffer),
71 }
72
73 if !self.text.is_empty() {
74 writeln!(buffer, "{}", self.text).expect("write text");
75 }
76
77 if let Some(hint) = &self.hint {
78 // If there's some text before the hint we want to leave an empty
79 // line separating the two.
80 if !self.text.is_empty() {
81 writeln!(buffer).expect("write hint");
82 }
83 let message = wrap(&format!("Hint: {hint}"));
84 writeln!(buffer, "{message}").expect("write hint");
85 }
86 }
87
88 fn write_span(&self, location: &Location, buffer: &mut Buffer) {
89 let mut file_map = HashMap::new();
90 let mut files = SimpleFiles::new();
91
92 let main_location_path = location.path.as_str();
93 let main_location_src = location.src.as_str();
94 let main_file_id = files.add(main_location_path, main_location_src);
95 let _ = file_map.insert(main_location_path, main_file_id);
96
97 let mut labels = vec![
98 location
99 .label
100 .to_codespan_label(main_file_id, LabelStyle::Primary),
101 ];
102
103 location
104 .extra_labels
105 .iter()
106 .map(|label| {
107 let (location_src, location_path) = match &label.src_info {
108 Some(info) => (info.0.as_str(), info.1.as_str()),
109 _ => (main_location_src, main_location_path),
110 };
111 match file_map.get(location_path) {
112 None => {
113 let file_id = files.add(location_path, location_src);
114 let _ = file_map.insert(location_path, file_id);
115 label
116 .label
117 .to_codespan_label(file_id, LabelStyle::Secondary)
118 }
119 Some(i) => label.label.to_codespan_label(*i, LabelStyle::Secondary),
120 }
121 })
122 .for_each(|label| labels.push(label));
123
124 let severity = match self.level {
125 Level::Error => Severity::Error,
126 Level::Warning => Severity::Warning,
127 };
128
129 let diagnostic = codespan_reporting::diagnostic::Diagnostic::new(severity)
130 .with_message(&self.title)
131 .with_labels(labels);
132 let config = codespan_reporting::term::Config::default();
133 codespan_reporting::term::emit_to_write_style(buffer, &config, &files, &diagnostic)
134 .expect("write_diagnostic");
135 }
136
137 fn write_title(&self, buffer: &mut Buffer) {
138 use std::io::Write;
139 use termcolor::{Color, ColorSpec, WriteColor};
140 let (kind, colour) = match self.level {
141 Level::Error => ("error", Color::Red),
142 Level::Warning => ("warning", Color::Yellow),
143 };
144 buffer
145 .set_color(ColorSpec::new().set_bold(true).set_fg(Some(colour)))
146 .expect("write_title_color1");
147 write!(buffer, "{kind}").expect("write_title_kind");
148 buffer
149 .set_color(ColorSpec::new().set_bold(true))
150 .expect("write_title_color2");
151 write!(buffer, ": {}\n\n", self.title).expect("write_title_title");
152 buffer
153 .set_color(&ColorSpec::new())
154 .expect("write_title_reset");
155 }
156}