@@ -35,98 +35,98 @@ impl AttrProcMacro for ExpandEnsures {
3535 }
3636}
3737
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 (
3950 ecx : & mut ExtCtxt < ' _ > ,
4051 attr_span : Span ,
4152 annotated : TokenStream ,
42- inject : impl FnOnce ( & mut Vec < TokenTree > ) -> Result < ( ) , ErrorGuaranteed > ,
53+ inject : impl FnOnce ( & mut TokenStream ) -> Result < ( ) , ErrorGuaranteed > ,
4354) -> Result < TokenStream , ErrorGuaranteed > {
44- let mut new_tts = Vec :: with_capacity ( annotated . len ( ) ) ;
55+ let mut new_tts = TokenStream :: default ( ) ;
4556 let mut cursor = annotated. iter ( ) ;
4657
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" ) ) ;
5674 }
5775
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+ }
6994 }
70- }
7195
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;
9698 }
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+ } ;
102101
103102 // At this point, we've transcribed everything from the `fn` through the formal parameter list
104103 // and return type declaration, (if any), but `tt` itself has *not* been transcribed.
105104 //
106105 // Now inject the AST contract form.
107106 //
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.
113107 inject ( & mut new_tts) ?;
114108
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
116110 // 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 ( ) ) ;
120112 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+ }
122122 }
123123
124124 // Record the span as a contract attribute expansion.
125125 // This is used later to stop users from using the extended syntax directly
126126 // which is gated via `rustc_contracts_internals`.
127127 ecx. psess ( ) . contract_attribute_spans . push ( attr_span) ;
128128
129- Ok ( TokenStream :: new ( new_tts) )
129+ Ok ( new_tts)
130130}
131131
132132fn expand_requires_tts (
@@ -135,16 +135,16 @@ fn expand_requires_tts(
135135 annotation : TokenStream ,
136136 annotated : TokenStream ,
137137) -> 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 (
140140 token:: Token :: from_ast_ident ( Ident :: new ( kw:: RustcContractRequires , attr_span) ) ,
141141 Spacing :: Joint ,
142142 ) ) ;
143- new_tts. push ( TokenTree :: Token (
143+ new_tts. push_tree ( TokenTree :: Token (
144144 token:: Token :: new ( token:: TokenKind :: OrOr , attr_span) ,
145145 Spacing :: Alone ,
146146 ) ) ;
147- new_tts. push ( TokenTree :: Delimited (
147+ new_tts. push_tree ( TokenTree :: Delimited (
148148 DelimSpan :: from_single ( attr_span) ,
149149 DelimSpacing :: new ( Spacing :: JointHidden , Spacing :: JointHidden ) ,
150150 token:: Delimiter :: Parenthesis ,
@@ -160,12 +160,12 @@ fn expand_ensures_tts(
160160 annotation : TokenStream ,
161161 annotated : TokenStream ,
162162) -> 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 (
165165 token:: Token :: from_ast_ident ( Ident :: new ( kw:: RustcContractEnsures , attr_span) ) ,
166166 Spacing :: Joint ,
167167 ) ) ;
168- new_tts. push ( TokenTree :: Delimited (
168+ new_tts. push_tree ( TokenTree :: Delimited (
169169 DelimSpan :: from_single ( attr_span) ,
170170 DelimSpacing :: new ( Spacing :: JointHidden , Spacing :: JointHidden ) ,
171171 token:: Delimiter :: Parenthesis ,
0 commit comments