diagnostic_on_unknown
The tracking issue for this feature is: #152900
The diagnostic_on_unknown feature allows use of the #[diagnostic::on_unknown] attribute. It should be
placed on use and module declarations as well as the crate root, though it is not an error to be located in other
positions. This attribute is a hint to the compiler to supplement the error message when the
annotated declaration is involved in a name resolution error.
Format parameters with the given named parameter will be replaced with the following text:
{Unresolved}— TheSimplePathSegmentof the import path that could not be resolved.{This}— The name of the annotated item. Onusestatements this is identical to{Unresolved}.
The original error message will not be suppressed but is emitted as a note instead.
On use declarations
#![allow(unused)]
#![feature(diagnostic_on_unknown)]
fn main() {
#[diagnostic::on_unknown(
message = "`{Unresolved}` doesn't exist",
label = "you did something silly here"
)]
use doesnt_exist;
}
This will result in the following error:
error[E0432]: `doesnt_exist` doesn't exist
--> src/lib.rs:7:5
|
7 | use doesnt_exist;
| ^^^^^^^^^^^^ you did something silly here
|
= note: unresolved import `doesnt_exist`
For more information about this error, try `rustc --explain E0432`.
On module declarations
#![allow(unused)]
#![feature(diagnostic_on_unknown)]
fn main() {
#[diagnostic::on_unknown(
message = "module `{This}` is empty, there is no `{Unresolved}` here",
label = "can't import something from an empty module"
)]
mod empty {}
use empty::what;
}
This will result in the following error:
error[E0432]: module `empty` is empty, there is no `what` here
--> src/lib.rs:9:5
|
9 | use empty::what;
| ^^^^^^^----
| |
| can't import something from an empty module
|
= note: unresolved import `empty::what`
On the crate root
This additionally requires the #![feature(custom_inner_attributes)] feature:
#![allow(unused)]
#![feature(diagnostic_on_unknown)]
#![feature(custom_inner_attributes)]
#![diagnostic::on_unknown(message = "Say `{Unresolved}` again!")]
fn main() {
use self::what;
}
Example
Consider the following pair of macros, one which creates a hidden module with a constant and another that reads it.
#![feature(macro_metavar_expr, macro_attr)]
macro_rules! instrument {
attr() { $vis:vis fn $fn_name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)? $body:block } => {
$vis fn $fn_name ($($arg_name:$arg_ty),*) $(-> $ret)? $body
#[doc(hidden)]
$vis mod $fn_name {
pub const LEN: usize = ${ count($arg_name) };
}
}
}
macro_rules! count_args {
($name:ident) => {{
$name::LEN
}}
}
#[instrument]
fn add(a: u8, b: u8) -> u8 {
a + b
}
fn main() {
let n = count_args!(add);
println!("`add` has {n} arguments");
}
If the #[instrument] macro is omitted it will emit this confusing error:
error[E0433]: cannot find module or crate `add` in this scope
--> src/main.rs:25:25
|
25 | let n = count_args!(add);
| ^^^ function `add` is not a crate or module
#[diagnostic::on_unknown] can be used to customize this error message:
#![feature(diagnostic_on_unknown)]
#![feature(macro_metavar_expr, macro_attr)]
#[allow(unused_macros)]
macro_rules! instrument {
attr() { $vis:vis fn $fn_name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)? $body:block } => {
$vis fn $fn_name ($($arg_name:$arg_ty),*) $(-> $ret)? $body
#[doc(hidden)]
$vis mod $fn_name {
pub const LEN: usize = ${ count($arg_name) };
}
}
}
macro_rules! count_args {
($name:ident) => {{
#[diagnostic::on_unknown(
message = "cannot count arguments of `{Unresolved}`",
label = "`{Unresolved}` is not a function decorated \
with the `#[instrument]` macro"
)]
use $name::LEN as length;
length
}}
}
// #[instrument]
fn add(a: u8, b: u8) -> u8 {
a + b
}
fn main() {
let n = count_args!(add);
println!("`add` has {n} arguments");
}
This produces:
error[E0432]: cannot count arguments of `add`
--> src/main.rs:33:25
|
33 | let n = count_args!(add);
| ^^^ `add` is not a function decorated with
| the `#[instrument]` macro
|
= note: unresolved import `add`
Edition differences
In the 2015 edition, use paths are relative to the crate root. For example, use empty will be resolved relative to the crate root and resolution will not encounter the mod empty {} declaration.
#![allow(unused)]
#![feature(diagnostic_on_unknown)]
fn main() {
mod foo {
#[diagnostic::on_unknown(message = "oh oh")]
mod empty {}
use empty::what;
}
}
error[E0432]: unresolved import `empty`
--> src/main.rs:9:8
|
9 | use empty::what;
| ^^^^^
|