From 51bec0df65bd34d0c5cab097dcc6e1cce6596d1d Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Sun, 3 May 2026 14:00:11 +1200 Subject: [PATCH 1/3] feat: #[wgsl_ignore] --- crates/example/src/examples.rs | 14 +++++++++++ crates/wgsl-rs-macros/src/lib.rs | 17 +++++++++++++ crates/wgsl-rs-macros/src/parse.rs | 38 ++++++++++++++++++++++++++++++ crates/wgsl-rs/src/lib.rs | 2 +- crates/wgsl-rs/src/std.rs | 2 +- 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/crates/example/src/examples.rs b/crates/example/src/examples.rs index ce72c5d..a0cf5d6 100644 --- a/crates/example/src/examples.rs +++ b/crates/example/src/examples.rs @@ -1449,3 +1449,17 @@ pub mod discard_example { vec4f(1.0, 0.0, 0.0, 1.0) } } + +#[wgsl] +pub mod ignore_items { + //! Demonstrates how to use the `#[wgsl_ignore]` attribute to ignore an item + //! during parsing, generating no WGSL code, but producing the expected Rust + //! code. + //! + //! This provides an escape hatch to use arbitrary Rust code within your + //! module without having a WGSL representation. + use wgsl_rs::std::*; + + #[wgsl_ignore] + const MODULE_PATH: &'static str = "hello/there/friend.png"; +} diff --git a/crates/wgsl-rs-macros/src/lib.rs b/crates/wgsl-rs-macros/src/lib.rs index e1ce4a9..aa21af5 100644 --- a/crates/wgsl-rs-macros/src/lib.rs +++ b/crates/wgsl-rs-macros/src/lib.rs @@ -854,6 +854,23 @@ pub fn wgsl_allow(_attr: TokenStream, token_stream: TokenStream) -> TokenStream token_stream } + +/// Ignores any item following the attribute during parsing, skipping WGSL code +/// generation. +/// +/// This provides an escape hatch to use arbitrary Rust code within your +/// module without having a WGSL representation, but only from the Rust side, +/// as the code annotated with `#[wgsl_ignore]` has no WGSL representation. +/// +/// # Note +/// +/// This attribute is stripped from the emitted Rust code by the `#[wgsl]` +/// macro, allowing statement-level attributes to work on stable Rust. +#[proc_macro_attribute] +pub fn wgsl_ignore(_attr: TokenStream, token_stream: TokenStream) -> TokenStream { + token_stream +} + /// Declares a uniform buffer binding. /// /// Emits `@group(G) @binding(B) var NAME: TYPE;` in WGSL. diff --git a/crates/wgsl-rs-macros/src/parse.rs b/crates/wgsl-rs-macros/src/parse.rs index a2565a4..192cc74 100644 --- a/crates/wgsl-rs-macros/src/parse.rs +++ b/crates/wgsl-rs-macros/src/parse.rs @@ -2547,6 +2547,18 @@ fn parse_wgsl_allow(attrs: &[syn::Attribute]) -> Result, Error> Ok(allowed) } +/// Parse `#[wgsl_ignore]` attribute. +/// +/// Returns `true` if `wgsl_ignore` was recognized. +fn attrs_contain_wgsl_ignore(attrs: &[syn::Attribute]) -> bool { + for attr in attrs { + if attr.path().is_ident("wgsl_ignore") { + return true; + } + } + false +} + /// Parse a for-loop pattern to extract the identifier and optional type /// annotation. fn parse_for_loop_pattern(pat: &syn::Pat) -> Result<(Ident, Option<(Token![:], Type)>), Error> { @@ -3756,6 +3768,10 @@ impl TryFrom<&syn::ItemMod> for ItemMod { // Only handle inline modules (with content) if let Some((_, items)) = &item_mod.content { for item in items { + if ItemMod::syn_item_has_wgsl_ignore_attribute(item) { + // Skip this item and produce no WGSL. + continue; + } content.push(Item::try_from(item)?); } Ok(ItemMod { ident, content }) @@ -3771,6 +3787,28 @@ impl TryFrom<&syn::ItemMod> for ItemMod { } impl ItemMod { + pub fn syn_item_has_wgsl_ignore_attribute(item: &syn::Item) -> bool { + let attrs: &[_] = match item { + syn::Item::Const(item_const) => &item_const.attrs, + syn::Item::Enum(item_enum) => &item_enum.attrs, + syn::Item::ExternCrate(item_extern_crate) => &item_extern_crate.attrs, + syn::Item::Fn(item_fn) => &item_fn.attrs, + syn::Item::ForeignMod(item_foreign_mod) => &item_foreign_mod.attrs, + syn::Item::Impl(item_impl) => &item_impl.attrs, + syn::Item::Macro(item_macro) => &item_macro.attrs, + syn::Item::Mod(item_mod) => &item_mod.attrs, + syn::Item::Static(item_static) => &item_static.attrs, + syn::Item::Struct(item_struct) => &item_struct.attrs, + syn::Item::Trait(item_trait) => &item_trait.attrs, + syn::Item::TraitAlias(item_trait_alias) => &item_trait_alias.attrs, + syn::Item::Type(item_type) => &item_type.attrs, + syn::Item::Union(item_union) => &item_union.attrs, + syn::Item::Use(item_use) => &item_use.attrs, + _ => &[], + }; + attrs_contain_wgsl_ignore(attrs) + } + pub fn imports(&self, wgsl_rs_crate_path: &syn::Path) -> Vec { fn is_wgsl_std(wgsl_rs_crate_path: &syn::Path, path: &syn::Path) -> bool { let wgsl_std = { diff --git a/crates/wgsl-rs/src/lib.rs b/crates/wgsl-rs/src/lib.rs index 9ab8863..966b497 100644 --- a/crates/wgsl-rs/src/lib.rs +++ b/crates/wgsl-rs/src/lib.rs @@ -1,5 +1,5 @@ //! WGSL in Rust. -pub use wgsl_rs_macros::{wgsl, wgsl_allow}; +pub use wgsl_rs_macros::{wgsl, wgsl_allow, wgsl_ignore}; /// A WGSL "module". /// diff --git a/crates/wgsl-rs/src/std.rs b/crates/wgsl-rs/src/std.rs index a00e725..1de980f 100644 --- a/crates/wgsl-rs/src/std.rs +++ b/crates/wgsl-rs/src/std.rs @@ -18,7 +18,7 @@ use std::{ pub use wgsl_rs_macros::{ builtin, compute, fragment, input, output, ptr, sampler, storage, texture, uniform, vertex, - wgsl_allow, workgroup, workgroup_size, + wgsl_allow, wgsl_ignore, workgroup, workgroup_size, }; pub use crate::{discard, get, get_mut, slab_read_array, slab_write_array}; From 97a5031572405ebbae91c5f666f92d244749fe27 Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Sun, 3 May 2026 14:02:21 +1200 Subject: [PATCH 2/3] warnings --- crates/example/src/examples.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/example/src/examples.rs b/crates/example/src/examples.rs index a0cf5d6..229a678 100644 --- a/crates/example/src/examples.rs +++ b/crates/example/src/examples.rs @@ -1461,5 +1461,5 @@ pub mod ignore_items { use wgsl_rs::std::*; #[wgsl_ignore] - const MODULE_PATH: &'static str = "hello/there/friend.png"; + const MODULE_PATH: &str = "hello/there/friend.png"; } From 2185bebd2255f4da2dc4811ffdc0055a9096d99b Mon Sep 17 00:00:00 2001 From: Schell Carl Scivally Date: Sun, 3 May 2026 14:09:13 +1200 Subject: [PATCH 3/3] better way to wgsl_ignore --- crates/wgsl-rs-macros/src/code_gen/formatter.rs | 3 +++ crates/wgsl-rs-macros/src/lib.rs | 10 ---------- crates/wgsl-rs-macros/src/parse.rs | 10 ++++++---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/crates/wgsl-rs-macros/src/code_gen/formatter.rs b/crates/wgsl-rs-macros/src/code_gen/formatter.rs index e54852b..ede5a69 100644 --- a/crates/wgsl-rs-macros/src/code_gen/formatter.rs +++ b/crates/wgsl-rs-macros/src/code_gen/formatter.rs @@ -2090,6 +2090,9 @@ impl GenerateCode for Item { Item::TraitImpl => { // Trait impl blocks are Rust-only and produce no WGSL. } + Item::Ignored => { + // This item was explicitly ignored with `#[wgsl_ignore]` + } } } } diff --git a/crates/wgsl-rs-macros/src/lib.rs b/crates/wgsl-rs-macros/src/lib.rs index aa21af5..4217596 100644 --- a/crates/wgsl-rs-macros/src/lib.rs +++ b/crates/wgsl-rs-macros/src/lib.rs @@ -844,11 +844,6 @@ pub fn builtin(_attr: TokenStream, token_stream: TokenStream) -> TokenStream { /// result /// } /// ``` -/// -/// # Note -/// -/// This attribute is stripped from the emitted Rust code by the `#[wgsl]` -/// macro, allowing statement-level attributes to work on stable Rust. #[proc_macro_attribute] pub fn wgsl_allow(_attr: TokenStream, token_stream: TokenStream) -> TokenStream { token_stream @@ -861,11 +856,6 @@ pub fn wgsl_allow(_attr: TokenStream, token_stream: TokenStream) -> TokenStream /// This provides an escape hatch to use arbitrary Rust code within your /// module without having a WGSL representation, but only from the Rust side, /// as the code annotated with `#[wgsl_ignore]` has no WGSL representation. -/// -/// # Note -/// -/// This attribute is stripped from the emitted Rust code by the `#[wgsl]` -/// macro, allowing statement-level attributes to work on stable Rust. #[proc_macro_attribute] pub fn wgsl_ignore(_attr: TokenStream, token_stream: TokenStream) -> TokenStream { token_stream diff --git a/crates/wgsl-rs-macros/src/parse.rs b/crates/wgsl-rs-macros/src/parse.rs index 192cc74..f232954 100644 --- a/crates/wgsl-rs-macros/src/parse.rs +++ b/crates/wgsl-rs-macros/src/parse.rs @@ -3768,10 +3768,6 @@ impl TryFrom<&syn::ItemMod> for ItemMod { // Only handle inline modules (with content) if let Some((_, items)) = &item_mod.content { for item in items { - if ItemMod::syn_item_has_wgsl_ignore_attribute(item) { - // Skip this item and produce no WGSL. - continue; - } content.push(Item::try_from(item)?); } Ok(ItemMod { ident, content }) @@ -5013,12 +5009,18 @@ pub enum Item { Trait, /// Trait impl blocks are Rust-only and produce no WGSL output. TraitImpl, + /// Skip this item, it is Rust-only + Ignored, } impl TryFrom<&syn::Item> for Item { type Error = Error; fn try_from(value: &syn::Item) -> Result { + if ItemMod::syn_item_has_wgsl_ignore_attribute(value) { + return Ok(Self::Ignored); + } + match value { syn::Item::Mod(item_mod) => Ok(Item::Mod(ItemMod::try_from(item_mod)?)), syn::Item::Macro(item_macro) => {