22个版本 (5个稳定版)

1.0.4 2020年7月31日
1.0.2 2020年4月9日
1.0.0 2020年3月25日
0.4.12 2020年3月23日
0.4.3 2019年11月7日

#1903过程宏

Download history 1004935/week @ 2024-03-14 1026748/week @ 2024-03-21 1011467/week @ 2024-03-28 1045171/week @ 2024-04-04 1061157/week @ 2024-04-11 1051899/week @ 2024-04-18 995415/week @ 2024-04-25 1009425/week @ 2024-05-02 991545/week @ 2024-05-09 1051101/week @ 2024-05-16 1008615/week @ 2024-05-23 1091435/week @ 2024-05-30 1037501/week @ 2024-06-06 1046088/week @ 2024-06-13 1028706/week @ 2024-06-20 866071/week @ 2024-06-27

每月下载量:4,190,081
13,587 个crate中使用(通过 proc-macro-error

MIT/Apache

11KB
238

使过程宏的错误报告既简单又容易

travis ci docs.rs unsafe forbidden

这个crate旨在使过程宏中的错误报告简单易用。尽可能少地迁移基于 panic! 的错误!

此外,你可以显式地将一个虚拟令牌流附加到你的错误中。

为了实现这一点,这个crate作为 proc_macro::Diagnosticcompile_error! 的小型适配器。它根据编译器的版本检测发出错误的最佳方式。当底层诊断类型最终稳定时,这个crate将简单地委托给它,无需更改你的代码!

因此,你只需使用这个crate,就可以在稳定版之前提前获得 proc_macro::Diagnostic 的一些功能,并且让你的错误报告代码面向未来。

[dependencies]
proc-macro-error = "1.0"

支持rustc 1.31及以上版本

文档和指南

快速示例

代码

#[proc_macro]
#[proc_macro_error]
pub fn make_fn(input: TokenStream) -> TokenStream {
    let mut input = TokenStream2::from(input).into_iter();
    let name = input.next().unwrap();
    if let Some(second) = input.next() {
        abort! { second,
            "I don't like this part!";
                note = "I see what you did there...";
                help = "I need only one part, you know?";
        }
    }

    quote!( fn #name() {} ).into()
}

这是在终端中渲染错误的方式

这是你的用户将在他们的IDE中看到的内容

示例

类似panic的使用

use proc_macro_error::{
    proc_macro_error,
    abort,
    abort_call_site,
    ResultExt,
    OptionExt,
};
use proc_macro::TokenStream;
use syn::{DeriveInput, parse_macro_input};
use quote::quote;

// This is your main entry point
#[proc_macro]
// This attribute *MUST* be placed on top of the #[proc_macro] function
#[proc_macro_error]
pub fn make_answer(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    if let Err(err) = some_logic(&input) {
        // we've got a span to blame, let's use it
        // This immediately aborts the proc-macro and shows the error
        //
        // You can use `proc_macro::Span`, `proc_macro2::Span`, and
        // anything that implements `quote::ToTokens` (almost every type from
        // `syn` and `proc_macro2`)
        abort!(err, "You made an error, go fix it: {}", err.msg);
    }

    // `Result` has some handy shortcuts if your error type implements
    // `Into<Diagnostic>`. `Option` has one unconditionally.
    more_logic(&input).expect_or_abort("What a careless user, behave!");

    if !more_logic_for_logic_god(&input) {
        // We don't have an exact location this time,
        // so just highlight the proc-macro invocation itself
        abort_call_site!(
            "Bad, bad user! Now go stand in the corner and think about what you did!");
    }

    // Now all the processing is done, return `proc_macro::TokenStream`
    quote!(/* stuff */).into()
}

proc_macro::Diagnostic 类似的使用

use proc_macro_error::*;
use proc_macro::TokenStream;
use syn::{spanned::Spanned, DeriveInput, ItemStruct, Fields, Attribute , parse_macro_input};
use quote::quote;

fn process_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
    attrs
        .iter()
        .filter_map(|attr| match process_attr(attr) {
            Ok(res) => Some(res),
            Err(msg) => {
                emit_error!(attr, "Invalid attribute: {}", msg);
                None
            }
        })
        .collect()
}

fn process_fields(_attrs: &Fields) -> Vec<TokenStream> {
    // processing fields in pretty much the same way as attributes
    unimplemented!()
}

#[proc_macro]
#[proc_macro_error]
pub fn make_answer(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemStruct);
    let attrs = process_attrs(&input.attrs);

    // abort right now if some errors were encountered
    // at the attributes processing stage
    abort_if_dirty();

    let fields = process_fields(&input.fields);

    // no need to think about emitted errors
    // #[proc_macro_error] will handle them for you
    //
    // just return a TokenStream as you normally would
    quote!(/* stuff */).into()
}

实际示例

限制

  • 警告只在nightly上发出,在稳定版上被忽略。
  • “帮助”建议在稳定版上不能有自己的span信息(本质上继承父span)。
  • 如果你的宏意外触发panic,将不会显示任何错误。这不是技术限制,而是有意设计。 panic 不用于错误报告。

MSRV策略

proc_macro_error 将始终与 proc-macro 圣三角:proc_macro2synquote 库兼容。换句话说,如果您拥有这个圣三角 - 您也可以使用 proc_macro_error

重要!

如果您想使用 #[proc_macro_error]synstructure 一起,您必须在 decl_derive! 调用内部放置该属性。不幸的是,由于 pre-1.34 rustc 中的一些错误,将 proc-macro 属性放置在宏调用内部不起作用,因此您的 MSRV 实际上是 1.34。

动机

proc-macros 中的错误处理很糟糕。目前的选择不多:要么将错误“向上冒泡”到宏的顶层并转换为 compile_error! 调用,要么使用古老的恐慌。这两种方式都很糟糕

  • 前者很糟糕,因为对于会崩溃宏的临界错误来说,取消展开适当的错误处理是相当多余的;因此,人们通常选择不费心去处理它,而是使用恐慌。简单的 .expect 太诱人了。

    此外,如果您决定在您的宏中实现基于 Result 的架构,一旦 proc_macro::Diagnostic 最终稳定,您将不得不完全重写它。这并不酷。

  • 后者很糟糕,因为没有方法可以通过 panic! 来传递范围信息。 rustc 会突出显示调用本身,但不会突出显示调用内的特定标记。

    此外,恐慌根本不是用于错误报告的;恐慌用于错误检测(例如在 None 上展开或超出范围的索引)或早期开发阶段,当时您需要尽快有一个原型,所以错误处理可以稍后再说。混合这些用法只会使事情变得更糟。

  • 有一个 proc_macro::Diagnostic,这是很棒的,但它已经实验了超过一年,不太可能很快稳定。

    这个库的 API 故意设计成与 proc_macro::Diagnostic 兼容,并在可能的情况下委托给它。一旦 Diagnostics 稳定,这个库将 始终 委托给它,用户端无需进行代码更改。

话虽如此,我们需要一个解决方案,但这个解决方案必须满足以下条件

  • 它必须比 panic! 更好。主要观点:它必须提供一种将范围信息传递给用户的方法。
  • 它必须尽可能少地迁移到 panic!。理想情况下,一个新的宏,具有类似的语义以及执行范围信息的能力。
  • 它必须与 proc_macro::Diagnostic 保持兼容。
  • 它必须在稳定版上可用.

该软件包旨在提供此类机制。您只需将顶层函数#[proc_macro]注解为#[proc_macro_error]属性,并将panic转换为适当的abort!/abort_call_site!,参见指南

免责声明

请注意,本软件包的用途仅限于过程宏中的错误报告,对于其他任何用途,请使用Result?(可能还需要使用许多辅助工具之一)。


许可证

根据您的选择,在Apache License, Version 2.0MIT许可证下授权。
除非您明确声明,否则您提交给本软件包的任何有意贡献,根据Apache-2.0许可证定义,均应按照上述方式双授权,不附加任何额外条款或条件。

lib.rs:

这是用于与proc-macro-error一起使用的#[proc_macro_error]属性。就是这样。

依赖关系

~85KB