Skip to content

Fix include error span #2444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions compiler/qsc_project/src/openqasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ where
{
let mut loaded_files = FxHashMap::default();
let mut pending_includes = vec![];
let mut errors = vec![];

// this is the root of the project
// it is the only file that has a full path.
Expand Down Expand Up @@ -61,11 +60,10 @@ where
pending_includes.push(include);
}
}
Err(e) => {
errors.push(super::project::Error::FileSystem {
about_path: doc_uri.to_string(),
error: e.to_string(),
});
Err(_e) => {
// If the source failed to resolve we don't push an error here.
// We will push an error later during lowering, so that we can
// construct the error with the right span.
}
}
}
Expand All @@ -76,7 +74,7 @@ where
path: doc_uri.clone(),
name: get_file_name_from_uri(doc_uri),
lints: Vec::default(),
errors,
errors: vec![],
project_type: super::ProjectType::OpenQASM(sources),
}
}
Expand Down
58 changes: 33 additions & 25 deletions compiler/qsc_qasm/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ mod prim;
mod scan;
mod stmt;

type QasmSourceResult = std::result::Result<QasmSource, crate::parser::Error>;
type QasmSourceResultVec = Vec<std::result::Result<QasmSource, crate::parser::Error>>;

struct Offsetter(pub(super) u32);

impl MutVisitor for Offsetter {
Expand Down Expand Up @@ -52,13 +55,20 @@ impl QasmParseResult {
self.source.has_errors()
}

#[must_use]
pub fn all_errors(&self) -> Vec<WithSource<crate::Error>> {
let mut self_errors = self.errors();
let include_errors = self
.source
.includes()
.iter()
.flat_map(QasmSource::all_errors)
.flat_map(|res| match res {
Ok(qasm_source) => qasm_source.all_errors(),
// If the source failed to resolve we don't push an error here.
// We will push an error later during lowering, so that we can
// construct the error with the right span.
Err(_) => vec![],
})
.map(|e| self.map_error(e))
.collect::<Vec<_>>();

Expand Down Expand Up @@ -100,7 +110,7 @@ fn update_offsets(source_map: &SourceMap, source: &mut QasmSource) {
offsetter.visit_program(&mut source.program);

// Recursively update the includes, their programs, and errors
for include in source.includes_mut() {
for include in source.includes_mut().iter_mut().flatten() {
update_offsets(source_map, include);
}
}
Expand Down Expand Up @@ -132,7 +142,7 @@ fn collect_source_files(source: &QasmSource, files: &mut Vec<(Arc<str>, Arc<str>
files.push((source.path(), source.source()));
// Collect all source files from the includes, this
// begins the recursive process of collecting all source files.
for include in source.includes() {
for include in source.includes().iter().flatten() {
collect_source_files(include, files);
}
}
Expand All @@ -151,7 +161,7 @@ pub struct QasmSource {
errors: Vec<Error>,
/// Any included files that were resolved.
/// Note that this is a recursive structure.
included: Vec<QasmSource>,
included: QasmSourceResultVec,
}

impl QasmSource {
Expand All @@ -161,7 +171,7 @@ impl QasmSource {
path: Arc<str>,
program: Program,
errors: Vec<Error>,
included: Vec<QasmSource>,
included: QasmSourceResultVec,
) -> QasmSource {
QasmSource {
path,
Expand All @@ -177,24 +187,33 @@ impl QasmSource {
if !self.errors().is_empty() {
return true;
}
self.includes().iter().any(QasmSource::has_errors)
self.includes().iter().any(|res| match res {
Ok(qasm_source) => qasm_source.has_errors(),
Err(_) => true,
})
}

#[must_use]
pub fn all_errors(&self) -> Vec<crate::parser::Error> {
let mut self_errors = self.errors();
let include_errors = self.includes().iter().flat_map(QasmSource::all_errors);
let include_errors = self.includes().iter().flat_map(|res| match res {
Ok(qasm_source) => qasm_source.all_errors(),
// If the source failed to resolve we don't push an error here.
// We will push an error later during lowering, so that we can
// construct the error with the right span.
Err(_) => vec![],
});
self_errors.extend(include_errors);
self_errors
}

#[must_use]
pub fn includes(&self) -> &Vec<QasmSource> {
pub fn includes(&self) -> &QasmSourceResultVec {
self.included.as_ref()
}

#[must_use]
pub fn includes_mut(&mut self) -> &mut Vec<QasmSource> {
pub fn includes_mut(&mut self) -> &mut QasmSourceResultVec {
self.included.as_mut()
}

Expand Down Expand Up @@ -226,7 +245,7 @@ impl QasmSource {
/// This function is the start of a recursive process that will resolve all
/// includes in the QASM file. Any includes are parsed as if their contents
/// were defined where the include statement is.
fn parse_qasm_file<R>(path: &Arc<str>, resolver: &mut R) -> QasmSource
fn parse_qasm_file<R>(path: &Arc<str>, resolver: &mut R) -> QasmSourceResult
where
R: SourceResolver,
{
Expand All @@ -239,22 +258,11 @@ where
// and cyclic includes.
resolver.ctx().pop_current_file();

parse_result
Ok(parse_result)
}
Err(e) => {
let error = crate::parser::error::ErrorKind::IO(e);
let error = crate::parser::Error(error, None);
QasmSource {
path: path.clone(),
source: Default::default(),
program: Program {
span: Span::default(),
statements: vec![].into_boxed_slice(),
version: None,
},
errors: vec![error],
included: vec![],
}
Err(crate::parser::Error(error, None))
}
}
}
Expand All @@ -270,7 +278,7 @@ where
fn parse_source_and_includes<P: AsRef<str>, R>(
source: P,
resolver: &mut R,
) -> (Program, Vec<Error>, Vec<QasmSource>)
) -> (Program, Vec<Error>, QasmSourceResultVec)
where
R: SourceResolver,
{
Expand All @@ -279,7 +287,7 @@ where
(program, errors, included)
}

fn parse_includes<R>(program: &Program, resolver: &mut R) -> Vec<QasmSource>
fn parse_includes<R>(program: &Program, resolver: &mut R) -> QasmSourceResultVec
where
R: SourceResolver,
{
Expand Down
9 changes: 8 additions & 1 deletion compiler/qsc_qasm/src/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ fn programs_with_includes_with_includes_can_be_parsed() -> miette::Result<(), Ve

let res = parse_all("source0.qasm", all_sources)?;
assert!(res.source.includes().len() == 1);
assert!(res.source.includes()[0].includes().len() == 1);
assert!(
res.source.includes()[0]
.as_ref()
.expect("file should exists")
.includes()
.len()
== 1
);
Ok(())
}
9 changes: 8 additions & 1 deletion compiler/qsc_qasm/src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl QasmSemanticParseResult {
!self.errors.is_empty()
}

#[must_use]
pub fn sytax_errors(&self) -> Vec<WithSource<crate::Error>> {
let mut self_errors = self
.source
Expand All @@ -58,7 +59,13 @@ impl QasmSemanticParseResult {
.source
.includes()
.iter()
.flat_map(QasmSource::all_errors)
.flat_map(|res| match res {
Ok(qasm_source) => qasm_source.all_errors(),
// If the source failed to resolve we don't push an error here.
// We will push an error later during lowering, so that we can
// construct the error with the right span.
Err(_) => vec![],
})
.map(|e| self.map_parse_error(e))
.collect::<Vec<_>>();

Expand Down
3 changes: 3 additions & 0 deletions compiler/qsc_qasm/src/semantic/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ pub enum SemanticErrorKind {
#[error("{0} can only appear in {1} scopes")]
#[diagnostic(code("Qasm.Lowerer.InvalidScope"))]
InvalidScope(String, String, #[label] Span),
#[error("{0}")]
#[diagnostic(code("Qasm.Lowerer.IO"))]
IO(String, #[label] Span),
#[error("measure statements must have a name")]
#[diagnostic(code("Qasm.Lowerer.MeasureExpressionsMustHaveName"))]
MeasureExpressionsMustHaveName(#[label] Span),
Expand Down
11 changes: 9 additions & 2 deletions compiler/qsc_qasm/src/semantic/lowerer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,15 @@ impl Lowerer {
continue;
}

let include = includes.next().expect("missing include");
self.lower_source(include);
match includes.next().expect("missing include") {
Ok(include) => self.lower_source(include),
Err(e) => {
self.push_semantic_error(SemanticErrorKind::IO(
e.to_string(),
include.span,
));
}
}
} else {
let mut stmts = self.lower_stmt(stmt);
self.stmts.append(&mut stmts);
Expand Down
23 changes: 23 additions & 0 deletions compiler/qsc_qasm/src/tests/statement/include.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,26 @@ fn cyclic_include_errors() {
source3.inc includes source1.inc"#]]
.assert_eq(&errors_string);
}

#[test]
fn missing_include_error() {
let main = r#"
include "source1.inc";
"#;
let all_sources = [("main.qasm".into(), main.into())];
let config = CompilerConfig::new(
QubitSemantics::Qiskit,
OutputSemantics::Qiskit,
ProgramType::File,
Some("Test".into()),
None,
);

let Err(errors) = compile_all_with_config("main.qasm", all_sources, config) else {
panic!("expected errors")
};

let errors: Vec<_> = errors.iter().map(|e| format!("{e}")).collect();
let errors_string = errors.join("\n");
expect!["Not Found Could not resolve include file: source1.inc"].assert_eq(&errors_string);
}
Loading