Skip to content

Commit acab6ef

Browse files
committed
rust: address review feedback on targeted attribute injection
1 parent 5d37962 commit acab6ef

5 files changed

Lines changed: 176 additions & 240 deletions

File tree

crates/guest-rust/macro/src/lib.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -629,16 +629,17 @@ impl Parse for Opt {
629629
}
630630
}
631631

632-
// Parse one `"selector": ["#[attr]", ...]` entry into a (selector, attribute) pair
633-
// per attribute.
632+
// Parse one `"selector": [#[attr] ...]` entry into a pair per attribute.
634633
fn attr_map_field_parse(input: ParseStream<'_>) -> Result<Vec<(String, String)>> {
635634
let selector = input.parse::<syn::LitStr>()?;
636635
input.parse::<Token![:]>()?;
637636
let contents;
638637
let bracket = syn::bracketed!(contents in input);
639-
let attrs = Punctuated::<syn::LitStr, Token![,]>::parse_terminated(&contents)?;
640-
// An empty list would otherwise flatten away silently and escape the
641-
// unused-selector check in the generator.
638+
let attrs = contents.call(syn::Attribute::parse_outer)?;
639+
if !contents.is_empty() {
640+
return Err(contents.error("expected an outer attribute, `#[...]`"));
641+
}
642+
// An empty list would flatten away and escape the unused-selector check.
642643
if attrs.is_empty() {
643644
return Err(Error::new(
644645
bracket.span.join(),
@@ -648,7 +649,7 @@ fn attr_map_field_parse(input: ParseStream<'_>) -> Result<Vec<(String, String)>>
648649
let selector = selector.value();
649650
Ok(attrs
650651
.into_iter()
651-
.map(|a| (selector.clone(), a.value()))
652+
.map(|a| (selector.clone(), a.to_token_stream().to_string()))
652653
.collect())
653654
}
654655

crates/guest-rust/src/lib.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -684,18 +684,13 @@ extern crate std;
684684
///
685685
/// // Extra attributes to emit on specific generated types (records, variants,
686686
/// // and enums), rather than on all types like `additional_derives`. A type is
687-
/// // selected by its bare kebab name, its package (`my:pkg`), its owning
688-
/// // interface (`my:pkg/types`), or its fully-qualified name; the qualified
689-
/// // forms written as in `with`, carrying the `@version` when versioned. An
690-
/// // injected `#[derive(...)]` folds into the generated derive (deduped); other
691-
/// // attributes are emitted verbatim, on every form including the borrowed one
692-
/// // under `Borrowing` (so owned-only derives fail there). See the CLI docs for
693-
/// // the full grammar.
687+
/// // selected by its bare name, its package, its interface, or its fully
688+
/// // qualified name, the qualified forms written as in `with`.
694689
/// //
695690
/// // By default this map is empty.
696691
/// additional_type_attributes: {
697-
/// "my-record": [r#"#[derive(serde::Serialize)]"#], // one type, by bare name
698-
/// "my:pkg/types": [r#"#[derive(Clone)]"#], // every type in an interface
692+
/// "my-record": [#[derive(serde::Serialize)]], // one type, by bare name
693+
/// "my:pkg/types": [#[derive(Hash)]], // every type in an interface
699694
/// },
700695
///
701696
/// // Like `additional_type_attributes`, but for generated record fields and
@@ -704,7 +699,7 @@ extern crate std;
704699
/// //
705700
/// // By default this map is empty.
706701
/// additional_member_attributes: {
707-
/// "my-record.my-field": [r#"#[serde(rename = "mf")]"#],
702+
/// "my-record.my-field": [#[serde(rename = "mf")]],
708703
/// },
709704
///
710705
/// // When generating bindings for interfaces that are not defined in the

crates/rust/src/interface.rs

Lines changed: 19 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::bindgen::{FunctionBindgen, POINTER_SIZE_EXPRESSION};
22
use crate::{
33
ConstructorReturnType, FnSig, Identifier, InterfaceName, Ownership, RuntimeItem, RustFlagsRepr,
4-
RustWasm, TypeGeneration, classify_constructor_return_type, full_wit_type_name, int_repr,
5-
to_rust_ident, to_upper_camel_case, wasm_type,
4+
RustWasm, TypeGeneration, TypeSelectors, classify_constructor_return_type, full_wit_type_name,
5+
int_repr, to_rust_ident, to_upper_camel_case, wasm_type,
66
};
77
use anyhow::Result;
88
use heck::*;
@@ -2087,44 +2087,8 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
20872087
result
20882088
}
20892089

2090-
/// The owning interface and its package, when `id` is owned by an interface.
2091-
fn type_owner(&self, id: TypeId) -> Option<(InterfaceId, &PackageName)> {
2092-
let TypeOwner::Interface(iface_id) = self.resolve.types[id].owner else {
2093-
return None;
2094-
};
2095-
let pkg_id = self.resolve.interfaces[iface_id].package?;
2096-
Some((iface_id, &self.resolve.packages[pkg_id].name))
2097-
}
2098-
2099-
/// Selector keys naming the type itself: its bare wit name and its
2100-
/// fully-qualified `ns:pkg/iface/type` name. Qualified keys are written exactly
2101-
/// as in `with`, so they carry the package `@version` when versioned.
2102-
fn type_identity_keys(&self, id: TypeId) -> Vec<String> {
2103-
let id = dealias(self.resolve, id);
2104-
match self.resolve.types[id].name.as_deref() {
2105-
Some(name) => vec![name.to_string(), full_wit_type_name(self.resolve, id)],
2106-
None => Vec::new(),
2107-
}
2108-
}
2109-
2110-
/// Every selector key that matches type `id`: its identity keys plus its
2111-
/// owning interface and package.
2112-
fn type_selector_keys(&self, id: TypeId) -> Vec<String> {
2113-
let id = dealias(self.resolve, id);
2114-
let mut keys = self.type_identity_keys(id);
2115-
if let Some((iface_id, pkg)) = self.type_owner(id) {
2116-
keys.push(pkg.to_string());
2117-
if let Some(iface) = self.resolve.id_of(iface_id) {
2118-
keys.push(iface);
2119-
}
2120-
}
2121-
keys
2122-
}
2123-
2124-
/// The attributes from `entries` whose selector satisfies `matches`, deduped in
2125-
/// configured order, paired with the distinct selectors that matched. The
2126-
/// matched selectors are recorded so `finish` can report selectors that matched
2127-
/// nothing, exactly as the `with` option reports unused remappings.
2090+
/// Matching attributes, deduped in configured order, plus the selectors that
2091+
/// matched, which `finish` uses to report the ones that did not.
21282092
fn matching_attrs(
21292093
entries: &[(String, String)],
21302094
matches: impl Fn(&str) -> bool,
@@ -2138,62 +2102,25 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
21382102
(attrs.into_iter().collect(), used.into_iter().collect())
21392103
}
21402104

2141-
/// Attributes configured via `additional_type_attributes` for type `id`.
2142-
fn additional_type_attrs(&mut self, id: TypeId) -> Vec<String> {
2143-
let keys = self.type_selector_keys(id);
2105+
fn additional_type_attrs(&mut self, selectors: &TypeSelectors) -> Vec<String> {
21442106
let (attrs, used) =
21452107
Self::matching_attrs(&self.r#gen.opts.additional_type_attributes, |sel| {
2146-
keys.iter().any(|k| k == sel)
2108+
selectors.matches(sel)
21472109
});
21482110
self.r#gen.used_type_attr_selectors.extend(used);
21492111
attrs
21502112
}
21512113

2152-
/// Attributes configured via `additional_member_attributes` for `member` (a
2153-
/// record field or enum/variant case) of type `id`, matched by `<type>.<member>`
2154-
/// for any type selector (bare, qualified, interface, or package) or a bare
2155-
/// `<member>`.
2156-
fn additional_member_attrs(&mut self, id: TypeId, member: &str) -> Vec<String> {
2157-
let type_keys = self.type_selector_keys(id);
2114+
/// `member` is a record field or an enum/variant case.
2115+
fn additional_member_attrs(&mut self, selectors: &TypeSelectors, member: &str) -> Vec<String> {
21582116
let (attrs, used) =
21592117
Self::matching_attrs(&self.r#gen.opts.additional_member_attributes, |sel| {
2160-
// member names are dot-free, so the last `.` splits `<type>.<member>`
2161-
sel == member
2162-
|| sel
2163-
.rsplit_once('.')
2164-
.is_some_and(|(ty, m)| m == member && type_keys.iter().any(|tk| tk == ty))
2118+
selectors.matches_member(sel, member)
21652119
});
21662120
self.r#gen.used_member_attr_selectors.extend(used);
21672121
attrs
21682122
}
21692123

2170-
/// Split injected attributes into derive paths and everything else. Derive
2171-
/// paths are merged into the generated `#[derive(...)]` so they dedup against
2172-
/// the built-in and `additional_derives` derives instead of colliding (E0119);
2173-
/// other attributes are emitted verbatim.
2174-
fn split_injected_derives(attrs: &[String]) -> (Vec<String>, Vec<String>) {
2175-
let mut derives = Vec::new();
2176-
let mut others = Vec::new();
2177-
for attr in attrs {
2178-
match attr
2179-
.trim()
2180-
.strip_prefix("#[derive(")
2181-
.and_then(|s| s.strip_suffix(")]"))
2182-
{
2183-
Some(inner) => derives.extend(
2184-
inner
2185-
.split(',')
2186-
.map(str::trim)
2187-
.filter(|p| !p.is_empty())
2188-
.map(String::from),
2189-
),
2190-
None => others.push(attr.clone()),
2191-
}
2192-
}
2193-
(derives, others)
2194-
}
2195-
2196-
/// Emit each attribute on its own line, verbatim.
21972124
fn push_attrs(&mut self, attrs: &[String]) {
21982125
for attr in attrs {
21992126
uwriteln!(self.src, "{attr}");
@@ -2211,12 +2138,12 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
22112138
.cloned()
22122139
.collect();
22132140
// Computed once, then emitted on every ownership mode below.
2214-
let (injected_derives, injected_attrs) =
2215-
Self::split_injected_derives(&self.additional_type_attrs(id));
2141+
let selectors = TypeSelectors::of(self.resolve, id);
2142+
let injected_attrs = self.additional_type_attrs(&selectors);
22162143
let field_attrs: Vec<Vec<String>> = record
22172144
.fields
22182145
.iter()
2219-
.map(|f| self.additional_member_attrs(id, &f.name))
2146+
.map(|f| self.additional_member_attrs(&selectors, &f.name))
22202147
.collect();
22212148
for (name, mode) in self.modes_of(id) {
22222149
self.rustdoc(docs);
@@ -2235,7 +2162,6 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
22352162
} else if info.is_clone() {
22362163
derives.insert("Clone".to_string());
22372164
}
2238-
derives.extend(injected_derives.iter().cloned());
22392165
if !derives.is_empty() {
22402166
self.push_str("#[derive(");
22412167
self.push_str(&derives.into_iter().collect::<Vec<_>>().join(", "));
@@ -2307,8 +2233,6 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
23072233
{
23082234
self.print_rust_enum(
23092235
id,
2310-
// Raw wit case names; `print_rust_enum` upper-camel-cases them at the
2311-
// emit site so member selectors still match the wit (kebab) name.
23122236
variant
23132237
.cases
23142238
.iter()
@@ -2334,12 +2258,12 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
23342258
.iter()
23352259
.cloned()
23362260
.collect();
2337-
let (injected_derives, injected_attrs) =
2338-
Self::split_injected_derives(&self.additional_type_attrs(id));
2261+
let selectors = TypeSelectors::of(self.resolve, id);
2262+
let injected_attrs = self.additional_type_attrs(&selectors);
23392263
let case_attrs: Vec<Vec<String>> = cases
23402264
.clone()
23412265
.into_iter()
2342-
.map(|(case_name, _, _)| self.additional_member_attrs(id, &case_name))
2266+
.map(|(case_name, _, _)| self.additional_member_attrs(&selectors, &case_name))
23432267
.collect();
23442268
for (name, mode) in self.modes_of(id) {
23452269
self.rustdoc(docs);
@@ -2357,7 +2281,6 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
23572281
} else if info.is_clone() {
23582282
derives.insert("Clone".to_string());
23592283
}
2360-
derives.extend(injected_derives.iter().cloned());
23612284
if !derives.is_empty() {
23622285
self.push_str("#[derive(");
23632286
self.push_str(&derives.into_iter().collect::<Vec<_>>().join(", "));
@@ -2485,9 +2408,8 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
24852408

24862409
let name = to_upper_camel_case(name);
24872410
self.rustdoc(docs);
2488-
let (injected_derives, injected_attrs) =
2489-
Self::split_injected_derives(&self.additional_type_attrs(id));
2490-
self.push_attrs(&injected_attrs);
2411+
let selectors = TypeSelectors::of(self.resolve, id);
2412+
let injected_attrs = self.additional_type_attrs(&selectors);
24912413
self.push_str("#[repr(");
24922414
self.int_repr(enum_.tag());
24932415
self.push_str(")]\n");
@@ -2506,14 +2428,14 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
25062428
.into_iter()
25072429
.map(|s| s.to_string()),
25082430
);
2509-
derives.extend(injected_derives);
25102431
self.push_str("#[derive(");
25112432
self.push_str(&derives.into_iter().collect::<Vec<_>>().join(", "));
25122433
self.push_str(")]\n");
2434+
self.push_attrs(&injected_attrs);
25132435
self.push_str(&format!("pub enum {name} {{\n"));
25142436
for case in enum_.cases.iter() {
25152437
self.rustdoc(&case.docs);
2516-
let case_attrs = self.additional_member_attrs(id, &case.name);
2438+
let case_attrs = self.additional_member_attrs(&selectors, &case.name);
25172439
self.push_attrs(&case_attrs);
25182440
self.push_str(&case.name.to_upper_camel_case());
25192441
self.push_str(",\n");

0 commit comments

Comments
 (0)