Skip to content

Commit

Permalink
rewrite: Change association method
Browse files Browse the repository at this point in the history
Before this patch, `thin_delegate` used a global variable in proc macro
to transfer definitions of traits and structs/enums to where it
generates impl of a trait.

This method is problematic. For example, it's not ensured that the same
process is used to invoke proc macros, even if it's in the same
compilation unit (crate). E.g. incremental builds like rust-analyzer.
See e.g. [1], [2], and [3] for more details.

We resolve this problem by using declarative macros to transfer the
data.

This CL updates fundamental structures of this crate and updates tests
as much as possible. Remaining tests will be updated in the next
patches.

[1] rust-lang/rust#44034
[2] https://users.rust-lang.org/t/simple-state-in-procedural-macro/68204
[3] https://stackoverflow.com/questions/52910783/is-it-possible-to-store-state-within-rusts-procedural-macros
  • Loading branch information
kenoss committed Sep 21, 2024
1 parent a382b8c commit 17f0e8e
Show file tree
Hide file tree
Showing 32 changed files with 600 additions and 798 deletions.
823 changes: 344 additions & 479 deletions src/lib.rs

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions src/punctuated_parser.rs

This file was deleted.

41 changes: 41 additions & 0 deletions src/self_replacer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use proc_macro2::Span;
use syn::visit_mut::VisitMut;

pub(crate) fn make_self_hygienic_in_signature(mut target: syn::Signature) -> syn::Signature {
let mut visitor = Visitor;
visitor.visit_signature_mut(&mut target);
target
}

/// Replaces `self` to avoid issues around macro hygienicity.
///
/// `thin_delegate` transfers definition of a trait and a struct/enum to
/// `#[thin_delegate::derive_delegate]` by using declarative macro.
/// `#[thin_delegate::internal_derive_delegate]` processes a token stream in the macro context.
/// If we use `self` in this token stream as is, an error like the following arise:
///
/// ```ignore
/// error[E0424]: expected value, found module `self`
/// --> src/main.rs:24:1
/// |
/// 3 | fn hello(&self) -> String;
/// | -- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters
/// ...
/// 24 | #[thin_delegate::derive_delegate]
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` value is a keyword only available in methods with a `self` parameter
/// |
/// = note: this error originates in the attribute macro `::thin_delegate::internal_derive_delegate` which comes from the expansion of the attribute macro `thin_delegate::derive_delegate` (in Nightly builds, run with -Z macro-backtrace for more info)
/// For more information about this error, try `rustc --explain E0424`.
/// ```
///
/// Rust's macro hygienicity forbids use of `self` in declarative macros.
/// We can resolve it by replacing `self` in the token stream by `self` generated in a proc macro,
/// which this `Visitor` does.
struct Visitor;

impl VisitMut for Visitor {
// We only replaces `self` in receiver position, as we need it for `syn::Signature`.
fn visit_receiver_mut(&mut self, node: &mut syn::Receiver) {
node.self_token = syn::Token![self](Span::call_site());
}
}
99 changes: 0 additions & 99 deletions src/storage.rs

This file was deleted.

7 changes: 0 additions & 7 deletions tests/ui.old/fail_num_of_generic_parameters_differ.stderr

This file was deleted.

16 changes: 0 additions & 16 deletions tests/ui.old/fail_parameter_cant_substituted_to_argument.rs

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions tests/ui.old/fail_register_conflicted.rs

This file was deleted.

5 changes: 0 additions & 5 deletions tests/ui.old/fail_register_conflicted.stderr

This file was deleted.

7 changes: 0 additions & 7 deletions tests/ui.old/fail_register_missing.rs

This file was deleted.

9 changes: 0 additions & 9 deletions tests/ui.old/fail_register_missing.stderr

This file was deleted.

14 changes: 0 additions & 14 deletions tests/ui.old/pass_generics_specialize_complex.rs

This file was deleted.

55 changes: 0 additions & 55 deletions tests/ui.old/pass_multiple_derive.rs

This file was deleted.

15 changes: 15 additions & 0 deletions tests/ui/_pass_generics_specialize_complex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// TODO: Support external crates.
#[thin_delegate::register]
pub trait AsRef<T: ?Sized> {
/// Converts this type into a shared reference of the (usually inferred) input type.
#[stable(feature = "rust1", since = "1.0.0")]
fn as_ref(&self) -> &T;
}

#[thin_delegate::register]
struct Hoge(Box<dyn Fn(usize) -> usize>);

#[thin_delegate::derive_delegate]
impl AsRef<(dyn Fn(usize) -> usize + 'static)> for Hoge {}

fn main() {}
55 changes: 55 additions & 0 deletions tests/ui/_pass_multiple_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// TODO: Support external crates.
#[thin_delegate::register]
pub trait ToString {
/// Converts the given value to a `String`.
///
/// # Examples
///
/// ```
/// let i = 5;
/// let five = String::from("5");
///
/// assert_eq!(five, i.to_string());
/// ```
#[rustc_conversion_suggestion]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "to_string_method")]
fn to_string(&self) -> String;
}

#[thin_delegate::register]
pub trait Hello: ToString {
fn hello(&self) -> String;
}

impl Hello for String {
fn hello(&self) -> String {
format!("hello, {}", &self.to_string())
}
}

impl Hello for char {
fn hello(&self) -> String {
format!("hello, {}", &self.to_string())
}
}

#[thin_delegate::register]
enum Hoge {
A(String),
B(char),
}

#[thin_delegate::derive_delegate]
impl ToString for Hoge {}

#[thin_delegate::derive_delegate]
impl Hello for Hoge {}

fn main() {
let hoge = Hoge::A("a".to_string());
assert_eq!(hoge.hello(), "hello, a");

let hoge = Hoge::B('b');
assert_eq!(hoge.hello(), "hello, b");
}
7 changes: 7 additions & 0 deletions tests/ui/fail_derive_delegate_for_impl_without_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[thin_delegate::register]
struct Hoge(String);

#[thin_delegate::derive_delegate]
impl Hoge {}

fn main() {}
5 changes: 5 additions & 0 deletions tests/ui/fail_derive_delegate_for_impl_without_trait.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected `impl <Trait> for <Type>`
--> tests/ui/fail_derive_delegate_for_impl_without_trait.rs:5:1
|
5 | impl Hoge {}
| ^^^^^^^^^^^^
Loading

0 comments on commit 17f0e8e

Please sign in to comment.