diff options
| author | Nick Cameron <ncameron@mozilla.com> | 2015-04-30 22:30:50 +1200 |
|---|---|---|
| committer | Nick Cameron <ncameron@mozilla.com> | 2015-04-30 22:30:50 +1200 |
| commit | b2ddd937b20d8fc26132cb7ec665784422d92926 (patch) | |
| tree | cf257df60ded1b45616d797b8feb71178cab0142 /src/libsyntax | |
| parent | c0a42aecbc85298fb6351253c4cd1824567b7a42 (diff) | |
| parent | f0bd14f7b15b978f8bf32bb368f63faa0f26c02e (diff) | |
| download | rust-b2ddd937b20d8fc26132cb7ec665784422d92926.tar.gz rust-b2ddd937b20d8fc26132cb7ec665784422d92926.zip | |
Merge branch 'master' into mulit-decor
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 16 | ||||
| -rw-r--r-- | src/libsyntax/ast_map/blocks.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/ast_map/mod.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/ast_util.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/codemap.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/diagnostics/metadata.rs | 155 | ||||
| -rw-r--r-- | src/libsyntax/diagnostics/plugin.rs | 96 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/quote.rs | 363 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 38 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 20 | ||||
| -rw-r--r-- | src/libsyntax/lib.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/comments.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 109 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 74 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 292 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 155 | ||||
| -rw-r--r-- | src/libsyntax/util/parser_testing.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/util/small_vector.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 14 |
21 files changed, 718 insertions, 660 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 399810cb7f5..3b7bfb1043a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -89,12 +89,6 @@ impl Ident { pub fn as_str<'a>(&'a self) -> &'a str { self.name.as_str() } - - pub fn encode_with_hygiene(&self) -> String { - format!("\x00name_{},ctxt_{}\x00", - self.name.usize(), - self.ctxt) - } } impl fmt::Debug for Ident { @@ -599,6 +593,12 @@ pub enum Pat_ { /// "None" means a * pattern where we don't bind the fields to names. PatEnum(Path, Option<Vec<P<Pat>>>), + /// An associated const named using the qualified path `<T>::CONST` or + /// `<T as Trait>::CONST`. Associated consts from inherent impls can be + /// refered to as simply `T::CONST`, in which case they will end up as + /// PatEnum, and the resolver will have to sort that out. + PatQPath(QSelf, Path), + /// Destructuring of a struct, e.g. `Foo {x, y, ..}` /// The `bool` is `true` in the presence of a `..` PatStruct(Path, Vec<Spanned<FieldPat>>, bool), @@ -1236,6 +1236,7 @@ pub struct TraitItem { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum TraitItem_ { + ConstTraitItem(P<Ty>, Option<P<Expr>>), MethodTraitItem(MethodSig, Option<P<Block>>), TypeTraitItem(TyParamBounds, Option<P<Ty>>), } @@ -1252,6 +1253,7 @@ pub struct ImplItem { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ImplItem_ { + ConstImplItem(P<Ty>, P<Expr>), MethodImplItem(MethodSig, P<Block>), TypeImplItem(P<Ty>), MacImplItem(Mac), @@ -1869,7 +1871,7 @@ pub struct MacroDef { } #[cfg(test)] -mod test { +mod tests { use serialize; use super::*; diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 1505d1e91b8..36a7f3a9381 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -222,8 +222,7 @@ impl<'a> FnLikeNode<'a> { ast::MethodImplItem(ref sig, ref body) => { method(ii.id, ii.ident, sig, Some(ii.vis), body, ii.span) } - ast::TypeImplItem(_) | - ast::MacImplItem(_) => { + _ => { panic!("impl method FnLikeNode that is not fn-like") } } diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs index 2a9a609ecd1..795391d4009 100644 --- a/src/libsyntax/ast_map/mod.rs +++ b/src/libsyntax/ast_map/mod.rs @@ -940,6 +940,12 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { } Some(NodeImplItem(ii)) => { match ii.node { + ConstImplItem(..) => { + format!("assoc const {} in {}{}", + token::get_ident(ii.ident), + map.path_to_string(id), + id_str) + } MethodImplItem(..) => { format!("method {} in {}{}", token::get_ident(ii.ident), @@ -959,9 +965,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { } Some(NodeTraitItem(ti)) => { let kind = match ti.node { + ConstTraitItem(..) => "assoc constant", MethodTraitItem(..) => "trait method", TypeTraitItem(..) => "assoc type", -// ConstTraitItem(..) => "assoc constant" }; format!("{} {} in {}{}", diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 78f06ce5fd5..8471fef3487 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -579,7 +579,7 @@ pub fn walk_pat<F>(pat: &Pat, mut it: F) -> bool where F: FnMut(&Pat) -> bool { } PatMac(_) => panic!("attempted to analyze unexpanded pattern"), PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) | - PatEnum(_, _) => { + PatEnum(_, _) | PatQPath(_, _) => { true } } @@ -632,7 +632,7 @@ pub fn lit_is_str(lit: &Lit) -> bool { } #[cfg(test)] -mod test { +mod tests { use ast::*; use super::*; diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 34ad192845c..5e0cb647c8b 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -949,7 +949,7 @@ pub struct MalformedCodemapPositions { // #[cfg(test)] -mod test { +mod tests { use super::*; use std::rc::Rc; diff --git a/src/libsyntax/diagnostics/metadata.rs b/src/libsyntax/diagnostics/metadata.rs new file mode 100644 index 00000000000..6cb4f70b860 --- /dev/null +++ b/src/libsyntax/diagnostics/metadata.rs @@ -0,0 +1,155 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This module contains utilities for outputting metadata for diagnostic errors. +//! +//! Each set of errors is mapped to a metadata file by a name, which is +//! currently always a crate name. + +use std::collections::BTreeMap; +use std::env; +use std::path::PathBuf; +use std::fs::{read_dir, create_dir_all, OpenOptions, File}; +use std::io::{Read, Write}; +use std::error::Error; +use rustc_serialize::json::{self, as_json}; + +use codemap::Span; +use ext::base::ExtCtxt; +use diagnostics::plugin::{ErrorMap, ErrorInfo}; + +pub use self::Uniqueness::*; + +// Default metadata directory to use for extended error JSON. +const ERROR_METADATA_DIR_DEFAULT: &'static str = "tmp/extended-errors"; + +// The name of the environment variable that sets the metadata dir. +const ERROR_METADATA_VAR: &'static str = "ERROR_METADATA_DIR"; + +/// JSON encodable/decodable version of `ErrorInfo`. +#[derive(PartialEq, RustcDecodable, RustcEncodable)] +pub struct ErrorMetadata { + pub description: Option<String>, + pub use_site: Option<ErrorLocation> +} + +/// Mapping from error codes to metadata that can be (de)serialized. +pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>; + +/// JSON encodable error location type with filename and line number. +#[derive(PartialEq, RustcDecodable, RustcEncodable)] +pub struct ErrorLocation { + pub filename: String, + pub line: usize +} + +impl ErrorLocation { + /// Create an error location from a span. + pub fn from_span(ecx: &ExtCtxt, sp: Span) -> ErrorLocation { + let loc = ecx.codemap().lookup_char_pos_adj(sp.lo); + ErrorLocation { + filename: loc.filename, + line: loc.line + } + } +} + +/// Type for describing the uniqueness of a set of error codes, as returned by `check_uniqueness`. +pub enum Uniqueness { + /// All errors in the set checked are unique according to the metadata files checked. + Unique, + /// One or more errors in the set occur in another metadata file. + /// This variant contains the first duplicate error code followed by the name + /// of the metadata file where the duplicate appears. + Duplicate(String, String) +} + +/// Get the directory where metadata files should be stored. +pub fn get_metadata_dir() -> PathBuf { + match env::var(ERROR_METADATA_VAR) { + Ok(v) => From::from(v), + Err(_) => From::from(ERROR_METADATA_DIR_DEFAULT) + } +} + +/// Get the path where error metadata for the set named by `name` should be stored. +fn get_metadata_path(name: &str) -> PathBuf { + get_metadata_dir().join(format!("{}.json", name)) +} + +/// Check that the errors in `err_map` aren't present in any metadata files in the +/// metadata directory except the metadata file corresponding to `name`. +pub fn check_uniqueness(name: &str, err_map: &ErrorMap) -> Result<Uniqueness, Box<Error>> { + let metadata_dir = get_metadata_dir(); + let metadata_path = get_metadata_path(name); + + // Create the error directory if it does not exist. + try!(create_dir_all(&metadata_dir)); + + // Check each file in the metadata directory. + for entry in try!(read_dir(&metadata_dir)) { + let path = try!(entry).path(); + + // Skip any existing file for this set. + if path == metadata_path { + continue; + } + + // Read the metadata file into a string. + let mut metadata_str = String::new(); + try!( + File::open(&path).and_then(|mut f| + f.read_to_string(&mut metadata_str)) + ); + + // Parse the JSON contents. + let metadata: ErrorMetadataMap = try!(json::decode(&metadata_str)); + + // Check for duplicates. + for err in err_map.keys() { + let err_code = err.as_str(); + if metadata.contains_key(err_code) { + return Ok(Duplicate( + err_code.to_string(), + path.to_string_lossy().into_owned() + )); + } + } + } + + Ok(Unique) +} + +/// Write metadata for the errors in `err_map` to disk, to a file corresponding to `name`. +pub fn output_metadata(ecx: &ExtCtxt, name: &str, err_map: &ErrorMap) + -> Result<(), Box<Error>> +{ + let metadata_path = get_metadata_path(name); + + // Open the dump file. + let mut dump_file = try!(OpenOptions::new() + .write(true) + .create(true) + .open(&metadata_path) + ); + + // Construct a serializable map. + let json_map = err_map.iter().map(|(k, &ErrorInfo { description, use_site })| { + let key = k.as_str().to_string(); + let value = ErrorMetadata { + description: description.map(|n| n.as_str().to_string()), + use_site: use_site.map(|sp| ErrorLocation::from_span(ecx, sp)) + }; + (key, value) + }).collect::<ErrorMetadataMap>(); + + try!(write!(&mut dump_file, "{}", as_json(&json_map))); + Ok(()) +} diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index 6de4edafa0b..16841bd9039 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -14,6 +14,7 @@ use std::collections::BTreeMap; use ast; use ast::{Ident, Name, TokenTree}; use codemap::Span; +use diagnostics::metadata::{check_uniqueness, output_metadata, Duplicate}; use ext::base::{ExtCtxt, MacEager, MacResult}; use ext::build::AstBuilder; use parse::token; @@ -24,32 +25,28 @@ use util::small_vector::SmallVector; const MAX_DESCRIPTION_WIDTH: usize = 80; thread_local! { - static REGISTERED_DIAGNOSTICS: RefCell<BTreeMap<Name, Option<Name>>> = { + static REGISTERED_DIAGNOSTICS: RefCell<ErrorMap> = { RefCell::new(BTreeMap::new()) } } -thread_local! { - static USED_DIAGNOSTICS: RefCell<BTreeMap<Name, Span>> = { - RefCell::new(BTreeMap::new()) - } + +/// Error information type. +pub struct ErrorInfo { + pub description: Option<Name>, + pub use_site: Option<Span> } +/// Mapping from error codes to metadata. +pub type ErrorMap = BTreeMap<Name, ErrorInfo>; + fn with_registered_diagnostics<T, F>(f: F) -> T where - F: FnOnce(&mut BTreeMap<Name, Option<Name>>) -> T, + F: FnOnce(&mut ErrorMap) -> T, { REGISTERED_DIAGNOSTICS.with(move |slot| { f(&mut *slot.borrow_mut()) }) } -fn with_used_diagnostics<T, F>(f: F) -> T where - F: FnOnce(&mut BTreeMap<Name, Span>) -> T, -{ - USED_DIAGNOSTICS.with(move |slot| { - f(&mut *slot.borrow_mut()) - }) -} - pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt, span: Span, token_tree: &[TokenTree]) @@ -58,23 +55,26 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt, (1, Some(&ast::TtToken(_, token::Ident(code, _)))) => code, _ => unreachable!() }; - with_used_diagnostics(|diagnostics| { - match diagnostics.insert(code.name, span) { - Some(previous_span) => { + + with_registered_diagnostics(|diagnostics| { + match diagnostics.get_mut(&code.name) { + // Previously used errors. + Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => { ecx.span_warn(span, &format!( "diagnostic code {} already used", &token::get_ident(code) )); ecx.span_note(previous_span, "previous invocation"); - }, - None => () - } - () - }); - with_registered_diagnostics(|diagnostics| { - if !diagnostics.contains_key(&code.name) { - ecx.span_err(span, &format!( - "used diagnostic code {} not registered", &token::get_ident(code) - )); + } + // Newly used errors. + Some(ref mut info) => { + info.use_site = Some(span); + } + // Unregistered errors. + None => { + ecx.span_err(span, &format!( + "used diagnostic code {} not registered", &token::get_ident(code) + )); + } } }); MacEager::expr(ecx.expr_tuple(span, Vec::new())) @@ -116,10 +116,14 @@ pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt, token::get_ident(*code), MAX_DESCRIPTION_WIDTH )); } - raw_msg }); + // Add the error to the map. with_registered_diagnostics(|diagnostics| { - if diagnostics.insert(code.name, description).is_some() { + let info = ErrorInfo { + description: description, + use_site: None + }; + if diagnostics.insert(code.name, info).is_some() { ecx.span_err(span, &format!( "diagnostic code {} already registered", &token::get_ident(*code) )); @@ -143,19 +147,43 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, span: Span, token_tree: &[TokenTree]) -> Box<MacResult+'cx> { - let name = match (token_tree.len(), token_tree.get(0)) { - (1, Some(&ast::TtToken(_, token::Ident(ref name, _)))) => name, + assert_eq!(token_tree.len(), 3); + let (crate_name, name) = match (&token_tree[0], &token_tree[2]) { + ( + // Crate name. + &ast::TtToken(_, token::Ident(ref crate_name, _)), + // DIAGNOSTICS ident. + &ast::TtToken(_, token::Ident(ref name, _)) + ) => (crate_name.as_str(), name), _ => unreachable!() }; + // Check uniqueness of errors and output metadata. + with_registered_diagnostics(|diagnostics| { + match check_uniqueness(crate_name, &*diagnostics) { + Ok(Duplicate(err, location)) => { + ecx.span_err(span, &format!( + "error {} from `{}' also found in `{}'", + err, crate_name, location + )); + }, + Ok(_) => (), + Err(e) => panic!("{}", e.description()) + } + + output_metadata(&*ecx, crate_name, &*diagnostics).ok().expect("metadata output error"); + }); + + // Construct the output expression. let (count, expr) = with_registered_diagnostics(|diagnostics| { let descriptions: Vec<P<ast::Expr>> = - diagnostics.iter().filter_map(|(code, description)| { - description.map(|description| { + diagnostics.iter().filter_map(|(code, info)| { + info.description.map(|description| { ecx.expr_tuple(span, vec![ ecx.expr_str(span, token::get_name(*code)), - ecx.expr_str(span, token::get_name(description))]) + ecx.expr_str(span, token::get_name(description)) + ]) }) }).collect(); (descriptions.len(), ecx.expr_vec(span, descriptions)) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 5033f406da0..b8a79b0658d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1580,7 +1580,7 @@ impl<'a, 'v> Visitor<'v> for MacroExterminator<'a> { #[cfg(test)] -mod test { +mod tests { use super::{pattern_bindings, expand_crate}; use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig}; use ast; diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 5776fa99740..e100b7705d8 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -30,16 +30,16 @@ pub mod rt { use ext::base::ExtCtxt; use parse::token; use parse; - use print::pprust; use ptr::P; + use std::rc::Rc; - use ast::{TokenTree, Generics, Expr}; + use ast::{TokenTree, Expr}; pub use parse::new_parser_from_tts; - pub use codemap::{BytePos, Span, dummy_spanned}; + pub use codemap::{BytePos, Span, dummy_spanned, DUMMY_SP}; pub trait ToTokens { - fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ; + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>; } impl ToTokens for TokenTree { @@ -70,277 +70,189 @@ pub mod rt { } } - /* Should be (when bugs in default methods are fixed): - - trait ToSource : ToTokens { - // Takes a thing and generates a string containing rust code for it. - pub fn to_source() -> String; - - // If you can make source, you can definitely make tokens. - pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] { - cx.parse_tts(self.to_source()) + impl ToTokens for ast::Ident { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(DUMMY_SP, token::Ident(*self, token::Plain))] } } - */ - - // FIXME: Move this trait to pprust and get rid of *_to_str? - pub trait ToSource { - // Takes a thing and generates a string containing rust code for it. - fn to_source(&self) -> String; + impl ToTokens for ast::Path { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtPath(Box::new(self.clone()))))] + } } - // FIXME (Issue #16472): This should go away after ToToken impls - // are revised to go directly to token-trees. - trait ToSourceWithHygiene : ToSource { - // Takes a thing and generates a string containing rust code - // for it, encoding Idents as special byte sequences to - // maintain hygiene across serialization and deserialization. - fn to_source_with_hygiene(&self) -> String; + impl ToTokens for ast::Ty { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtTy(P(self.clone()))))] + } } - macro_rules! impl_to_source { - (P<$t:ty>, $pp:ident) => ( - impl ToSource for P<$t> { - fn to_source(&self) -> String { - pprust::$pp(&**self) - } - } - impl ToSourceWithHygiene for P<$t> { - fn to_source_with_hygiene(&self) -> String { - pprust::with_hygiene::$pp(&**self) - } - } - ); - ($t:ty, $pp:ident) => ( - impl ToSource for $t { - fn to_source(&self) -> String { - pprust::$pp(self) - } - } - impl ToSourceWithHygiene for $t { - fn to_source_with_hygiene(&self) -> String { - pprust::with_hygiene::$pp(self) - } - } - ); + impl ToTokens for ast::Block { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtBlock(P(self.clone()))))] + } } - fn slice_to_source<'a, T: ToSource>(sep: &'static str, xs: &'a [T]) -> String { - xs.iter() - .map(|i| i.to_source()) - .collect::<Vec<String>>() - .connect(sep) - .to_string() + impl ToTokens for P<ast::Item> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtItem(self.clone())))] + } } - fn slice_to_source_with_hygiene<'a, T: ToSourceWithHygiene>( - sep: &'static str, xs: &'a [T]) -> String { - xs.iter() - .map(|i| i.to_source_with_hygiene()) - .collect::<Vec<String>>() - .connect(sep) - .to_string() + impl ToTokens for P<ast::ImplItem> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtImplItem(self.clone())))] + } } - macro_rules! impl_to_source_slice { - ($t:ty, $sep:expr) => ( - impl ToSource for [$t] { - fn to_source(&self) -> String { - slice_to_source($sep, self) - } - } - - impl ToSourceWithHygiene for [$t] { - fn to_source_with_hygiene(&self) -> String { - slice_to_source_with_hygiene($sep, self) - } - } - ) + impl ToTokens for P<ast::TraitItem> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtTraitItem(self.clone())))] + } } - impl ToSource for ast::Ident { - fn to_source(&self) -> String { - token::get_ident(*self).to_string() + impl ToTokens for P<ast::Stmt> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtStmt(self.clone())))] } } - impl ToSourceWithHygiene for ast::Ident { - fn to_source_with_hygiene(&self) -> String { - self.encode_with_hygiene() + impl ToTokens for P<ast::Expr> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtExpr(self.clone())))] } } - impl_to_source! { ast::Path, path_to_string } - impl_to_source! { ast::Ty, ty_to_string } - impl_to_source! { ast::Block, block_to_string } - impl_to_source! { ast::Arg, arg_to_string } - impl_to_source! { Generics, generics_to_string } - impl_to_source! { ast::WhereClause, where_clause_to_string } - impl_to_source! { P<ast::Item>, item_to_string } - impl_to_source! { P<ast::ImplItem>, impl_item_to_string } - impl_to_source! { P<ast::TraitItem>, trait_item_to_string } - impl_to_source! { P<ast::Stmt>, stmt_to_string } - impl_to_source! { P<ast::Expr>, expr_to_string } - impl_to_source! { P<ast::Pat>, pat_to_string } - impl_to_source! { ast::Arm, arm_to_string } - impl_to_source_slice! { ast::Ty, ", " } - impl_to_source_slice! { P<ast::Item>, "\n\n" } - - impl ToSource for ast::Attribute_ { - fn to_source(&self) -> String { - pprust::attribute_to_string(&dummy_spanned(self.clone())) + impl ToTokens for P<ast::Pat> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(self.span, token::Interpolated(token::NtPat(self.clone())))] } } - impl ToSourceWithHygiene for ast::Attribute_ { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + + impl ToTokens for ast::Arm { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtArm(self.clone())))] } } - impl ToSource for str { - fn to_source(&self) -> String { - let lit = dummy_spanned(ast::LitStr( - token::intern_and_get_ident(self), ast::CookedStr)); - pprust::lit_to_string(&lit) - } + macro_rules! impl_to_tokens_slice { + ($t: ty, $sep: expr) => { + impl ToTokens for [$t] { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + let mut v = vec![]; + for (i, x) in self.iter().enumerate() { + if i > 0 { + v.push_all(&$sep); + } + v.extend(x.to_tokens(cx)); + } + v + } + } + }; } - impl ToSourceWithHygiene for str { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + + impl_to_tokens_slice! { ast::Ty, [ast::TtToken(DUMMY_SP, token::Comma)] } + impl_to_tokens_slice! { P<ast::Item>, [] } + + impl ToTokens for P<ast::MetaItem> { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtMeta(self.clone())))] } } - impl ToSource for () { - fn to_source(&self) -> String { - "()".to_string() + impl ToTokens for ast::Attribute { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + let mut r = vec![]; + // FIXME: The spans could be better + r.push(ast::TtToken(self.span, token::Pound)); + if self.node.style == ast::AttrInner { + r.push(ast::TtToken(self.span, token::Not)); + } + r.push(ast::TtDelimited(self.span, Rc::new(ast::Delimited { + delim: token::Bracket, + open_span: self.span, + tts: self.node.value.to_tokens(cx), + close_span: self.span, + }))); + r } } - impl ToSourceWithHygiene for () { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + + impl ToTokens for str { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + let lit = ast::LitStr( + token::intern_and_get_ident(self), ast::CookedStr); + dummy_spanned(lit).to_tokens(cx) } } - impl ToSource for bool { - fn to_source(&self) -> String { - let lit = dummy_spanned(ast::LitBool(*self)); - pprust::lit_to_string(&lit) + impl ToTokens for () { + fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> { + vec![ast::TtDelimited(DUMMY_SP, Rc::new(ast::Delimited { + delim: token::Paren, + open_span: DUMMY_SP, + tts: vec![], + close_span: DUMMY_SP, + }))] } } - impl ToSourceWithHygiene for bool { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + + impl ToTokens for ast::Lit { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + // FIXME: This is wrong + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprLit(P(self.clone())), + span: DUMMY_SP, + }).to_tokens(cx) } } - impl ToSource for char { - fn to_source(&self) -> String { - let lit = dummy_spanned(ast::LitChar(*self)); - pprust::lit_to_string(&lit) + impl ToTokens for bool { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + dummy_spanned(ast::LitBool(*self)).to_tokens(cx) } } - impl ToSourceWithHygiene for char { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + + impl ToTokens for char { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { + dummy_spanned(ast::LitChar(*self)).to_tokens(cx) } } - macro_rules! impl_to_source_int { + macro_rules! impl_to_tokens_int { (signed, $t:ty, $tag:expr) => ( - impl ToSource for $t { - fn to_source(&self) -> String { + impl ToTokens for $t { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { let lit = ast::LitInt(*self as u64, ast::SignedIntLit($tag, ast::Sign::new(*self))); - pprust::lit_to_string(&dummy_spanned(lit)) - } - } - impl ToSourceWithHygiene for $t { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + dummy_spanned(lit).to_tokens(cx) } } ); (unsigned, $t:ty, $tag:expr) => ( - impl ToSource for $t { - fn to_source(&self) -> String { + impl ToTokens for $t { + fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { let lit = ast::LitInt(*self as u64, ast::UnsignedIntLit($tag)); - pprust::lit_to_string(&dummy_spanned(lit)) - } - } - impl ToSourceWithHygiene for $t { - fn to_source_with_hygiene(&self) -> String { - self.to_source() + dummy_spanned(lit).to_tokens(cx) } } ); } - impl_to_source_int! { signed, isize, ast::TyIs } - impl_to_source_int! { signed, i8, ast::TyI8 } - impl_to_source_int! { signed, i16, ast::TyI16 } - impl_to_source_int! { signed, i32, ast::TyI32 } - impl_to_source_int! { signed, i64, ast::TyI64 } - - impl_to_source_int! { unsigned, usize, ast::TyUs } - impl_to_source_int! { unsigned, u8, ast::TyU8 } - impl_to_source_int! { unsigned, u16, ast::TyU16 } - impl_to_source_int! { unsigned, u32, ast::TyU32 } - impl_to_source_int! { unsigned, u64, ast::TyU64 } - - // Alas ... we write these out instead. All redundant. - - macro_rules! impl_to_tokens { - ($t:ty) => ( - impl ToTokens for $t { - fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { - cx.parse_tts_with_hygiene(self.to_source_with_hygiene()) - } - } - ) - } + impl_to_tokens_int! { signed, isize, ast::TyIs } + impl_to_tokens_int! { signed, i8, ast::TyI8 } + impl_to_tokens_int! { signed, i16, ast::TyI16 } + impl_to_tokens_int! { signed, i32, ast::TyI32 } + impl_to_tokens_int! { signed, i64, ast::TyI64 } - macro_rules! impl_to_tokens_lifetime { - ($t:ty) => ( - impl<'a> ToTokens for $t { - fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> { - cx.parse_tts_with_hygiene(self.to_source_with_hygiene()) - } - } - ) - } - - impl_to_tokens! { ast::Ident } - impl_to_tokens! { ast::Path } - impl_to_tokens! { P<ast::Item> } - impl_to_tokens! { P<ast::ImplItem> } - impl_to_tokens! { P<ast::TraitItem> } - impl_to_tokens! { P<ast::Pat> } - impl_to_tokens! { ast::Arm } - impl_to_tokens_lifetime! { &'a [P<ast::Item>] } - impl_to_tokens! { ast::Ty } - impl_to_tokens_lifetime! { &'a [ast::Ty] } - impl_to_tokens! { Generics } - impl_to_tokens! { ast::WhereClause } - impl_to_tokens! { P<ast::Stmt> } - impl_to_tokens! { P<ast::Expr> } - impl_to_tokens! { ast::Block } - impl_to_tokens! { ast::Arg } - impl_to_tokens! { ast::Attribute_ } - impl_to_tokens_lifetime! { &'a str } - impl_to_tokens! { () } - impl_to_tokens! { char } - impl_to_tokens! { bool } - impl_to_tokens! { isize } - impl_to_tokens! { i8 } - impl_to_tokens! { i16 } - impl_to_tokens! { i32 } - impl_to_tokens! { i64 } - impl_to_tokens! { usize } - impl_to_tokens! { u8 } - impl_to_tokens! { u16 } - impl_to_tokens! { u32 } - impl_to_tokens! { u64 } + impl_to_tokens_int! { unsigned, usize, ast::TyUs } + impl_to_tokens_int! { unsigned, u8, ast::TyU8 } + impl_to_tokens_int! { unsigned, u16, ast::TyU16 } + impl_to_tokens_int! { unsigned, u32, ast::TyU32 } + impl_to_tokens_int! { unsigned, u64, ast::TyU64 } pub trait ExtParseUtils { fn parse_item(&self, s: String) -> P<ast::Item>; @@ -349,12 +261,6 @@ pub mod rt { fn parse_tts(&self, s: String) -> Vec<ast::TokenTree>; } - trait ExtParseUtilsWithHygiene { - // FIXME (Issue #16472): This should go away after ToToken impls - // are revised to go directly to token-trees. - fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree>; - } - impl<'a> ExtParseUtils for ExtCtxt<'a> { fn parse_item(&self, s: String) -> P<ast::Item> { @@ -386,19 +292,6 @@ pub mod rt { self.parse_sess()) } } - - impl<'a> ExtParseUtilsWithHygiene for ExtCtxt<'a> { - - fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree> { - use parse::with_hygiene::parse_tts_from_source_str; - parse_tts_from_source_str("<quote expansion>".to_string(), - s, - self.cfg(), - self.parse_sess()) - } - - } - } pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d0975c76e93..28deb4eec3f 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -93,10 +93,6 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ ("fundamental", "1.0.0", Active), - // Deprecate after snapshot - // SNAP 5520801 - ("unsafe_destructor", "1.0.0", Active), - // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. ("issue_5723_bootstrap", "1.0.0", Accepted), @@ -155,6 +151,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 ("negate_unsigned", "1.0.0", Active), + + // Allows the definition of associated constants in `trait` or `impl` + // blocks. + ("associated_consts", "1.0.0", Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -205,8 +205,6 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ ("link_args", Normal), ("macro_escape", Normal), - ("unsafe_destructor", Gated("unsafe_destructor", - "`#[unsafe_destructor]` does nothing anymore")), ("staged_api", Gated("staged_api", "staged_api is for use by rustc only")), ("plugin", Gated("plugin", @@ -394,7 +392,7 @@ impl<'a> Context<'a> { are reserved for internal compiler diagnostics"); } else if name.starts_with("derive_") { self.gate_feature("custom_derive", attr.span, - "attributes of the form `#[derive_*]` are reserved + "attributes of the form `#[derive_*]` are reserved \ for the compiler"); } else { self.gate_feature("custom_attribute", attr.span, @@ -620,7 +618,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { pattern.span, "multiple-element slice matches anywhere \ but at the end of a slice (e.g. \ - `[0, ..xs, 0]` are experimental") + `[0, ..xs, 0]`) are experimental") } ast::PatVec(..) => { self.gate_feature("slice_patterns", @@ -659,6 +657,30 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } visit::walk_fn(self, fn_kind, fn_decl, block, span); } + + fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { + match ti.node { + ast::ConstTraitItem(..) => { + self.gate_feature("associated_consts", + ti.span, + "associated constants are experimental") + } + _ => {} + } + visit::walk_trait_item(self, ti); + } + + fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + match ii.node { + ast::ConstImplItem(..) => { + self.gate_feature("associated_consts", + ii.span, + "associated constants are experimental") + } + _ => {} + } + visit::walk_impl_item(self, ii); + } } fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8ba36cefc65..adfda988b23 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -682,6 +682,13 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T) token::NtMeta(meta_item) => token::NtMeta(fld.fold_meta_item(meta_item)), token::NtPath(path) => token::NtPath(Box::new(fld.fold_path(*path))), token::NtTT(tt) => token::NtTT(P(fld.fold_tt(&*tt))), + token::NtArm(arm) => token::NtArm(fld.fold_arm(arm)), + token::NtImplItem(arm) => + token::NtImplItem(fld.fold_impl_item(arm) + .expect_one("expected fold to produce exactly one item")), + token::NtTraitItem(arm) => + token::NtTraitItem(fld.fold_trait_item(arm) + .expect_one("expected fold to produce exactly one item")), } } @@ -973,6 +980,10 @@ pub fn noop_fold_trait_item<T: Folder>(i: P<TraitItem>, folder: &mut T) ident: folder.fold_ident(ident), attrs: fold_attrs(attrs, folder), node: match node { + ConstTraitItem(ty, default) => { + ConstTraitItem(folder.fold_ty(ty), + default.map(|x| folder.fold_expr(x))) + } MethodTraitItem(sig, body) => { MethodTraitItem(noop_fold_method_sig(sig, folder), body.map(|x| folder.fold_block(x))) @@ -994,6 +1005,9 @@ pub fn noop_fold_impl_item<T: Folder>(i: P<ImplItem>, folder: &mut T) attrs: fold_attrs(attrs, folder), vis: vis, node: match node { + ConstImplItem(ty, expr) => { + ConstImplItem(folder.fold_ty(ty), folder.fold_expr(expr)) + } MethodImplItem(sig, body) => { MethodImplItem(noop_fold_method_sig(sig, folder), folder.fold_block(body)) @@ -1127,6 +1141,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> { PatEnum(folder.fold_path(pth), pats.map(|pats| pats.move_map(|x| folder.fold_pat(x)))) } + PatQPath(qself, pth) => { + let qself = QSelf {ty: folder.fold_ty(qself.ty), .. qself}; + PatQPath(qself, folder.fold_path(pth)) + } PatStruct(pth, fields, etc) => { let pth = folder.fold_path(pth); let fs = fields.move_map(|f| { @@ -1343,7 +1361,7 @@ pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T) } #[cfg(test)] -mod test { +mod tests { use std::io; use ast; use util::parser_testing::{string_to_crate, matches_codepattern}; diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index d8beeb6a550..275400009f5 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -25,13 +25,14 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/nightly/")] +#![feature(associated_consts)] #![feature(collections)] #![feature(core)] #![feature(libc)] #![feature(rustc_private)] #![feature(staged_api)] -#![feature(unicode)] #![feature(str_char)] +#![feature(unicode)] extern crate arena; extern crate fmt_macros; @@ -69,6 +70,7 @@ pub mod diagnostics { pub mod macros; pub mod plugin; pub mod registry; + pub mod metadata; } pub mod syntax { diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs index fb3a96f4c28..1577b50ad76 100644 --- a/src/libsyntax/parse/lexer/comments.rs +++ b/src/libsyntax/parse/lexer/comments.rs @@ -383,7 +383,7 @@ pub fn gather_comments_and_literals(span_diagnostic: &diagnostic::SpanHandler, } #[cfg(test)] -mod test { +mod tests { use super::*; #[test] fn test_block_doc_comment_1() { diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 8e37b983e21..6b0674c9a41 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -19,7 +19,6 @@ use str::char_at; use std::borrow::Cow; use std::char; -use std::fmt; use std::mem::replace; use std::rc::Rc; @@ -71,11 +70,6 @@ pub struct StringReader<'a> { pub peek_tok: token::Token, pub peek_span: Span, - // FIXME (Issue #16472): This field should go away after ToToken impls - // are revised to go directly to token-trees. - /// Is \x00<name>,<ctxt>\x00 is interpreted as encoded ast::Ident? - read_embedded_ident: bool, - // cache a direct reference to the source text, so that we don't have to // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time. source_text: Rc<String> @@ -130,17 +124,6 @@ impl<'a> Reader for TtReader<'a> { } } -// FIXME (Issue #16472): This function should go away after -// ToToken impls are revised to go directly to token-trees. -pub fn make_reader_with_embedded_idents<'b>(span_diagnostic: &'b SpanHandler, - filemap: Rc<codemap::FileMap>) - -> StringReader<'b> { - let mut sr = StringReader::new_raw(span_diagnostic, filemap); - sr.read_embedded_ident = true; - sr.advance_token(); - sr -} - impl<'a> StringReader<'a> { /// For comments.rs, which hackily pokes into pos and curr pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler, @@ -162,7 +145,6 @@ impl<'a> StringReader<'a> { /* dummy values; not read */ peek_tok: token::Eof, peek_span: codemap::DUMMY_SP, - read_embedded_ident: false, source_text: source_text }; sr.bump(); @@ -578,81 +560,6 @@ impl<'a> StringReader<'a> { }) } - // FIXME (Issue #16472): The scan_embedded_hygienic_ident function - // should go away after we revise the syntax::ext::quote::ToToken - // impls to go directly to token-trees instead of thing -> string - // -> token-trees. (The function is currently used to resolve - // Issues #15750 and #15962.) - // - // Since this function is only used for certain internal macros, - // and the functionality it provides is not exposed to end user - // programs, pnkfelix deliberately chose to write it in a way that - // favors rustc debugging effectiveness over runtime efficiency. - - /// Scan through input of form \x00name_NNNNNN,ctxt_CCCCCCC\x00 - /// whence: `NNNNNN` is a string of characters forming an integer - /// (the name) and `CCCCCCC` is a string of characters forming an - /// integer (the ctxt), separate by a comma and delimited by a - /// `\x00` marker. - #[inline(never)] - fn scan_embedded_hygienic_ident(&mut self) -> ast::Ident { - fn bump_expecting_char<'a,D:fmt::Debug>(r: &mut StringReader<'a>, - c: char, - described_c: D, - whence: &str) { - match r.curr { - Some(r_c) if r_c == c => r.bump(), - Some(r_c) => panic!("expected {:?}, hit {:?}, {}", described_c, r_c, whence), - None => panic!("expected {:?}, hit EOF, {}", described_c, whence), - } - } - - let whence = "while scanning embedded hygienic ident"; - - // skip over the leading `\x00` - bump_expecting_char(self, '\x00', "nul-byte", whence); - - // skip over the "name_" - for c in "name_".chars() { - bump_expecting_char(self, c, c, whence); - } - - let start_bpos = self.last_pos; - let base = 10; - - // find the integer representing the name - self.scan_digits(base, base); - let encoded_name : u32 = self.with_str_from(start_bpos, |s| { - u32::from_str_radix(s, 10).unwrap_or_else(|_| { - panic!("expected digits representing a name, got {:?}, {}, range [{:?},{:?}]", - s, whence, start_bpos, self.last_pos); - }) - }); - - // skip over the `,` - bump_expecting_char(self, ',', "comma", whence); - - // skip over the "ctxt_" - for c in "ctxt_".chars() { - bump_expecting_char(self, c, c, whence); - } - - // find the integer representing the ctxt - let start_bpos = self.last_pos; - self.scan_digits(base, base); - let encoded_ctxt : ast::SyntaxContext = self.with_str_from(start_bpos, |s| { - u32::from_str_radix(s, 10).unwrap_or_else(|_| { - panic!("expected digits representing a ctxt, got {:?}, {}", s, whence); - }) - }); - - // skip over the `\x00` - bump_expecting_char(self, '\x00', "nul-byte", whence); - - ast::Ident { name: ast::Name(encoded_name), - ctxt: encoded_ctxt, } - } - /// Scan through any digits (base `scan_radix`) or underscores, /// and return how many digits there were. /// @@ -1020,20 +927,6 @@ impl<'a> StringReader<'a> { return token::Literal(num, suffix) } - if self.read_embedded_ident { - match (c.unwrap(), self.nextch(), self.nextnextch()) { - ('\x00', Some('n'), Some('a')) => { - let ast_ident = self.scan_embedded_hygienic_ident(); - return if self.curr_is(':') && self.nextch_is(':') { - token::Ident(ast_ident, token::ModName) - } else { - token::Ident(ast_ident, token::Plain) - }; - } - _ => {} - } - } - match c.expect("next_token_inner called at EOF") { // One-byte tokens. ';' => { self.bump(); return token::Semi; } @@ -1501,7 +1394,7 @@ fn ident_continue(c: Option<char>) -> bool { } #[cfg(test)] -mod test { +mod tests { use super::*; use codemap::{BytePos, CodeMap, Span, NO_EXPANSION}; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 1a1713a8ba6..8c9ce5f78d4 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -166,9 +166,6 @@ pub fn parse_stmt_from_source_str(name: String, maybe_aborted(p.parse_stmt(), p) } -// Note: keep in sync with `with_hygiene::parse_tts_from_source_str` -// until #16472 is resolved. -// // Warning: This parses with quote_depth > 0, which is not the default. pub fn parse_tts_from_source_str(name: String, source: String, @@ -186,8 +183,6 @@ pub fn parse_tts_from_source_str(name: String, maybe_aborted(panictry!(p.parse_all_token_trees()),p) } -// Note: keep in sync with `with_hygiene::new_parser_from_source_str` -// until #16472 is resolved. // Create a new parser from a source string pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess, cfg: ast::CrateConfig, @@ -220,8 +215,6 @@ pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess, p } -// Note: keep this in sync with `with_hygiene::filemap_to_parser` until -// #16472 is resolved. /// Given a filemap and config, return a parser pub fn filemap_to_parser<'a>(sess: &'a ParseSess, filemap: Rc<FileMap>, @@ -277,8 +270,6 @@ pub fn string_to_filemap(sess: &ParseSess, source: String, path: String) sess.span_diagnostic.cm.new_filemap(path, source) } -// Note: keep this in sync with `with_hygiene::filemap_to_tts` (apart -// from the StringReader constructor), until #16472 is resolved. /// Given a filemap, produce a sequence of token-trees pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>) -> Vec<ast::TokenTree> { @@ -300,69 +291,6 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess, p } -// FIXME (Issue #16472): The `with_hygiene` mod should go away after -// ToToken impls are revised to go directly to token-trees. -pub mod with_hygiene { - use ast; - use codemap::FileMap; - use parse::parser::Parser; - use std::rc::Rc; - use super::ParseSess; - use super::{maybe_aborted, string_to_filemap, tts_to_parser}; - - // Note: keep this in sync with `super::parse_tts_from_source_str` until - // #16472 is resolved. - // - // Warning: This parses with quote_depth > 0, which is not the default. - pub fn parse_tts_from_source_str(name: String, - source: String, - cfg: ast::CrateConfig, - sess: &ParseSess) -> Vec<ast::TokenTree> { - let mut p = new_parser_from_source_str( - sess, - cfg, - name, - source - ); - p.quote_depth += 1; - // right now this is re-creating the token trees from ... token trees. - maybe_aborted(panictry!(p.parse_all_token_trees()),p) - } - - // Note: keep this in sync with `super::new_parser_from_source_str` until - // #16472 is resolved. - // Create a new parser from a source string - fn new_parser_from_source_str<'a>(sess: &'a ParseSess, - cfg: ast::CrateConfig, - name: String, - source: String) -> Parser<'a> { - filemap_to_parser(sess, string_to_filemap(sess, source, name), cfg) - } - - // Note: keep this in sync with `super::filemap_to_parserr` until - // #16472 is resolved. - /// Given a filemap and config, return a parser - fn filemap_to_parser<'a>(sess: &'a ParseSess, - filemap: Rc<FileMap>, - cfg: ast::CrateConfig) -> Parser<'a> { - tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg) - } - - // Note: keep this in sync with `super::filemap_to_tts` until - // #16472 is resolved. - /// Given a filemap, produce a sequence of token-trees - fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>) - -> Vec<ast::TokenTree> { - // it appears to me that the cfg doesn't matter here... indeed, - // parsing tt's probably shouldn't require a parser at all. - use super::lexer::make_reader_with_embedded_idents as make_reader; - let cfg = Vec::new(); - let srdr = make_reader(&sess.span_diagnostic, filemap); - let mut p1 = Parser::new(sess, cfg, Box::new(srdr)); - panictry!(p1.parse_all_token_trees()) - } -} - /// Abort if necessary pub fn maybe_aborted<T>(result: T, p: Parser) -> T { p.abort_if_errors(); @@ -761,7 +689,7 @@ pub fn integer_lit(s: &str, suffix: Option<&str>, sd: &SpanHandler, sp: Span) -> } #[cfg(test)] -mod test { +mod tests { use super::*; use std::rc::Rc; use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION}; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 68006a8979a..5f76c214927 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -17,8 +17,8 @@ use ast::{Public, Unsafety}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block}; use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause}; -use ast::{Crate, CrateConfig, Decl, DeclItem}; -use ast::{DeclLocal, DefaultBlock, DefaultReturn}; +use ast::{ConstImplItem, ConstTraitItem, Crate, CrateConfig}; +use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn}; use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; @@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces}; use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource}; use ast::{MutTy, BiMul, Mutability}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot}; -use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion}; -use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle}; +use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange}; +use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti}; +use ast::PatWildSingle; use ast::{PolyTraitRef, QSelf}; use ast::{Return, BiShl, BiShr, Stmt, StmtDecl}; use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; @@ -87,9 +88,9 @@ use std::slice; bitflags! { flags Restrictions: u8 { - const UNRESTRICTED = 0b0000, - const RESTRICTION_STMT_EXPR = 0b0001, - const RESTRICTION_NO_STRUCT_LITERAL = 0b0010, + const UNRESTRICTED = 0, + const RESTRICTION_STMT_EXPR = 1 << 0, + const RESTRICTION_NO_STRUCT_LITERAL = 1 << 1, } } @@ -109,6 +110,15 @@ pub enum PathParsingMode { LifetimeAndTypesWithColons, } +/// How to parse a qualified path, whether to allow trailing parameters. +#[derive(Copy, Clone, PartialEq)] +pub enum QPathParsingMode { + /// No trailing parameters, e.g. `<T as Trait>::Item` + NoParameters, + /// Optional parameters, e.g. `<T as Trait>::item::<'a, U>` + MaybeParameters, +} + /// How to parse a bound, whether to allow bound modifiers such as `?`. #[derive(Copy, Clone, PartialEq)] pub enum BoundParsingMode { @@ -329,7 +339,7 @@ impl<'a> Parser<'a> { buffer_start: 0, buffer_end: 0, tokens_consumed: 0, - restrictions: UNRESTRICTED, + restrictions: Restrictions::UNRESTRICTED, quote_depth: 0, obsolete_set: HashSet::new(), mod_path_stack: Vec::new(), @@ -902,7 +912,9 @@ impl<'a> Parser<'a> { pub fn bump(&mut self) -> PResult<()> { self.last_span = self.span; // Stash token for error recovery (sometimes; clone is not necessarily cheap). - self.last_token = if self.token.is_ident() || self.token.is_path() { + self.last_token = if self.token.is_ident() || + self.token.is_path() || + self.token == token::Comma { Some(Box::new(self.token.clone())) } else { None @@ -1150,7 +1162,8 @@ impl<'a> Parser<'a> { &token::OpenDelim(token::Brace), &token::CloseDelim(token::Brace), seq_sep_none(), - |p| { + |p| -> PResult<P<TraitItem>> { + maybe_whole!(no_clone p, NtTraitItem); let mut attrs = p.parse_outer_attributes(); let lo = p.span.lo; @@ -1158,6 +1171,20 @@ impl<'a> Parser<'a> { let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param()); try!(p.expect(&token::Semi)); (ident, TypeTraitItem(bounds, default)) + } else if try!(p.eat_keyword(keywords::Const)) { + let ident = try!(p.parse_ident()); + try!(p.expect(&token::Colon)); + let ty = try!(p.parse_ty_sum()); + let default = if p.check(&token::Eq) { + try!(p.bump()); + let expr = try!(p.parse_expr_nopanic()); + try!(p.commit_expr_expecting(&expr, token::Semi)); + Some(expr) + } else { + try!(p.expect(&token::Semi)); + None + }; + (ident, ConstTraitItem(ty, default)) } else { let style = try!(p.parse_unsafety()); let abi = if try!(p.eat_keyword(keywords::Extern)) { @@ -1331,36 +1358,9 @@ impl<'a> Parser<'a> { try!(self.expect(&token::CloseDelim(token::Paren))); TyTypeof(e) } else if try!(self.eat_lt()) { - // QUALIFIED PATH `<TYPE as TRAIT_REF>::item` - let self_type = try!(self.parse_ty_sum()); - - let mut path = if try!(self.eat_keyword(keywords::As) ){ - try!(self.parse_path(LifetimeAndTypesWithoutColons)) - } else { - ast::Path { - span: self.span, - global: false, - segments: vec![] - } - }; - - let qself = QSelf { - ty: self_type, - position: path.segments.len() - }; - try!(self.expect(&token::Gt)); - try!(self.expect(&token::ModSep)); - - path.segments.push(ast::PathSegment { - identifier: try!(self.parse_ident()), - parameters: ast::PathParameters::none() - }); - - if path.segments.len() == 1 { - path.span.lo = self.last_span.lo; - } - path.span.hi = self.last_span.hi; + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); TyPath(Some(qself), path) } else if self.check(&token::ModSep) || @@ -1577,6 +1577,61 @@ impl<'a> Parser<'a> { } } + // QUALIFIED PATH `<TYPE [as TRAIT_REF]>::IDENT[::<PARAMS>]` + // Assumes that the leading `<` has been parsed already. + pub fn parse_qualified_path(&mut self, mode: QPathParsingMode) + -> PResult<(QSelf, ast::Path)> { + let self_type = try!(self.parse_ty_sum()); + let mut path = if try!(self.eat_keyword(keywords::As)) { + try!(self.parse_path(LifetimeAndTypesWithoutColons)) + } else { + ast::Path { + span: self.span, + global: false, + segments: vec![] + } + }; + + let qself = QSelf { + ty: self_type, + position: path.segments.len() + }; + + try!(self.expect(&token::Gt)); + try!(self.expect(&token::ModSep)); + + let item_name = try!(self.parse_ident()); + let parameters = match mode { + QPathParsingMode::NoParameters => ast::PathParameters::none(), + QPathParsingMode::MaybeParameters => { + if try!(self.eat(&token::ModSep)) { + try!(self.expect_lt()); + // Consumed `item::<`, go look for types + let (lifetimes, types, bindings) = + try!(self.parse_generic_values_after_lt()); + ast::AngleBracketedParameters(ast::AngleBracketedParameterData { + lifetimes: lifetimes, + types: OwnedSlice::from_vec(types), + bindings: OwnedSlice::from_vec(bindings), + }) + } else { + ast::PathParameters::none() + } + } + }; + path.segments.push(ast::PathSegment { + identifier: item_name, + parameters: parameters + }); + + if path.segments.len() == 1 { + path.span.lo = self.last_span.lo; + } + path.span.hi = self.last_span.hi; + + Ok((qself, path)) + } + /// Parses a path and optional type parameter bounds, depending on the /// mode. The `mode` parameter determines whether lifetimes, types, and/or /// bounds are permitted and whether `::` must precede type parameter @@ -2040,49 +2095,10 @@ impl<'a> Parser<'a> { } _ => { if try!(self.eat_lt()){ - // QUALIFIED PATH `<TYPE as TRAIT_REF>::item::<'a, T>` - let self_type = try!(self.parse_ty_sum()); - let mut path = if try!(self.eat_keyword(keywords::As) ){ - try!(self.parse_path(LifetimeAndTypesWithoutColons)) - } else { - ast::Path { - span: self.span, - global: false, - segments: vec![] - } - }; - let qself = QSelf { - ty: self_type, - position: path.segments.len() - }; - try!(self.expect(&token::Gt)); - try!(self.expect(&token::ModSep)); - let item_name = try!(self.parse_ident()); - let parameters = if try!(self.eat(&token::ModSep) ){ - try!(self.expect_lt()); - // Consumed `item::<`, go look for types - let (lifetimes, types, bindings) = - try!(self.parse_generic_values_after_lt()); - ast::AngleBracketedParameters(ast::AngleBracketedParameterData { - lifetimes: lifetimes, - types: OwnedSlice::from_vec(types), - bindings: OwnedSlice::from_vec(bindings), - }) - } else { - ast::PathParameters::none() - }; - path.segments.push(ast::PathSegment { - identifier: item_name, - parameters: parameters - }); - - if path.segments.len() == 1 { - path.span.lo = self.last_span.lo; - } - path.span.hi = self.last_span.hi; + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters)); - let hi = self.span.hi; return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path))); } if try!(self.eat_keyword(keywords::Move) ){ @@ -2182,7 +2198,10 @@ impl<'a> Parser<'a> { if self.check(&token::OpenDelim(token::Brace)) { // This is a struct literal, unless we're prohibited // from parsing struct literals here. - if !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL) { + let prohibited = self.restrictions.contains( + Restrictions::RESTRICTION_NO_STRUCT_LITERAL + ); + if !prohibited { // It's a struct literal. try!(self.bump()); let mut fields = Vec::new(); @@ -2743,7 +2762,7 @@ impl<'a> Parser<'a> { } pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> PResult<P<Expr>> { - let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL; + let restrictions = self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL; let op_span = self.span; match self.token { token::Eq => { @@ -2798,7 +2817,7 @@ impl<'a> Parser<'a> { if self.token.can_begin_expr() { // parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. if self.token == token::OpenDelim(token::Brace) { - return !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL); + return !self.restrictions.contains(Restrictions::RESTRICTION_NO_STRUCT_LITERAL); } true } else { @@ -2812,7 +2831,7 @@ impl<'a> Parser<'a> { return self.parse_if_let_expr(); } let lo = self.last_span.lo; - let cond = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let thn = try!(self.parse_block()); let mut els: Option<P<Expr>> = None; let mut hi = thn.span.hi; @@ -2830,7 +2849,7 @@ impl<'a> Parser<'a> { try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat_nopanic()); try!(self.expect(&token::Eq)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let thn = try!(self.parse_block()); let (hi, els) = if try!(self.eat_keyword(keywords::Else) ){ let expr = try!(self.parse_else_expr()); @@ -2889,7 +2908,7 @@ impl<'a> Parser<'a> { let lo = self.last_span.lo; let pat = try!(self.parse_pat_nopanic()); try!(self.expect_keyword(keywords::In)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let loop_block = try!(self.parse_block()); let hi = self.last_span.hi; @@ -2902,7 +2921,7 @@ impl<'a> Parser<'a> { return self.parse_while_let_expr(opt_ident); } let lo = self.last_span.lo; - let cond = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let body = try!(self.parse_block()); let hi = body.span.hi; return Ok(self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident))); @@ -2914,7 +2933,7 @@ impl<'a> Parser<'a> { try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat_nopanic()); try!(self.expect(&token::Eq)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let body = try!(self.parse_block()); let hi = body.span.hi; return Ok(self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident))); @@ -2929,7 +2948,7 @@ impl<'a> Parser<'a> { fn parse_match_expr(&mut self) -> PResult<P<Expr>> { let lo = self.last_span.lo; - let discriminant = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); try!(self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace))); let mut arms: Vec<Arm> = Vec::new(); while self.token != token::CloseDelim(token::Brace) { @@ -2941,6 +2960,8 @@ impl<'a> Parser<'a> { } pub fn parse_arm_nopanic(&mut self) -> PResult<Arm> { + maybe_whole!(no_clone self, NtArm); + let attrs = self.parse_outer_attributes(); let pats = try!(self.parse_pats()); let mut guard = None; @@ -2948,7 +2969,7 @@ impl<'a> Parser<'a> { guard = Some(try!(self.parse_expr_nopanic())); } try!(self.expect(&token::FatArrow)); - let expr = try!(self.parse_expr_res(RESTRICTION_STMT_EXPR)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); let require_comma = !classify::expr_is_simple_block(&*expr) @@ -2970,7 +2991,7 @@ impl<'a> Parser<'a> { /// Parse an expression pub fn parse_expr_nopanic(&mut self) -> PResult<P<Expr>> { - return self.parse_expr_res(UNRESTRICTED); + return self.parse_expr_res(Restrictions::UNRESTRICTED); } /// Parse an expression, subject to the given restrictions @@ -3153,16 +3174,25 @@ impl<'a> Parser<'a> { fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> { if self.is_path_start() { let lo = self.span.lo; - let path = try!(self.parse_path(LifetimeAndTypesWithColons)); + let (qself, path) = if try!(self.eat_lt()) { + // Parse a qualified path + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); + (Some(qself), path) + } else { + // Parse an unqualified path + (None, try!(self.parse_path(LifetimeAndTypesWithColons))) + }; let hi = self.last_span.hi; - Ok(self.mk_expr(lo, hi, ExprPath(None, path))) + Ok(self.mk_expr(lo, hi, ExprPath(qself, path))) } else { self.parse_literal_maybe_minus() } } fn is_path_start(&self) -> bool { - (self.token == token::ModSep || self.token.is_ident() || self.token.is_path()) + (self.token == token::Lt || self.token == token::ModSep + || self.token.is_ident() || self.token.is_path()) && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False) } @@ -3238,25 +3268,44 @@ impl<'a> Parser<'a> { pat = try!(self.parse_pat_ident(BindByValue(MutImmutable))); } } else { - // Parse as a general path - let path = try!(self.parse_path(LifetimeAndTypesWithColons)); + let (qself, path) = if try!(self.eat_lt()) { + // Parse a qualified path + let (qself, path) = + try!(self.parse_qualified_path(QPathParsingMode::NoParameters)); + (Some(qself), path) + } else { + // Parse an unqualified path + (None, try!(self.parse_path(LifetimeAndTypesWithColons))) + }; match self.token { token::DotDotDot => { // Parse range let hi = self.last_span.hi; - let begin = self.mk_expr(lo, hi, ExprPath(None, path)); + let begin = self.mk_expr(lo, hi, ExprPath(qself, path)); try!(self.bump()); let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); } token::OpenDelim(token::Brace) => { - // Parse struct pattern + if qself.is_some() { + let span = self.span; + self.span_err(span, + "unexpected `{` after qualified path"); + self.abort_if_errors(); + } + // Parse struct pattern try!(self.bump()); let (fields, etc) = try!(self.parse_pat_fields()); try!(self.bump()); pat = PatStruct(path, fields, etc); } token::OpenDelim(token::Paren) => { + if qself.is_some() { + let span = self.span; + self.span_err(span, + "unexpected `(` after qualified path"); + self.abort_if_errors(); + } // Parse tuple struct or enum pattern if self.look_ahead(1, |t| *t == token::DotDot) { // This is a "top constructor only" pat @@ -3273,6 +3322,10 @@ impl<'a> Parser<'a> { pat = PatEnum(path, Some(args)); } } + _ if qself.is_some() => { + // Parse qualified path + pat = PatQPath(qself.unwrap(), path); + } _ => { // Parse nullary enum pat = PatEnum(path, Some(vec![])); @@ -3514,7 +3567,7 @@ impl<'a> Parser<'a> { } // Remainder are line-expr stmts. - let e = try!(self.parse_expr_res(RESTRICTION_STMT_EXPR)); + let e = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID)) } } @@ -3523,7 +3576,7 @@ impl<'a> Parser<'a> { /// Is this expression a successfully-parsed statement? fn expr_is_complete(&mut self, e: &Expr) -> bool { - self.restrictions.contains(RESTRICTION_STMT_EXPR) && + self.restrictions.contains(Restrictions::RESTRICTION_STMT_EXPR) && !classify::expr_requires_semi_to_be_stmt(e) } @@ -3807,8 +3860,37 @@ impl<'a> Parser<'a> { fn parse_generic_values_after_lt(&mut self) -> PResult<(Vec<ast::Lifetime>, Vec<P<Ty>>, Vec<P<TypeBinding>>)> { + let span_lo = self.span.lo; let lifetimes = try!(self.parse_lifetimes(token::Comma)); + let missing_comma = !lifetimes.is_empty() && + !self.token.is_like_gt() && + self.last_token + .as_ref().map_or(true, + |x| &**x != &token::Comma); + + if missing_comma { + + let msg = format!("expected `,` or `>` after lifetime \ + name, found `{}`", + self.this_token_to_string()); + self.span_err(self.span, &msg); + + let span_hi = self.span.hi; + let span_hi = if self.parse_ty_nopanic().is_ok() { + self.span.hi + } else { + span_hi + }; + + let msg = format!("did you mean a single argument type &'a Type, \ + or did you mean the comma-separated arguments \ + 'a, Type?"); + self.span_note(mk_sp(span_lo, span_hi), &msg); + + self.abort_if_errors() + } + // First parse types. let (types, returned) = try!(self.parse_seq_to_gt_or_return( Some(token::Comma), @@ -4304,6 +4386,8 @@ impl<'a> Parser<'a> { /// Parse an impl item. pub fn parse_impl_item(&mut self) -> PResult<P<ImplItem>> { + maybe_whole!(no_clone self, NtImplItem); + let mut attrs = self.parse_outer_attributes(); let lo = self.span.lo; let vis = try!(self.parse_visibility()); @@ -4313,6 +4397,14 @@ impl<'a> Parser<'a> { let typ = try!(self.parse_ty_sum()); try!(self.expect(&token::Semi)); (name, TypeImplItem(typ)) + } else if try!(self.eat_keyword(keywords::Const)) { + let name = try!(self.parse_ident()); + try!(self.expect(&token::Colon)); + let typ = try!(self.parse_ty_sum()); + try!(self.expect(&token::Eq)); + let expr = try!(self.parse_expr_nopanic()); + try!(self.commit_expr_expecting(&expr, token::Semi)); + (name, ConstImplItem(typ, expr)) } else { let (name, inner_attrs, node) = try!(self.parse_impl_method(vis)); attrs.extend(inner_attrs.into_iter()); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 2bb74944ce9..0106de913bb 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -173,6 +173,14 @@ pub enum Token { } impl Token { + /// Returns `true` if the token starts with '>'. + pub fn is_like_gt(&self) -> bool { + match *self { + BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true, + _ => false, + } + } + /// Returns `true` if the token can appear at the start of an expression. pub fn can_begin_expr(&self) -> bool { match *self { @@ -373,6 +381,10 @@ pub enum Nonterminal { NtMeta(P<ast::MetaItem>), NtPath(Box<ast::Path>), NtTT(P<ast::TokenTree>), // needs P'ed to break a circularity + // These is not exposed to macros, but is used by quasiquote. + NtArm(ast::Arm), + NtImplItem(P<ast::ImplItem>), + NtTraitItem(P<ast::TraitItem>), } impl fmt::Debug for Nonterminal { @@ -388,6 +400,9 @@ impl fmt::Debug for Nonterminal { NtMeta(..) => f.pad("NtMeta(..)"), NtPath(..) => f.pad("NtPath(..)"), NtTT(..) => f.pad("NtTT(..)"), + NtArm(..) => f.pad("NtArm(..)"), + NtImplItem(..) => f.pad("NtImplItem(..)"), + NtTraitItem(..) => f.pad("NtTraitItem(..)"), } } } @@ -746,7 +761,7 @@ pub fn fresh_mark() -> ast::Mrk { } #[cfg(test)] -mod test { +mod tests { use super::*; use ast; use ext::mtwt; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b56ecf7ae80..828a334a40f 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -66,7 +66,6 @@ pub struct State<'a> { cur_cmnt_and_lit: CurrentCommentAndLiteral, boxes: Vec<pp::Breaks>, ann: &'a (PpAnn+'a), - encode_idents_with_hygiene: bool, } pub fn rust_printer<'a>(writer: Box<Write+'a>) -> State<'a> { @@ -87,7 +86,6 @@ pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>, }, boxes: Vec::new(), ann: ann, - encode_idents_with_hygiene: false, } } @@ -179,7 +177,6 @@ impl<'a> State<'a> { }, boxes: Vec::new(), ann: ann, - encode_idents_with_hygiene: false, } } } @@ -290,103 +287,99 @@ pub fn token_to_string(tok: &Token) -> String { token::SpecialVarNt(var) => format!("${}", var.as_str()), token::Interpolated(ref nt) => match *nt { - token::NtExpr(ref e) => expr_to_string(&**e), - token::NtMeta(ref e) => meta_item_to_string(&**e), - token::NtTy(ref e) => ty_to_string(&**e), - token::NtPath(ref e) => path_to_string(&**e), - token::NtItem(..) => "an interpolated item".to_string(), - token::NtBlock(..) => "an interpolated block".to_string(), - token::NtStmt(..) => "an interpolated statement".to_string(), - token::NtPat(..) => "an interpolated pattern".to_string(), - token::NtIdent(..) => "an interpolated identifier".to_string(), - token::NtTT(..) => "an interpolated tt".to_string(), + token::NtExpr(ref e) => expr_to_string(&**e), + token::NtMeta(ref e) => meta_item_to_string(&**e), + token::NtTy(ref e) => ty_to_string(&**e), + token::NtPath(ref e) => path_to_string(&**e), + token::NtItem(..) => "an interpolated item".to_string(), + token::NtBlock(..) => "an interpolated block".to_string(), + token::NtStmt(..) => "an interpolated statement".to_string(), + token::NtPat(..) => "an interpolated pattern".to_string(), + token::NtIdent(..) => "an interpolated identifier".to_string(), + token::NtTT(..) => "an interpolated tt".to_string(), + token::NtArm(..) => "an interpolated arm".to_string(), + token::NtImplItem(..) => "an interpolated impl item".to_string(), + token::NtTraitItem(..) => "an interpolated trait item".to_string(), } } } -// FIXME (Issue #16472): the thing_to_string_impls macro should go away -// after we revise the syntax::ext::quote::ToToken impls to go directly -// to token-trees instead of thing -> string -> token-trees. - -macro_rules! thing_to_string_impls { - ($to_string:ident) => { - pub fn ty_to_string(ty: &ast::Ty) -> String { - $to_string(|s| s.print_type(ty)) + to_string(|s| s.print_type(ty)) } pub fn bounds_to_string(bounds: &[ast::TyParamBound]) -> String { - $to_string(|s| s.print_bounds("", bounds)) + to_string(|s| s.print_bounds("", bounds)) } pub fn pat_to_string(pat: &ast::Pat) -> String { - $to_string(|s| s.print_pat(pat)) + to_string(|s| s.print_pat(pat)) } pub fn arm_to_string(arm: &ast::Arm) -> String { - $to_string(|s| s.print_arm(arm)) + to_string(|s| s.print_arm(arm)) } pub fn expr_to_string(e: &ast::Expr) -> String { - $to_string(|s| s.print_expr(e)) + to_string(|s| s.print_expr(e)) } pub fn lifetime_to_string(e: &ast::Lifetime) -> String { - $to_string(|s| s.print_lifetime(e)) + to_string(|s| s.print_lifetime(e)) } pub fn tt_to_string(tt: &ast::TokenTree) -> String { - $to_string(|s| s.print_tt(tt)) + to_string(|s| s.print_tt(tt)) } pub fn tts_to_string(tts: &[ast::TokenTree]) -> String { - $to_string(|s| s.print_tts(tts)) + to_string(|s| s.print_tts(tts)) } pub fn stmt_to_string(stmt: &ast::Stmt) -> String { - $to_string(|s| s.print_stmt(stmt)) + to_string(|s| s.print_stmt(stmt)) } pub fn attr_to_string(attr: &ast::Attribute) -> String { - $to_string(|s| s.print_attribute(attr)) + to_string(|s| s.print_attribute(attr)) } pub fn item_to_string(i: &ast::Item) -> String { - $to_string(|s| s.print_item(i)) + to_string(|s| s.print_item(i)) } pub fn impl_item_to_string(i: &ast::ImplItem) -> String { - $to_string(|s| s.print_impl_item(i)) + to_string(|s| s.print_impl_item(i)) } pub fn trait_item_to_string(i: &ast::TraitItem) -> String { - $to_string(|s| s.print_trait_item(i)) + to_string(|s| s.print_trait_item(i)) } pub fn generics_to_string(generics: &ast::Generics) -> String { - $to_string(|s| s.print_generics(generics)) + to_string(|s| s.print_generics(generics)) } pub fn where_clause_to_string(i: &ast::WhereClause) -> String { - $to_string(|s| s.print_where_clause(i)) + to_string(|s| s.print_where_clause(i)) } pub fn fn_block_to_string(p: &ast::FnDecl) -> String { - $to_string(|s| s.print_fn_block_args(p)) + to_string(|s| s.print_fn_block_args(p)) } pub fn path_to_string(p: &ast::Path) -> String { - $to_string(|s| s.print_path(p, false, 0)) + to_string(|s| s.print_path(p, false, 0)) } pub fn ident_to_string(id: &ast::Ident) -> String { - $to_string(|s| s.print_ident(*id)) + to_string(|s| s.print_ident(*id)) } pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident, opt_explicit_self: Option<&ast::ExplicitSelf_>, generics: &ast::Generics) -> String { - $to_string(|s| { + to_string(|s| { try!(s.head("")); try!(s.print_fn(decl, unsafety, abi::Rust, Some(name), generics, opt_explicit_self, ast::Inherited)); @@ -396,7 +389,7 @@ pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ide } pub fn block_to_string(blk: &ast::Block) -> String { - $to_string(|s| { + to_string(|s| { // containing cbox, will be closed by print-block at } try!(s.cbox(indent_unit)); // head-ibox, will be closed by print-block after { @@ -406,59 +399,31 @@ pub fn block_to_string(blk: &ast::Block) -> String { } pub fn meta_item_to_string(mi: &ast::MetaItem) -> String { - $to_string(|s| s.print_meta_item(mi)) + to_string(|s| s.print_meta_item(mi)) } pub fn attribute_to_string(attr: &ast::Attribute) -> String { - $to_string(|s| s.print_attribute(attr)) + to_string(|s| s.print_attribute(attr)) } pub fn lit_to_string(l: &ast::Lit) -> String { - $to_string(|s| s.print_literal(l)) + to_string(|s| s.print_literal(l)) } pub fn explicit_self_to_string(explicit_self: &ast::ExplicitSelf_) -> String { - $to_string(|s| s.print_explicit_self(explicit_self, ast::MutImmutable).map(|_| {})) + to_string(|s| s.print_explicit_self(explicit_self, ast::MutImmutable).map(|_| {})) } pub fn variant_to_string(var: &ast::Variant) -> String { - $to_string(|s| s.print_variant(var)) + to_string(|s| s.print_variant(var)) } pub fn arg_to_string(arg: &ast::Arg) -> String { - $to_string(|s| s.print_arg(arg)) + to_string(|s| s.print_arg(arg)) } pub fn mac_to_string(arg: &ast::Mac) -> String { - $to_string(|s| s.print_mac(arg, ::parse::token::Paren)) -} - -} } - -thing_to_string_impls! { to_string } - -// FIXME (Issue #16472): the whole `with_hygiene` mod should go away -// after we revise the syntax::ext::quote::ToToken impls to go directly -// to token-trees instea of thing -> string -> token-trees. - -pub mod with_hygiene { - use abi; - use ast; - use std::io; - use super::indent_unit; - - // This function is the trick that all the rest of the routines - // hang on. - pub fn to_string_hyg<F>(f: F) -> String where - F: FnOnce(&mut super::State) -> io::Result<()>, - { - super::to_string(move |s| { - s.encode_idents_with_hygiene = true; - f(s) - }) - } - - thing_to_string_impls! { to_string_hyg } + to_string(|s| s.print_mac(arg, ::parse::token::Paren)) } pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> String { @@ -796,6 +761,26 @@ impl<'a> State<'a> { } } + fn print_associated_const(&mut self, + ident: ast::Ident, + ty: &ast::Ty, + default: Option<&ast::Expr>, + vis: ast::Visibility) + -> io::Result<()> + { + try!(word(&mut self.s, &visibility_qualified(vis, ""))); + try!(self.word_space("const")); + try!(self.print_ident(ident)); + try!(self.word_space(":")); + try!(self.print_type(ty)); + if let Some(expr) = default { + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_expr(expr)); + } + word(&mut self.s, ";") + } + fn print_associated_type(&mut self, ident: ast::Ident, bounds: Option<&ast::TyParamBounds>, @@ -1268,6 +1253,11 @@ impl<'a> State<'a> { try!(self.maybe_print_comment(ti.span.lo)); try!(self.print_outer_attributes(&ti.attrs)); match ti.node { + ast::ConstTraitItem(ref ty, ref default) => { + try!(self.print_associated_const(ti.ident, &ty, + default.as_ref().map(|expr| &**expr), + ast::Inherited)); + } ast::MethodTraitItem(ref sig, ref body) => { if body.is_some() { try!(self.head("")); @@ -1294,6 +1284,9 @@ impl<'a> State<'a> { try!(self.maybe_print_comment(ii.span.lo)); try!(self.print_outer_attributes(&ii.attrs)); match ii.node { + ast::ConstImplItem(ref ty, ref expr) => { + try!(self.print_associated_const(ii.ident, &ty, Some(&expr), ii.vis)); + } ast::MethodImplItem(ref sig, ref body) => { try!(self.head("")); try!(self.print_method_sig(ii.ident, sig, ii.vis)); @@ -2005,12 +1998,7 @@ impl<'a> State<'a> { } pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> { - if self.encode_idents_with_hygiene { - let encoded = ident.encode_with_hygiene(); - try!(word(&mut self.s, &encoded[..])) - } else { - try!(word(&mut self.s, &token::get_ident(ident))) - } + try!(word(&mut self.s, &token::get_ident(ident))); self.ann.post(self, NodeIdent(&ident)) } @@ -2191,6 +2179,9 @@ impl<'a> State<'a> { } } } + ast::PatQPath(ref qself, ref path) => { + try!(self.print_qpath(path, qself, false)); + } ast::PatStruct(ref path, ref fields, etc) => { try!(self.print_path(path, true, 0)); try!(self.nbsp()); @@ -3007,7 +2998,7 @@ impl<'a> State<'a> { fn repeat(s: &str, n: usize) -> String { iter::repeat(s).take(n).collect() } #[cfg(test)] -mod test { +mod tests { use super::*; use ast; diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs index 6adeb30a94e..d016eb39239 100644 --- a/src/libsyntax/util/parser_testing.rs +++ b/src/libsyntax/util/parser_testing.rs @@ -142,7 +142,7 @@ pub fn is_whitespace(c: char) -> bool { } #[cfg(test)] -mod test { +mod tests { use super::*; #[test] fn eqmodws() { diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index 153f9d4a26d..5353d12b678 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -204,7 +204,7 @@ impl<T> MoveMap<T> for SmallVector<T> { } #[cfg(test)] -mod test { +mod tests { use super::*; #[test] diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 4c70fc9f81f..6cf791b10be 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -464,6 +464,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { } } } + PatQPath(ref qself, ref path) => { + visitor.visit_ty(&qself.ty); + visitor.visit_path(path, pattern.id) + } PatStruct(ref path, ref fields, _) => { visitor.visit_path(path, pattern.id); for field in fields { @@ -619,6 +623,12 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai visitor.visit_attribute(attr); } match trait_item.node { + ConstTraitItem(ref ty, ref default) => { + visitor.visit_ty(ty); + if let Some(ref expr) = *default { + visitor.visit_expr(expr); + } + } MethodTraitItem(ref sig, None) => { visitor.visit_explicit_self(&sig.explicit_self); visitor.visit_generics(&sig.generics); @@ -641,6 +651,10 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt visitor.visit_attribute(attr); } match impl_item.node { + ConstImplItem(ref ty, ref expr) => { + visitor.visit_ty(ty); + visitor.visit_expr(expr); + } MethodImplItem(ref sig, ref body) => { visitor.visit_fn(FkMethod(impl_item.ident, sig, Some(impl_item.vis)), &sig.decl, body, impl_item.span, impl_item.id); |
