Skip to content

Commit 113a807

Browse files
committed
Improve previous import/export diagnostic.
The error now includes the previous span.
1 parent 54f6981 commit 113a807

5 files changed

Lines changed: 46 additions & 14 deletions

File tree

crates/wac-parser/src/resolution.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,11 @@ pub enum Error {
641641
/// The kind of extern name.
642642
kind: ExternKind,
643643
/// The span where the error occurred.
644-
#[label(primary, "duplicate name `{name}`")]
644+
#[label(primary, "duplicate {kind} name `{name}`")]
645645
span: SourceSpan,
646+
/// The span where the error occurred.
647+
#[label("previous {kind} here")]
648+
previous: SourceSpan,
646649
},
647650
/// An invalid extern name was encountered.
648651
#[error("{kind} name `{name}` is not valid")]

crates/wac-parser/src/resolution/ast.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ struct Import<'a> {
3535
item: ItemId,
3636
}
3737

38+
struct Export {
39+
/// The span where the export was first introduced.
40+
span: SourceSpan,
41+
/// The exported item.
42+
item: ItemId,
43+
}
44+
3845
struct State<'a> {
3946
resolver: Option<Box<dyn PackageResolver>>,
4047
scopes: Vec<Scope>,
@@ -47,6 +54,8 @@ struct State<'a> {
4754
/// The map of imported items.
4855
/// This is used to keep track of implicit imports and merge them together.
4956
imports: IndexMap<String, Import<'a>>,
57+
/// The map of exported items.
58+
exports: IndexMap<String, Export>,
5059
}
5160

5261
impl<'a> State<'a> {
@@ -59,6 +68,7 @@ impl<'a> State<'a> {
5968
package_map: Default::default(),
6069
aliases: Default::default(),
6170
imports: Default::default(),
71+
exports: Default::default(),
6272
}
6373
}
6474

@@ -143,15 +153,13 @@ impl<'a> State<'a> {
143153
pub struct AstResolver<'a> {
144154
document: &'a ast::Document<'a>,
145155
definitions: Definitions,
146-
exports: IndexMap<String, ItemId>,
147156
}
148157

149158
impl<'a> AstResolver<'a> {
150159
pub fn new(document: &'a ast::Document<'a>) -> Self {
151160
Self {
152161
document,
153162
definitions: Default::default(),
154-
exports: Default::default(),
155163
}
156164
}
157165

@@ -183,7 +191,11 @@ impl<'a> AstResolver<'a> {
183191
.into_iter()
184192
.map(|(k, v)| (k, v.item))
185193
.collect(),
186-
exports: self.exports,
194+
exports: state
195+
.exports
196+
.into_iter()
197+
.map(|(k, v)| (k, v.item))
198+
.collect(),
187199
})
188200
}
189201

@@ -268,6 +280,7 @@ impl<'a> AstResolver<'a> {
268280
name: name.to_owned(),
269281
kind: ExternKind::Import,
270282
span,
283+
previous: existing.span,
271284
});
272285
}
273286
_ => unreachable!(),
@@ -298,13 +311,19 @@ impl<'a> AstResolver<'a> {
298311
) -> ResolutionResult<()> {
299312
log::debug!("resolving type statement");
300313

301-
let (name, item) = match stmt {
302-
ast::TypeStatement::Interface(i) => (i.id.string, self.interface_decl(state, i)?),
303-
ast::TypeStatement::World(w) => (w.id.string, self.world_decl(state, w)?),
304-
ast::TypeStatement::Type(t) => (t.id().string, self.type_decl(state, t)?),
314+
let (id, item) = match stmt {
315+
ast::TypeStatement::Interface(i) => (i.id, self.interface_decl(state, i)?),
316+
ast::TypeStatement::World(w) => (w.id, self.world_decl(state, w)?),
317+
ast::TypeStatement::Type(t) => (*t.id(), self.type_decl(state, t)?),
305318
};
306319

307-
let prev = self.exports.insert(name.to_owned(), item);
320+
let prev = state.exports.insert(
321+
id.string.to_owned(),
322+
Export {
323+
span: id.span,
324+
item,
325+
},
326+
);
308327
assert!(prev.is_none());
309328
Ok(())
310329
}
@@ -387,15 +406,16 @@ impl<'a> AstResolver<'a> {
387406
}
388407
}
389408

390-
if self.exports.contains_key(name) {
409+
if let Some(existing) = state.exports.get(name) {
391410
return Err(Error::DuplicateExternName {
392411
name: name.to_owned(),
393412
kind: ExternKind::Export,
394413
span,
414+
previous: existing.span,
395415
});
396416
}
397417

398-
let prev = self.exports.insert(name.to_owned(), item);
418+
let prev = state.exports.insert(name.to_owned(), Export { span, item });
399419
assert!(prev.is_none());
400420

401421
Ok(())

crates/wac-parser/tests/resolution/fail/export-duplicate-name.wac.result

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ failed to resolve document
22

33
× duplicate export `x`
44
╭─[tests/resolution/fail/export-duplicate-name.wac:5:15]
5+
3 │ import f: func();
56
4 │ export f with "x";
7+
· ─┬─
8+
· ╰── previous export here
69
5 │ export f with "x";
710
· ─┬─
8-
· ╰── duplicate name `x`
11+
· ╰── duplicate export name `x`
912
╰────

crates/wac-parser/tests/resolution/fail/import-duplicate-name.wac.result

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ failed to resolve document
22

33
× duplicate import `foo`
44
╭─[tests/resolution/fail/import-duplicate-name.wac:4:15]
5+
2 │
56
3 │ import x with "foo": func();
7+
· ──┬──
8+
· ╰── previous import here
69
4 │ import y with "foo": interface {};
710
· ──┬──
8-
· ╰── duplicate name `foo`
11+
· ╰── duplicate import name `foo`
912
╰────

crates/wac-parser/tests/resolution/fail/windows-file.wac.result

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ failed to resolve document
22

33
× duplicate import `x`
44
╭─[tests/resolution/fail/windows-file.wac:4:8]
5+
2 │
56
3 │ import x: func();
7+
· ┬
8+
· ╰── previous import here
69
4 │ import x: func();
710
· ┬
8-
· ╰── duplicate name `x`
11+
· ╰── duplicate import name `x`
912
╰────

0 commit comments

Comments
 (0)