Skip to content

Commit 7e101db

Browse files
IceSentryrobtfm
andauthored
Report missing import error (#91)
# Objective If an import is missing from the module it currently just unwraps and panics without any explanations. # Solution Don't unwrap and bubble up the error instead. The error reporting is a bit weird because it just points at the start of the file and I'm not sure how to improve it. --------- Co-authored-by: robtfm <[email protected]>
1 parent 5be8be7 commit 7e101db

File tree

8 files changed

+176
-20
lines changed

8 files changed

+176
-20
lines changed

src/compose/mod.rs

+68-20
Original file line numberDiff line numberDiff line change
@@ -1318,20 +1318,22 @@ impl Composer {
13181318
&mut self,
13191319
module_set: &ComposableModuleDefinition,
13201320
shader_defs: &HashMap<String, ShaderDefValue>,
1321-
) -> Result<ComposableModule, ComposerError> {
1321+
) -> Result<ComposableModule, EnsureImportsError> {
13221322
let PreprocessOutput {
13231323
preprocessed_source,
13241324
imports,
13251325
} = self
13261326
.preprocessor
13271327
.preprocess(&module_set.sanitized_source, shader_defs)
1328-
.map_err(|inner| ComposerError {
1329-
inner,
1330-
source: ErrSource::Module {
1331-
name: module_set.name.to_owned(),
1332-
offset: 0,
1333-
defs: shader_defs.clone(),
1334-
},
1328+
.map_err(|inner| {
1329+
EnsureImportsError::from(ComposerError {
1330+
inner,
1331+
source: ErrSource::Module {
1332+
name: module_set.name.to_owned(),
1333+
offset: 0,
1334+
defs: shader_defs.clone(),
1335+
},
1336+
})
13351337
})?;
13361338

13371339
self.ensure_imports(imports.iter().map(|import| &import.definition), shader_defs)?;
@@ -1346,17 +1348,19 @@ impl Composer {
13461348
&preprocessed_source,
13471349
imports,
13481350
)
1351+
.map_err(|err| err.into())
13491352
}
13501353

13511354
// build required ComposableModules for a given set of shader_defs
13521355
fn ensure_imports<'a>(
13531356
&mut self,
13541357
imports: impl IntoIterator<Item = &'a ImportDefinition>,
13551358
shader_defs: &HashMap<String, ShaderDefValue>,
1356-
) -> Result<(), ComposerError> {
1359+
) -> Result<(), EnsureImportsError> {
13571360
for ImportDefinition { import, .. } in imports.into_iter() {
1358-
// we've already ensured imports exist when they were added
1359-
let module_set = self.module_sets.get(import).unwrap();
1361+
let Some(module_set) = self.module_sets.get(import) else {
1362+
return Err(EnsureImportsError::MissingImport(import.to_owned()));
1363+
};
13601364
if module_set.get_module(shader_defs).is_some() {
13611365
continue;
13621366
}
@@ -1381,6 +1385,29 @@ impl Composer {
13811385
}
13821386
}
13831387

1388+
pub enum EnsureImportsError {
1389+
MissingImport(String),
1390+
ComposerError(ComposerError),
1391+
}
1392+
1393+
impl EnsureImportsError {
1394+
fn into_composer_error(self, err_source: ErrSource) -> ComposerError {
1395+
match self {
1396+
EnsureImportsError::MissingImport(import) => ComposerError {
1397+
inner: ComposerErrorInner::ImportNotFound(import.to_owned(), 0),
1398+
source: err_source,
1399+
},
1400+
EnsureImportsError::ComposerError(err) => err,
1401+
}
1402+
}
1403+
}
1404+
1405+
impl From<ComposerError> for EnsureImportsError {
1406+
fn from(value: ComposerError) -> Self {
1407+
EnsureImportsError::ComposerError(value)
1408+
}
1409+
}
1410+
13841411
#[derive(Default)]
13851412
pub struct ComposableModuleDescriptor<'a> {
13861413
pub source: &'a str,
@@ -1594,12 +1621,7 @@ impl Composer {
15941621

15951622
let sanitized_source = self.sanitize_and_set_auto_bindings(source);
15961623

1597-
let PreprocessorMetaData {
1598-
name,
1599-
defines,
1600-
imports,
1601-
..
1602-
} = self
1624+
let PreprocessorMetaData { name, defines, .. } = self
16031625
.preprocessor
16041626
.get_preprocessor_metadata(&sanitized_source, true)
16051627
.map_err(|inner| ComposerError {
@@ -1614,6 +1636,18 @@ impl Composer {
16141636

16151637
let name = name.unwrap_or_default();
16161638

1639+
let PreprocessOutput { imports, .. } = self
1640+
.preprocessor
1641+
.preprocess(&sanitized_source, &shader_defs)
1642+
.map_err(|inner| ComposerError {
1643+
inner,
1644+
source: ErrSource::Constructing {
1645+
path: file_path.to_owned(),
1646+
source: sanitized_source.to_owned(),
1647+
offset: 0,
1648+
},
1649+
})?;
1650+
16171651
// make sure imports have been added
16181652
// and gather additional defs specified at module level
16191653
for (import_name, offset) in imports
@@ -1643,7 +1677,7 @@ impl Composer {
16431677
inner: ComposerErrorInner::ImportNotFound(import_name.clone(), offset),
16441678
source: ErrSource::Constructing {
16451679
path: file_path.to_owned(),
1646-
source: sanitized_source,
1680+
source: sanitized_source.to_owned(),
16471681
offset: 0,
16481682
},
16491683
});
@@ -1652,8 +1686,22 @@ impl Composer {
16521686
self.ensure_imports(
16531687
imports.iter().map(|import| &import.definition),
16541688
&shader_defs,
1655-
)?;
1656-
self.ensure_imports(additional_imports, &shader_defs)?;
1689+
)
1690+
.map_err(|err| {
1691+
err.into_composer_error(ErrSource::Constructing {
1692+
path: file_path.to_owned(),
1693+
source: sanitized_source.to_owned(),
1694+
offset: 0,
1695+
})
1696+
})?;
1697+
self.ensure_imports(additional_imports, &shader_defs)
1698+
.map_err(|err| {
1699+
err.into_composer_error(ErrSource::Constructing {
1700+
path: file_path.to_owned(),
1701+
source: sanitized_source.to_owned(),
1702+
offset: 0,
1703+
})
1704+
})?;
16571705

16581706
let definition = ComposableModuleDefinition {
16591707
name,

src/compose/test.rs

+68
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,74 @@ mod test {
999999
output_eq!(wgsl, "tests/expected/conditional_import_b.txt");
10001000
}
10011001

1002+
#[test]
1003+
fn conditional_missing_import() {
1004+
let mut composer = Composer::default();
1005+
1006+
composer
1007+
.add_composable_module(ComposableModuleDescriptor {
1008+
source: include_str!("tests/conditional_import_fail/mod_a_b.wgsl"),
1009+
file_path: "tests/conditional_import_fail/mod_a_b.wgsl",
1010+
..Default::default()
1011+
})
1012+
.unwrap();
1013+
1014+
let error = composer
1015+
.make_naga_module(NagaModuleDescriptor {
1016+
source: include_str!("tests/conditional_import_fail/top.wgsl"),
1017+
file_path: "tests/conditional_import_fail/top.wgsl",
1018+
..Default::default()
1019+
})
1020+
.err()
1021+
.unwrap();
1022+
1023+
let text = error.emit_to_string(&composer);
1024+
1025+
// let mut f = std::fs::File::create("conditional_missing_import.txt").unwrap();
1026+
// f.write_all(text.as_bytes()).unwrap();
1027+
// drop(f);
1028+
1029+
output_eq!(text, "tests/expected/conditional_missing_import.txt");
1030+
}
1031+
1032+
#[test]
1033+
fn conditional_missing_import_nested() {
1034+
let mut composer = Composer::default();
1035+
1036+
composer
1037+
.add_composable_module(ComposableModuleDescriptor {
1038+
source: include_str!("tests/conditional_import_fail/mod_a_b.wgsl"),
1039+
file_path: "tests/conditional_import_fail/mod_a_b.wgsl",
1040+
..Default::default()
1041+
})
1042+
.unwrap();
1043+
1044+
composer
1045+
.add_composable_module(ComposableModuleDescriptor {
1046+
source: include_str!("tests/conditional_import_fail/middle.wgsl"),
1047+
file_path: "tests/conditional_import_fail/middle.wgsl",
1048+
..Default::default()
1049+
})
1050+
.unwrap();
1051+
1052+
let error = composer
1053+
.make_naga_module(NagaModuleDescriptor {
1054+
source: include_str!("tests/conditional_import_fail/top_nested.wgsl"),
1055+
file_path: "tests/conditional_import_fail/top_nested.wgsl",
1056+
..Default::default()
1057+
})
1058+
.err()
1059+
.unwrap();
1060+
1061+
let text = error.emit_to_string(&composer);
1062+
1063+
// let mut f = std::fs::File::create("conditional_missing_import_nested.txt").unwrap();
1064+
// f.write_all(text.as_bytes()).unwrap();
1065+
// drop(f);
1066+
1067+
output_eq!(text, "tests/expected/conditional_missing_import_nested.txt");
1068+
}
1069+
10021070
#[cfg(feature = "test_shader")]
10031071
#[test]
10041072
fn rusty_imports() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#define_import_path middle
2+
3+
#ifdef USE_A
4+
#import a::b
5+
#endif
6+
7+
fn mid_fn() -> u32 {
8+
return b::C;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#define_import_path a::b
2+
3+
const C: u32 = 1u;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifdef USE_A
2+
#import a::b
3+
#endif
4+
5+
fn main() -> u32 {
6+
return b::C;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import middle
2+
3+
fn main() -> u32 {
4+
return middle::mid_fn();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: required import 'b' not found
2+
┌─ tests/conditional_import_fail/top.wgsl:6:12
3+
4+
6 │ return b::C;
5+
│ ^
6+
7+
= missing import 'b'
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: required import 'b' not found
2+
┌─ tests/conditional_import_fail/top_nested.wgsl:1:1
3+
4+
1 │ #import middle
5+
│ ^
6+
7+
= missing import 'b'
8+

0 commit comments

Comments
 (0)