microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
alex/pythontelem

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_frontend/src/error/tests.rs

198lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![allow(clippy::needless_raw_string_hashes)]
5
6use super::WithSource;
7use crate::compile::SourceMap;
8use expect_test::expect;
9use miette::Diagnostic;
10use qsc_data_structures::span::Span;
11use std::{error::Error, fmt::Write, iter, str::from_utf8};
12use thiserror::Error;
13
14#[derive(Clone, Debug, Diagnostic, Error)]
15enum TestError {
16 #[error("Error: {0}")]
17 #[diagnostic(code("Qsc.Test.Error.NoSpans"))]
18 NoSpans(String),
19 #[error("Error: {0}")]
20 #[diagnostic(code("Qsc.Test.Error.TwoSpans"))]
21 TwoSpans(
22 String,
23 #[label("first label")] Span,
24 #[label("second label")] Span,
25 ),
26}
27
28#[test]
29fn no_files() {
30 let sources = SourceMap::default();
31 let error = TestError::NoSpans("value".into());
32 let formatted_error = format_error(&WithSource::from_map(&sources, error));
33
34 expect![[r#"
35 Error: value
36 "#]]
37 .assert_eq(&formatted_error);
38}
39
40#[test]
41fn error_spans_two_files() {
42 let test1_contents = "namespace Foo {}";
43 let test2_contents = "namespace Bar {}";
44 let mut sources = SourceMap::default();
45 let test1_offset = sources.push("test1.qs".into(), test1_contents.into());
46 let test2_offset = sources.push("test2.qs".into(), test2_contents.into());
47
48 let error = TestError::TwoSpans(
49 "value".into(),
50 span_with_offset(test1_offset, 10, 13),
51 span_with_offset(test2_offset, 10, 13),
52 );
53
54 let formatted_error = format_error(&WithSource::from_map(&sources, error));
55
56 expect![[r#"
57 Error: value
58 [first label] [test1.qs] [Foo]
59 [second label] [test2.qs] [Bar]
60 "#]]
61 .assert_eq(&formatted_error);
62}
63
64#[test]
65fn error_spans_begin() {
66 let test1_contents = "namespace Foo {}";
67 let test2_contents = "namespace Bar {}";
68 let mut sources = SourceMap::default();
69 let test1_offset = sources.push("test1.qs".into(), test1_contents.into());
70 let test2_offset = sources.push("test2.qs".into(), test2_contents.into());
71
72 let error = TestError::TwoSpans(
73 "value".into(),
74 span_with_offset(test1_offset, 0, 13),
75 span_with_offset(test2_offset, 0, 13),
76 );
77
78 let formatted_error = format_error(&WithSource::from_map(&sources, error));
79
80 expect![[r#"
81 Error: value
82 [first label] [test1.qs] [namespace Foo]
83 [second label] [test2.qs] [namespace Bar]
84 "#]]
85 .assert_eq(&formatted_error);
86}
87
88#[allow(clippy::cast_possible_truncation)]
89#[test]
90fn error_spans_eof() {
91 let test1_contents = "namespace Foo {}";
92 let test2_contents = "namespace Bar {}";
93 let mut sources = SourceMap::default();
94 let test1_offset = sources.push("test1.qs".into(), test1_contents.into());
95 let test2_offset = sources.push("test2.qs".into(), test2_contents.into());
96
97 let error = TestError::TwoSpans(
98 "value".into(),
99 span_with_offset(
100 test1_offset,
101 test1_contents.len() as u32,
102 test1_contents.len() as u32,
103 ),
104 span_with_offset(
105 test2_offset,
106 test2_contents.len() as u32,
107 test2_contents.len() as u32,
108 ),
109 );
110
111 let formatted_error = format_error(&WithSource::from_map(&sources, error));
112
113 expect![[r#"
114 Error: value
115 [first label] [test1.qs] []
116 [second label] [test2.qs] []
117 "#]]
118 .assert_eq(&formatted_error);
119}
120
121#[test]
122fn resolve_spans() {
123 let test1_contents = "namespace Foo {}";
124 let test2_contents = "namespace Bar {}";
125 let mut sources = SourceMap::default();
126 let test1_offset = sources.push("test1.qs".into(), test1_contents.into());
127 let test2_offset = sources.push("test2.qs".into(), test2_contents.into());
128
129 let error = TestError::TwoSpans(
130 "value".into(),
131 span_with_offset(test1_offset, 10, 13),
132 span_with_offset(test2_offset, 10, 13),
133 );
134
135 let with_source = WithSource::from_map(&sources, error);
136
137 let resolved_spans = with_source
138 .labels()
139 .expect("expected labels to exist")
140 .map(|l| {
141 let resolved = with_source.resolve_span(l.inner());
142 (
143 resolved.0.name.to_string(),
144 resolved.1.offset(),
145 resolved.1.len(),
146 )
147 })
148 .collect::<Vec<_>>();
149
150 expect![[r#"
151 [
152 (
153 "test1.qs",
154 10,
155 3,
156 ),
157 (
158 "test2.qs",
159 10,
160 3,
161 ),
162 ]
163 "#]]
164 .assert_debug_eq(&resolved_spans);
165}
166
167fn span_with_offset(offset: u32, lo: u32, hi: u32) -> Span {
168 Span {
169 lo: lo + offset,
170 hi: hi + offset,
171 }
172}
173
174fn format_error(error: &WithSource<TestError>) -> String {
175 let mut s = String::new();
176 write!(s, "{error}").expect("writing should succeed");
177 for e in iter::successors(error.source(), |&e| e.source()) {
178 write!(s, ": {e}").expect("writing should succeed");
179 }
180 for label in error.labels().into_iter().flatten() {
181 let span = error
182 .source_code()
183 .expect("expected valid source code")
184 .read_span(label.inner(), 0, 0)
185 .expect("expected to be able to read span");
186
187 write!(
188 s,
189 "\n [{}] [{}] [{}]",
190 label.label().unwrap_or(""),
191 span.name().expect("expected source file name"),
192 from_utf8(span.data()).expect("expected valid utf-8 string"),
193 )
194 .expect("writing should succeed");
195 }
196 writeln!(s).expect("writing should succeed");
197 s
198}
199