Skip to content

Commit b41cf99

Browse files
tamirmsclaude
andcommitted
Remove post-filter validation, use checked arithmetic in wire sizes
The IR emitter faithfully represents the filtered XDR — semantic validation of the source (union coverage, dangling references) is the consumer's responsibility, not the emitter's. Remove validate_union_coverage and simplify filter_spec to just filter. Use checked arithmetic in wire size computation: checked_add, checked_mul, and u32::try_from for const resolution. Return None (variable size) on overflow or unresolvable constants instead of panicking or silently wrapping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 160756c commit b41cf99

File tree

10 files changed

+163
-218
lines changed

10 files changed

+163
-218
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ xdr-json: src/generated.rs
5252
mkdir -p xdr-json
5353
cargo run --features cli -- types schema-files --out-dir xdr-json
5454

55-
xdr-definitions-json: $(sort $(wildcard xdr/curr/*.x))
55+
xdr-definitions-json: $(sort $(wildcard xdr/*.x))
5656
mkdir -p xdr-definitions-json
5757
cargo run --manifest-path xdr-generator-rust/generator/Cargo.toml -- \
58-
$(addprefix --input ,$(sort $(wildcard xdr/curr/*.x))) \
59-
--json-ast xdr-definitions-json/curr.json
58+
$(addprefix --input ,$(sort $(wildcard xdr/*.x))) \
59+
--json-ast xdr-definitions-json/xdr.json
6060

6161
clean:
6262
rm -f src/generated.rs
Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,10 @@
16881688
"fixed_wire_size": null,
16891689
"kind": "const"
16901690
},
1691+
"TestNextType": {
1692+
"fixed_wire_size": 4,
1693+
"kind": "struct"
1694+
},
16911695
"ThresholdIndexes": {
16921696
"fixed_wire_size": 4,
16931697
"kind": "enum"
@@ -1967,56 +1971,56 @@
19671971
"definitions": [],
19681972
"files": [
19691973
{
1970-
"name": "xdr/curr/Stellar-SCP.x",
1974+
"name": "xdr/Stellar-SCP.x",
19711975
"sha256": "6aed428fb6c2d000f5bc1eef0ba685d6108f3faa96208ffa588c0e2990813939"
19721976
},
19731977
{
1974-
"name": "xdr/curr/Stellar-contract-config-setting.x",
1978+
"name": "xdr/Stellar-contract-config-setting.x",
19751979
"sha256": "a034a3eb4d8b94f5c4c573fe14a1afc548aa316e1e897aa70e5a1688aada3c77"
19761980
},
19771981
{
1978-
"name": "xdr/curr/Stellar-contract-env-meta.x",
1982+
"name": "xdr/Stellar-contract-env-meta.x",
19791983
"sha256": "75a271414d852096fea3283c63b7f2a702f2905f78fc28eb60ec7d7bd366a780"
19801984
},
19811985
{
1982-
"name": "xdr/curr/Stellar-contract-meta.x",
1986+
"name": "xdr/Stellar-contract-meta.x",
19831987
"sha256": "f01532c11ca044e19d9f9f16fe373e9af64835da473be556b9a807ee3319ae0d"
19841988
},
19851989
{
1986-
"name": "xdr/curr/Stellar-contract-spec.x",
1990+
"name": "xdr/Stellar-contract-spec.x",
19871991
"sha256": "7d99679155f6ce029f4f2bd8e1bf09524ef2f3e4ca8973265085cfcfdbdae987"
19881992
},
19891993
{
1990-
"name": "xdr/curr/Stellar-contract.x",
1994+
"name": "xdr/Stellar-contract.x",
19911995
"sha256": "dce61df115c93fef5bb352beac1b504a518cb11dcb8ee029b1bb1b5f8fe52982"
19921996
},
19931997
{
1994-
"name": "xdr/curr/Stellar-exporter.x",
1998+
"name": "xdr/Stellar-exporter.x",
19951999
"sha256": "a00c83d02e8c8382e06f79a191f1fb5abd097a4bbcab8481c67467e3270e0529"
19962000
},
19972001
{
1998-
"name": "xdr/curr/Stellar-internal.x",
2002+
"name": "xdr/Stellar-internal.x",
19992003
"sha256": "227835866c1b2122d1eaf28839ba85ea7289d1cb681dda4ca619c2da3d71fe00"
20002004
},
20012005
{
2002-
"name": "xdr/curr/Stellar-ledger-entries.x",
2006+
"name": "xdr/Stellar-ledger-entries.x",
20032007
"sha256": "5157cad76b008b3606fe5bc2cfe87596827d8e02d16cbec3cedc297bb571aa54"
20042008
},
20052009
{
2006-
"name": "xdr/curr/Stellar-ledger.x",
2010+
"name": "xdr/Stellar-ledger.x",
20072011
"sha256": "cf936606885dd265082e553aa433c2cf47b720b6d58839b154cf71096b885d1e"
20082012
},
20092013
{
2010-
"name": "xdr/curr/Stellar-overlay.x",
2014+
"name": "xdr/Stellar-overlay.x",
20112015
"sha256": "8c9b9c13c86fa4672f03d741705b41e7221be0fc48e1ea6eeb1ba07d31ec0723"
20122016
},
20132017
{
2014-
"name": "xdr/curr/Stellar-transaction.x",
2018+
"name": "xdr/Stellar-transaction.x",
20152019
"sha256": "30d03669fb29ca48fdda1c84258473fe6d798f3b881c0224b34df1a1f9e21e80"
20162020
},
20172021
{
2018-
"name": "xdr/curr/Stellar-types.x",
2019-
"sha256": "4d7a1d1f1fa0034ddbff27d8a533e59b6154bef295306c6256066def77a5a999"
2022+
"name": "xdr/Stellar-types.x",
2023+
"sha256": "f3a360cbb07f24637ead188e885a0b4a47b903ca475f9798be5fa12453075e28"
20202024
}
20212025
],
20222026
"namespaces": [
@@ -24324,6 +24328,26 @@
2432424328
"name": "ClaimableBalanceID",
2432524329
"parent": null,
2432624330
"source": "union ClaimableBalanceID switch (ClaimableBalanceIDType type)\n{\ncase CLAIMABLE_BALANCE_ID_TYPE_V0:\n Hash v0;\n};"
24331+
},
24332+
{
24333+
"cfg": {
24334+
"feature": "TEST_FEATURE"
24335+
},
24336+
"file_index": 12,
24337+
"is_nested": false,
24338+
"kind": "struct",
24339+
"members": [
24340+
{
24341+
"name": "value",
24342+
"type_": {
24343+
"ident": "int32",
24344+
"kind": "ident"
24345+
}
24346+
}
24347+
],
24348+
"name": "TestNextType",
24349+
"parent": null,
24350+
"source": "struct TestNextType\n{\n int32 value;\n};"
2432724351
}
2432824352
],
2432924353
"name": "stellar",

xdr-generator-rust/generator/src/generator.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,11 @@ impl RustGenerator {
302302

303303
let discriminant_type = type_ref(&u.discriminant.type_, None, &self.type_info);
304304
let discriminant_is_builtin = is_builtin_type(&u.discriminant.type_)
305-
|| self.type_info.resolve_typedef_to_builtin(&u.discriminant.type_).is_some();
305+
|| matches!(&u.discriminant.type_, xdr_parser::ast::Type::Ident { ident: n } if {
306+
self.type_info.definitions.get(&type_name(n))
307+
.map(|d| matches!(d, Definition::Typedef(t) if is_builtin_type(&t.type_)))
308+
.unwrap_or(false)
309+
});
306310

307311
let discriminant_prefix = if !discriminant_is_builtin {
308312
self.type_info

xdr-generator-rust/generator/src/main.rs

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ mod tests;
1212
use clap::Parser;
1313
use generator::RustGenerator;
1414
use options::RustOptions;
15-
use std::collections::{HashMap, HashSet};
15+
use std::collections::HashSet;
1616
use std::fs;
1717
use std::path::PathBuf;
18-
use xdr_parser::ast::{Definition, UnionCaseValue, XdrSpec};
18+
use xdr_parser::ast::{Definition, XdrSpec};
1919

2020
/// XDR code generator.
2121
#[derive(Parser, Debug)]
@@ -88,7 +88,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
8888
let feature_lower = feature.to_lowercase();
8989
let features: HashSet<String> =
9090
std::iter::once(feature_lower.clone()).collect();
91-
filter_spec(&mut ir_spec, &features)?;
91+
filter_spec(&mut ir_spec, &features);
9292
vec![feature_lower]
9393
} else {
9494
Vec::new()
@@ -133,7 +133,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
133133
};
134134

135135
let generator = RustGenerator::new(&spec, options);
136-
generator.generate_to_file(&spec, output)?;
136+
let output_dir = output.with_extension("");
137+
generator.generate_to_dir(&spec, output, &output_dir)?;
137138
eprintln!("Generated: {}", output.display());
138139
}
139140

@@ -142,68 +143,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
142143

143144
/// Filter an XDR spec in-place: remove definitions and sub-elements whose cfg
144145
/// evaluates to false, then clear all remaining cfg annotations.
145-
/// Validates that unions still cover all discriminant values after filtering.
146-
fn filter_spec(spec: &mut XdrSpec, features: &HashSet<String>) -> Result<(), Box<dyn std::error::Error>> {
147-
// Filter top-level definitions.
146+
fn filter_spec(spec: &mut XdrSpec, features: &HashSet<String>) {
148147
filter_definitions(&mut spec.definitions, features);
149-
150-
// Filter definitions inside namespaces.
151148
for ns in &mut spec.namespaces {
152-
if !ns.namespaces.is_empty() {
153-
return Err("nested namespaces are not supported in the JSON IR".into());
154-
}
155149
filter_definitions(&mut ns.definitions, features);
156150
}
157-
158-
// Validate union arm coverage: every surviving enum discriminant value
159-
// must be covered by a surviving union arm. The parser rejects default
160-
// arms, so all cases must be explicit.
161-
validate_union_coverage(spec)?;
162-
163-
Ok(())
164-
}
165-
166-
/// Validate that every union whose discriminant is an enum covers all
167-
/// surviving enum member values after cfg filtering.
168-
fn validate_union_coverage(spec: &XdrSpec) -> Result<(), Box<dyn std::error::Error>> {
169-
let enums: HashMap<&str, HashMap<&str, i32>> = spec
170-
.all_definitions()
171-
.filter_map(|def| match def {
172-
Definition::Enum(e) => Some((
173-
e.name.as_str(),
174-
e.members.iter().map(|m| (m.name.as_str(), m.value)).collect(),
175-
)),
176-
_ => None,
177-
})
178-
.collect();
179-
180-
for def in spec.all_definitions() {
181-
let Definition::Union(u) = def else { continue };
182-
let xdr_parser::ast::Type::Ident { ident: ref disc_type } = u.discriminant.type_ else { continue };
183-
let Some(members) = enums.get(disc_type.as_str()) else { continue };
184-
185-
let covered: HashSet<i32> = u.arms.iter()
186-
.flat_map(|arm| &arm.cases)
187-
.filter_map(|case| match &case.value {
188-
UnionCaseValue::Literal { literal } => Some(*literal),
189-
UnionCaseValue::Ident { ident } => members.get(ident.as_str()).copied(),
190-
})
191-
.collect();
192-
193-
let missing: Vec<i32> = members.values()
194-
.copied()
195-
.filter(|v| !covered.contains(v))
196-
.collect();
197-
if !missing.is_empty() {
198-
return Err(format!(
199-
"union {:?} does not cover discriminant values {:?} \
200-
after cfg filtering (enum {:?})",
201-
u.name, missing, disc_type
202-
).into());
203-
}
204-
}
205-
206-
Ok(())
207151
}
208152

209153
/// Filter a list of definitions: remove those whose cfg is false, filter
@@ -227,7 +171,7 @@ fn filter_definitions(defs: &mut Vec<Definition>, features: &HashSet<String>) {
227171
keep
228172
});
229173
}
230-
_ => {}
174+
Definition::Struct(_) | Definition::Typedef(_) | Definition::Const(_) => {}
231175
}
232176
def.set_cfg(None);
233177
}

xdr-generator-rust/generator/src/naming.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub(crate) fn field_name(name: &str) -> String {
1919

2020
fn escape_type_name(name: &str) -> String {
2121
match name {
22+
"type" => "type_".to_string(),
2223
"Error" => "SError".to_string(),
2324
_ => name.to_string(),
2425
}

0 commit comments

Comments
 (0)