]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/token.rs
concerning well-formed suggestions for unused shorthand field patterns
[rust.git] / src / libsyntax / parse / token.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 pub use self::BinOpToken::*;
12 pub use self::Nonterminal::*;
13 pub use self::DelimToken::*;
14 pub use self::Lit::*;
15 pub use self::Token::*;
16
17 use ast::{self};
18 use parse::ParseSess;
19 use print::pprust;
20 use ptr::P;
21 use serialize::{Decodable, Decoder, Encodable, Encoder};
22 use symbol::keywords;
23 use syntax::parse::parse_stream_from_source_str;
24 use syntax_pos::{self, Span, FileName};
25 use tokenstream::{TokenStream, TokenTree};
26 use tokenstream;
27
28 use std::cell::Cell;
29 use std::{cmp, fmt};
30 use std::rc::Rc;
31
32 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
33 pub enum BinOpToken {
34     Plus,
35     Minus,
36     Star,
37     Slash,
38     Percent,
39     Caret,
40     And,
41     Or,
42     Shl,
43     Shr,
44 }
45
46 /// A delimiter token
47 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
48 pub enum DelimToken {
49     /// A round parenthesis: `(` or `)`
50     Paren,
51     /// A square bracket: `[` or `]`
52     Bracket,
53     /// A curly brace: `{` or `}`
54     Brace,
55     /// An empty delimiter
56     NoDelim,
57 }
58
59 impl DelimToken {
60     pub fn len(self) -> usize {
61         if self == NoDelim { 0 } else { 1 }
62     }
63
64     pub fn is_empty(self) -> bool {
65         self == NoDelim
66     }
67 }
68
69 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
70 pub enum Lit {
71     Byte(ast::Name),
72     Char(ast::Name),
73     Integer(ast::Name),
74     Float(ast::Name),
75     Str_(ast::Name),
76     StrRaw(ast::Name, usize), /* raw str delimited by n hash symbols */
77     ByteStr(ast::Name),
78     ByteStrRaw(ast::Name, usize), /* raw byte str delimited by n hash symbols */
79 }
80
81 impl Lit {
82     pub fn short_name(&self) -> &'static str {
83         match *self {
84             Byte(_) => "byte",
85             Char(_) => "char",
86             Integer(_) => "integer",
87             Float(_) => "float",
88             Str_(_) | StrRaw(..) => "string",
89             ByteStr(_) | ByteStrRaw(..) => "byte string"
90         }
91     }
92 }
93
94 fn ident_can_begin_expr(ident: ast::Ident) -> bool {
95     let ident_token: Token = Ident(ident);
96
97     !ident_token.is_reserved_ident() ||
98     ident_token.is_path_segment_keyword() ||
99     [
100         keywords::Do.name(),
101         keywords::Box.name(),
102         keywords::Break.name(),
103         keywords::Continue.name(),
104         keywords::False.name(),
105         keywords::For.name(),
106         keywords::If.name(),
107         keywords::Loop.name(),
108         keywords::Match.name(),
109         keywords::Move.name(),
110         keywords::Return.name(),
111         keywords::True.name(),
112         keywords::Unsafe.name(),
113         keywords::While.name(),
114         keywords::Yield.name(),
115     ].contains(&ident.name)
116 }
117
118 fn ident_can_begin_type(ident: ast::Ident) -> bool {
119     let ident_token: Token = Ident(ident);
120
121     !ident_token.is_reserved_ident() ||
122     ident_token.is_path_segment_keyword() ||
123     [
124         keywords::For.name(),
125         keywords::Impl.name(),
126         keywords::Fn.name(),
127         keywords::Unsafe.name(),
128         keywords::Extern.name(),
129         keywords::Typeof.name(),
130     ].contains(&ident.name)
131 }
132
133 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
134 pub enum Token {
135     /* Expression-operator symbols. */
136     Eq,
137     Lt,
138     Le,
139     EqEq,
140     Ne,
141     Ge,
142     Gt,
143     AndAnd,
144     OrOr,
145     Not,
146     Tilde,
147     BinOp(BinOpToken),
148     BinOpEq(BinOpToken),
149
150     /* Structural symbols */
151     At,
152     Dot,
153     DotDot,
154     DotDotDot,
155     DotDotEq,
156     DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
157     Comma,
158     Semi,
159     Colon,
160     ModSep,
161     RArrow,
162     LArrow,
163     FatArrow,
164     Pound,
165     Dollar,
166     Question,
167     /// An opening delimiter, eg. `{`
168     OpenDelim(DelimToken),
169     /// A closing delimiter, eg. `}`
170     CloseDelim(DelimToken),
171
172     /* Literals */
173     Literal(Lit, Option<ast::Name>),
174
175     /* Name components */
176     Ident(ast::Ident),
177     Underscore,
178     Lifetime(ast::Ident),
179
180     // The `LazyTokenStream` is a pure function of the `Nonterminal`,
181     // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc.
182     Interpolated(Rc<(Nonterminal, LazyTokenStream)>),
183     // Can be expanded into several tokens.
184     /// Doc comment
185     DocComment(ast::Name),
186
187     // Junk. These carry no data because we don't really care about the data
188     // they *would* carry, and don't really want to allocate a new ident for
189     // them. Instead, users could extract that from the associated span.
190
191     /// Whitespace
192     Whitespace,
193     /// Comment
194     Comment,
195     Shebang(ast::Name),
196
197     Eof,
198 }
199
200 impl Token {
201     pub fn interpolated(nt: Nonterminal) -> Token {
202         Token::Interpolated(Rc::new((nt, LazyTokenStream::new())))
203     }
204
205     /// Returns `true` if the token starts with '>'.
206     pub fn is_like_gt(&self) -> bool {
207         match *self {
208             BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true,
209             _ => false,
210         }
211     }
212
213     /// Returns `true` if the token can appear at the start of an expression.
214     pub fn can_begin_expr(&self) -> bool {
215         match *self {
216             Ident(ident)                => ident_can_begin_expr(ident), // value name or keyword
217             OpenDelim(..)                     | // tuple, array or block
218             Literal(..)                       | // literal
219             Not                               | // operator not
220             BinOp(Minus)                      | // unary minus
221             BinOp(Star)                       | // dereference
222             BinOp(Or) | OrOr                  | // closure
223             BinOp(And)                        | // reference
224             AndAnd                            | // double reference
225             // DotDotDot is no longer supported, but we need some way to display the error
226             DotDot | DotDotDot | DotDotEq     | // range notation
227             Lt | BinOp(Shl)                   | // associated path
228             ModSep                            | // global path
229             Pound                             => true, // expression attributes
230             Interpolated(ref nt) => match nt.0 {
231                 NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
232                 _ => false,
233             },
234             _ => false,
235         }
236     }
237
238     /// Returns `true` if the token can appear at the start of a type.
239     pub fn can_begin_type(&self) -> bool {
240         match *self {
241             Ident(ident)                => ident_can_begin_type(ident), // type name or keyword
242             OpenDelim(Paren)            | // tuple
243             OpenDelim(Bracket)          | // array
244             Underscore                  | // placeholder
245             Not                         | // never
246             BinOp(Star)                 | // raw pointer
247             BinOp(And)                  | // reference
248             AndAnd                      | // double reference
249             Question                    | // maybe bound in trait object
250             Lifetime(..)                | // lifetime bound in trait object
251             Lt | BinOp(Shl)             | // associated path
252             ModSep                      => true, // global path
253             Interpolated(ref nt) => match nt.0 {
254                 NtIdent(..) | NtTy(..) | NtPath(..) | NtLifetime(..) => true,
255                 _ => false,
256             },
257             _ => false,
258         }
259     }
260
261     /// Returns `true` if the token can appear at the start of a generic bound.
262     pub fn can_begin_bound(&self) -> bool {
263         self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
264         self == &Question || self == &OpenDelim(Paren)
265     }
266
267     /// Returns `true` if the token is any literal
268     pub fn is_lit(&self) -> bool {
269         match *self {
270             Literal(..) => true,
271             _           => false,
272         }
273     }
274
275     pub fn ident(&self) -> Option<ast::Ident> {
276         match *self {
277             Ident(ident) => Some(ident),
278             Interpolated(ref nt) => match nt.0 {
279                 NtIdent(ident) => Some(ident.node),
280                 _ => None,
281             },
282             _ => None,
283         }
284     }
285
286     /// Returns `true` if the token is an identifier.
287     pub fn is_ident(&self) -> bool {
288         self.ident().is_some()
289     }
290
291     /// Returns `true` if the token is a documentation comment.
292     pub fn is_doc_comment(&self) -> bool {
293         match *self {
294             DocComment(..)   => true,
295             _                => false,
296         }
297     }
298
299     /// Returns `true` if the token is interpolated.
300     pub fn is_interpolated(&self) -> bool {
301         match *self {
302             Interpolated(..) => true,
303             _                => false,
304         }
305     }
306
307     /// Returns `true` if the token is an interpolated path.
308     pub fn is_path(&self) -> bool {
309         if let Interpolated(ref nt) = *self {
310             if let NtPath(..) = nt.0 {
311                 return true;
312             }
313         }
314         false
315     }
316
317     /// Returns a lifetime with the span and a dummy id if it is a lifetime,
318     /// or the original lifetime if it is an interpolated lifetime, ignoring
319     /// the span.
320     pub fn lifetime(&self, span: Span) -> Option<ast::Lifetime> {
321         match *self {
322             Lifetime(ident) =>
323                 Some(ast::Lifetime { ident: ident, span: span, id: ast::DUMMY_NODE_ID }),
324             Interpolated(ref nt) => match nt.0 {
325                 NtLifetime(lifetime) => Some(lifetime),
326                 _ => None,
327             },
328             _ => None,
329         }
330     }
331
332     /// Returns `true` if the token is a lifetime.
333     pub fn is_lifetime(&self) -> bool {
334         self.lifetime(syntax_pos::DUMMY_SP).is_some()
335     }
336
337     /// Returns `true` if the token is either the `mut` or `const` keyword.
338     pub fn is_mutability(&self) -> bool {
339         self.is_keyword(keywords::Mut) ||
340         self.is_keyword(keywords::Const)
341     }
342
343     pub fn is_qpath_start(&self) -> bool {
344         self == &Lt || self == &BinOp(Shl)
345     }
346
347     pub fn is_path_start(&self) -> bool {
348         self == &ModSep || self.is_qpath_start() || self.is_path() ||
349         self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
350     }
351
352     /// Returns `true` if the token is a given keyword, `kw`.
353     pub fn is_keyword(&self, kw: keywords::Keyword) -> bool {
354         self.ident().map(|ident| ident.name == kw.name()).unwrap_or(false)
355     }
356
357     pub fn is_path_segment_keyword(&self) -> bool {
358         match self.ident() {
359             Some(id) => id.name == keywords::Super.name() ||
360                         id.name == keywords::SelfValue.name() ||
361                         id.name == keywords::SelfType.name() ||
362                         id.name == keywords::Extern.name() ||
363                         id.name == keywords::Crate.name() ||
364                         id.name == keywords::DollarCrate.name(),
365             None => false,
366         }
367     }
368
369     // Returns true for reserved identifiers used internally for elided lifetimes,
370     // unnamed method parameters, crate root module, error recovery etc.
371     pub fn is_special_ident(&self) -> bool {
372         match self.ident() {
373             Some(id) => id.name <= keywords::DollarCrate.name(),
374             _ => false,
375         }
376     }
377
378     /// Returns `true` if the token is a keyword used in the language.
379     pub fn is_used_keyword(&self) -> bool {
380         match self.ident() {
381             Some(id) => id.name >= keywords::As.name() && id.name <= keywords::While.name(),
382             _ => false,
383         }
384     }
385
386     /// Returns `true` if the token is a keyword reserved for possible future use.
387     pub fn is_unused_keyword(&self) -> bool {
388         match self.ident() {
389             Some(id) => id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name(),
390             _ => false,
391         }
392     }
393
394     pub fn glue(self, joint: Token) -> Option<Token> {
395         Some(match self {
396             Eq => match joint {
397                 Eq => EqEq,
398                 Gt => FatArrow,
399                 _ => return None,
400             },
401             Lt => match joint {
402                 Eq => Le,
403                 Lt => BinOp(Shl),
404                 Le => BinOpEq(Shl),
405                 BinOp(Minus) => LArrow,
406                 _ => return None,
407             },
408             Gt => match joint {
409                 Eq => Ge,
410                 Gt => BinOp(Shr),
411                 Ge => BinOpEq(Shr),
412                 _ => return None,
413             },
414             Not => match joint {
415                 Eq => Ne,
416                 _ => return None,
417             },
418             BinOp(op) => match joint {
419                 Eq => BinOpEq(op),
420                 BinOp(And) if op == And => AndAnd,
421                 BinOp(Or) if op == Or => OrOr,
422                 Gt if op == Minus => RArrow,
423                 _ => return None,
424             },
425             Dot => match joint {
426                 Dot => DotDot,
427                 DotDot => DotDotDot,
428                 DotEq => DotDotEq,
429                 _ => return None,
430             },
431             DotDot => match joint {
432                 Dot => DotDotDot,
433                 Eq => DotDotEq,
434                 _ => return None,
435             },
436             Colon => match joint {
437                 Colon => ModSep,
438                 _ => return None,
439             },
440
441             Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
442             DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
443             Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None,
444
445             Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
446             Whitespace | Comment | Shebang(..) | Eof => return None,
447         })
448     }
449
450     /// Returns tokens that are likely to be typed accidentally instead of the current token.
451     /// Enables better error recovery when the wrong token is found.
452     pub fn similar_tokens(&self) -> Option<Vec<Token>> {
453         match *self {
454             Comma => Some(vec![Dot, Lt]),
455             Semi => Some(vec![Colon]),
456             _ => None
457         }
458     }
459
460     /// Returns `true` if the token is either a special identifier or a keyword.
461     pub fn is_reserved_ident(&self) -> bool {
462         self.is_special_ident() || self.is_used_keyword() || self.is_unused_keyword()
463     }
464
465     pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
466         -> TokenStream
467     {
468         let nt = match *self {
469             Token::Interpolated(ref nt) => nt,
470             _ => panic!("only works on interpolated tokens"),
471         };
472
473         // An `Interpolated` token means that we have a `Nonterminal`
474         // which is often a parsed AST item. At this point we now need
475         // to convert the parsed AST to an actual token stream, e.g.
476         // un-parse it basically.
477         //
478         // Unfortunately there's not really a great way to do that in a
479         // guaranteed lossless fashion right now. The fallback here is
480         // to just stringify the AST node and reparse it, but this loses
481         // all span information.
482         //
483         // As a result, some AST nodes are annotated with the token
484         // stream they came from. Attempt to extract these lossless
485         // token streams before we fall back to the stringification.
486         let mut tokens = None;
487
488         match nt.0 {
489             Nonterminal::NtItem(ref item) => {
490                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
491             }
492             Nonterminal::NtTraitItem(ref item) => {
493                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
494             }
495             Nonterminal::NtImplItem(ref item) => {
496                 tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
497             }
498             Nonterminal::NtIdent(ident) => {
499                 let token = Token::Ident(ident.node);
500                 tokens = Some(TokenTree::Token(ident.span, token).into());
501             }
502             Nonterminal::NtLifetime(lifetime) => {
503                 let token = Token::Lifetime(lifetime.ident);
504                 tokens = Some(TokenTree::Token(lifetime.span, token).into());
505             }
506             Nonterminal::NtTT(ref tt) => {
507                 tokens = Some(tt.clone().into());
508             }
509             _ => {}
510         }
511
512         tokens.unwrap_or_else(|| {
513             nt.1.force(|| {
514                 // FIXME(jseyfried): Avoid this pretty-print + reparse hack
515                 let source = pprust::token_to_string(self);
516                 parse_stream_from_source_str(FileName::MacroExpansion, source, sess, Some(span))
517             })
518         })
519     }
520 }
521
522 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash)]
523 /// For interpolation during macro expansion.
524 pub enum Nonterminal {
525     NtItem(P<ast::Item>),
526     NtBlock(P<ast::Block>),
527     NtStmt(ast::Stmt),
528     NtPat(P<ast::Pat>),
529     NtExpr(P<ast::Expr>),
530     NtTy(P<ast::Ty>),
531     NtIdent(ast::SpannedIdent),
532     /// Stuff inside brackets for attributes
533     NtMeta(ast::MetaItem),
534     NtPath(ast::Path),
535     NtVis(ast::Visibility),
536     NtTT(TokenTree),
537     // These are not exposed to macros, but are used by quasiquote.
538     NtArm(ast::Arm),
539     NtImplItem(ast::ImplItem),
540     NtTraitItem(ast::TraitItem),
541     NtGenerics(ast::Generics),
542     NtWhereClause(ast::WhereClause),
543     NtArg(ast::Arg),
544     NtLifetime(ast::Lifetime),
545 }
546
547 impl fmt::Debug for Nonterminal {
548     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549         match *self {
550             NtItem(..) => f.pad("NtItem(..)"),
551             NtBlock(..) => f.pad("NtBlock(..)"),
552             NtStmt(..) => f.pad("NtStmt(..)"),
553             NtPat(..) => f.pad("NtPat(..)"),
554             NtExpr(..) => f.pad("NtExpr(..)"),
555             NtTy(..) => f.pad("NtTy(..)"),
556             NtIdent(..) => f.pad("NtIdent(..)"),
557             NtMeta(..) => f.pad("NtMeta(..)"),
558             NtPath(..) => f.pad("NtPath(..)"),
559             NtTT(..) => f.pad("NtTT(..)"),
560             NtArm(..) => f.pad("NtArm(..)"),
561             NtImplItem(..) => f.pad("NtImplItem(..)"),
562             NtTraitItem(..) => f.pad("NtTraitItem(..)"),
563             NtGenerics(..) => f.pad("NtGenerics(..)"),
564             NtWhereClause(..) => f.pad("NtWhereClause(..)"),
565             NtArg(..) => f.pad("NtArg(..)"),
566             NtVis(..) => f.pad("NtVis(..)"),
567             NtLifetime(..) => f.pad("NtLifetime(..)"),
568         }
569     }
570 }
571
572 pub fn is_op(tok: &Token) -> bool {
573     match *tok {
574         OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
575         Ident(..) | Underscore | Lifetime(..) | Interpolated(..) |
576         Whitespace | Comment | Shebang(..) | Eof => false,
577         _ => true,
578     }
579 }
580
581 pub struct LazyTokenStream(Cell<Option<TokenStream>>);
582
583 impl Clone for LazyTokenStream {
584     fn clone(&self) -> Self {
585         let opt_stream = self.0.take();
586         self.0.set(opt_stream.clone());
587         LazyTokenStream(Cell::new(opt_stream))
588     }
589 }
590
591 impl cmp::Eq for LazyTokenStream {}
592 impl PartialEq for LazyTokenStream {
593     fn eq(&self, _other: &LazyTokenStream) -> bool {
594         true
595     }
596 }
597
598 impl fmt::Debug for LazyTokenStream {
599     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
600         fmt::Debug::fmt(&self.clone().0.into_inner(), f)
601     }
602 }
603
604 impl LazyTokenStream {
605     pub fn new() -> Self {
606         LazyTokenStream(Cell::new(None))
607     }
608
609     pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream {
610         let mut opt_stream = self.0.take();
611         if opt_stream.is_none() {
612             opt_stream = Some(f());
613         }
614         self.0.set(opt_stream.clone());
615         opt_stream.clone().unwrap()
616     }
617 }
618
619 impl Encodable for LazyTokenStream {
620     fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
621         Ok(())
622     }
623 }
624
625 impl Decodable for LazyTokenStream {
626     fn decode<D: Decoder>(_: &mut D) -> Result<LazyTokenStream, D::Error> {
627         Ok(LazyTokenStream::new())
628     }
629 }
630
631 impl ::std::hash::Hash for LazyTokenStream {
632     fn hash<H: ::std::hash::Hasher>(&self, _hasher: &mut H) {}
633 }
634
635 fn prepend_attrs(sess: &ParseSess,
636                  attrs: &[ast::Attribute],
637                  tokens: Option<&tokenstream::TokenStream>,
638                  span: syntax_pos::Span)
639     -> Option<tokenstream::TokenStream>
640 {
641     let tokens = tokens?;
642     if attrs.len() == 0 {
643         return Some(tokens.clone())
644     }
645     let mut builder = tokenstream::TokenStreamBuilder::new();
646     for attr in attrs {
647         assert_eq!(attr.style, ast::AttrStyle::Outer,
648                    "inner attributes should prevent cached tokens from existing");
649         // FIXME: Avoid this pretty-print + reparse hack as bove
650         let name = FileName::MacroExpansion;
651         let source = pprust::attr_to_string(attr);
652         let stream = parse_stream_from_source_str(name, source, sess, Some(span));
653         builder.push(stream);
654     }
655     builder.push(tokens.clone());
656     Some(builder.build())
657 }