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,19 @@ 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 ;
25
26
use rustc_middle:: ty:: query:: Providers ;
26
- use rustc_middle:: ty:: TyCtxt ;
27
+ use rustc_middle:: ty:: { ParamEnv , TyCtxt } ;
27
28
use rustc_session:: lint:: builtin:: {
28
29
CONFLICTING_REPR_HINTS , INVALID_DOC_ATTRIBUTES , UNUSED_ATTRIBUTES ,
29
30
} ;
30
31
use rustc_session:: parse:: feature_err;
31
32
use rustc_span:: symbol:: { kw, sym, Symbol } ;
32
33
use rustc_span:: { Span , DUMMY_SP } ;
33
34
use rustc_target:: spec:: abi:: Abi ;
35
+ use std:: cell:: Cell ;
34
36
use std:: collections:: hash_map:: Entry ;
35
37
36
38
pub ( crate ) fn target_from_impl_item < ' tcx > (
@@ -62,8 +64,27 @@ enum ItemLike<'tcx> {
62
64
ForeignItem ,
63
65
}
64
66
67
+ #[ derive( Copy , Clone ) ]
68
+ pub ( crate ) enum ProcMacroKind {
69
+ FunctionLike ,
70
+ Derive ,
71
+ Attribute ,
72
+ }
73
+
74
+ impl IntoDiagnosticArg for ProcMacroKind {
75
+ fn into_diagnostic_arg ( self ) -> rustc_errors:: DiagnosticArgValue < ' static > {
76
+ match self {
77
+ ProcMacroKind :: Attribute => "attribute proc macro" ,
78
+ ProcMacroKind :: Derive => "derive proc macro" ,
79
+ ProcMacroKind :: FunctionLike => "function-like proc macro" ,
80
+ }
81
+ . into_diagnostic_arg ( )
82
+ }
83
+ }
84
+
65
85
struct CheckAttrVisitor < ' tcx > {
66
86
tcx : TyCtxt < ' tcx > ,
87
+ abort : Cell < bool > ,
67
88
}
68
89
69
90
impl CheckAttrVisitor < ' _ > {
@@ -172,7 +193,7 @@ impl CheckAttrVisitor<'_> {
172
193
sym:: path => self . check_generic_attr ( hir_id, attr, target, Target :: Mod ) ,
173
194
sym:: plugin_registrar => self . check_plugin_registrar ( hir_id, attr, target) ,
174
195
sym:: macro_export => self . check_macro_export ( hir_id, attr, target) ,
175
- sym:: ignore | sym:: should_panic | sym :: proc_macro_derive => {
196
+ sym:: ignore | sym:: should_panic => {
176
197
self . check_generic_attr ( hir_id, attr, target, Target :: Fn )
177
198
}
178
199
sym:: automatically_derived => {
@@ -182,6 +203,16 @@ impl CheckAttrVisitor<'_> {
182
203
self . check_generic_attr ( hir_id, attr, target, Target :: Mod )
183
204
}
184
205
sym:: rustc_object_lifetime_default => self . check_object_lifetime_default ( hir_id) ,
206
+ sym:: proc_macro => {
207
+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: FunctionLike )
208
+ }
209
+ sym:: proc_macro_attribute => {
210
+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: Attribute ) ;
211
+ }
212
+ sym:: proc_macro_derive => {
213
+ self . check_generic_attr ( hir_id, attr, target, Target :: Fn ) ;
214
+ self . check_proc_macro ( hir_id, target, ProcMacroKind :: Derive )
215
+ }
185
216
_ => { }
186
217
}
187
218
@@ -2052,6 +2083,90 @@ impl CheckAttrVisitor<'_> {
2052
2083
errors:: Unused { attr_span : attr. span , note } ,
2053
2084
) ;
2054
2085
}
2086
+
2087
+ fn check_proc_macro ( & self , hir_id : HirId , target : Target , kind : ProcMacroKind ) {
2088
+ let expected_input_count = match kind {
2089
+ ProcMacroKind :: Attribute => 2 ,
2090
+ ProcMacroKind :: Derive | ProcMacroKind :: FunctionLike => 1 ,
2091
+ } ;
2092
+
2093
+ let expected_signature = match kind {
2094
+ ProcMacroKind :: Attribute => "fn(TokenStream, TokenStream) -> TokenStream" ,
2095
+ ProcMacroKind :: Derive | ProcMacroKind :: FunctionLike => "fn(TokenStream) -> TokenStream" ,
2096
+ } ;
2097
+
2098
+ let tcx = self . tcx ;
2099
+ if target == Target :: Fn {
2100
+ let Some ( tokenstream) = tcx. get_diagnostic_item ( sym:: TokenStream ) else { return } ;
2101
+ let tokenstream = tcx. type_of ( tokenstream) ;
2102
+
2103
+ let id = hir_id. expect_owner ( ) ;
2104
+ let hir_sig = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . unwrap ( ) ;
2105
+
2106
+ let sig = tcx. fn_sig ( id) ;
2107
+
2108
+ if sig. abi ( ) != Abi :: Rust {
2109
+ tcx. sess
2110
+ . emit_err ( ProcMacroInvalidAbi { span : hir_sig. span , abi : sig. abi ( ) . name ( ) } ) ;
2111
+ self . abort . set ( true ) ;
2112
+ }
2113
+
2114
+ if sig. unsafety ( ) == Unsafety :: Unsafe {
2115
+ tcx. sess . emit_err ( ProcMacroUnsafe { span : hir_sig. span } ) ;
2116
+ self . abort . set ( true ) ;
2117
+ }
2118
+
2119
+ let output = sig. output ( ) . skip_binder ( ) ;
2120
+
2121
+ // Typecheck the output
2122
+ if tcx. normalize_erasing_regions ( ParamEnv :: empty ( ) , output) != tokenstream {
2123
+ tcx. sess . emit_err ( ProcMacroTypeError {
2124
+ span : hir_sig. decl . output . span ( ) ,
2125
+ found : output,
2126
+ kind,
2127
+ expected_signature,
2128
+ } ) ;
2129
+ self . abort . set ( true ) ;
2130
+ }
2131
+
2132
+ // Typecheck "expected_input_count" inputs, emitting
2133
+ // `ProcMacroMissingArguments` if there are not enough.
2134
+ if let Some ( args) = sig. inputs ( ) . skip_binder ( ) . get ( 0 ..expected_input_count) {
2135
+ for ( arg, input) in args. iter ( ) . zip ( hir_sig. decl . inputs ) {
2136
+ if tcx. normalize_erasing_regions ( ParamEnv :: empty ( ) , * arg) != tokenstream {
2137
+ tcx. sess . emit_err ( ProcMacroTypeError {
2138
+ span : input. span ,
2139
+ found : * arg,
2140
+ kind,
2141
+ expected_signature,
2142
+ } ) ;
2143
+ self . abort . set ( true ) ;
2144
+ }
2145
+ }
2146
+ } else {
2147
+ tcx. sess . emit_err ( ProcMacroMissingArguments {
2148
+ expected_input_count,
2149
+ span : hir_sig. span ,
2150
+ kind,
2151
+ expected_signature,
2152
+ } ) ;
2153
+ self . abort . set ( true ) ;
2154
+ }
2155
+
2156
+ // Check that there are not too many arguments
2157
+ let body_id = tcx. hir ( ) . body_owned_by ( id. def_id ) ;
2158
+ let excess = tcx. hir ( ) . body ( body_id) . params . get ( expected_input_count..) ;
2159
+ if let Some ( excess @ [ begin @ end] | excess @ [ begin, .., end] ) = excess {
2160
+ tcx. sess . emit_err ( ProcMacroDiffArguments {
2161
+ span : begin. span . to ( end. span ) ,
2162
+ count : excess. len ( ) ,
2163
+ kind,
2164
+ expected_signature,
2165
+ } ) ;
2166
+ self . abort . set ( true ) ;
2167
+ }
2168
+ }
2169
+ }
2055
2170
}
2056
2171
2057
2172
impl < ' tcx > Visitor < ' tcx > for CheckAttrVisitor < ' tcx > {
@@ -2214,12 +2329,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
2214
2329
}
2215
2330
2216
2331
fn check_mod_attrs ( tcx : TyCtxt < ' _ > , module_def_id : LocalDefId ) {
2217
- let check_attr_visitor = & mut CheckAttrVisitor { tcx } ;
2332
+ let check_attr_visitor = & mut CheckAttrVisitor { tcx, abort : Cell :: new ( false ) } ;
2218
2333
tcx. hir ( ) . visit_item_likes_in_module ( module_def_id, check_attr_visitor) ;
2219
2334
if module_def_id. is_top_level_module ( ) {
2220
2335
check_attr_visitor. check_attributes ( CRATE_HIR_ID , DUMMY_SP , Target :: Mod , None ) ;
2221
2336
check_invalid_crate_level_attr ( tcx, tcx. hir ( ) . krate_attrs ( ) ) ;
2222
2337
}
2338
+ if check_attr_visitor. abort . get ( ) {
2339
+ tcx. sess . abort_if_errors ( )
2340
+ }
2223
2341
}
2224
2342
2225
2343
pub ( crate ) fn provide ( providers : & mut Providers ) {
0 commit comments