summaryrefslogtreecommitdiffstats
path: root/third_party/rust/codespan-reporting
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/codespan-reporting')
-rw-r--r--third_party/rust/codespan-reporting/.cargo-checksum.json1
-rw-r--r--third_party/rust/codespan-reporting/CHANGELOG.md408
-rw-r--r--third_party/rust/codespan-reporting/Cargo.lock554
-rw-r--r--third_party/rust/codespan-reporting/Cargo.toml58
-rw-r--r--third_party/rust/codespan-reporting/examples/custom_files.rs197
-rw-r--r--third_party/rust/codespan-reporting/examples/peg_calculator.rs68
-rw-r--r--third_party/rust/codespan-reporting/examples/readme_preview.rs356
-rw-r--r--third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs105
-rw-r--r--third_party/rust/codespan-reporting/examples/term.rs175
-rw-r--r--third_party/rust/codespan-reporting/src/diagnostic.rs209
-rw-r--r--third_party/rust/codespan-reporting/src/files.rs443
-rw-r--r--third_party/rust/codespan-reporting/src/lib.rs7
-rw-r--r--third_party/rust/codespan-reporting/src/term.rs121
-rw-r--r--third_party/rust/codespan-reporting/src/term/config.rs321
-rw-r--r--third_party/rust/codespan-reporting/src/term/renderer.rs1020
-rw-r--r--third_party/rust/codespan-reporting/src/term/views.rs478
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_no_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_ascii_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_no_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap29
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap29
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap29
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_no_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap42
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap42
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_no_color.snap42
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_no_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_ascii_no_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_no_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__short_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message__short_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_no_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap21
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_no_color.snap21
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__short_no_color.snap13
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_color.snap12
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_no_color.snap12
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_ascii_no_color.snap35
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap35
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_no_color.snap35
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_color.snap8
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_no_color.snap8
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_omit__rich_no_color.snap38
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap25
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap25
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_no_color.snap25
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_color.snap6
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_no_color.snap6
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_ascii_no_color.snap58
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap58
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap58
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_no_color.snap11
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__medium_no_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_ascii_no_color.snap14
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_no_color.snap14
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__short_no_color.snap6
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_color.snap8
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_no_color.snap8
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_ascii_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap17
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_no_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_no_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_ascii_no_color.snap14
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap14
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap14
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_color.snap6
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_no_color.snap6
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_2_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_3_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_6_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_default_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_3_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_6_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_default_no_color.snap23
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode__medium_no_color.snap29
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode__rich_no_color.snap36
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode__short_no_color.snap7
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__medium_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap29
-rw-r--r--third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap9
-rw-r--r--third_party/rust/codespan-reporting/tests/support/color_buffer.rs137
-rw-r--r--third_party/rust/codespan-reporting/tests/support/mod.rs31
-rw-r--r--third_party/rust/codespan-reporting/tests/term.rs1053
110 files changed, 7335 insertions, 0 deletions
diff --git a/third_party/rust/codespan-reporting/.cargo-checksum.json b/third_party/rust/codespan-reporting/.cargo-checksum.json
new file mode 100644
index 0000000000..643c4571a9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"8a2b57ab18c006ff09e7adf2ac1f49b669a9f909562d40dc7e02bdb0ddc4637a","Cargo.lock":"8934aeb5b0a6bbc9ebb2a1d12a356a52773d6fc8fb815d7bf9f40cd8ee631bbb","Cargo.toml":"e8a590d176da1a52d74f27019a351a5536c1530b7b39cbd03959e98bfebc72c1","examples/custom_files.rs":"9838cce42b692c81e6935faee23fb2d9b1dcef3d91fd62a1f2d0b1b4b25ca0e2","examples/peg_calculator.rs":"ed6ec885c18e266859df0727c94e98f8a214321336e915632189bee0a9af8f0f","examples/readme_preview.rs":"8fac03e3a58e09cbaf768ff247d895c3c71fa6efffae75b051e8fbfd1a1bea1b","examples/reusable_diagnostic.rs":"1950213ddd5e7e6c23689b786831807c6b43bdc2ed573852755831896ea524ed","examples/term.rs":"a5f0ea3a8a1d39ec6198c1e00511b25340787206df4ab08c9181949af6ee9cac","src/diagnostic.rs":"bf1e6b8ce3027f2d86bc391e6159b611d4964e5fd7f9fa532736599633934040","src/files.rs":"ef8eed9306a66d6c3bca7809c70259a68b53d1f5dab9647aa2cc0c101c5f77d2","src/lib.rs":"8d6b9662d6b8fb77fd141b79179d127e328f1162f666e5d8c9b6184501f471f6","src/term.rs":"4ceacd3cb4270083321f39cd2ec74fece79bd828db5e54810ebfe6e62a02f9d6","src/term/config.rs":"ac1b09ae8a6c52e6a8068271356cb869ee10aec186e14c12ee019bddd1d5f667","src/term/renderer.rs":"5277a686316fd79c364fbfb3a338593f7c1b6a57f23563f9b57ea66b8f55ca46","src/term/views.rs":"96f4d13623e496c2c5937a0776a180328b420c1267e5e23d2535d4643e2032db","tests/snapshots/term__empty__medium_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__medium_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty__rich_ascii_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__rich_color.snap":"bf40415825f8d2555e5f1f22d87f79c5815061d41f8ebe980766dc655d1eef6d","tests/snapshots/term__empty__rich_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__short_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__short_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty_ranges__medium_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__medium_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap":"6044c49318c793a2c6075b665da93bedd5db649e79073054f6ed0b79d9df47d9","tests/snapshots/term__empty_ranges__rich_color.snap":"46d636261b92f635129391a7f9c0f3fddd1dc538819ce99aedf7c85e45a839f1","tests/snapshots/term__empty_ranges__rich_no_color.snap":"60dd9a44ce9a874a2eeea0bd4318f4c81c9995ba4ffe468978559a260abadec6","tests/snapshots/term__empty_ranges__short_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__short_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__fizz_buzz__medium_color.snap":"7243f5311f35a1f0769eef74155c4dedc85b2504720b03ac93baf65b6a89cb52","tests/snapshots/term__fizz_buzz__medium_no_color.snap":"f08a0ac4e4c818d581c7ca84ea29fd29067ac07d19dabc007fb3b0af41245986","tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap":"c875d5c4b4bfb007bd4bcc99d94b09c726c866f70bda89e37a0077666ddd9b05","tests/snapshots/term__fizz_buzz__rich_color.snap":"55b4e35a2589670126d408dfa62c7ee885b7a60dc047fc0bb5ef92d6b31a3b1b","tests/snapshots/term__fizz_buzz__rich_no_color.snap":"9a0e5381e480a77d048172e393abb5bdb502165a89266378aeedfcb6e4938990","tests/snapshots/term__fizz_buzz__short_color.snap":"931f54fa6bce28ac654abc09b3974e1c1a45d2e07b42967a0b8b95b96781fbb9","tests/snapshots/term__fizz_buzz__short_no_color.snap":"b41b26cccea0cf312fbe31e8399f0d13e35fc73f03a0a431382b640cbf591f5b","tests/snapshots/term__message__medium_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__medium_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message__rich_ascii_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__rich_color.snap":"0c737dab7f1b332c51c111bf4f7c9ec62e810817c5d62cbe2433677a1488826f","tests/snapshots/term__message__rich_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_and_notes__medium_color.snap":"662f6396dfc5b386d560b2aca9ac16106d7b14ee2abea981dd178c1ccbf2c256","tests/snapshots/term__message_and_notes__medium_no_color.snap":"44cc76150fa84908d77159635df98333f8aac85b4fb08e1815267176c953c518","tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__rich_color.snap":"e374968a58d73e564004f99ab5fade4f449fb0d1a8ec53ea523a131bcb62899c","tests/snapshots/term__message_and_notes__rich_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message_and_notes__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__rich_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__short_no_color.snap":"a3cea73a584a91ae08cef8b4f17171c87aa643437cc6450f47fdf3e73d21df41","tests/snapshots/term__multifile__medium_color.snap":"3814c9f6e71e9bf5c31cb60be2311fd113ed77b2b86899e02f9e2b863f1df329","tests/snapshots/term__multifile__medium_no_color.snap":"e60241dd266446049e2a2fa77d46b13e04f8be10e971076606f8f9eef4457340","tests/snapshots/term__multifile__rich_ascii_no_color.snap":"8726c673ee427d9e13f374abec7fc6c0e37dde662012564f57be18d96d7cd39b","tests/snapshots/term__multifile__rich_color.snap":"ef7d6fe9fc97f8eafa51cb246f4b0ca53b61025aff5915bd86cc65bc813b6f6c","tests/snapshots/term__multifile__rich_no_color.snap":"96c58db425a2eba517c56690f2344396370784c2992581696f63be86a0621b39","tests/snapshots/term__multifile__short_color.snap":"cb654ad52babb928b20b1f6e3a7aee15f715282da789b4b961ef6ca6113324da","tests/snapshots/term__multifile__short_no_color.snap":"946d3eb63f7d8f1e00b4202db50c3c86b9d01452b3ec160c76b1655d31c843d6","tests/snapshots/term__multiline_omit__rich_no_color.snap":"c2c9a923d51551fcc777b7ebcf54f188a41855a62f939cfffaaebd87b8aaa55d","tests/snapshots/term__multiline_overlapping__medium_color.snap":"56ebba06bb5d4afbfd8fc7aa5e027910add76acbeda5da48e74da5218f95c2b2","tests/snapshots/term__multiline_overlapping__medium_no_color.snap":"149c321447cbd917c8c8bf42aca1310bc7a2f53a8df3efdecf0d30bde1ccc836","tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap":"a32627a61a1e682e1a169ba46c50bca8d4292c6c253049891fcf048539507ab8","tests/snapshots/term__multiline_overlapping__rich_color.snap":"01f0a70739c03a738320848a579c6c74696d9e7afed4de3729e6b63cbda3e67c","tests/snapshots/term__multiline_overlapping__rich_no_color.snap":"729cdf6c159cc051354661973d81204360f6e46fa3848938aa4afd21135b8cea","tests/snapshots/term__multiline_overlapping__short_color.snap":"2ce68675d9ceb7b461a9e02eb39e3a196b9ec98027c9076adb91139711a6bacc","tests/snapshots/term__multiline_overlapping__short_no_color.snap":"fd1edf7cb7a6e02e514eac0f6231cdd36bec235b4ab3263f6454f6c756d3f825","tests/snapshots/term__overlapping__medium_color.snap":"5444be575c8640c58a6aa8b50dd64c8fc63594741defc9fa214c33b651dd4028","tests/snapshots/term__overlapping__medium_no_color.snap":"7fbad37e55a77d6ff97b64253ce26b3d78aff3fa3f3911e53d21cf1f5373e4d2","tests/snapshots/term__overlapping__rich_ascii_no_color.snap":"a8bc87ab545e17c8f2fd78f1efa4026ab62ad126e7f80dfebd3ce0ceb658c4c4","tests/snapshots/term__overlapping__rich_color.snap":"838c6be71d27dbf129651cf4634638112c45b143a0f65b6d6875eff45c94c2c8","tests/snapshots/term__overlapping__rich_no_color.snap":"8b0ae77b69f00245cea434b422ffe8e27f80ffca0010ed924f2ea3741f983947","tests/snapshots/term__overlapping__short_color.snap":"e38418c4f5652fb736446961bed9685151655704dcac12438ab153695a7730f4","tests/snapshots/term__overlapping__short_no_color.snap":"7714827d440e0ea3867d42d951eab1bead34b42c219baa9cb007ed8f553a1961","tests/snapshots/term__position_indicator__medium_no_color.snap":"cebbd419d8a627688f6a653eb7a819704a0149bade9b7580788d428f8eb33daf","tests/snapshots/term__position_indicator__rich_ascii_no_color.snap":"8ce0ba088cd72a1e2e32bc5025f9ed347afc71a7d105bd750165368d6dbafe65","tests/snapshots/term__position_indicator__rich_no_color.snap":"02c39a4bda3efda11d01e0dd48f56275b92894d2e9d8fa66b4f779bcb1fa4ff8","tests/snapshots/term__position_indicator__short_no_color.snap":"97719ff1de01f320490358ffded0953d2a96d79021b2d13b9995207d06e16d7f","tests/snapshots/term__same_line__medium_color.snap":"1313706a93ab35585c5cf290af342fa1e1906684dd4f1b0704a5e2f3ee724c96","tests/snapshots/term__same_line__medium_no_color.snap":"b918a8f898c8e6830d2a0ccf43761daf1ebc0699b4f51d6f096411e788a8b4fe","tests/snapshots/term__same_line__rich_ascii_no_color.snap":"8cde34de3f8ca4da2d286ec581ca1bc155ca422088523f91e83f6bf4f9405413","tests/snapshots/term__same_line__rich_color.snap":"fc093d51cb3dbeb4824b5c55cf224a0013a69313fbf3ce219074c58bf4dcef83","tests/snapshots/term__same_line__rich_no_color.snap":"c50495bc7da9757246a7d697d214961ed0a7bdd3ab275de67ba9e1a6fd39d2d4","tests/snapshots/term__same_line__short_color.snap":"ab9a0b24d0af89baea2d07930dc3e04c3537042ea61bcc1392940f995d3a8be5","tests/snapshots/term__same_line__short_no_color.snap":"ae9884e1a5bcec9260ea8139246726aba1e4c4029156c1ab50f1ccb5d63397f5","tests/snapshots/term__same_ranges__medium_color.snap":"58a870d2d20773d9516b23752ba112e2f95935dc18ae14215597f3d68963a181","tests/snapshots/term__same_ranges__medium_no_color.snap":"ec73645042c5e13a8bd1eed26837dba4c286e6780acffc218a0f423c0b993210","tests/snapshots/term__same_ranges__rich_ascii_no_color.snap":"d805c605c1ca56f39d41bb4150713d3f850ca63924a770d1d72b6230753b0b4a","tests/snapshots/term__same_ranges__rich_color.snap":"56f2fa66f1074fb167f1c8e61133a445267f6915c94f7331e9a68d553ebcb796","tests/snapshots/term__same_ranges__rich_no_color.snap":"34b82a6ce9ccbd08e4e964c78eb27f765e30f0198616ab5d4f15f02a62d2e37e","tests/snapshots/term__same_ranges__short_color.snap":"d5b7bf052b326f38ea81bd9ca970a409cda8c9445d800c1468ddc452782e1626","tests/snapshots/term__same_ranges__short_no_color.snap":"16dfc93660a12f0687c0335924469266d6cf803aef34f72bdcf0e3d4b55a902a","tests/snapshots/term__tab_columns__tab_width_2_no_color.snap":"3fa128b8c18d87cdb59001dedb79ed059ca1927a24fce60c10b8139ee6ae38d1","tests/snapshots/term__tab_columns__tab_width_3_no_color.snap":"793e99ef600f3da03b13b50d5c44859ed9dd32400affd0a0100c0155c3fdca72","tests/snapshots/term__tab_columns__tab_width_6_no_color.snap":"93a92a73b3ca3e517d5a4e4e215579dada60ff95af79c0c6d8b1116ba75130e0","tests/snapshots/term__tab_columns__tab_width_default_no_color.snap":"ec082ca97f84631c19fc139f615bc0da588cd5fe706ba7307dd80bac3db20cb2","tests/snapshots/term__tabbed__tab_width_3_no_color.snap":"f8e7318f381ef21526cb9b9f459be404e8e7ee3abbb7174f8a45e3bb2ae81ae4","tests/snapshots/term__tabbed__tab_width_6_no_color.snap":"0a4ece01e179e1b8b7b13d1789d391761ced6c45c459dbaef3d8256793864578","tests/snapshots/term__tabbed__tab_width_default_no_color.snap":"f77441824f2f327d559f70e56973d7798a42e20de302ed351d68a28bc6f05b10","tests/snapshots/term__unicode__medium_no_color.snap":"3faa0672b2c04198b0268a6f0266606dfeee56e58373b478d886d503bc17678a","tests/snapshots/term__unicode__rich_no_color.snap":"60c5045f536d26f99cbd48e818405d04b992ee1fcb6898aedeabddc626e01b47","tests/snapshots/term__unicode__short_no_color.snap":"b7c0ac33ac0cb30fe5f8dca0cd6f6fe43c707cc96eaef11b177e08a91859e9d2","tests/snapshots/term__unicode_spans__medium_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/snapshots/term__unicode_spans__rich_no_color.snap":"2a89e43664d367fedf343e2de070e5719a9de69c8806b5b5edec6d854c63a9c9","tests/snapshots/term__unicode_spans__short_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/support/color_buffer.rs":"8de39e81ab157ae18edb9ca8b2c015fee248e15624dc172e593354b43d3897bd","tests/support/mod.rs":"c708541328abf4a613b9d67333e0b88cd91ddb42968002ce515b0b851d12e516","tests/term.rs":"8b9189738b21d664ba2c2b8ab34e05420b6aae01bed35703bcd5d3a73e0f8689"},"package":"3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"} \ No newline at end of file
diff --git a/third_party/rust/codespan-reporting/CHANGELOG.md b/third_party/rust/codespan-reporting/CHANGELOG.md
new file mode 100644
index 0000000000..68109e0665
--- /dev/null
+++ b/third_party/rust/codespan-reporting/CHANGELOG.md
@@ -0,0 +1,408 @@
+# 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]
+
+## [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.
+
+ <details>
+ <summary>Example</summary>
+
+ 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
+ │ ^^^^^
+ ```
+
+ </details>
+
+## [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.
+
+ <details>
+ <summary>Example</summary>
+
+ 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
+ ```
+
+ </details>
+
+### 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.
+
+ <details>
+ <summary>Example</summary>
+
+ 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 '('
+ │
+ ```
+
+ </details>
+
+- 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.
+
+ <details>
+ <summary>Example</summary>
+
+ 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`
+
+ ```
+
+ </details>
+
+## [0.9.2] - 2020-03-29
+
+### Changed
+
+- Render overlapping multiline marks on the same lines of source code.
+
+ <details>
+ <summary>Example</summary>
+
+ 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<ByteIndex, LineIndexOutOfBoundsError>`
+ 3 │ Ordering::Equal => Ok(self.source_span().end()),
+ │ ---------------------------- this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+ 4 │ Ordering::Greater => LineIndexOutOfBoundsError {
+ │ ╭──────────────────────────────────^
+ 5 │ │ given: line_index,
+ 6 │ │ max: self.last_line_index(),
+ 7 │ │ },
+ │ ╰─────────────^ expected enum `Result`, found struct `LineIndexOutOfBoundsError`
+ │
+ = expected type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+ 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<ByteIndex, LineIndexOutOfBoundsError>`
+ 3 │ │ Ordering::Equal => Ok(self.source_span().end()),
+ │ │ ---------------------------- this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+ 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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+ ```
+
+ </details>
+
+## [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.
+
+ <details>
+ <summary>Example</summary>
+
+ 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
+ │
+ ```
+
+ </details>
+
+## [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
diff --git a/third_party/rust/codespan-reporting/Cargo.lock b/third_party/rust/codespan-reporting/Cargo.lock
new file mode 100644
index 0000000000..ff92d96df3
--- /dev/null
+++ b/third_party/rust/codespan-reporting/Cargo.lock
@@ -0,0 +1,554 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "cc"
+version = "1.0.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+
+[[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 = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+dependencies = [
+ "anyhow",
+ "insta",
+ "lazy_static",
+ "peg",
+ "rustyline",
+ "serde",
+ "structopt",
+ "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-next"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
+dependencies = [
+ "cfg-if 1.0.0",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dtoa"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "getrandom"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+dependencies = [
+ "libc",
+]
+
+[[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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+
+[[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.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[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.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "nix"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 0.1.10",
+ "libc",
+]
+
+[[package]]
+name = "peg"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367"
+dependencies = [
+ "peg-macros",
+ "peg-runtime",
+]
+
+[[package]]
+name = "peg-macros"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d"
+dependencies = [
+ "peg-runtime",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "peg-runtime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+]
+
+[[package]]
+name = "rustyline"
+version = "6.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
+dependencies = [
+ "cfg-if 0.1.10",
+ "dirs-next",
+ "libc",
+ "log",
+ "memchr",
+ "nix",
+ "scopeguard",
+ "unicode-segmentation",
+ "unicode-width",
+ "utf8parse",
+ "winapi",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.123"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.123"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.62"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.8.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
+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 = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "structopt"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+
+[[package]]
+name = "unindent"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
diff --git a/third_party/rust/codespan-reporting/Cargo.toml b/third_party/rust/codespan-reporting/Cargo.toml
new file mode 100644
index 0000000000..5a3d588b3a
--- /dev/null
+++ b/third_party/rust/codespan-reporting/Cargo.toml
@@ -0,0 +1,58 @@
+# 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 believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "codespan-reporting"
+version = "0.11.1"
+authors = ["Brendan Zabarauskas <bjzaba@yahoo.com.au>"]
+exclude = ["assets/**"]
+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"
+[dependencies.serde]
+version = "1"
+features = ["derive"]
+optional = true
+
+[dependencies.termcolor]
+version = "1"
+
+[dependencies.unicode-width]
+version = "0.1"
+[dev-dependencies.anyhow]
+version = "1"
+
+[dev-dependencies.insta]
+version = "1.6.3"
+
+[dev-dependencies.lazy_static]
+version = "1.4"
+
+[dev-dependencies.peg]
+version = "0.6"
+
+[dev-dependencies.rustyline]
+version = "6"
+
+[dev-dependencies.structopt]
+version = "0.3"
+
+[dev-dependencies.unindent]
+version = "0.1"
+
+[features]
+ascii-only = []
+serialization = ["serde", "serde/rc"]
diff --git a/third_party/rust/codespan-reporting/examples/custom_files.rs b/third_party/rust/codespan-reporting/examples/custom_files.rs
new file mode 100644
index 0000000000..dbbacda591
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/custom_files.rs
@@ -0,0 +1,197 @@
+//! An example that shows how to implement a simple custom file database.
+//! The database uses 32-bit file-ids, which could be useful for optimizing
+//! memory usage.
+//!
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example custom_files
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::term;
+use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
+use std::ops::Range;
+
+fn main() -> anyhow::Result<()> {
+ let mut files = files::Files::new();
+
+ let file_id0 = files.add("0.greeting", "hello world!").unwrap();
+ let file_id1 = files.add("1.greeting", "bye world").unwrap();
+
+ let messages = vec![
+ Message::UnwantedGreetings {
+ greetings: vec![(file_id0, 0..5), (file_id1, 0..3)],
+ },
+ Message::OverTheTopExclamations {
+ exclamations: vec![(file_id0, 11..12)],
+ },
+ ];
+
+ let writer = StandardStream::stderr(ColorChoice::Always);
+ let config = term::Config::default();
+ for message in &messages {
+ let writer = &mut writer.lock();
+ term::emit(writer, &config, &files, &message.to_diagnostic())?;
+ }
+
+ Ok(())
+}
+
+/// A module containing the file implementation
+mod files {
+ use codespan_reporting::files;
+ use std::ops::Range;
+
+ /// A file that is backed by an `Arc<String>`.
+ #[derive(Debug, Clone)]
+ struct File {
+ /// The name of the file.
+ name: String,
+ /// The source code of the file.
+ source: String,
+ /// The starting byte indices in the source code.
+ line_starts: Vec<usize>,
+ }
+
+ impl File {
+ fn line_start(&self, line_index: usize) -> Result<usize, files::Error> {
+ use std::cmp::Ordering;
+
+ match line_index.cmp(&self.line_starts.len()) {
+ Ordering::Less => Ok(self
+ .line_starts
+ .get(line_index)
+ .expect("failed despite previous check")
+ .clone()),
+ Ordering::Equal => Ok(self.source.len()),
+ Ordering::Greater => Err(files::Error::LineTooLarge {
+ given: line_index,
+ max: self.line_starts.len() - 1,
+ }),
+ }
+ }
+ }
+
+ /// An opaque file identifier.
+ #[derive(Copy, Clone, PartialEq, Eq)]
+ pub struct FileId(u32);
+
+ #[derive(Debug, Clone)]
+ pub struct Files {
+ files: Vec<File>,
+ }
+
+ impl Files {
+ /// Create a new files database.
+ pub fn new() -> Files {
+ Files { files: Vec::new() }
+ }
+
+ /// Add a file to the database, returning the handle that can be used to
+ /// refer to it again.
+ pub fn add(
+ &mut self,
+ name: impl Into<String>,
+ source: impl Into<String>,
+ ) -> Option<FileId> {
+ use std::convert::TryFrom;
+
+ let file_id = FileId(u32::try_from(self.files.len()).ok()?);
+ let name = name.into();
+ let source = source.into();
+ let line_starts = files::line_starts(&source).collect();
+
+ self.files.push(File {
+ name,
+ line_starts,
+ source,
+ });
+
+ Some(file_id)
+ }
+
+ /// Get the file corresponding to the given id.
+ fn get(&self, file_id: FileId) -> Result<&File, files::Error> {
+ self.files
+ .get(file_id.0 as usize)
+ .ok_or(files::Error::FileMissing)
+ }
+ }
+
+ impl<'files> files::Files<'files> for Files {
+ type FileId = FileId;
+ type Name = &'files str;
+ type Source = &'files str;
+
+ fn name(&self, file_id: FileId) -> Result<&str, files::Error> {
+ Ok(self.get(file_id)?.name.as_ref())
+ }
+
+ fn source(&self, file_id: FileId) -> Result<&str, files::Error> {
+ Ok(&self.get(file_id)?.source)
+ }
+
+ fn line_index(&self, file_id: FileId, byte_index: usize) -> Result<usize, files::Error> {
+ self.get(file_id)?
+ .line_starts
+ .binary_search(&byte_index)
+ .or_else(|next_line| Ok(next_line - 1))
+ }
+
+ fn line_range(
+ &self,
+ file_id: FileId,
+ line_index: usize,
+ ) -> Result<Range<usize>, files::Error> {
+ let file = self.get(file_id)?;
+ let line_start = file.line_start(line_index)?;
+ let next_line_start = file.line_start(line_index + 1)?;
+
+ Ok(line_start..next_line_start)
+ }
+ }
+}
+
+/// A Diagnostic message.
+enum Message {
+ UnwantedGreetings {
+ greetings: Vec<(files::FileId, Range<usize>)>,
+ },
+ OverTheTopExclamations {
+ exclamations: Vec<(files::FileId, Range<usize>)>,
+ },
+}
+
+impl Message {
+ fn to_diagnostic(&self) -> Diagnostic<files::FileId> {
+ match self {
+ Message::UnwantedGreetings { greetings } => Diagnostic::error()
+ .with_message("greetings are not allowed")
+ .with_labels(
+ greetings
+ .iter()
+ .map(|(file_id, range)| {
+ Label::primary(*file_id, range.clone()).with_message("a greeting")
+ })
+ .collect(),
+ )
+ .with_notes(vec![
+ "found greetings!".to_owned(),
+ "pleas no greetings :(".to_owned(),
+ ]),
+ Message::OverTheTopExclamations { exclamations } => Diagnostic::error()
+ .with_message("over-the-top exclamations")
+ .with_labels(
+ exclamations
+ .iter()
+ .map(|(file_id, range)| {
+ Label::primary(*file_id, range.clone()).with_message("an exclamation")
+ })
+ .collect(),
+ )
+ .with_notes(vec!["ridiculous!".to_owned()]),
+ }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/peg_calculator.rs b/third_party/rust/codespan-reporting/examples/peg_calculator.rs
new file mode 100644
index 0000000000..882ce6da8c
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/peg_calculator.rs
@@ -0,0 +1,68 @@
+//! An example of using `peg` with `codespan_reporting`.
+//!
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example peg_calculator
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term;
+use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
+use rustyline::error::ReadlineError;
+use rustyline::Editor;
+
+peg::parser! {
+ grammar arithmetic() for str {
+ rule number() -> i64
+ = n:$(['0'..='9']+) { n.parse().unwrap() }
+
+ pub rule calculate() -> i64 = precedence!{
+ x:(@) "+" y:@ { x + y }
+ x:(@) "-" y:@ { x - y }
+ "-" v:@ { - v }
+ --
+ x:(@) "*" y:@ { x * y }
+ x:(@) "/" y:@ { x / y }
+ --
+ x:@ "^" y:(@) { i64::pow(x, y as u32) }
+ v:@ "!" { (1..v+1).product() }
+ --
+ "(" v:calculate() ")" { v }
+ n:number() { n }
+ }
+ }
+}
+
+fn main() -> anyhow::Result<()> {
+ let writer = StandardStream::stderr(ColorChoice::Always);
+ let config = codespan_reporting::term::Config::default();
+ let mut editor = Editor::<()>::new();
+
+ loop {
+ let line = match editor.readline("> ") {
+ Ok(line) => line,
+ Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => return Ok(()),
+ Err(error) => return Err(error.into()),
+ };
+
+ match arithmetic::calculate(&line) {
+ Ok(number) => println!("{}", number),
+ Err(error) => {
+ let file = SimpleFile::new("<repl>", line);
+
+ let start = error.location.offset;
+ let diagnostic = Diagnostic::error()
+ .with_message("parse error")
+ .with_labels(vec![
+ Label::primary((), start..start).with_message("parse error")
+ ])
+ .with_notes(vec![format!("expected: {}", error.expected)]);
+
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+ }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/readme_preview.rs b/third_party/rust/codespan-reporting/examples/readme_preview.rs
new file mode 100644
index 0000000000..2cf96f60cb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/readme_preview.rs
@@ -0,0 +1,356 @@
+//! Renders the preview SVG for the README.
+//!
+//! To update the preview, execute the following command from the top level of
+//! the repository:
+//!
+//! ```sh
+//! cargo run --example readme_preview svg > codespan-reporting/assets/readme_preview.svg
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term::termcolor::{Color, ColorSpec, StandardStream, WriteColor};
+use codespan_reporting::term::{self, ColorArg};
+use std::io::{self, Write};
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub enum Opts {
+ /// Render SVG output
+ Svg,
+ /// Render Stderr output
+ Stderr {
+ /// Configure coloring of output
+ #[structopt(
+ long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ color: ColorArg,
+ },
+}
+
+fn main() -> anyhow::Result<()> {
+ let file = SimpleFile::new(
+ "FizzBuzz.fun",
+ unindent::unindent(
+ r#"
+ module FizzBuzz where
+
+ fizz₁ : Nat → String
+ fizz₁ num = case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+
+ fizz₂ : Nat → String
+ fizz₂ num =
+ case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+ "#,
+ ),
+ );
+
+ let diagnostics = [Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary((), 328..331).with_message("expected `String`, found `Nat`"),
+ Label::secondary((), 211..331).with_message("`case` clauses have incompatible types"),
+ Label::secondary((), 258..268).with_message("this is found to be of type `String`"),
+ Label::secondary((), 284..290).with_message("this is found to be of type `String`"),
+ Label::secondary((), 306..312).with_message("this is found to be of type `String`"),
+ Label::secondary((), 186..192).with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )])];
+
+ // let mut files = SimpleFiles::new();
+ match Opts::from_args() {
+ Opts::Svg => {
+ let mut buffer = Vec::new();
+ let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer));
+ let config = codespan_reporting::term::Config {
+ styles: codespan_reporting::term::Styles::with_blue(Color::Blue),
+ ..codespan_reporting::term::Config::default()
+ };
+
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer, &config, &file, &diagnostic)?;
+ }
+
+ let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1;
+
+ let padding = 10;
+ let font_size = 12;
+ let line_spacing = 3;
+ let width = 882;
+ let height = padding + num_lines * (font_size + line_spacing) + padding;
+
+ let stdout = std::io::stdout();
+ let writer = &mut stdout.lock();
+
+ write!(
+ writer,
+ r#"<svg viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">
+ <style>
+ /* https://github.com/aaron-williamson/base16-alacritty/blob/master/colors/base16-tomorrow-night-256.yml */
+ pre {{
+ background: #1d1f21;
+ margin: 0;
+ padding: {padding}px;
+ border-radius: 6px;
+ color: #ffffff;
+ font: {font_size}px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+ }}
+
+ pre .bold {{ font-weight: bold; }}
+
+ pre .fg.black {{ color: #1d1f21; }}
+ pre .fg.red {{ color: #cc6666; }}
+ pre .fg.green {{ color: #b5bd68; }}
+ pre .fg.yellow {{ color: #f0c674; }}
+ pre .fg.blue {{ color: #81a2be; }}
+ pre .fg.magenta {{ color: #b294bb; }}
+ pre .fg.cyan {{ color: #8abeb7; }}
+ pre .fg.white {{ color: #c5c8c6; }}
+
+ pre .fg.black.bright {{ color: #969896; }}
+ pre .fg.red.bright {{ color: #cc6666; }}
+ pre .fg.green.bright {{ color: #b5bd68; }}
+ pre .fg.yellow.bright {{ color: #f0c674; }}
+ pre .fg.blue.bright {{ color: #81a2be; }}
+ pre .fg.magenta.bright {{ color: #b294bb; }}
+ pre .fg.cyan.bright {{ color: #8abeb7; }}
+ pre .fg.white.bright {{ color: #ffffff; }}
+
+ pre .bg.black {{ background-color: #1d1f21; }}
+ pre .bg.red {{ background-color: #cc6666; }}
+ pre .bg.green {{ background-color: #b5bd68; }}
+ pre .bg.yellow {{ background-color: #f0c674; }}
+ pre .bg.blue {{ background-color: #81a2be; }}
+ pre .bg.magenta {{ background-color: #b294bb; }}
+ pre .bg.cyan {{ background-color: #8abeb7; }}
+ pre .bg.white {{ background-color: #c5c8c6; }}
+
+ pre .bg.black.bright {{ background-color: #969896; }}
+ pre .bg.red.bright {{ background-color: #cc6666; }}
+ pre .bg.green.bright {{ background-color: #b5bd68; }}
+ pre .bg.yellow.bright {{ background-color: #f0c674; }}
+ pre .bg.blue.bright {{ background-color: #81a2be; }}
+ pre .bg.magenta.bright {{ background-color: #b294bb; }}
+ pre .bg.cyan.bright {{ background-color: #8abeb7; }}
+ pre .bg.white.bright {{ background-color: #ffffff; }}
+ </style>
+
+ <foreignObject x="0" y="0" width="{width}" height="{height}">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <pre>"#,
+ padding = padding,
+ font_size = font_size,
+ width = width,
+ height = height,
+ )?;
+
+ writer.write_all(&buffer)?;
+
+ write!(
+ writer,
+ "</pre>
+ </div>
+ </foreignObject>
+</svg>
+"
+ )?;
+ }
+ Opts::Stderr { color } => {
+ let writer = StandardStream::stderr(color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Rudimentary HTML escaper which performs the following conversions:
+///
+/// - `<` ⇒ `&lt;`
+/// - `>` ⇒ `&gt;`
+/// - `&` ⇒ `&amp;`
+pub struct HtmlEscapeWriter<W> {
+ upstream: W,
+}
+
+impl<W> HtmlEscapeWriter<W> {
+ pub fn new(upstream: W) -> HtmlEscapeWriter<W> {
+ HtmlEscapeWriter { upstream }
+ }
+}
+
+impl<W: Write> Write for HtmlEscapeWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let mut last_term = 0usize;
+ for (i, byte) in buf.iter().enumerate() {
+ let escape = match byte {
+ b'<' => &b"&lt;"[..],
+ b'>' => &b"&gt;"[..],
+ b'&' => &b"&amp;"[..],
+ _ => continue,
+ };
+ self.upstream.write_all(&buf[last_term..i])?;
+ last_term = i + 1;
+ self.upstream.write_all(escape)?;
+ }
+ self.upstream.write_all(&buf[last_term..])?;
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.upstream.flush()
+ }
+}
+
+impl<W: WriteColor> WriteColor for HtmlEscapeWriter<W> {
+ fn supports_color(&self) -> bool {
+ self.upstream.supports_color()
+ }
+
+ fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
+ self.upstream.set_color(spec)
+ }
+
+ fn reset(&mut self) -> io::Result<()> {
+ self.upstream.reset()
+ }
+}
+
+pub struct SvgWriter<W> {
+ upstream: W,
+ color: ColorSpec,
+}
+
+impl<W> SvgWriter<W> {
+ pub fn new(upstream: W) -> SvgWriter<W> {
+ SvgWriter {
+ upstream,
+ color: ColorSpec::new(),
+ }
+ }
+}
+
+impl<W: Write> Write for SvgWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.upstream.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.upstream.flush()
+ }
+}
+
+impl<W: Write> WriteColor for SvgWriter<W> {
+ fn supports_color(&self) -> bool {
+ true
+ }
+
+ fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
+ #![allow(unused_assignments)]
+
+ if self.color == *spec {
+ return Ok(());
+ } else {
+ if !self.color.is_none() {
+ write!(self, "</span>")?;
+ }
+ self.color = spec.clone();
+ }
+
+ if spec.is_none() {
+ write!(self, "</span>")?;
+ return Ok(());
+ } else {
+ write!(self, "<span class=\"")?;
+ }
+
+ let mut first = true;
+
+ fn write_first<W: Write>(first: bool, writer: &mut SvgWriter<W>) -> io::Result<bool> {
+ if !first {
+ write!(writer, " ")?;
+ }
+
+ Ok(false)
+ };
+
+ fn write_color<W: Write>(color: &Color, writer: &mut SvgWriter<W>) -> io::Result<()> {
+ match color {
+ Color::Black => write!(writer, "black"),
+ Color::Blue => write!(writer, "blue"),
+ Color::Green => write!(writer, "green"),
+ Color::Red => write!(writer, "red"),
+ Color::Cyan => write!(writer, "cyan"),
+ Color::Magenta => write!(writer, "magenta"),
+ Color::Yellow => write!(writer, "yellow"),
+ Color::White => write!(writer, "white"),
+ // TODO: other colors
+ _ => Ok(()),
+ }
+ };
+
+ if let Some(fg) = spec.fg() {
+ first = write_first(first, self)?;
+ write!(self, "fg ")?;
+ write_color(fg, self)?;
+ }
+
+ if let Some(bg) = spec.bg() {
+ first = write_first(first, self)?;
+ write!(self, "bg ")?;
+ write_color(bg, self)?;
+ }
+
+ if spec.bold() {
+ first = write_first(first, self)?;
+ write!(self, "bold")?;
+ }
+
+ if spec.underline() {
+ first = write_first(first, self)?;
+ write!(self, "underline")?;
+ }
+
+ if spec.intense() {
+ first = write_first(first, self)?;
+ write!(self, "bright")?;
+ }
+
+ write!(self, "\">")?;
+
+ Ok(())
+ }
+
+ fn reset(&mut self) -> io::Result<()> {
+ let color = self.color.clone();
+
+ if color != ColorSpec::new() {
+ write!(self, "</span>")?;
+ self.color = ColorSpec::new();
+ }
+
+ Ok(())
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs
new file mode 100644
index 0000000000..d05dee8549
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs
@@ -0,0 +1,105 @@
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFile;
+use codespan_reporting::term::termcolor::StandardStream;
+use codespan_reporting::term::{self, ColorArg};
+use std::ops::Range;
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub struct Opts {
+ #[structopt(long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ color: ColorArg,
+}
+
+fn main() -> anyhow::Result<()> {
+ let file = SimpleFile::new(
+ "main.rs",
+ unindent::unindent(
+ r#"
+ fn main() {
+ let foo: i32 = "hello, world";
+ foo += 1;
+ }
+ "#,
+ ),
+ );
+
+ let errors = [
+ Error::MismatchType(
+ Item::new(20..23, "i32"),
+ Item::new(31..45, "\"hello, world\""),
+ ),
+ Error::MutatingImmutable(Item::new(20..23, "foo"), Item::new(51..59, "foo += 1")),
+ ];
+
+ let opts = Opts::from_args();
+ let writer = StandardStream::stderr(opts.color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in errors.iter().map(Error::report) {
+ term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
+ }
+
+ Ok(())
+}
+
+/// An error enum that represent all possible errors within your program
+enum Error {
+ MismatchType(Item, Item),
+ MutatingImmutable(Item, Item),
+}
+
+impl Error {
+ fn report(&self) -> Diagnostic<()> {
+ match self {
+ Error::MismatchType(left, right) => Diagnostic::error()
+ .with_code("E0308")
+ .with_message("mismatch types")
+ .with_labels(vec![
+ Label::primary((), right.range.clone()).with_message(format!(
+ "Expected `{}`, found: `{}`",
+ left.content, right.content,
+ )),
+ Label::secondary((), left.range.clone()).with_message("expected due to this"),
+ ]),
+ Error::MutatingImmutable(original, mutating) => Diagnostic::error()
+ .with_code("E0384")
+ .with_message(format!(
+ "cannot mutate immutable variable `{}`",
+ original.content,
+ ))
+ .with_labels(vec![
+ Label::secondary((), original.range.clone()).with_message(unindent::unindent(
+ &format!(
+ r#"
+ first assignment to `{0}`
+ help: make this binding mutable: `mut {0}`
+ "#,
+ original.content,
+ ),
+ )),
+ Label::primary((), mutating.range.clone())
+ .with_message("cannot assign twice to immutable variable"),
+ ]),
+ }
+ }
+}
+
+/// An item in the source code to be used in the `Error` enum.
+/// In a more complex program it could also contain a `files::FileId` to handle errors that occur inside multiple files.
+struct Item {
+ range: Range<usize>,
+ content: String,
+}
+
+impl Item {
+ fn new(range: Range<usize>, content: impl Into<String>) -> Item {
+ let content = content.into();
+ Item { range, content }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/examples/term.rs b/third_party/rust/codespan-reporting/examples/term.rs
new file mode 100644
index 0000000000..19bf8503c0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/examples/term.rs
@@ -0,0 +1,175 @@
+//! To run this example, execute the following command from the top level of
+//! this repository:
+//!
+//! ```sh
+//! cargo run --example term
+//! ```
+
+use codespan_reporting::diagnostic::{Diagnostic, Label};
+use codespan_reporting::files::SimpleFiles;
+use codespan_reporting::term::termcolor::StandardStream;
+use codespan_reporting::term::{self, ColorArg};
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "emit")]
+pub struct Opts {
+ /// Configure coloring of output
+ #[structopt(
+ long = "color",
+ parse(try_from_str),
+ default_value = "auto",
+ possible_values = ColorArg::VARIANTS,
+ case_insensitive = true
+ )]
+ pub color: ColorArg,
+}
+
+fn main() -> anyhow::Result<()> {
+ let opts = Opts::from_args();
+ let mut files = SimpleFiles::new();
+
+ let file_id1 = files.add(
+ "Data/Nat.fun",
+ unindent::unindent(
+ "
+ module Data.Nat where
+
+ data Nat : Type where
+ zero : Nat
+ succ : Nat → Nat
+
+ {-# BUILTIN NATRAL Nat #-}
+
+ infixl 6 _+_ _-_
+
+ _+_ : Nat → Nat → Nat
+ zero + n₂ = n₂
+ succ n₁ + n₂ = succ (n₁ + n₂)
+
+ _-_ : Nat → Nat → Nat
+ n₁ - zero = n₁
+ zero - succ n₂ = zero
+ succ n₁ - succ n₂ = n₁ - n₂
+ ",
+ ),
+ );
+
+ let file_id2 = files.add(
+ "Test.fun",
+ unindent::unindent(
+ r#"
+ module Test where
+
+ _ : Nat
+ _ = 123 + "hello"
+ "#,
+ ),
+ );
+
+ let file_id3 = files.add(
+ "FizzBuzz.fun",
+ unindent::unindent(
+ r#"
+ module FizzBuzz where
+
+ fizz₁ : Nat → String
+ fizz₁ num = case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+
+ fizz₂ : Nat → String
+ fizz₂ num =
+ case (mod num 5) (mod num 3) of
+ 0 0 => "FizzBuzz"
+ 0 _ => "Fizz"
+ _ 0 => "Buzz"
+ _ _ => num
+ "#,
+ ),
+ );
+
+ let diagnostics = [
+ // Unknown builtin error
+ Diagnostic::error()
+ .with_message("unknown builtin: `NATRAL`")
+ .with_labels(vec![
+ Label::primary(file_id1, 96..102).with_message("unknown builtin")
+ ])
+ .with_notes(vec![
+ "there is a builtin with a similar name: `NATURAL`".to_owned()
+ ]),
+ // Unused parameter warning
+ Diagnostic::warning()
+ .with_message("unused parameter pattern: `n₂`")
+ .with_labels(vec![
+ Label::primary(file_id1, 285..289).with_message("unused parameter")
+ ])
+ .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
+ // Unexpected type error
+ Diagnostic::error()
+ .with_message("unexpected type in application of `_+_`")
+ .with_code("E0001")
+ .with_labels(vec![
+ Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
+ Label::secondary(file_id1, 130..155)
+ .with_message("based on the definition of `_+_`"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `Nat`
+ found type `String`
+ ",
+ )]),
+ // Incompatible match clause error
+ Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary(file_id3, 163..166).with_message("expected `String`, found `Nat`"),
+ Label::secondary(file_id3, 62..166)
+ .with_message("`case` clauses have incompatible types"),
+ Label::secondary(file_id3, 41..47)
+ .with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )]),
+ // Incompatible match clause error
+ Diagnostic::error()
+ .with_message("`case` clauses have incompatible types")
+ .with_code("E0308")
+ .with_labels(vec![
+ Label::primary(file_id3, 328..331).with_message("expected `String`, found `Nat`"),
+ Label::secondary(file_id3, 211..331)
+ .with_message("`case` clauses have incompatible types"),
+ Label::secondary(file_id3, 258..268)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 284..290)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 306..312)
+ .with_message("this is found to be of type `String`"),
+ Label::secondary(file_id3, 186..192)
+ .with_message("expected type `String` found here"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `String`
+ found type `Nat`
+ ",
+ )]),
+ ];
+
+ let writer = StandardStream::stderr(opts.color.into());
+ let config = codespan_reporting::term::Config::default();
+ for diagnostic in &diagnostics {
+ term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/codespan-reporting/src/diagnostic.rs b/third_party/rust/codespan-reporting/src/diagnostic.rs
new file mode 100644
index 0000000000..c1f98bd435
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/diagnostic.rs
@@ -0,0 +1,209 @@
+//! Diagnostic data structures.
+
+#[cfg(feature = "serialization")]
+use serde::{Deserialize, Serialize};
+use std::ops::Range;
+
+/// 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, PartialEq, Eq, Hash, Debug)]
+#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
+pub enum Severity {
+ /// An unexpected bug.
+ Bug,
+ /// An error.
+ Error,
+ /// A warning.
+ Warning,
+ /// A note.
+ Note,
+ /// A help message.
+ Help,
+}
+
+impl Severity {
+ /// We want bugs to be the maximum severity, errors next, etc...
+ fn to_cmp_int(self) -> u8 {
+ match self {
+ Severity::Bug => 5,
+ Severity::Error => 4,
+ Severity::Warning => 3,
+ Severity::Note => 2,
+ Severity::Help => 1,
+ }
+ }
+}
+
+impl PartialOrd for Severity {
+ fn partial_cmp(&self, other: &Severity) -> Option<std::cmp::Ordering> {
+ u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int())
+ }
+}
+
+#[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<FileId> {
+ /// 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<usize>,
+ /// An optional message to provide some additional information for the
+ /// underlined code. These should not include line breaks.
+ pub message: String,
+}
+
+impl<FileId> Label<FileId> {
+ /// Create a new label.
+ pub fn new(
+ style: LabelStyle,
+ file_id: FileId,
+ range: impl Into<Range<usize>>,
+ ) -> Label<FileId> {
+ 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<Range<usize>>) -> Label<FileId> {
+ 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<Range<usize>>) -> Label<FileId> {
+ Label::new(LabelStyle::Secondary, file_id, range)
+ }
+
+ /// Add a message to the diagnostic.
+ pub fn with_message(mut self, message: impl Into<String>) -> Label<FileId> {
+ self.message = message.into();
+ 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<FileId> {
+ /// The overall severity of the diagnostic
+ pub severity: Severity,
+ /// An optional code that identifies this diagnostic.
+ pub code: Option<String>,
+ /// 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<Label<FileId>>,
+ /// Notes that are associated with the primary cause of the diagnostic.
+ /// These can include line breaks for improved formatting.
+ pub notes: Vec<String>,
+}
+
+impl<FileId> Diagnostic<FileId> {
+ /// Create a new diagnostic.
+ pub fn new(severity: Severity) -> Diagnostic<FileId> {
+ 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<FileId> {
+ Diagnostic::new(Severity::Bug)
+ }
+
+ /// Create a new diagnostic with a severity of [`Severity::Error`].
+ ///
+ /// [`Severity::Error`]: Severity::Error
+ pub fn error() -> Diagnostic<FileId> {
+ Diagnostic::new(Severity::Error)
+ }
+
+ /// Create a new diagnostic with a severity of [`Severity::Warning`].
+ ///
+ /// [`Severity::Warning`]: Severity::Warning
+ pub fn warning() -> Diagnostic<FileId> {
+ Diagnostic::new(Severity::Warning)
+ }
+
+ /// Create a new diagnostic with a severity of [`Severity::Note`].
+ ///
+ /// [`Severity::Note`]: Severity::Note
+ pub fn note() -> Diagnostic<FileId> {
+ Diagnostic::new(Severity::Note)
+ }
+
+ /// Create a new diagnostic with a severity of [`Severity::Help`].
+ ///
+ /// [`Severity::Help`]: Severity::Help
+ pub fn help() -> Diagnostic<FileId> {
+ Diagnostic::new(Severity::Help)
+ }
+
+ /// Set the error code of the diagnostic.
+ pub fn with_code(mut self, code: impl Into<String>) -> Diagnostic<FileId> {
+ self.code = Some(code.into());
+ self
+ }
+
+ /// Set the message of the diagnostic.
+ pub fn with_message(mut self, message: impl Into<String>) -> Diagnostic<FileId> {
+ self.message = message.into();
+ self
+ }
+
+ /// Add some labels to the diagnostic.
+ pub fn with_labels(mut self, mut labels: Vec<Label<FileId>>) -> Diagnostic<FileId> {
+ self.labels.append(&mut labels);
+ self
+ }
+
+ /// Add some notes to the diagnostic.
+ pub fn with_notes(mut self, mut notes: Vec<String>) -> Diagnostic<FileId> {
+ self.notes.append(&mut notes);
+ self
+ }
+}
diff --git a/third_party/rust/codespan-reporting/src/files.rs b/third_party/rust/codespan-reporting/src/files.rs
new file mode 100644
index 0000000000..b25cd79788
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/files.rs
@@ -0,0 +1,443 @@
+//! 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 std::ops::Range;
+
+/// 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.
+ Io(std::io::Error),
+}
+
+impl From<std::io::Error> for Error {
+ fn from(err: std::io::Error) -> Error {
+ Error::Io(err)
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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"),
+ Error::Io(err) => write!(f, "{}", err),
+ }
+ }
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match &self {
+ 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 + std::fmt::Display;
+ /// The source code of a file.
+ type Source: 'a + AsRef<str>;
+
+ /// The user-facing name of a file.
+ fn name(&'a self, id: Self::FileId) -> Result<Self::Name, Error>;
+
+ /// The source code of a file.
+ fn source(&'a self, id: Self::FileId) -> Result<Self::Source, Error>;
+
+ /// 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<usize, Error>;
+
+ /// 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<usize, Error> {
+ 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<usize, Error> {
+ 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<Location, Error> {
+ 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<Range<usize>, 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<usize>, byte_index: usize) -> usize {
+ let end_index = std::cmp::min(byte_index, std::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<usize> {
+/// 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>(source: &'source str) -> impl 'source + Iterator<Item = usize> {
+ std::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<Name, Source> {
+ /// 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<usize>,
+}
+
+impl<Name, Source> SimpleFile<Name, Source>
+where
+ Name: std::fmt::Display,
+ Source: AsRef<str>,
+{
+ /// Create a new source file.
+ pub fn new(name: Name, source: Source) -> SimpleFile<Name, Source> {
+ 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<usize, Error> {
+ use std::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<Name, Source>
+where
+ Name: 'a + std::fmt::Display + Clone,
+ Source: 'a + AsRef<str>,
+{
+ type FileId = ();
+ type Name = Name;
+ type Source = &'a str;
+
+ fn name(&self, (): ()) -> Result<Name, Error> {
+ Ok(self.name.clone())
+ }
+
+ fn source(&self, (): ()) -> Result<&str, Error> {
+ Ok(self.source.as_ref())
+ }
+
+ fn line_index(&self, (): (), byte_index: usize) -> Result<usize, Error> {
+ Ok(self
+ .line_starts
+ .binary_search(&byte_index)
+ .unwrap_or_else(|next_line| next_line - 1))
+ }
+
+ fn line_range(&self, (): (), line_index: usize) -> Result<Range<usize>, 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<SimpleFile>` that implements the `Files` trait.
+#[derive(Debug, Clone)]
+pub struct SimpleFiles<Name, Source> {
+ files: Vec<SimpleFile<Name, Source>>,
+}
+
+impl<Name, Source> SimpleFiles<Name, Source>
+where
+ Name: std::fmt::Display,
+ Source: AsRef<str>,
+{
+ /// Create a new files database.
+ pub fn new() -> SimpleFiles<Name, Source> {
+ 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<Name, Source>, Error> {
+ self.files.get(file_id).ok_or(Error::FileMissing)
+ }
+}
+
+impl<'a, Name, Source> Files<'a> for SimpleFiles<Name, Source>
+where
+ Name: 'a + std::fmt::Display + Clone,
+ Source: 'a + AsRef<str>,
+{
+ type FileId = usize;
+ type Name = Name;
+ type Source = &'a str;
+
+ fn name(&self, file_id: usize) -> Result<Name, Error> {
+ 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<usize, Error> {
+ self.get(file_id)?.line_index((), byte_index)
+ }
+
+ fn line_range(&self, file_id: usize, line_index: usize) -> Result<Range<usize>, 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::<Vec<_>>();
+
+ assert_eq!(line_sources, ["foo\n", "bar\r\n", "\n", "baz"]);
+ }
+}
diff --git a/third_party/rust/codespan-reporting/src/lib.rs b/third_party/rust/codespan-reporting/src/lib.rs
new file mode 100644
index 0000000000..28d7f24deb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/lib.rs
@@ -0,0 +1,7 @@
+//! Diagnostic reporting support for the codespan crate.
+
+#![forbid(unsafe_code)]
+
+pub mod diagnostic;
+pub mod files;
+pub mod term;
diff --git a/third_party/rust/codespan-reporting/src/term.rs b/third_party/rust/codespan-reporting/src/term.rs
new file mode 100644
index 0000000000..59baeb04e1
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/term.rs
@@ -0,0 +1,121 @@
+//! Terminal back-end for emitting diagnostics.
+
+use std::str::FromStr;
+use termcolor::{ColorChoice, WriteColor};
+
+use crate::diagnostic::Diagnostic;
+use crate::files::Files;
+
+mod config;
+mod renderer;
+mod views;
+
+pub use termcolor;
+
+pub use self::config::{Chars, Config, DisplayStyle, Styles};
+
+/// A command line argument that configures the coloring of the output.
+///
+/// This can be used with command line argument parsers like [`clap`] or [`structopt`].
+///
+/// [`clap`]: https://crates.io/crates/clap
+/// [`structopt`]: https://crates.io/crates/structopt
+///
+/// # Example
+///
+/// ```rust
+/// use codespan_reporting::term::termcolor::StandardStream;
+/// use codespan_reporting::term::ColorArg;
+/// use structopt::StructOpt;
+///
+/// #[derive(Debug, StructOpt)]
+/// #[structopt(name = "groovey-app")]
+/// pub struct Opts {
+/// /// Configure coloring of output
+/// #[structopt(
+/// long = "color",
+/// default_value = "auto",
+/// possible_values = ColorArg::VARIANTS,
+/// case_insensitive = true,
+/// )]
+/// pub color: ColorArg,
+/// }
+///
+/// let opts = Opts::from_args();
+/// let writer = StandardStream::stderr(opts.color.into());
+/// ```
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct ColorArg(pub ColorChoice);
+
+impl ColorArg {
+ /// Allowed values the argument.
+ ///
+ /// This is useful for generating documentation via [`clap`] or `structopt`'s
+ /// `possible_values` configuration.
+ ///
+ /// [`clap`]: https://crates.io/crates/clap
+ /// [`structopt`]: https://crates.io/crates/structopt
+ pub const VARIANTS: &'static [&'static str] = &["auto", "always", "ansi", "never"];
+}
+
+impl FromStr for ColorArg {
+ type Err = &'static str;
+
+ fn from_str(src: &str) -> Result<ColorArg, &'static str> {
+ match src {
+ _ if src.eq_ignore_ascii_case("auto") => Ok(ColorArg(ColorChoice::Auto)),
+ _ if src.eq_ignore_ascii_case("always") => Ok(ColorArg(ColorChoice::Always)),
+ _ if src.eq_ignore_ascii_case("ansi") => Ok(ColorArg(ColorChoice::AlwaysAnsi)),
+ _ if src.eq_ignore_ascii_case("never") => Ok(ColorArg(ColorChoice::Never)),
+ _ => Err("valid values: auto, always, ansi, never"),
+ }
+ }
+}
+
+impl Into<ColorChoice> for ColorArg {
+ fn into(self) -> ColorChoice {
+ self.0
+ }
+}
+
+/// 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>>(
+ writer: &mut dyn WriteColor,
+ config: &Config,
+ files: &'files F,
+ diagnostic: &Diagnostic<F::FileId>,
+) -> 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(test)]
+mod tests {
+ 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::<u8>::new());
+ let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]);
+
+ emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap();
+ }
+}
diff --git a/third_party/rust/codespan-reporting/src/term/config.rs b/third_party/rust/codespan-reporting/src/term/config.rs
new file mode 100644
index 0000000000..c92a6b6047
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/term/config.rs
@@ -0,0 +1,321 @@
+use termcolor::{Color, ColorSpec};
+
+use crate::diagnostic::{LabelStyle, Severity};
+
+/// 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.
+ 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,
+}
+
+impl Default for Config {
+ fn default() -> Config {
+ Config {
+ display_style: DisplayStyle::Rich,
+ tab_width: 4,
+ styles: Styles::default(),
+ chars: Chars::default(),
+ start_context_lines: 3,
+ end_context_lines: 1,
+ }
+ }
+}
+
+/// 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.
+#[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,
+}
+
+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(),
+ }
+ }
+}
+
+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: '|',
+ }
+ }
+}
diff --git a/third_party/rust/codespan-reporting/src/term/renderer.rs b/third_party/rust/codespan-reporting/src/term/renderer.rs
new file mode 100644
index 0000000000..eeb8965d25
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/term/renderer.rs
@@ -0,0 +1,1020 @@
+use std::io::{self, Write};
+use std::ops::Range;
+use termcolor::{ColorSpec, WriteColor};
+
+use crate::diagnostic::{LabelStyle, Severity};
+use crate::files::{Error, Location};
+use crate::term::{Chars, Config, Styles};
+
+/// 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<usize>, &'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> {
+ writer: &'writer mut dyn WriteColor,
+ config: &'config Config,
+}
+
+impl<'writer, 'config> Renderer<'writer, 'config> {
+ /// Construct a renderer from the given writer and config.
+ pub fn new(
+ writer: &'writer mut dyn WriteColor,
+ config: &'config Config,
+ ) -> Renderer<'writer, 'config> {
+ Renderer { writer, config }
+ }
+
+ fn chars(&self) -> &'config Chars {
+ &self.config.chars
+ }
+
+ 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
+ // ```
+ 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
+ // ```
+ self.set_color(&self.styles().header_message)?;
+ write!(self, ": {}", message)?;
+ 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)?;
+
+ self.set_color(&self.styles().source_border)?;
+ write!(self, "{}", self.chars().snippet_start)?;
+ 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
+ /// │ ╭─│─────────^
+ /// ```
+ 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 {
+ self.set_color(self.styles().label(severity, LabelStyle::Primary))?;
+ in_primary = true;
+ } else if !is_primary && in_primary {
+ self.reset()?;
+ in_primary = false;
+ }
+
+ match ch {
+ '\t' => (0..metrics.unicode_width).try_for_each(|_| write!(self, " "))?,
+ _ => write!(self, "{}", ch)?,
+ }
+ }
+ if in_primary {
+ 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 = std::cmp::max(max_label_start, range.start);
+ max_label_end = std::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(std::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 => self.reset()?,
+ Some(label_style) => {
+ 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() {
+ self.reset()?;
+ }
+ // Write first trailing label message
+ if let Some((_, (label_style, _, message))) = trailing_label {
+ write!(self, " ")?;
+ self.set_color(self.styles().label(severity, *label_style))?;
+ write!(self, "{}", message)?;
+ 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
+ // ```
+ 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),
+ )?;
+ self.set_color(self.styles().label(severity, *label_style))?;
+ write!(self, "{}", message)?;
+ 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 => {
+ self.set_color(&self.styles().note_bullet)?;
+ write!(self, "{}", self.chars().note_bullet)?;
+ 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<Item = (usize, char)>,
+ ) -> impl Iterator<Item = (Metrics, char)> {
+ 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> {
+ self.set_color(&self.styles().line_number)?;
+ write!(
+ self,
+ "{line_number: >width$}",
+ line_number = line_number,
+ width = outer_padding,
+ )?;
+ self.reset()?;
+ write!(self, " ")?;
+ Ok(())
+ }
+
+ /// The left-hand border of a source line.
+ fn border_left(&mut self) -> Result<(), Error> {
+ self.set_color(&self.styles().source_border)?;
+ write!(self, "{}", self.chars().source_border_left)?;
+ self.reset()?;
+ Ok(())
+ }
+
+ /// The broken left-hand border of a source line.
+ fn border_left_break(&mut self) -> Result<(), Error> {
+ self.set_color(&self.styles().source_border)?;
+ write!(self, "{}", self.chars().source_border_left_break)?;
+ self.reset()?;
+ Ok(())
+ }
+
+ /// Write vertical lines pointing to carets.
+ fn caret_pointers(
+ &mut self,
+ severity: Severity,
+ max_label_start: usize,
+ single_labels: &[SingleLabel<'_>],
+ trailing_label: Option<(usize, &SingleLabel<'_>)>,
+ char_indices: impl Iterator<Item = (usize, char)>,
+ ) -> 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,
+ Some(label_style) => {
+ self.set_color(self.styles().label(severity, label_style))?;
+ write!(self, "{}", self.chars().pointer_left)?;
+ 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,
+ severity: Severity,
+ label_style: LabelStyle,
+ underline: Option<LabelStyle>,
+ ) -> Result<(), Error> {
+ match underline {
+ None => write!(self, " ")?,
+ // Continue an underline horizontally
+ Some(label_style) => {
+ self.set_color(self.styles().label(severity, label_style))?;
+ write!(self, "{}", self.chars().multi_top)?;
+ self.reset()?;
+ }
+ }
+ self.set_color(self.styles().label(severity, label_style))?;
+ write!(self, "{}", self.chars().multi_left)?;
+ self.reset()?;
+ Ok(())
+ }
+
+ /// The top-left of a multi-line label.
+ ///
+ /// ```text
+ /// ╭
+ /// ```
+ fn label_multi_top_left(
+ &mut self,
+ severity: Severity,
+ label_style: LabelStyle,
+ ) -> Result<(), Error> {
+ write!(self, " ")?;
+ self.set_color(self.styles().label(severity, label_style))?;
+ write!(self, "{}", self.chars().multi_top_left)?;
+ self.reset()?;
+ Ok(())
+ }
+
+ /// The bottom left of a multi-line label.
+ ///
+ /// ```text
+ /// ╰
+ /// ```
+ fn label_multi_bottom_left(
+ &mut self,
+ severity: Severity,
+ label_style: LabelStyle,
+ ) -> Result<(), Error> {
+ write!(self, " ")?;
+ self.set_color(self.styles().label(severity, label_style))?;
+ write!(self, "{}", self.chars().multi_bottom_left)?;
+ self.reset()?;
+ Ok(())
+ }
+
+ /// Multi-line label top.
+ ///
+ /// ```text
+ /// ─────────────^
+ /// ```
+ fn label_multi_top_caret(
+ &mut self,
+ severity: Severity,
+ label_style: LabelStyle,
+ source: &str,
+ start: usize,
+ ) -> Result<(), Error> {
+ 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)?;
+ 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,
+ severity: Severity,
+ label_style: LabelStyle,
+ source: &str,
+ start: usize,
+ message: &str,
+ ) -> Result<(), Error> {
+ 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)?;
+ }
+ self.reset()?;
+ writeln!(self)?;
+ Ok(())
+ }
+
+ /// Writes an empty gutter space, or continues an underline horizontally.
+ fn inner_gutter_column(
+ &mut self,
+ severity: Severity,
+ underline: Option<Underline>,
+ ) -> Result<(), Error> {
+ match underline {
+ None => self.inner_gutter_space(),
+ Some((label_style, vertical_bound)) => {
+ 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)?;
+ 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(())
+ }
+}
+
+impl<'writer, 'config> Write for Renderer<'writer, 'config> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.writer.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.writer.flush()
+ }
+}
+
+impl<'writer, 'config> WriteColor for Renderer<'writer, 'config> {
+ 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<usize>, range1: &Range<usize>) -> bool {
+ let start = std::cmp::max(range0.start, range1.start);
+ let end = std::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<Item = &'labels SingleLabel<'diagnostic>> {
+ single_labels
+ .iter()
+ .enumerate()
+ .filter(|(_, (_, _, message))| !message.is_empty())
+ .filter(move |(i, _)| trailing_label.map_or(true, |(j, _)| *i != j))
+ .map(|(_, label)| label)
+}
diff --git a/third_party/rust/codespan-reporting/src/term/views.rs b/third_party/rust/codespan-reporting/src/term/views.rs
new file mode 100644
index 0000000000..f09d9582a6
--- /dev/null
+++ b/third_party/rust/codespan-reporting/src/term/views.rs
@@ -0,0 +1,478 @@
+use std::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;
+
+/// Count the number of decimal digits in `n`.
+fn count_digits(mut n: usize) -> usize {
+ let mut count = 0;
+ while n != 0 {
+ count += 1;
+ n /= 10; // remove last digit
+ }
+ count
+}
+
+/// Output a richly formatted diagnostic, with source code previews.
+pub struct RichDiagnostic<'diagnostic, 'config, FileId> {
+ diagnostic: &'diagnostic Diagnostic<FileId>,
+ config: &'config Config,
+}
+
+impl<'diagnostic, 'config, FileId> RichDiagnostic<'diagnostic, 'config, FileId>
+where
+ FileId: Copy + PartialEq,
+{
+ pub fn new(
+ diagnostic: &'diagnostic Diagnostic<FileId>,
+ config: &'config Config,
+ ) -> RichDiagnostic<'diagnostic, 'config, FileId> {
+ RichDiagnostic { diagnostic, config }
+ }
+
+ pub fn render<'files>(
+ &self,
+ files: &'files impl Files<'files, FileId = FileId>,
+ renderer: &mut Renderer<'_, '_>,
+ ) -> Result<(), Error>
+ where
+ FileId: 'files,
+ {
+ use std::collections::BTreeMap;
+
+ struct LabeledFile<'diagnostic, FileId> {
+ file_id: FileId,
+ start: usize,
+ name: String,
+ location: Location,
+ num_multi_labels: usize,
+ lines: BTreeMap<usize, Line<'diagnostic>>,
+ max_label_style: LabelStyle,
+ }
+
+ impl<'diagnostic, FileId> LabeledFile<'diagnostic, FileId> {
+ fn get_or_insert_line(
+ &mut self,
+ line_index: usize,
+ line_range: Range<usize>,
+ 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: std::ops::Range<usize>,
+ // TODO: How do we reuse these allocations?
+ single_labels: Vec<SingleLabel<'diagnostic>>,
+ 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::<LabeledFile<'_, _>>::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 = std::cmp::max(outer_padding, count_digits(start_line_number));
+ outer_padding = std::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")
+ }
+ };
+
+ 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<usize>` 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 = std::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, _)) = 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,
+ &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<FileId>,
+ show_notes: bool,
+}
+
+impl<'diagnostic, FileId> ShortDiagnostic<'diagnostic, FileId>
+where
+ FileId: Copy + PartialEq,
+{
+ pub fn new(
+ diagnostic: &'diagnostic Diagnostic<FileId>,
+ show_notes: bool,
+ ) -> ShortDiagnostic<'diagnostic, FileId> {
+ ShortDiagnostic {
+ diagnostic,
+ show_notes,
+ }
+ }
+
+ pub fn render<'files>(
+ &self,
+ files: &'files impl Files<'files, FileId = FileId>,
+ 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(())
+ }
+}
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_color.snap
new file mode 100644
index 0000000000..e4a14fb540
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_color.snap
@@ -0,0 +1,11 @@
+---
+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}: {/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_no_color.snap
new file mode 100644
index 0000000000..6a8bf45306
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__medium_no_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+bug:
+error:
+warning:
+note:
+help:
+bug:
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..1ec1a94e44
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_ascii_no_color.snap
@@ -0,0 +1,17 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+bug:
+
+error:
+
+warning:
+
+note:
+
+help:
+
+bug:
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_color.snap
new file mode 100644
index 0000000000..f94d46bce9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_color.snap
@@ -0,0 +1,17 @@
+---
+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}: {/}
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_no_color.snap
new file mode 100644
index 0000000000..1ec1a94e44
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__rich_no_color.snap
@@ -0,0 +1,17 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+bug:
+
+error:
+
+warning:
+
+note:
+
+help:
+
+bug:
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_color.snap
new file mode 100644
index 0000000000..e4a14fb540
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_color.snap
@@ -0,0 +1,11 @@
+---
+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}: {/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_no_color.snap
new file mode 100644
index 0000000000..6a8bf45306
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty__short_no_color.snap
@@ -0,0 +1,11 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+bug:
+error:
+warning:
+note:
+help:
+bug:
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_color.snap
new file mode 100644
index 0000000000..3713ee4bd5
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_color.snap
@@ -0,0 +1,9 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_no_color.snap
new file mode 100644
index 0000000000..635a8303d1
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..bbf44fbd48
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap
@@ -0,0 +1,29 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap
new file mode 100644
index 0000000000..89bb2c0303
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_color.snap
@@ -0,0 +1,29 @@
+---
+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{/}
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap
new file mode 100644
index 0000000000..862605cec0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__rich_no_color.snap
@@ -0,0 +1,29 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap
new file mode 100644
index 0000000000..3713ee4bd5
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_color.snap
@@ -0,0 +1,9 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap
new file mode 100644
index 0000000000..635a8303d1
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__empty_ranges__short_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_color.snap
new file mode 100644
index 0000000000..ef97c2c0e3
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_color.snap
@@ -0,0 +1,11 @@
+---
+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`
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_no_color.snap
new file mode 100644
index 0000000000..1120064845
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__medium_no_color.snap
@@ -0,0 +1,11 @@
+---
+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`
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..55cb453326
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap
@@ -0,0 +1,42 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap
new file mode 100644
index 0000000000..79dded26bd
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_color.snap
@@ -0,0 +1,42 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_no_color.snap
new file mode 100644
index 0000000000..a0b7260a92
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__rich_no_color.snap
@@ -0,0 +1,42 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_color.snap
new file mode 100644
index 0000000000..d25ba9d61b
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_color.snap
@@ -0,0 +1,7 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_no_color.snap
new file mode 100644
index 0000000000..00a243f7ba
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__fizz_buzz__short_no_color.snap
@@ -0,0 +1,7 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_color.snap
new file mode 100644
index 0000000000..f0b16d89fe
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_color.snap
@@ -0,0 +1,9 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_no_color.snap
new file mode 100644
index 0000000000..43565acacb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..d3abe8418e
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_ascii_no_color.snap
@@ -0,0 +1,13 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_color.snap
new file mode 100644
index 0000000000..c1bd4237a8
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_color.snap
@@ -0,0 +1,13 @@
+---
+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{/}
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_no_color.snap
new file mode 100644
index 0000000000..d3abe8418e
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__rich_no_color.snap
@@ -0,0 +1,13 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_color.snap
new file mode 100644
index 0000000000..f0b16d89fe
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_color.snap
@@ -0,0 +1,9 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_no_color.snap
new file mode 100644
index 0000000000..43565acacb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message__short_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_color.snap
new file mode 100644
index 0000000000..f44b2a436a
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_color.snap
@@ -0,0 +1,13 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_no_color.snap
new file mode 100644
index 0000000000..bab7a657dd
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__medium_no_color.snap
@@ -0,0 +1,13 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..d4e209ba14
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap
@@ -0,0 +1,17 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_color.snap
new file mode 100644
index 0000000000..56900e80ef
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_color.snap
@@ -0,0 +1,17 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_no_color.snap
new file mode 100644
index 0000000000..d4e209ba14
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__rich_no_color.snap
@@ -0,0 +1,17 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_color.snap
new file mode 100644
index 0000000000..f0b16d89fe
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_color.snap
@@ -0,0 +1,9 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_no_color.snap
new file mode 100644
index 0000000000..43565acacb
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_and_notes__short_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..e82fb7c5e6
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap
@@ -0,0 +1,21 @@
+---
+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?
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_no_color.snap
new file mode 100644
index 0000000000..e82fb7c5e6
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__rich_no_color.snap
@@ -0,0 +1,21 @@
+---
+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?
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__short_no_color.snap
new file mode 100644
index 0000000000..ccddd8db4f
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__message_errorcode__short_no_color.snap
@@ -0,0 +1,13 @@
+---
+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?
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_color.snap
new file mode 100644
index 0000000000..6c03d83fd0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_color.snap
@@ -0,0 +1,12 @@
+---
+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`
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_no_color.snap
new file mode 100644
index 0000000000..db33fc01f9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__medium_no_color.snap
@@ -0,0 +1,12 @@
+---
+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`
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..e162ed2928
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_ascii_no_color.snap
@@ -0,0 +1,35 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap
new file mode 100644
index 0000000000..514be49db4
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_color.snap
@@ -0,0 +1,35 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_no_color.snap
new file mode 100644
index 0000000000..a626b54b1e
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__rich_no_color.snap
@@ -0,0 +1,35 @@
+---
+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`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_color.snap
new file mode 100644
index 0000000000..e9f40664a7
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_color.snap
@@ -0,0 +1,8 @@
+---
+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 `_+_`{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_no_color.snap
new file mode 100644
index 0000000000..e3e020e363
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multifile__short_no_color.snap
@@ -0,0 +1,8 @@
+---
+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 `_+_`
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_omit__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_omit__rich_no_color.snap
new file mode 100644
index 0000000000..bb9363d91b
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_omit__rich_no_color.snap
@@ -0,0 +1,38 @@
+---
+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}`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_color.snap
new file mode 100644
index 0000000000..afddf2456e
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_color.snap
@@ -0,0 +1,9 @@
+---
+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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_no_color.snap
new file mode 100644
index 0000000000..0e50bd86d9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..3623d4d96a
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap
@@ -0,0 +1,25 @@
+---
+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<ByteIndex, LineIndexOutOfBoundsError>`
+3 | | Ordering::Equal => Ok(self.source_span().end()),
+ | | ---------------------------- this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap
new file mode 100644
index 0000000000..10701fbbb5
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_color.snap
@@ -0,0 +1,25 @@
+---
+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<ByteIndex, LineIndexOutOfBoundsError>`{/}
+{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<ByteIndex, LineIndexOutOfBoundsError>`{/}
+{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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_no_color.snap
new file mode 100644
index 0000000000..f6802c80e2
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__rich_no_color.snap
@@ -0,0 +1,25 @@
+---
+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<ByteIndex, LineIndexOutOfBoundsError>`
+3 │ │ Ordering::Equal => Ok(self.source_span().end()),
+ │ │ ---------------------------- this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+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<ByteIndex, LineIndexOutOfBoundsError>`
+ found type `LineIndexOutOfBoundsError`
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_color.snap
new file mode 100644
index 0000000000..808c6d09d4
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_color.snap
@@ -0,0 +1,6 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_no_color.snap
new file mode 100644
index 0000000000..0254b6e779
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__multiline_overlapping__short_no_color.snap
@@ -0,0 +1,6 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_color.snap
new file mode 100644
index 0000000000..9ccf3e5c4b
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_color.snap
@@ -0,0 +1,17 @@
+---
+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`.
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_no_color.snap
new file mode 100644
index 0000000000..3b9eac98f9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__medium_no_color.snap
@@ -0,0 +1,17 @@
+---
+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`.
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..b6002fcfc0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_ascii_no_color.snap
@@ -0,0 +1,58 @@
+---
+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<u32>) -> impl Into<impl Debug> { 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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap
new file mode 100644
index 0000000000..d29a429aea
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_color.snap
@@ -0,0 +1,58 @@
+---
+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<u32>) -> 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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap
new file mode 100644
index 0000000000..32f8eeca91
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__rich_no_color.snap
@@ -0,0 +1,58 @@
+---
+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<u32>) -> impl Into<impl Debug> { 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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_color.snap
new file mode 100644
index 0000000000..8e33cbf8c9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_color.snap
@@ -0,0 +1,11 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_no_color.snap
new file mode 100644
index 0000000000..47ed5db2f0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__overlapping__short_no_color.snap
@@ -0,0 +1,11 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__medium_no_color.snap
new file mode 100644
index 0000000000..0e40c83596
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__medium_no_color.snap
@@ -0,0 +1,7 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..d99ad148f3
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_ascii_no_color.snap
@@ -0,0 +1,14 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_no_color.snap
new file mode 100644
index 0000000000..35217e4bef
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__rich_no_color.snap
@@ -0,0 +1,14 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__short_no_color.snap
new file mode 100644
index 0000000000..3ec348d0ab
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__position_indicator__short_no_color.snap
@@ -0,0 +1,6 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_color.snap
new file mode 100644
index 0000000000..43e1eb0b78
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_color.snap
@@ -0,0 +1,8 @@
+---
+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`.
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_no_color.snap
new file mode 100644
index 0000000000..6bb55edb8d
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__medium_no_color.snap
@@ -0,0 +1,8 @@
+---
+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`.
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..60e7afbdf2
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_ascii_no_color.snap
@@ -0,0 +1,17 @@
+---
+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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap
new file mode 100644
index 0000000000..38addbb216
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_color.snap
@@ -0,0 +1,17 @@
+---
+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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap
new file mode 100644
index 0000000000..75511a45bc
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__rich_no_color.snap
@@ -0,0 +1,17 @@
+---
+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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_color.snap
new file mode 100644
index 0000000000..c2d86c7546
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_color.snap
@@ -0,0 +1,7 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_no_color.snap
new file mode 100644
index 0000000000..d4f94e1c0c
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_line__short_no_color.snap
@@ -0,0 +1,7 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_color.snap
new file mode 100644
index 0000000000..095b975897
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_color.snap
@@ -0,0 +1,7 @@
+---
+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{/}
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_no_color.snap
new file mode 100644
index 0000000000..a452022ed0
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__medium_no_color.snap
@@ -0,0 +1,7 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+same_range:1:5: error: Unexpected token
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_ascii_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_ascii_no_color.snap
new file mode 100644
index 0000000000..d64f0991cd
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_ascii_no_color.snap
@@ -0,0 +1,14 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error: Unexpected token
+ --> same_range:1:5
+ |
+1 | ::S { }
+ | ^
+ | |
+ | Unexpected '{'
+ | Expected '('
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap
new file mode 100644
index 0000000000..e6ec885e91
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_color.snap
@@ -0,0 +1,14 @@
+---
+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 '('{/}
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap
new file mode 100644
index 0000000000..09510e6fbf
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__rich_no_color.snap
@@ -0,0 +1,14 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+error: Unexpected token
+ ┌─ same_range:1:5
+ │
+1 │ ::S { }
+ │ ^
+ │ │
+ │ Unexpected '{'
+ │ Expected '('
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_color.snap
new file mode 100644
index 0000000000..14ccc40b74
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_color.snap
@@ -0,0 +1,6 @@
+---
+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{/}
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_no_color.snap
new file mode 100644
index 0000000000..94bdc592d4
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__same_ranges__short_no_color.snap
@@ -0,0 +1,6 @@
+---
+source: codespan-reporting/tests/term.rs
+expression: TEST_DATA.emit_no_color(&config)
+---
+same_range:1:5: error: Unexpected token
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_2_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_2_no_color.snap
new file mode 100644
index 0000000000..0faa8d83e1
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_2_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+ │ ^^^^^
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_3_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_3_no_color.snap
new file mode 100644
index 0000000000..40f20a68dc
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_3_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+ │ ^^^^^
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_6_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_6_no_color.snap
new file mode 100644
index 0000000000..018a402775
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_6_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+ │ ^^^^^
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_default_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_default_no_color.snap
new file mode 100644
index 0000000000..67cd187a7b
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tab_columns__tab_width_default_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+ │ ^^^^^
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_3_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_3_no_color.snap
new file mode 100644
index 0000000000..f7328c3196
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_3_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_6_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_6_no_color.snap
new file mode 100644
index 0000000000..bcd1b2b660
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_6_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_default_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_default_no_color.snap
new file mode 100644
index 0000000000..d3a41a07d7
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__tabbed__tab_width_default_no_color.snap
@@ -0,0 +1,23 @@
+---
+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
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__medium_no_color.snap
new file mode 100644
index 0000000000..b5f8780d05
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__medium_no_color.snap
@@ -0,0 +1,29 @@
+---
+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`.
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__rich_no_color.snap
new file mode 100644
index 0000000000..520d10a28f
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__rich_no_color.snap
@@ -0,0 +1,36 @@
+---
+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`.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__short_no_color.snap
new file mode 100644
index 0000000000..ad1dae1983
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode__short_no_color.snap
@@ -0,0 +1,7 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__medium_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__medium_no_color.snap
new file mode 100644
index 0000000000..1b19451afa
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__medium_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap
new file mode 100644
index 0000000000..653a60e0e9
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__rich_no_color.snap
@@ -0,0 +1,29 @@
+---
+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.
+
+
diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap
new file mode 100644
index 0000000000..1b19451afa
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/snapshots/term__unicode_spans__short_no_color.snap
@@ -0,0 +1,9 @@
+---
+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
+
diff --git a/third_party/rust/codespan-reporting/tests/support/color_buffer.rs b/third_party/rust/codespan-reporting/tests/support/color_buffer.rs
new file mode 100644
index 0000000000..b51360ce63
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/support/color_buffer.rs
@@ -0,0 +1,137 @@
+use 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<u8>,
+ 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<usize> {
+ 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<bool> {
+ 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(())
+ }
+}
diff --git a/third_party/rust/codespan-reporting/tests/support/mod.rs b/third_party/rust/codespan-reporting/tests/support/mod.rs
new file mode 100644
index 0000000000..4f7313ac36
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/support/mod.rs
@@ -0,0 +1,31 @@
+use 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<Diagnostic<F::FileId>>,
+}
+
+impl<'files, F: Files<'files>> TestData<'files, F> {
+ fn emit<W: WriteColor>(&'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()
+ }
+}
diff --git a/third_party/rust/codespan-reporting/tests/term.rs b/third_party/rust/codespan-reporting/tests/term.rs
new file mode 100644
index 0000000000..035db9b459
--- /dev/null
+++ b/third_party/rust/codespan-reporting/tests/term.rs
@@ -0,0 +1,1053 @@
+use 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<u32>) -> impl Into<u32> { x }
+
+ fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { 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<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
+ 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<T>(Rc<T>);
+
+ 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<ByteIndex, LineIndexOutOfBoundsError>`"),
+ 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<ByteIndex, LineIndexOutOfBoundsError>`"),
+ ])
+ .with_notes(vec![unindent::unindent(
+ "
+ expected type `Result<ByteIndex, LineIndexOutOfBoundsError>`
+ 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::<Vec<_>>();
+
+ 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 = format!("{}", r#"🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄"#);
+ let invalid_start = 1;
+ let invalid_end = "🐄".len() - 1;
+ assert_eq!(moon_phases.is_char_boundary(invalid_start), false);
+ assert_eq!(moon_phases.is_char_boundary(invalid_end), false);
+ 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);
+}