1//! To run this example, execute the following command from the top level of
2//! this repository:
3//!
4//! ```sh
5//! cargo run --example term
6//! ```
7
8use codespan_reporting::diagnostic::{Diagnostic, Label};
9use codespan_reporting::files::SimpleFiles;
10use codespan_reporting::term::termcolor::StandardStream;
11use codespan_reporting::term::{self, ColorArg};
12use structopt::StructOpt;
13
14#[derive(Debug, StructOpt)]
15#[structopt(name = "emit")]
16pub struct Opts {
17    /// Configure coloring of output
18    #[structopt(
19        long = "color",
20        parse(try_from_str),
21        default_value = "auto",
22        possible_values = ColorArg::VARIANTS,
23        case_insensitive = true
24    )]
25    pub color: ColorArg,
26}
27
28fn main() -> anyhow::Result<()> {
29    let opts = Opts::from_args();
30    let mut files = SimpleFiles::new();
31
32    let file_id1 = files.add(
33        "Data/Nat.fun",
34        unindent::unindent(
35            "
36                module Data.Nat where
37
38                data Nat : Type where
39                    zero : Nat
40                    succ : NatNat
41
42                {-# BUILTIN NATRAL Nat #-}
43
44                infixl 6 _+_ _-_
45
46                _+_ : NatNatNat
47                zero    + n₂ = n₂
48                succ n₁ + n₂ = succ (n₁ + n₂)
49
50                _-_ : NatNatNat
51                n₁      - zero    = n₁
52                zero    - succ n₂ = zero
53                succ n₁ - succ n₂ = n₁ - n₂
54            ",
55        ),
56    );
57
58    let file_id2 = files.add(
59        "Test.fun",
60        unindent::unindent(
61            r#"
62                module Test where
63
64                _ : Nat
65                _ = 123 + "hello"
66            "#,
67        ),
68    );
69
70    let file_id3 = files.add(
71        "FizzBuzz.fun",
72        unindent::unindent(
73            r#"
74                module FizzBuzz where
75
76                fizz₁ : NatString
77                fizznum = case (mod num 5) (mod num 3) of
78                    0 0 => "FizzBuzz"
79                    0 _ => "Fizz"
80                    _ 0 => "Buzz"
81                    _ _ => num
82
83                fizz₂ : NatString
84                fizznum =
85                    case (mod num 5) (mod num 3) of
86                        0 0 => "FizzBuzz"
87                        0 _ => "Fizz"
88                        _ 0 => "Buzz"
89                        _ _ => num
90            "#,
91        ),
92    );
93
94    let diagnostics = [
95        // Unknown builtin error
96        Diagnostic::error()
97            .with_message("unknown builtin: `NATRAL`")
98            .with_labels(vec![
99                Label::primary(file_id1, 96..102).with_message("unknown builtin")
100            ])
101            .with_notes(vec![
102                "there is a builtin with a similar name: `NATURAL`".to_owned()
103            ]),
104        // Unused parameter warning
105        Diagnostic::warning()
106            .with_message("unused parameter pattern: `n₂`")
107            .with_labels(vec![
108                Label::primary(file_id1, 285..289).with_message("unused parameter")
109            ])
110            .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
111        // Unexpected type error
112        Diagnostic::error()
113            .with_message("unexpected type in application of `_+_`")
114            .with_code("E0001")
115            .with_labels(vec![
116                Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
117                Label::secondary(file_id1, 130..155)
118                    .with_message("based on the definition of `_+_`"),
119            ])
120            .with_notes(vec![unindent::unindent(
121                "
122                    expected type `Nat`
123                       found type `String`
124                ",
125            )]),
126        // Incompatible match clause error
127        Diagnostic::error()
128            .with_message("`case` clauses have incompatible types")
129            .with_code("E0308")
130            .with_labels(vec![
131                Label::primary(file_id3, 163..166).with_message("expected `String`, found `Nat`"),
132                Label::secondary(file_id3, 62..166)
133                    .with_message("`case` clauses have incompatible types"),
134                Label::secondary(file_id3, 41..47)
135                    .with_message("expected type `String` found here"),
136            ])
137            .with_notes(vec![unindent::unindent(
138                "
139                    expected type `String`
140                       found type `Nat`
141                ",
142            )]),
143        // Incompatible match clause error
144        Diagnostic::error()
145            .with_message("`case` clauses have incompatible types")
146            .with_code("E0308")
147            .with_labels(vec![
148                Label::primary(file_id3, 328..331).with_message("expected `String`, found `Nat`"),
149                Label::secondary(file_id3, 211..331)
150                    .with_message("`case` clauses have incompatible types"),
151                Label::secondary(file_id3, 258..268)
152                    .with_message("this is found to be of type `String`"),
153                Label::secondary(file_id3, 284..290)
154                    .with_message("this is found to be of type `String`"),
155                Label::secondary(file_id3, 306..312)
156                    .with_message("this is found to be of type `String`"),
157                Label::secondary(file_id3, 186..192)
158                    .with_message("expected type `String` found here"),
159            ])
160            .with_notes(vec![unindent::unindent(
161                "
162                    expected type `String`
163                       found type `Nat`
164                ",
165            )]),
166    ];
167
168    let writer = StandardStream::stderr(opts.color.into());
169    let config = codespan_reporting::term::Config::default();
170    for diagnostic in &diagnostics {
171        term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
172    }
173
174    Ok(())
175}
176