microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-2145

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_frontend/src/error/tests.rs

196lines · modecode

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