#proc-macro #error #proc-macro-attributes

proc-macro-error

几乎可以无缝替换进程宏中的panic

34个版本 (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.1.5 2019年7月19日

#1进程宏

Download history 1061800/week @ 2024-04-19 981277/week @ 2024-04-26 996501/week @ 2024-05-03 1024428/week @ 2024-05-10 1046701/week @ 2024-05-17 1011943/week @ 2024-05-24 1089379/week @ 2024-05-31 1046615/week @ 2024-06-07 1025637/week @ 2024-06-14 1048311/week @ 2024-06-21 996318/week @ 2024-06-28 1032839/week @ 2024-07-05 1074007/week @ 2024-07-12 1094131/week @ 2024-07-19 1100027/week @ 2024-07-26 1146308/week @ 2024-08-02

每月4,618,501次下载
用于 13,955 个crate(581个直接使用)

MIT/Apache

60KB
776

使得进程宏中的错误报告变得简单易用

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版本中发出,在稳定版本中被忽略。
  • “帮助”建议在稳定版本中不能有它们自己的范围信息(本质上继承了父范围)。
  • 如果您的宏意外触发了panic,则不会显示任何错误。这不是技术限制,而是一种故意的设计。《panic》不是用于错误报告。

MSRV策略

proc_macro_error 将始终与进程宏的圣三一:proc_macro2synquote crate 兼容。换句话说,如果三一可用 - proc_macro_error 也可用。

重要!

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

动机

proc-macro 中的错误处理很糟糕。目前的选择不多:要么将错误“冒泡”到宏的顶层并转换为 compile_error! 调用,要么只是使用传统的 panic。这两种方法都很糟糕

  • 前者很糟糕,因为仅仅为了那些会崩溃宏的关键错误而展开错误处理是相当冗余的;因此,人们通常选择不去管它,而是使用 panic。简单的 .expect 实在是太诱人了。

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

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

    此外,panic 并不是用于错误报告的;panic 是用于错误检测(如 None 的解包或越界索引)或早期开发阶段,那时您需要尽快得到原型,因此错误处理可以等待。混合这些用法只会让事情变得更糟。

  • 存在 proc_macro::Diagnostic,它很棒,但它已经实验性超过一年,并且不太可能在不久的将来稳定下来。

    这个包的 API 是有意设计为与 proc_macro::Diagnostic 兼容的,并在可能的情况下将其委托给它。一旦 Diagnostics 稳定,这个包将 始终将其委托给它,用户方面不需要进行任何代码更改。

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

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

这个包旨在提供这样的机制。您所要做的就是使用 #[proc_macro] 属性注释您的顶层 #[proc_macro_error] 函数,并将适当的 panic 改为 abort!/abort_call_site!,请参阅指南

免责声明

请注意,此crate不打算用于除过程宏错误报告之外的任何用途,对于其他任何用途请使用Result?(可能还需要使用许多其他辅助工具)。


许可协议

您可以根据自己的选择,在Apache License, Version 2.0MIT许可协议下使用。
除非您明确表示,否则,根据Apache-2.0许可协议定义的,您有意提交以包含在此crate中的任何贡献,都将按照上述方式双重许可,不附加任何额外条款或条件。

依赖项

~18–430KB