microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/bloch

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_frontend/src/error.rs

159lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use crate::compile::{Source, SourceMap};
8use miette::{Diagnostic, MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents};
9use std::{
10 error::Error,
11 fmt::{self, Debug, Display, Formatter},
12};
13
14#[derive(Clone, Debug)]
15pub struct WithSource<E> {
16 sources: Vec<Source>,
17 error: E,
18}
19
20impl<E: Diagnostic + Send + Sync> WithSource<E> {
21 pub fn error(&self) -> &E {
22 &self.error
23 }
24
25 pub fn into_error(self) -> E {
26 self.error
27 }
28
29 /// Construct a diagnostic with source information from a source map.
30 /// Since errors may contain labeled spans from any source file in the
31 /// compilation, the entire source map is needed to resolve offsets.
32 pub fn from_map(sources: &SourceMap, error: E) -> Self {
33 // Filter the source map to the relevant sources
34 // to avoid cloning all of them.
35 let mut filtered = Vec::<Source>::new();
36
37 for offset in error
38 .labels()
39 .into_iter()
40 .flatten()
41 .map(|label| u32::try_from(label.offset()).expect("offset should fit into u32"))
42 {
43 let source = sources
44 .find_by_offset(offset)
45 .expect("expected to find source at offset");
46
47 // Keep the vector sorted by source offsets
48 match filtered.binary_search_by_key(&source.offset, |s| s.offset) {
49 Ok(_) => {} // source already in vector
50 Err(pos) => filtered.insert(pos, source.clone()),
51 }
52 }
53
54 Self {
55 sources: filtered,
56 error,
57 }
58 }
59
60 pub fn into_with_source<T>(self) -> WithSource<T>
61 where
62 T: From<E>,
63 {
64 WithSource {
65 sources: self.sources,
66 error: self.error.into(),
67 }
68 }
69
70 /// Takes a span that uses `SourceMap` offsets, and returns
71 /// a span that is relative to the `Source` that the span falls into,
72 /// along with a reference to the `Source`.
73 pub fn resolve_span(&self, span: &SourceSpan) -> (&Source, SourceSpan) {
74 let offset = u32::try_from(span.offset()).expect("expected the offset to fit into u32");
75 let source = self
76 .sources
77 .iter()
78 .rev()
79 .find(|source| offset >= source.offset)
80 .expect("expected to find source at span");
81 (source, with_offset(span, |o| o - (source.offset as usize)))
82 }
83}
84
85impl<E: Diagnostic> Error for WithSource<E> {
86 fn source(&self) -> Option<&(dyn Error + 'static)> {
87 self.error.source()
88 }
89}
90
91impl<E: Diagnostic + Send + Sync> Diagnostic for WithSource<E> {
92 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
93 self.error.code()
94 }
95
96 fn severity(&self) -> Option<miette::Severity> {
97 self.error.severity()
98 }
99
100 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
101 self.error.help()
102 }
103
104 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
105 self.error.url()
106 }
107
108 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
109 Some(self)
110 }
111
112 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
113 self.error.labels()
114 }
115
116 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
117 self.error.related()
118 }
119
120 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
121 self.error.diagnostic_source()
122 }
123}
124
125impl<E: Diagnostic + Display> Display for WithSource<E> {
126 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
127 std::fmt::Display::fmt(&self.error, f)
128 }
129}
130
131impl<E: Diagnostic + Sync + Send> SourceCode for WithSource<E> {
132 fn read_span<'a>(
133 &'a self,
134 span: &SourceSpan,
135 context_lines_before: usize,
136 context_lines_after: usize,
137 ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
138 let (source, source_relative_span) = self.resolve_span(span);
139
140 let contents = source.contents.read_span(
141 &source_relative_span,
142 context_lines_before,
143 context_lines_after,
144 )?;
145
146 Ok(Box::new(MietteSpanContents::new_named(
147 source.name.to_string(),
148 contents.data(),
149 with_offset(contents.span(), |o| o + (source.offset as usize)),
150 contents.line(),
151 contents.column(),
152 contents.line_count(),
153 )))
154 }
155}
156
157fn with_offset(span: &SourceSpan, f: impl FnOnce(usize) -> usize) -> SourceSpan {
158 SourceSpan::new(f(span.offset()).into(), span.len())
159}
160