From 49276b0ec28e161e7f2aa86bfb345f8bf0ed9493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 6 Aug 2026 16:59:59 +0000 Subject: [PATCH] Detect bad number of generics caused by bad derive When a derive macro expands the annotated item's name directly using `quote!`, it keep the item's Span context (instead of having a new context). This means that the generic Span context machinery which provides feedback that an error happened due to a derive doesn't kick in. If a derive macro isn't written to take into account the existence of type parameters, an error for "mismatched number of type parameters" will be emitted. We now detect the case when this happens due to the derive macro, and customize the output to point that out, as well as avoid giving suggestions that will always be wrong. --- .../wrong_number_of_generic_args.rs | 25 +++++++++++--- .../src/hir_ty_lowering/generics.rs | 2 ++ .../derives/auxiliary/derive-no-generics.rs | 34 +++++++++++++++++++ .../derive-macro-unsupported-type-params.rs | 13 +++++++ ...erive-macro-unsupported-type-params.stderr | 28 +++++++++++++++ 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 tests/ui/derives/auxiliary/derive-no-generics.rs create mode 100644 tests/ui/derives/derive-macro-unsupported-type-params.rs create mode 100644 tests/ui/derives/derive-macro-unsupported-type-params.stderr diff --git a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs index 6717d11d0f3de..f2b166e0b2a01 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs @@ -3,7 +3,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, Diagnostic, MultiSpan, pluralize}; use rustc_hir as hir; use rustc_middle::ty::{self as ty, AssocItem, AssocItems, TyCtxt}; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{DefId, LocalDefId}; use tracing::debug; /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. @@ -30,6 +30,9 @@ pub(crate) struct WrongNumberOfGenericArgs<'a, 'tcx> { /// DefId of the generic type pub(crate) def_id: DefId, + + /// DefId of the type that contains the generic + pub(crate) cx_def_id: LocalDefId, } // Provides information about the kind of arguments that were provided for @@ -94,6 +97,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset: usize, gen_args: &'a hir::GenericArgs<'a>, def_id: DefId, + cx_def_id: LocalDefId, ) -> Self { let angle_brackets = if gen_args.span_ext().is_none() { if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied } @@ -110,6 +114,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { params_offset, gen_args, def_id, + cx_def_id, } } @@ -1163,10 +1168,20 @@ impl<'a> Diagnostic<'a> for WrongNumberOfGenericArgs<'_, '_> { err.code(E0107); err.span(self.path_segment.ident.span); - self.notify(&mut err); - self.suggest(&mut err); - self.show_definition(&mut err); - self.note_synth_provided(&mut err); + let cx_span = self.tcx.def_span(self.cx_def_id); + if cx_span.in_derive_expansion() { + // Very likely this is a botched `derive` which passes the iten name straight + // through, but doesn't support type parameters. + err.span_label( + cx_span.ctxt().outer_expn_data().call_site, + "this derive macro might not support items with generic parameters", + ); + } else { + self.notify(&mut err); + self.suggest(&mut err); + self.show_definition(&mut err); + self.note_synth_provided(&mut err); + } err } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 5f14b32dcabb1..9eda20fa0d9c4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -503,6 +503,7 @@ pub(crate) fn check_generic_arg_count( has_self as usize, gen_args, def_id, + cx.item_def_id(), )); Err(reported) @@ -617,6 +618,7 @@ pub(crate) fn check_generic_arg_count( params_offset, gen_args, def_id, + cx.item_def_id(), )) .emit_err_unless_delay(all_params_are_binded || has_invalid_bound) }); diff --git a/tests/ui/derives/auxiliary/derive-no-generics.rs b/tests/ui/derives/auxiliary/derive-no-generics.rs new file mode 100644 index 0000000000000..ea9a2e5b1c89a --- /dev/null +++ b/tests/ui/derives/auxiliary/derive-no-generics.rs @@ -0,0 +1,34 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, quote}; + +#[proc_macro_derive(A)] +pub fn derive(item: TokenStream) -> TokenStream { + let mut tokens = item.into_iter(); + let _enum = tokens.next(); + let name = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let _ = tokens.next().unwrap(); + let TokenTree::Group(group) = tokens.next().unwrap() else { panic!() }; + let mut group = group.stream().into_iter(); + let variant = group.next().unwrap(); + let TokenTree::Group(args) = group.next().unwrap() else { panic!() }; + let arg = args.stream().into_iter().next().unwrap(); + let tokens = quote! { + trait X {} + impl X for $name {} + + impl $name { + fn foo(&self) { + if let Self :: $variant(val) = self { + let _: $arg = val; + } + } + } + + }; + tokens +} diff --git a/tests/ui/derives/derive-macro-unsupported-type-params.rs b/tests/ui/derives/derive-macro-unsupported-type-params.rs new file mode 100644 index 0000000000000..d80064b4eb094 --- /dev/null +++ b/tests/ui/derives/derive-macro-unsupported-type-params.rs @@ -0,0 +1,13 @@ +//@ proc-macro: derive-no-generics.rs +#![crate_type = "lib"] + +#[macro_use] +extern crate derive_no_generics; + +#[derive(A)] +enum A { + //~^ ERROR: missing generics for enum `A` + //~| ERROR: missing generics for enum `A` + Variant(T), + //~^ ERROR: cannot find type `T` in this scope +} diff --git a/tests/ui/derives/derive-macro-unsupported-type-params.stderr b/tests/ui/derives/derive-macro-unsupported-type-params.stderr new file mode 100644 index 0000000000000..e57490e79cc9f --- /dev/null +++ b/tests/ui/derives/derive-macro-unsupported-type-params.stderr @@ -0,0 +1,28 @@ +error[E0425]: cannot find type `T` in this scope + --> $DIR/derive-macro-unsupported-type-params.rs:11:13 + | +LL | Variant(T), + | ^ not found in this scope + +error[E0107]: missing generics for enum `A` + --> $DIR/derive-macro-unsupported-type-params.rs:8:6 + | +LL | #[derive(A)] + | - this derive macro might not support items with generic parameters +LL | enum A { + | ^ + +error[E0107]: missing generics for enum `A` + --> $DIR/derive-macro-unsupported-type-params.rs:8:6 + | +LL | #[derive(A)] + | - this derive macro might not support items with generic parameters +LL | enum A { + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0425. +For more information about an error, try `rustc --explain E0107`.