Implement #[proc_macro_lint] to generate LintId for macro-generated warnings by dtolnay · Pull Request #135432 · rust-lang/rust (original) (raw)
- API change proposal: Require a LintId for all proc-macro warnings libs-team#524
This PR unblocks an initial round of stabilizations of #54140 as discussed in #54140 (comment).
An id for a procedural macro warning is declared using #[proc_macro_lint]
on a pub static
contained in the crate root with type proc_macro::LintId
. The attribute fills in a unique value for the static's value:
use proc_macro::{LintId, TokenStream};
#[proc_macro_lint] pub static ambiguous_thing: LintId;
#[proc_macro_derive(Foo)] pub fn derive_foo(input: TokenStream) -> TokenStream { if ... { proc_macro::warning(crate::ambiguous_thing, span, "..."); } ... }
Within the same proc macro crate, the static (ambiguous_thing
) exists in the value namespace. It can be used as a value for passing to public APIs provided by the proc_macro
crate. The value's type is proc_macro::LintId
, which implements Copy
and Debug
.
Downstream of the proc macro crate, the same static exists in the macro namespace. It can be re-exported in the macro namespace using pub use
. Currently it is not useful for anything else.
The use of 2 namespaces in such a way is identical to how all proc macros already work. For example, inside a proc macro crate containing #[proc_macro] pub fn foo(input: TokenStream) -> TokenStream
, this function exists in the value namespace and is callable as a function. In downstream crates, the same function exists in the macro namespace and is callable as a function-like macro.
Future work
- Some of the public unstable API of
proc_macro
needs to be redesigned to require that aLintId
must always be provided when a macro creates a warning. In this PR, I have made this change only forDiagnostic::span_warning
andDiagnostic::warning
. There is another constructorDiagnostic::new
which takes aproc_macro::Level
and can be passedLevel::Warning
. In this PR I have not touched that function, which means it is not on track for stabilizing. This is fine because it has already fallen out of favor in the tracking issue discussion and was not suggested for stabilization. See for example Tracking Issue: Procedural Macro Diagnostics (RFC 1566) #54140 (comment). - Procedural macro
LintId
needs to be integrated into theallow
/warn
/deny
lint level system. If a cratefoo_macros
defines aLintId
calledambiguous_thing
, and it is re-exported in the crate root of a cratefoo
, then users offoo
need to be able to writeallow(foo::ambiguous_thing)
ordeny(foo::ambiguous_thing)
. - Procedural macro
LintId
needs to be integrated with theunknown_lints
lint. If a user writesdeny(foo::ambiguous_thing)
whenfoo
is not a proc macro crate declaring aambiguous_thing
lint id, nor is this resolved as a re-export of such a lint id, this needs to triggerunknown_lints
. - Rustdoc will need to render documentation for lint ids.
- Mechanism for a proc macro crate to set a default level for each of its lints. By default they are warn-by-default, but some lints might be better as allow-by-default or deny-by-default.
- A style lint that enforces a case convention for lint ids (snake_case).
- Rust-analyzer will want to provide autocomplete for proc macro lint ids inside
allow
/warn
/deny
.
Importantly, none of the above blocks stabilization of warning diagnostics APIs in proc_macro
(as long as we do it carefully, i.e. not Diagnostic::new
).