1
1
#![ allow( unused_imports, unused_variables) ]
2
2
3
3
use rustc_ast:: token;
4
- use rustc_ast:: tokenstream:: { DelimSpacing , DelimSpan , Spacing , TokenStream , TokenTree } ;
4
+ use rustc_ast:: tokenstream:: { DelimSpacing , DelimSpan , Spacing } ;
5
+ use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
5
6
use rustc_errors:: ErrorGuaranteed ;
6
7
use rustc_expand:: base:: { AttrProcMacro , ExtCtxt } ;
7
- use rustc_span:: symbol:: { sym, Symbol } ;
8
+ use rustc_span:: symbol:: { kw , Ident , sym, Symbol } ;
8
9
use rustc_span:: Span ;
9
10
10
11
pub ( crate ) struct ExpandRequires ;
@@ -19,9 +20,9 @@ impl AttrProcMacro for ExpandRequires {
19
20
ecx : & ' cx mut ExtCtxt < ' _ > ,
20
21
span : Span ,
21
22
annotation : TokenStream ,
22
- annotated : TokenStream ,
23
+ annotated : TokenStream ,
23
24
) -> Result < TokenStream , ErrorGuaranteed > {
24
- todo ! ( )
25
+ expand_requires_tts ( ecx , span , annotation , annotated )
25
26
}
26
27
}
27
28
@@ -31,9 +32,9 @@ impl AttrProcMacro for ExpandCaptures {
31
32
ecx : & ' cx mut ExtCtxt < ' _ > ,
32
33
span : Span ,
33
34
annotation : TokenStream ,
34
- annotated : TokenStream ,
35
+ annotated : TokenStream ,
35
36
) -> Result < TokenStream , ErrorGuaranteed > {
36
- todo ! ( )
37
+ todo ! ( )
37
38
}
38
39
}
39
40
@@ -43,8 +44,113 @@ impl AttrProcMacro for ExpandEnsures {
43
44
ecx : & ' cx mut ExtCtxt < ' _ > ,
44
45
span : Span ,
45
46
annotation : TokenStream ,
46
- annotated : TokenStream ,
47
+ annotated : TokenStream ,
47
48
) -> Result < TokenStream , ErrorGuaranteed > {
48
- todo ! ( )
49
+ todo ! ( )
49
50
}
50
51
}
52
+
53
+ fn expand_injecting_circa_where_clause (
54
+ _ecx : & mut ExtCtxt < ' _ > ,
55
+ attr_span : Span ,
56
+ annotated : TokenStream ,
57
+ inject : impl FnOnce ( & mut Vec < TokenTree > ) -> Result < ( ) , ErrorGuaranteed > ,
58
+ ) -> Result < TokenStream , ErrorGuaranteed > {
59
+ let mut new_tts = Vec :: with_capacity ( annotated. len ( ) ) ;
60
+ let mut cursor = annotated. into_trees ( ) ;
61
+
62
+ // Find the `fn name<G,...>(x:X,...)` and inject the AST contract forms right after
63
+ // the formal parameters (and return type if any).
64
+ while let Some ( tt) = cursor. next_ref ( ) {
65
+ new_tts. push ( tt. clone ( ) ) ;
66
+ if let TokenTree :: Token ( tok, _) = tt && tok. is_ident_named ( kw:: Fn ) {
67
+ break ;
68
+ }
69
+ }
70
+
71
+ // Found the `fn` keyword, now find the formal parameters.
72
+ //
73
+ // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo<F: Fn(X) -> Y>` ?
74
+ while let Some ( tt) = cursor. next_ref ( ) {
75
+ new_tts. push ( tt. clone ( ) ) ;
76
+
77
+ if let TokenTree :: Delimited ( _, _, token:: Delimiter :: Parenthesis , _) = tt {
78
+ break ;
79
+ }
80
+ if let TokenTree :: Token ( token:: Token { kind : token:: TokenKind :: Semi , .. } , _) = tt {
81
+ panic ! ( "contract attribute applied to fn without parameter list." ) ;
82
+ }
83
+ }
84
+
85
+ // There *might* be a return type declaration (and figuring out where that ends would require parsing an arbitrary type expression, e.g. `-> Foo<args ...>`
86
+ //
87
+ // Instead of trying to figure that out, scan ahead and look for the first occurence of a `where`, a `{ ... }`, or a `;`.
88
+ //
89
+ // FIXME: this might still fall into a trap for something like `-> Ctor<T, const { 0 }>`. I
90
+ // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form
91
+ // prefixed by e.g. `const`, so we should still be able to filter them out without having to
92
+ // parse the type expression itself. But rather than try to fix things with hacks like that,
93
+ // time might be better spent extending the attribute expander to suport tt-annotation atop
94
+ // ast-annotated, which would be an elegant way to sidestep all of this.
95
+ let mut opt_next_tt = cursor. next_ref ( ) ;
96
+ while let Some ( next_tt) = opt_next_tt {
97
+ if let TokenTree :: Token ( tok, _) = next_tt && tok. is_ident_named ( kw:: Where ) {
98
+ break ;
99
+ }
100
+ if let TokenTree :: Delimited ( _, _, token:: Delimiter :: Brace , _) = next_tt {
101
+ break ;
102
+ }
103
+ if let TokenTree :: Token ( token:: Token { kind : token:: TokenKind :: Semi , .. } , _) = next_tt {
104
+ break ;
105
+ }
106
+
107
+ // for anything else, transcribe the tt and keep looking.
108
+ new_tts. push ( next_tt. clone ( ) ) ;
109
+ opt_next_tt = cursor. next_ref ( ) ;
110
+ continue ;
111
+ }
112
+
113
+ // At this point, we've transcribed everything from the `fn` through the formal parameter
114
+ // list and return type declaration, (if any), but `tt` itself has *not* been transcribed.
115
+ //
116
+ // Now inject the AST contract form.
117
+ //
118
+ // FIXME: this kind of manual token tree munging does not have significant precedent among rustc
119
+ // builtin macros, probably because most builtin macros use direct AST manipulation to accomplish
120
+ // similar goals. But since our attributes need to take arbitrary expressions, and our attribute
121
+ // infrastructure does not yet support mixing a token-tree annotation with an AST annotated, we
122
+ // end up doing token tree manipulation.
123
+ inject ( & mut new_tts) ?;
124
+
125
+ // Above we injected the internal AST requires contruct. Now copy over all the other token trees.
126
+ if let Some ( tt) = opt_next_tt {
127
+ new_tts. push ( tt. clone ( ) ) ;
128
+ }
129
+ while let Some ( tt) = cursor. next_ref ( ) {
130
+ new_tts. push ( tt. clone ( ) ) ;
131
+ }
132
+
133
+ Ok ( TokenStream :: new ( new_tts) )
134
+ }
135
+
136
+ fn expand_requires_tts (
137
+ _ecx : & mut ExtCtxt < ' _ > ,
138
+ attr_span : Span ,
139
+ annotation : TokenStream ,
140
+ annotated : TokenStream ,
141
+ ) -> Result < TokenStream , ErrorGuaranteed > {
142
+ expand_injecting_circa_where_clause (
143
+ _ecx,
144
+ attr_span,
145
+ annotated,
146
+ |new_tts| {
147
+ new_tts. push ( TokenTree :: Token ( token:: Token :: from_ast_ident ( Ident :: new ( kw:: RustcContractRequires , attr_span) ) ,
148
+ Spacing :: Joint ) ) ;
149
+ new_tts. push ( TokenTree :: Delimited ( DelimSpan :: from_single ( attr_span) ,
150
+ DelimSpacing :: new ( Spacing :: JointHidden , Spacing :: JointHidden ) ,
151
+ token:: Delimiter :: Parenthesis ,
152
+ annotation) ) ;
153
+ Ok ( ( ) )
154
+ } ,
155
+ )
156
+ }
0 commit comments