diff options
Diffstat (limited to 'crates/parser/src/grammar/type_params.rs')
-rw-r--r-- | crates/parser/src/grammar/type_params.rs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs new file mode 100644 index 000000000..90dabb4c0 --- /dev/null +++ b/crates/parser/src/grammar/type_params.rs | |||
@@ -0,0 +1,209 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use super::*; | ||
4 | |||
5 | pub(super) fn opt_type_param_list(p: &mut Parser) { | ||
6 | if !p.at(T![<]) { | ||
7 | return; | ||
8 | } | ||
9 | type_param_list(p); | ||
10 | } | ||
11 | |||
12 | fn type_param_list(p: &mut Parser) { | ||
13 | assert!(p.at(T![<])); | ||
14 | let m = p.start(); | ||
15 | p.bump(T![<]); | ||
16 | |||
17 | while !p.at(EOF) && !p.at(T![>]) { | ||
18 | let m = p.start(); | ||
19 | |||
20 | // test generic_lifetime_type_attribute | ||
21 | // fn foo<#[derive(Lifetime)] 'a, #[derive(Type)] T>(_: &'a T) { | ||
22 | // } | ||
23 | attributes::outer_attributes(p); | ||
24 | |||
25 | match p.current() { | ||
26 | LIFETIME => lifetime_param(p, m), | ||
27 | IDENT => type_param(p, m), | ||
28 | CONST_KW => type_const_param(p, m), | ||
29 | _ => { | ||
30 | m.abandon(p); | ||
31 | p.err_and_bump("expected type parameter") | ||
32 | } | ||
33 | } | ||
34 | if !p.at(T![>]) && !p.expect(T![,]) { | ||
35 | break; | ||
36 | } | ||
37 | } | ||
38 | p.expect(T![>]); | ||
39 | m.complete(p, GENERIC_PARAM_LIST); | ||
40 | } | ||
41 | |||
42 | fn lifetime_param(p: &mut Parser, m: Marker) { | ||
43 | assert!(p.at(LIFETIME)); | ||
44 | p.bump(LIFETIME); | ||
45 | if p.at(T![:]) { | ||
46 | lifetime_bounds(p); | ||
47 | } | ||
48 | m.complete(p, LIFETIME_PARAM); | ||
49 | } | ||
50 | |||
51 | fn type_param(p: &mut Parser, m: Marker) { | ||
52 | assert!(p.at(IDENT)); | ||
53 | name(p); | ||
54 | if p.at(T![:]) { | ||
55 | bounds(p); | ||
56 | } | ||
57 | // test type_param_default | ||
58 | // struct S<T = i32>; | ||
59 | if p.at(T![=]) { | ||
60 | p.bump(T![=]); | ||
61 | types::type_(p) | ||
62 | } | ||
63 | m.complete(p, TYPE_PARAM); | ||
64 | } | ||
65 | |||
66 | // test const_param | ||
67 | // struct S<const N: u32>; | ||
68 | fn type_const_param(p: &mut Parser, m: Marker) { | ||
69 | assert!(p.at(CONST_KW)); | ||
70 | p.bump(T![const]); | ||
71 | name(p); | ||
72 | types::ascription(p); | ||
73 | m.complete(p, CONST_PARAM); | ||
74 | } | ||
75 | |||
76 | // test type_param_bounds | ||
77 | // struct S<T: 'a + ?Sized + (Copy)>; | ||
78 | pub(super) fn bounds(p: &mut Parser) { | ||
79 | assert!(p.at(T![:])); | ||
80 | p.bump(T![:]); | ||
81 | bounds_without_colon(p); | ||
82 | } | ||
83 | |||
84 | fn lifetime_bounds(p: &mut Parser) { | ||
85 | assert!(p.at(T![:])); | ||
86 | p.bump(T![:]); | ||
87 | while p.at(LIFETIME) { | ||
88 | p.bump(LIFETIME); | ||
89 | if !p.eat(T![+]) { | ||
90 | break; | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | pub(super) fn bounds_without_colon_m(p: &mut Parser, marker: Marker) -> CompletedMarker { | ||
96 | while type_bound(p) { | ||
97 | if !p.eat(T![+]) { | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | marker.complete(p, TYPE_BOUND_LIST) | ||
103 | } | ||
104 | |||
105 | pub(super) fn bounds_without_colon(p: &mut Parser) { | ||
106 | let m = p.start(); | ||
107 | bounds_without_colon_m(p, m); | ||
108 | } | ||
109 | |||
110 | fn type_bound(p: &mut Parser) -> bool { | ||
111 | let m = p.start(); | ||
112 | let has_paren = p.eat(T!['(']); | ||
113 | p.eat(T![?]); | ||
114 | match p.current() { | ||
115 | LIFETIME => p.bump(LIFETIME), | ||
116 | T![for] => types::for_type(p), | ||
117 | _ if paths::is_use_path_start(p) => types::path_type_(p, false), | ||
118 | _ => { | ||
119 | m.abandon(p); | ||
120 | return false; | ||
121 | } | ||
122 | } | ||
123 | if has_paren { | ||
124 | p.expect(T![')']); | ||
125 | } | ||
126 | m.complete(p, TYPE_BOUND); | ||
127 | |||
128 | true | ||
129 | } | ||
130 | |||
131 | // test where_clause | ||
132 | // fn foo() | ||
133 | // where | ||
134 | // 'a: 'b + 'c, | ||
135 | // T: Clone + Copy + 'static, | ||
136 | // Iterator::Item: 'a, | ||
137 | // <T as Iterator>::Item: 'a | ||
138 | // {} | ||
139 | pub(super) fn opt_where_clause(p: &mut Parser) { | ||
140 | if !p.at(T![where]) { | ||
141 | return; | ||
142 | } | ||
143 | let m = p.start(); | ||
144 | p.bump(T![where]); | ||
145 | |||
146 | while is_where_predicate(p) { | ||
147 | where_predicate(p); | ||
148 | |||
149 | let comma = p.eat(T![,]); | ||
150 | |||
151 | if is_where_clause_end(p) { | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | if !comma { | ||
156 | p.error("expected comma"); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | m.complete(p, WHERE_CLAUSE); | ||
161 | } | ||
162 | |||
163 | fn is_where_predicate(p: &mut Parser) -> bool { | ||
164 | match p.current() { | ||
165 | LIFETIME => true, | ||
166 | T![impl] => false, | ||
167 | token => types::TYPE_FIRST.contains(token), | ||
168 | } | ||
169 | } | ||
170 | |||
171 | fn is_where_clause_end(p: &mut Parser) -> bool { | ||
172 | matches!(p.current(), T!['{'] | T![;] | T![=]) | ||
173 | } | ||
174 | |||
175 | fn where_predicate(p: &mut Parser) { | ||
176 | let m = p.start(); | ||
177 | match p.current() { | ||
178 | LIFETIME => { | ||
179 | p.bump(LIFETIME); | ||
180 | if p.at(T![:]) { | ||
181 | bounds(p); | ||
182 | } else { | ||
183 | p.error("expected colon"); | ||
184 | } | ||
185 | } | ||
186 | T![impl] => { | ||
187 | p.error("expected lifetime or type"); | ||
188 | } | ||
189 | _ => { | ||
190 | // test where_pred_for | ||
191 | // fn for_trait<F>() | ||
192 | // where | ||
193 | // for<'a> F: Fn(&'a str) | ||
194 | // { } | ||
195 | if p.at(T![for]) { | ||
196 | types::for_binder(p); | ||
197 | } | ||
198 | |||
199 | types::type_(p); | ||
200 | |||
201 | if p.at(T![:]) { | ||
202 | bounds(p); | ||
203 | } else { | ||
204 | p.error("expected colon"); | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | m.complete(p, WHERE_PRED); | ||
209 | } | ||