@@ -35,98 +35,98 @@ impl AttrProcMacro for ExpandEnsures {
35
35
}
36
36
}
37
37
38
- fn expand_injecting_circa_where_clause (
38
+ /// Expand the function signature to include the contract clause.
39
+ ///
40
+ /// The contracts clause will be injected before the function body and the optional where clause.
41
+ /// For that, we search for the body / where token, and invoke the `inject` callback to generate the
42
+ /// contract clause in the right place.
43
+ ///
44
+ // FIXME: this kind of manual token tree munging does not have significant precedent among
45
+ // rustc builtin macros, probably because most builtin macros use direct AST manipulation to
46
+ // accomplish similar goals. But since our attributes need to take arbitrary expressions, and
47
+ // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST
48
+ // annotated, we end up doing token tree manipulation.
49
+ fn expand_contract_clause (
39
50
ecx : & mut ExtCtxt < ' _ > ,
40
51
attr_span : Span ,
41
52
annotated : TokenStream ,
42
- inject : impl FnOnce ( & mut Vec < TokenTree > ) -> Result < ( ) , ErrorGuaranteed > ,
53
+ inject : impl FnOnce ( & mut TokenStream ) -> Result < ( ) , ErrorGuaranteed > ,
43
54
) -> Result < TokenStream , ErrorGuaranteed > {
44
- let mut new_tts = Vec :: with_capacity ( annotated . len ( ) ) ;
55
+ let mut new_tts = TokenStream :: default ( ) ;
45
56
let mut cursor = annotated. iter ( ) ;
46
57
47
- // Find the `fn name<G,...>(x:X,...)` and inject the AST contract forms right after
48
- // the formal parameters (and return type if any).
49
- while let Some ( tt) = cursor. next ( ) {
50
- new_tts. push ( tt. clone ( ) ) ;
51
- if let TokenTree :: Token ( tok, _) = tt
52
- && tok. is_ident_named ( kw:: Fn )
53
- {
54
- break ;
55
- }
58
+ let is_kw = |tt : & TokenTree , sym : Symbol | {
59
+ if let TokenTree :: Token ( token, _) = tt { token. is_ident_named ( sym) } else { false }
60
+ } ;
61
+
62
+ // Find the `fn` keyword to check if this is a function.
63
+ if cursor
64
+ . find ( |tt| {
65
+ new_tts. push_tree ( ( * tt) . clone ( ) ) ;
66
+ is_kw ( tt, kw:: Fn )
67
+ } )
68
+ . is_none ( )
69
+ {
70
+ return Err ( ecx
71
+ . sess
72
+ . dcx ( )
73
+ . span_err ( attr_span, "contract annotations can only be used on functions" ) ) ;
56
74
}
57
75
58
- // Found the `fn` keyword, now find the formal parameters.
59
- //
60
- // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo<F: Fn(X) -> Y>` ?
61
- while let Some ( tt) = cursor. next ( ) {
62
- new_tts. push ( tt. clone ( ) ) ;
63
-
64
- if let TokenTree :: Delimited ( _, _, token:: Delimiter :: Parenthesis , _) = tt {
65
- break ;
66
- }
67
- if let TokenTree :: Token ( token:: Token { kind : token:: TokenKind :: Semi , .. } , _) = tt {
68
- panic ! ( "contract attribute applied to fn without parameter list." ) ;
76
+ // Found the `fn` keyword, now find either the `where` token or the function body.
77
+ let next_tt = loop {
78
+ let Some ( tt) = cursor. next ( ) else {
79
+ return Err ( ecx. sess . dcx ( ) . span_err (
80
+ attr_span,
81
+ "contract annotations is only supported in functions with bodies" ,
82
+ ) ) ;
83
+ } ;
84
+ // If `tt` is the last element. Check if it is the function body.
85
+ if cursor. peek ( ) . is_none ( ) {
86
+ if let TokenTree :: Delimited ( _, _, token:: Delimiter :: Brace , _) = tt {
87
+ break tt;
88
+ } else {
89
+ return Err ( ecx. sess . dcx ( ) . span_err (
90
+ attr_span,
91
+ "contract annotations is only supported in functions with bodies" ,
92
+ ) ) ;
93
+ }
69
94
}
70
- }
71
95
72
- // There *might* be a return type declaration (and figuring out where that ends would require
73
- // parsing an arbitrary type expression, e.g. `-> Foo<args ...>`
74
- //
75
- // Instead of trying to figure that out, scan ahead and look for the first occurence of a
76
- // `where`, a `{ ... }`, or a `;`.
77
- //
78
- // FIXME: this might still fall into a trap for something like `-> Ctor<T, const { 0 }>`. I
79
- // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form
80
- // prefixed by e.g. `const`, so we should still be able to filter them out without having to
81
- // parse the type expression itself. But rather than try to fix things with hacks like that,
82
- // time might be better spent extending the attribute expander to suport tt-annotation atop
83
- // ast-annotated, which would be an elegant way to sidestep all of this.
84
- let mut opt_next_tt = cursor. next ( ) ;
85
- while let Some ( next_tt) = opt_next_tt {
86
- if let TokenTree :: Token ( tok, _) = next_tt
87
- && tok. is_ident_named ( kw:: Where )
88
- {
89
- break ;
90
- }
91
- if let TokenTree :: Delimited ( _, _, token:: Delimiter :: Brace , _) = next_tt {
92
- break ;
93
- }
94
- if let TokenTree :: Token ( token:: Token { kind : token:: TokenKind :: Semi , .. } , _) = next_tt {
95
- break ;
96
+ if is_kw ( tt, kw:: Where ) {
97
+ break tt;
96
98
}
97
-
98
- // for anything else, transcribe the tt and keep looking.
99
- new_tts. push ( next_tt. clone ( ) ) ;
100
- opt_next_tt = cursor. next ( ) ;
101
- }
99
+ new_tts. push_tree ( tt. clone ( ) ) ;
100
+ } ;
102
101
103
102
// At this point, we've transcribed everything from the `fn` through the formal parameter list
104
103
// and return type declaration, (if any), but `tt` itself has *not* been transcribed.
105
104
//
106
105
// Now inject the AST contract form.
107
106
//
108
- // FIXME: this kind of manual token tree munging does not have significant precedent among
109
- // rustc builtin macros, probably because most builtin macros use direct AST manipulation to
110
- // accomplish similar goals. But since our attributes need to take arbitrary expressions, and
111
- // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST
112
- // annotated, we end up doing token tree manipulation.
113
107
inject ( & mut new_tts) ?;
114
108
115
- // Above we injected the internal AST requires/ensures contruct . Now copy over all the other
109
+ // Above we injected the internal AST requires/ensures construct . Now copy over all the other
116
110
// token trees.
117
- if let Some ( tt) = opt_next_tt {
118
- new_tts. push ( tt. clone ( ) ) ;
119
- }
111
+ new_tts. push_tree ( next_tt. clone ( ) ) ;
120
112
while let Some ( tt) = cursor. next ( ) {
121
- new_tts. push ( tt. clone ( ) ) ;
113
+ new_tts. push_tree ( tt. clone ( ) ) ;
114
+ if cursor. peek ( ) . is_none ( )
115
+ && !matches ! ( tt, TokenTree :: Delimited ( _, _, token:: Delimiter :: Brace , _) )
116
+ {
117
+ return Err ( ecx. sess . dcx ( ) . span_err (
118
+ attr_span,
119
+ "contract annotations is only supported in functions with bodies" ,
120
+ ) ) ;
121
+ }
122
122
}
123
123
124
124
// Record the span as a contract attribute expansion.
125
125
// This is used later to stop users from using the extended syntax directly
126
126
// which is gated via `rustc_contracts_internals`.
127
127
ecx. psess ( ) . contract_attribute_spans . push ( attr_span) ;
128
128
129
- Ok ( TokenStream :: new ( new_tts) )
129
+ Ok ( new_tts)
130
130
}
131
131
132
132
fn expand_requires_tts (
@@ -135,16 +135,16 @@ fn expand_requires_tts(
135
135
annotation : TokenStream ,
136
136
annotated : TokenStream ,
137
137
) -> Result < TokenStream , ErrorGuaranteed > {
138
- expand_injecting_circa_where_clause ( _ecx, attr_span, annotated, |new_tts| {
139
- new_tts. push ( TokenTree :: Token (
138
+ expand_contract_clause ( _ecx, attr_span, annotated, |new_tts| {
139
+ new_tts. push_tree ( TokenTree :: Token (
140
140
token:: Token :: from_ast_ident ( Ident :: new ( kw:: RustcContractRequires , attr_span) ) ,
141
141
Spacing :: Joint ,
142
142
) ) ;
143
- new_tts. push ( TokenTree :: Token (
143
+ new_tts. push_tree ( TokenTree :: Token (
144
144
token:: Token :: new ( token:: TokenKind :: OrOr , attr_span) ,
145
145
Spacing :: Alone ,
146
146
) ) ;
147
- new_tts. push ( TokenTree :: Delimited (
147
+ new_tts. push_tree ( TokenTree :: Delimited (
148
148
DelimSpan :: from_single ( attr_span) ,
149
149
DelimSpacing :: new ( Spacing :: JointHidden , Spacing :: JointHidden ) ,
150
150
token:: Delimiter :: Parenthesis ,
@@ -160,12 +160,12 @@ fn expand_ensures_tts(
160
160
annotation : TokenStream ,
161
161
annotated : TokenStream ,
162
162
) -> Result < TokenStream , ErrorGuaranteed > {
163
- expand_injecting_circa_where_clause ( _ecx, attr_span, annotated, |new_tts| {
164
- new_tts. push ( TokenTree :: Token (
163
+ expand_contract_clause ( _ecx, attr_span, annotated, |new_tts| {
164
+ new_tts. push_tree ( TokenTree :: Token (
165
165
token:: Token :: from_ast_ident ( Ident :: new ( kw:: RustcContractEnsures , attr_span) ) ,
166
166
Spacing :: Joint ,
167
167
) ) ;
168
- new_tts. push ( TokenTree :: Delimited (
168
+ new_tts. push_tree ( TokenTree :: Delimited (
169
169
DelimSpan :: from_single ( attr_span) ,
170
170
DelimSpacing :: new ( Spacing :: JointHidden , Spacing :: JointHidden ) ,
171
171
token:: Delimiter :: Parenthesis ,
0 commit comments