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_macroneeds to be redesigned to require that aLintIdmust always be provided when a macro creates a warning. In this PR, I have made this change only forDiagnostic::span_warningandDiagnostic::warning. There is another constructorDiagnostic::newwhich takes aproc_macro::Leveland 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
LintIdneeds to be integrated into theallow/warn/denylint level system. If a cratefoo_macrosdefines aLintIdcalledambiguous_thing, and it is re-exported in the crate root of a cratefoo, then users offooneed to be able to writeallow(foo::ambiguous_thing)ordeny(foo::ambiguous_thing). - Procedural macro
LintIdneeds to be integrated with theunknown_lintslint. If a user writesdeny(foo::ambiguous_thing)whenfoois not a proc macro crate declaring aambiguous_thinglint 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).