From ee0a9cc93fd0bf99f1f52c8675fc6afb40a2b21b Mon Sep 17 00:00:00 2001 From: Val Lorentz Date: Sat, 4 May 2024 11:08:35 +0200 Subject: [PATCH] Improve compiler diagnostic when HandlerFn is not implemented For example, if I add a `u64` parameter to a handler, the compiler now reports this: ``` error[E0277]: Invalid command handler --> sable_ircd/src/command/handlers/who.rs:5:1 | 5 | #[command_handler("WHO")] | ^^^^^^^^^^^^^^^^^^^^^^^^^ `for<'a, 'b, 'c, 'd, 'e> fn(&'a server::ClientServer, &'b sable_network::prelude::Network, &'c (dyn command_response::CommandResponse + 'c), source_types::UserSource<'d>, &'e str, u64) -> Result<(), command::error::CommandError> {handle_who}` is not a valid command handler | = help: the trait `handler::HandlerFn<'_, _, _>` is not implemented for fn item `for<'a, 'b, 'c, 'd, 'e> fn(&'a server::ClientServer, &'b sable_network::prelude::Network, &'c (dyn command_response::CommandResponse + 'c), source_types::UserSource<'d>, &'e str, u64) -> Result<(), command::error::CommandError> {handle_who}` = note: All parameter types must implement `AmbientArgument` or `PositionalArgument` = note: Return type must be `CommandResult` note: required by a bound in `call_handler` --> sable_ircd/src/command/plumbing/mod.rs:47:20 | 45 | pub(crate) fn call_handler<'a, Amb, Pos>( | ------------ required by a bound in this function 46 | ctx: &'a dyn Command, 47 | handler: &impl HandlerFn<'a, Amb, Pos>, | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_handler` = note: this error originates in the attribute macro `command_handler` (in Nightly builds, run with -Z macro-backtrace for more info) ``` instead of this: ``` error[E0277]: the trait bound `for<'a, 'b, 'c, 'd, 'e> fn(&'a server::ClientServer, &'b sable_network::prelude::Network, &'c (dyn command_response::CommandResponse + 'c), source_types::UserSource<'d>, &'e str, u64) -> Result<(), command::error::CommandError> {handle_who}: handler::HandlerFn<'_, _, _>` is not satisfied --> sable_ircd/src/command/handlers/who.rs:5:1 | 5 | #[command_handler("WHO")] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `handler::HandlerFn<'_, _, _>` is not implemented for fn item `for<'a, 'b, 'c, 'd, 'e> fn(&'a server::ClientServer, &'b sable_network::prelude::Network, &'c (dyn command_response::CommandResponse + 'c), source_types::UserSource<'d>, &'e str, u64) -> Result<(), command::error::CommandError> {handle_who}` | note: required by a bound in `call_handler` --> sable_ircd/src/command/plumbing/mod.rs:47:20 | 45 | pub(crate) fn call_handler<'a, Amb, Pos>( | ------------ required by a bound in this function 46 | ctx: &'a dyn Command, 47 | handler: &impl HandlerFn<'a, Amb, Pos>, | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_handler` = note: this error originates in the attribute macro `command_handler` (in Nightly builds, run with -Z macro-backtrace for more info) ``` This uses the `diagnostic::on_unimplemented` attribute stabilized by [Rust 1.78](https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html) --- sable_ircd/src/command/plumbing/handler.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sable_ircd/src/command/plumbing/handler.rs b/sable_ircd/src/command/plumbing/handler.rs index 0be4da97..4a3a64ea 100644 --- a/sable_ircd/src/command/plumbing/handler.rs +++ b/sable_ircd/src/command/plumbing/handler.rs @@ -1,9 +1,23 @@ use super::*; +// TODO: once rustc implements support for it, use `if(not(Self::Output=CommandResult))` +// to pick the right note to display. +#[diagnostic::on_unimplemented( + message = "Invalid command handler", + label = "`{Self}` is not a valid command handler", + note = "All parameter types must implement `AmbientArgument` or `PositionalArgument`", + note = "Return type must be `CommandResult`" +)] pub trait HandlerFn<'ctx, Ambient, Positional> { fn call(&self, ctx: &'ctx dyn Command, args: ArgListIter<'ctx>) -> CommandResult; } +#[diagnostic::on_unimplemented( + message = "Invalid command handler", + label = "`{Self}` is not a valid command handler", + note = "All parameter types must implement `AmbientArgument` or `PositionalArgument`", + note = "Return type must be `CommandResult`" +)] pub trait AsyncHandlerFn<'ctx, Ambient, Positional>: Send + Sync { fn call( &'ctx self,