66
77use crate :: errors:: {
88 self , AttrApplication , DebugVisualizerUnreadable , InvalidAttrAtCrateLevel , ObjectLifetimeErr ,
9- OnlyHasEffectOn , TransparentIncompatible , UnrecognizedReprHint ,
9+ OnlyHasEffectOn , ProcMacroDiffArguments , ProcMacroInvalidAbi , ProcMacroMissingArguments ,
10+ ProcMacroTypeError , ProcMacroUnsafe , TransparentIncompatible , UnrecognizedReprHint ,
1011} ;
1112use rustc_ast:: { ast, AttrStyle , Attribute , LitKind , MetaItemKind , MetaItemLit , NestedMetaItem } ;
1213use rustc_data_structures:: fx:: FxHashMap ;
13- use rustc_errors:: { fluent, Applicability , MultiSpan } ;
14+ use rustc_errors:: { fluent, Applicability , IntoDiagnosticArg , MultiSpan } ;
1415use rustc_expand:: base:: resolve_path;
1516use rustc_feature:: { AttributeDuplicates , AttributeType , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
1617use rustc_hir as hir;
@@ -19,18 +20,20 @@ use rustc_hir::intravisit::{self, Visitor};
1920use rustc_hir:: {
2021 self , FnSig , ForeignItem , HirId , Item , ItemKind , TraitItem , CRATE_HIR_ID , CRATE_OWNER_ID ,
2122} ;
22- use rustc_hir:: { MethodKind , Target } ;
23+ use rustc_hir:: { MethodKind , Target , Unsafety } ;
2324use rustc_middle:: hir:: nested_filter;
2425use rustc_middle:: middle:: resolve_lifetime:: ObjectLifetimeDefault ;
26+ use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
2527use rustc_middle:: ty:: query:: Providers ;
26- use rustc_middle:: ty:: TyCtxt ;
28+ use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
2729use rustc_session:: lint:: builtin:: {
2830 CONFLICTING_REPR_HINTS , INVALID_DOC_ATTRIBUTES , UNUSED_ATTRIBUTES ,
2931} ;
3032use rustc_session:: parse:: feature_err;
3133use rustc_span:: symbol:: { kw, sym, Symbol } ;
3234use rustc_span:: { Span , DUMMY_SP } ;
3335use rustc_target:: spec:: abi:: Abi ;
36+ use std:: cell:: Cell ;
3437use std:: collections:: hash_map:: Entry ;
3538
3639pub ( crate ) fn target_from_impl_item < ' tcx > (
@@ -62,8 +65,29 @@ enum ItemLike<'tcx> {
6265 ForeignItem ,
6366}
6467
68+ #[ derive( Copy , Clone ) ]
69+ pub ( crate ) enum ProcMacroKind {
70+ FunctionLike ,
71+ Derive ,
72+ Attribute ,
73+ }
74+
75+ impl IntoDiagnosticArg for ProcMacroKind {
76+ fn into_diagnostic_arg ( self ) -> rustc_errors:: DiagnosticArgValue < ' static > {
77+ match self {
78+ ProcMacroKind :: Attribute => "attribute proc macro" ,
79+ ProcMacroKind :: Derive => "derive proc macro" ,
80+ ProcMacroKind :: FunctionLike => "function-like proc macro" ,
81+ }
82+ . into_diagnostic_arg ( )
83+ }
84+ }
85+
6586struct CheckAttrVisitor < ' tcx > {
6687 tcx : TyCtxt < ' tcx > ,
88+
89+ // Whether or not this visitor should abort after finding errors
90+ abort : Cell < bool > ,
6791}
6892
6993impl CheckAttrVisitor < ' _ > {
@@ -173,7 +197,7 @@ impl CheckAttrVisitor<'_> {
173197 sym:: path => self . check_generic_attr ( hir_id, attr, target, Target :: Mod ) ,
174198 sym:: plugin_registrar => self . check_plugin_registrar ( hir_id, attr, target) ,
175199 sym:: macro_export => self . check_macro_export ( hir_id, attr, target) ,
176- sym:: ignore | sym:: should_panic | sym :: proc_macro_derive => {
200+ sym:: ignore | sym:: should_panic => {
177201 self . check_generic_attr ( hir_id, attr, target, Target :: Fn )
178202 }
179203 sym:: automatically_derived => {
@@ -183,6 +207,16 @@ impl CheckAttrVisitor<'_> {
183207 self . check_generic_attr ( hir_id, attr, target, Target :: Mod )
184208 }
185209 sym:: rustc_object_lifetime_default => self . check_object_lifetime_default ( hir_id) ,
210+ sym:: proc_macro => {
211+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: FunctionLike )
212+ }
213+ sym:: proc_macro_attribute => {
214+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: Attribute ) ;
215+ }
216+ sym:: proc_macro_derive => {
217+ self . check_generic_attr ( hir_id, attr, target, Target :: Fn ) ;
218+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: Derive )
219+ }
186220 _ => { }
187221 }
188222
@@ -2063,6 +2097,103 @@ impl CheckAttrVisitor<'_> {
20632097 errors:: Unused { attr_span : attr. span , note } ,
20642098 ) ;
20652099 }
2100+
2101+ /// A best effort attempt to create an error for a mismatching proc macro signature.
2102+ ///
2103+ /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
2104+ fn check_proc_macro ( & self , hir_id : HirId , target : Target , kind : ProcMacroKind ) {
2105+ let expected_input_count = match kind {
2106+ ProcMacroKind :: Attribute => 2 ,
2107+ ProcMacroKind :: Derive | ProcMacroKind :: FunctionLike => 1 ,
2108+ } ;
2109+
2110+ let expected_signature = match kind {
2111+ ProcMacroKind :: Attribute => "fn(TokenStream, TokenStream) -> TokenStream" ,
2112+ ProcMacroKind :: Derive | ProcMacroKind :: FunctionLike => "fn(TokenStream) -> TokenStream" ,
2113+ } ;
2114+
2115+ let tcx = self . tcx ;
2116+ if target == Target :: Fn {
2117+ let Some ( tokenstream) = tcx. get_diagnostic_item ( sym:: TokenStream ) else { return } ;
2118+ let tokenstream = tcx. type_of ( tokenstream) ;
2119+
2120+ let id = hir_id. expect_owner ( ) ;
2121+ let hir_sig = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . unwrap ( ) ;
2122+
2123+ let sig = tcx. liberate_late_bound_regions ( id. to_def_id ( ) , tcx. fn_sig ( id) ) ;
2124+ let sig = tcx. normalize_erasing_regions ( ParamEnv :: empty ( ) , sig) ;
2125+
2126+ // We don't currently require that the function signature is equal to
2127+ // `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
2128+ // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
2129+ //
2130+ // Properly checking this means pulling in additional `rustc` crates, so we don't.
2131+ let drcx = DeepRejectCtxt { treat_obligation_params : TreatParams :: AsInfer } ;
2132+
2133+ if sig. abi != Abi :: Rust {
2134+ tcx. sess . emit_err ( ProcMacroInvalidAbi { span : hir_sig. span , abi : sig. abi . name ( ) } ) ;
2135+ self . abort . set ( true ) ;
2136+ }
2137+
2138+ if sig. unsafety == Unsafety :: Unsafe {
2139+ tcx. sess . emit_err ( ProcMacroUnsafe { span : hir_sig. span } ) ;
2140+ self . abort . set ( true ) ;
2141+ }
2142+
2143+ let output = sig. output ( ) ;
2144+
2145+ // Typecheck the output
2146+ if !drcx. types_may_unify ( output, tokenstream) {
2147+ tcx. sess . emit_err ( ProcMacroTypeError {
2148+ span : hir_sig. decl . output . span ( ) ,
2149+ found : output,
2150+ kind,
2151+ expected_signature,
2152+ } ) ;
2153+ self . abort . set ( true ) ;
2154+ }
2155+
2156+ if sig. inputs ( ) . len ( ) < expected_input_count {
2157+ tcx. sess . emit_err ( ProcMacroMissingArguments {
2158+ expected_input_count,
2159+ span : hir_sig. span ,
2160+ kind,
2161+ expected_signature,
2162+ } ) ;
2163+ self . abort . set ( true ) ;
2164+ }
2165+
2166+ // Check that the inputs are correct, if there are enough.
2167+ if sig. inputs ( ) . len ( ) >= expected_input_count {
2168+ for ( arg, input) in
2169+ sig. inputs ( ) . iter ( ) . zip ( hir_sig. decl . inputs ) . take ( expected_input_count)
2170+ {
2171+ if !drcx. types_may_unify ( * arg, tokenstream) {
2172+ tcx. sess . emit_err ( ProcMacroTypeError {
2173+ span : input. span ,
2174+ found : * arg,
2175+ kind,
2176+ expected_signature,
2177+ } ) ;
2178+ self . abort . set ( true ) ;
2179+ }
2180+ }
2181+ }
2182+
2183+ // Check that there are not too many arguments
2184+ let body_id = tcx. hir ( ) . body_owned_by ( id. def_id ) ;
2185+ let excess = tcx. hir ( ) . body ( body_id) . params . get ( expected_input_count..) ;
2186+ if let Some ( excess @ [ begin @ end] | excess @ [ begin, .., end] ) = excess {
2187+ tcx. sess . emit_err ( ProcMacroDiffArguments {
2188+ span : begin. span . to ( end. span ) ,
2189+ count : excess. len ( ) ,
2190+ kind,
2191+ expected_signature,
2192+ } ) ;
2193+ self . abort . set ( true ) ;
2194+ }
2195+ }
2196+ }
20662197}
20672198
20682199impl < ' tcx > Visitor < ' tcx > for CheckAttrVisitor < ' tcx > {
@@ -2225,12 +2356,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
22252356}
22262357
22272358fn check_mod_attrs ( tcx : TyCtxt < ' _ > , module_def_id : LocalDefId ) {
2228- let check_attr_visitor = & mut CheckAttrVisitor { tcx } ;
2359+ let check_attr_visitor = & mut CheckAttrVisitor { tcx, abort : Cell :: new ( false ) } ;
22292360 tcx. hir ( ) . visit_item_likes_in_module ( module_def_id, check_attr_visitor) ;
22302361 if module_def_id. is_top_level_module ( ) {
22312362 check_attr_visitor. check_attributes ( CRATE_HIR_ID , DUMMY_SP , Target :: Mod , None ) ;
22322363 check_invalid_crate_level_attr ( tcx, tcx. hir ( ) . krate_attrs ( ) ) ;
22332364 }
2365+ if check_attr_visitor. abort . get ( ) {
2366+ tcx. sess . abort_if_errors ( )
2367+ }
22342368}
22352369
22362370pub ( crate ) fn provide ( providers : & mut Providers ) {
0 commit comments