Skip to content

Proc macro hygiene regression #46489

Open
@SimonSapin

Description

@SimonSapin

Something in the range bb42071...f9b0897, likely #46343 (CC @jseyfried), broke Servo.

We have a #[dom_struct] attribute implemented in a dom_struct crate like this:

#[proc_macro_attribute]
pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
    if !args.is_empty() {
        panic!("#[dom_struct] takes no arguments");
    }
    let attributes = quote! {
        #[derive(DenyPublicFields, DomObject, JSTraceable, MallocSizeOf)]
        #[must_root]
        #[repr(C)]
    };
    iter::once(attributes).chain(iter::once(input)).collect()
}

Each of the derives is defined in a respective crate. The script crate depends on all of these crates and uses #[dom_struct]. Some of the derives generate code that reference items defined in script. For example, #[derive(DomObject)] implements the script::dom::bindings::refector::DomObject trait.

Since rustc 1.24.0-nightly (f9b0897 2017-12-02), every use of #[dom_struct] fails with:

error[E0433]: failed to resolve. Could not find `js` in `{{root}}`
  --> components/script/dom/attr.rs:28:1
   |
28 | #[dom_struct]
   | ^^^^^^^^^^^^^ Could not find `js` in `{{root}}`

error[E0433]: failed to resolve. Could not find `dom` in `{{root}}`
  --> components/script/dom/attr.rs:28:1
   |
28 | #[dom_struct]
   | ^^^^^^^^^^^^^ Could not find `dom` in `{{root}}`

error[E0433]: failed to resolve. Could not find `js` in `{{root}}`
  --> components/script/dom/attr.rs:28:1
   |
28 | #[dom_struct]
   | ^^^^^^^^^^^^^ Could not find `js` in `{{root}}`

error[E0433]: failed to resolve. Could not find `malloc_size_of` in `{{root}}`
  --> components/script/dom/attr.rs:28:1
   |
28 | #[dom_struct]
   | ^^^^^^^^^^^^^ Could not find `malloc_size_of` in `{{root}}`

I suppose that these errors come from code generated by the derives, and that {{root}} refers to the root of the dom_struct crate where the #[derive(…)] tokens come from. Indeed, the js and dom are not an cannot be available there, they’re in the script crate which depends on dom_struct.

We can work around this by erasing hygiene data in the #[derive(…)] tokens:

--- a/components/dom_struct/lib.rs
+++ b/components/dom_struct/lib.rs
@@ -17,7 +17,9 @@ pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
     let attributes = quote! {
         #[derive(DenyPublicFields, DomObject, JSTraceable, MallocSizeOf)]
         #[must_root]
         #[repr(C)]
     };
+    let attributes = attributes.to_string().parse().unwrap();
     iter::once(attributes).chain(iter::once(input)).collect()
 }

… but this seems like a valid pattern that shouldn’t necessitate such hacks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-hygieneArea: Macro hygieneA-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)C-bugCategory: This is a bug.P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions