Example of structuring a proc macro crate for testability

Overview

testing-proc-macros

Example of structuring a proc macro crate for testability. See accompanying blog post for details.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

You might also like...
Rust macro to use a match-like syntax as a elegant alternative to nesting if-else statement

cond Rust macro to use a match-like syntax as an elegant alternative to many if-else statements. I got the idea from empty Go switch statements. I tho

An example project demonstrating integration with Rust for the ESP32-S2 and ESP32-C3 microcontrollers.

Rust ESP32 Example An example project demonstrating integration with Rust for the ESP32-S2 and ESP32-C3 microcontrollers.

Rust / C / Cgo example

whatever How to build (This has been tested on Linux & macOS) Build the Rust code: $ cargo build The library is in ./target/debug/libwhatever.a Build

An example of Brainf*** JIT-compiler with only the standard library.

jit-compiler An example of Brainf*** JIT-compiler with only the standard library. Prerequisite Rust(1.56.0-nightly or later, but it must work kind of

The Voting example based on MoonZoon and Solana.
The Voting example based on MoonZoon and Solana.

Voting example The Rust-only Voting example based on MoonZoon and Solana. MoonZoon is a Rust Fullstack Framework. Solana is a decentralized blockchain

A variation of the solana helloworld program example with a client written in Rust instead of Typescript

Simple Solana Smart Contract Example This repository demonstrates how to create and invoke a program on the Solana blockchain. In Solana the word prog

A basic rp2040-hal project with blinky and rtt logging example code.

A basic rp2040-hal project with blinky and rtt logging example code. With this you can quickly get started on a new rp2040 project

A example bevy application using bevy-kajiya for its renderer
A example bevy application using bevy-kajiya for its renderer

☀️ bevy-kajiya playground A example bevy application using bevy-kajiya for its renderer NOTE: only tested on Windows. For more context, check out the

An example Kibana plugin written in Rust and Typescript

An example Kibana plugin written in Rust and Typescript

Comments
  • Update .stderr files

    Update .stderr files

    With compiler 1.60.0:

    $ rustc --version rustc 1.60.0 (7737e0b5c 2022-04-04)

    The contents of teh .stderr files is slightly different so the .stderr files needed updating using TRYBUILD=overwrite cargo test.

    Without this change I was getting errors like the following, with the change the tests pass.

      test tests/ui/has-arguments.rs ... mismatch
    
      EXPECTED:
      ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
      error: this attribute takes no arguments
    
        = help: use `#[contracts]`
    
       --> $DIR/has-arguments.rs:3:1
        |
      3 | #[contracts(a, b)]
        | ^^^^^^^^^^^^^^^^^^
        |
        = note: this error originates in the attribute macro `contracts` (in Nightly builds, run with -Z macro-backtrace for more info)
      ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
    
      ACTUAL OUTPUT:
      ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
      error: this attribute takes no arguments
    
               = help: use `#[contracts]`
    
       --> tests/ui/has-arguments.rs:3:1
        |
      3 | #[contracts(a, b)]
        | ^^^^^^^^^^^^^^^^^^
        |
        = note: this error originates in the attribute macro `contracts` (in Nightly builds, run with -Z macro-backtrace for more info)
      ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
      note: If the actual output is the correct output you can bless it by rerunning
            your test with the environment variable TRYBUILD=overwrite
    
      test tests/ui/has-expr-argument.rs ... mismatch
    
    opened by winksaville 3
  • Normalized paths in stderr files

    Normalized paths in stderr files

    I spent a little time looking at why when I run TRYBUILD=overwrite carto test the *.stderr files do not have $DIR/xxx in the output. My conclusion is the compiler use to include the $DIR/xxx output, but now outputs the actual path.

    You can see that in this change on trybuild the stderr test files were updated and they do not have the $DIR/xxx:

    wink@3900x 22-05-18T00:16:29.992Z:~/prgs/rust/clones/dtolnay-trybuild (master)
    $ git diff 6cbc5a2843a66dfe934479cf2cf31afbe4b4dbb3^ 6cbc5a2843a66dfe934479cf2cf31afbe4b4dbb3
    diff --git a/tests/ui/compile-fail-2.stderr b/tests/ui/compile-fail-2.stderr
    index a59ae39..daa33d4 100644
    --- a/tests/ui/compile-fail-2.stderr
    +++ b/tests/ui/compile-fail-2.stderr
    @@ -1,5 +1,5 @@
     error: ERROR
    - --> $DIR/compile-fail-2.rs:1:1
    + --> tests/ui/compile-fail-2.rs:1:1
       |
     1 | compile_error!("ERROR");
       | ^^^^^^^^^^^^^^^^^^^^^^^^
    diff --git a/tests/ui/compile-fail-3.stderr b/tests/ui/compile-fail-3.stderr
    index 27619cf..7fc2291 100644
    --- a/tests/ui/compile-fail-3.stderr
    +++ b/tests/ui/compile-fail-3.stderr
    @@ -1,5 +1,5 @@
     error[E0277]: `*mut _` cannot be shared between threads safely
    -   --> $DIR/compile-fail-3.rs:7:5
    +   --> tests/ui/compile-fail-3.rs:7:5
         |
     7   |     thread::spawn(|| {
         |     ^^^^^^^^^^^^^ `*mut _` cannot be shared between threads safely
    wink@3900x 22-05-18T00:16:44.332Z:~/prgs/rust/clones/dtolnay-trybuild (master)
    

    So my suggestion is that in the future we don't manually add the $DIR/ and instead use the generated files as is. If you'd like I can create another PR with the those unmodified files.

    opened by winksaville 1
  • invalid syntax test cases cause panic

    invalid syntax test cases cause panic

    First off, thanks for writing this excellent blog post on structuring and testing procedural macros! I really like the way it's broken down in to parsing, analysis, intermediate representation, and code generation. I am currently attempting to implement a procedural macro of my own following this structure but I've run into an issue that is probably more specific to proc-macro-error than it is your guide, but I thought it would be worth raising as an issue here anyway before I start trying to dig into that codebase.

    The issue is with writing test cases to validate what happens when the parsing stage encounters invalid syntax. I imagine that tests for the analysis stage could be similarly affected, but I haven't made it that far yet in my own macro implementation :upside_down_face:. (I also wasn't intentionally trying to test invalid syntax, I just haven't made it far enough in my parse implementation for the syntax i'm targeting to be properly parsed.)

    So the problem is that the parse function calls proc-macro-error macros like abort and abort_call_site which results in a panic in tests that validate error cases since according to its docs:

    This attribute MUST be present on the top level of your macro (the function annotated with any of #[proc_macro], #[proc_macro_derive], #[proc_macro_attribute]).

    To illustrate with an example, here is a contrived test that I created in a clone of this repo:

    zsh/3 3540 [148]  (git)-[main]-% git diff
    diff --git a/src/parse.rs b/src/parse.rs
    index 5572a87..709b66b 100644
    --- a/src/parse.rs
    +++ b/src/parse.rs
    @@ -51,4 +51,18 @@ mod tests {
                 ),
             );
         }
    +
    +    #[test]
    +    fn invalid_syntax() {
    +        parse(
    +            quote!(ident),
    +            quote!(
    +                #[inline]
    +                #[precondition(x <>> == 0)]
    +                fn even_to_odd(x: u32) -> u32 {
    +                    x + 1
    +                }
    +            ),
    +        );
    +    }
     }
    

    (basically just the existing parse::valid_syntax test with a simple ident argument added in the first positional passed to parse)

    The docs for proc_macro_error indicate an argument that can be passed to the called allow_not_macro (eg #[proc_macro_error(allow_not_macro)], but that comes with the caveat:

    Pay attention: the function this attribute is applied to must return proc_macro::TokenStream.

    This is problematic since the parse example in your tutorial relies on proc_macro2::TokenStream.

    Anyway, like I mentioned earlier I don't think this is necessarily a problem with this tutorial but thought it would be useful to mention in case any other bright-eyed wannabe Rustaceans find themselves dealing with a similar situation. I'll probably dig into the proc-macro-error crate after submitting this issue to see if it's possible for #[proc_macro_error(allow_not_macro)] to accept proc_macro2::TokenStream as well as proc_macro::TokenStream.

    Thanks again for the great tutorial!

    opened by waynr 1
Owner
Ferrous Systems
Rust knowledge. Collected.
Ferrous Systems
Macros to make writing proc-macro crates easy

proc-easy Macros to make writing proc-macro crates easy. This crate provides mainly macros and supporting types and traits to reduce amount of boilerp

Zakarum 7 Jan 1, 2023
secmem-proc is a crate designed to harden a process against low-privileged attackers running on the same system trying to obtain secret memory contents of the current process.

secmem-proc is a crate designed to harden a process against low-privileged attackers running on the same system trying to obtain secret memory contents of the current process. More specifically, the crate disables core dumps and tries to disable tracing on unix-like OSes.

null 3 Dec 19, 2022
This crate provides a convenient macro that allows you to generate type wrappers that promise to always uphold arbitrary invariants that you specified.

prae This crate provides a convenient macro that allows you to generate type wrappers that promise to always uphold arbitrary invariants that you spec

null 96 Dec 4, 2022
This crate defines a single macro that is a brainfunct compile-time interpreter.

Compile Protection This crate defines a single macro that is a brainfunct compile-time interpreter. One example is as follows #![recursion_limit = "18

John Marsden 7 Nov 29, 2021
proc macros for generating mut and non-mut methods without duplicating code

mwt Hey! You! Read this before using! mwt was thrown together pretty quickly for personal use, because I couldn't find an existing crate that does thi

null 1 Dec 24, 2021
Provide expansion of proc-macros, in a way that rustc directs you directly to the issues at hand

expander Expands a proc-macro into a file, and uses a include! directive in place. Advantages Only expands a particular proc-macro, not all of them. I

Bernhard Schuster 16 Oct 5, 2022
📦 Crate Protocol allows anyone to create, manage, and trade a tokenized basket of assets, which we refer to as a Crate.

?? Crate Protocol Crate Protocol allows anyone to create, manage, and trade a tokenized basket of assets, which we refer to as a Crate. A Crate is alw

Crate Protocol 63 Oct 31, 2022
The nightly_crimes!{} macro commits horrible crimes to allow you to enable nightly features on the stable compiler.

The nightly_crimes!{} macro commits horrible crimes to allow you to enable nightly features on the stable compiler.

Mara Bos 151 Dec 16, 2022
A stupid macro that compiles and executes Rust and spits the output directly into your Rust code

inline-rust This is a stupid macro inspired by inline-python that compiles and executes Rust and spits the output directly into your Rust code. There

William 19 Nov 29, 2022
Attribute macro for implementing methods on both Foo and ArchivedFoo.

rkyv_impl Implement methods for Foo and ArchivedFoo in a single impl block. use rkyv::Archive; use rkyv_impl::*; use std::iter::Sum; #[derive(Archive

Duncan 5 Aug 6, 2023