summaryrefslogtreecommitdiffstats
path: root/third_party/rust/codespan-reporting/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/codespan-reporting/examples
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/codespan-reporting/examples')
-rw-r--r--third_party/rust/codespan-reporting/examples/custom_files.rs197
-rw-r--r--third_party/rust/codespan-reporting/examples/peg_calculator.rs68
-rw-r--r--third_party/rust/codespan-reporting/examples/readme_preview.rs356
-rw-r--r--third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs105
-rw-r--r--third_party/rust/codespan-reporting/examples/term.rs175
5 files changed, 901 insertions, 0 deletions
diff --git a/third_party/rust/codespan-reporting/examples/custom_files.rs b/third_party/rust/codespan-reporting/examples/custom_files.rs
new file mode 100644
index 0000000000..dbbacda591
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/custom_files.rs
@@ -0,0 +1,197 @@
+//! An example that shows how to implement a simple custom file database.
+//! The database uses 32-bit file-ids, which could be useful for optimizing
+//! memory usage.
+//!
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example custom_files
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::term;
+use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
+use std::ops::Range;
+
+fn main() -> anyhow::Result<()> {
+ let mut files = files::Files::new();
+
+ let file_id0 = files.add("0.greeting", "hello world!").unwrap();
+ let file_id1 = files.add("1.greeting", "bye world").unwrap();
+
+ let messages = vec![
+ Message::UnwantedGreetings {
+ greetings: vec![(file_id0, 0..5), (file_id1, 0..3)],
+ },
+ Message::OverTheTopExclamations {
+ exclamations: vec![(file_id0, 11..12)],
+ },
+ ];
+
+ let writer = StandardStream::stderr(ColorChoice::Always);
+ let config = term::Config::default();
+ for message in &messages {
+ let writer = &mut writer.lock();
+ term::emit(writer, &config, &files, &message.to_diagnostic())?;
+ }
+
+ Ok(())
+}
+
+/// A module containing the file implementation
+mod files {
+ use codespan_reporting::files;
+ use std::ops::Range;
+
+ /// A file that is backed by an `Arc<String>`.
+ #[derive(Debug, Clone)]
+ struct File {
+ /// The name of the file.
+ name: String,
+ /// The source code of the file.
+ source: String,
+ /// The starting byte indices in the source code.
+ line_starts: Vec<usize>,
+ }
+
+ impl File {
+ fn line_start(&self, line_index: usize) -> Result<usize, files::Error> {
+ use std::cmp::Ordering;
+
+ match line_index.cmp(&self.line_starts.len()) {
+ Ordering::Less => Ok(self
+ .line_starts
+ .get(line_index)
+ .expect("failed despite previous check")
+ .clone()),
+ Ordering::Equal => Ok(self.source.len()),
+ Ordering::Greater => Err(files::Error::LineTooLarge {
+ given: line_index,
+ max: self.line_starts.len() - 1,
+ }),
+ }
+ }
+ }
+
+ /// An opaque file identifier.
+ #[derive(Copy, Clone, PartialEq, Eq)]
+ pub struct FileId(u32);
+
+ #[derive(Debug, Clone)]
+ pub struct Files {
+ files: Vec<File>,
+ }
+
+ impl Files {
+ /// Create a new files database.
+ pub fn new() -> Files {
+ Files { files: Vec::new() }
+ }
+
+ /// Add a file to the database, returning the handle that can be used to
+ /// refer to it again.
+ pub fn add(
+ &mut self,
+ name: impl Into<String>,
+ source: impl Into<String>,
+ ) -> Option<FileId> {
+ use std::convert::TryFrom;
+
+ let file_id = FileId(u32::try_from(self.files.len()).ok()?);
+ let name = name.into();
+ let source = source.into();
+ let line_starts = files::line_starts(&source).collect();
+
+ self.files.push(File {
+ name,
+ line_starts,
+ source,
+ });
+
+ Some(file_id)
+ }
+
+ /// Get the file corresponding to the given id.
+ fn get(&self, file_id: FileId) -> Result<&File, files::Error> {
+ self.files
+ .get(file_id.0 as usize)
+ .ok_or(files::Error::FileMissing)
+ }
+ }
+
+ impl<'files> files::Files<'files> for Files {
+ type FileId = FileId;
+ type Name = &'files str;
+ type Source = &'files str;
+
+ fn name(&self, file_id: FileId) -> Result<&str, files::Error> {
+ Ok(self.get(file_id)?.name.as_ref())
+ }
+
+ fn source(&self, file_id: FileId) -> Result<&str, files::Error> {
+ Ok(&self.get(file_id)?.source)
+ }
+
+ fn line_index(&self, file_id: FileId, byte_index: usize) -> Result<usize, files::Error> {
+ self.get(file_id)?
+ .line_starts
+ .binary_search(&byte_index)
+ .or_else(|next_line| Ok(next_line - 1))
+ }
+
+ fn line_range(
+ &self,
+ file_id: FileId,
+ line_index: usize,
+ ) -> Result<Range<usize>, files::Error> {
+ let file = self.get(file_id)?;
+ let line_start = file.line_start(line_index)?;
+ let next_line_start = file.line_start(line_index + 1)?;
+
+ Ok(line_start..next_line_start)
+ }
+ }
+}
+
+/// A Diagnostic message.
+enum Message {
+ UnwantedGreetings {
+ greetings: Vec<(files::FileId, Range<usize>)>,
+ },
+ OverTheTopExclamations {
+ exclamations: Vec<(files::FileId, Range<usize>)>,
+ },
+}
+
+impl Message {
+ fn to_diagnostic(&self) -> Diagnostic<files::FileId> {
+ match self {
+ Message::UnwantedGreetings { greetings } => Diagnostic::error()
+ .with_message("greetings are not allowed")
+ .with_labels(
+ greetings
+ .iter()
+ .map(|(file_id, range)| {
+ Label::primary(*file_id, range.clone()).with_message("a greeting")
+ })
+ .collect(),
+ )
+ .with_notes(vec![
+ "found greetings!".to_owned(),
+ "pleas no greetings :(".to_owned(),
+ ]),
+ Message::OverTheTopExclamations { exclamations } => Diagnostic::error()
+ .with_message("over-the-top exclamations")
+ .with_labels(
+ exclamations
+ .iter()
+ .map(|(file_id, range)| {
+ Label::primary(*file_id, range.clone()).with_message("an exclamation")
+ })
+ .collect(),
+ )
+ .with_notes(vec!["ridiculous!".to_owned()]),
+ }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/peg_calculator.rs b/third_party/rust/codespan-reporting/examples/peg_calculator.rs
new file mode 100644
index 0000000000..882ce6da8c
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/peg_calculator.rs
@@ -0,0 +1,68 @@
+//! An example of using `peg` with `codespan_reporting`.
+//!
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example peg_calculator
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term;
+use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
+use rustyline::error::ReadlineError;
+use rustyline::Editor;
+
+peg::parser! {
+ grammar arithmetic() for str {
+ rule number() -> i64
+ = n:$(['0'..='9']+) { n.parse().unwrap() }
+
+ pub rule calculate() -> i64 = precedence!{
+ x:(@) "+" y:@ { x + y }
+ x:(@) "-" y:@ { x - y }
+ "-" v:@ { - v }
+ --
+ x:(@) "*" y:@ { x * y }
+ x:(@) "/" y:@ { x / y }
+ --
+ x:@ "^" y:(@) { i64::pow(x, y as u32) }
+ v:@ "!" { (1..v+1).product() }
+ --
+ "(" v:calculate() ")" { v }
+ n:number() { n }
+ }
+ }
+}
+
+fn main() -> anyhow::Result<()> {
+ let writer = StandardStream::stderr(ColorChoice::Always);
+ let config = codespan_reporting::term::Config::default();
+ let mut editor = Editor::<()>::new();
+
+ loop {
+ let line = match editor.readline("> ") {
+ Ok(line) => line,
+ Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => return Ok(()),
+ Err(error) => return Err(error.into()),
+ };
+
+ match arithmetic::calculate(&line) {
+ Ok(number) => println!("{}", number),
+ Err(error) => {
+ let file = SimpleFile::new("<repl>", line);
+
+ let start = error.location.offset;
+ let diagnostic = Diagnostic::error()
+ .with_message("parse error")
+ .with_labels(vec![
+ Label::primary((), start..start).with_message("parse error")
+ ])
+ .with_notes(vec![format!("expected: {}", error.expected)]);
+
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+ }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/readme_preview.rs b/third_party/rust/codespan-reporting/examples/readme_preview.rs
new file mode 100644
index 0000000000..2cf96f60cb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/readme_preview.rs
@@ -0,0 +1,356 @@
+//! Renders the preview SVG for the README.
+//!
+//! To update the preview, execute the following command from the top level of
+//! the repository:
+//!
+//! ```sh
+//! cargo run --example readme_preview svg > codespan-reporting/assets/readme_preview.svg
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term::termcolor::{Color, ColorSpec, StandardStream, WriteColor};
+use codespan_reporting::term::{self, ColorArg};
+use std::io::{self, Write};
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub enum Opts {
+ /// Render SVG output
+ Svg,
+ /// Render Stderr output
+ Stderr {
+ /// Configure coloring of output
+ #[structopt(
+ long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ color: ColorArg,
+ },
+}
+
+fn main() -> anyhow::Result<()> {
+ let file = SimpleFile::new(
+ "FizzBuzz.fun",
+ unindent::unindent(
+ r#"
+ module FizzBuzz where
+
+ fizz₁ : Nat → String
+ fizz₁ num = case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+
+ fizz₂ : Nat → String
+ fizz₂ num =
+ case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+ "#,
+ ),
+ );
+
+ let diagnostics = [Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary((), 328..331).with_message("expected `String`, found `Nat`"),
+ Label::secondary((), 211..331).with_message("`case` clauses have incompatible types"),
+ Label::secondary((), 258..268).with_message("this is found to be of type `String`"),
+ Label::secondary((), 284..290).with_message("this is found to be of type `String`"),
+ Label::secondary((), 306..312).with_message("this is found to be of type `String`"),
+ Label::secondary((), 186..192).with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )])];
+
+ // let mut files = SimpleFiles::new();
+ match Opts::from_args() {
+ Opts::Svg => {
+ let mut buffer = Vec::new();
+ let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer));
+ let config = codespan_reporting::term::Config {
+ styles: codespan_reporting::term::Styles::with_blue(Color::Blue),
+ ..codespan_reporting::term::Config::default()
+ };
+
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer, &config, &file, &diagnostic)?;
+ }
+
+ let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1;
+
+ let padding = 10;
+ let font_size = 12;
+ let line_spacing = 3;
+ let width = 882;
+ let height = padding + num_lines * (font_size + line_spacing) + padding;
+
+ let stdout = std::io::stdout();
+ let writer = &mut stdout.lock();
+
+ write!(
+ writer,
+ r#"<svg viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">
+ <style>
+ /* https://github.com/aaron-williamson/base16-alacritty/blob/master/colors/base16-tomorrow-night-256.yml */
+ pre {{
+ background: #1d1f21;
+ margin: 0;
+ padding: {padding}px;
+ border-radius: 6px;
+ color: #ffffff;
+ font: {font_size}px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+ }}
+
+ pre .bold {{ font-weight: bold; }}
+
+ pre .fg.black {{ color: #1d1f21; }}
+ pre .fg.red {{ color: #cc6666; }}
+ pre .fg.green {{ color: #b5bd68; }}
+ pre .fg.yellow {{ color: #f0c674; }}
+ pre .fg.blue {{ color: #81a2be; }}
+ pre .fg.magenta {{ color: #b294bb; }}
+ pre .fg.cyan {{ color: #8abeb7; }}
+ pre .fg.white {{ color: #c5c8c6; }}
+
+ pre .fg.black.bright {{ color: #969896; }}
+ pre .fg.red.bright {{ color: #cc6666; }}
+ pre .fg.green.bright {{ color: #b5bd68; }}
+ pre .fg.yellow.bright {{ color: #f0c674; }}
+ pre .fg.blue.bright {{ color: #81a2be; }}
+ pre .fg.magenta.bright {{ color: #b294bb; }}
+ pre .fg.cyan.bright {{ color: #8abeb7; }}
+ pre .fg.white.bright {{ color: #ffffff; }}
+
+ pre .bg.black {{ background-color: #1d1f21; }}
+ pre .bg.red {{ background-color: #cc6666; }}
+ pre .bg.green {{ background-color: #b5bd68; }}
+ pre .bg.yellow {{ background-color: #f0c674; }}
+ pre .bg.blue {{ background-color: #81a2be; }}
+ pre .bg.magenta {{ background-color: #b294bb; }}
+ pre .bg.cyan {{ background-color: #8abeb7; }}
+ pre .bg.white {{ background-color: #c5c8c6; }}
+
+ pre .bg.black.bright {{ background-color: #969896; }}
+ pre .bg.red.bright {{ background-color: #cc6666; }}
+ pre .bg.green.bright {{ background-color: #b5bd68; }}
+ pre .bg.yellow.bright {{ background-color: #f0c674; }}
+ pre .bg.blue.bright {{ background-color: #81a2be; }}
+ pre .bg.magenta.bright {{ background-color: #b294bb; }}
+ pre .bg.cyan.bright {{ background-color: #8abeb7; }}
+ pre .bg.white.bright {{ background-color: #ffffff; }}
+ </style>
+
+ <foreignObject x="0" y="0" width="{width}" height="{height}">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <pre>"#,
+ padding = padding,
+ font_size = font_size,
+ width = width,
+ height = height,
+ )?;
+
+ writer.write_all(&buffer)?;
+
+ write!(
+ writer,
+ "</pre>
+ </div>
+ </foreignObject>
+</svg>
+"
+ )?;
+ }
+ Opts::Stderr { color } => {
+ let writer = StandardStream::stderr(color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Rudimentary HTML escaper which performs the following conversions:
+///
+/// - `<` ⇒ `&lt;`
+/// - `>` ⇒ `&gt;`
+/// - `&` ⇒ `&amp;`
+pub struct HtmlEscapeWriter<W> {
+ upstream: W,
+}
+
+impl<W> HtmlEscapeWriter<W> {
+ pub fn new(upstream: W) -> HtmlEscapeWriter<W> {
+ HtmlEscapeWriter { upstream }
+ }
+}
+
+impl<W: Write> Write for HtmlEscapeWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut last_term = 0usize;
+ for (i, byte) in buf.iter().enumerate() {
+ let escape = match byte {
+ b'<' => &b"&lt;"[..],
+ b'>' => &b"&gt;"[..],
+ b'&' => &b"&amp;"[..],
+ _ => continue,
+ };
+ self.upstream.write_all(&buf[last_term..i])?;
+ last_term = i + 1;
+ self.upstream.write_all(escape)?;
+ }
+ self.upstream.write_all(&buf[last_term..])?;
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.upstream.flush()
+ }
+}
+
+impl<W: WriteColor> WriteColor for HtmlEscapeWriter<W> {
+ fn supports_color(&self) -> bool {
+ self.upstream.supports_color()
+ }
+
+ fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
+ self.upstream.set_color(spec)
+ }
+
+ fn reset(&mut self) -> io::Result<()> {
+ self.upstream.reset()
+ }
+}
+
+pub struct SvgWriter<W> {
+ upstream: W,
+ color: ColorSpec,
+}
+
+impl<W> SvgWriter<W> {
+ pub fn new(upstream: W) -> SvgWriter<W> {
+ SvgWriter {
+ upstream,
+ color: ColorSpec::new(),
+ }
+ }
+}
+
+impl<W: Write> Write for SvgWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.upstream.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.upstream.flush()
+ }
+}
+
+impl<W: Write> WriteColor for SvgWriter<W> {
+ fn supports_color(&self) -> bool {
+ true
+ }
+
+ fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
+ #![allow(unused_assignments)]
+
+ if self.color == *spec {
+ return Ok(());
+ } else {
+ if !self.color.is_none() {
+ write!(self, "</span>")?;
+ }
+ self.color = spec.clone();
+ }
+
+ if spec.is_none() {
+ write!(self, "</span>")?;
+ return Ok(());
+ } else {
+ write!(self, "<span class=\"")?;
+ }
+
+ let mut first = true;
+
+ fn write_first<W: Write>(first: bool, writer: &mut SvgWriter<W>) -> io::Result<bool> {
+ if !first {
+ write!(writer, " ")?;
+ }
+
+ Ok(false)
+ };
+
+ fn write_color<W: Write>(color: &Color, writer: &mut SvgWriter<W>) -> io::Result<()> {
+ match color {
+ Color::Black => write!(writer, "black"),
+ Color::Blue => write!(writer, "blue"),
+ Color::Green => write!(writer, "green"),
+ Color::Red => write!(writer, "red"),
+ Color::Cyan => write!(writer, "cyan"),
+ Color::Magenta => write!(writer, "magenta"),
+ Color::Yellow => write!(writer, "yellow"),
+ Color::White => write!(writer, "white"),
+ // TODO: other colors
+ _ => Ok(()),
+ }
+ };
+
+ if let Some(fg) = spec.fg() {
+ first = write_first(first, self)?;
+ write!(self, "fg ")?;
+ write_color(fg, self)?;
+ }
+
+ if let Some(bg) = spec.bg() {
+ first = write_first(first, self)?;
+ write!(self, "bg ")?;
+ write_color(bg, self)?;
+ }
+
+ if spec.bold() {
+ first = write_first(first, self)?;
+ write!(self, "bold")?;
+ }
+
+ if spec.underline() {
+ first = write_first(first, self)?;
+ write!(self, "underline")?;
+ }
+
+ if spec.intense() {
+ first = write_first(first, self)?;
+ write!(self, "bright")?;
+ }
+
+ write!(self, "\">")?;
+
+ Ok(())
+ }
+
+ fn reset(&mut self) -> io::Result<()> {
+ let color = self.color.clone();
+
+ if color != ColorSpec::new() {
+ write!(self, "</span>")?;
+ self.color = ColorSpec::new();
+ }
+
+ Ok(())
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs
new file mode 100644
index 0000000000..d05dee8549
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs
@@ -0,0 +1,105 @@
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term::termcolor::StandardStream;
+use codespan_reporting::term::{self, ColorArg};
+use std::ops::Range;
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub struct Opts {
+ #[structopt(long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ color: ColorArg,
+}
+
+fn main() -> anyhow::Result<()> {
+ let file = SimpleFile::new(
+ "main.rs",
+ unindent::unindent(
+ r#"
+ fn main() {
+ let foo: i32 = "hello, world";
+ foo += 1;
+ }
+ "#,
+ ),
+ );
+
+ let errors = [
+ Error::MismatchType(
+ Item::new(20..23, "i32"),
+ Item::new(31..45, "\"hello, world\""),
+ ),
+ Error::MutatingImmutable(Item::new(20..23, "foo"), Item::new(51..59, "foo += 1")),
+ ];
+
+ let opts = Opts::from_args();
+ let writer = StandardStream::stderr(opts.color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in errors.iter().map(Error::report) {
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+
+ Ok(())
+}
+
+/// An error enum that represent all possible errors within your program
+enum Error {
+ MismatchType(Item, Item),
+ MutatingImmutable(Item, Item),
+}
+
+impl Error {
+ fn report(&self) -> Diagnostic<()> {
+ match self {
+ Error::MismatchType(left, right) => Diagnostic::error()
+ .with_code("E0308")
+ .with_message("mismatch types")
+ .with_labels(vec![
+ Label::primary((), right.range.clone()).with_message(format!(
+ "Expected `{}`, found: `{}`",
+ left.content, right.content,
+ )),
+ Label::secondary((), left.range.clone()).with_message("expected due to this"),
+ ]),
+ Error::MutatingImmutable(original, mutating) => Diagnostic::error()
+ .with_code("E0384")
+ .with_message(format!(
+ "cannot mutate immutable variable `{}`",
+ original.content,
+ ))
+ .with_labels(vec![
+ Label::secondary((), original.range.clone()).with_message(unindent::unindent(
+ &format!(
+ r#"
+ first assignment to `{0}`
+ help: make this binding mutable: `mut {0}`
+ "#,
+ original.content,
+ ),
+ )),
+ Label::primary((), mutating.range.clone())
+ .with_message("cannot assign twice to immutable variable"),
+ ]),
+ }
+ }
+}
+
+/// An item in the source code to be used in the `Error` enum.
+/// In a more complex program it could also contain a `files::FileId` to handle errors that occur inside multiple files.
+struct Item {
+ range: Range<usize>,
+ content: String,
+}
+
+impl Item {
+ fn new(range: Range<usize>, content: impl Into<String>) -> Item {
+ let content = content.into();
+ Item { range, content }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/term.rs b/third_party/rust/codespan-reporting/examples/term.rs
new file mode 100644
index 0000000000..19bf8503c0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/term.rs
@@ -0,0 +1,175 @@
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example term
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFiles;
+use codespan_reporting::term::termcolor::StandardStream;
+use codespan_reporting::term::{self, ColorArg};
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub struct Opts {
+ /// Configure coloring of output
+ #[structopt(
+ long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ pub color: ColorArg,
+}
+
+fn main() -> anyhow::Result<()> {
+ let opts = Opts::from_args();
+ let mut files = SimpleFiles::new();
+
+ let file_id1 = files.add(
+ "Data/Nat.fun",
+ unindent::unindent(
+ "
+ module Data.Nat where
+
+ data Nat : Type where
+ zero : Nat
+ succ : Nat → Nat
+
+ {-# BUILTIN NATRAL Nat #-}
+
+ infixl 6 _+_ _-_
+
+ _+_ : Nat → Nat → Nat
+ zero + n₂ = n₂
+ succ n₁ + n₂ = succ (n₁ + n₂)
+
+ _-_ : Nat → Nat → Nat
+ n₁ - zero = n₁
+ zero - succ n₂ = zero
+ succ n₁ - succ n₂ = n₁ - n₂
+ ",
+ ),
+ );
+
+ let file_id2 = files.add(
+ "Test.fun",
+ unindent::unindent(
+ r#"
+ module Test where
+
+ _ : Nat
+ _ = 123 + "hello"
+ "#,
+ ),
+ );
+
+ let file_id3 = files.add(
+ "FizzBuzz.fun",
+ unindent::unindent(
+ r#"
+ module FizzBuzz where
+
+ fizz₁ : Nat → String
+ fizz₁ num = case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+
+ fizz₂ : Nat → String
+ fizz₂ num =
+ case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+ "#,
+ ),
+ );
+
+ let diagnostics = [
+ // Unknown builtin error
+ Diagnostic::error()
+ .with_message("unknown builtin: `NATRAL`")
+ .with_labels(vec![
+ Label::primary(file_id1, 96..102).with_message("unknown builtin")
+ ])
+ .with_notes(vec![
+ "there is a builtin with a similar name: `NATURAL`".to_owned()
+ ]),
+ // Unused parameter warning
+ Diagnostic::warning()
+ .with_message("unused parameter pattern: `n₂`")
+ .with_labels(vec![
+ Label::primary(file_id1, 285..289).with_message("unused parameter")
+ ])
+ .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
+ // Unexpected type error
+ Diagnostic::error()
+ .with_message("unexpected type in application of `_+_`")
+ .with_code("E0001")
+ .with_labels(vec![
+ Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
+ Label::secondary(file_id1, 130..155)
+ .with_message("based on the definition of `_+_`"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `Nat`
+ found type `String`
+ ",
+ )]),
+ // Incompatible match clause error
+ Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary(file_id3, 163..166).with_message("expected `String`, found `Nat`"),
+ Label::secondary(file_id3, 62..166)
+ .with_message("`case` clauses have incompatible types"),
+ Label::secondary(file_id3, 41..47)
+ .with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )]),
+ // Incompatible match clause error
+ Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary(file_id3, 328..331).with_message("expected `String`, found `Nat`"),
+ Label::secondary(file_id3, 211..331)
+ .with_message("`case` clauses have incompatible types"),
+ Label::secondary(file_id3, 258..268)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 284..290)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 306..312)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 186..192)
+ .with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )]),
+ ];
+
+ let writer = StandardStream::stderr(opts.color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
+ }
+
+ Ok(())
+}