codespan-reporting-0.12.0/.cargo_vcs_info.json0000644000000002030000000000100147120ustar { "git": { "sha1": "3d3a03a8fcc30cc10b783b16c62029d5c232059b", "dirty": true }, "path_in_vcs": "codespan-reporting" }codespan-reporting-0.12.0/CHANGELOG.md000064400000000000000000000402111046102023000153160ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] The minimum supported rustc version is now `1.67.0` (was `1.40.0`). This is because some dependencies now require this Rust version. ### Added - Add the option to display a configurable amount of lines in front of and after any label. - The `Severity` enum now implements full `Ord`. (#335) ### Changed - Broken lines are now rendered properly with multiline spans. We used to render the wrong lines in the gutter when there were multiline spans and there were breaks in the file.
Example Before: ```text error[0001]: oh noes, a cupcake has occurred! ┌─ test:4:1 │ 4 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake 5 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly │ ╭─│─────────' · │ 10 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. 11 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow │ │ ╰─────────────────────────────' blah blah · │ │ 19 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry 20 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin │ ╰──────────' blah blah ``` After: ```text error[0001]: oh noes, a cupcake has occurred! ┌─ test:4:1 │ 4 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake 5 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly │ ╭─│─────────' · │ │ 10 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. 11 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow │ │ ╰─────────────────────────────' blah blah · │ 19 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry 20 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin │ ╰──────────' blah blah ```
## [0.11.1] - 2021-01-18 ### Added - Add `Chars::{box_drawing, ascii}` functions, the latter supporting a rustc-style of output that only uses ASCII characters (not above U+007F) for use cases that do not allow for box drawing characters, e.g. terminals that do not support them. ### Changed - `Diagnostic::with_labels` and `Diagnostic::with_notes` now append additional labels rather tan overwriting them, meaning that the documentation and behaviour match more closely. The behaviour will only differ if you call the same builder methods multiple times. If you call every builder method once only, nothing should change. - `config::Chars::snippet_start` is now a String instead of a single `char`. ## [0.11.0] - 2020-11-30 There is now a [code of conduct](https://github.com/brendanzab/codespan/blob/master/CODE_OF_CONDUCT.md) and a [contributing guide](https://github.com/brendanzab/codespan/blob/master/CONTRIBUTING.md). Some versions were skipped to sync up with the `codespan-lsp` crate. The release process has been changed so this should not happen again. ### Added - If a label spans over multiple lines, not all lines are rendered. The number of lines rendered at beginning and end is configurable separately. - There is now a custom error type. - There now is a medium rendering mode that is like the short rendering mode but also shows notes from the diagnostic. - `PartialEq` and `Eq` implementations for the `diagnostic::{Diagnostic, Label, Severity}` types. ### Changed - All errors now use the error type `codespan_reporting::file::Error`. This type also replaces the custom error type for `codespan-lsp`. ### Fixed - Empty error codes are not rendered. - The locus ("location of the diagnostic") is now computed so it is always at the first primary label, or at the first secondary label if no primary labels are available. - All `unwrap`s outside of tests and examples have been removed. - Some internal improvements, including various code style improvements by using Clippy. - Improved documentation, also mentioning how the ordering of labels is handled. ## [0.9.5] - 2020-06-24 ### Changed - Sections of source code that are marked with primary labels are now rendered using the primary highlight color. - Tab stops are now rendered properly. We used to just render `\t` characters in source snippets with the same number of spaces.
Example For example, when rendering with a tab width of `3` we would print: ```text warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ ``` Now we properly take into account the column of the tab character: ```text warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ ```
## [0.9.4] - 2020-05-18 ### Changed - We have made the caret rendering easier to read when there are multiple labels on the same line. We also avoid printing trailing borders on the final source source snippet if no notes are present.
Example Instead of this: ```text ┌─ one_line.rs:3:5 │ 3 │ v.push(v.pop().unwrap()); │ - first borrow later used by call │ ---- first mutable borrow occurs here │ ^ second mutable borrow occurs here │ ``` …we now render the following: ```text ┌─ one_line.rs:3:5 │ 3 │ v.push(v.pop().unwrap()); │ - ---- ^ second mutable borrow occurs here │ │ │ │ │ first mutable borrow occurs here │ first borrow later used by call ```
### Fixed - Diagnostic rendering no longer panics if label ranges are between UTF-8 character boundaries. ## [0.9.3] - 2020-04-29 ### Changed - Some panics were fixed when invalid unicode boundaries are supplied. - Labels that marked the same span were originally rendered in reverse order. This was a mistake! We've now fixed this.
Example For example, this diagnostic: ```text ┌─ same_range:1:7 │ 1 │ ::S { } │ - Expected '(' │ ^ Unexpected '{' │ ``` …will now be rendered as: ```text ┌─ same_range:1:7 │ 1 │ ::S { } │ ^ Unexpected '{' │ - Expected '(' │ ```
- We've reduced the prominence of the 'locus' on source snippets by simplifying the border and reducing the spacing around it. This is to help focus attention on the underlined source snippet and error messages, rather than the location, which should be a secondary focus.
Example For example we originally rendered this: ```text error: unknown builtin: `NATRAL` ┌── Data/Nat.fun:7:13 ─── │ 7 │ {-# BUILTIN NATRAL Nat #-} │ ^^^^^^ unknown builtin │ = there is a builtin with a similar name: `NATURAL` ``` …and now we render this: ```text error: unknown builtin: `NATRAL` ┌─ Data/Nat.fun:7:13 │ 7 │ {-# BUILTIN NATRAL Nat #-} │ ^^^^^^ unknown builtin │ = there is a builtin with a similar name: `NATURAL` ```
## [0.9.2] - 2020-03-29 ### Changed - Render overlapping multiline marks on the same lines of source code.
Example For example: ```text error[E0308]: match arms have incompatible types ┌── codespan/src/file.rs:1:9 ─── │ 1 │ ╭ match line_index.compare(self.last_line_index()) { 2 │ │ Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), 3 │ │ Ordering::Equal => Ok(self.source_span().end()), 4 │ │ Ordering::Greater => LineIndexOutOfBoundsError { 5 │ │ given: line_index, 6 │ │ max: self.last_line_index(), 7 │ │ }, 8 │ │ } │ ╰─────────' `match` arms have incompatible types · 2 │ Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), │ --------------------------------------------- this is found to be of type `Result` 3 │ Ordering::Equal => Ok(self.source_span().end()), │ ---------------------------- this is found to be of type `Result` 4 │ Ordering::Greater => LineIndexOutOfBoundsError { │ ╭──────────────────────────────────^ 5 │ │ given: line_index, 6 │ │ max: self.last_line_index(), 7 │ │ }, │ ╰─────────────^ expected enum `Result`, found struct `LineIndexOutOfBoundsError` │ = expected type `Result` found type `LineIndexOutOfBoundsError` ``` …is now rendered as: ```text error[E0308]: match arms have incompatible types ┌── codespan/src/file.rs:1:9 ─── │ 1 │ ╭ match line_index.compare(self.last_line_index()) { 2 │ │ Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), │ │ --------------------------------------------- this is found to be of type `Result` 3 │ │ Ordering::Equal => Ok(self.source_span().end()), │ │ ---------------------------- this is found to be of type `Result` 4 │ │ Ordering::Greater => LineIndexOutOfBoundsError { │ ╭─│──────────────────────────────────^ 5 │ │ │ given: line_index, 6 │ │ │ max: self.last_line_index(), 7 │ │ │ }, │ ╰─│─────────────^ expected enum `Result`, found struct `LineIndexOutOfBoundsError` 8 │ │ } │ ╰─────────' `match` arms have incompatible types │ = expected type `Result` found type `LineIndexOutOfBoundsError` ```
## [0.9.1] - 2020-03-23 ### Added - `codespan_reporting::diagnostic::Diagnostic` now implements `Debug`. ### Changed - Single-line labels are now rendered together, under the same source line.
Example For example: ```text ┌── one_line.rs:3:5 ─── │ 3 │ v.push(v.pop().unwrap()); │ - first borrow later used by call · 3 │ v.push(v.pop().unwrap()); │ ---- first mutable borrow occurs here · 3 │ v.push(v.pop().unwrap()); │ ^ second mutable borrow occurs here │ ``` …is now rendered as: ```text ┌── one_line.rs:3:5 ─── │ 3 │ v.push(v.pop().unwrap()); │ - first borrow later used by call │ ---- first mutable borrow occurs here │ ^ second mutable borrow occurs here │ ```
## [0.9.0] - 2020-03-11 ### Added - The `codespan_reporting::files` module was added as a way to decouple `codespan_reporting` from `codespan`. - `codespan_reporting::files::Files` allows users to implement custom file databases that work with `codespan_reporting`. This should make it easier to integrate with libraries like Salsa, and also makes it less invasive to use `codespan_reporting` on existing projects. - `codespan_reporting::files::SimpleFile` is a simple implementation of `codespan_reporting::files::Files` where only a single file is needed. - `codespan_reporting::files::SimpleFiles` is a simple implementation of `codespan_reporting::files::Files` where multiple files are needed. ### Changed - The `codespan_reporting::diagnostic` module has been greatly revamped, making the builder API format more nicely with rustfmt, and allowing for multiple primary labels. - The output of `codespan_reporting::term::emit` was improved, with the following changes: - labels on consecutive lines no longer render breaks between them - source lines are rendered when there is only one line between labels - the inner gutter of code snippets is now aligned consistently - the outer gutter of consecutive code snippets are now aligned consistently - `codespan_reporting::term::emit` now takes writers as a trait object (rather than using static dispatch) in order to reduce coda bloat and improve compile times. - The field names in `codespan_reporting::term::Chars` were tweaked for consistency. ### Removed - `codespan_reporting` no longer depends on `codespan`. Note that `codespan` can _still_ be used with `codespan_reporting`, as `codespan::Files` now implements `codespan_reporting::files::Files`. ## [0.8.0] - 2020-02-24 ## [0.7.0] - 2020-01-06 ## [0.6.0] - 2019-12-18 ## [0.5.0] - 2019-10-02 ## [0.4.1] - 2019-08-25 ## [0.4.0] - 2019-08-22 ## [0.3.0] - 2019-05-01 ## [0.2.1] - 2019-02-26 ## [0.2.0] - 2018-10-11 [Unreleased]: https://github.com/brendanzab/codespan/compare/v0.11.1...HEAD [0.11.1]: https://github.com/brendanzab/codespan/compare/v0.11.0..v0.11.1 [0.11.0]: https://github.com/brendanzab/codespan/compare/v0.9.5...v0.11.0 [0.9.5]: https://github.com/brendanzab/codespan/compare/v0.9.4...v0.9.5 [0.9.4]: https://github.com/brendanzab/codespan/compare/v0.9.3...v0.9.4 [0.9.3]: https://github.com/brendanzab/codespan/compare/v0.9.2...v0.9.3 [0.9.2]: https://github.com/brendanzab/codespan/compare/v0.9.1...v0.9.2 [0.9.1]: https://github.com/brendanzab/codespan/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/brendanzab/codespan/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/brendanzab/codespan/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/brendanzab/codespan/compare/v0.6.0...v0.7.0 [0.6.0]: https://github.com/brendanzab/codespan/compare/v0.5.0...v0.6.0 [0.5.0]: https://github.com/brendanzab/codespan/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/brendanzab/codespan/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/brendanzab/codespan/compare/v0.3.0...v0.4.0 [0.3.0]: https://github.com/brendanzab/codespan/compare/v0.2.1...v0.3.0 [0.2.1]: https://github.com/brendanzab/codespan/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/brendanzab/codespan/releases/tag/v0.2.0 codespan-reporting-0.12.0/Cargo.lock0000644000000301000000000000100126640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9ff2deb543832ee7b1a08060c38cc6af5816e96d3fcb6fc2e99bd15634e5c7f" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "cc" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "codespan-reporting" version = "0.12.0" dependencies = [ "anyhow", "insta", "lazy_static", "peg", "pico-args", "rustyline", "serde", "termcolor", "unicode-width", "unindent", ] [[package]] name = "console" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" dependencies = [ "encode_unicode", "lazy_static", "libc", "terminal_size", "winapi", ] [[package]] name = "dirs" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ "cfg-if 0.1.10", "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "dtoa" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" [[package]] name = "encode_unicode" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8927c1166f271b9c3950500ffada737e31321b43fba0ac2100d954974d2020b5" [[package]] name = "getrandom" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if 1.0.0", "libc", "wasi", ] [[package]] name = "insta" version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd354a2c8c8083d58414597a4ecada1984f9b82ea7e87eeabddc869eaf120992" dependencies = [ "console", "lazy_static", "serde", "serde_json", "serde_yaml", "similar", "uuid", ] [[package]] name = "itoa" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.2", "libc", "redox_syscall", ] [[package]] name = "linked-hash-map" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "memchr" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" dependencies = [ "libc", ] [[package]] name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" dependencies = [ "bitflags 1.3.2", "cc", "cfg-if 0.1.10", "libc", "void", ] [[package]] name = "peg" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" dependencies = [ "peg-macros", "peg-runtime", ] [[package]] name = "peg-macros" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", "proc-macro2", "quote", ] [[package]] name = "peg-runtime" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" [[package]] name = "pico-args" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "proc-macro2" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", "thiserror", ] [[package]] name = "rustyline" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de64be8eecbe428b6924f1d8430369a01719fbb182c26fa431ddbb0a95f5315d" dependencies = [ "cfg-if 0.1.10", "dirs", "libc", "log", "memchr", "nix", "unicode-segmentation", "unicode-width", "utf8parse", "winapi", ] [[package]] name = "ryu" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" [[package]] name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_yaml" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" dependencies = [ "dtoa", "linked-hash-map", "serde", "yaml-rust", ] [[package]] name = "similar" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a04629d2e8ecdcf30e0188e3699ed6d50d5750d0219db146a790065fe92a897" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e288a11a53da73056d3c7cbdee83b1d1ce3344c3210a1f8b56c32f9662a87e9" dependencies = [ "libc", "winapi", ] [[package]] name = "thiserror" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unindent" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3508be0ce1bacc38d579b69bffb4b8d469f5af0c388ff4890b2b294e61671ffe" [[package]] name = "utf8parse" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" [[package]] name = "uuid" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "yaml-rust" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628" dependencies = [ "linked-hash-map", ] codespan-reporting-0.12.0/Cargo.toml0000644000000043760000000000100127270ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.67" name = "codespan-reporting" version = "0.12.0" authors = ["Brendan Zabarauskas "] build = false exclude = ["assets/**"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Beautiful diagnostic reporting for text-based programming languages" homepage = "https://github.com/brendanzab/codespan" documentation = "https://docs.rs/codespan-reporting" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/brendanzab/codespan" [features] ascii-only = [] default = [ "std", "termcolor", ] serialization = ["serde"] std = ["serde?/std"] termcolor = [ "std", "dep:termcolor", ] [lib] name = "codespan_reporting" path = "src/lib.rs" [[example]] name = "custom_files" path = "examples/custom_files.rs" [[example]] name = "peg_calculator" path = "examples/peg_calculator.rs" [[example]] name = "readme_preview" path = "examples/readme_preview.rs" [[example]] name = "reusable_diagnostic" path = "examples/reusable_diagnostic.rs" [[example]] name = "term" path = "examples/term.rs" [[test]] name = "term" path = "tests/term.rs" [dependencies.serde] version = "1" features = [ "derive", "alloc", ] optional = true default-features = false [dependencies.termcolor] version = "1.0.4" optional = true [dependencies.unicode-width] version = ">=0.1,<0.3" [dev-dependencies.anyhow] version = "1" [dev-dependencies.insta] version = "1.6.3" [dev-dependencies.lazy_static] version = "1.4" [dev-dependencies.peg] version = "0.7" [dev-dependencies.pico-args] version = "0.5.0" [dev-dependencies.rustyline] version = "6" [dev-dependencies.unindent] version = "0.1" [lints.clippy] alloc_instead_of_core = "warn" std_instead_of_alloc = "warn" std_instead_of_core = "warn" codespan-reporting-0.12.0/Cargo.toml.orig000064400000000000000000000025241046102023000164010ustar 00000000000000[package] name = "codespan-reporting" version = "0.12.0" readme = "../README.md" license = "Apache-2.0" authors = ["Brendan Zabarauskas "] description = "Beautiful diagnostic reporting for text-based programming languages" homepage = "https://github.com/brendanzab/codespan" repository = "https://github.com/brendanzab/codespan" documentation = "https://docs.rs/codespan-reporting" exclude = ["assets/**"] edition = "2021" rust-version = "1.67" [dependencies] serde = { version = "1", default-features = false, optional = true, features = ["derive", "alloc"] } termcolor = { version = "1.0.4", optional = true } unicode-width = ">=0.1,<0.3" [dev-dependencies] pico-args = "0.5.0" anyhow = "1" insta = "1.6.3" lazy_static = "1.4" peg = "0.7" rustyline = "6" unindent = "0.1" [features] default = ["std", "termcolor"] std = ["serde?/std"] termcolor = ["std", "dep:termcolor"] serialization = ["serde"] ascii-only = [] [lints.clippy] # Certain items from `core` are re-exported in `alloc` and `std`, and likewise `alloc` has items # re-exported in `std`. # `core` is available on all platforms, `alloc` is available on almost all, and `std` is only # available on some. # These lints ensure we don't import from a "less available" crate without reason. alloc_instead_of_core = "warn" std_instead_of_alloc = "warn" std_instead_of_core = "warn"codespan-reporting-0.12.0/LICENSE000064400000000000000000000261351046102023000145230ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. codespan-reporting-0.12.0/README.md000064400000000000000000000134031046102023000147670ustar 00000000000000# codespan-reporting [![Continuous integration][actions-badge]][actions-url] [![Crates.io][crate-badge]][crate-url] [![Docs.rs][docs-badge]][docs-url] [![Matrix][matrix-badge]][matrix-lobby] [actions-badge]: https://img.shields.io/github/actions/workflow/status/brendanzab/codespan/ci.yml?branch=master [actions-url]: https://github.com/brendanzab/codespan/actions [crate-url]: https://crates.io/crates/codespan-reporting [crate-badge]: https://img.shields.io/crates/v/codespan-reporting.svg [docs-url]: https://docs.rs/codespan-reporting [docs-badge]: https://docs.rs/codespan-reporting/badge.svg [matrix-badge]: https://img.shields.io/badge/matrix-%23codespan%3Amatrix.org-blue.svg [matrix-lobby]: https://app.element.io/#/room/#codespan:matrix.org Beautiful diagnostic reporting for text-based programming languages. ![Example preview](./codespan-reporting/assets/readme_preview.svg?sanitize=true) Languages like Rust and Elm already support beautiful error reporting output, but it can take a significant amount work to implement this for new programming languages! The `codespan-reporting` crate aims to make beautiful error diagnostics easy and relatively painless for everyone! We're still working on improving the crate to help it support broader use cases, and improving the quality of the diagnostic rendering, so stay tuned for updates and please give us feedback if you have it. Contributions are also very welcome! ## Example ```rust use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; // `files::SimpleFile` and `files::SimpleFiles` help you get up and running with // `codespan-reporting` quickly! More complicated use cases can be supported // by creating custom implementations of the `files::Files` trait. let mut files = SimpleFiles::new(); let file_id = 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 "#, ), ); // We normally recommend creating a custom diagnostic data type for your // application, and then converting that to `codespan-reporting`'s diagnostic // type, but for the sake of this example we construct it directly. let diagnostic = Diagnostic::error() .with_message("`case` clauses have incompatible types") .with_code("E0308") .with_labels(vec![ Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"), Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"), Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"), Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"), Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"), Label::secondary(file_id, 186..192).with_message("expected type `String` found here"), ]) .with_notes(vec![unindent::unindent( " expected type `String` found type `Nat` ", )]); // We now set up the writer and configuration, and then finally render the // diagnostic to standard error. let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; ``` ## Running the CLI example To get an idea of what the colored CLI output looks like, clone the [repository](https://github.com/brendanzab/codespan) and run the following shell command: ```sh cargo run --example term ``` More examples of using `codespan-reporting` can be found in the [examples directory](./codespan-reporting/examples). ## Projects using codespan-reporting `codespan-reporting` is currently used in the following projects: - [cargo-deny](https://github.com/EmbarkStudios/cargo-deny) - [cargo-about](https://github.com/EmbarkStudios/cargo-about) - [CXX](https://github.com/dtolnay/cxx) - [full_moon](https://github.com/Kampfkarren/full-moon) - [Gleam](https://github.com/gleam-lang/gleam) - [Gluon](https://github.com/gluon-lang/gluon) - [MDBook LinkCheck](https://github.com/Michael-F-Bryan/mdbook-linkcheck) - [mos](https://github.com/datatrash/mos) - [Pikelet](https://github.com/pikelet-lang/pikelet) - [Naga](https://github.com/gfx-rs/wgpu/tree/trunk/naga) - [Spade](https://gitlab.com/spade-lang/spade) ... [any many more](https://crates.io/crates/codespan-reporting/reverse_dependencies) ## Alternatives to codespan-reporting There are a number of alternatives to `codespan-reporting`, including: - [annotate-snippets][annotate-snippets] - [codemap][codemap] - [language-reporting][language-reporting] (a fork of codespan) These are all ultimately inspired by rustc's excellent [error reporting infrastructure][librustc_errors]. [annotate-snippets]: https://crates.io/crates/annotate-snippets [codemap]: https://crates.io/crates/codemap [language-reporting]: https://crates.io/crates/language-reporting [librustc_errors]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_errors/src ## Contributing A guide to contributing to codespan-reporting [can be found here](/CONTRIBUTING.md). ## Code of Conduct Please note that this project is released with a [Code of Conduct](./CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. codespan-reporting-0.12.0/examples/custom_files.rs000064400000000000000000000137771046102023000204060ustar 00000000000000//! 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 core::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 core::ops::Range; /// A file that is backed by an `Arc`. #[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, } impl File { fn line_start(&self, line_index: usize) -> Result { use core::cmp::Ordering; match line_index.cmp(&self.line_starts.len()) { Ordering::Less => Ok(*self .line_starts .get(line_index) .expect("failed despite previous check")), 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, } 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, source: impl Into, ) -> Option { use core::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 { 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, 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)>, }, OverTheTopExclamations { exclamations: Vec<(files::FileId, Range)>, }, } impl Message { fn to_diagnostic(&self) -> Diagnostic { 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()]), } } } codespan-reporting-0.12.0/examples/peg_calculator.rs000064400000000000000000000043051046102023000206610ustar 00000000000000//! 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; #[allow(clippy::redundant_closure_call)] 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("", 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)?; } } } } codespan-reporting-0.12.0/examples/readme_preview.rs000064400000000000000000000260141046102023000206740ustar 00000000000000//! 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, ColorChoice, ColorSpec, StandardStream, WriteColor, }; use codespan_reporting::term::{self}; use std::io::{self, Write}; #[derive(Debug)] pub enum Opts { /// Render SVG output Svg, /// Render Stderr output Stderr { /// Configure coloring of output color: ColorChoice, }, } fn parse_args() -> Result { let mut pargs = pico_args::Arguments::from_env(); match pargs.subcommand()? { Some(value) => match value.as_str() { "svg" => Ok(Opts::Svg), "stderr" => { let color = pargs .opt_value_from_str("--color")? .unwrap_or(ColorChoice::Auto); Ok(Opts::Stderr { color }) } _ => Err(pico_args::Error::Utf8ArgumentParsingFailed { value, cause: "not a valid subcommand".to_owned(), }), }, None => Err(pico_args::Error::MissingArgument), } } 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` ", )])]; match parse_args()? { Opts::Svg => { let mut buffer = Vec::new(); let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer)); let config = codespan_reporting::term::Config { #[cfg(feature = "termcolor")] 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#"
"#,
                padding = padding,
                font_size = font_size,
                width = width,
                height = height,
            )?;

            writer.write_all(&buffer)?;

            write!(
                writer,
                "
" )?; } Opts::Stderr { color } => { let writer = StandardStream::stderr(color); 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: /// /// - `<` ⇒ `<` /// - `>` ⇒ `>` /// - `&` ⇒ `&` pub struct HtmlEscapeWriter { upstream: W, } impl HtmlEscapeWriter { pub fn new(upstream: W) -> HtmlEscapeWriter { HtmlEscapeWriter { upstream } } } impl Write for HtmlEscapeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let mut last_term = 0usize; for (i, byte) in buf.iter().enumerate() { let escape = match byte { b'<' => &b"<"[..], b'>' => &b">"[..], b'&' => &b"&"[..], _ => 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 WriteColor for HtmlEscapeWriter { 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 { upstream: W, color: ColorSpec, } impl SvgWriter { pub fn new(upstream: W) -> SvgWriter { SvgWriter { upstream, color: ColorSpec::new(), } } } impl Write for SvgWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.upstream.write(buf) } fn flush(&mut self) -> io::Result<()> { self.upstream.flush() } } impl WriteColor for SvgWriter { 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, "")?; } self.color = spec.clone(); } if spec.is_none() { write!(self, "")?; return Ok(()); } else { write!(self, "(first: bool, writer: &mut SvgWriter) -> io::Result { if !first { write!(writer, " ")?; } Ok(false) } fn write_color(color: &Color, writer: &mut SvgWriter) -> 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, "")?; self.color = ColorSpec::new(); } Ok(()) } } codespan-reporting-0.12.0/examples/reusable_diagnostic.rs000064400000000000000000000065571046102023000217160ustar 00000000000000use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::{self}; use core::ops::Range; #[derive(Debug)] pub struct Opts { color: ColorChoice, } fn parse_args() -> Result { let mut pargs = pico_args::Arguments::from_env(); let color = pargs .opt_value_from_str("--color")? .unwrap_or(ColorChoice::Auto); Ok(Opts { color }) } 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 { color } = parse_args()?; let writer = StandardStream::stderr(color); 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, content: String, } impl Item { fn new(range: Range, content: impl Into) -> Item { let content = content.into(); Item { range, content } } } codespan-reporting-0.12.0/examples/term.rs000064400000000000000000000133661046102023000166530ustar 00000000000000//! 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; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; #[derive(Debug)] pub struct Opts { /// Configure coloring of output pub color: ColorChoice, } fn parse_args() -> Result { let mut pargs = pico_args::Arguments::from_env(); let color = pargs .opt_value_from_str("--color")? .unwrap_or(ColorChoice::Auto); Ok(Opts { color }) } fn main() -> anyhow::Result<()> { let Opts { color } = parse_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(color); let config = codespan_reporting::term::Config::default(); for diagnostic in &diagnostics { term::emit(&mut writer.lock(), &config, &files, diagnostic)?; } Ok(()) } codespan-reporting-0.12.0/src/diagnostic.rs000064400000000000000000000160221046102023000167710ustar 00000000000000//! Diagnostic data structures. use alloc::{ string::{String, ToString}, vec::Vec, }; use core::ops::Range; #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; /// A severity level for diagnostic messages. /// /// These are ordered in the following way: /// /// ```rust /// use codespan_reporting::diagnostic::Severity; /// /// assert!(Severity::Bug > Severity::Error); /// assert!(Severity::Error > Severity::Warning); /// assert!(Severity::Warning > Severity::Note); /// assert!(Severity::Note > Severity::Help); /// ``` #[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum Severity { /// A help message. Help, /// A note. Note, /// A warning. Warning, /// An error. Error, /// An unexpected bug. Bug, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum LabelStyle { /// Labels that describe the primary cause of a diagnostic. Primary, /// Labels that provide additional context for a diagnostic. Secondary, } /// A label describing an underlined region of code associated with a diagnostic. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Label { /// The style of the label. pub style: LabelStyle, /// The file that we are labelling. pub file_id: FileId, /// The range in bytes we are going to include in the final snippet. pub range: Range, /// An optional message to provide some additional information for the /// underlined code. These should not include line breaks. pub message: String, } impl Label { /// Create a new label. pub fn new( style: LabelStyle, file_id: FileId, range: impl Into>, ) -> Label { Label { style, file_id, range: range.into(), message: String::new(), } } /// Create a new label with a style of [`LabelStyle::Primary`]. /// /// [`LabelStyle::Primary`]: LabelStyle::Primary pub fn primary(file_id: FileId, range: impl Into>) -> Label { Label::new(LabelStyle::Primary, file_id, range) } /// Create a new label with a style of [`LabelStyle::Secondary`]. /// /// [`LabelStyle::Secondary`]: LabelStyle::Secondary pub fn secondary(file_id: FileId, range: impl Into>) -> Label { Label::new(LabelStyle::Secondary, file_id, range) } /// Add a message to the diagnostic. pub fn with_message(mut self, message: impl ToString) -> Label { self.message = message.to_string(); self } } /// Represents a diagnostic message that can provide information like errors and /// warnings to the user. /// /// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Diagnostic { /// The overall severity of the diagnostic pub severity: Severity, /// An optional code that identifies this diagnostic. pub code: Option, /// The main message associated with this diagnostic. /// /// These should not include line breaks, and in order support the 'short' /// diagnostic display mod, the message should be specific enough to make /// sense on its own, without additional context provided by labels and notes. pub message: String, /// Source labels that describe the cause of the diagnostic. /// The order of the labels inside the vector does not have any meaning. /// The labels are always arranged in the order they appear in the source code. pub labels: Vec>, /// Notes that are associated with the primary cause of the diagnostic. /// These can include line breaks for improved formatting. pub notes: Vec, } impl Diagnostic { /// Create a new diagnostic. pub fn new(severity: Severity) -> Diagnostic { Diagnostic { severity, code: None, message: String::new(), labels: Vec::new(), notes: Vec::new(), } } /// Create a new diagnostic with a severity of [`Severity::Bug`]. /// /// [`Severity::Bug`]: Severity::Bug pub fn bug() -> Diagnostic { Diagnostic::new(Severity::Bug) } /// Create a new diagnostic with a severity of [`Severity::Error`]. /// /// [`Severity::Error`]: Severity::Error pub fn error() -> Diagnostic { Diagnostic::new(Severity::Error) } /// Create a new diagnostic with a severity of [`Severity::Warning`]. /// /// [`Severity::Warning`]: Severity::Warning pub fn warning() -> Diagnostic { Diagnostic::new(Severity::Warning) } /// Create a new diagnostic with a severity of [`Severity::Note`]. /// /// [`Severity::Note`]: Severity::Note pub fn note() -> Diagnostic { Diagnostic::new(Severity::Note) } /// Create a new diagnostic with a severity of [`Severity::Help`]. /// /// [`Severity::Help`]: Severity::Help pub fn help() -> Diagnostic { Diagnostic::new(Severity::Help) } /// Set the error code of the diagnostic. pub fn with_code(mut self, code: impl ToString) -> Diagnostic { self.code = Some(code.to_string()); self } /// Set the message of the diagnostic. pub fn with_message(mut self, message: impl ToString) -> Diagnostic { self.message = message.to_string(); self } /// Add a label to the diagnostic. pub fn with_label(mut self, label: Label) -> Diagnostic { self.labels.push(label); self } /// Add some labels to the diagnostic. pub fn with_labels(mut self, mut labels: Vec>) -> Diagnostic { self.labels.append(&mut labels); self } /// Add some labels to the diagnostic. pub fn with_labels_iter( mut self, labels: impl IntoIterator>, ) -> Diagnostic { self.labels.extend(labels); self } /// Add a note to the diagnostic. pub fn with_note(mut self, note: impl ToString) -> Diagnostic { self.notes.push(note.to_string()); self } /// Add some notes to the diagnostic. pub fn with_notes(mut self, mut notes: Vec) -> Diagnostic { self.notes.append(&mut notes); self } /// Add some notes to the diagnostic. pub fn with_notes_iter( mut self, notes: impl IntoIterator, ) -> Diagnostic { self.notes.extend(notes); self } } codespan-reporting-0.12.0/src/files.rs000064400000000000000000000363041046102023000157540ustar 00000000000000//! Source file support for diagnostic reporting. //! //! The main trait defined in this module is the [`Files`] trait, which provides //! provides the minimum amount of functionality required for printing [`Diagnostics`] //! with the [`term::emit`] function. //! //! Simple implementations of this trait are implemented: //! //! - [`SimpleFile`]: For single-file use-cases //! - [`SimpleFiles`]: For multi-file use-cases //! //! These data structures provide a pretty minimal API, however, //! so end-users are encouraged to create their own implementations for their //! own specific use-cases, such as an implementation that accesses the file //! system directly (and caches the line start locations), or an implementation //! using an incremental compilation library like [`salsa`]. //! //! [`term::emit`]: crate::term::emit //! [`Diagnostics`]: crate::diagnostic::Diagnostic //! [`Files`]: Files //! [`SimpleFile`]: SimpleFile //! [`SimpleFiles`]: SimpleFiles //! //! [`salsa`]: https://crates.io/crates/salsa use alloc::vec::Vec; use core::ops::Range; #[cfg(feature = "std")] use std::error; #[cfg(not(feature = "std"))] use core::error; /// An enum representing an error that happened while looking up a file or a piece of content in that file. #[derive(Debug)] #[non_exhaustive] pub enum Error { /// A required file is not in the file database. FileMissing, /// The file is present, but does not contain the specified byte index. IndexTooLarge { given: usize, max: usize }, /// The file is present, but does not contain the specified line index. LineTooLarge { given: usize, max: usize }, /// The file is present and contains the specified line index, but the line does not contain the specified column index. ColumnTooLarge { given: usize, max: usize }, /// The given index is contained in the file, but is not a boundary of a UTF-8 code point. InvalidCharBoundary { given: usize }, /// There was a error while doing IO. #[cfg(feature = "std")] Io(std::io::Error), /// There was a error during formatting. FormatError, } #[cfg(feature = "std")] impl From for Error { fn from(err: std::io::Error) -> Error { Error::Io(err) } } impl From for Error { fn from(_err: core::fmt::Error) -> Error { Error::FormatError } } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Error::FileMissing => write!(f, "file missing"), Error::IndexTooLarge { given, max } => { write!(f, "invalid index {}, maximum index is {}", given, max) } Error::LineTooLarge { given, max } => { write!(f, "invalid line {}, maximum line is {}", given, max) } Error::ColumnTooLarge { given, max } => { write!(f, "invalid column {}, maximum column {}", given, max) } Error::InvalidCharBoundary { .. } => write!(f, "index is not a code point boundary"), #[cfg(feature = "std")] Error::Io(err) => write!(f, "{}", err), Error::FormatError => write!(f, "formatting error"), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self { #[cfg(feature = "std")] Error::Io(err) => Some(err), _ => None, } } } /// A minimal interface for accessing source files when rendering diagnostics. /// /// A lifetime parameter `'a` is provided to allow any of the returned values to returned by reference. /// This is to workaround the lack of higher kinded lifetime parameters. /// This can be ignored if this is not needed, however. pub trait Files<'a> { /// A unique identifier for files in the file provider. This will be used /// for rendering `diagnostic::Label`s in the corresponding source files. type FileId: 'a + Copy + PartialEq; /// The user-facing name of a file, to be displayed in diagnostics. type Name: 'a + core::fmt::Display; /// The source code of a file. type Source: 'a + AsRef; /// The user-facing name of a file. fn name(&'a self, id: Self::FileId) -> Result; /// The source code of a file. fn source(&'a self, id: Self::FileId) -> Result; /// The index of the line at the given byte index. /// If the byte index is past the end of the file, returns the maximum line index in the file. /// This means that this function only fails if the file is not present. /// /// # Note for trait implementors /// /// This can be implemented efficiently by performing a binary search over /// a list of line starts that was computed by calling the [`line_starts`] /// function that is exported from the [`files`] module. It might be useful /// to pre-compute and cache these line starts. /// /// [`line_starts`]: crate::files::line_starts /// [`files`]: crate::files fn line_index(&'a self, id: Self::FileId, byte_index: usize) -> Result; /// The user-facing line number at the given line index. /// It is not necessarily checked that the specified line index /// is actually in the file. /// /// # Note for trait implementors /// /// This is usually 1-indexed from the beginning of the file, but /// can be useful for implementing something like the /// [C preprocessor's `#line` macro][line-macro]. /// /// [line-macro]: https://en.cppreference.com/w/c/preprocessor/line #[allow(unused_variables)] fn line_number(&'a self, id: Self::FileId, line_index: usize) -> Result { Ok(line_index + 1) } /// The user-facing column number at the given line index and byte index. /// /// # Note for trait implementors /// /// This is usually 1-indexed from the the start of the line. /// A default implementation is provided, based on the [`column_index`] /// function that is exported from the [`files`] module. /// /// [`files`]: crate::files /// [`column_index`]: crate::files::column_index fn column_number( &'a self, id: Self::FileId, line_index: usize, byte_index: usize, ) -> Result { let source = self.source(id)?; let line_range = self.line_range(id, line_index)?; let column_index = column_index(source.as_ref(), line_range, byte_index); Ok(column_index + 1) } /// Convenience method for returning line and column number at the given /// byte index in the file. fn location(&'a self, id: Self::FileId, byte_index: usize) -> Result { let line_index = self.line_index(id, byte_index)?; Ok(Location { line_number: self.line_number(id, line_index)?, column_number: self.column_number(id, line_index, byte_index)?, }) } /// The byte range of line in the source of the file. fn line_range(&'a self, id: Self::FileId, line_index: usize) -> Result, Error>; } /// A user-facing location in a source file. /// /// Returned by [`Files::location`]. /// /// [`Files::location`]: Files::location #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Location { /// The user-facing line number. pub line_number: usize, /// The user-facing column number. pub column_number: usize, } /// The column index at the given byte index in the source file. /// This is the number of characters to the given byte index. /// /// If the byte index is smaller than the start of the line, then `0` is returned. /// If the byte index is past the end of the line, the column index of the last /// character `+ 1` is returned. /// /// # Example /// /// ```rust /// use codespan_reporting::files; /// /// let source = "\n\n🗻∈🌏\n\n"; /// /// assert_eq!(files::column_index(source, 0..1, 0), 0); /// assert_eq!(files::column_index(source, 2..13, 0), 0); /// assert_eq!(files::column_index(source, 2..13, 2 + 0), 0); /// assert_eq!(files::column_index(source, 2..13, 2 + 1), 0); /// assert_eq!(files::column_index(source, 2..13, 2 + 4), 1); /// assert_eq!(files::column_index(source, 2..13, 2 + 8), 2); /// assert_eq!(files::column_index(source, 2..13, 2 + 10), 2); /// assert_eq!(files::column_index(source, 2..13, 2 + 11), 3); /// assert_eq!(files::column_index(source, 2..13, 2 + 12), 3); /// ``` pub fn column_index(source: &str, line_range: Range, byte_index: usize) -> usize { let end_index = core::cmp::min(byte_index, core::cmp::min(line_range.end, source.len())); (line_range.start..end_index) .filter(|byte_index| source.is_char_boundary(byte_index + 1)) .count() } /// Return the starting byte index of each line in the source string. /// /// This can make it easier to implement [`Files::line_index`] by allowing /// implementors of [`Files`] to pre-compute the line starts, then search for /// the corresponding line range, as shown in the example below. /// /// [`Files`]: Files /// [`Files::line_index`]: Files::line_index /// /// # Example /// /// ```rust /// use codespan_reporting::files; /// /// let source = "foo\nbar\r\n\nbaz"; /// let line_starts: Vec<_> = files::line_starts(source).collect(); /// /// assert_eq!( /// line_starts, /// [ /// 0, // "foo\n" /// 4, // "bar\r\n" /// 9, // "" /// 10, // "baz" /// ], /// ); /// /// fn line_index(line_starts: &[usize], byte_index: usize) -> Option { /// match line_starts.binary_search(&byte_index) { /// Ok(line) => Some(line), /// Err(next_line) => Some(next_line - 1), /// } /// } /// /// assert_eq!(line_index(&line_starts, 5), Some(1)); /// ``` // NOTE: this is copied in `codespan::file::line_starts` and should be kept in sync. pub fn line_starts(source: &str) -> impl '_ + Iterator { core::iter::once(0).chain(source.match_indices('\n').map(|(i, _)| i + 1)) } /// A file database that contains a single source file. /// /// Because there is only single file in this database we use `()` as a [`FileId`]. /// /// This is useful for simple language tests, but it might be worth creating a /// custom implementation when a language scales beyond a certain size. /// /// [`FileId`]: Files::FileId #[derive(Debug, Clone)] pub struct SimpleFile { /// The name of the file. name: Name, /// The source code of the file. source: Source, /// The starting byte indices in the source code. line_starts: Vec, } impl SimpleFile where Name: core::fmt::Display, Source: AsRef, { /// Create a new source file. pub fn new(name: Name, source: Source) -> SimpleFile { SimpleFile { name, line_starts: line_starts(source.as_ref()).collect(), source, } } /// Return the name of the file. pub fn name(&self) -> &Name { &self.name } /// Return the source of the file. pub fn source(&self) -> &Source { &self.source } /// Return the starting byte index of the line with the specified line index. /// Convenience method that already generates errors if necessary. fn line_start(&self, line_index: usize) -> Result { use core::cmp::Ordering; match line_index.cmp(&self.line_starts.len()) { Ordering::Less => Ok(self .line_starts .get(line_index) .cloned() .expect("failed despite previous check")), Ordering::Equal => Ok(self.source.as_ref().len()), Ordering::Greater => Err(Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1, }), } } } impl<'a, Name, Source> Files<'a> for SimpleFile where Name: 'a + core::fmt::Display + Clone, Source: 'a + AsRef, { type FileId = (); type Name = Name; type Source = &'a str; fn name(&self, (): ()) -> Result { Ok(self.name.clone()) } fn source(&self, (): ()) -> Result<&str, Error> { Ok(self.source.as_ref()) } fn line_index(&self, (): (), byte_index: usize) -> Result { Ok(self .line_starts .binary_search(&byte_index) .unwrap_or_else(|next_line| next_line - 1)) } fn line_range(&self, (): (), line_index: usize) -> Result, Error> { let line_start = self.line_start(line_index)?; let next_line_start = self.line_start(line_index + 1)?; Ok(line_start..next_line_start) } } /// A file database that can store multiple source files. /// /// This is useful for simple language tests, but it might be worth creating a /// custom implementation when a language scales beyond a certain size. /// It is a glorified `Vec` that implements the `Files` trait. #[derive(Debug, Default, Clone)] pub struct SimpleFiles { files: Vec>, } impl SimpleFiles where Name: core::fmt::Display, Source: AsRef, { /// Create a new files database. pub fn new() -> SimpleFiles { SimpleFiles { 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: Name, source: Source) -> usize { let file_id = self.files.len(); self.files.push(SimpleFile::new(name, source)); file_id } /// Get the file corresponding to the given id. pub fn get(&self, file_id: usize) -> Result<&SimpleFile, Error> { self.files.get(file_id).ok_or(Error::FileMissing) } } impl<'a, Name, Source> Files<'a> for SimpleFiles where Name: 'a + core::fmt::Display + Clone, Source: 'a + AsRef, { type FileId = usize; type Name = Name; type Source = &'a str; fn name(&self, file_id: usize) -> Result { Ok(self.get(file_id)?.name().clone()) } fn source(&self, file_id: usize) -> Result<&str, Error> { Ok(self.get(file_id)?.source().as_ref()) } fn line_index(&self, file_id: usize, byte_index: usize) -> Result { self.get(file_id)?.line_index((), byte_index) } fn line_range(&self, file_id: usize, line_index: usize) -> Result, Error> { self.get(file_id)?.line_range((), line_index) } } #[cfg(test)] mod test { use super::*; const TEST_SOURCE: &str = "foo\nbar\r\n\nbaz"; #[test] fn line_starts() { let file = SimpleFile::new("test", TEST_SOURCE); assert_eq!( file.line_starts, [ 0, // "foo\n" 4, // "bar\r\n" 9, // "" 10, // "baz" ], ); } #[test] fn line_span_sources() { let file = SimpleFile::new("test", TEST_SOURCE); let line_sources = (0..4) .map(|line| { let line_range = file.line_range((), line).unwrap(); &file.source[line_range] }) .collect::>(); assert_eq!(line_sources, ["foo\n", "bar\r\n", "\n", "baz"]); } } codespan-reporting-0.12.0/src/lib.rs000064400000000000000000000003171046102023000154130ustar 00000000000000//! Diagnostic reporting support for the codespan crate. #![forbid(unsafe_code)] #![no_std] extern crate alloc; #[cfg(feature = "std")] extern crate std; pub mod diagnostic; pub mod files; pub mod term; codespan-reporting-0.12.0/src/term/config.rs000064400000000000000000000301751046102023000170660ustar 00000000000000use alloc::string::String; #[cfg(feature = "termcolor")] use { crate::diagnostic::{LabelStyle, Severity}, termcolor::{Color, ColorSpec}, }; /// Configures how a diagnostic is rendered. #[derive(Clone, Debug)] pub struct Config { /// The display style to use when rendering diagnostics. /// Defaults to: [`DisplayStyle::Rich`]. /// /// [`DisplayStyle::Rich`]: DisplayStyle::Rich pub display_style: DisplayStyle, /// Column width of tabs. /// Defaults to: `4`. pub tab_width: usize, /// Styles to use when rendering the diagnostic. #[cfg(feature = "termcolor")] pub styles: Styles, /// Characters to use when rendering the diagnostic. pub chars: Chars, /// The minimum number of lines to be shown after the line on which a multiline [`Label`] begins. /// /// Defaults to: `3`. /// /// [`Label`]: crate::diagnostic::Label pub start_context_lines: usize, /// The minimum number of lines to be shown before the line on which a multiline [`Label`] ends. /// /// Defaults to: `1`. /// /// [`Label`]: crate::diagnostic::Label pub end_context_lines: usize, /// The minimum number of lines before a label that should be included for context. /// /// Defaults to: `0`. pub before_label_lines: usize, /// The minimum number of lines after a label that should be included for context. /// /// Defaults to: `0`. pub after_label_lines: usize, } impl Default for Config { fn default() -> Config { Config { display_style: DisplayStyle::Rich, tab_width: 4, #[cfg(feature = "termcolor")] styles: Styles::default(), chars: Chars::default(), start_context_lines: 3, end_context_lines: 1, before_label_lines: 0, after_label_lines: 0, } } } /// The display style to use when rendering diagnostics. #[derive(Clone, Debug)] pub enum DisplayStyle { /// Output a richly formatted diagnostic, with source code previews. /// /// ```text /// error[E0001]: unexpected type in `+` application /// ┌─ test:2:9 /// │ /// 2 │ (+ test "") /// │ ^^ expected `Int` but found `String` /// │ /// = expected type `Int` /// found type `String` /// /// error[E0002]: Bad config found /// /// ``` Rich, /// Output a condensed diagnostic, with a line number, severity, message and notes (if any). /// /// ```text /// test:2:9: error[E0001]: unexpected type in `+` application /// = expected type `Int` /// found type `String` /// /// error[E0002]: Bad config found /// ``` Medium, /// Output a short diagnostic, with a line number, severity, and message. /// /// ```text /// test:2:9: error[E0001]: unexpected type in `+` application /// error[E0002]: Bad config found /// ``` Short, } /// Styles to use when rendering the diagnostic. #[cfg(feature = "termcolor")] #[derive(Clone, Debug)] pub struct Styles { /// The style to use when rendering bug headers. /// Defaults to `fg:red bold intense`. pub header_bug: ColorSpec, /// The style to use when rendering error headers. /// Defaults to `fg:red bold intense`. pub header_error: ColorSpec, /// The style to use when rendering warning headers. /// Defaults to `fg:yellow bold intense`. pub header_warning: ColorSpec, /// The style to use when rendering note headers. /// Defaults to `fg:green bold intense`. pub header_note: ColorSpec, /// The style to use when rendering help headers. /// Defaults to `fg:cyan bold intense`. pub header_help: ColorSpec, /// The style to use when the main diagnostic message. /// Defaults to `bold intense`. pub header_message: ColorSpec, /// The style to use when rendering bug labels. /// Defaults to `fg:red`. pub primary_label_bug: ColorSpec, /// The style to use when rendering error labels. /// Defaults to `fg:red`. pub primary_label_error: ColorSpec, /// The style to use when rendering warning labels. /// Defaults to `fg:yellow`. pub primary_label_warning: ColorSpec, /// The style to use when rendering note labels. /// Defaults to `fg:green`. pub primary_label_note: ColorSpec, /// The style to use when rendering help labels. /// Defaults to `fg:cyan`. pub primary_label_help: ColorSpec, /// The style to use when rendering secondary labels. /// Defaults `fg:blue` (or `fg:cyan` on windows). pub secondary_label: ColorSpec, /// The style to use when rendering the line numbers. /// Defaults `fg:blue` (or `fg:cyan` on windows). pub line_number: ColorSpec, /// The style to use when rendering the source code borders. /// Defaults `fg:blue` (or `fg:cyan` on windows). pub source_border: ColorSpec, /// The style to use when rendering the note bullets. /// Defaults `fg:blue` (or `fg:cyan` on windows). pub note_bullet: ColorSpec, } #[cfg(feature = "termcolor")] impl Styles { /// The style used to mark a header at a given severity. pub fn header(&self, severity: Severity) -> &ColorSpec { match severity { Severity::Bug => &self.header_bug, Severity::Error => &self.header_error, Severity::Warning => &self.header_warning, Severity::Note => &self.header_note, Severity::Help => &self.header_help, } } /// The style used to mark a primary or secondary label at a given severity. pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec { match (label_style, severity) { (LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug, (LabelStyle::Primary, Severity::Error) => &self.primary_label_error, (LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning, (LabelStyle::Primary, Severity::Note) => &self.primary_label_note, (LabelStyle::Primary, Severity::Help) => &self.primary_label_help, (LabelStyle::Secondary, _) => &self.secondary_label, } } #[doc(hidden)] pub fn with_blue(blue: Color) -> Styles { let header = ColorSpec::new().set_bold(true).set_intense(true).clone(); Styles { header_bug: header.clone().set_fg(Some(Color::Red)).clone(), header_error: header.clone().set_fg(Some(Color::Red)).clone(), header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(), header_note: header.clone().set_fg(Some(Color::Green)).clone(), header_help: header.clone().set_fg(Some(Color::Cyan)).clone(), header_message: header, primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(), primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(), primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(), primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(), primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(), secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(), line_number: ColorSpec::new().set_fg(Some(blue)).clone(), source_border: ColorSpec::new().set_fg(Some(blue)).clone(), note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(), } } } #[cfg(feature = "termcolor")] impl Default for Styles { fn default() -> Styles { // Blue is really difficult to see on the standard windows command line #[cfg(windows)] const BLUE: Color = Color::Cyan; #[cfg(not(windows))] const BLUE: Color = Color::Blue; Self::with_blue(BLUE) } } /// Characters to use when rendering the diagnostic. /// /// By using [`Chars::ascii()`] you can switch to an ASCII-only format suitable /// for rendering on terminals that do not support box drawing characters. #[derive(Clone, Debug)] pub struct Chars { /// The characters to use for the top-left border of the snippet. /// Defaults to: `"┌─"` or `"-->"` with [`Chars::ascii()`]. pub snippet_start: String, /// The character to use for the left border of the source. /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`]. pub source_border_left: char, /// The character to use for the left border break of the source. /// Defaults to: `'·'` or `'.'` with [`Chars::ascii()`]. pub source_border_left_break: char, /// The character to use for the note bullet. /// Defaults to: `'='`. pub note_bullet: char, /// The character to use for marking a single-line primary label. /// Defaults to: `'^'`. pub single_primary_caret: char, /// The character to use for marking a single-line secondary label. /// Defaults to: `'-'`. pub single_secondary_caret: char, /// The character to use for marking the start of a multi-line primary label. /// Defaults to: `'^'`. pub multi_primary_caret_start: char, /// The character to use for marking the end of a multi-line primary label. /// Defaults to: `'^'`. pub multi_primary_caret_end: char, /// The character to use for marking the start of a multi-line secondary label. /// Defaults to: `'\''`. pub multi_secondary_caret_start: char, /// The character to use for marking the end of a multi-line secondary label. /// Defaults to: `'\''`. pub multi_secondary_caret_end: char, /// The character to use for the top-left corner of a multi-line label. /// Defaults to: `'╭'` or `'/'` with [`Chars::ascii()`]. pub multi_top_left: char, /// The character to use for the top of a multi-line label. /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`]. pub multi_top: char, /// The character to use for the bottom-left corner of a multi-line label. /// Defaults to: `'╰'` or `'\'` with [`Chars::ascii()`]. pub multi_bottom_left: char, /// The character to use when marking the bottom of a multi-line label. /// Defaults to: `'─'` or `'-'` with [`Chars::ascii()`]. pub multi_bottom: char, /// The character to use for the left of a multi-line label. /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`]. pub multi_left: char, /// The character to use for the left of a pointer underneath a caret. /// Defaults to: `'│'` or `'|'` with [`Chars::ascii()`]. pub pointer_left: char, } impl Default for Chars { fn default() -> Chars { Chars::box_drawing() } } impl Chars { /// A character set that uses Unicode box drawing characters. pub fn box_drawing() -> Chars { Chars { snippet_start: "┌─".into(), source_border_left: '│', source_border_left_break: '·', note_bullet: '=', single_primary_caret: '^', single_secondary_caret: '-', multi_primary_caret_start: '^', multi_primary_caret_end: '^', multi_secondary_caret_start: '\'', multi_secondary_caret_end: '\'', multi_top_left: '╭', multi_top: '─', multi_bottom_left: '╰', multi_bottom: '─', multi_left: '│', pointer_left: '│', } } /// A character set that only uses ASCII characters. /// /// This is useful if your terminal's font does not support box drawing /// characters well and results in output that looks similar to rustc's /// diagnostic output. pub fn ascii() -> Chars { Chars { snippet_start: "-->".into(), source_border_left: '|', source_border_left_break: '.', note_bullet: '=', single_primary_caret: '^', single_secondary_caret: '-', multi_primary_caret_start: '^', multi_primary_caret_end: '^', multi_secondary_caret_start: '\'', multi_secondary_caret_end: '\'', multi_top_left: '/', multi_top: '-', multi_bottom_left: '\\', multi_bottom: '-', multi_left: '|', pointer_left: '|', } } } codespan-reporting-0.12.0/src/term/renderer.rs000064400000000000000000001204611046102023000174250ustar 00000000000000use alloc::string::String; use core::ops::Range; use crate::diagnostic::{LabelStyle, Severity}; use crate::files::{Error, Location}; use crate::term::{Chars, Config}; #[cfg(feature = "termcolor")] use { crate::term::Styles, termcolor::{ColorSpec, WriteColor}, }; #[cfg(feature = "std")] use std::io::{self, Write}; #[cfg(not(feature = "std"))] use core::fmt::{Arguments, Write}; /// The 'location focus' of a source code snippet. pub struct Locus { /// The user-facing name of the file. pub name: String, /// The location. pub location: Location, } /// Single-line label, with an optional message. /// /// ```text /// ^^^^^^^^^ blah blah /// ``` pub type SingleLabel<'diagnostic> = (LabelStyle, Range, &'diagnostic str); /// A multi-line label to render. /// /// Locations are relative to the start of where the source code is rendered. pub enum MultiLabel<'diagnostic> { /// Multi-line label top. /// The contained value indicates where the label starts. /// /// ```text /// ╭────────────^ /// ``` /// /// Can also be rendered at the beginning of the line /// if there is only whitespace before the label starts. /// /// /// ```text /// ╭ /// ``` Top(usize), /// Left vertical labels for multi-line labels. /// /// ```text /// │ /// ``` Left, /// Multi-line label bottom, with an optional message. /// The first value indicates where the label ends. /// /// ```text /// ╰────────────^ blah blah /// ``` Bottom(usize, &'diagnostic str), } #[derive(Copy, Clone)] enum VerticalBound { Top, Bottom, } type Underline = (LabelStyle, VerticalBound); /// A renderer of display list entries. /// /// The following diagram gives an overview of each of the parts of the renderer's output: /// /// ```text /// ┌ outer gutter /// │ ┌ left border /// │ │ ┌ inner gutter /// │ │ │ ┌─────────────────────────── source ─────────────────────────────┐ /// │ │ │ │ │ /// ┌──────────────────────────────────────────────────────────────────────────── /// header ── │ error[0001]: oh noes, a cupcake has occurred! /// snippet start ── │ ┌─ test:9:0 /// snippet empty ── │ │ /// snippet line ── │ 9 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake /// snippet line ── │ 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly /// │ │ ╭─│─────────^ /// snippet break ── │ · │ │ /// snippet line ── │ 33 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. /// snippet line ── │ 34 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow /// │ │ │ ╰─────────────────────────────^ blah blah /// snippet break ── │ · │ /// snippet line ── │ 38 │ │ Brownie lemon drops chocolate jelly-o candy canes. Danish marzipan /// snippet line ── │ 39 │ │ jujubes soufflé carrot cake marshmallow tiramisu caramels candy canes. /// │ │ │ ^^^^^^^^^^^^^^^^^^^ -------------------- blah blah /// │ │ │ │ /// │ │ │ blah blah /// │ │ │ note: this is a note /// snippet line ── │ 40 │ │ Fruitcake jelly-o danish toffee. Tootsie roll pastry cheesecake /// snippet line ── │ 41 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry /// snippet line ── │ 42 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin /// │ │ │ ^^^^^^^^^^^^^^^^^^ blah blah /// │ │ ╰──────────^ blah blah /// snippet break ── │ · /// snippet line ── │ 82 │ gingerbread toffee chupa chups chupa chups jelly-o cotton candy. /// │ │ ^^^^^^ ------- blah blah /// snippet empty ── │ │ /// snippet note ── │ = blah blah /// snippet note ── │ = blah blah blah /// │ blah blah /// snippet note ── │ = blah blah blah /// │ blah blah /// empty ── │ /// ``` /// /// Filler text from http://www.cupcakeipsum.com pub struct Renderer<'writer, 'config> { #[cfg(feature = "termcolor")] writer: &'writer mut dyn WriteColor, #[cfg(not(feature = "termcolor"))] writer: &'writer mut dyn Write, config: &'config Config, } impl<'writer, 'config> Renderer<'writer, 'config> { /// Construct a renderer from the given writer and config. pub fn new( #[cfg(feature = "termcolor")] writer: &'writer mut dyn WriteColor, #[cfg(not(feature = "termcolor"))] writer: &'writer mut dyn Write, config: &'config Config, ) -> Renderer<'writer, 'config> { Renderer { writer, config } } fn chars(&self) -> &'config Chars { &self.config.chars } #[cfg(feature = "termcolor")] fn styles(&self) -> &'config Styles { &self.config.styles } /// Diagnostic header, with severity, code, and message. /// /// ```text /// error[E0001]: unexpected type in `+` application /// ``` pub fn render_header( &mut self, locus: Option<&Locus>, severity: Severity, code: Option<&str>, message: &str, ) -> Result<(), Error> { // Write locus // // ```text // test:2:9: // ``` if let Some(locus) = locus { self.snippet_locus(locus)?; write!(self, ": ")?; } // Write severity name // // ```text // error // ``` #[cfg(feature = "termcolor")] self.set_color(self.styles().header(severity))?; match severity { Severity::Bug => write!(self, "bug")?, Severity::Error => write!(self, "error")?, Severity::Warning => write!(self, "warning")?, Severity::Help => write!(self, "help")?, Severity::Note => write!(self, "note")?, } // Write error code // // ```text // [E0001] // ``` if let Some(code) = &code.filter(|code| !code.is_empty()) { write!(self, "[{}]", code)?; } // Write diagnostic message // // ```text // : unexpected type in `+` application // ``` #[cfg(feature = "termcolor")] self.set_color(&self.styles().header_message)?; write!(self, ": {}", message)?; #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; Ok(()) } /// Empty line. pub fn render_empty(&mut self) -> Result<(), Error> { writeln!(self)?; Ok(()) } /// Top left border and locus. /// /// ```text /// ┌─ test:2:9 /// ``` pub fn render_snippet_start( &mut self, outer_padding: usize, locus: &Locus, ) -> Result<(), Error> { self.outer_gutter(outer_padding)?; #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().snippet_start)?; #[cfg(feature = "termcolor")] self.reset()?; write!(self, " ")?; self.snippet_locus(locus)?; writeln!(self)?; Ok(()) } /// A line of source code. /// /// ```text /// 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly /// │ ╭─│─────────^ /// ``` #[allow(clippy::too_many_arguments)] pub fn render_snippet_source( &mut self, outer_padding: usize, line_number: usize, source: &str, severity: Severity, single_labels: &[SingleLabel<'_>], num_multi_labels: usize, multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], ) -> Result<(), Error> { // Trim trailing newlines, linefeeds, and null chars from source, if they exist. // FIXME: Use the number of trimmed placeholders when rendering single line carets let source = source.trim_end_matches(['\n', '\r', '\0'].as_ref()); // Write source line // // ```text // 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly // ``` { // Write outer gutter (with line number) and border self.outer_gutter_number(line_number, outer_padding)?; self.border_left()?; // Write inner gutter (with multi-line continuations on the left if necessary) let mut multi_labels_iter = multi_labels.iter().peekable(); for label_column in 0..num_multi_labels { match multi_labels_iter.peek() { Some((label_index, label_style, label)) if *label_index == label_column => { match label { MultiLabel::Top(start) if *start <= source.len() - source.trim_start().len() => { self.label_multi_top_left(severity, *label_style)?; } MultiLabel::Top(..) => self.inner_gutter_space()?, MultiLabel::Left | MultiLabel::Bottom(..) => { self.label_multi_left(severity, *label_style, None)?; } } multi_labels_iter.next(); } Some((_, _, _)) | None => self.inner_gutter_space()?, } } // Write source text write!(self, " ")?; let mut in_primary = false; for (metrics, ch) in self.char_metrics(source.char_indices()) { let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); // Check if we are overlapping a primary label let is_primary = single_labels.iter().any(|(ls, range, _)| { *ls == LabelStyle::Primary && is_overlapping(range, &column_range) }) || multi_labels.iter().any(|(_, ls, label)| { *ls == LabelStyle::Primary && match label { MultiLabel::Top(start) => column_range.start >= *start, MultiLabel::Left => true, MultiLabel::Bottom(start, _) => column_range.end <= *start, } }); // Set the source color if we are in a primary label if is_primary && !in_primary { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, LabelStyle::Primary))?; in_primary = true; } else if !is_primary && in_primary { #[cfg(feature = "termcolor")] self.reset()?; in_primary = false; } match ch { '\t' => (0..metrics.unicode_width).try_for_each(|_| write!(self, " "))?, _ => write!(self, "{}", ch)?, } } if in_primary { #[cfg(feature = "termcolor")] self.reset()?; } writeln!(self)?; } // Write single labels underneath source // // ```text // │ - ---- ^^^ second mutable borrow occurs here // │ │ │ // │ │ first mutable borrow occurs here // │ first borrow later used by call // │ help: some help here // ``` if !single_labels.is_empty() { // Our plan is as follows: // // 1. Do an initial scan to find: // - The number of non-empty messages. // - The right-most start and end positions of labels. // - A candidate for a trailing label (where the label's message // is printed to the left of the caret). // 2. Check if the trailing label candidate overlaps another label - // if so we print it underneath the carets with the other labels. // 3. Print a line of carets, and (possibly) the trailing message // to the left. // 4. Print vertical lines pointing to the carets, and the messages // for those carets. // // We try our best avoid introducing new dynamic allocations, // instead preferring to iterate over the labels multiple times. It // is unclear what the performance tradeoffs are however, so further // investigation may be required. // The number of non-empty messages to print. let mut num_messages = 0; // The right-most start position, eg: // // ```text // -^^^^---- ^^^^^^^ // │ // right-most start position // ``` let mut max_label_start = 0; // The right-most end position, eg: // // ```text // -^^^^---- ^^^^^^^ // │ // right-most end position // ``` let mut max_label_end = 0; // A trailing message, eg: // // ```text // ^^^ second mutable borrow occurs here // ``` let mut trailing_label = None; for (label_index, label) in single_labels.iter().enumerate() { let (_, range, message) = label; if !message.is_empty() { num_messages += 1; } max_label_start = core::cmp::max(max_label_start, range.start); max_label_end = core::cmp::max(max_label_end, range.end); // This is a candidate for the trailing label, so let's record it. if range.end == max_label_end { if message.is_empty() { trailing_label = None; } else { trailing_label = Some((label_index, label)); } } } if let Some((trailing_label_index, (_, trailing_range, _))) = trailing_label { // Check to see if the trailing label candidate overlaps any of // the other labels on the current line. if single_labels .iter() .enumerate() .filter(|(label_index, _)| *label_index != trailing_label_index) .any(|(_, (_, range, _))| is_overlapping(trailing_range, range)) { // If it does, we'll instead want to render it below the // carets along with the other hanging labels. trailing_label = None; } } // Write a line of carets // // ```text // │ ^^^^^^ -------^^^^^^^^^-------^^^^^----- ^^^^ trailing label message // ``` self.outer_gutter(outer_padding)?; self.border_left()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; write!(self, " ")?; let mut previous_label_style = None; let placeholder_metrics = Metrics { byte_index: source.len(), unicode_width: 1, }; for (metrics, ch) in self .char_metrics(source.char_indices()) // Add a placeholder source column at the end to allow for // printing carets at the end of lines, eg: // // ```text // 1 │ Hello world! // │ ^ // ``` .chain(core::iter::once((placeholder_metrics, '\0'))) { // Find the current label style at this column let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); let current_label_style = single_labels .iter() .filter(|(_, range, _)| is_overlapping(range, &column_range)) .map(|(label_style, _, _)| *label_style) .max_by_key(label_priority_key); // Update writer style if necessary if previous_label_style != current_label_style { match current_label_style { None => { #[cfg(feature = "termcolor")] self.reset()?; } #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; } } } let caret_ch = match current_label_style { Some(LabelStyle::Primary) => Some(self.chars().single_primary_caret), Some(LabelStyle::Secondary) => Some(self.chars().single_secondary_caret), // Only print padding if we are before the end of the last single line caret None if metrics.byte_index < max_label_end => Some(' '), None => None, }; if let Some(caret_ch) = caret_ch { // FIXME: improve rendering of carets between character boundaries (0..metrics.unicode_width).try_for_each(|_| write!(self, "{}", caret_ch))?; } previous_label_style = current_label_style; } // Reset style if it was previously set if previous_label_style.is_some() { #[cfg(feature = "termcolor")] self.reset()?; } // Write first trailing label message #[cfg_attr(not(feature = "termcolor"), allow(unused))] if let Some((_, (label_style, _, message))) = trailing_label { write!(self, " ")?; #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, *label_style))?; write!(self, "{}", message)?; #[cfg(feature = "termcolor")] self.reset()?; } writeln!(self)?; // Write hanging labels pointing to carets // // ```text // │ │ │ // │ │ first mutable borrow occurs here // │ first borrow later used by call // │ help: some help here // ``` if num_messages > trailing_label.iter().count() { // Write first set of vertical lines before hanging labels // // ```text // │ │ │ // ``` self.outer_gutter(outer_padding)?; self.border_left()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; write!(self, " ")?; self.caret_pointers( severity, max_label_start, single_labels, trailing_label, source.char_indices(), )?; writeln!(self)?; // Write hanging labels pointing to carets // // ```text // │ │ first mutable borrow occurs here // │ first borrow later used by call // │ help: some help here // ``` #[cfg_attr(not(feature = "termcolor"), allow(unused))] for (label_style, range, message) in hanging_labels(single_labels, trailing_label).rev() { self.outer_gutter(outer_padding)?; self.border_left()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; write!(self, " ")?; self.caret_pointers( severity, max_label_start, single_labels, trailing_label, source .char_indices() .take_while(|(byte_index, _)| *byte_index < range.start), )?; #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, *label_style))?; write!(self, "{}", message)?; #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; } } } // Write top or bottom label carets underneath source // // ```text // │ ╰───│──────────────────^ woops // │ ╭─│─────────^ // ``` for (multi_label_index, (_, label_style, label)) in multi_labels.iter().enumerate() { let (label_style, range, bottom_message) = match label { MultiLabel::Left => continue, // no label caret needed // no label caret needed if this can be started in front of the line MultiLabel::Top(start) if *start <= source.len() - source.trim_start().len() => { continue } MultiLabel::Top(range) => (*label_style, range, None), MultiLabel::Bottom(range, message) => (*label_style, range, Some(message)), }; self.outer_gutter(outer_padding)?; self.border_left()?; // Write inner gutter. // // ```text // │ ╭─│───│ // ``` let mut underline = None; let mut multi_labels_iter = multi_labels.iter().enumerate().peekable(); for label_column in 0..num_multi_labels { match multi_labels_iter.peek() { Some((i, (label_index, ls, label))) if *label_index == label_column => { match label { MultiLabel::Left => { self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; } MultiLabel::Top(..) if multi_label_index > *i => { self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; } MultiLabel::Bottom(..) if multi_label_index < *i => { self.label_multi_left(severity, *ls, underline.map(|(s, _)| s))?; } MultiLabel::Top(..) if multi_label_index == *i => { underline = Some((*ls, VerticalBound::Top)); self.label_multi_top_left(severity, label_style)? } MultiLabel::Bottom(..) if multi_label_index == *i => { underline = Some((*ls, VerticalBound::Bottom)); self.label_multi_bottom_left(severity, label_style)?; } MultiLabel::Top(..) | MultiLabel::Bottom(..) => { self.inner_gutter_column(severity, underline)?; } } multi_labels_iter.next(); } Some((_, _)) | None => self.inner_gutter_column(severity, underline)?, } } // Finish the top or bottom caret match bottom_message { None => self.label_multi_top_caret(severity, label_style, source, *range)?, Some(message) => { self.label_multi_bottom_caret(severity, label_style, source, *range, message)? } } } Ok(()) } /// An empty source line, for providing additional whitespace to source snippets. /// /// ```text /// │ │ │ /// ``` pub fn render_snippet_empty( &mut self, outer_padding: usize, severity: Severity, num_multi_labels: usize, multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], ) -> Result<(), Error> { self.outer_gutter(outer_padding)?; self.border_left()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; writeln!(self)?; Ok(()) } /// A broken source line, for labeling skipped sections of source. /// /// ```text /// · │ │ /// ``` pub fn render_snippet_break( &mut self, outer_padding: usize, severity: Severity, num_multi_labels: usize, multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], ) -> Result<(), Error> { self.outer_gutter(outer_padding)?; self.border_left_break()?; self.inner_gutter(severity, num_multi_labels, multi_labels)?; writeln!(self)?; Ok(()) } /// Additional notes. /// /// ```text /// = expected type `Int` /// found type `String` /// ``` pub fn render_snippet_note( &mut self, outer_padding: usize, message: &str, ) -> Result<(), Error> { for (note_line_index, line) in message.lines().enumerate() { self.outer_gutter(outer_padding)?; match note_line_index { 0 => { #[cfg(feature = "termcolor")] self.set_color(&self.styles().note_bullet)?; write!(self, "{}", self.chars().note_bullet)?; #[cfg(feature = "termcolor")] self.reset()?; } _ => write!(self, " ")?, } // Write line of message writeln!(self, " {}", line)?; } Ok(()) } /// Adds tab-stop aware unicode-width computations to an iterator over /// character indices. Assumes that the character indices begin at the start /// of the line. fn char_metrics( &self, char_indices: impl Iterator, ) -> impl Iterator { use unicode_width::UnicodeWidthChar; let tab_width = self.config.tab_width; let mut unicode_column = 0; char_indices.map(move |(byte_index, ch)| { let metrics = Metrics { byte_index, unicode_width: match (ch, tab_width) { ('\t', 0) => 0, // Guard divide-by-zero ('\t', _) => tab_width - (unicode_column % tab_width), (ch, _) => ch.width().unwrap_or(0), }, }; unicode_column += metrics.unicode_width; (metrics, ch) }) } /// Location focus. fn snippet_locus(&mut self, locus: &Locus) -> Result<(), Error> { write!( self, "{name}:{line_number}:{column_number}", name = locus.name, line_number = locus.location.line_number, column_number = locus.location.column_number, )?; Ok(()) } /// The outer gutter of a source line. fn outer_gutter(&mut self, outer_padding: usize) -> Result<(), Error> { write!(self, "{space: >width$} ", space = "", width = outer_padding)?; Ok(()) } /// The outer gutter of a source line, with line number. fn outer_gutter_number( &mut self, line_number: usize, outer_padding: usize, ) -> Result<(), Error> { #[cfg(feature = "termcolor")] self.set_color(&self.styles().line_number)?; write!( self, "{line_number: >width$}", line_number = line_number, width = outer_padding, )?; #[cfg(feature = "termcolor")] self.reset()?; write!(self, " ")?; Ok(()) } /// The left-hand border of a source line. fn border_left(&mut self) -> Result<(), Error> { #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().source_border_left)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// The broken left-hand border of a source line. fn border_left_break(&mut self) -> Result<(), Error> { #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().source_border_left_break)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// Write vertical lines pointing to carets. fn caret_pointers( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, max_label_start: usize, single_labels: &[SingleLabel<'_>], trailing_label: Option<(usize, &SingleLabel<'_>)>, char_indices: impl Iterator, ) -> Result<(), Error> { for (metrics, ch) in self.char_metrics(char_indices) { let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); let label_style = hanging_labels(single_labels, trailing_label) .filter(|(_, range, _)| column_range.contains(&range.start)) .map(|(label_style, _, _)| *label_style) .max_by_key(label_priority_key); let mut spaces = match label_style { None => 0..metrics.unicode_width, #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().pointer_left)?; #[cfg(feature = "termcolor")] self.reset()?; 1..metrics.unicode_width } }; // Only print padding if we are before the end of the last single line caret if metrics.byte_index <= max_label_start { spaces.try_for_each(|_| write!(self, " "))?; } } Ok(()) } /// The left of a multi-line label. /// /// ```text /// │ /// ``` fn label_multi_left( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, underline: Option, ) -> Result<(), Error> { match underline { None => write!(self, " ")?, // Continue an underline horizontally #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_top)?; #[cfg(feature = "termcolor")] self.reset()?; } } #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_left)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// The top-left of a multi-line label. /// /// ```text /// ╭ /// ``` fn label_multi_top_left( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, ) -> Result<(), Error> { write!(self, " ")?; #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_top_left)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// The bottom left of a multi-line label. /// /// ```text /// ╰ /// ``` fn label_multi_bottom_left( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, ) -> Result<(), Error> { write!(self, " ")?; #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_bottom_left)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// Multi-line label top. /// /// ```text /// ─────────────^ /// ``` fn label_multi_top_caret( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, label_style: LabelStyle, source: &str, start: usize, ) -> Result<(), Error> { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; for (metrics, _) in self .char_metrics(source.char_indices()) .take_while(|(metrics, _)| metrics.byte_index < start + 1) { // FIXME: improve rendering of carets between character boundaries (0..metrics.unicode_width) .try_for_each(|_| write!(self, "{}", self.chars().multi_top))?; } let caret_start = match label_style { LabelStyle::Primary => self.config.chars.multi_primary_caret_start, LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, }; write!(self, "{}", caret_start)?; #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; Ok(()) } /// Multi-line label bottom, with a message. /// /// ```text /// ─────────────^ expected `Int` but found `String` /// ``` fn label_multi_bottom_caret( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, label_style: LabelStyle, source: &str, start: usize, message: &str, ) -> Result<(), Error> { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; for (metrics, _) in self .char_metrics(source.char_indices()) .take_while(|(metrics, _)| metrics.byte_index < start) { // FIXME: improve rendering of carets between character boundaries (0..metrics.unicode_width) .try_for_each(|_| write!(self, "{}", self.chars().multi_bottom))?; } let caret_end = match label_style { LabelStyle::Primary => self.config.chars.multi_primary_caret_start, LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, }; write!(self, "{}", caret_end)?; if !message.is_empty() { write!(self, " {}", message)?; } #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; Ok(()) } /// Writes an empty gutter space, or continues an underline horizontally. fn inner_gutter_column( &mut self, #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, underline: Option, ) -> Result<(), Error> { match underline { None => self.inner_gutter_space(), #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some((label_style, vertical_bound)) => { #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; let ch = match vertical_bound { VerticalBound::Top => self.config.chars.multi_top, VerticalBound::Bottom => self.config.chars.multi_bottom, }; write!(self, "{0}{0}", ch)?; #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } } } /// Writes an empty gutter space. fn inner_gutter_space(&mut self) -> Result<(), Error> { write!(self, " ")?; Ok(()) } /// Writes an inner gutter, with the left lines if necessary. fn inner_gutter( &mut self, severity: Severity, num_multi_labels: usize, multi_labels: &[(usize, LabelStyle, MultiLabel<'_>)], ) -> Result<(), Error> { let mut multi_labels_iter = multi_labels.iter().peekable(); for label_column in 0..num_multi_labels { match multi_labels_iter.peek() { Some((label_index, ls, label)) if *label_index == label_column => match label { MultiLabel::Left | MultiLabel::Bottom(..) => { self.label_multi_left(severity, *ls, None)?; multi_labels_iter.next(); } MultiLabel::Top(..) => { self.inner_gutter_space()?; multi_labels_iter.next(); } }, Some((_, _, _)) | None => self.inner_gutter_space()?, } } Ok(()) } } #[cfg(not(feature = "std"))] impl Write for Renderer<'_, '_> { fn write_str(&mut self, s: &str) -> core::fmt::Result { self.writer.write_str(s) } fn write_char(&mut self, c: char) -> core::fmt::Result { self.writer.write_char(c) } fn write_fmt(&mut self, args: Arguments<'_>) -> core::fmt::Result { self.writer.write_fmt(args) } } #[cfg(feature = "std")] impl Write for Renderer<'_, '_> { fn write(&mut self, buf: &[u8]) -> io::Result { self.writer.write(buf) } fn flush(&mut self) -> io::Result<()> { self.writer.flush() } } #[cfg(feature = "termcolor")] impl WriteColor for Renderer<'_, '_> { fn supports_color(&self) -> bool { self.writer.supports_color() } fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { self.writer.set_color(spec) } fn reset(&mut self) -> io::Result<()> { self.writer.reset() } fn is_synchronous(&self) -> bool { self.writer.is_synchronous() } } struct Metrics { byte_index: usize, unicode_width: usize, } /// Check if two ranges overlap fn is_overlapping(range0: &Range, range1: &Range) -> bool { let start = core::cmp::max(range0.start, range1.start); let end = core::cmp::min(range0.end, range1.end); start < end } /// For prioritizing primary labels over secondary labels when rendering carets. fn label_priority_key(label_style: &LabelStyle) -> u8 { match label_style { LabelStyle::Secondary => 0, LabelStyle::Primary => 1, } } /// Return an iterator that yields the labels that require hanging messages /// rendered underneath them. fn hanging_labels<'labels, 'diagnostic>( single_labels: &'labels [SingleLabel<'diagnostic>], trailing_label: Option<(usize, &'labels SingleLabel<'diagnostic>)>, ) -> impl 'labels + DoubleEndedIterator> { single_labels .iter() .enumerate() .filter(|(_, (_, _, message))| !message.is_empty()) .filter(move |(i, _)| trailing_label.map_or(true, |(j, _)| *i != j)) .map(|(_, label)| label) } codespan-reporting-0.12.0/src/term/views.rs000064400000000000000000000474721046102023000167660ustar 00000000000000use alloc::{ string::{String, ToString}, vec, vec::Vec, }; use core::ops::Range; use crate::diagnostic::{Diagnostic, LabelStyle}; use crate::files::{Error, Files, Location}; use crate::term::renderer::{Locus, MultiLabel, Renderer, SingleLabel}; use crate::term::Config; /// Calculate the number of decimal digits in `n`. fn count_digits(n: usize) -> usize { n.ilog10() as usize + 1 } /// Output a richly formatted diagnostic, with source code previews. pub struct RichDiagnostic<'diagnostic, 'config, FileId> { diagnostic: &'diagnostic Diagnostic, config: &'config Config, } impl<'diagnostic, 'config, FileId> RichDiagnostic<'diagnostic, 'config, FileId> where FileId: Copy + PartialEq, { pub fn new( diagnostic: &'diagnostic Diagnostic, config: &'config Config, ) -> RichDiagnostic<'diagnostic, 'config, FileId> { RichDiagnostic { diagnostic, config } } pub fn render<'files>( &self, files: &'files (impl Files<'files, FileId = FileId> + ?Sized), renderer: &mut Renderer<'_, '_>, ) -> Result<(), Error> where FileId: 'files, { use alloc::collections::BTreeMap; struct LabeledFile<'diagnostic, FileId> { file_id: FileId, start: usize, name: String, location: Location, num_multi_labels: usize, lines: BTreeMap>, max_label_style: LabelStyle, } impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> { fn get_or_insert_line( &mut self, line_index: usize, line_range: Range, line_number: usize, ) -> &mut Line<'diagnostic> { self.lines.entry(line_index).or_insert_with(|| Line { range: line_range, number: line_number, single_labels: vec![], multi_labels: vec![], // This has to be false by default so we know if it must be rendered by another condition already. must_render: false, }) } } struct Line<'diagnostic> { number: usize, range: core::ops::Range, // TODO: How do we reuse these allocations? single_labels: Vec>, multi_labels: Vec<(usize, LabelStyle, MultiLabel<'diagnostic>)>, must_render: bool, } // TODO: Make this data structure external, to allow for allocation reuse let mut labeled_files = Vec::>::new(); // Keep track of the outer padding to use when rendering the // snippets of source code. let mut outer_padding = 0; // Group labels by file for label in &self.diagnostic.labels { let start_line_index = files.line_index(label.file_id, label.range.start)?; let start_line_number = files.line_number(label.file_id, start_line_index)?; let start_line_range = files.line_range(label.file_id, start_line_index)?; let end_line_index = files.line_index(label.file_id, label.range.end)?; let end_line_number = files.line_number(label.file_id, end_line_index)?; let end_line_range = files.line_range(label.file_id, end_line_index)?; outer_padding = core::cmp::max(outer_padding, count_digits(start_line_number)); outer_padding = core::cmp::max(outer_padding, count_digits(end_line_number)); // NOTE: This could be made more efficient by using an associative // data structure like a hashmap or B-tree, but we use a vector to // preserve the order that unique files appear in the list of labels. let labeled_file = match labeled_files .iter_mut() .find(|labeled_file| label.file_id == labeled_file.file_id) { Some(labeled_file) => { // another diagnostic also referenced this file if labeled_file.max_label_style > label.style || (labeled_file.max_label_style == label.style && labeled_file.start > label.range.start) { // this label has a higher style or has the same style but starts earlier labeled_file.start = label.range.start; labeled_file.location = files.location(label.file_id, label.range.start)?; labeled_file.max_label_style = label.style; } labeled_file } None => { // no other diagnostic referenced this file yet labeled_files.push(LabeledFile { file_id: label.file_id, start: label.range.start, name: files.name(label.file_id)?.to_string(), location: files.location(label.file_id, label.range.start)?, num_multi_labels: 0, lines: BTreeMap::new(), max_label_style: label.style, }); // this unwrap should never fail because we just pushed an element labeled_files .last_mut() .expect("just pushed an element that disappeared") } }; // insert context lines before label // start from 1 because 0 would be the start of the label itself for offset in 1..self.config.before_label_lines + 1 { let index = if let Some(index) = start_line_index.checked_sub(offset) { index } else { // we are going from smallest to largest offset, so if // the offset can not be subtracted from the start we // reached the first line break; }; if let Ok(range) = files.line_range(label.file_id, index) { let line = labeled_file.get_or_insert_line(index, range, start_line_number - offset); line.must_render = true; } else { break; } } // insert context lines after label // start from 1 because 0 would be the end of the label itself for offset in 1..self.config.after_label_lines + 1 { let index = end_line_index .checked_add(offset) .expect("line index too big"); if let Ok(range) = files.line_range(label.file_id, index) { let line = labeled_file.get_or_insert_line(index, range, end_line_number + offset); line.must_render = true; } else { break; } } if start_line_index == end_line_index { // Single line // // ```text // 2 │ (+ test "") // │ ^^ expected `Int` but found `String` // ``` let label_start = label.range.start - start_line_range.start; // Ensure that we print at least one caret, even when we // have a zero-length source range. let label_end = usize::max(label.range.end - start_line_range.start, label_start + 1); let line = labeled_file.get_or_insert_line( start_line_index, start_line_range, start_line_number, ); // Ensure that the single line labels are lexicographically // sorted by the range of source code that they cover. let index = match line.single_labels.binary_search_by(|(_, range, _)| { // `Range` doesn't implement `Ord`, so convert to `(usize, usize)` // to piggyback off its lexicographic comparison implementation. (range.start, range.end).cmp(&(label_start, label_end)) }) { // If the ranges are the same, order the labels in reverse // to how they were originally specified in the diagnostic. // This helps with printing in the renderer. Ok(index) | Err(index) => index, }; line.single_labels .insert(index, (label.style, label_start..label_end, &label.message)); // If this line is not rendered, the SingleLabel is not visible. line.must_render = true; } else { // Multiple lines // // ```text // 4 │ fizz₁ num = case (mod num 5) (mod num 3) of // │ ╭─────────────^ // 5 │ │ 0 0 => "FizzBuzz" // 6 │ │ 0 _ => "Fizz" // 7 │ │ _ 0 => "Buzz" // 8 │ │ _ _ => num // │ ╰──────────────^ `case` clauses have incompatible types // ``` let label_index = labeled_file.num_multi_labels; labeled_file.num_multi_labels += 1; // First labeled line let label_start = label.range.start - start_line_range.start; let start_line = labeled_file.get_or_insert_line( start_line_index, start_line_range.clone(), start_line_number, ); start_line.multi_labels.push(( label_index, label.style, MultiLabel::Top(label_start), )); // The first line has to be rendered so the start of the label is visible. start_line.must_render = true; // Marked lines // // ```text // 5 │ │ 0 0 => "FizzBuzz" // 6 │ │ 0 _ => "Fizz" // 7 │ │ _ 0 => "Buzz" // ``` for line_index in (start_line_index + 1)..end_line_index { let line_range = files.line_range(label.file_id, line_index)?; let line_number = files.line_number(label.file_id, line_index)?; outer_padding = core::cmp::max(outer_padding, count_digits(line_number)); let line = labeled_file.get_or_insert_line(line_index, line_range, line_number); line.multi_labels .push((label_index, label.style, MultiLabel::Left)); // The line should be rendered to match the configuration of how much context to show. line.must_render |= // Is this line part of the context after the start of the label? line_index - start_line_index <= self.config.start_context_lines || // Is this line part of the context before the end of the label? end_line_index - line_index <= self.config.end_context_lines; } // Last labeled line // // ```text // 8 │ │ _ _ => num // │ ╰──────────────^ `case` clauses have incompatible types // ``` let label_end = label.range.end - end_line_range.start; let end_line = labeled_file.get_or_insert_line( end_line_index, end_line_range, end_line_number, ); end_line.multi_labels.push(( label_index, label.style, MultiLabel::Bottom(label_end, &label.message), )); // The last line has to be rendered so the end of the label is visible. end_line.must_render = true; } } // Header and message // // ```text // error[E0001]: unexpected type in `+` application // ``` renderer.render_header( None, self.diagnostic.severity, self.diagnostic.code.as_deref(), self.diagnostic.message.as_str(), )?; // Source snippets // // ```text // ┌─ test:2:9 // │ // 2 │ (+ test "") // │ ^^ expected `Int` but found `String` // │ // ``` let mut labeled_files = labeled_files.into_iter().peekable(); while let Some(labeled_file) = labeled_files.next() { let source = files.source(labeled_file.file_id)?; let source = source.as_ref(); // Top left border and locus. // // ```text // ┌─ test:2:9 // ``` if !labeled_file.lines.is_empty() { renderer.render_snippet_start( outer_padding, &Locus { name: labeled_file.name, location: labeled_file.location, }, )?; renderer.render_snippet_empty( outer_padding, self.diagnostic.severity, labeled_file.num_multi_labels, &[], )?; } let mut lines = labeled_file .lines .iter() .filter(|(_, line)| line.must_render) .peekable(); while let Some((line_index, line)) = lines.next() { renderer.render_snippet_source( outer_padding, line.number, &source[line.range.clone()], self.diagnostic.severity, &line.single_labels, labeled_file.num_multi_labels, &line.multi_labels, )?; // Check to see if we need to render any intermediate stuff // before rendering the next line. if let Some((next_line_index, next_line)) = lines.peek() { match next_line_index.checked_sub(*line_index) { // Consecutive lines Some(1) => {} // One line between the current line and the next line Some(2) => { // Write a source line let file_id = labeled_file.file_id; // This line was not intended to be rendered initially. // To render the line right, we have to get back the original labels. let labels = labeled_file .lines .get(&(line_index + 1)) .map_or(&[][..], |line| &line.multi_labels[..]); renderer.render_snippet_source( outer_padding, files.line_number(file_id, line_index + 1)?, &source[files.line_range(file_id, line_index + 1)?], self.diagnostic.severity, &[], labeled_file.num_multi_labels, labels, )?; } // More than one line between the current line and the next line. Some(_) | None => { // Source break // // ```text // · // ``` renderer.render_snippet_break( outer_padding, self.diagnostic.severity, labeled_file.num_multi_labels, &next_line.multi_labels, )?; } } } } // Check to see if we should render a trailing border after the // final line of the snippet. if labeled_files.peek().is_none() && self.diagnostic.notes.is_empty() { // We don't render a border if we are at the final newline // without trailing notes, because it would end up looking too // spaced-out in combination with the final new line. } else { // Render the trailing snippet border. renderer.render_snippet_empty( outer_padding, self.diagnostic.severity, labeled_file.num_multi_labels, &[], )?; } } // Additional notes // // ```text // = expected type `Int` // found type `String` // ``` for note in &self.diagnostic.notes { renderer.render_snippet_note(outer_padding, note)?; } renderer.render_empty() } } /// Output a short diagnostic, with a line number, severity, and message. pub struct ShortDiagnostic<'diagnostic, FileId> { diagnostic: &'diagnostic Diagnostic, show_notes: bool, } impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId> where FileId: Copy + PartialEq, { pub fn new( diagnostic: &'diagnostic Diagnostic, show_notes: bool, ) -> ShortDiagnostic<'diagnostic, FileId> { ShortDiagnostic { diagnostic, show_notes, } } pub fn render<'files>( &self, files: &'files (impl Files<'files, FileId = FileId> + ?Sized), renderer: &mut Renderer<'_, '_>, ) -> Result<(), Error> where FileId: 'files, { // Located headers // // ```text // test:2:9: error[E0001]: unexpected type in `+` application // ``` let mut primary_labels_encountered = 0; let labels = self.diagnostic.labels.iter(); for label in labels.filter(|label| label.style == LabelStyle::Primary) { primary_labels_encountered += 1; renderer.render_header( Some(&Locus { name: files.name(label.file_id)?.to_string(), location: files.location(label.file_id, label.range.start)?, }), self.diagnostic.severity, self.diagnostic.code.as_deref(), self.diagnostic.message.as_str(), )?; } // Fallback to printing a non-located header if no primary labels were encountered // // ```text // error[E0002]: Bad config found // ``` if primary_labels_encountered == 0 { renderer.render_header( None, self.diagnostic.severity, self.diagnostic.code.as_deref(), self.diagnostic.message.as_str(), )?; } if self.show_notes { // Additional notes // // ```text // = expected type `Int` // found type `String` // ``` for note in &self.diagnostic.notes { renderer.render_snippet_note(0, note)?; } } Ok(()) } } codespan-reporting-0.12.0/src/term.rs000064400000000000000000000040761046102023000156220ustar 00000000000000//! Terminal back-end for emitting diagnostics. #[cfg(feature = "termcolor")] use termcolor::WriteColor; use crate::diagnostic::Diagnostic; use crate::files::Files; mod config; mod renderer; mod views; #[cfg(feature = "termcolor")] pub use termcolor; pub use self::config::{Chars, Config, DisplayStyle}; #[cfg(feature = "termcolor")] pub use self::config::Styles; /// Emit a diagnostic using the given writer, context, config, and files. /// /// The return value covers all error cases. These error case can arise if: /// * a file was removed from the file database. /// * a file was changed so that it is too small to have an index /// * IO fails pub fn emit<'files, F: Files<'files> + ?Sized>( #[cfg(feature = "termcolor")] writer: &mut dyn WriteColor, #[cfg(all(not(feature = "termcolor"), feature = "std"))] writer: &mut dyn std::io::Write, #[cfg(all(not(feature = "termcolor"), not(feature = "std")))] writer: &mut dyn core::fmt::Write, config: &Config, files: &'files F, diagnostic: &Diagnostic, ) -> Result<(), super::files::Error> { use self::renderer::Renderer; use self::views::{RichDiagnostic, ShortDiagnostic}; let mut renderer = Renderer::new(writer, config); match config.display_style { DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer), DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer), DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer), } } #[cfg(all(test, feature = "termcolor"))] mod tests { use alloc::{vec, vec::Vec}; use super::*; use crate::diagnostic::Label; use crate::files::SimpleFiles; #[test] fn unsized_emit() { let mut files = SimpleFiles::new(); let id = files.add("test", ""); let mut writer = termcolor::NoColor::new(Vec::::new()); let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]); emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap(); } } codespan-reporting-0.12.0/tests/snapshots/term__empty__medium_color.snap000064400000000000000000000005461046102023000250040ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}bug{bold bright}: {/} {fg:Red bold bright}error{bold bright}: {/} {fg:Yellow bold bright}warning{bold bright}: {/} {fg:Green bold bright}note{bold bright}: {/} {fg:Cyan bold bright}help{bold bright}: {/} {fg:Red bold bright}bug{bold bright}: {/} codespan-reporting-0.12.0/tests/snapshots/term__empty__medium_no_color.snap000064400000000000000000000002131046102023000254670ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- bug: error: warning: note: help: bug: codespan-reporting-0.12.0/tests/snapshots/term__empty__rich_ascii_no_color.snap000064400000000000000000000002211046102023000263030ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- bug: error: warning: note: help: bug: codespan-reporting-0.12.0/tests/snapshots/term__empty__rich_color.snap000064400000000000000000000005541046102023000244500ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}bug{bold bright}: {/} {fg:Red bold bright}error{bold bright}: {/} {fg:Yellow bold bright}warning{bold bright}: {/} {fg:Green bold bright}note{bold bright}: {/} {fg:Cyan bold bright}help{bold bright}: {/} {fg:Red bold bright}bug{bold bright}: {/} codespan-reporting-0.12.0/tests/snapshots/term__empty__rich_no_color.snap000064400000000000000000000002211046102023000251330ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- bug: error: warning: note: help: bug: codespan-reporting-0.12.0/tests/snapshots/term__empty__short_color.snap000064400000000000000000000005461046102023000246630ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}bug{bold bright}: {/} {fg:Red bold bright}error{bold bright}: {/} {fg:Yellow bold bright}warning{bold bright}: {/} {fg:Green bold bright}note{bold bright}: {/} {fg:Cyan bold bright}help{bold bright}: {/} {fg:Red bold bright}bug{bold bright}: {/} codespan-reporting-0.12.0/tests/snapshots/term__empty__short_no_color.snap000064400000000000000000000002131046102023000253460ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- bug: error: warning: note: help: bug: codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__medium_color.snap000064400000000000000000000005451046102023000263420ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- hello:1:7: {fg:Green bold bright}note{bold bright}: middle{/} hello:1:13: {fg:Green bold bright}note{bold bright}: end of line{/} hello:2:11: {fg:Green bold bright}note{bold bright}: end of line{/} hello:3:4: {fg:Green bold bright}note{bold bright}: end of file{/} codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__medium_no_color.snap000064400000000000000000000003201046102023000270250ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- hello:1:7: note: middle hello:1:13: note: end of line hello:2:11: note: end of line hello:3:4: note: end of file codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap000064400000000000000000000006231046102023000276500ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- note: middle --> hello:1:7 | 1 | Hello world! | ^ middle note: end of line --> hello:1:13 | 1 | Hello world! | ^ end of line note: end of line --> hello:2:11 | 2 | Bye world! | ^ end of line note: end of file --> hello:3:4 | 3 | | ^ end of file codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__rich_color.snap000064400000000000000000000016611046102023000260070ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Green bold bright}note{bold bright}: middle{/} {fg:Blue}┌─{/} hello:1:7 {fg:Blue}│{/} {fg:Blue}1{/} {fg:Blue}│{/} Hello {fg:Green}w{/}orld! {fg:Blue}│{/} {fg:Green}^{/} {fg:Green}middle{/} {fg:Green bold bright}note{bold bright}: end of line{/} {fg:Blue}┌─{/} hello:1:13 {fg:Blue}│{/} {fg:Blue}1{/} {fg:Blue}│{/} Hello world! {fg:Blue}│{/} {fg:Green}^{/} {fg:Green}end of line{/} {fg:Green bold bright}note{bold bright}: end of line{/} {fg:Blue}┌─{/} hello:2:11 {fg:Blue}│{/} {fg:Blue}2{/} {fg:Blue}│{/} Bye world! {fg:Blue}│{/} {fg:Green}^{/} {fg:Green}end of line{/} {fg:Green bold bright}note{bold bright}: end of file{/} {fg:Blue}┌─{/} hello:3:4 {fg:Blue}│{/} {fg:Blue}3{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Green}^{/} {fg:Green}end of file{/} codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__rich_no_color.snap000064400000000000000000000006671046102023000265100ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- note: middle ┌─ hello:1:7 │ 1 │ Hello world! │ ^ middle note: end of line ┌─ hello:1:13 │ 1 │ Hello world! │ ^ end of line note: end of line ┌─ hello:2:11 │ 2 │ Bye world! │ ^ end of line note: end of file ┌─ hello:3:4 │ 3 │ │ ^ end of file codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__short_color.snap000064400000000000000000000005451046102023000262210ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- hello:1:7: {fg:Green bold bright}note{bold bright}: middle{/} hello:1:13: {fg:Green bold bright}note{bold bright}: end of line{/} hello:2:11: {fg:Green bold bright}note{bold bright}: end of line{/} hello:3:4: {fg:Green bold bright}note{bold bright}: end of file{/} codespan-reporting-0.12.0/tests/snapshots/term__empty_ranges__short_no_color.snap000064400000000000000000000003201046102023000267040ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- hello:1:7: note: middle hello:1:13: note: end of line hello:2:11: note: end of line hello:3:4: note: end of file codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__medium_color.snap000064400000000000000000000006571046102023000257050ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- FizzBuzz.fun:8:12: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} {fg:Blue}={/} expected type `String` found type `Nat` FizzBuzz.fun:16:16: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} {fg:Blue}={/} expected type `String` found type `Nat` codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__medium_no_color.snap000064400000000000000000000005221046102023000263700ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- FizzBuzz.fun:8:12: error[E0308]: `case` clauses have incompatible types = expected type `String` found type `Nat` FizzBuzz.fun:16:16: error[E0308]: `case` clauses have incompatible types = expected type `String` found type `Nat` codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap000064400000000000000000000025131046102023000272070ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0308]: `case` clauses have incompatible types --> FizzBuzz.fun:8:12 | 3 | fizz₁ : Nat → String | ------ expected type `String` found here 4 | fizz₁ num = case (mod num 5) (mod num 3) of | /-------------' 5 | | 0 0 => "FizzBuzz" 6 | | 0 _ => "Fizz" 7 | | _ 0 => "Buzz" 8 | | _ _ => num | | ^^^ expected `String`, found `Nat` | \--------------' `case` clauses have incompatible types | = expected type `String` found type `Nat` error[E0308]: `case` clauses have incompatible types --> FizzBuzz.fun:16:16 | 10 | fizz₂ : Nat → String | ------ expected type `String` found here 11 | fizz₂ num = 12 | / case (mod num 5) (mod num 3) of 13 | | 0 0 => "FizzBuzz" | | ---------- this is found to be of type `String` 14 | | 0 _ => "Fizz" | | ------ this is found to be of type `String` 15 | | _ 0 => "Buzz" | | ------ this is found to be of type `String` 16 | | _ _ => num | | ^^^ expected `String`, found `Nat` | \------------------' `case` clauses have incompatible types | = expected type `String` found type `Nat` codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__rich_color.snap000064400000000000000000000050021046102023000253370ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} {fg:Blue}┌─{/} FizzBuzz.fun:8:12 {fg:Blue}│{/} {fg:Blue}3{/} {fg:Blue}│{/} fizz₁ : Nat → String {fg:Blue}│{/} {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/} {fg:Blue}4{/} {fg:Blue}│{/} fizz₁ num = case (mod num 5) (mod num 3) of {fg:Blue}│{/} {fg:Blue}╭{/}{fg:Blue}─────────────'{/} {fg:Blue}5{/} {fg:Blue}│{/} {fg:Blue}│{/} 0 0 => "FizzBuzz" {fg:Blue}6{/} {fg:Blue}│{/} {fg:Blue}│{/} 0 _ => "Fizz" {fg:Blue}7{/} {fg:Blue}│{/} {fg:Blue}│{/} _ 0 => "Buzz" {fg:Blue}8{/} {fg:Blue}│{/} {fg:Blue}│{/} _ _ => {fg:Red}num{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Red}^^^{/} {fg:Red}expected `String`, found `Nat`{/} {fg:Blue}│{/} {fg:Blue}╰{/}{fg:Blue}──────────────' `case` clauses have incompatible types{/} {fg:Blue}│{/} {fg:Blue}={/} expected type `String` found type `Nat` {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} {fg:Blue}┌─{/} FizzBuzz.fun:16:16 {fg:Blue}│{/} {fg:Blue}10{/} {fg:Blue}│{/} fizz₂ : Nat → String {fg:Blue}│{/} {fg:Blue}------{/} {fg:Blue}expected type `String` found here{/} {fg:Blue}11{/} {fg:Blue}│{/} fizz₂ num = {fg:Blue}12{/} {fg:Blue}│{/} {fg:Blue}╭{/} case (mod num 5) (mod num 3) of {fg:Blue}13{/} {fg:Blue}│{/} {fg:Blue}│{/} 0 0 => "FizzBuzz" {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}----------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}14{/} {fg:Blue}│{/} {fg:Blue}│{/} 0 _ => "Fizz" {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}15{/} {fg:Blue}│{/} {fg:Blue}│{/} _ 0 => "Buzz" {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}------{/} {fg:Blue}this is found to be of type `String`{/} {fg:Blue}16{/} {fg:Blue}│{/} {fg:Blue}│{/} _ _ => {fg:Red}num{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Red}^^^{/} {fg:Red}expected `String`, found `Nat`{/} {fg:Blue}│{/} {fg:Blue}╰{/}{fg:Blue}──────────────────' `case` clauses have incompatible types{/} {fg:Blue}│{/} {fg:Blue}={/} expected type `String` found type `Nat` codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__rich_no_color.snap000064400000000000000000000030031046102023000260320ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0308]: `case` clauses have incompatible types ┌─ FizzBuzz.fun:8:12 │ 3 │ fizz₁ : Nat → String │ ------ expected type `String` found here 4 │ fizz₁ num = case (mod num 5) (mod num 3) of │ ╭─────────────' 5 │ │ 0 0 => "FizzBuzz" 6 │ │ 0 _ => "Fizz" 7 │ │ _ 0 => "Buzz" 8 │ │ _ _ => num │ │ ^^^ expected `String`, found `Nat` │ ╰──────────────' `case` clauses have incompatible types │ = expected type `String` found type `Nat` error[E0308]: `case` clauses have incompatible types ┌─ FizzBuzz.fun:16:16 │ 10 │ fizz₂ : Nat → String │ ------ expected type `String` found here 11 │ fizz₂ num = 12 │ ╭ case (mod num 5) (mod num 3) of 13 │ │ 0 0 => "FizzBuzz" │ │ ---------- this is found to be of type `String` 14 │ │ 0 _ => "Fizz" │ │ ------ this is found to be of type `String` 15 │ │ _ 0 => "Buzz" │ │ ------ this is found to be of type `String` 16 │ │ _ _ => num │ │ ^^^ expected `String`, found `Nat` │ ╰──────────────────' `case` clauses have incompatible types │ = expected type `String` found type `Nat` codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__short_color.snap000064400000000000000000000004651046102023000255610ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- FizzBuzz.fun:8:12: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} FizzBuzz.fun:16:16: {fg:Red bold bright}error[E0308]{bold bright}: `case` clauses have incompatible types{/} codespan-reporting-0.12.0/tests/snapshots/term__fizz_buzz__short_no_color.snap000064400000000000000000000003601046102023000262470ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- FizzBuzz.fun:8:12: error[E0308]: `case` clauses have incompatible types FizzBuzz.fun:16:16: error[E0308]: `case` clauses have incompatible types codespan-reporting-0.12.0/tests/snapshots/term__message__medium_color.snap000064400000000000000000000004661046102023000252730ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Green bold bright}note{bold bright}: a message{/} {fg:Cyan bold bright}help{bold bright}: a message{/} codespan-reporting-0.12.0/tests/snapshots/term__message__medium_no_color.snap000064400000000000000000000002431046102023000257600ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message warning: a message note: a message help: a message codespan-reporting-0.12.0/tests/snapshots/term__message__rich_ascii_no_color.snap000064400000000000000000000002471046102023000266010ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message warning: a message note: a message help: a message codespan-reporting-0.12.0/tests/snapshots/term__message__rich_color.snap000064400000000000000000000004721046102023000247350ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Green bold bright}note{bold bright}: a message{/} {fg:Cyan bold bright}help{bold bright}: a message{/} codespan-reporting-0.12.0/tests/snapshots/term__message__rich_no_color.snap000064400000000000000000000002471046102023000254310ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message warning: a message note: a message help: a message codespan-reporting-0.12.0/tests/snapshots/term__message__short_color.snap000064400000000000000000000004661046102023000251520ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Green bold bright}note{bold bright}: a message{/} {fg:Cyan bold bright}help{bold bright}: a message{/} codespan-reporting-0.12.0/tests/snapshots/term__message__short_no_color.snap000064400000000000000000000002431046102023000256370ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message warning: a message note: a message help: a message codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__medium_color.snap000064400000000000000000000006161046102023000273220ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Green bold bright}note{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Cyan bold bright}help{bold bright}: a message{/} {fg:Blue}={/} a note codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__medium_no_color.snap000064400000000000000000000003131046102023000300100ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message = a note warning: a message = a note note: a message = a note help: a message = a note codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap000064400000000000000000000003171046102023000306310ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message = a note warning: a message = a note note: a message = a note help: a message = a note codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__rich_color.snap000064400000000000000000000006221046102023000267640ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Green bold bright}note{bold bright}: a message{/} {fg:Blue}={/} a note {fg:Cyan bold bright}help{bold bright}: a message{/} {fg:Blue}={/} a note codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__rich_no_color.snap000064400000000000000000000003171046102023000274610ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message = a note warning: a message = a note note: a message = a note help: a message = a note codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__short_color.snap000064400000000000000000000004661046102023000272040ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: a message{/} {fg:Yellow bold bright}warning{bold bright}: a message{/} {fg:Green bold bright}note{bold bright}: a message{/} {fg:Cyan bold bright}help{bold bright}: a message{/} codespan-reporting-0.12.0/tests/snapshots/term__message_and_notes__short_no_color.snap000064400000000000000000000002431046102023000276710ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: a message warning: a message note: a message help: a message codespan-reporting-0.12.0/tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap000064400000000000000000000005161046102023000306440ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0001]: a message warning[W001]: a message note[N0815]: a message help[H4711]: a message error: where did my errorcode go? warning: where did my errorcode go? note: where did my errorcode go? help: where did my errorcode go? codespan-reporting-0.12.0/tests/snapshots/term__message_errorcode__rich_no_color.snap000064400000000000000000000005161046102023000274740ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0001]: a message warning[W001]: a message note[N0815]: a message help[H4711]: a message error: where did my errorcode go? warning: where did my errorcode go? note: where did my errorcode go? help: where did my errorcode go? codespan-reporting-0.12.0/tests/snapshots/term__message_errorcode__short_no_color.snap000064400000000000000000000005061046102023000277050ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0001]: a message warning[W001]: a message note[N0815]: a message help[H4711]: a message error: where did my errorcode go? warning: where did my errorcode go? note: where did my errorcode go? help: where did my errorcode go? codespan-reporting-0.12.0/tests/snapshots/term__multifile__medium_color.snap000064400000000000000000000010661046102023000256360ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- Data/Nat.fun:7:13: {fg:Red bold bright}error{bold bright}: unknown builtin: `NATRAL`{/} {fg:Blue}={/} there is a builtin with a similar name: `NATURAL` Data/Nat.fun:17:16: {fg:Yellow bold bright}warning{bold bright}: unused parameter pattern: `n₂`{/} {fg:Blue}={/} consider using a wildcard pattern: `_` Test.fun:4:11: {fg:Red bold bright}error[E0001]{bold bright}: unexpected type in application of `_+_`{/} {fg:Blue}={/} expected type `Nat` found type `String` codespan-reporting-0.12.0/tests/snapshots/term__multifile__medium_no_color.snap000064400000000000000000000006461046102023000263350ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- Data/Nat.fun:7:13: error: unknown builtin: `NATRAL` = there is a builtin with a similar name: `NATURAL` Data/Nat.fun:17:16: warning: unused parameter pattern: `n₂` = consider using a wildcard pattern: `_` Test.fun:4:11: error[E0001]: unexpected type in application of `_+_` = expected type `Nat` found type `String` codespan-reporting-0.12.0/tests/snapshots/term__multifile__rich_ascii_no_color.snap000064400000000000000000000014661046102023000271530ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: unknown builtin: `NATRAL` --> Data/Nat.fun:7:13 | 7 | {-# BUILTIN NATRAL Nat #-} | ^^^^^^ unknown builtin | = there is a builtin with a similar name: `NATURAL` warning: unused parameter pattern: `n₂` --> Data/Nat.fun:17:16 | 17 | zero - succ n₂ = zero | ^^ unused parameter | = consider using a wildcard pattern: `_` error[E0001]: unexpected type in application of `_+_` --> Test.fun:4:11 | 4 | _ = 123 + "hello" | ^^^^^^^ expected `Nat`, found `String` | --> Data/Nat.fun:11:1 | 11 | _+_ : Nat → Nat → Nat | --------------------- based on the definition of `_+_` | = expected type `Nat` found type `String` codespan-reporting-0.12.0/tests/snapshots/term__multifile__rich_color.snap000064400000000000000000000026261046102023000253060ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: unknown builtin: `NATRAL`{/} {fg:Blue}┌─{/} Data/Nat.fun:7:13 {fg:Blue}│{/} {fg:Blue}7{/} {fg:Blue}│{/} {-# BUILTIN {fg:Red}NATRAL{/} Nat #-} {fg:Blue}│{/} {fg:Red}^^^^^^{/} {fg:Red}unknown builtin{/} {fg:Blue}│{/} {fg:Blue}={/} there is a builtin with a similar name: `NATURAL` {fg:Yellow bold bright}warning{bold bright}: unused parameter pattern: `n₂`{/} {fg:Blue}┌─{/} Data/Nat.fun:17:16 {fg:Blue}│{/} {fg:Blue}17{/} {fg:Blue}│{/} zero - succ {fg:Yellow}n₂{/} = zero {fg:Blue}│{/} {fg:Yellow}^^{/} {fg:Yellow}unused parameter{/} {fg:Blue}│{/} {fg:Blue}={/} consider using a wildcard pattern: `_` {fg:Red bold bright}error[E0001]{bold bright}: unexpected type in application of `_+_`{/} {fg:Blue}┌─{/} Test.fun:4:11 {fg:Blue}│{/} {fg:Blue} 4{/} {fg:Blue}│{/} _ = 123 + {fg:Red}"hello"{/} {fg:Blue}│{/} {fg:Red}^^^^^^^{/} {fg:Red}expected `Nat`, found `String`{/} {fg:Blue}│{/} {fg:Blue}┌─{/} Data/Nat.fun:11:1 {fg:Blue}│{/} {fg:Blue}11{/} {fg:Blue}│{/} _+_ : Nat → Nat → Nat {fg:Blue}│{/} {fg:Blue}---------------------{/} {fg:Blue}based on the definition of `_+_`{/} {fg:Blue}│{/} {fg:Blue}={/} expected type `Nat` found type `String` codespan-reporting-0.12.0/tests/snapshots/term__multifile__rich_no_color.snap000064400000000000000000000015421046102023000257760ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: unknown builtin: `NATRAL` ┌─ Data/Nat.fun:7:13 │ 7 │ {-# BUILTIN NATRAL Nat #-} │ ^^^^^^ unknown builtin │ = there is a builtin with a similar name: `NATURAL` warning: unused parameter pattern: `n₂` ┌─ Data/Nat.fun:17:16 │ 17 │ zero - succ n₂ = zero │ ^^ unused parameter │ = consider using a wildcard pattern: `_` error[E0001]: unexpected type in application of `_+_` ┌─ Test.fun:4:11 │ 4 │ _ = 123 + "hello" │ ^^^^^^^ expected `Nat`, found `String` │ ┌─ Data/Nat.fun:11:1 │ 11 │ _+_ : Nat → Nat → Nat │ --------------------- based on the definition of `_+_` │ = expected type `Nat` found type `String` codespan-reporting-0.12.0/tests/snapshots/term__multifile__short_color.snap000064400000000000000000000006021046102023000255100ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- Data/Nat.fun:7:13: {fg:Red bold bright}error{bold bright}: unknown builtin: `NATRAL`{/} Data/Nat.fun:17:16: {fg:Yellow bold bright}warning{bold bright}: unused parameter pattern: `n₂`{/} Test.fun:4:11: {fg:Red bold bright}error[E0001]{bold bright}: unexpected type in application of `_+_`{/} codespan-reporting-0.12.0/tests/snapshots/term__multifile__short_no_color.snap000064400000000000000000000004261046102023000262100ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- Data/Nat.fun:7:13: error: unknown builtin: `NATRAL` Data/Nat.fun:17:16: warning: unused parameter pattern: `n₂` Test.fun:4:11: error[E0001]: unexpected type in application of `_+_` codespan-reporting-0.12.0/tests/snapshots/term__multiline_omit__rich_no_color.snap000064400000000000000000000014271046102023000270400ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[empty_if]: empty elseif block ┌─ empty_if_comments.lua:1:1 │ 1 │ ╭ elseif 3 then 2 │ │ 3 │ │ ╭ 4 │ │ │ 5 │ │ │ · │ │ 8 │ │ │ 9 │ │ │ │ │ ╰' content should be in here 10 │ │ else │ ╰───^ error[E0308]: mismatched types ┌─ src/lib.rs:2:6 │ 2 │ 1 │ ╭─────^ 3 │ │ + 1 4 │ │ + 1 · │ 7 │ │ +1 │ │ - missing whitespace 8 │ │ + 1 9 │ │ + 1 10 │ │ + 1 │ ╰───────^ expected (), found integer │ = note: expected type `()` found type `{integer}` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__medium_color.snap000064400000000000000000000005061046102023000302520ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- codespan/src/file.rs:4:34: {fg:Red bold bright}error[E0308]{bold bright}: match arms have incompatible types{/} {fg:Blue}={/} expected type `Result` found type `LineIndexOutOfBoundsError` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__medium_no_color.snap000064400000000000000000000004311046102023000307430ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- codespan/src/file.rs:4:34: error[E0308]: match arms have incompatible types = expected type `Result` found type `LineIndexOutOfBoundsError` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap000064400000000000000000000023021046102023000315570ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0308]: match arms have incompatible types --> codespan/src/file.rs:4:34 | 1 | / match line_index.compare(self.last_line_index()) { 2 | | Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), | | --------------------------------------------- this is found to be of type `Result` 3 | | Ordering::Equal => Ok(self.source_span().end()), | | ---------------------------- this is found to be of type `Result` 4 | | Ordering::Greater => LineIndexOutOfBoundsError { | /-|----------------------------------^ 5 | | | given: line_index, 6 | | | max: self.last_line_index(), 7 | | | }, | \-|-------------^ expected enum `Result`, found struct `LineIndexOutOfBoundsError` 8 | | } | \---------' `match` arms have incompatible types | = expected type `Result` found type `LineIndexOutOfBoundsError` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__rich_color.snap000064400000000000000000000040571046102023000277240ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error[E0308]{bold bright}: match arms have incompatible types{/} {fg:Blue}┌─{/} codespan/src/file.rs:4:34 {fg:Blue}│{/} {fg:Blue}1{/} {fg:Blue}│{/} {fg:Blue}╭{/} match line_index.compare(self.last_line_index()) { {fg:Blue}2{/} {fg:Blue}│{/} {fg:Blue}│{/} Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}---------------------------------------------{/} {fg:Blue}this is found to be of type `Result`{/} {fg:Blue}3{/} {fg:Blue}│{/} {fg:Blue}│{/} Ordering::Equal => Ok(self.source_span().end()), {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}----------------------------{/} {fg:Blue}this is found to be of type `Result`{/} {fg:Blue}4{/} {fg:Blue}│{/} {fg:Blue}│{/} Ordering::Greater => {fg:Red}LineIndexOutOfBoundsError {{/} {fg:Blue}│{/} {fg:Red}╭{/}{fg:Red}─{/}{fg:Blue}│{/}{fg:Red}──────────────────────────────────^{/} {fg:Blue}5{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Red} given: line_index,{/} {fg:Blue}6{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Red} max: self.last_line_index(),{/} {fg:Blue}7{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Red} }{/}, {fg:Blue}│{/} {fg:Red}╰{/}{fg:Red}─{/}{fg:Blue}│{/}{fg:Red}─────────────^ expected enum `Result`, found struct `LineIndexOutOfBoundsError`{/} {fg:Blue}8{/} {fg:Blue}│{/} {fg:Blue}│{/} } {fg:Blue}│{/} {fg:Blue}╰{/}{fg:Blue}─────────' `match` arms have incompatible types{/} {fg:Blue}│{/} {fg:Blue}={/} expected type `Result` found type `LineIndexOutOfBoundsError` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__rich_no_color.snap000064400000000000000000000025731046102023000304210ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0308]: match arms have incompatible types ┌─ codespan/src/file.rs:4:34 │ 1 │ ╭ match line_index.compare(self.last_line_index()) { 2 │ │ Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]), │ │ --------------------------------------------- this is found to be of type `Result` 3 │ │ Ordering::Equal => Ok(self.source_span().end()), │ │ ---------------------------- this is found to be of type `Result` 4 │ │ Ordering::Greater => LineIndexOutOfBoundsError { │ ╭─│──────────────────────────────────^ 5 │ │ │ given: line_index, 6 │ │ │ max: self.last_line_index(), 7 │ │ │ }, │ ╰─│─────────────^ expected enum `Result`, found struct `LineIndexOutOfBoundsError` 8 │ │ } │ ╰─────────' `match` arms have incompatible types │ = expected type `Result` found type `LineIndexOutOfBoundsError` codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__short_color.snap000064400000000000000000000003141046102023000301260ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- codespan/src/file.rs:4:34: {fg:Red bold bright}error[E0308]{bold bright}: match arms have incompatible types{/} codespan-reporting-0.12.0/tests/snapshots/term__multiline_overlapping__short_no_color.snap000064400000000000000000000002531046102023000306240ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- codespan/src/file.rs:4:34: error[E0308]: match arms have incompatible types codespan-reporting-0.12.0/tests/snapshots/term__overlapping__medium_color.snap000064400000000000000000000027101046102023000261670ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- nested_impl_trait.rs:5:56: {fg:Red bold bright}error[E0666]{bold bright}: nested `impl Trait` is not allowed{/} typeck_type_placeholder_item.rs:1:18: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} typeck_type_placeholder_item.rs:2:25: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} typeck_type_placeholder_item.rs:2:28: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} no_send_res_ports.rs:25:5: {fg:Red bold bright}error[E0277]{bold bright}: `std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Blue}={/} help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` {fg:Blue}={/} note: required because it appears within the type `Port<()>` {fg:Blue}={/} note: required because it appears within the type `main::Foo` {fg:Blue}={/} note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` {fg:Red bold bright}error{bold bright}: aborting due 5 previous errors{/} {fg:Blue}={/} Some errors have detailed explanations: E0121, E0277, E0666. {fg:Blue}={/} For more information about an error, try `rustc --explain E0121`. codespan-reporting-0.12.0/tests/snapshots/term__overlapping__medium_no_color.snap000064400000000000000000000022531046102023000266650ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- nested_impl_trait.rs:5:56: error[E0666]: nested `impl Trait` is not allowed typeck_type_placeholder_item.rs:1:18: error[E0121]: the type placeholder `_` is not allowed within types on item signatures typeck_type_placeholder_item.rs:2:25: error[E0121]: the type placeholder `_` is not allowed within types on item signatures typeck_type_placeholder_item.rs:2:28: error[E0121]: the type placeholder `_` is not allowed within types on item signatures no_send_res_ports.rs:25:5: error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely = help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `Port<()>` = note: required because it appears within the type `main::Foo` = note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` error: aborting due 5 previous errors = Some errors have detailed explanations: E0121, E0277, E0666. = For more information about an error, try `rustc --explain E0121`. codespan-reporting-0.12.0/tests/snapshots/term__overlapping__rich_ascii_no_color.snap000064400000000000000000000046131046102023000275040ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0666]: nested `impl Trait` is not allowed --> nested_impl_trait.rs:5:56 | 5 | fn bad_in_ret_position(x: impl Into) -> impl Into { x } | ----------^^^^^^^^^^- | | | | | nested `impl Trait` here | outer `impl Trait` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> typeck_type_placeholder_item.rs:1:18 | 1 | fn fn_test1() -> _ { 5 } | ^ | | | not allowed in type signatures | help: replace with the correct return type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures --> typeck_type_placeholder_item.rs:2:25 | 2 | fn fn_test2(x: i32) -> (_, _) { (x, x) } | -^--^- | || | | || not allowed in type signatures | |not allowed in type signatures | help: replace with the correct return type: `(i32, i32)` error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely --> no_send_res_ports.rs:25:5 | 25 | thread::spawn(move|| { | ^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely | /-------------------' 26 | | let y = x; 27 | | println!("{:?}", y); 28 | | }); | \------' within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` | --> libstd/thread/mod.rs:5:8 | 5 | F: Send + 'static, | ---- required by this bound in `std::thread::spawn` | = help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `Port<()>` = note: required because it appears within the type `main::Foo` = note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` error: aborting due 5 previous errors = Some errors have detailed explanations: E0121, E0277, E0666. = For more information about an error, try `rustc --explain E0121`. codespan-reporting-0.12.0/tests/snapshots/term__overlapping__rich_color.snap000064400000000000000000000074211046102023000256400ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error[E0666]{bold bright}: nested `impl Trait` is not allowed{/} {fg:Blue}┌─{/} nested_impl_trait.rs:5:56 {fg:Blue}│{/} {fg:Blue}5{/} {fg:Blue}│{/} fn bad_in_ret_position(x: impl Into) -> impl Into<{fg:Red}impl Debug{/}> { x } {fg:Blue}│{/} {fg:Blue}----------{fg:Red}^^^^^^^^^^{fg:Blue}-{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Red}nested `impl Trait` here{/} {fg:Blue}│{/} {fg:Blue}outer `impl Trait`{/} {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} {fg:Blue}┌─{/} typeck_type_placeholder_item.rs:1:18 {fg:Blue}│{/} {fg:Blue}1{/} {fg:Blue}│{/} fn fn_test1() -> {fg:Red}_{/} { 5 } {fg:Blue}│{/} {fg:Red}^{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Red}not allowed in type signatures{/} {fg:Blue}│{/} {fg:Blue}help: replace with the correct return type: `i32`{/} {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} {fg:Blue}┌─{/} typeck_type_placeholder_item.rs:2:25 {fg:Blue}│{/} {fg:Blue}2{/} {fg:Blue}│{/} fn fn_test2(x: i32) -> ({fg:Red}_{/}, {fg:Red}_{/}) { (x, x) } {fg:Blue}│{/} {fg:Blue}-{fg:Red}^{fg:Blue}--{fg:Red}^{fg:Blue}-{/} {fg:Blue}│{/} {fg:Blue}│{/}{fg:Red}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Blue}│{/}{fg:Red}│{/} {fg:Red}not allowed in type signatures{/} {fg:Blue}│{/} {fg:Blue}│{/}{fg:Red}not allowed in type signatures{/} {fg:Blue}│{/} {fg:Blue}help: replace with the correct return type: `(i32, i32)`{/} {fg:Red bold bright}error[E0277]{bold bright}: `std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Blue}┌─{/} no_send_res_ports.rs:25:5 {fg:Blue}│{/} {fg:Blue}25{/} {fg:Blue}│{/} {fg:Red}thread::spawn{/}(move|| { {fg:Blue}│{/} {fg:Red}^^^^^^^^^^^^^{/} {fg:Red}`std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Blue}│{/} {fg:Blue}╭{/}{fg:Blue}───────────────────'{/} {fg:Blue}26{/} {fg:Blue}│{/} {fg:Blue}│{/} let y = x; {fg:Blue}27{/} {fg:Blue}│{/} {fg:Blue}│{/} println!("{:?}", y); {fg:Blue}28{/} {fg:Blue}│{/} {fg:Blue}│{/} }); {fg:Blue}│{/} {fg:Blue}╰{/}{fg:Blue}──────' within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`{/} {fg:Blue}│{/} {fg:Blue}┌─{/} libstd/thread/mod.rs:5:8 {fg:Blue}│{/} {fg:Blue} 5{/} {fg:Blue}│{/} F: Send + 'static, {fg:Blue}│{/} {fg:Blue}----{/} {fg:Blue}required by this bound in `std::thread::spawn`{/} {fg:Blue}│{/} {fg:Blue}={/} help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` {fg:Blue}={/} note: required because it appears within the type `Port<()>` {fg:Blue}={/} note: required because it appears within the type `main::Foo` {fg:Blue}={/} note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` {fg:Red bold bright}error{bold bright}: aborting due 5 previous errors{/} {fg:Blue}={/} Some errors have detailed explanations: E0121, E0277, E0666. {fg:Blue}={/} For more information about an error, try `rustc --explain E0121`. codespan-reporting-0.12.0/tests/snapshots/term__overlapping__rich_no_color.snap000064400000000000000000000050521046102023000263320ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0666]: nested `impl Trait` is not allowed ┌─ nested_impl_trait.rs:5:56 │ 5 │ fn bad_in_ret_position(x: impl Into) -> impl Into { x } │ ----------^^^^^^^^^^- │ │ │ │ │ nested `impl Trait` here │ outer `impl Trait` error[E0121]: the type placeholder `_` is not allowed within types on item signatures ┌─ typeck_type_placeholder_item.rs:1:18 │ 1 │ fn fn_test1() -> _ { 5 } │ ^ │ │ │ not allowed in type signatures │ help: replace with the correct return type: `i32` error[E0121]: the type placeholder `_` is not allowed within types on item signatures ┌─ typeck_type_placeholder_item.rs:2:25 │ 2 │ fn fn_test2(x: i32) -> (_, _) { (x, x) } │ -^--^- │ ││ │ │ ││ not allowed in type signatures │ │not allowed in type signatures │ help: replace with the correct return type: `(i32, i32)` error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely ┌─ no_send_res_ports.rs:25:5 │ 25 │ thread::spawn(move|| { │ ^^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely │ ╭───────────────────' 26 │ │ let y = x; 27 │ │ println!("{:?}", y); 28 │ │ }); │ ╰──────' within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` │ ┌─ libstd/thread/mod.rs:5:8 │ 5 │ F: Send + 'static, │ ---- required by this bound in `std::thread::spawn` │ = help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `Port<()>` = note: required because it appears within the type `main::Foo` = note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]` error: aborting due 5 previous errors = Some errors have detailed explanations: E0121, E0277, E0666. = For more information about an error, try `rustc --explain E0121`. codespan-reporting-0.12.0/tests/snapshots/term__overlapping__short_color.snap000064400000000000000000000015731046102023000260540ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- nested_impl_trait.rs:5:56: {fg:Red bold bright}error[E0666]{bold bright}: nested `impl Trait` is not allowed{/} typeck_type_placeholder_item.rs:1:18: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} typeck_type_placeholder_item.rs:2:25: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} typeck_type_placeholder_item.rs:2:28: {fg:Red bold bright}error[E0121]{bold bright}: the type placeholder `_` is not allowed within types on item signatures{/} no_send_res_ports.rs:25:5: {fg:Red bold bright}error[E0277]{bold bright}: `std::rc::Rc<()>` cannot be sent between threads safely{/} {fg:Red bold bright}error{bold bright}: aborting due 5 previous errors{/} codespan-reporting-0.12.0/tests/snapshots/term__overlapping__short_no_color.snap000064400000000000000000000012461046102023000265450ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- nested_impl_trait.rs:5:56: error[E0666]: nested `impl Trait` is not allowed typeck_type_placeholder_item.rs:1:18: error[E0121]: the type placeholder `_` is not allowed within types on item signatures typeck_type_placeholder_item.rs:2:25: error[E0121]: the type placeholder `_` is not allowed within types on item signatures typeck_type_placeholder_item.rs:2:28: error[E0121]: the type placeholder `_` is not allowed within types on item signatures no_send_res_ports.rs:25:5: error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely error: aborting due 5 previous errors codespan-reporting-0.12.0/tests/snapshots/term__position_indicator__medium_no_color.snap000064400000000000000000000004001046102023000302270ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- tests/main.js:4:3: warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode codespan-reporting-0.12.0/tests/snapshots/term__position_indicator__rich_ascii_no_color.snap000064400000000000000000000006441046102023000310560ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode --> tests/main.js:4:3 | 1 | "use strict"; | ------------ Strict mode is first declared here . 4 | "use strict"; | ^^^^^^^^^^^^ This strict mode declaration is redundant codespan-reporting-0.12.0/tests/snapshots/term__position_indicator__rich_no_color.snap000064400000000000000000000006621046102023000277060ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode ┌─ tests/main.js:4:3 │ 1 │ "use strict"; │ ------------ Strict mode is first declared here · 4 │ "use strict"; │ ^^^^^^^^^^^^ This strict mode declaration is redundant codespan-reporting-0.12.0/tests/snapshots/term__position_indicator__short_no_color.snap000064400000000000000000000003771046102023000301230ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- tests/main.js:4:3: warning[ParserWarning]: The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode codespan-reporting-0.12.0/tests/snapshots/term__same_line__medium_color.snap000064400000000000000000000005631046102023000256010ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- one_line.rs:3:12: {fg:Red bold bright}error[E0499]{bold bright}: cannot borrow `v` as mutable more than once at a time{/} {fg:Red bold bright}error{bold bright}: aborting due to previous error{/} {fg:Blue}={/} For more information about this error, try `rustc --explain E0499`. codespan-reporting-0.12.0/tests/snapshots/term__same_line__medium_no_color.snap000064400000000000000000000004421046102023000262710ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- one_line.rs:3:12: error[E0499]: cannot borrow `v` as mutable more than once at a time error: aborting due to previous error = For more information about this error, try `rustc --explain E0499`. codespan-reporting-0.12.0/tests/snapshots/term__same_line__rich_ascii_no_color.snap000064400000000000000000000007461046102023000271150ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0499]: cannot borrow `v` as mutable more than once at a time --> one_line.rs:3:12 | 3 | v.push(v.pop().unwrap()); | - ---- ^ second mutable borrow occurs here | | | | | first mutable borrow occurs here | first borrow later used by call error: aborting due to previous error = For more information about this error, try `rustc --explain E0499`. codespan-reporting-0.12.0/tests/snapshots/term__same_line__rich_color.snap000064400000000000000000000014411046102023000252420ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error[E0499]{bold bright}: cannot borrow `v` as mutable more than once at a time{/} {fg:Blue}┌─{/} one_line.rs:3:12 {fg:Blue}│{/} {fg:Blue}3{/} {fg:Blue}│{/} v.push({fg:Red}v{/}.pop().unwrap()); {fg:Blue}│{/} {fg:Blue}-{/} {fg:Blue}----{/} {fg:Red}^{/} {fg:Red}second mutable borrow occurs here{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}│{/} {fg:Blue}first mutable borrow occurs here{/} {fg:Blue}│{/} {fg:Blue}first borrow later used by call{/} {fg:Red bold bright}error{bold bright}: aborting due to previous error{/} {fg:Blue}={/} For more information about this error, try `rustc --explain E0499`. codespan-reporting-0.12.0/tests/snapshots/term__same_line__rich_no_color.snap000064400000000000000000000007731046102023000257450ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0499]: cannot borrow `v` as mutable more than once at a time ┌─ one_line.rs:3:12 │ 3 │ v.push(v.pop().unwrap()); │ - ---- ^ second mutable borrow occurs here │ │ │ │ │ first mutable borrow occurs here │ first borrow later used by call error: aborting due to previous error = For more information about this error, try `rustc --explain E0499`. codespan-reporting-0.12.0/tests/snapshots/term__same_line__short_color.snap000064400000000000000000000004401046102023000254520ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- one_line.rs:3:12: {fg:Red bold bright}error[E0499]{bold bright}: cannot borrow `v` as mutable more than once at a time{/} {fg:Red bold bright}error{bold bright}: aborting due to previous error{/} codespan-reporting-0.12.0/tests/snapshots/term__same_line__short_no_color.snap000064400000000000000000000003331046102023000261470ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- one_line.rs:3:12: error[E0499]: cannot borrow `v` as mutable more than once at a time error: aborting due to previous error codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__medium_color.snap000064400000000000000000000002511046102023000261230ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- same_range:1:5: {fg:Red bold bright}error{bold bright}: Unexpected token{/} codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__medium_no_color.snap000064400000000000000000000002101046102023000266120ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- same_range:1:5: error: Unexpected token codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__rich_ascii_no_color.snap000064400000000000000000000003351046102023000274370ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: Unexpected token --> same_range:1:5 | 1 | ::S { } | ^ | | | Unexpected '{' | Expected '(' codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__rich_color.snap000064400000000000000000000006471046102023000256010ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- {fg:Red bold bright}error{bold bright}: Unexpected token{/} {fg:Blue}┌─{/} same_range:1:5 {fg:Blue}│{/} {fg:Blue}1{/} {fg:Blue}│{/} ::S {fg:Red}{{/} } {fg:Blue}│{/} {fg:Red}^{/} {fg:Blue}│{/} {fg:Red}│{/} {fg:Blue}│{/} {fg:Red}Unexpected '{'{/} {fg:Blue}│{/} {fg:Blue}Expected '('{/} codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__rich_no_color.snap000064400000000000000000000003561046102023000262720ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: Unexpected token ┌─ same_range:1:5 │ 1 │ ::S { } │ ^ │ │ │ Unexpected '{' │ Expected '(' codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__short_color.snap000064400000000000000000000002501046102023000260010ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_color(&config) --- same_range:1:5: {fg:Red bold bright}error{bold bright}: Unexpected token{/} codespan-reporting-0.12.0/tests/snapshots/term__same_ranges__short_no_color.snap000064400000000000000000000002071046102023000264770ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- same_range:1:5: error: Unexpected token codespan-reporting-0.12.0/tests/snapshots/term__surrounding_lines__rich_no_color.snap000064400000000000000000000012421046102023000275520ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error: Unknown attribute macro ┌─ surroundingLines.fun:1:3 │ 1 │ #[foo] │ ^^^ No attribute macro `foo` known 2 │ fn main() { error: Missing argument for format ┌─ surroundingLines.fun:5:9 │ 2 │ fn main() { 3 │ println!( 4 │ "{}", │ -- Unable to use `{}`-directive to display `Foo` 5 │ Foo │ ^^^ No instance of std::fmt::Display exists for type Foo 6 │ ); error: Syntax error ┌─ surroundingLines.fun:9:11 │ 7 │ } 8 │ 9 │ struct Foo │ ^ Missing a semicolon codespan-reporting-0.12.0/tests/snapshots/term__tab_columns__tab_width_2_no_color.snap000064400000000000000000000006431046102023000275540ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ codespan-reporting-0.12.0/tests/snapshots/term__tab_columns__tab_width_3_no_color.snap000064400000000000000000000006531046102023000275560ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ codespan-reporting-0.12.0/tests/snapshots/term__tab_columns__tab_width_6_no_color.snap000064400000000000000000000007031046102023000275550ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ codespan-reporting-0.12.0/tests/snapshots/term__tab_columns__tab_width_default_no_color.snap000064400000000000000000000006631046102023000310410ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: tab test ┌─ tab_columns:1:2 │ 1 │ hello │ ^^^^^ 2 │ ∙ hello │ ^^^^^ 3 │ ∙∙ hello │ ^^^^^ 4 │ ∙∙∙ hello │ ^^^^^ 5 │ ∙∙∙∙ hello │ ^^^^^ 6 │ ∙∙∙∙∙ hello │ ^^^^^ 7 │ ∙∙∙∙∙∙ hello │ ^^^^^ codespan-reporting-0.12.0/tests/snapshots/term__tabbed__tab_width_3_no_color.snap000064400000000000000000000007671046102023000264770ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: unknown weapon `DogJaw` ┌─ tabbed:3:11 │ 3 │ Weapon: DogJaw │ ^^^^^^ the weapon warning: unknown condition `attack-cooldown` ┌─ tabbed:4:23 │ 4 │ ReloadingCondition: attack-cooldown │ ^^^^^^^^^^^^^^^ the condition warning: unknown field `Foo` ┌─ tabbed:5:2 │ 5 │ Foo: Bar │ ^^^ the field codespan-reporting-0.12.0/tests/snapshots/term__tabbed__tab_width_6_no_color.snap000064400000000000000000000010331046102023000264650ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: unknown weapon `DogJaw` ┌─ tabbed:3:11 │ 3 │ Weapon: DogJaw │ ^^^^^^ the weapon warning: unknown condition `attack-cooldown` ┌─ tabbed:4:23 │ 4 │ ReloadingCondition: attack-cooldown │ ^^^^^^^^^^^^^^^ the condition warning: unknown field `Foo` ┌─ tabbed:5:2 │ 5 │ Foo: Bar │ ^^^ the field codespan-reporting-0.12.0/tests/snapshots/term__tabbed__tab_width_default_no_color.snap000064400000000000000000000007771046102023000277620ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- warning: unknown weapon `DogJaw` ┌─ tabbed:3:11 │ 3 │ Weapon: DogJaw │ ^^^^^^ the weapon warning: unknown condition `attack-cooldown` ┌─ tabbed:4:23 │ 4 │ ReloadingCondition: attack-cooldown │ ^^^^^^^^^^^^^^^ the condition warning: unknown field `Foo` ┌─ tabbed:5:2 │ 5 │ Foo: Bar │ ^^^ the field codespan-reporting-0.12.0/tests/snapshots/term__unicode__medium_no_color.snap000064400000000000000000000011541046102023000257640ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- unicode.rs:1:8: error[E0703]: invalid ABI: found `路濫狼á́́` = valid ABIs: - aapcs - amdgpu-kernel - C - cdecl - efiapi - fastcall - msp430-interrupt - platform-intrinsic - ptx-kernel - Rust - rust-call - rust-intrinsic - stdcall - system - sysv64 - thiscall - unadjusted - vectorcall - win64 - x86-interrupt error: aborting due to previous error = For more information about this error, try `rustc --explain E0703`. codespan-reporting-0.12.0/tests/snapshots/term__unicode__rich_no_color.snap000064400000000000000000000013451046102023000254330ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E0703]: invalid ABI: found `路濫狼á́́` ┌─ unicode.rs:1:8 │ 1 │ extern "路濫狼á́́" fn foo() {} │ ^^^^^^^^^ invalid ABI │ = valid ABIs: - aapcs - amdgpu-kernel - C - cdecl - efiapi - fastcall - msp430-interrupt - platform-intrinsic - ptx-kernel - Rust - rust-call - rust-intrinsic - stdcall - system - sysv64 - thiscall - unadjusted - vectorcall - win64 - x86-interrupt error: aborting due to previous error = For more information about this error, try `rustc --explain E0703`. codespan-reporting-0.12.0/tests/snapshots/term__unicode__short_no_color.snap000064400000000000000000000003101046102023000256340ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- unicode.rs:1:8: error[E0703]: invalid ABI: found `路濫狼á́́` error: aborting due to previous error codespan-reporting-0.12.0/tests/snapshots/term__unicode_spans__medium_no_color.snap000064400000000000000000000003631046102023000271710ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- moon_jump.rs:1:1: error[E01]: cow may not jump during new moon. note: invalid unicode range note: invalid unicode range note: invalid unicode range codespan-reporting-0.12.0/tests/snapshots/term__unicode_spans__rich_no_color.snap000064400000000000000000000014731046102023000266410ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- error[E01]: cow may not jump during new moon. ┌─ moon_jump.rs:1:1 │ 1 │ 🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄 │ ^^ Invalid jump note: invalid unicode range ┌─ moon_jump.rs:1:1 │ 1 │ 🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄 │ -- Cow range does not start at boundary. note: invalid unicode range ┌─ moon_jump.rs:1:3 │ 1 │ 🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄 │ -- Cow range does not end at boundary. note: invalid unicode range ┌─ moon_jump.rs:1:1 │ 1 │ 🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄 │ ------ Cow does not start or end at boundary. codespan-reporting-0.12.0/tests/snapshots/term__unicode_spans__short_no_color.snap000064400000000000000000000003631046102023000270500ustar 00000000000000--- source: codespan-reporting/tests/term.rs expression: TEST_DATA.emit_no_color(&config) --- moon_jump.rs:1:1: error[E01]: cow may not jump during new moon. note: invalid unicode range note: invalid unicode range note: invalid unicode range codespan-reporting-0.12.0/tests/support/color_buffer.rs000064400000000000000000000066521046102023000214130ustar 00000000000000use std::io; use std::io::prelude::*; use termcolor::{ColorSpec, WriteColor}; // Color tester from: // https://github.com/wycats/language-reporting/blob/b021c87e0d4916b5f32756151bf215c220eee52d/crates/render-tree/src/stylesheet/accumulator.rs /// A facility for creating visually inspectable representations of colored output /// so they can be easily tested. /// /// A new color is represented as `{style}` and a reset is represented by `{/}`. /// /// Attributes are printed in this order: /// /// - Foreground color as `fg:Color` /// - Background color as `bg:Color` /// - Bold as `bold` /// - Underline as `underline` /// - Intense as `bright` /// /// For example, the style "intense, bold red foreground" would be printed as: /// /// ```text /// {fg:Red bold intense} /// ``` /// /// Since this implementation attempts to make it possible to faithfully /// understand what real WriteColor implementations would do, it tries /// to approximate the contract in the WriteColor trait: "Subsequent /// writes to this write will use these settings until either reset is /// called or new color settings are set.") /// /// - If set_color is called with a style, `{...}` is emitted containing the /// color attributes. /// - If set_color is called with no style, `{/}` is emitted /// - If reset is called, `{/}` is emitted. pub struct ColorBuffer { buf: Vec, color: ColorSpec, } impl ColorBuffer { pub fn new() -> ColorBuffer { ColorBuffer { buf: Vec::new(), color: ColorSpec::new(), } } pub fn into_string(self) -> String { String::from_utf8(self.buf).unwrap() } } impl io::Write for ColorBuffer { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf.extend(buf); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl WriteColor for ColorBuffer { 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 { self.color = spec.clone(); } if spec.is_none() { write!(self, "{{/}}")?; return Ok(()); } else { write!(self, "{{")?; } let mut first = true; fn write_first(first: bool, write: &mut ColorBuffer) -> io::Result { if !first { write!(write, " ")?; } Ok(false) } if let Some(fg) = spec.fg() { first = write_first(first, self)?; write!(self, "fg:{:?}", fg)?; } if let Some(bg) = spec.bg() { first = write_first(first, self)?; write!(self, "bg:{:?}", bg)?; } 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, "{{/}}")?; self.color = ColorSpec::new(); } Ok(()) } } codespan-reporting-0.12.0/tests/support/mod.rs000064400000000000000000000017011046102023000175110ustar 00000000000000use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::Files; use codespan_reporting::term::{emit, Config}; use termcolor::{Buffer, WriteColor}; mod color_buffer; use self::color_buffer::ColorBuffer; pub struct TestData<'files, F: Files<'files>> { pub files: F, pub diagnostics: Vec>, } impl<'files, F: Files<'files>> TestData<'files, F> { fn emit(&'files self, mut writer: W, config: &Config) -> W { for diagnostic in &self.diagnostics { emit(&mut writer, config, &self.files, diagnostic).unwrap(); } writer } pub fn emit_color(&'files self, config: &Config) -> String { self.emit(ColorBuffer::new(), config).into_string() } pub fn emit_no_color(&'files self, config: &Config) -> String { let buffer = self.emit(Buffer::no_color(), config); String::from_utf8_lossy(buffer.as_slice()).into_owned() } } codespan-reporting-0.12.0/tests/term.rs000064400000000000000000001165641046102023000162030ustar 00000000000000use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::{SimpleFile, SimpleFiles}; use codespan_reporting::term::{termcolor::Color, Chars, Config, DisplayStyle, Styles}; mod support; use self::support::TestData; lazy_static::lazy_static! { static ref TEST_CONFIG: Config = Config { // Always use blue so tests are consistent across platforms styles: Styles::with_blue(Color::Blue), ..Config::default() }; } macro_rules! test_emit { (rich_color) => { #[test] fn rich_color() { let config = Config { display_style: DisplayStyle::Rich, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_color(&config)); } }; (medium_color) => { #[test] fn medium_color() { let config = Config { display_style: DisplayStyle::Medium, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_color(&config)); } }; (short_color) => { #[test] fn short_color() { let config = Config { display_style: DisplayStyle::Short, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_color(&config)); } }; (rich_no_color) => { #[test] fn rich_no_color() { let config = Config { display_style: DisplayStyle::Rich, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } }; (medium_no_color) => { #[test] fn medium_no_color() { let config = Config { display_style: DisplayStyle::Medium, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } }; (short_no_color) => { #[test] fn short_no_color() { let config = Config { display_style: DisplayStyle::Short, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } }; (rich_ascii_no_color) => { #[test] fn rich_ascii_no_color() { let config = Config { display_style: DisplayStyle::Rich, chars: Chars::ascii(), ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } }; } mod empty { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { let files = SimpleFiles::new(); let diagnostics = vec![ Diagnostic::bug(), Diagnostic::error(), Diagnostic::warning(), Diagnostic::note(), Diagnostic::help(), Diagnostic::bug(), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } /// Based on: /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/codemap_tests/one_line.stderr mod same_line { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id1 = files.add( "one_line.rs", unindent::unindent(r#" fn main() { let mut v = vec![Some("foo"), Some("bar")]; v.push(v.pop().unwrap()); } "#), ); let diagnostics = vec![ Diagnostic::error() .with_code("E0499") .with_message("cannot borrow `v` as mutable more than once at a time") .with_labels(vec![ Label::primary(file_id1, 71..72) .with_message("second mutable borrow occurs here"), Label::secondary(file_id1, 64..65) .with_message("first borrow later used by call"), Label::secondary(file_id1, 66..70) .with_message("first mutable borrow occurs here"), ]), Diagnostic::error() .with_message("aborting due to previous error") .with_notes(vec![ "For more information about this error, try `rustc --explain E0499`.".to_owned(), ]), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } /// Based on: /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/nested_impl_trait.stderr /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/typeck/typeck_type_placeholder_item.stderr /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/no_send_res_ports.stderr mod overlapping { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id1 = files.add( "nested_impl_trait.rs", unindent::unindent(r#" use std::fmt::Debug; fn fine(x: impl Into) -> impl Into { x } fn bad_in_ret_position(x: impl Into) -> impl Into { x } "#), ); let file_id2 = files.add( "typeck_type_placeholder_item.rs", unindent::unindent(r#" fn fn_test1() -> _ { 5 } fn fn_test2(x: i32) -> (_, _) { (x, x) } "#), ); let file_id3 = files.add( "libstd/thread/mod.rs", unindent::unindent(r#" #[stable(feature = "rust1", since = "1.0.0")] pub fn spawn(self, f: F) -> io::Result> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static, { unsafe { self.spawn_unchecked(f) } } "#), ); let file_id4 = files.add( "no_send_res_ports.rs", unindent::unindent(r#" use std::thread; use std::rc::Rc; #[derive(Debug)] struct Port(Rc); fn main() { #[derive(Debug)] struct Foo { _x: Port<()>, } impl Drop for Foo { fn drop(&mut self) {} } fn foo(x: Port<()>) -> Foo { Foo { _x: x } } let x = foo(Port(Rc::new(()))); thread::spawn(move|| { let y = x; println!("{:?}", y); }); } "#), ); let diagnostics = vec![ Diagnostic::error() .with_code("E0666") .with_message("nested `impl Trait` is not allowed") .with_labels(vec![ Label::primary(file_id1, 129..139) .with_message("nested `impl Trait` here"), Label::secondary(file_id1, 119..140) .with_message("outer `impl Trait`"), ]), Diagnostic::error() .with_code("E0121") .with_message("the type placeholder `_` is not allowed within types on item signatures") .with_labels(vec![ Label::primary(file_id2, 17..18) .with_message("not allowed in type signatures"), Label::secondary(file_id2, 17..18) .with_message("help: replace with the correct return type: `i32`"), ]), Diagnostic::error() .with_code("E0121") .with_message("the type placeholder `_` is not allowed within types on item signatures") .with_labels(vec![ Label::primary(file_id2, 49..50) .with_message("not allowed in type signatures"), Label::primary(file_id2, 52..53) .with_message("not allowed in type signatures"), Label::secondary(file_id2, 48..54) .with_message("help: replace with the correct return type: `(i32, i32)`"), ]), Diagnostic::error() .with_code("E0277") .with_message("`std::rc::Rc<()>` cannot be sent between threads safely") .with_labels(vec![ Label::primary(file_id4, 339..352) .with_message("`std::rc::Rc<()>` cannot be sent between threads safely"), Label::secondary(file_id4, 353..416) .with_message("within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`"), Label::secondary(file_id3, 141..145) .with_message("required by this bound in `std::thread::spawn`"), ]) .with_notes(vec![ "help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`".to_owned(), "note: required because it appears within the type `Port<()>`".to_owned(), "note: required because it appears within the type `main::Foo`".to_owned(), "note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`".to_owned(), ]), Diagnostic::error() .with_message("aborting due 5 previous errors") .with_notes(vec![ "Some errors have detailed explanations: E0121, E0277, E0666.".to_owned(), "For more information about an error, try `rustc --explain E0121`.".to_owned(), ]), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod message { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { let files = SimpleFiles::new(); let diagnostics = vec![ Diagnostic::error().with_message("a message"), Diagnostic::warning().with_message("a message"), Diagnostic::note().with_message("a message"), Diagnostic::help().with_message("a message"), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod message_and_notes { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { let files = SimpleFiles::new(); let diagnostics = vec![ Diagnostic::error().with_message("a message").with_notes(vec!["a note".to_owned()]), Diagnostic::warning().with_message("a message").with_notes(vec!["a note".to_owned()]), Diagnostic::note().with_message("a message").with_notes(vec!["a note".to_owned()]), Diagnostic::help().with_message("a message").with_notes(vec!["a note".to_owned()]), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod message_errorcode { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { let files = SimpleFiles::new(); let diagnostics = vec![ Diagnostic::error().with_message("a message").with_code("E0001"), Diagnostic::warning().with_message("a message").with_code("W001"), Diagnostic::note().with_message("a message").with_code("N0815"), Diagnostic::help().with_message("a message").with_code("H4711"), Diagnostic::error().with_message("where did my errorcode go?").with_code(""), Diagnostic::warning().with_message("where did my errorcode go?").with_code(""), Diagnostic::note().with_message("where did my errorcode go?").with_code(""), Diagnostic::help().with_message("where did my errorcode go?").with_code(""), ]; TestData { files, diagnostics } }; } test_emit!(rich_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod empty_ranges { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = { let file = SimpleFile::new("hello", "Hello world!\nBye world!\n "); let eof = file.source().len(); let diagnostics = vec![ Diagnostic::note() .with_message("middle") .with_labels(vec![Label::primary((), 6..6).with_message("middle")]), Diagnostic::note() .with_message("end of line") .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]), Diagnostic::note() .with_message("end of line") .with_labels(vec![Label::primary((), 23..23).with_message("end of line")]), Diagnostic::note() .with_message("end of file") .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]), ]; TestData { files: file, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod same_ranges { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = { let file = SimpleFile::new("same_range", "::S { }"); let diagnostics = vec![ Diagnostic::error() .with_message("Unexpected token") .with_labels(vec![ Label::primary((), 4..4).with_message("Unexpected '{'"), Label::secondary((), 4..4).with_message("Expected '('"), ]), ]; TestData { files: file, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod multifile { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { 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 diagnostics = vec![ // 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` ", )]), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod fizz_buzz { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id = 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 = vec![ // Incompatible match clause error Diagnostic::error() .with_message("`case` clauses have incompatible types") .with_code("E0308") .with_labels(vec![ Label::primary(file_id, 163..166).with_message("expected `String`, found `Nat`"), Label::secondary(file_id, 62..166).with_message("`case` clauses have incompatible types"), Label::secondary(file_id, 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_id, 328..331).with_message("expected `String`, found `Nat`"), Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"), Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"), Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"), Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"), Label::secondary(file_id, 186..192).with_message("expected type `String` found here"), ]) .with_notes(vec![unindent::unindent( " expected type `String` found type `Nat` ", )]), ]; TestData { files, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod multiline_overlapping { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { let file = SimpleFile::new( "codespan/src/file.rs", [ " match line_index.compare(self.last_line_index()) {", " Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),", " Ordering::Equal => Ok(self.source_span().end()),", " Ordering::Greater => LineIndexOutOfBoundsError {", " given: line_index,", " max: self.last_line_index(),", " },", " }", ].join("\n"), ); let diagnostics = vec![ Diagnostic::error() .with_message("match arms have incompatible types") .with_code("E0308") .with_labels(vec![ // this secondary label is before the primary label to test the locus calculation (see issue #259) Label::secondary((), 89..134).with_message("this is found to be of type `Result`"), Label::primary((), 230..351).with_message("expected enum `Result`, found struct `LineIndexOutOfBoundsError`"), Label::secondary((), 8..362).with_message("`match` arms have incompatible types"), Label::secondary((), 167..195).with_message("this is found to be of type `Result`"), ]) .with_notes(vec![unindent::unindent( " expected type `Result` found type `LineIndexOutOfBoundsError` ", )]), ]; TestData { files: file, diagnostics } }; } test_emit!(rich_color); test_emit!(medium_color); test_emit!(short_color); test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod tabbed { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id = files.add( "tabbed", [ "Entity:", "\tArmament:", "\t\tWeapon: DogJaw", "\t\tReloadingCondition:\tattack-cooldown", "\tFoo: Bar", ] .join("\n"), ); let diagnostics = vec![ Diagnostic::warning() .with_message("unknown weapon `DogJaw`") .with_labels(vec![Label::primary(file_id, 29..35).with_message("the weapon")]), Diagnostic::warning() .with_message("unknown condition `attack-cooldown`") .with_labels(vec![Label::primary(file_id, 58..73).with_message("the condition")]), Diagnostic::warning() .with_message("unknown field `Foo`") .with_labels(vec![Label::primary(file_id, 75..78).with_message("the field")]), ]; TestData { files, diagnostics } }; } #[test] fn tab_width_default_no_color() { let config = TEST_CONFIG.clone(); insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } #[test] fn tab_width_3_no_color() { let config = Config { tab_width: 3, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } #[test] fn tab_width_6_no_color() { let config = Config { tab_width: 6, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } } mod tab_columns { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let source = [ "\thello", "∙\thello", "∙∙\thello", "∙∙∙\thello", "∙∙∙∙\thello", "∙∙∙∙∙\thello", "∙∙∙∙∙∙\thello", ].join("\n"); let hello_ranges = source .match_indices("hello") .map(|(start, hello)| start..(start+hello.len())) .collect::>(); let file_id = files.add("tab_columns", source); let diagnostics = vec![ Diagnostic::warning() .with_message("tab test") .with_labels( hello_ranges .into_iter() .map(|range| Label::primary(file_id, range)) .collect(), ), ]; TestData { files, diagnostics } }; } #[test] fn tab_width_default_no_color() { let config = TEST_CONFIG.clone(); insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } #[test] fn tab_width_2_no_color() { let config = Config { tab_width: 2, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } #[test] fn tab_width_3_no_color() { let config = Config { tab_width: 3, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } #[test] fn tab_width_6_no_color() { let config = Config { tab_width: 6, ..TEST_CONFIG.clone() }; insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); } } /// Based on: /// - https://github.com/TheSamsa/rust/blob/75cf41afb468152611212271bae026948cd3ba46/src/test/ui/codemap_tests/unicode.stderr mod unicode { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { let prefix = r#"extern "#; let abi = r#""路濫狼á́́""#; let suffix = r#" fn foo() {}"#; let file = SimpleFile::new( "unicode.rs", format!("{}{}{}", prefix, abi, suffix), ); let diagnostics = vec![ Diagnostic::error() .with_code("E0703") .with_message("invalid ABI: found `路濫狼á́́`") .with_labels(vec![ Label::primary((), prefix.len()..(prefix.len() + abi.len())) .with_message("invalid ABI"), ]) .with_notes(vec![unindent::unindent( " valid ABIs: - aapcs - amdgpu-kernel - C - cdecl - efiapi - fastcall - msp430-interrupt - platform-intrinsic - ptx-kernel - Rust - rust-call - rust-intrinsic - stdcall - system - sysv64 - thiscall - unadjusted - vectorcall - win64 - x86-interrupt ", )]), Diagnostic::error() .with_message("aborting due to previous error") .with_notes(vec![ "For more information about this error, try `rustc --explain E0703`.".to_owned(), ]), ]; TestData { files: file, diagnostics } }; } test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); } mod unicode_spans { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { let moon_phases = r#"🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄"#.to_string(); let invalid_start = 1; let invalid_end = "🐄".len() - 1; assert!(!moon_phases.is_char_boundary(invalid_start)); assert!(!moon_phases.is_char_boundary(invalid_end)); assert_eq!("🐄".len(), 4); let file = SimpleFile::new( "moon_jump.rs", moon_phases, ); let diagnostics = vec![ Diagnostic::error() .with_code("E01") .with_message("cow may not jump during new moon.") .with_labels(vec![ Label::primary((), invalid_start..invalid_end) .with_message("Invalid jump"), ]), Diagnostic::note() .with_message("invalid unicode range") .with_labels(vec![ Label::secondary((), invalid_start.."🐄".len()) .with_message("Cow range does not start at boundary."), ]), Diagnostic::note() .with_message("invalid unicode range") .with_labels(vec![ Label::secondary((), "🐄🌑".len().."🐄🌑🐄".len() - 1) .with_message("Cow range does not end at boundary."), ]), Diagnostic::note() .with_message("invalid unicode range") .with_labels(vec![ Label::secondary((), invalid_start.."🐄🌑🐄".len() - 1) .with_message("Cow does not start or end at boundary."), ]), ]; TestData{files: file, diagnostics } }; } test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); } mod position_indicator { use super::*; lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { let file = SimpleFile::new( "tests/main.js", [ "\"use strict\";", "let zero=0;", "function foo() {", " \"use strict\";", " one=1;", "}", ].join("\n"), ); let diagnostics = vec![ Diagnostic::warning() .with_code("ParserWarning") .with_message("The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode") .with_labels(vec![ Label::primary((), 45..57) .with_message("This strict mode declaration is redundant"), Label::secondary((), 0..12) .with_message("Strict mode is first declared here"), ]), ]; TestData{files: file, diagnostics } }; } test_emit!(rich_no_color); test_emit!(medium_no_color); test_emit!(short_no_color); test_emit!(rich_ascii_no_color); } mod multiline_omit { use super::*; lazy_static::lazy_static! { static ref TEST_CONFIG: Config = Config { styles: Styles::with_blue(Color::Blue), start_context_lines: 2, end_context_lines: 1, ..Config::default() }; static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id1 = files.add( "empty_if_comments.lua", [ "elseif 3 then", // primary label starts here "", // context line "", "", "", "", "", "", "", // context line "else", // primary label ends here ] .join("\n"), ); let file_id2 = files.add( "src/lib.rs", [ "fn main() {", " 1", // primary label starts here " + 1", // context line " + 1", // skip " + 1", // skip " + 1", // skip " +1", // secondary label here " + 1", // this single line will not be skipped; the previously filtered out label must be retrieved " + 1", // context line " + 1", // primary label ends here "}", ] .join("\n"), ); let diagnostics = vec![ Diagnostic::error() .with_message("empty elseif block") .with_code("empty_if") .with_labels(vec![ Label::primary(file_id1, 0..23), Label::secondary(file_id1, 15..21).with_message("content should be in here"), ]), Diagnostic::error() .with_message("mismatched types") .with_code("E0308") .with_labels(vec![ Label::primary(file_id2, 17..80).with_message("expected (), found integer"), Label::secondary(file_id2, 55..55).with_message("missing whitespace"), ]) .with_notes(vec![ "note:\texpected type `()`\n\tfound type `{integer}`".to_owned() ]), ]; TestData { files, diagnostics } }; } test_emit!(rich_no_color); } mod surrounding_lines { use super::*; lazy_static::lazy_static! { static ref TEST_CONFIG: Config = Config { styles: Styles::with_blue(Color::Blue), before_label_lines: 2, after_label_lines: 1, ..Config::default() }; static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { let mut files = SimpleFiles::new(); let file_id = files.add( "surroundingLines.fun", unindent::unindent( r#" #[foo] fn main() { println!( "{}", Foo ); } struct Foo"#, ), ); let diagnostics = vec![ Diagnostic::error() .with_message("Unknown attribute macro") .with_labels(vec![ Label::primary(file_id, 2..5).with_message("No attribute macro `foo` known"), ]), Diagnostic::error() .with_message("Missing argument for format") .with_labels(vec![ Label::primary(file_id, 55..58).with_message("No instance of std::fmt::Display exists for type Foo"), Label::secondary(file_id, 42..44).with_message("Unable to use `{}`-directive to display `Foo`"), ]), Diagnostic::error() .with_message("Syntax error") .with_labels(vec![ Label::primary(file_id, 79..79).with_message("Missing a semicolon"), ]), ]; TestData { files, diagnostics } }; } test_emit!(rich_no_color); }