1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
//! Macro for declaring/implementing traits with fake associated consts (in stable Rust) //! //! Currently very fragile in terms of syntax: does not support traits/impls with _any_ kind of //! generic parameters (either lifetimes or types). //! //! The same macro is used for declaring a trait with associated consts, implementing such a trait, //! and accessing the consts. //! //! The syntax is the same as that proposed for associated consts, _except_ that: //! //! - all consts must be at the beginning of the trait/impl, before any functions //! - const declarations end with a comma, instead of a semicolon (this is due to a limitation of //! the macro system -- a type followed by a semicolon is for some reason not parseable) //! //! See the tests for example usage. //! //! At the moment they are not consts at all -- they simply expand to static functions with the //! same name as the declared const. You may therefore access the const by calling //! `Trait::CONST()`, or (for future proofing, in case the macro implementation changes), call the //! macro again to access the const, as `guilty!(Trait::CONST)`. /// Macro for declaring/implementing traits with fake associated consts /// /// See the [crate-level documentation](index.html) for more. #[macro_export] macro_rules! guilty { // These are the user facing invocations: // FIXME what if the traits have docs/attributes? // 1. define a private trait ($(#[$attr:meta])* trait $traitname:ident $body:tt) => { guilty!(INTERNAL: DEFINE TRAIT, [$(#[$attr])*] [trait] [$traitname], $body); }; // 2. define a public trait ($(#[$attr:meta])* pub trait $traitname:ident $body:tt) => { guilty!(INTERNAL: DEFINE TRAIT, [$(#[$attr])*] [pub trait] [$traitname], $body); }; // 3. implement a trait (public or private) (impl $traitname:ident for $structname:ident $body:tt) => { guilty!(INTERNAL: DEFINE IMPL, $traitname, $structname, $body); }; // 4. access a const declared with this macro ($structname:ident :: $constname:ident) => { guilty!(INTERNAL: ACCESS CONST, $structname, $constname); }; // Following are the internal macro calls // Since you can't export a macro which calls other unexported macros, guilty! calls itself // recursively in order to continue parsing. The invocation syntax for all these recursive // calls starts with the tokens `INTERNAL:`. // // The general strategy for parsing these declarations is we parse one const declaration from // the beginning of the trait/impl at a time, turning it into a static function which is // appended to the end of the trait/impl. When there are no more consts, the recursion stops // and the trait/impl is outputted (with an indirection through AS ITEM to appease the parser). // parse-trait-defconst: parse a trait with a const (that has a default value) as the first declaration // the square brackets contain [trait Trait] or [pub trait Trait] // this calls on to: // - itself if there is another default-valued const // - parse-trait-nodefconst if there is another const with no default value // - def-trait-fn/def-trait-attr/def-trait-ty if there are no more consts // FIXME what if the const has docs/attributes? (INTERNAL: DEFINE TRAIT, [$(#[$attr:meta])*] [$($before:ident)+] [$($traitname:tt)*], { const $constname:ident : $consttype:ty = $constdefault:expr, $($body:tt)* }) => { guilty!(INTERNAL: DEFINE TRAIT, [$(#[$attr])*] [$($before)+] [$($traitname)*], { $($body)* #[allow(non_snake_case)] fn $constname() -> $consttype { $constdefault } }); }; // parse-trait-nodefconst: parse a trait with a const (that has no default value) as the first declaration // this calls on to: // - itself is there is another non-default-valued const // - parse-trait-defconst if there is another default-valued const // - def-trait-fn/def-trait-attr/def-trait-ty if there are no more consts (INTERNAL: DEFINE TRAIT, [$(#[$attr:meta])*] [$($before:ident)+] [$($traitname:tt)*], { const $constname:ident : $consttype:ty, $($body:tt)* }) => { guilty!(INTERNAL: DEFINE TRAIT, [$(#[$attr])*] [$($before)+] [$($traitname)*], { $($body)* #[allow(non_snake_case)] fn $constname() -> $consttype; }); }; // def-trait-fn: output a trait that has no consts at the beginning (starts with an unadorned fn) // indirection through item-redir (INTERNAL: DEFINE TRAIT, [$(#[$attr:meta])*] [$($before:ident)+] [$($traitname:tt)*], { fn $($body:tt)* }) => { guilty!(INTERNAL: AS ITEM, $(#[$attr])* $($before)+ $($traitname)* { fn $($body)* }); }; // def-trait-attr: output a trait that has no consts at the beginning (starts with fn that has // docs/attributes) // indirection through item-redir (INTERNAL: DEFINE TRAIT, [$(#[$attr:meta])*] [$($before:ident)+] [$($traitname:tt)*], { # $($body:tt)* }) => { guilty!(INTERNAL: AS ITEM, $(#[$attr])* $($before)+ $($traitname)* { # $($body)* }); }; // def-trait-ty: output a trait that has no consts at the beginning (starts with an associated type) // indirection through item-redir (INTERNAL: DEFINE TRAIT, [$(#[$attr:meta])*] [$($before:ident)+] [$($traitname:tt)*], { type $($body:tt)* }) => { guilty!(INTERNAL: AS ITEM, $(#[$attr])* $($before)+ $($traitname)* { type $($body)* }); }; // parse-impl-const: parse an impl with a const as the first declaration // calls on to: // - itself if there is another const // - def-impl-fn/def-impl-ty if there are no more consts (INTERNAL: DEFINE IMPL, $traitname:ty, $structname:ident, { const $constname:ident : $consttype:ty = $constvalue:expr, $($body:tt)* }) => { guilty!(INTERNAL: DEFINE IMPL, $traitname, $structname, { $($body)* fn $constname() -> $consttype { $constvalue } }); }; // def-impl-fn: output an impl that has no consts at the beginning (starts with fn) // indirection through item-redir (INTERNAL: DEFINE IMPL, $traitname:ty, $structname:ident, { fn $($body:tt)* }) => { guilty!(INTERNAL: AS ITEM, impl $traitname for $structname { fn $($body)* }); }; // def-impl-ty: output an impl that has no consts at the beginning (starts with type) // indirection through item-redir (INTERNAL: DEFINE IMPL, $traitname:ty, $structname:ident, { type $($body:tt)* }) => { guilty!(INTERNAL: AS ITEM, impl $traitname for $structname { type $($body)* }); }; // FIXME: need another DEFINE IMPL that's like def-trait-attr? // access: access a const defined with this macro // For now, it just calls the function, since we turn consts into functions. In the future, it // might do something more clever if the implementation changes. (INTERNAL: ACCESS CONST, $structname:ident, $constname:ident) => {{ $structname :: $constname () }}; // item-redir: Item redirection. // For some reason the parser sometimes complains "expected item" when you are trying to output // a perfectly good item. The solution (sometimes) is to redirect through a macro like this. (INTERNAL: AS ITEM, $i:item) => ($i) } /* * BEFORE * trait Trait { const WithDefault: i32 = 0, const NoDefault: Self, fn with_impl(&self) -> &Self { self } fn no_impl(&self) -> &Self; } struct Struct { i: i32 } impl Trait for Struct { const WithDefault: i32 = 42, const NoDefault: Self = Self { i: 42 }, fn no_impl(&self) -> &Self { self } } */ /* * AFTER * trait Trait { fn with_impl(&self) -> &Self { self } fn no_impl(&self) -> &Self; #[allow(non_snake_case)] fn WithDefault() -> i32 { 0 } #[allow(non_snake_case)] fn NoDefault() -> Self; } struct Struct { i: i32 } impl Trait for Struct { fn no_impl(&self) -> &Self { self } fn WithDefault() -> i32 { 42 } fn NoDefault() -> Self { Struct { i: 42 } } } */