diff --git a/crates/example/src/examples.rs b/crates/example/src/examples.rs index ce72c5d..229a678 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: &str = "hello/there/friend.png"; +} 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 e1ce4a9..4217596 100644 --- a/crates/wgsl-rs-macros/src/lib.rs +++ b/crates/wgsl-rs-macros/src/lib.rs @@ -844,16 +844,23 @@ 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 } + +/// 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. +#[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..f232954 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> { @@ -3771,6 +3783,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 = { @@ -4975,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) => { 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};