]>
Commit | Line | Data |
---|---|---|
3c0e092e | 1 | use crate::base::ExtCtxt; |
e74abb32 | 2 | |
3dfed10e | 3 | use rustc_ast as ast; |
5e7ed085 | 4 | use rustc_ast::token; |
5869c6ff XL |
5 | use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; |
6 | use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; | |
74b04a01 | 7 | use rustc_ast_pretty::pprust; |
17df50a5 | 8 | use rustc_data_structures::fx::FxHashMap; |
dfeec247 | 9 | use rustc_data_structures::sync::Lrc; |
3c0e092e | 10 | use rustc_errors::{Diagnostic, PResult}; |
dfeec247 XL |
11 | use rustc_parse::lexer::nfc_normalize; |
12 | use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; | |
74b04a01 | 13 | use rustc_session::parse::ParseSess; |
17df50a5 | 14 | use rustc_span::def_id::CrateNum; |
f9f354fc | 15 | use rustc_span::symbol::{self, kw, sym, Symbol}; |
5e7ed085 | 16 | use rustc_span::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; |
416331ca | 17 | |
e74abb32 | 18 | use pm::bridge::{server, TokenTree}; |
dfeec247 | 19 | use pm::{Delimiter, Level, LineColumn, Spacing}; |
416331ca | 20 | use std::ops::Bound; |
dfeec247 | 21 | use std::{ascii, panic}; |
a1dfa0c6 XL |
22 | |
23 | trait FromInternal<T> { | |
24 | fn from_internal(x: T) -> Self; | |
25 | } | |
26 | ||
27 | trait ToInternal<T> { | |
28 | fn to_internal(self) -> T; | |
29 | } | |
30 | ||
31 | impl FromInternal<token::DelimToken> for Delimiter { | |
32 | fn from_internal(delim: token::DelimToken) -> Delimiter { | |
33 | match delim { | |
34 | token::Paren => Delimiter::Parenthesis, | |
35 | token::Brace => Delimiter::Brace, | |
36 | token::Bracket => Delimiter::Bracket, | |
37 | token::NoDelim => Delimiter::None, | |
38 | } | |
39 | } | |
40 | } | |
41 | ||
42 | impl ToInternal<token::DelimToken> for Delimiter { | |
43 | fn to_internal(self) -> token::DelimToken { | |
44 | match self { | |
45 | Delimiter::Parenthesis => token::Paren, | |
46 | Delimiter::Brace => token::Brace, | |
47 | Delimiter::Bracket => token::Bracket, | |
48 | Delimiter::None => token::NoDelim, | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
3c0e092e | 53 | impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> |
a1dfa0c6 XL |
54 | for TokenTree<Group, Punct, Ident, Literal> |
55 | { | |
dfeec247 | 56 | fn from_internal( |
3c0e092e | 57 | ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>), |
dfeec247 | 58 | ) -> Self { |
74b04a01 | 59 | use rustc_ast::token::*; |
a1dfa0c6 | 60 | |
1b1a35ee | 61 | let joint = spacing == Joint; |
dc9dc135 | 62 | let Token { kind, span } = match tree { |
0731742a XL |
63 | tokenstream::TokenTree::Delimited(span, delim, tts) => { |
64 | let delimiter = Delimiter::from_internal(delim); | |
f035d41b | 65 | return TokenTree::Group(Group { delimiter, stream: tts, span, flatten: false }); |
a1dfa0c6 | 66 | } |
dc9dc135 | 67 | tokenstream::TokenTree::Token(token) => token, |
a1dfa0c6 XL |
68 | }; |
69 | ||
70 | macro_rules! tt { | |
9fa01778 | 71 | ($ty:ident { $($field:ident $(: $value:expr)*),+ $(,)? }) => ( |
a1dfa0c6 | 72 | TokenTree::$ty(self::$ty { |
dc9dc135 | 73 | $($field $(: $value)*,)+ |
a1dfa0c6 XL |
74 | span, |
75 | }) | |
0731742a XL |
76 | ); |
77 | ($ty:ident::$method:ident($($value:expr),*)) => ( | |
78 | TokenTree::$ty(self::$ty::$method($($value,)* span)) | |
79 | ); | |
a1dfa0c6 XL |
80 | } |
81 | macro_rules! op { | |
82 | ($a:expr) => { | |
0731742a | 83 | tt!(Punct::new($a, joint)) |
a1dfa0c6 XL |
84 | }; |
85 | ($a:expr, $b:expr) => {{ | |
0731742a XL |
86 | stack.push(tt!(Punct::new($b, joint))); |
87 | tt!(Punct::new($a, true)) | |
a1dfa0c6 XL |
88 | }}; |
89 | ($a:expr, $b:expr, $c:expr) => {{ | |
0731742a XL |
90 | stack.push(tt!(Punct::new($c, joint))); |
91 | stack.push(tt!(Punct::new($b, true))); | |
92 | tt!(Punct::new($a, true)) | |
a1dfa0c6 XL |
93 | }}; |
94 | } | |
95 | ||
dc9dc135 | 96 | match kind { |
a1dfa0c6 XL |
97 | Eq => op!('='), |
98 | Lt => op!('<'), | |
99 | Le => op!('<', '='), | |
100 | EqEq => op!('=', '='), | |
101 | Ne => op!('!', '='), | |
102 | Ge => op!('>', '='), | |
103 | Gt => op!('>'), | |
104 | AndAnd => op!('&', '&'), | |
105 | OrOr => op!('|', '|'), | |
106 | Not => op!('!'), | |
107 | Tilde => op!('~'), | |
108 | BinOp(Plus) => op!('+'), | |
109 | BinOp(Minus) => op!('-'), | |
110 | BinOp(Star) => op!('*'), | |
111 | BinOp(Slash) => op!('/'), | |
112 | BinOp(Percent) => op!('%'), | |
113 | BinOp(Caret) => op!('^'), | |
114 | BinOp(And) => op!('&'), | |
115 | BinOp(Or) => op!('|'), | |
116 | BinOp(Shl) => op!('<', '<'), | |
117 | BinOp(Shr) => op!('>', '>'), | |
118 | BinOpEq(Plus) => op!('+', '='), | |
119 | BinOpEq(Minus) => op!('-', '='), | |
120 | BinOpEq(Star) => op!('*', '='), | |
121 | BinOpEq(Slash) => op!('/', '='), | |
122 | BinOpEq(Percent) => op!('%', '='), | |
123 | BinOpEq(Caret) => op!('^', '='), | |
124 | BinOpEq(And) => op!('&', '='), | |
125 | BinOpEq(Or) => op!('|', '='), | |
126 | BinOpEq(Shl) => op!('<', '<', '='), | |
127 | BinOpEq(Shr) => op!('>', '>', '='), | |
128 | At => op!('@'), | |
129 | Dot => op!('.'), | |
130 | DotDot => op!('.', '.'), | |
131 | DotDotDot => op!('.', '.', '.'), | |
132 | DotDotEq => op!('.', '.', '='), | |
133 | Comma => op!(','), | |
134 | Semi => op!(';'), | |
135 | Colon => op!(':'), | |
136 | ModSep => op!(':', ':'), | |
137 | RArrow => op!('-', '>'), | |
138 | LArrow => op!('<', '-'), | |
139 | FatArrow => op!('=', '>'), | |
140 | Pound => op!('#'), | |
141 | Dollar => op!('$'), | |
142 | Question => op!('?'), | |
143 | SingleQuote => op!('\''), | |
144 | ||
dc9dc135 | 145 | Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), |
3c0e092e | 146 | Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)), |
dc9dc135 | 147 | Lifetime(name) => { |
f9f354fc | 148 | let ident = symbol::Ident::new(name, span).without_first_quote(); |
3c0e092e | 149 | stack.push(tt!(Ident::new(rustc.sess(), ident.name, false))); |
0731742a | 150 | tt!(Punct::new('\'', true)) |
a1dfa0c6 | 151 | } |
dc9dc135 | 152 | Literal(lit) => tt!(Literal { lit }), |
3dfed10e | 153 | DocComment(_, attr_style, data) => { |
a1dfa0c6 | 154 | let mut escaped = String::new(); |
3dfed10e | 155 | for ch in data.as_str().chars() { |
a1dfa0c6 XL |
156 | escaped.extend(ch.escape_debug()); |
157 | } | |
5099ac24 | 158 | let stream = [ |
dc9dc135 | 159 | Ident(sym::doc, false), |
a1dfa0c6 | 160 | Eq, |
dc9dc135 | 161 | TokenKind::lit(token::Str, Symbol::intern(&escaped), None), |
a1dfa0c6 XL |
162 | ] |
163 | .into_iter() | |
dc9dc135 | 164 | .map(|kind| tokenstream::TokenTree::token(kind, span)) |
a1dfa0c6 XL |
165 | .collect(); |
166 | stack.push(TokenTree::Group(Group { | |
167 | delimiter: Delimiter::Bracket, | |
168 | stream, | |
169 | span: DelimSpan::from_single(span), | |
f035d41b | 170 | flatten: false, |
a1dfa0c6 | 171 | })); |
3dfed10e | 172 | if attr_style == ast::AttrStyle::Inner { |
0731742a | 173 | stack.push(tt!(Punct::new('!', false))); |
a1dfa0c6 | 174 | } |
0731742a | 175 | tt!(Punct::new('#', false)) |
a1dfa0c6 XL |
176 | } |
177 | ||
5e7ed085 FG |
178 | Interpolated(nt) if let NtIdent(ident, is_raw) = *nt => { |
179 | TokenTree::Ident(Ident::new(rustc.sess(), ident.name, is_raw, ident.span)) | |
94222f64 | 180 | } |
9fa01778 | 181 | Interpolated(nt) => { |
3c0e092e | 182 | let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No); |
94222f64 XL |
183 | TokenTree::Group(Group { |
184 | delimiter: Delimiter::None, | |
185 | stream, | |
186 | span: DelimSpan::from_single(span), | |
3c0e092e | 187 | flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()), |
94222f64 | 188 | }) |
a1dfa0c6 XL |
189 | } |
190 | ||
191 | OpenDelim(..) | CloseDelim(..) => unreachable!(), | |
1b1a35ee | 192 | Eof => unreachable!(), |
a1dfa0c6 XL |
193 | } |
194 | } | |
195 | } | |
196 | ||
197 | impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { | |
198 | fn to_internal(self) -> TokenStream { | |
74b04a01 | 199 | use rustc_ast::token::*; |
a1dfa0c6 XL |
200 | |
201 | let (ch, joint, span) = match self { | |
202 | TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), | |
f035d41b | 203 | TokenTree::Group(Group { delimiter, stream, span, .. }) => { |
74b04a01 XL |
204 | return tokenstream::TokenTree::Delimited(span, delimiter.to_internal(), stream) |
205 | .into(); | |
a1dfa0c6 | 206 | } |
0731742a | 207 | TokenTree::Ident(self::Ident { sym, is_raw, span }) => { |
dc9dc135 | 208 | return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); |
a1dfa0c6 XL |
209 | } |
210 | TokenTree::Literal(self::Literal { | |
dc9dc135 | 211 | lit: token::Lit { kind: token::Integer, symbol, suffix }, |
a1dfa0c6 | 212 | span, |
74b04a01 | 213 | }) if symbol.as_str().starts_with('-') => { |
a1dfa0c6 | 214 | let minus = BinOp(BinOpToken::Minus); |
dc9dc135 XL |
215 | let symbol = Symbol::intern(&symbol.as_str()[1..]); |
216 | let integer = TokenKind::lit(token::Integer, symbol, suffix); | |
217 | let a = tokenstream::TokenTree::token(minus, span); | |
218 | let b = tokenstream::TokenTree::token(integer, span); | |
5099ac24 | 219 | return [a, b].into_iter().collect(); |
a1dfa0c6 XL |
220 | } |
221 | TokenTree::Literal(self::Literal { | |
dc9dc135 | 222 | lit: token::Lit { kind: token::Float, symbol, suffix }, |
a1dfa0c6 | 223 | span, |
74b04a01 | 224 | }) if symbol.as_str().starts_with('-') => { |
a1dfa0c6 | 225 | let minus = BinOp(BinOpToken::Minus); |
dc9dc135 XL |
226 | let symbol = Symbol::intern(&symbol.as_str()[1..]); |
227 | let float = TokenKind::lit(token::Float, symbol, suffix); | |
228 | let a = tokenstream::TokenTree::token(minus, span); | |
229 | let b = tokenstream::TokenTree::token(float, span); | |
5099ac24 | 230 | return [a, b].into_iter().collect(); |
a1dfa0c6 | 231 | } |
dc9dc135 | 232 | TokenTree::Literal(self::Literal { lit, span }) => { |
dfeec247 | 233 | return tokenstream::TokenTree::token(Literal(lit), span).into(); |
a1dfa0c6 XL |
234 | } |
235 | }; | |
236 | ||
dc9dc135 | 237 | let kind = match ch { |
a1dfa0c6 XL |
238 | '=' => Eq, |
239 | '<' => Lt, | |
240 | '>' => Gt, | |
241 | '!' => Not, | |
242 | '~' => Tilde, | |
243 | '+' => BinOp(Plus), | |
244 | '-' => BinOp(Minus), | |
245 | '*' => BinOp(Star), | |
246 | '/' => BinOp(Slash), | |
247 | '%' => BinOp(Percent), | |
248 | '^' => BinOp(Caret), | |
249 | '&' => BinOp(And), | |
250 | '|' => BinOp(Or), | |
251 | '@' => At, | |
252 | '.' => Dot, | |
253 | ',' => Comma, | |
254 | ';' => Semi, | |
255 | ':' => Colon, | |
256 | '#' => Pound, | |
257 | '$' => Dollar, | |
258 | '?' => Question, | |
259 | '\'' => SingleQuote, | |
260 | _ => unreachable!(), | |
261 | }; | |
262 | ||
dc9dc135 | 263 | let tree = tokenstream::TokenTree::token(kind, span); |
1b1a35ee | 264 | TokenStream::new(vec![(tree, if joint { Joint } else { Alone })]) |
a1dfa0c6 XL |
265 | } |
266 | } | |
267 | ||
dfeec247 XL |
268 | impl ToInternal<rustc_errors::Level> for Level { |
269 | fn to_internal(self) -> rustc_errors::Level { | |
a1dfa0c6 | 270 | match self { |
3c0e092e | 271 | Level::Error => rustc_errors::Level::Error { lint: false }, |
dfeec247 XL |
272 | Level::Warning => rustc_errors::Level::Warning, |
273 | Level::Note => rustc_errors::Level::Note, | |
274 | Level::Help => rustc_errors::Level::Help, | |
a1dfa0c6 XL |
275 | _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), |
276 | } | |
277 | } | |
278 | } | |
279 | ||
3dfed10e XL |
280 | pub struct FreeFunctions; |
281 | ||
a1dfa0c6 XL |
282 | #[derive(Clone)] |
283 | pub struct TokenStreamIter { | |
284 | cursor: tokenstream::Cursor, | |
285 | stack: Vec<TokenTree<Group, Punct, Ident, Literal>>, | |
286 | } | |
287 | ||
288 | #[derive(Clone)] | |
289 | pub struct Group { | |
290 | delimiter: Delimiter, | |
291 | stream: TokenStream, | |
292 | span: DelimSpan, | |
f035d41b XL |
293 | /// A hack used to pass AST fragments to attribute and derive macros |
294 | /// as a single nonterminal token instead of a token stream. | |
295 | /// FIXME: It needs to be removed, but there are some compatibility issues (see #73345). | |
296 | flatten: bool, | |
a1dfa0c6 XL |
297 | } |
298 | ||
299 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] | |
300 | pub struct Punct { | |
301 | ch: char, | |
302 | // NB. not using `Spacing` here because it doesn't implement `Hash`. | |
303 | joint: bool, | |
304 | span: Span, | |
305 | } | |
306 | ||
0731742a XL |
307 | impl Punct { |
308 | fn new(ch: char, joint: bool, span: Span) -> Punct { | |
dfeec247 XL |
309 | const LEGAL_CHARS: &[char] = &[ |
310 | '=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', '&', '|', '@', '.', ',', ';', | |
311 | ':', '#', '$', '?', '\'', | |
312 | ]; | |
0731742a XL |
313 | if !LEGAL_CHARS.contains(&ch) { |
314 | panic!("unsupported character `{:?}`", ch) | |
315 | } | |
316 | Punct { ch, joint, span } | |
317 | } | |
318 | } | |
319 | ||
a1dfa0c6 XL |
320 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
321 | pub struct Ident { | |
322 | sym: Symbol, | |
a1dfa0c6 | 323 | is_raw: bool, |
0731742a XL |
324 | span: Span, |
325 | } | |
326 | ||
327 | impl Ident { | |
f9f354fc | 328 | fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { |
a2a8927a | 329 | let sym = nfc_normalize(sym.as_str()); |
48663c56 | 330 | let string = sym.as_str(); |
a2a8927a | 331 | if !rustc_lexer::is_ident(string) { |
0731742a XL |
332 | panic!("`{:?}` is not a valid identifier", string) |
333 | } | |
e74abb32 | 334 | if is_raw && !sym.can_be_raw() { |
532ac7d7 | 335 | panic!("`{}` cannot be a raw identifier", string); |
0731742a | 336 | } |
f9f354fc | 337 | sess.symbol_gallery.insert(sym, span); |
0731742a XL |
338 | Ident { sym, is_raw, span } |
339 | } | |
340 | fn dollar_crate(span: Span) -> Ident { | |
341 | // `$crate` is accepted as an ident only if it comes from the compiler. | |
dc9dc135 | 342 | Ident { sym: kw::DollarCrate, is_raw: false, span } |
0731742a | 343 | } |
a1dfa0c6 XL |
344 | } |
345 | ||
346 | // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. | |
347 | #[derive(Clone, Debug)] | |
348 | pub struct Literal { | |
349 | lit: token::Lit, | |
a1dfa0c6 XL |
350 | span: Span, |
351 | } | |
352 | ||
3c0e092e XL |
353 | pub(crate) struct Rustc<'a, 'b> { |
354 | ecx: &'a mut ExtCtxt<'b>, | |
a1dfa0c6 XL |
355 | def_site: Span, |
356 | call_site: Span, | |
e74abb32 | 357 | mixed_site: Span, |
17df50a5 | 358 | krate: CrateNum, |
17df50a5 | 359 | rebased_spans: FxHashMap<usize, Span>, |
a1dfa0c6 XL |
360 | } |
361 | ||
3c0e092e XL |
362 | impl<'a, 'b> Rustc<'a, 'b> { |
363 | pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self { | |
364 | let expn_data = ecx.current_expansion.id.expn_data(); | |
a1dfa0c6 | 365 | Rustc { |
3c0e092e XL |
366 | def_site: ecx.with_def_site_ctxt(expn_data.def_site), |
367 | call_site: ecx.with_call_site_ctxt(expn_data.call_site), | |
368 | mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site), | |
136023e0 | 369 | krate: expn_data.macro_def_id.unwrap().krate, |
17df50a5 | 370 | rebased_spans: FxHashMap::default(), |
3c0e092e | 371 | ecx, |
a1dfa0c6 XL |
372 | } |
373 | } | |
dc9dc135 | 374 | |
3c0e092e XL |
375 | fn sess(&self) -> &ParseSess { |
376 | self.ecx.parse_sess() | |
377 | } | |
378 | ||
dc9dc135 | 379 | fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal { |
dfeec247 | 380 | Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) } |
dc9dc135 | 381 | } |
a1dfa0c6 XL |
382 | } |
383 | ||
3c0e092e | 384 | impl server::Types for Rustc<'_, '_> { |
3dfed10e | 385 | type FreeFunctions = FreeFunctions; |
a1dfa0c6 XL |
386 | type TokenStream = TokenStream; |
387 | type TokenStreamBuilder = tokenstream::TokenStreamBuilder; | |
388 | type TokenStreamIter = TokenStreamIter; | |
389 | type Group = Group; | |
390 | type Punct = Punct; | |
391 | type Ident = Ident; | |
392 | type Literal = Literal; | |
393 | type SourceFile = Lrc<SourceFile>; | |
394 | type MultiSpan = Vec<Span>; | |
395 | type Diagnostic = Diagnostic; | |
396 | type Span = Span; | |
397 | } | |
398 | ||
3c0e092e | 399 | impl server::FreeFunctions for Rustc<'_, '_> { |
3dfed10e | 400 | fn track_env_var(&mut self, var: &str, value: Option<&str>) { |
3c0e092e XL |
401 | self.sess() |
402 | .env_depinfo | |
403 | .borrow_mut() | |
404 | .insert((Symbol::intern(var), value.map(Symbol::intern))); | |
3dfed10e | 405 | } |
136023e0 XL |
406 | |
407 | fn track_path(&mut self, path: &str) { | |
3c0e092e | 408 | self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path)); |
136023e0 | 409 | } |
3dfed10e XL |
410 | } |
411 | ||
3c0e092e | 412 | impl server::TokenStream for Rustc<'_, '_> { |
a1dfa0c6 | 413 | fn new(&mut self) -> Self::TokenStream { |
e74abb32 | 414 | TokenStream::default() |
a1dfa0c6 XL |
415 | } |
416 | fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { | |
417 | stream.is_empty() | |
418 | } | |
419 | fn from_str(&mut self, src: &str) -> Self::TokenStream { | |
60c5eb7d | 420 | parse_stream_from_source_str( |
dc9dc135 | 421 | FileName::proc_macro_source_code(src), |
a1dfa0c6 | 422 | src.to_string(), |
3c0e092e | 423 | self.sess(), |
a1dfa0c6 XL |
424 | Some(self.call_site), |
425 | ) | |
426 | } | |
427 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { | |
f035d41b | 428 | pprust::tts_to_string(stream) |
a1dfa0c6 | 429 | } |
3c0e092e XL |
430 | fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> { |
431 | // Parse the expression from our tokenstream. | |
432 | let expr: PResult<'_, _> = try { | |
433 | let mut p = rustc_parse::stream_to_parser( | |
434 | self.sess(), | |
435 | stream.clone(), | |
436 | Some("proc_macro expand expr"), | |
437 | ); | |
438 | let expr = p.parse_expr()?; | |
439 | if p.token != token::Eof { | |
440 | p.unexpected()?; | |
441 | } | |
442 | expr | |
443 | }; | |
5e7ed085 FG |
444 | let expr = expr.map_err(|mut err| { |
445 | err.emit(); | |
446 | })?; | |
3c0e092e XL |
447 | |
448 | // Perform eager expansion on the expression. | |
449 | let expr = self | |
450 | .ecx | |
451 | .expander() | |
452 | .fully_expand_fragment(crate::expand::AstFragment::Expr(expr)) | |
453 | .make_expr(); | |
454 | ||
455 | // NOTE: For now, limit `expand_expr` to exclusively expand to literals. | |
456 | // This may be relaxed in the future. | |
457 | // We don't use `nt_to_tokenstream` as the tokenstream currently cannot | |
458 | // be recovered in the general case. | |
459 | match &expr.kind { | |
460 | ast::ExprKind::Lit(l) => { | |
461 | Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into()) | |
462 | } | |
463 | ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { | |
464 | ast::ExprKind::Lit(l) => match l.token { | |
465 | token::Lit { kind: token::Integer | token::Float, .. } => { | |
a2a8927a | 466 | Ok(Self::TokenStream::from_iter([ |
3c0e092e XL |
467 | // FIXME: The span of the `-` token is lost when |
468 | // parsing, so we cannot faithfully recover it here. | |
469 | tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span), | |
470 | tokenstream::TokenTree::token(token::Literal(l.token), l.span), | |
a2a8927a | 471 | ])) |
3c0e092e XL |
472 | } |
473 | _ => Err(()), | |
474 | }, | |
475 | _ => Err(()), | |
476 | }, | |
477 | _ => Err(()), | |
478 | } | |
479 | } | |
a1dfa0c6 XL |
480 | fn from_token_tree( |
481 | &mut self, | |
482 | tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>, | |
483 | ) -> Self::TokenStream { | |
484 | tree.to_internal() | |
485 | } | |
486 | fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { | |
dfeec247 | 487 | TokenStreamIter { cursor: stream.trees(), stack: vec![] } |
a1dfa0c6 XL |
488 | } |
489 | } | |
490 | ||
3c0e092e | 491 | impl server::TokenStreamBuilder for Rustc<'_, '_> { |
a1dfa0c6 XL |
492 | fn new(&mut self) -> Self::TokenStreamBuilder { |
493 | tokenstream::TokenStreamBuilder::new() | |
494 | } | |
495 | fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) { | |
496 | builder.push(stream); | |
497 | } | |
498 | fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream { | |
499 | builder.build() | |
500 | } | |
501 | } | |
502 | ||
3c0e092e | 503 | impl server::TokenStreamIter for Rustc<'_, '_> { |
a1dfa0c6 XL |
504 | fn next( |
505 | &mut self, | |
506 | iter: &mut Self::TokenStreamIter, | |
507 | ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> { | |
508 | loop { | |
509 | let tree = iter.stack.pop().or_else(|| { | |
1b1a35ee | 510 | let next = iter.cursor.next_with_spacing()?; |
6a06907d | 511 | Some(TokenTree::from_internal((next, &mut iter.stack, self))) |
a1dfa0c6 | 512 | })?; |
f035d41b XL |
513 | // A hack used to pass AST fragments to attribute and derive macros |
514 | // as a single nonterminal token instead of a token stream. | |
515 | // Such token needs to be "unwrapped" and not represented as a delimited group. | |
516 | // FIXME: It needs to be removed, but there are some compatibility issues (see #73345). | |
a1dfa0c6 | 517 | if let TokenTree::Group(ref group) = tree { |
f035d41b | 518 | if group.flatten { |
0731742a | 519 | iter.cursor.append(group.stream.clone()); |
a1dfa0c6 XL |
520 | continue; |
521 | } | |
522 | } | |
523 | return Some(tree); | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
3c0e092e | 528 | impl server::Group for Rustc<'_, '_> { |
a1dfa0c6 | 529 | fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { |
f035d41b XL |
530 | Group { |
531 | delimiter, | |
532 | stream, | |
533 | span: DelimSpan::from_single(server::Span::call_site(self)), | |
534 | flatten: false, | |
535 | } | |
a1dfa0c6 XL |
536 | } |
537 | fn delimiter(&mut self, group: &Self::Group) -> Delimiter { | |
538 | group.delimiter | |
539 | } | |
540 | fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { | |
541 | group.stream.clone() | |
542 | } | |
543 | fn span(&mut self, group: &Self::Group) -> Self::Span { | |
544 | group.span.entire() | |
545 | } | |
546 | fn span_open(&mut self, group: &Self::Group) -> Self::Span { | |
547 | group.span.open | |
548 | } | |
549 | fn span_close(&mut self, group: &Self::Group) -> Self::Span { | |
550 | group.span.close | |
551 | } | |
552 | fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { | |
553 | group.span = DelimSpan::from_single(span); | |
554 | } | |
555 | } | |
556 | ||
3c0e092e | 557 | impl server::Punct for Rustc<'_, '_> { |
a1dfa0c6 | 558 | fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { |
0731742a | 559 | Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) |
a1dfa0c6 XL |
560 | } |
561 | fn as_char(&mut self, punct: Self::Punct) -> char { | |
562 | punct.ch | |
563 | } | |
564 | fn spacing(&mut self, punct: Self::Punct) -> Spacing { | |
dfeec247 | 565 | if punct.joint { Spacing::Joint } else { Spacing::Alone } |
a1dfa0c6 XL |
566 | } |
567 | fn span(&mut self, punct: Self::Punct) -> Self::Span { | |
568 | punct.span | |
569 | } | |
570 | fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { | |
571 | Punct { span, ..punct } | |
572 | } | |
573 | } | |
574 | ||
3c0e092e | 575 | impl server::Ident for Rustc<'_, '_> { |
a1dfa0c6 | 576 | fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { |
3c0e092e | 577 | Ident::new(self.sess(), Symbol::intern(string), is_raw, span) |
a1dfa0c6 XL |
578 | } |
579 | fn span(&mut self, ident: Self::Ident) -> Self::Span { | |
580 | ident.span | |
581 | } | |
582 | fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { | |
583 | Ident { span, ..ident } | |
584 | } | |
585 | } | |
586 | ||
3c0e092e | 587 | impl server::Literal for Rustc<'_, '_> { |
17df50a5 | 588 | fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> { |
94222f64 | 589 | let name = FileName::proc_macro_source_code(s); |
3c0e092e | 590 | let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned()); |
94222f64 XL |
591 | |
592 | let first_span = parser.token.span.data(); | |
593 | let minus_present = parser.eat(&token::BinOp(token::Minus)); | |
594 | ||
595 | let lit_span = parser.token.span.data(); | |
5e7ed085 FG |
596 | let token::Literal(mut lit) = parser.token.kind else { |
597 | return Err(()); | |
17df50a5 | 598 | }; |
94222f64 XL |
599 | |
600 | // Check no comment or whitespace surrounding the (possibly negative) | |
601 | // literal, or more tokens after it. | |
602 | if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() { | |
17df50a5 XL |
603 | return Err(()); |
604 | } | |
94222f64 XL |
605 | |
606 | if minus_present { | |
607 | // If minus is present, check no comment or whitespace in between it | |
608 | // and the literal token. | |
609 | if first_span.hi.0 != lit_span.lo.0 { | |
610 | return Err(()); | |
611 | } | |
612 | ||
613 | // Check literal is a kind we allow to be negated in a proc macro token. | |
614 | match lit.kind { | |
615 | token::LitKind::Bool | |
616 | | token::LitKind::Byte | |
617 | | token::LitKind::Char | |
618 | | token::LitKind::Str | |
619 | | token::LitKind::StrRaw(_) | |
620 | | token::LitKind::ByteStr | |
621 | | token::LitKind::ByteStrRaw(_) | |
622 | | token::LitKind::Err => return Err(()), | |
623 | token::LitKind::Integer | token::LitKind::Float => {} | |
624 | } | |
625 | ||
626 | // Synthesize a new symbol that includes the minus sign. | |
c295e0f8 | 627 | let symbol = Symbol::intern(&s[..1 + lit.symbol.as_str().len()]); |
94222f64 XL |
628 | lit = token::Lit::new(lit.kind, symbol, lit.suffix); |
629 | } | |
630 | ||
17df50a5 XL |
631 | Ok(Literal { lit, span: self.call_site }) |
632 | } | |
94222f64 XL |
633 | fn to_string(&mut self, literal: &Self::Literal) -> String { |
634 | literal.lit.to_string() | |
635 | } | |
f9f354fc XL |
636 | fn debug_kind(&mut self, literal: &Self::Literal) -> String { |
637 | format!("{:?}", literal.lit.kind) | |
638 | } | |
639 | fn symbol(&mut self, literal: &Self::Literal) -> String { | |
640 | literal.lit.symbol.to_string() | |
641 | } | |
642 | fn suffix(&mut self, literal: &Self::Literal) -> Option<String> { | |
643 | literal.lit.suffix.as_ref().map(Symbol::to_string) | |
a1dfa0c6 XL |
644 | } |
645 | fn integer(&mut self, n: &str) -> Self::Literal { | |
dc9dc135 | 646 | self.lit(token::Integer, Symbol::intern(n), None) |
a1dfa0c6 XL |
647 | } |
648 | fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { | |
dc9dc135 | 649 | self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind))) |
a1dfa0c6 XL |
650 | } |
651 | fn float(&mut self, n: &str) -> Self::Literal { | |
dc9dc135 | 652 | self.lit(token::Float, Symbol::intern(n), None) |
a1dfa0c6 XL |
653 | } |
654 | fn f32(&mut self, n: &str) -> Self::Literal { | |
416331ca | 655 | self.lit(token::Float, Symbol::intern(n), Some(sym::f32)) |
a1dfa0c6 XL |
656 | } |
657 | fn f64(&mut self, n: &str) -> Self::Literal { | |
416331ca | 658 | self.lit(token::Float, Symbol::intern(n), Some(sym::f64)) |
a1dfa0c6 XL |
659 | } |
660 | fn string(&mut self, string: &str) -> Self::Literal { | |
661 | let mut escaped = String::new(); | |
662 | for ch in string.chars() { | |
663 | escaped.extend(ch.escape_debug()); | |
664 | } | |
dc9dc135 | 665 | self.lit(token::Str, Symbol::intern(&escaped), None) |
a1dfa0c6 XL |
666 | } |
667 | fn character(&mut self, ch: char) -> Self::Literal { | |
668 | let mut escaped = String::new(); | |
669 | escaped.extend(ch.escape_unicode()); | |
dc9dc135 | 670 | self.lit(token::Char, Symbol::intern(&escaped), None) |
a1dfa0c6 XL |
671 | } |
672 | fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { | |
673 | let string = bytes | |
674 | .iter() | |
675 | .cloned() | |
676 | .flat_map(ascii::escape_default) | |
677 | .map(Into::<char>::into) | |
678 | .collect::<String>(); | |
dc9dc135 | 679 | self.lit(token::ByteStr, Symbol::intern(&string), None) |
a1dfa0c6 XL |
680 | } |
681 | fn span(&mut self, literal: &Self::Literal) -> Self::Span { | |
682 | literal.span | |
683 | } | |
684 | fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { | |
685 | literal.span = span; | |
686 | } | |
687 | fn subspan( | |
688 | &mut self, | |
689 | literal: &Self::Literal, | |
690 | start: Bound<usize>, | |
691 | end: Bound<usize>, | |
692 | ) -> Option<Self::Span> { | |
693 | let span = literal.span; | |
694 | let length = span.hi().to_usize() - span.lo().to_usize(); | |
695 | ||
696 | let start = match start { | |
697 | Bound::Included(lo) => lo, | |
1b1a35ee | 698 | Bound::Excluded(lo) => lo.checked_add(1)?, |
a1dfa0c6 XL |
699 | Bound::Unbounded => 0, |
700 | }; | |
701 | ||
702 | let end = match end { | |
1b1a35ee | 703 | Bound::Included(hi) => hi.checked_add(1)?, |
a1dfa0c6 XL |
704 | Bound::Excluded(hi) => hi, |
705 | Bound::Unbounded => length, | |
706 | }; | |
707 | ||
708 | // Bounds check the values, preventing addition overflow and OOB spans. | |
f035d41b XL |
709 | if start > u32::MAX as usize |
710 | || end > u32::MAX as usize | |
711 | || (u32::MAX - start as u32) < span.lo().to_u32() | |
712 | || (u32::MAX - end as u32) < span.lo().to_u32() | |
a1dfa0c6 XL |
713 | || start >= end |
714 | || end > length | |
715 | { | |
716 | return None; | |
717 | } | |
718 | ||
719 | let new_lo = span.lo() + BytePos::from_usize(start); | |
720 | let new_hi = span.lo() + BytePos::from_usize(end); | |
721 | Some(span.with_lo(new_lo).with_hi(new_hi)) | |
722 | } | |
723 | } | |
724 | ||
3c0e092e | 725 | impl server::SourceFile for Rustc<'_, '_> { |
a1dfa0c6 XL |
726 | fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { |
727 | Lrc::ptr_eq(file1, file2) | |
728 | } | |
729 | fn path(&mut self, file: &Self::SourceFile) -> String { | |
730 | match file.name { | |
ba9703b0 XL |
731 | FileName::Real(ref name) => name |
732 | .local_path() | |
17df50a5 | 733 | .expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`") |
a1dfa0c6 XL |
734 | .to_str() |
735 | .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") | |
736 | .to_string(), | |
17df50a5 | 737 | _ => file.name.prefer_local().to_string(), |
a1dfa0c6 XL |
738 | } |
739 | } | |
740 | fn is_real(&mut self, file: &Self::SourceFile) -> bool { | |
741 | file.is_real_file() | |
742 | } | |
743 | } | |
744 | ||
3c0e092e | 745 | impl server::MultiSpan for Rustc<'_, '_> { |
a1dfa0c6 XL |
746 | fn new(&mut self) -> Self::MultiSpan { |
747 | vec![] | |
748 | } | |
749 | fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) { | |
750 | spans.push(span) | |
751 | } | |
752 | } | |
753 | ||
3c0e092e | 754 | impl server::Diagnostic for Rustc<'_, '_> { |
a1dfa0c6 XL |
755 | fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { |
756 | let mut diag = Diagnostic::new(level.to_internal(), msg); | |
757 | diag.set_span(MultiSpan::from_spans(spans)); | |
758 | diag | |
759 | } | |
760 | fn sub( | |
761 | &mut self, | |
762 | diag: &mut Self::Diagnostic, | |
763 | level: Level, | |
764 | msg: &str, | |
765 | spans: Self::MultiSpan, | |
766 | ) { | |
767 | diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); | |
768 | } | |
5e7ed085 FG |
769 | fn emit(&mut self, mut diag: Self::Diagnostic) { |
770 | self.sess().span_diagnostic.emit_diagnostic(&mut diag); | |
a1dfa0c6 XL |
771 | } |
772 | } | |
773 | ||
3c0e092e | 774 | impl server::Span for Rustc<'_, '_> { |
a1dfa0c6 | 775 | fn debug(&mut self, span: Self::Span) -> String { |
3c0e092e | 776 | if self.ecx.ecfg.span_debug { |
f035d41b XL |
777 | format!("{:?}", span) |
778 | } else { | |
779 | format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) | |
780 | } | |
a1dfa0c6 XL |
781 | } |
782 | fn def_site(&mut self) -> Self::Span { | |
783 | self.def_site | |
784 | } | |
785 | fn call_site(&mut self) -> Self::Span { | |
786 | self.call_site | |
787 | } | |
e74abb32 XL |
788 | fn mixed_site(&mut self) -> Self::Span { |
789 | self.mixed_site | |
790 | } | |
a1dfa0c6 | 791 | fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { |
3c0e092e | 792 | self.sess().source_map().lookup_char_pos(span.lo()).file |
a1dfa0c6 XL |
793 | } |
794 | fn parent(&mut self, span: Self::Span) -> Option<Self::Span> { | |
c295e0f8 | 795 | span.parent_callsite() |
a1dfa0c6 XL |
796 | } |
797 | fn source(&mut self, span: Self::Span) -> Self::Span { | |
798 | span.source_callsite() | |
799 | } | |
800 | fn start(&mut self, span: Self::Span) -> LineColumn { | |
3c0e092e | 801 | let loc = self.sess().source_map().lookup_char_pos(span.lo()); |
dfeec247 | 802 | LineColumn { line: loc.line, column: loc.col.to_usize() } |
a1dfa0c6 XL |
803 | } |
804 | fn end(&mut self, span: Self::Span) -> LineColumn { | |
3c0e092e | 805 | let loc = self.sess().source_map().lookup_char_pos(span.hi()); |
dfeec247 | 806 | LineColumn { line: loc.line, column: loc.col.to_usize() } |
a1dfa0c6 | 807 | } |
c295e0f8 XL |
808 | fn before(&mut self, span: Self::Span) -> Self::Span { |
809 | span.shrink_to_lo() | |
810 | } | |
811 | fn after(&mut self, span: Self::Span) -> Self::Span { | |
812 | span.shrink_to_hi() | |
813 | } | |
a1dfa0c6 | 814 | fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> { |
3c0e092e XL |
815 | let self_loc = self.sess().source_map().lookup_char_pos(first.lo()); |
816 | let other_loc = self.sess().source_map().lookup_char_pos(second.lo()); | |
a1dfa0c6 XL |
817 | |
818 | if self_loc.file.name != other_loc.file.name { | |
819 | return None; | |
820 | } | |
821 | ||
822 | Some(first.to(second)) | |
823 | } | |
824 | fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { | |
825 | span.with_ctxt(at.ctxt()) | |
826 | } | |
dfeec247 | 827 | fn source_text(&mut self, span: Self::Span) -> Option<String> { |
3c0e092e | 828 | self.sess().source_map().span_to_snippet(span).ok() |
532ac7d7 | 829 | } |
17df50a5 XL |
830 | /// Saves the provided span into the metadata of |
831 | /// *the crate we are currently compiling*, which must | |
832 | /// be a proc-macro crate. This id can be passed to | |
833 | /// `recover_proc_macro_span` when our current crate | |
834 | /// is *run* as a proc-macro. | |
835 | /// | |
836 | /// Let's suppose that we have two crates - `my_client` | |
837 | /// and `my_proc_macro`. The `my_proc_macro` crate | |
838 | /// contains a procedural macro `my_macro`, which | |
839 | /// is implemented as: `quote! { "hello" }` | |
840 | /// | |
841 | /// When we *compile* `my_proc_macro`, we will execute | |
842 | /// the `quote` proc-macro. This will save the span of | |
843 | /// "hello" into the metadata of `my_proc_macro`. As a result, | |
844 | /// the body of `my_proc_macro` (after expansion) will end | |
5e7ed085 | 845 | /// up containing a call that looks like this: |
17df50a5 XL |
846 | /// `proc_macro::Ident::new("hello", proc_macro::Span::recover_proc_macro_span(0))` |
847 | /// | |
848 | /// where `0` is the id returned by this function. | |
849 | /// When `my_proc_macro` *executes* (during the compilation of `my_client`), | |
850 | /// the call to `recover_proc_macro_span` will load the corresponding | |
851 | /// span from the metadata of `my_proc_macro` (which we have access to, | |
852 | /// since we've loaded `my_proc_macro` from disk in order to execute it). | |
853 | /// In this way, we have obtained a span pointing into `my_proc_macro` | |
136023e0 | 854 | fn save_span(&mut self, span: Self::Span) -> usize { |
3c0e092e | 855 | self.sess().save_proc_macro_span(span) |
17df50a5 XL |
856 | } |
857 | fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span { | |
3c0e092e | 858 | let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site); |
17df50a5 | 859 | *self.rebased_spans.entry(id).or_insert_with(|| { |
136023e0 XL |
860 | // FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding, |
861 | // replace it with a def-site context until we are encoding it properly. | |
862 | resolver.get_proc_macro_quoted_span(krate, id).with_ctxt(def_site.ctxt()) | |
17df50a5 XL |
863 | }) |
864 | } | |
a1dfa0c6 | 865 | } |