6
6
7
7
use crate :: errors:: {
8
8
self , AttrApplication , DebugVisualizerUnreadable , InvalidAttrAtCrateLevel , ObjectLifetimeErr ,
9
- OnlyHasEffectOn , TransparentIncompatible , UnrecognizedReprHint ,
9
+ OnlyHasEffectOn , ProcMacroDiffArguments , ProcMacroInvalidAbi , ProcMacroMissingArguments ,
10
+ ProcMacroTypeError , ProcMacroUnsafe , TransparentIncompatible , UnrecognizedReprHint ,
10
11
} ;
11
12
use rustc_ast:: { ast, AttrStyle , Attribute , LitKind , MetaItemKind , MetaItemLit , NestedMetaItem } ;
12
13
use rustc_data_structures:: fx:: FxHashMap ;
13
- use rustc_errors:: { fluent, Applicability , MultiSpan } ;
14
+ use rustc_errors:: { fluent, Applicability , IntoDiagnosticArg , MultiSpan } ;
14
15
use rustc_expand:: base:: resolve_path;
15
16
use rustc_feature:: { AttributeDuplicates , AttributeType , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
16
17
use rustc_hir as hir;
@@ -19,18 +20,20 @@ use rustc_hir::intravisit::{self, Visitor};
19
20
use rustc_hir:: {
20
21
self , FnSig , ForeignItem , HirId , Item , ItemKind , TraitItem , CRATE_HIR_ID , CRATE_OWNER_ID ,
21
22
} ;
22
- use rustc_hir:: { MethodKind , Target } ;
23
+ use rustc_hir:: { MethodKind , Target , Unsafety } ;
23
24
use rustc_middle:: hir:: nested_filter;
24
25
use rustc_middle:: middle:: resolve_lifetime:: ObjectLifetimeDefault ;
26
+ use rustc_middle:: ty:: fast_reject:: { DeepRejectCtxt , TreatParams } ;
25
27
use rustc_middle:: ty:: query:: Providers ;
26
- use rustc_middle:: ty:: TyCtxt ;
28
+ use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
27
29
use rustc_session:: lint:: builtin:: {
28
30
CONFLICTING_REPR_HINTS , INVALID_DOC_ATTRIBUTES , UNUSED_ATTRIBUTES ,
29
31
} ;
30
32
use rustc_session:: parse:: feature_err;
31
33
use rustc_span:: symbol:: { kw, sym, Symbol } ;
32
34
use rustc_span:: { Span , DUMMY_SP } ;
33
35
use rustc_target:: spec:: abi:: Abi ;
36
+ use std:: cell:: Cell ;
34
37
use std:: collections:: hash_map:: Entry ;
35
38
36
39
pub ( crate ) fn target_from_impl_item < ' tcx > (
@@ -62,8 +65,29 @@ enum ItemLike<'tcx> {
62
65
ForeignItem ,
63
66
}
64
67
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
+
65
86
struct CheckAttrVisitor < ' tcx > {
66
87
tcx : TyCtxt < ' tcx > ,
88
+
89
+ // Whether or not this visitor should abort after finding errors
90
+ abort : Cell < bool > ,
67
91
}
68
92
69
93
impl CheckAttrVisitor < ' _ > {
@@ -173,7 +197,7 @@ impl CheckAttrVisitor<'_> {
173
197
sym:: path => self . check_generic_attr ( hir_id, attr, target, Target :: Mod ) ,
174
198
sym:: plugin_registrar => self . check_plugin_registrar ( hir_id, attr, target) ,
175
199
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 => {
177
201
self . check_generic_attr ( hir_id, attr, target, Target :: Fn )
178
202
}
179
203
sym:: automatically_derived => {
@@ -183,6 +207,16 @@ impl CheckAttrVisitor<'_> {
183
207
self . check_generic_attr ( hir_id, attr, target, Target :: Mod )
184
208
}
185
209
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
+ }
186
220
_ => { }
187
221
}
188
222
@@ -2063,6 +2097,103 @@ impl CheckAttrVisitor<'_> {
2063
2097
errors:: Unused { attr_span : attr. span , note } ,
2064
2098
) ;
2065
2099
}
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
+ }
2066
2197
}
2067
2198
2068
2199
impl < ' tcx > Visitor < ' tcx > for CheckAttrVisitor < ' tcx > {
@@ -2225,12 +2356,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
2225
2356
}
2226
2357
2227
2358
fn 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 ) } ;
2229
2360
tcx. hir ( ) . visit_item_likes_in_module ( module_def_id, check_attr_visitor) ;
2230
2361
if module_def_id. is_top_level_module ( ) {
2231
2362
check_attr_visitor. check_attributes ( CRATE_HIR_ID , DUMMY_SP , Target :: Mod , None ) ;
2232
2363
check_invalid_crate_level_attr ( tcx, tcx. hir ( ) . krate_attrs ( ) ) ;
2233
2364
}
2365
+ if check_attr_visitor. abort . get ( ) {
2366
+ tcx. sess . abort_if_errors ( )
2367
+ }
2234
2368
}
2235
2369
2236
2370
pub ( crate ) fn provide ( providers : & mut Providers ) {
0 commit comments