Skip to content

Commit 66e106b

Browse files
wit-parser: Add validation hooks for custom linting (#2419)
* add public span to resolve items * add span property where needed * make render_location public * add source_map to Span * use a single source map * add spans to more items * add tests for new spanned items * add push_source_map public API and make adjust_spans pub(crate) * adjust spans upfront
1 parent eedf8b3 commit 66e106b

10 files changed

Lines changed: 805 additions & 14 deletions

File tree

crates/wit-component/src/encoding.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,6 +2932,7 @@ impl<'a> Shims<'a> {
29322932
result: wit_result,
29332933
docs: Default::default(),
29342934
stability: Stability::Unknown,
2935+
span: None,
29352936
},
29362937
if async_ {
29372938
AbiVariant::GuestImportAsync
@@ -3029,6 +3030,7 @@ fn task_return_options_and_type(
30293030
result: None,
30303031
docs: Default::default(),
30313032
stability: Stability::Unknown,
3033+
span: None,
30323034
};
30333035
let abi = AbiVariant::GuestImport;
30343036
let options = RequiredOptions::for_import(resolve, &func_tmp, abi);

crates/wit-component/src/metadata.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl Default for Bindgen {
9292
include_names: Default::default(),
9393
package: Some(package),
9494
stability: Default::default(),
95+
span: None,
9596
});
9697
resolve.packages[package]
9798
.worlds

crates/wit-dylib/tests/roundtrip.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
133133
result: Some(Type::U32),
134134
stability: Default::default(),
135135
docs: Default::default(),
136+
span: None,
136137
},
137138
);
138139
funcs.insert(
@@ -144,6 +145,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
144145
result: None,
145146
stability: Default::default(),
146147
docs: Default::default(),
148+
span: None,
147149
},
148150
);
149151
funcs.insert(
@@ -155,10 +157,12 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
155157
result: Some(Type::U32),
156158
stability: Default::default(),
157159
docs: Default::default(),
160+
span: None,
158161
},
159162
);
160163
funcs
161164
},
165+
span: None,
162166
});
163167

164168
// Generate two worlds in our custom package, one for the callee and one for
@@ -173,6 +177,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
173177
includes: Default::default(),
174178
include_names: Default::default(),
175179
docs: Default::default(),
180+
span: None,
176181
});
177182
let caller = resolve.worlds.alloc(World {
178183
name: "caller".to_string(),
@@ -183,6 +188,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
183188
includes: Default::default(),
184189
include_names: Default::default(),
185190
docs: Default::default(),
191+
span: None,
186192
});
187193

188194
// Add an extra import/export for our synthesized interfaces as well.
@@ -214,6 +220,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
214220
result: None,
215221
stability: Default::default(),
216222
docs: Default::default(),
223+
span: None,
217224
}),
218225
);
219226

@@ -316,13 +323,15 @@ fn update_resources(resolve: &mut Resolve) {
316323
owner: TypeOwner::None,
317324
docs: Default::default(),
318325
stability: Default::default(),
326+
span: None,
319327
});
320328
let borrow = resolve.types.alloc(TypeDef {
321329
name: None,
322330
kind: TypeDefKind::Handle(Handle::Borrow(resource_id)),
323331
owner: TypeOwner::None,
324332
docs: Default::default(),
325333
stability: Default::default(),
334+
span: None,
326335
});
327336
let iface = &mut resolve.interfaces[interface_id];
328337
let ctor = format!("[constructor]{resource_name}");
@@ -339,6 +348,7 @@ fn update_resources(resolve: &mut Resolve) {
339348
result: Some(Type::Id(own)),
340349
stability: Default::default(),
341350
docs: Default::default(),
351+
span: None,
342352
},
343353
);
344354
iface.functions.insert(
@@ -350,6 +360,7 @@ fn update_resources(resolve: &mut Resolve) {
350360
result: Some(Type::U32),
351361
stability: Default::default(),
352362
docs: Default::default(),
363+
span: None,
353364
},
354365
);
355366
}

crates/wit-parser/src/ast.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,13 +1689,13 @@ fn eat_id(tokens: &mut Tokenizer<'_>, expected: &str) -> Result<Span> {
16891689
/// [`UnresolvedPackage`].
16901690
///
16911691
/// [`UnresolvedPackage`]: crate::UnresolvedPackage
1692-
#[derive(Clone, Default)]
1692+
#[derive(Clone, Default, Debug)]
16931693
pub struct SourceMap {
16941694
sources: Vec<Source>,
16951695
offset: u32,
16961696
}
16971697

1698-
#[derive(Clone)]
1698+
#[derive(Clone, Debug)]
16991699
struct Source {
17001700
offset: u32,
17011701
path: String,
@@ -1754,6 +1754,21 @@ impl SourceMap {
17541754
self.offset = new_offset;
17551755
}
17561756

1757+
/// Appends all sources from another `SourceMap` into this one.
1758+
///
1759+
/// Returns the byte offset that should be added to all `Span.start` and
1760+
/// `Span.end` values from the appended source map to make them valid
1761+
/// in the combined source map.
1762+
pub fn append(&mut self, other: SourceMap) -> u32 {
1763+
let base = self.offset;
1764+
for mut source in other.sources {
1765+
source.offset += base;
1766+
self.sources.push(source);
1767+
}
1768+
self.offset += other.offset;
1769+
base
1770+
}
1771+
17571772
/// Parses the files added to this source map into a
17581773
/// [`UnresolvedPackageGroup`].
17591774
pub fn parse(self) -> Result<UnresolvedPackageGroup> {
@@ -1900,7 +1915,8 @@ impl SourceMap {
19001915
return msg;
19011916
}
19021917

1903-
pub(crate) fn render_location(&self, span: Span) -> String {
1918+
/// Renders a span as a human-readable location string (e.g., "file.wit:10:5").
1919+
pub fn render_location(&self, span: Span) -> String {
19041920
let src = self.source_for_offset(span.start);
19051921
let start = src.to_relative_offset(span.start);
19061922
let (line, col) = src.linecol(start);

crates/wit-parser/src/ast/lex.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,22 @@ struct CrlfFold<'a> {
2121
}
2222

2323
/// A span, designating a range of bytes where a token is located.
24-
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
24+
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
2525
pub struct Span {
2626
/// The start of the range.
2727
pub start: u32,
2828
/// The end of the range (exclusive).
2929
pub end: u32,
3030
}
3131

32+
impl Span {
33+
/// Adjusts this span by adding the given byte offset to both start and end.
34+
pub fn adjust(&mut self, offset: u32) {
35+
self.start += offset;
36+
self.end += offset;
37+
}
38+
}
39+
3240
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
3341
pub enum Token {
3442
Whitespace,

crates/wit-parser/src/ast/resolve.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ impl<'a> Resolver<'a> {
329329
stability: Default::default(),
330330
functions: IndexMap::default(),
331331
package: None,
332+
span: Some(span),
332333
})
333334
}
334335

@@ -348,6 +349,7 @@ impl<'a> Resolver<'a> {
348349
includes: Default::default(),
349350
include_names: Default::default(),
350351
stability: Default::default(),
352+
span: Some(span),
351353
})
352354
}
353355

@@ -599,6 +601,7 @@ impl<'a> Resolver<'a> {
599601
kind: TypeDefKind::Unknown,
600602
name: Some(name.name.name.to_string()),
601603
owner: TypeOwner::Interface(iface),
604+
span: Some(name.name.span),
602605
});
603606
self.unknown_type_spans.push(name.name.span);
604607
self.type_spans.push(name.name.span);
@@ -951,6 +954,7 @@ impl<'a> Resolver<'a> {
951954
kind,
952955
name: Some(def.name.name.to_string()),
953956
owner,
957+
span: Some(def.name.span),
954958
});
955959
self.type_spans.push(def.name.span);
956960
self.define_interface_name(&def.name, TypeOrItem::Type(id))?;
@@ -999,13 +1003,15 @@ impl<'a> Resolver<'a> {
9991003
)),
10001004
};
10011005
self.type_spans.push(name.name.span);
1006+
let span = name.name.span;
10021007
let name = name.as_.as_ref().unwrap_or(&name.name);
10031008
let id = self.types.alloc(TypeDef {
10041009
docs: Docs::default(),
10051010
stability: stability.clone(),
10061011
kind: TypeDefKind::Type(Type::Id(id)),
10071012
name: Some(name.name.to_string()),
10081013
owner,
1014+
span: Some(span),
10091015
});
10101016
self.define_interface_name(name, TypeOrItem::Type(id))?;
10111017
}
@@ -1096,6 +1102,7 @@ impl<'a> Resolver<'a> {
10961102
kind,
10971103
params,
10981104
result,
1105+
span: Some(func.span),
10991106
})
11001107
}
11011108

@@ -1263,6 +1270,7 @@ impl<'a> Resolver<'a> {
12631270
docs: self.docs(&field.docs),
12641271
name: field.name.name.to_string(),
12651272
ty: self.resolve_type(&field.ty, stability)?,
1273+
span: Some(field.name.span),
12661274
})
12671275
})
12681276
.collect::<Result<Vec<_>>>()?;
@@ -1275,6 +1283,7 @@ impl<'a> Resolver<'a> {
12751283
.map(|flag| Flag {
12761284
docs: self.docs(&flag.docs),
12771285
name: flag.name.name.to_string(),
1286+
span: Some(flag.name.span),
12781287
})
12791288
.collect::<Vec<_>>();
12801289
TypeDefKind::Flags(Flags { flags })
@@ -1299,6 +1308,7 @@ impl<'a> Resolver<'a> {
12991308
docs: self.docs(&case.docs),
13001309
name: case.name.name.to_string(),
13011310
ty: self.resolve_optional_type(case.ty.as_ref(), stability)?,
1311+
span: Some(case.name.span),
13021312
})
13031313
})
13041314
.collect::<Result<Vec<_>>>()?;
@@ -1315,6 +1325,7 @@ impl<'a> Resolver<'a> {
13151325
Ok(EnumCase {
13161326
docs: self.docs(&case.docs),
13171327
name: case.name.name.to_string(),
1328+
span: Some(case.name.span),
13181329
})
13191330
})
13201331
.collect::<Result<Vec<_>>>()?;
@@ -1453,6 +1464,7 @@ impl<'a> Resolver<'a> {
14531464
docs: Docs::default(),
14541465
stability,
14551466
owner: TypeOwner::None,
1467+
span: Some(ty.span()),
14561468
},
14571469
ty.span(),
14581470
))
@@ -1654,6 +1666,7 @@ impl<'a> Resolver<'a> {
16541666
kind,
16551667
name: None,
16561668
owner: TypeOwner::None,
1669+
span: Some(span),
16571670
},
16581671
span,
16591672
);

crates/wit-parser/src/decoding.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ impl ComponentInfo {
311311
includes: Default::default(),
312312
include_names: Default::default(),
313313
stability: Default::default(),
314+
span: None,
314315
});
315316
let mut package = Package {
316317
// Similar to `world_name` above this is arbitrarily chosen as it's
@@ -900,6 +901,7 @@ impl WitPackageDecoder<'_> {
900901
functions: IndexMap::default(),
901902
package: None,
902903
stability: Default::default(),
904+
span: None,
903905
})
904906
});
905907

@@ -955,6 +957,7 @@ impl WitPackageDecoder<'_> {
955957
functions: IndexMap::default(),
956958
package: None,
957959
stability: Default::default(),
960+
span: None,
958961
};
959962

960963
let owner = TypeOwner::Interface(self.resolve.interfaces.next_id());
@@ -1051,6 +1054,7 @@ impl WitPackageDecoder<'_> {
10511054
docs: Default::default(),
10521055
stability: Default::default(),
10531056
owner,
1057+
span: None,
10541058
});
10551059

10561060
// If this is a resource then doubly-register it in `self.resources` so
@@ -1088,6 +1092,7 @@ impl WitPackageDecoder<'_> {
10881092
include_names: Default::default(),
10891093
package: None,
10901094
stability: Default::default(),
1095+
span: None,
10911096
};
10921097

10931098
let owner = TypeOwner::World(self.resolve.worlds.next_id());
@@ -1238,6 +1243,7 @@ impl WitPackageDecoder<'_> {
12381243
name: name.to_string(),
12391244
params,
12401245
result,
1246+
span: None,
12411247
})
12421248
}
12431249

@@ -1287,6 +1293,7 @@ impl WitPackageDecoder<'_> {
12871293
stability: Default::default(),
12881294
owner: TypeOwner::None,
12891295
kind,
1296+
span: None,
12901297
});
12911298
let prev = self.type_map.insert(id.into(), ty);
12921299
assert!(prev.is_none());
@@ -1353,6 +1360,7 @@ impl WitPackageDecoder<'_> {
13531360
format!("failed to convert record field '{name}'")
13541361
})?,
13551362
docs: Default::default(),
1363+
span: None,
13561364
})
13571365
})
13581366
.collect::<Result<_>>()?;
@@ -1371,6 +1379,7 @@ impl WitPackageDecoder<'_> {
13711379
None => None,
13721380
},
13731381
docs: Default::default(),
1382+
span: None,
13741383
})
13751384
})
13761385
.collect::<Result<_>>()?;
@@ -1383,6 +1392,7 @@ impl WitPackageDecoder<'_> {
13831392
.map(|name| Flag {
13841393
name: name.to_string(),
13851394
docs: Default::default(),
1395+
span: None,
13861396
})
13871397
.collect();
13881398
Ok(TypeDefKind::Flags(Flags { flags }))
@@ -1395,6 +1405,7 @@ impl WitPackageDecoder<'_> {
13951405
.map(|name| EnumCase {
13961406
name: name.into(),
13971407
docs: Default::default(),
1408+
span: None,
13981409
})
13991410
.collect();
14001411
Ok(TypeDefKind::Enum(Enum { cases }))

0 commit comments

Comments
 (0)