]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/token.rs
syntax: Some code cleanup
[rust.git] / src / libsyntax / parse / token.rs
1 pub use BinOpToken::*;
2 pub use Nonterminal::*;
3 pub use DelimToken::*;
4 pub use LitKind::*;
5 pub use Token::*;
6
7 use crate::ast::{self};
8 use crate::parse::ParseSess;
9 use crate::print::pprust;
10 use crate::ptr::P;
11 use crate::symbol::kw;
12 use crate::syntax::parse::parse_stream_from_source_str;
13 use crate::tokenstream::{self, DelimSpan, TokenStream, TokenTree};
14
15 use syntax_pos::symbol::{self, Symbol};
16 use syntax_pos::{self, Span, FileName};
17 use log::info;
18
19 use std::fmt;
20 use std::mem;
21 #[cfg(target_arch = "x86_64")]
22 use rustc_data_structures::static_assert_size;
23 use rustc_data_structures::sync::Lrc;
24
25 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
26 pub enum BinOpToken {
27     Plus,
28     Minus,
29     Star,
30     Slash,
31     Percent,
32     Caret,
33     And,
34     Or,
35     Shl,
36     Shr,
37 }
38
39 /// A delimiter token.
40 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
41 pub enum DelimToken {
42     /// A round parenthesis (i.e., `(` or `)`).
43     Paren,
44     /// A square bracket (i.e., `[` or `]`).
45     Bracket,
46     /// A curly brace (i.e., `{` or `}`).
47     Brace,
48     /// An empty delimiter.
49     NoDelim,
50 }
51
52 impl DelimToken {
53     pub fn len(self) -> usize {
54         if self == NoDelim { 0 } else { 1 }
55     }
56
57     pub fn is_empty(self) -> bool {
58         self == NoDelim
59     }
60 }
61
62 #[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug)]
63 pub enum LitKind {
64     Bool, // AST only, must never appear in a `Token`
65     Byte,
66     Char,
67     Integer,
68     Float,
69     Str,
70     StrRaw(u16), // raw string delimited by `n` hash symbols
71     ByteStr,
72     ByteStrRaw(u16), // raw byte string delimited by `n` hash symbols
73     Err,
74 }
75
76 /// A literal token.
77 #[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug)]
78 pub struct Lit {
79     pub kind: LitKind,
80     pub symbol: Symbol,
81     pub suffix: Option<Symbol>,
82 }
83
84 impl LitKind {
85     /// An English article for the literal token kind.
86     crate fn article(self) -> &'static str {
87         match self {
88             Integer | Err => "an",
89             _ => "a",
90         }
91     }
92
93     crate fn descr(self) -> &'static str {
94         match self {
95             Bool => panic!("literal token contains `Lit::Bool`"),
96             Byte => "byte",
97             Char => "char",
98             Integer => "integer",
99             Float => "float",
100             Str | StrRaw(..) => "string",
101             ByteStr | ByteStrRaw(..) => "byte string",
102             Err => "error",
103         }
104     }
105
106     crate fn may_have_suffix(self) -> bool {
107         match self {
108             Integer | Float | Err => true,
109             _ => false,
110         }
111     }
112 }
113
114 impl Lit {
115     pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Lit {
116         Lit { kind, symbol, suffix }
117     }
118 }
119
120 pub(crate) fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool {
121     let ident_token: Token = Ident(ident, is_raw);
122
123     !ident_token.is_reserved_ident() ||
124     ident_token.is_path_segment_keyword() ||
125     [
126         kw::Async,
127
128         // FIXME: remove when `await!(..)` syntax is removed
129         // https://github.com/rust-lang/rust/issues/60610
130         kw::Await,
131
132         kw::Do,
133         kw::Box,
134         kw::Break,
135         kw::Continue,
136         kw::False,
137         kw::For,
138         kw::If,
139         kw::Loop,
140         kw::Match,
141         kw::Move,
142         kw::Return,
143         kw::True,
144         kw::Unsafe,
145         kw::While,
146         kw::Yield,
147         kw::Static,
148     ].contains(&ident.name)
149 }
150
151 fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool {
152     let ident_token: Token = Ident(ident, is_raw);
153
154     !ident_token.is_reserved_ident() ||
155     ident_token.is_path_segment_keyword() ||
156     [
157         kw::Underscore,
158         kw::For,
159         kw::Impl,
160         kw::Fn,
161         kw::Unsafe,
162         kw::Extern,
163         kw::Typeof,
164         kw::Dyn,
165     ].contains(&ident.name)
166 }
167
168 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
169 pub enum Token {
170     /* Expression-operator symbols. */
171     Eq,
172     Lt,
173     Le,
174     EqEq,
175     Ne,
176     Ge,
177     Gt,
178     AndAnd,
179     OrOr,
180     Not,
181     Tilde,
182     BinOp(BinOpToken),
183     BinOpEq(BinOpToken),
184
185     /* Structural symbols */
186     At,
187     Dot,
188     DotDot,
189     DotDotDot,
190     DotDotEq,
191     Comma,
192     Semi,
193     Colon,
194     ModSep,
195     RArrow,
196     LArrow,
197     FatArrow,
198     Pound,
199     Dollar,
200     Question,
201     /// Used by proc macros for representing lifetimes, not generated by lexer right now.
202     SingleQuote,
203     /// An opening delimiter (e.g., `{`).
204     OpenDelim(DelimToken),
205     /// A closing delimiter (e.g., `}`).
206     CloseDelim(DelimToken),
207
208     /* Literals */
209     Literal(Lit),
210
211     /* Name components */
212     Ident(ast::Ident, /* is_raw */ bool),
213     Lifetime(ast::Ident),
214
215     Interpolated(Lrc<Nonterminal>),
216
217     // Can be expanded into several tokens.
218     /// A doc comment.
219     DocComment(ast::Name),
220
221     // Junk. These carry no data because we don't really care about the data
222     // they *would* carry, and don't really want to allocate a new ident for
223     // them. Instead, users could extract that from the associated span.
224
225     /// Whitespace.
226     Whitespace,
227     /// A comment.
228     Comment,
229     Shebang(ast::Name),
230
231     Eof,
232 }
233
234 // `Token` is used a lot. Make sure it doesn't unintentionally get bigger.
235 #[cfg(target_arch = "x86_64")]
236 static_assert_size!(Token, 16);
237
238 impl Token {
239     /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
240     pub fn from_ast_ident(ident: ast::Ident) -> Token {
241         Ident(ident, ident.is_raw_guess())
242     }
243
244     crate fn is_like_plus(&self) -> bool {
245         match *self {
246             BinOp(Plus) | BinOpEq(Plus) => true,
247             _ => false,
248         }
249     }
250
251     /// Returns `true` if the token can appear at the start of an expression.
252     crate fn can_begin_expr(&self) -> bool {
253         match *self {
254             Ident(ident, is_raw)              =>
255                 ident_can_begin_expr(ident, is_raw), // value name or keyword
256             OpenDelim(..)                     | // tuple, array or block
257             Literal(..)                       | // literal
258             Not                               | // operator not
259             BinOp(Minus)                      | // unary minus
260             BinOp(Star)                       | // dereference
261             BinOp(Or) | OrOr                  | // closure
262             BinOp(And)                        | // reference
263             AndAnd                            | // double reference
264             // DotDotDot is no longer supported, but we need some way to display the error
265             DotDot | DotDotDot | DotDotEq     | // range notation
266             Lt | BinOp(Shl)                   | // associated path
267             ModSep                            | // global path
268             Lifetime(..)                      | // labeled loop
269             Pound                             => true, // expression attributes
270             Interpolated(ref nt) => match **nt {
271                 NtLiteral(..) |
272                 NtIdent(..)   |
273                 NtExpr(..)    |
274                 NtBlock(..)   |
275                 NtPath(..)    |
276                 NtLifetime(..) => true,
277                 _ => false,
278             },
279             _ => false,
280         }
281     }
282
283     /// Returns `true` if the token can appear at the start of a type.
284     crate fn can_begin_type(&self) -> bool {
285         match *self {
286             Ident(ident, is_raw)        =>
287                 ident_can_begin_type(ident, is_raw), // type name or keyword
288             OpenDelim(Paren)            | // tuple
289             OpenDelim(Bracket)          | // array
290             Not                         | // never
291             BinOp(Star)                 | // raw pointer
292             BinOp(And)                  | // reference
293             AndAnd                      | // double reference
294             Question                    | // maybe bound in trait object
295             Lifetime(..)                | // lifetime bound in trait object
296             Lt | BinOp(Shl)             | // associated path
297             ModSep                      => true, // global path
298             Interpolated(ref nt) => match **nt {
299                 NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true,
300                 _ => false,
301             },
302             _ => false,
303         }
304     }
305
306     /// Returns `true` if the token can appear at the start of a const param.
307     pub fn can_begin_const_arg(&self) -> bool {
308         match self {
309             OpenDelim(Brace) => true,
310             Interpolated(ref nt) => match **nt {
311                 NtExpr(..) => true,
312                 NtBlock(..) => true,
313                 NtLiteral(..) => true,
314                 _ => false,
315             }
316             _ => self.can_begin_literal_or_bool(),
317         }
318     }
319
320     /// Returns `true` if the token can appear at the start of a generic bound.
321     crate fn can_begin_bound(&self) -> bool {
322         self.is_path_start() || self.is_lifetime() || self.is_keyword(kw::For) ||
323         self == &Question || self == &OpenDelim(Paren)
324     }
325
326     pub fn lit(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Token {
327         Literal(Lit::new(kind, symbol, suffix))
328     }
329
330     /// Returns `true` if the token is any literal
331     crate fn is_lit(&self) -> bool {
332         match *self {
333             Literal(..) => true,
334             _           => false,
335         }
336     }
337
338     crate fn expect_lit(&self) -> Lit {
339         match *self {
340             Literal(lit) => lit,
341             _=> panic!("`expect_lit` called on non-literal"),
342         }
343     }
344
345     /// Returns `true` if the token is any literal, a minus (which can prefix a literal,
346     /// for example a '-42', or one of the boolean idents).
347     crate fn can_begin_literal_or_bool(&self) -> bool {
348         match *self {
349             Literal(..)  => true,
350             BinOp(Minus) => true,
351             Ident(ident, false) if ident.name == kw::True => true,
352             Ident(ident, false) if ident.name == kw::False => true,
353             Interpolated(ref nt) => match **nt {
354                 NtLiteral(..) => true,
355                 _             => false,
356             },
357             _            => false,
358         }
359     }
360
361     /// Returns an identifier if this token is an identifier.
362     pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> {
363         match *self {
364             Ident(ident, is_raw) => Some((ident, is_raw)),
365             Interpolated(ref nt) => match **nt {
366                 NtIdent(ident, is_raw) => Some((ident, is_raw)),
367                 _ => None,
368             },
369             _ => None,
370         }
371     }
372     /// Returns a lifetime identifier if this token is a lifetime.
373     pub fn lifetime(&self) -> Option<ast::Ident> {
374         match *self {
375             Lifetime(ident) => Some(ident),
376             Interpolated(ref nt) => match **nt {
377                 NtLifetime(ident) => Some(ident),
378                 _ => None,
379             },
380             _ => None,
381         }
382     }
383     /// Returns `true` if the token is an identifier.
384     pub fn is_ident(&self) -> bool {
385         self.ident().is_some()
386     }
387     /// Returns `true` if the token is a lifetime.
388     crate fn is_lifetime(&self) -> bool {
389         self.lifetime().is_some()
390     }
391
392     /// Returns `true` if the token is a identifier whose name is the given
393     /// string slice.
394     crate fn is_ident_named(&self, name: &str) -> bool {
395         match self.ident() {
396             Some((ident, _)) => ident.as_str() == name,
397             None => false
398         }
399     }
400
401     /// Returns `true` if the token is an interpolated path.
402     fn is_path(&self) -> bool {
403         if let Interpolated(ref nt) = *self {
404             if let NtPath(..) = **nt {
405                 return true;
406             }
407         }
408         false
409     }
410
411     /// Returns `true` if the token is either the `mut` or `const` keyword.
412     crate fn is_mutability(&self) -> bool {
413         self.is_keyword(kw::Mut) ||
414         self.is_keyword(kw::Const)
415     }
416
417     crate fn is_qpath_start(&self) -> bool {
418         self == &Lt || self == &BinOp(Shl)
419     }
420
421     crate fn is_path_start(&self) -> bool {
422         self == &ModSep || self.is_qpath_start() || self.is_path() ||
423         self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
424     }
425
426     /// Returns `true` if the token is a given keyword, `kw`.
427     pub fn is_keyword(&self, kw: Symbol) -> bool {
428         self.ident().map(|(ident, is_raw)| ident.name == kw && !is_raw).unwrap_or(false)
429     }
430
431     pub fn is_path_segment_keyword(&self) -> bool {
432         match self.ident() {
433             Some((id, false)) => id.is_path_segment_keyword(),
434             _ => false,
435         }
436     }
437
438     // Returns true for reserved identifiers used internally for elided lifetimes,
439     // unnamed method parameters, crate root module, error recovery etc.
440     pub fn is_special_ident(&self) -> bool {
441         match self.ident() {
442             Some((id, false)) => id.is_special(),
443             _ => false,
444         }
445     }
446
447     /// Returns `true` if the token is a keyword used in the language.
448     crate fn is_used_keyword(&self) -> bool {
449         match self.ident() {
450             Some((id, false)) => id.is_used_keyword(),
451             _ => false,
452         }
453     }
454
455     /// Returns `true` if the token is a keyword reserved for possible future use.
456     crate fn is_unused_keyword(&self) -> bool {
457         match self.ident() {
458             Some((id, false)) => id.is_unused_keyword(),
459             _ => false,
460         }
461     }
462
463     /// Returns `true` if the token is either a special identifier or a keyword.
464     pub fn is_reserved_ident(&self) -> bool {
465         match self.ident() {
466             Some((id, false)) => id.is_reserved(),
467             _ => false,
468         }
469     }
470
471     crate fn glue(self, joint: Token) -> Option<Token> {
472         Some(match self {
473             Eq => match joint {
474                 Eq => EqEq,
475                 Gt => FatArrow,
476                 _ => return None,
477             },
478             Lt => match joint {
479                 Eq => Le,
480                 Lt => BinOp(Shl),
481                 Le => BinOpEq(Shl),
482                 BinOp(Minus) => LArrow,
483                 _ => return None,
484             },
485             Gt => match joint {
486                 Eq => Ge,
487                 Gt => BinOp(Shr),
488                 Ge => BinOpEq(Shr),
489                 _ => return None,
490             },
491             Not => match joint {
492                 Eq => Ne,
493                 _ => return None,
494             },
495             BinOp(op) => match joint {
496                 Eq => BinOpEq(op),
497                 BinOp(And) if op == And => AndAnd,
498                 BinOp(Or) if op == Or => OrOr,
499                 Gt if op == Minus => RArrow,
500                 _ => return None,
501             },
502             Dot => match joint {
503                 Dot => DotDot,
504                 DotDot => DotDotDot,
505                 _ => return None,
506             },
507             DotDot => match joint {
508                 Dot => DotDotDot,
509                 Eq => DotDotEq,
510                 _ => return None,
511             },
512             Colon => match joint {
513                 Colon => ModSep,
514                 _ => return None,
515             },
516             SingleQuote => match joint {
517                 Ident(ident, false) => {
518                     let name = Symbol::intern(&format!("'{}", ident));
519                     Lifetime(symbol::Ident {
520                         name,
521                         span: ident.span,
522                     })
523                 }
524                 _ => return None,
525             },
526
527             Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot |
528             DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
529             Question | OpenDelim(..) | CloseDelim(..) |
530             Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
531             Whitespace | Comment | Shebang(..) | Eof => return None,
532         })
533     }
534
535     /// Returns tokens that are likely to be typed accidentally instead of the current token.
536     /// Enables better error recovery when the wrong token is found.
537     crate fn similar_tokens(&self) -> Option<Vec<Token>> {
538         match *self {
539             Comma => Some(vec![Dot, Lt, Semi]),
540             Semi => Some(vec![Colon, Comma]),
541             _ => None
542         }
543     }
544
545     // See comments in `Nonterminal::to_tokenstream` for why we care about
546     // *probably* equal here rather than actual equality
547     crate fn probably_equal_for_proc_macro(&self, other: &Token) -> bool {
548         if mem::discriminant(self) != mem::discriminant(other) {
549             return false
550         }
551         match (self, other) {
552             (&Eq, &Eq) |
553             (&Lt, &Lt) |
554             (&Le, &Le) |
555             (&EqEq, &EqEq) |
556             (&Ne, &Ne) |
557             (&Ge, &Ge) |
558             (&Gt, &Gt) |
559             (&AndAnd, &AndAnd) |
560             (&OrOr, &OrOr) |
561             (&Not, &Not) |
562             (&Tilde, &Tilde) |
563             (&At, &At) |
564             (&Dot, &Dot) |
565             (&DotDot, &DotDot) |
566             (&DotDotDot, &DotDotDot) |
567             (&DotDotEq, &DotDotEq) |
568             (&Comma, &Comma) |
569             (&Semi, &Semi) |
570             (&Colon, &Colon) |
571             (&ModSep, &ModSep) |
572             (&RArrow, &RArrow) |
573             (&LArrow, &LArrow) |
574             (&FatArrow, &FatArrow) |
575             (&Pound, &Pound) |
576             (&Dollar, &Dollar) |
577             (&Question, &Question) |
578             (&Whitespace, &Whitespace) |
579             (&Comment, &Comment) |
580             (&Eof, &Eof) => true,
581
582             (&BinOp(a), &BinOp(b)) |
583             (&BinOpEq(a), &BinOpEq(b)) => a == b,
584
585             (&OpenDelim(a), &OpenDelim(b)) |
586             (&CloseDelim(a), &CloseDelim(b)) => a == b,
587
588             (&DocComment(a), &DocComment(b)) |
589             (&Shebang(a), &Shebang(b)) => a == b,
590
591             (&Literal(a), &Literal(b)) => a == b,
592
593             (&Lifetime(a), &Lifetime(b)) => a.name == b.name,
594             (&Ident(a, b), &Ident(c, d)) => b == d && (a.name == c.name ||
595                                                        a.name == kw::DollarCrate ||
596                                                        c.name == kw::DollarCrate),
597
598             (&Interpolated(_), &Interpolated(_)) => false,
599
600             _ => panic!("forgot to add a token?"),
601         }
602     }
603 }
604
605 #[derive(Clone, RustcEncodable, RustcDecodable)]
606 /// For interpolation during macro expansion.
607 pub enum Nonterminal {
608     NtItem(P<ast::Item>),
609     NtBlock(P<ast::Block>),
610     NtStmt(ast::Stmt),
611     NtPat(P<ast::Pat>),
612     NtExpr(P<ast::Expr>),
613     NtTy(P<ast::Ty>),
614     NtIdent(ast::Ident, /* is_raw */ bool),
615     NtLifetime(ast::Ident),
616     NtLiteral(P<ast::Expr>),
617     /// Stuff inside brackets for attributes
618     NtMeta(ast::MetaItem),
619     NtPath(ast::Path),
620     NtVis(ast::Visibility),
621     NtTT(TokenTree),
622     // Used only for passing items to proc macro attributes (they are not
623     // strictly necessary for that, `Annotatable` can be converted into
624     // tokens directly, but doing that naively regresses pretty-printing).
625     NtTraitItem(ast::TraitItem),
626     NtImplItem(ast::ImplItem),
627     NtForeignItem(ast::ForeignItem),
628 }
629
630 impl PartialEq for Nonterminal {
631     fn eq(&self, rhs: &Self) -> bool {
632         match (self, rhs) {
633             (NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) =>
634                 ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs,
635             (NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs,
636             (NtTT(tt_lhs), NtTT(tt_rhs)) => tt_lhs == tt_rhs,
637             // FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them
638             // correctly based on data from AST. This will prevent them from matching each other
639             // in macros. The comparison will become possible only when each nonterminal has an
640             // attached token stream from which it was parsed.
641             _ => false,
642         }
643     }
644 }
645
646 impl fmt::Debug for Nonterminal {
647     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648         match *self {
649             NtItem(..) => f.pad("NtItem(..)"),
650             NtBlock(..) => f.pad("NtBlock(..)"),
651             NtStmt(..) => f.pad("NtStmt(..)"),
652             NtPat(..) => f.pad("NtPat(..)"),
653             NtExpr(..) => f.pad("NtExpr(..)"),
654             NtTy(..) => f.pad("NtTy(..)"),
655             NtIdent(..) => f.pad("NtIdent(..)"),
656             NtLiteral(..) => f.pad("NtLiteral(..)"),
657             NtMeta(..) => f.pad("NtMeta(..)"),
658             NtPath(..) => f.pad("NtPath(..)"),
659             NtTT(..) => f.pad("NtTT(..)"),
660             NtImplItem(..) => f.pad("NtImplItem(..)"),
661             NtTraitItem(..) => f.pad("NtTraitItem(..)"),
662             NtForeignItem(..) => f.pad("NtForeignItem(..)"),
663             NtVis(..) => f.pad("NtVis(..)"),
664             NtLifetime(..) => f.pad("NtLifetime(..)"),
665         }
666     }
667 }
668
669 impl Nonterminal {
670     pub fn to_tokenstream(&self, sess: &ParseSess, span: Span) -> TokenStream {
671         // A `Nonterminal` is often a parsed AST item. At this point we now
672         // need to convert the parsed AST to an actual token stream, e.g.
673         // un-parse it basically.
674         //
675         // Unfortunately there's not really a great way to do that in a
676         // guaranteed lossless fashion right now. The fallback here is to just
677         // stringify the AST node and reparse it, but this loses all span
678         // information.
679         //
680         // As a result, some AST nodes are annotated with the token stream they
681         // came from. Here we attempt to extract these lossless token streams
682         // before we fall back to the stringification.
683         let tokens = match *self {
684             Nonterminal::NtItem(ref item) => {
685                 prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
686             }
687             Nonterminal::NtTraitItem(ref item) => {
688                 prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
689             }
690             Nonterminal::NtImplItem(ref item) => {
691                 prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span)
692             }
693             Nonterminal::NtIdent(ident, is_raw) => {
694                 let token = Token::Ident(ident, is_raw);
695                 Some(TokenTree::Token(ident.span, token).into())
696             }
697             Nonterminal::NtLifetime(ident) => {
698                 let token = Token::Lifetime(ident);
699                 Some(TokenTree::Token(ident.span, token).into())
700             }
701             Nonterminal::NtTT(ref tt) => {
702                 Some(tt.clone().into())
703             }
704             _ => None,
705         };
706
707         // FIXME(#43081): Avoid this pretty-print + reparse hack
708         let source = pprust::nonterminal_to_string(self);
709         let filename = FileName::macro_expansion_source_code(&source);
710         let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span));
711
712         // During early phases of the compiler the AST could get modified
713         // directly (e.g., attributes added or removed) and the internal cache
714         // of tokens my not be invalidated or updated. Consequently if the
715         // "lossless" token stream disagrees with our actual stringification
716         // (which has historically been much more battle-tested) then we go
717         // with the lossy stream anyway (losing span information).
718         //
719         // Note that the comparison isn't `==` here to avoid comparing spans,
720         // but it *also* is a "probable" equality which is a pretty weird
721         // definition. We mostly want to catch actual changes to the AST
722         // like a `#[cfg]` being processed or some weird `macro_rules!`
723         // expansion.
724         //
725         // What we *don't* want to catch is the fact that a user-defined
726         // literal like `0xf` is stringified as `15`, causing the cached token
727         // stream to not be literal `==` token-wise (ignoring spans) to the
728         // token stream we got from stringification.
729         //
730         // Instead the "probably equal" check here is "does each token
731         // recursively have the same discriminant?" We basically don't look at
732         // the token values here and assume that such fine grained token stream
733         // modifications, including adding/removing typically non-semantic
734         // tokens such as extra braces and commas, don't happen.
735         if let Some(tokens) = tokens {
736             if tokens.probably_equal_for_proc_macro(&tokens_for_real) {
737                 return tokens
738             }
739             info!("cached tokens found, but they're not \"probably equal\", \
740                    going with stringified version");
741         }
742         return tokens_for_real
743     }
744 }
745
746 crate fn is_op(tok: &Token) -> bool {
747     match *tok {
748         OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
749         Ident(..) | Lifetime(..) | Interpolated(..) |
750         Whitespace | Comment | Shebang(..) | Eof => false,
751         _ => true,
752     }
753 }
754
755 fn prepend_attrs(sess: &ParseSess,
756                  attrs: &[ast::Attribute],
757                  tokens: Option<&tokenstream::TokenStream>,
758                  span: syntax_pos::Span)
759     -> Option<tokenstream::TokenStream>
760 {
761     let tokens = tokens?;
762     if attrs.len() == 0 {
763         return Some(tokens.clone())
764     }
765     let mut builder = tokenstream::TokenStreamBuilder::new();
766     for attr in attrs {
767         assert_eq!(attr.style, ast::AttrStyle::Outer,
768                    "inner attributes should prevent cached tokens from existing");
769
770         let source = pprust::attr_to_string(attr);
771         let macro_filename = FileName::macro_expansion_source_code(&source);
772         if attr.is_sugared_doc {
773             let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
774             builder.push(stream);
775             continue
776         }
777
778         // synthesize # [ $path $tokens ] manually here
779         let mut brackets = tokenstream::TokenStreamBuilder::new();
780
781         // For simple paths, push the identifier directly
782         if attr.path.segments.len() == 1 && attr.path.segments[0].args.is_none() {
783             let ident = attr.path.segments[0].ident;
784             let token = Ident(ident, ident.as_str().starts_with("r#"));
785             brackets.push(tokenstream::TokenTree::Token(ident.span, token));
786
787         // ... and for more complicated paths, fall back to a reparse hack that
788         // should eventually be removed.
789         } else {
790             let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
791             brackets.push(stream);
792         }
793
794         brackets.push(attr.tokens.clone());
795
796         // The span we list here for `#` and for `[ ... ]` are both wrong in
797         // that it encompasses more than each token, but it hopefully is "good
798         // enough" for now at least.
799         builder.push(tokenstream::TokenTree::Token(attr.span, Pound));
800         let delim_span = DelimSpan::from_single(attr.span);
801         builder.push(tokenstream::TokenTree::Delimited(
802             delim_span, DelimToken::Bracket, brackets.build().into()));
803     }
804     builder.push(tokens.clone());
805     Some(builder.build())
806 }