Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / diagnostic.rs
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}