diff --git a/Cargo.lock b/Cargo.lock index fa749e5e3aebe..5b446c517c915 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3325,8 +3325,6 @@ dependencies = [ "log", "rustc", "rustc_data_structures", - "rustc_errors", - "syntax", "syntax_pos", ] @@ -3348,7 +3346,6 @@ dependencies = [ "log", "memmap", "num_cpus", - "parking_lot 0.9.0", "rustc", "rustc_apfloat", "rustc_codegen_utils", @@ -3367,7 +3364,6 @@ dependencies = [ name = "rustc_codegen_utils" version = "0.0.0" dependencies = [ - "flate2", "log", "punycode", "rustc", @@ -3562,7 +3558,6 @@ name = "rustc_mir" version = "0.0.0" dependencies = [ "arena", - "byteorder", "either", "graphviz", "log", @@ -3615,7 +3610,6 @@ name = "rustc_plugin_impl" version = "0.0.0" dependencies = [ "rustc", - "rustc_errors", "rustc_metadata", "syntax", "syntax_pos", @@ -3639,7 +3633,6 @@ version = "0.0.0" dependencies = [ "arena", "bitflags", - "indexmap", "log", "rustc", "rustc_data_structures", @@ -3661,7 +3654,6 @@ dependencies = [ "rustc_codegen_utils", "rustc_data_structures", "rustc_target", - "rustc_typeck", "serde_json", "syntax", "syntax_pos", @@ -3692,9 +3684,7 @@ checksum = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee" name = "rustc_traits" version = "0.0.0" dependencies = [ - "bitflags", "chalk-engine", - "graphviz", "log", "rustc", "rustc_data_structures", @@ -4057,7 +4047,6 @@ version = "0.0.0" dependencies = [ "alloc", "backtrace", - "cc", "cfg-if", "compiler_builtins", "core", @@ -4242,7 +4231,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_lexer", - "rustc_macros", "rustc_target", "scoped-tls", "serialize", @@ -4258,7 +4246,6 @@ dependencies = [ "log", "rustc_data_structures", "rustc_errors", - "rustc_lexer", "rustc_target", "smallvec", "syntax", diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index da49223dfb285..0a9e076ec5869 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -2581,7 +2581,7 @@ pub trait Iterator { /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (x * x).cmp(&y)), Ordering::Equal); /// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn cmp_by(mut self, other: I, mut cmp: F) -> Ordering where Self: Sized, @@ -2664,7 +2664,7 @@ pub trait Iterator { /// Some(Ordering::Greater) /// ); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn partial_cmp_by(mut self, other: I, mut partial_cmp: F) -> Option where Self: Sized, @@ -2729,7 +2729,7 @@ pub trait Iterator { /// /// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y)); /// ``` - #[unstable(feature = "iter_order_by", issue = "0")] + #[unstable(feature = "iter_order_by", issue = "64295")] fn eq_by(mut self, other: I, mut eq: F) -> bool where Self: Sized, diff --git a/src/librustc_ast_borrowck/Cargo.toml b/src/librustc_ast_borrowck/Cargo.toml index 024b2640e1e8e..40c4c1fc3fee6 100644 --- a/src/librustc_ast_borrowck/Cargo.toml +++ b/src/librustc_ast_borrowck/Cargo.toml @@ -12,11 +12,9 @@ doctest = false [dependencies] log = "0.4" -syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } # for "clarity", rename the graphviz crate to dot; graphviz within `borrowck` # refers to the borrowck-specific graphviz adapter traits. dot = { path = "../libgraphviz", package = "graphviz" } rustc = { path = "../librustc" } -errors = { path = "../librustc_errors", package = "rustc_errors" } rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index bc028d6624279..2e3666e609657 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -17,7 +17,6 @@ memmap = "0.6" log = "0.4.5" libc = "0.2.44" jobserver = "0.1.11" -parking_lot = "0.9" tempfile = "3.1" rustc_serialize = { path = "../libserialize", package = "serialize" } diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml index 89b50c5daccae..c8c219d039a73 100644 --- a/src/librustc_codegen_utils/Cargo.toml +++ b/src/librustc_codegen_utils/Cargo.toml @@ -10,7 +10,6 @@ path = "lib.rs" test = false [dependencies] -flate2 = "1.0" log = "0.4" punycode = "0.4.0" rustc-demangle = "0.1.16" diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index 0691390bead4b..f296753a0304c 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -24,6 +24,5 @@ rustc_lexer = { path = "../librustc_lexer" } rustc_serialize = { path = "../libserialize", package = "serialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -byteorder = { version = "1.3" } rustc_apfloat = { path = "../librustc_apfloat" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/librustc_mir/error_codes.rs b/src/librustc_mir/error_codes.rs index ba299e9463b8d..196bcf147f8f8 100644 --- a/src/librustc_mir/error_codes.rs +++ b/src/librustc_mir/error_codes.rs @@ -1993,6 +1993,69 @@ fn get_owned_iterator() -> IntoIter { ``` "##, +E0524: r##" +A variable which requires unique access is being used in more than one closure +at the same time. + +Erroneous code example: + +```compile_fail,E0524 +fn set(x: &mut isize) { + *x += 4; +} + +fn dragoooon(x: &mut isize) { + let mut c1 = || set(x); + let mut c2 = || set(x); // error! + + c2(); + c1(); +} +``` + +To solve this issue, multiple solutions are available. First, is it required +for this variable to be used in more than one closure at a time? If it is the +case, use reference counted types such as `Rc` (or `Arc` if it runs +concurrently): + +``` +use std::rc::Rc; +use std::cell::RefCell; + +fn set(x: &mut isize) { + *x += 4; +} + +fn dragoooon(x: &mut isize) { + let x = Rc::new(RefCell::new(x)); + let y = Rc::clone(&x); + let mut c1 = || { let mut x2 = x.borrow_mut(); set(&mut x2); }; + let mut c2 = || { let mut x2 = y.borrow_mut(); set(&mut x2); }; // ok! + + c2(); + c1(); +} +``` + +If not, just run closures one at a time: + +``` +fn set(x: &mut isize) { + *x += 4; +} + +fn dragoooon(x: &mut isize) { + { // This block isn't necessary since non-lexical lifetimes, it's just to + // make it more clear. + let mut c1 = || set(&mut *x); + c1(); + } // `c1` has been dropped here so we're free to use `x` again! + let mut c2 = || set(&mut *x); + c2(); +} +``` +"##, + E0595: r##" #### Note: this error code is no longer emitted by the compiler. @@ -2393,7 +2456,6 @@ There are some known bugs that trigger this message. // E0385, // {} in an aliasable location E0493, // destructors cannot be evaluated at compile-time E0521, // borrowed data escapes outside of closure - E0524, // two closures require unique access to `..` at the same time E0526, // shuffle indices are not constant E0594, // cannot assign to {} // E0598, // lifetime of {} is too short to guarantee its contents can be... diff --git a/src/librustc_plugin/Cargo.toml b/src/librustc_plugin/Cargo.toml index 84a743ed1ad7d..3f11430dc82cb 100644 --- a/src/librustc_plugin/Cargo.toml +++ b/src/librustc_plugin/Cargo.toml @@ -15,4 +15,3 @@ rustc = { path = "../librustc" } rustc_metadata = { path = "../librustc_metadata" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rustc_errors = { path = "../librustc_errors" } diff --git a/src/librustc_resolve/Cargo.toml b/src/librustc_resolve/Cargo.toml index 548f982fe3bf0..936e72ef2c571 100644 --- a/src/librustc_resolve/Cargo.toml +++ b/src/librustc_resolve/Cargo.toml @@ -12,7 +12,6 @@ doctest = false [dependencies] bitflags = "1.0" -indexmap = "1" log = "0.4" syntax = { path = "../libsyntax" } rustc = { path = "../librustc" } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 3900a3dbb3872..73ad0670659b5 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -18,7 +18,7 @@ use syntax::ext::base::{self, InvocationRes, Indeterminate, SpecialDerives}; use syntax::ext::base::{MacroKind, SyntaxExtension}; use syntax::ext::expand::{AstFragment, AstFragmentKind, Invocation, InvocationKind}; use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind}; -use syntax::ext::tt::macro_rules; +use syntax::ext::compile_declarative_macro; use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name}; use syntax::feature_gate::GateIssue; use syntax::symbol::{Symbol, kw, sym}; @@ -843,7 +843,7 @@ impl<'a> Resolver<'a> { /// Compile the macro into a `SyntaxExtension` and possibly replace it with a pre-defined /// extension partially or entirely for built-in macros and legacy plugin macros. crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension { - let mut result = macro_rules::compile( + let mut result = compile_declarative_macro( &self.session.parse_sess, self.session.features_untracked(), item, edition ); diff --git a/src/librustc_save_analysis/Cargo.toml b/src/librustc_save_analysis/Cargo.toml index 88bb76d2aba3a..b89c83d630b7d 100644 --- a/src/librustc_save_analysis/Cargo.toml +++ b/src/librustc_save_analysis/Cargo.toml @@ -14,7 +14,6 @@ rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_target = { path = "../librustc_target" } -rustc_typeck = { path = "../librustc_typeck" } serde_json = "1" syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml index bb28ac839a544..b86a3a5e9639b 100644 --- a/src/librustc_traits/Cargo.toml +++ b/src/librustc_traits/Cargo.toml @@ -9,8 +9,6 @@ name = "rustc_traits" path = "lib.rs" [dependencies] -bitflags = "1.0" -graphviz = { path = "../libgraphviz" } log = { version = "0.4" } rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index af1d2402f88e7..ee4b367b5c5b9 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -53,9 +53,6 @@ fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.7.0", features = ['rustc-dep-of-std', 'alloc'] } -[build-dependencies] -cc = "1.0" - [features] default = ["std_detect_file_io", "std_detect_dlsym_getauxval"] diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b5265fe369e83..8933f027a065f 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -114,6 +114,9 @@ pub struct Metadata(fs_imp::FileAttr); /// information like the entry's path and possibly other metadata can be /// learned. /// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// /// # Errors /// /// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent @@ -1962,6 +1965,9 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { /// /// [changes]: ../io/index.html#platform-specific-behavior /// +/// The order in which this iterator returns entries is platform and filesystem +/// dependent. +/// /// # Errors /// /// This function will return an error in the following situations, but is not @@ -1994,6 +2000,25 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +/// +/// ```rust,no_run +/// use std::{fs, io}; +/// +/// fn main() -> io::Result<()> { +/// let mut entries = fs::read_dir(".")? +/// .map(|res| res.map(|e| e.path())) +/// .collect::, io::Error>>()?; +/// +/// // The order in which `read_dir` returns entries is not guaranteed. If reproducible +/// // ordering is required the entries should be explicitly sorted. +/// +/// entries.sort(); +/// +/// // The entries have now been sorted by their path. +/// +/// Ok(()) +/// } +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn read_dir>(path: P) -> io::Result { fs_imp::readdir(path.as_ref()).map(ReadDir) diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index f85b5d632f16b..e92c0d1c58e41 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -236,8 +236,8 @@ impl LocalKey { #[stable(feature = "rust1", since = "1.0.0")] pub fn with(&'static self, f: F) -> R where F: FnOnce(&T) -> R { - self.try_with(f).expect("cannot access a TLS value during or \ - after it is destroyed") + self.try_with(f).expect("cannot access a Thread Local Storage value \ + during or after destruction") } /// Acquires a reference to the value in this TLS key. diff --git a/src/libsyntax/Cargo.toml b/src/libsyntax/Cargo.toml index d4a9acc1569b4..196cf4d9dfae8 100644 --- a/src/libsyntax/Cargo.toml +++ b/src/libsyntax/Cargo.toml @@ -19,6 +19,5 @@ syntax_pos = { path = "../libsyntax_pos" } errors = { path = "../librustc_errors", package = "rustc_errors" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_lexer = { path = "../librustc_lexer" } -rustc_macros = { path = "../librustc_macros" } rustc_target = { path = "../librustc_target" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b80c530731dfc..2cd802f22d306 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -6,7 +6,7 @@ use crate::config::StripUnconfigured; use crate::ext::base::*; use crate::ext::proc_macro::{collect_derives, MarkAttrs}; use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind}; -use crate::ext::tt::macro_rules::annotate_err_with_kind; +use crate::ext::mbe::macro_rules::annotate_err_with_kind; use crate::ext::placeholders::{placeholder, PlaceholderExpander}; use crate::feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err}; use crate::mut_visit::*; @@ -115,8 +115,8 @@ macro_rules! ast_fragments { } } - impl<'a> MacResult for crate::ext::tt::macro_rules::ParserAnyMacro<'a> { - $(fn $make_ast(self: Box>) + impl<'a> MacResult for crate::ext::mbe::macro_rules::ParserAnyMacro<'a> { + $(fn $make_ast(self: Box>) -> Option<$AstTy> { Some(self.make(AstFragmentKind::$Kind).$make_ast()) })* diff --git a/src/libsyntax/ext/mbe.rs b/src/libsyntax/ext/mbe.rs new file mode 100644 index 0000000000000..a87da791c9b4f --- /dev/null +++ b/src/libsyntax/ext/mbe.rs @@ -0,0 +1,166 @@ +//! This module implements declarative macros: old `macro_rules` and the newer +//! `macro`. Declarative macros are also known as "macro by example", and that's +//! why we call this module `mbe`. For external documentation, prefer the +//! official terminology: "declarative macros". + +crate mod transcribe; +crate mod macro_check; +crate mod macro_parser; +crate mod macro_rules; +crate mod quoted; + +use crate::ast; +use crate::parse::token::{self, Token, TokenKind}; +use crate::tokenstream::{DelimSpan}; + +use syntax_pos::{BytePos, Span}; + +use rustc_data_structures::sync::Lrc; + +/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note +/// that the delimiter itself might be `NoDelim`. +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] +struct Delimited { + delim: token::DelimToken, + tts: Vec, +} + +impl Delimited { + /// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter. + fn open_tt(&self, span: Span) -> TokenTree { + let open_span = if span.is_dummy() { + span + } else { + span.with_hi(span.lo() + BytePos(self.delim.len() as u32)) + }; + TokenTree::token(token::OpenDelim(self.delim), open_span) + } + + /// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter. + fn close_tt(&self, span: Span) -> TokenTree { + let close_span = if span.is_dummy() { + span + } else { + span.with_lo(span.hi() - BytePos(self.delim.len() as u32)) + }; + TokenTree::token(token::CloseDelim(self.delim), close_span) + } +} + +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] +struct SequenceRepetition { + /// The sequence of token trees + tts: Vec, + /// The optional separator + separator: Option, + /// Whether the sequence can be repeated zero (*), or one or more times (+) + kleene: KleeneToken, + /// The number of `Match`s that appear in the sequence (and subsequences) + num_captures: usize, +} + +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] +struct KleeneToken { + span: Span, + op: KleeneOp, +} + +impl KleeneToken { + fn new(op: KleeneOp, span: Span) -> KleeneToken { + KleeneToken { span, op } + } +} + +/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star) +/// for token sequences. +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] +enum KleeneOp { + /// Kleene star (`*`) for zero or more repetitions + ZeroOrMore, + /// Kleene plus (`+`) for one or more repetitions + OneOrMore, + /// Kleene optional (`?`) for zero or one reptitions + ZeroOrOne, +} + +/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)` +/// are "first-class" token trees. Useful for parsing macros. +#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)] +enum TokenTree { + Token(Token), + Delimited(DelimSpan, Lrc), + /// A kleene-style repetition sequence + Sequence(DelimSpan, Lrc), + /// e.g., `$var` + MetaVar(Span, ast::Ident), + /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. + MetaVarDecl( + Span, + ast::Ident, /* name to bind */ + ast::Ident, /* kind of nonterminal */ + ), +} + +impl TokenTree { + /// Return the number of tokens in the tree. + fn len(&self) -> usize { + match *self { + TokenTree::Delimited(_, ref delimed) => match delimed.delim { + token::NoDelim => delimed.tts.len(), + _ => delimed.tts.len() + 2, + }, + TokenTree::Sequence(_, ref seq) => seq.tts.len(), + _ => 0, + } + } + + /// Returns `true` if the given token tree is delimited. + fn is_delimited(&self) -> bool { + match *self { + TokenTree::Delimited(..) => true, + _ => false, + } + } + + /// Returns `true` if the given token tree is a token of the given kind. + fn is_token(&self, expected_kind: &TokenKind) -> bool { + match self { + TokenTree::Token(Token { kind: actual_kind, .. }) => actual_kind == expected_kind, + _ => false, + } + } + + /// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences. + fn get_tt(&self, index: usize) -> TokenTree { + match (self, index) { + (&TokenTree::Delimited(_, ref delimed), _) if delimed.delim == token::NoDelim => { + delimed.tts[index].clone() + } + (&TokenTree::Delimited(span, ref delimed), _) => { + if index == 0 { + return delimed.open_tt(span.open); + } + if index == delimed.tts.len() + 1 { + return delimed.close_tt(span.close); + } + delimed.tts[index - 1].clone() + } + (&TokenTree::Sequence(_, ref seq), _) => seq.tts[index].clone(), + _ => panic!("Cannot expand a token tree"), + } + } + + /// Retrieves the `TokenTree`'s span. + fn span(&self) -> Span { + match *self { + TokenTree::Token(Token { span, .. }) + | TokenTree::MetaVar(span, _) + | TokenTree::MetaVarDecl(span, _, _) => span, + TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(), + } + } + + fn token(kind: TokenKind, span: Span) -> TokenTree { + TokenTree::Token(Token::new(kind, span)) + } +} diff --git a/src/libsyntax/ext/tt/macro_check.rs b/src/libsyntax/ext/mbe/macro_check.rs similarity index 99% rename from src/libsyntax/ext/tt/macro_check.rs rename to src/libsyntax/ext/mbe/macro_check.rs index 5af97199902e6..97074f5cbe46c 100644 --- a/src/libsyntax/ext/tt/macro_check.rs +++ b/src/libsyntax/ext/mbe/macro_check.rs @@ -106,7 +106,7 @@ //! bound. use crate::ast::NodeId; use crate::early_buffered_lints::BufferedEarlyLintId; -use crate::ext::tt::quoted::{KleeneToken, TokenTree}; +use crate::ext::mbe::{KleeneToken, TokenTree}; use crate::parse::token::TokenKind; use crate::parse::token::{DelimToken, Token}; use crate::parse::ParseSess; @@ -196,7 +196,7 @@ struct MacroState<'a> { /// - `node_id` is used to emit lints /// - `span` is used when no spans are available /// - `lhses` and `rhses` should have the same length and represent the macro definition -pub fn check_meta_variables( +pub(super) fn check_meta_variables( sess: &ParseSess, node_id: NodeId, span: Span, diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/mbe/macro_parser.rs similarity index 98% rename from src/libsyntax/ext/tt/macro_parser.rs rename to src/libsyntax/ext/mbe/macro_parser.rs index dbf14daa30e75..b51384d3b15e1 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/mbe/macro_parser.rs @@ -70,12 +70,12 @@ //! eof: [a $( a )* a b ยท] //! ``` -pub use NamedMatch::*; -pub use ParseResult::*; +crate use NamedMatch::*; +crate use ParseResult::*; use TokenTreeOrTokenTreeSlice::*; use crate::ast::{Ident, Name}; -use crate::ext::tt::quoted::{self, TokenTree}; +use crate::ext::mbe::{self, TokenTree}; use crate::parse::{Directory, ParseSess}; use crate::parse::parser::{Parser, PathStyle}; use crate::parse::token::{self, DocComment, Nonterminal, Token}; @@ -195,7 +195,7 @@ struct MatcherPos<'root, 'tt> { // `None`. /// The KleeneOp of this sequence if we are in a repetition. - seq_op: Option, + seq_op: Option, /// The separator if we are in a repetition. sep: Option, @@ -267,7 +267,7 @@ impl<'root, 'tt> DerefMut for MatcherPosHandle<'root, 'tt> { } /// Represents the possible results of an attempted parse. -pub enum ParseResult { +crate enum ParseResult { /// Parsed successfully. Success(T), /// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected @@ -279,10 +279,10 @@ pub enum ParseResult { /// A `ParseResult` where the `Success` variant contains a mapping of `Ident`s to `NamedMatch`es. /// This represents the mapping of metavars to the token trees they bind to. -pub type NamedParseResult = ParseResult>; +crate type NamedParseResult = ParseResult>; /// Count how many metavars are named in the given matcher `ms`. -pub fn count_names(ms: &[TokenTree]) -> usize { +pub(super) fn count_names(ms: &[TokenTree]) -> usize { ms.iter().fold(0, |count, elt| { count + match *elt { TokenTree::Sequence(_, ref seq) => seq.num_captures, @@ -352,7 +352,7 @@ fn initial_matcher_pos<'root, 'tt>(ms: &'tt [TokenTree], open: Span) -> MatcherP /// only on the nesting depth of `ast::TTSeq`s in the originating /// token tree it was derived from. #[derive(Debug, Clone)] -pub enum NamedMatch { +crate enum NamedMatch { MatchedSeq(Lrc, DelimSpan), MatchedNonterminal(Lrc), } @@ -415,7 +415,7 @@ fn nameize>( /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For /// other tokens, this is "unexpected token...". -pub fn parse_failure_msg(tok: &Token) -> String { +crate fn parse_failure_msg(tok: &Token) -> String { match tok.kind { token::Eof => "unexpected end of macro invocation".to_string(), _ => format!( @@ -532,7 +532,7 @@ fn inner_parse_loop<'root, 'tt>( } // We don't need a separator. Move the "dot" back to the beginning of the matcher // and try to match again UNLESS we are only allowed to have _one_ repetition. - else if item.seq_op != Some(quoted::KleeneOp::ZeroOrOne) { + else if item.seq_op != Some(mbe::KleeneOp::ZeroOrOne) { item.match_cur = item.match_lo; item.idx = 0; cur_items.push(item); @@ -555,8 +555,8 @@ fn inner_parse_loop<'root, 'tt>( // implicitly disallowing OneOrMore from having 0 matches here. Thus, that will // result in a "no rules expected token" error by virtue of this matcher not // working. - if seq.kleene.op == quoted::KleeneOp::ZeroOrMore - || seq.kleene.op == quoted::KleeneOp::ZeroOrOne + if seq.kleene.op == mbe::KleeneOp::ZeroOrMore + || seq.kleene.op == mbe::KleeneOp::ZeroOrOne { let mut new_item = item.clone(); new_item.match_cur += seq.num_captures; @@ -648,7 +648,7 @@ fn inner_parse_loop<'root, 'tt>( /// - `directory`: Information about the file locations (needed for the black-box parser) /// - `recurse_into_modules`: Whether or not to recurse into modules (needed for the black-box /// parser) -pub fn parse( +pub(super) fn parse( sess: &ParseSess, tts: TokenStream, ms: &[TokenTree], diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/mbe/macro_rules.rs similarity index 90% rename from src/libsyntax/ext/tt/macro_rules.rs rename to src/libsyntax/ext/mbe/macro_rules.rs index b27e9c543377a..816baadb12fbb 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/mbe/macro_rules.rs @@ -4,12 +4,12 @@ use crate::edition::Edition; use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander}; use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind}; use crate::ext::expand::{AstFragment, AstFragmentKind}; -use crate::ext::tt::macro_check; -use crate::ext::tt::macro_parser::{parse, parse_failure_msg}; -use crate::ext::tt::macro_parser::{Error, Failure, Success}; -use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq}; -use crate::ext::tt::quoted; -use crate::ext::tt::transcribe::transcribe; +use crate::ext::mbe; +use crate::ext::mbe::macro_check; +use crate::ext::mbe::macro_parser::{parse, parse_failure_msg}; +use crate::ext::mbe::macro_parser::{Error, Failure, Success}; +use crate::ext::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedParseResult}; +use crate::ext::mbe::transcribe::transcribe; use crate::feature_gate::Features; use crate::parse::parser::Parser; use crate::parse::token::TokenKind::*; @@ -35,7 +35,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \ `literal`, `path`, `meta`, `tt`, `item` and `vis`"; -pub struct ParserAnyMacro<'a> { +crate struct ParserAnyMacro<'a> { parser: Parser<'a>, /// Span of the expansion site of the macro this parser is for @@ -45,7 +45,11 @@ pub struct ParserAnyMacro<'a> { arm_span: Span, } -pub fn annotate_err_with_kind(err: &mut DiagnosticBuilder<'_>, kind: AstFragmentKind, span: Span) { +crate fn annotate_err_with_kind( + err: &mut DiagnosticBuilder<'_>, + kind: AstFragmentKind, + span: Span, +) { match kind { AstFragmentKind::Ty => { err.span_label(span, "this macro call doesn't expand to a type"); @@ -58,7 +62,7 @@ pub fn annotate_err_with_kind(err: &mut DiagnosticBuilder<'_>, kind: AstFragment } impl<'a> ParserAnyMacro<'a> { - pub fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { + crate fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self; let fragment = panictry!(parser.parse_ast_fragment(kind, true).map_err(|mut e| { if parser.token == token::Eof && e.message().ends_with(", found ``") { @@ -131,8 +135,8 @@ struct MacroRulesMacroExpander { name: ast::Ident, span: Span, transparency: Transparency, - lhses: Vec, - rhses: Vec, + lhses: Vec, + rhses: Vec, valid: bool, } @@ -165,8 +169,8 @@ fn generic_extension<'cx>( name: ast::Ident, transparency: Transparency, arg: TokenStream, - lhses: &[quoted::TokenTree], - rhses: &[quoted::TokenTree], + lhses: &[mbe::TokenTree], + rhses: &[mbe::TokenTree], ) -> Box { if cx.trace_macros() { trace_macros_note(cx, sp, format!("expanding `{}! {{ {} }}`", name, arg)); @@ -178,7 +182,7 @@ fn generic_extension<'cx>( for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers let lhs_tt = match *lhs { - quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..], + mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], _ => cx.span_bug(sp, "malformed macro lhs"), }; @@ -186,7 +190,7 @@ fn generic_extension<'cx>( Success(named_matches) => { let rhs = match rhses[i] { // ignore delimiters - quoted::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), + mbe::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), _ => cx.span_bug(sp, "malformed macro rhs"), }; let arm_span = rhses[i].span(); @@ -254,7 +258,7 @@ fn generic_extension<'cx>( for lhs in lhses { // try each arm's matchers let lhs_tt = match *lhs { - quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..], + mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], _ => continue, }; match TokenTree::parse(cx, lhs_tt, arg.clone()) { @@ -284,8 +288,8 @@ fn generic_extension<'cx>( // // Holy self-referential! -/// Converts a `macro_rules!` invocation into a syntax extension. -pub fn compile( +/// Converts a macro item into a syntax extension. +pub fn compile_declarative_macro( sess: &ParseSess, features: &Features, def: &ast::Item, @@ -308,32 +312,32 @@ pub fn compile( // ...quasiquoting this would be nice. // These spans won't matter, anyways let argument_gram = vec![ - quoted::TokenTree::Sequence( + mbe::TokenTree::Sequence( DelimSpan::dummy(), - Lrc::new(quoted::SequenceRepetition { + Lrc::new(mbe::SequenceRepetition { tts: vec![ - quoted::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec), - quoted::TokenTree::token(token::FatArrow, def.span), - quoted::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec), + mbe::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec), + mbe::TokenTree::token(token::FatArrow, def.span), + mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec), ], separator: Some(Token::new( if body.legacy { token::Semi } else { token::Comma }, def.span, )), - kleene: quoted::KleeneToken::new(quoted::KleeneOp::OneOrMore, def.span), + kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span), num_captures: 2, }), ), // to phase into semicolon-termination instead of semicolon-separation - quoted::TokenTree::Sequence( + mbe::TokenTree::Sequence( DelimSpan::dummy(), - Lrc::new(quoted::SequenceRepetition { - tts: vec![quoted::TokenTree::token( + Lrc::new(mbe::SequenceRepetition { + tts: vec![mbe::TokenTree::token( if body.legacy { token::Semi } else { token::Comma }, def.span, )], separator: None, - kleene: quoted::KleeneToken::new(quoted::KleeneOp::ZeroOrMore, def.span), + kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span), num_captures: 0, }), ), @@ -363,7 +367,7 @@ pub fn compile( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = quoted::parse( + let tt = mbe::quoted::parse( tt.clone().into(), true, sess, @@ -380,7 +384,7 @@ pub fn compile( } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) - .collect::>(), + .collect::>(), _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"), }; @@ -390,7 +394,7 @@ pub fn compile( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return quoted::parse( + return mbe::quoted::parse( tt.clone().into(), false, sess, @@ -405,7 +409,7 @@ pub fn compile( } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) - .collect::>(), + .collect::>(), _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"), }; @@ -450,11 +454,11 @@ fn check_lhs_nt_follows( sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], - lhs: "ed::TokenTree, + lhs: &mbe::TokenTree, ) -> bool { // lhs is going to be like TokenTree::Delimited(...), where the // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. - if let quoted::TokenTree::Delimited(_, ref tts) = *lhs { + if let mbe::TokenTree::Delimited(_, ref tts) = *lhs { check_matcher(sess, features, attrs, &tts.tts) } else { let msg = "invalid macro matcher; matchers must be contained in balanced delimiters"; @@ -467,8 +471,8 @@ fn check_lhs_nt_follows( /// Checks that the lhs contains no repetition which could match an empty token /// tree, because then the matcher would hang indefinitely. -fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool { - use quoted::TokenTree; +fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { + use mbe::TokenTree; for tt in tts { match *tt { TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (), @@ -482,8 +486,8 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool { && seq.tts.iter().all(|seq_tt| match *seq_tt { TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis, TokenTree::Sequence(_, ref sub_seq) => { - sub_seq.kleene.op == quoted::KleeneOp::ZeroOrMore - || sub_seq.kleene.op == quoted::KleeneOp::ZeroOrOne + sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore + || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne } _ => false, }) @@ -502,9 +506,9 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool { true } -fn check_rhs(sess: &ParseSess, rhs: "ed::TokenTree) -> bool { +fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool { match *rhs { - quoted::TokenTree::Delimited(..) => return true, + mbe::TokenTree::Delimited(..) => return true, _ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited"), } false @@ -514,7 +518,7 @@ fn check_matcher( sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], - matcher: &[quoted::TokenTree], + matcher: &[mbe::TokenTree], ) -> bool { let first_sets = FirstSets::new(matcher); let empty_suffix = TokenSet::empty(); @@ -546,8 +550,8 @@ struct FirstSets { } impl FirstSets { - fn new(tts: &[quoted::TokenTree]) -> FirstSets { - use quoted::TokenTree; + fn new(tts: &[mbe::TokenTree]) -> FirstSets { + use mbe::TokenTree; let mut sets = FirstSets { first: FxHashMap::default() }; build_recur(&mut sets, tts); @@ -594,8 +598,8 @@ impl FirstSets { // Reverse scan: Sequence comes before `first`. if subfirst.maybe_empty - || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore - || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne + || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore + || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne { // If sequence is potentially empty, then // union them (preserving first emptiness). @@ -615,8 +619,8 @@ impl FirstSets { // walks forward over `tts` until all potential FIRST tokens are // identified. - fn first(&self, tts: &[quoted::TokenTree]) -> TokenSet { - use quoted::TokenTree; + fn first(&self, tts: &[mbe::TokenTree]) -> TokenSet { + use mbe::TokenTree; let mut first = TokenSet::empty(); for tt in tts.iter() { @@ -652,8 +656,8 @@ impl FirstSets { assert!(first.maybe_empty); first.add_all(subfirst); if subfirst.maybe_empty - || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore - || seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne + || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore + || seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne { // Continue scanning for more first // tokens, but also make sure we @@ -674,7 +678,7 @@ impl FirstSets { } } -// A set of `quoted::TokenTree`s, which may include `TokenTree::Match`s +// A set of `mbe::TokenTree`s, which may include `TokenTree::Match`s // (for macro-by-example syntactic variables). It also carries the // `maybe_empty` flag; that is true if and only if the matcher can // match an empty token sequence. @@ -686,7 +690,7 @@ impl FirstSets { // (Notably, we must allow for *-op to occur zero times.) #[derive(Clone, Debug)] struct TokenSet { - tokens: Vec, + tokens: Vec, maybe_empty: bool, } @@ -698,13 +702,13 @@ impl TokenSet { // Returns the set `{ tok }` for the single-token (and thus // non-empty) sequence [tok]. - fn singleton(tok: quoted::TokenTree) -> Self { + fn singleton(tok: mbe::TokenTree) -> Self { TokenSet { tokens: vec![tok], maybe_empty: false } } // Changes self to be the set `{ tok }`. // Since `tok` is always present, marks self as non-empty. - fn replace_with(&mut self, tok: quoted::TokenTree) { + fn replace_with(&mut self, tok: mbe::TokenTree) { self.tokens.clear(); self.tokens.push(tok); self.maybe_empty = false; @@ -719,7 +723,7 @@ impl TokenSet { } // Adds `tok` to the set for `self`, marking sequence as non-empy. - fn add_one(&mut self, tok: quoted::TokenTree) { + fn add_one(&mut self, tok: mbe::TokenTree) { if !self.tokens.contains(&tok) { self.tokens.push(tok); } @@ -727,7 +731,7 @@ impl TokenSet { } // Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.) - fn add_one_maybe(&mut self, tok: quoted::TokenTree) { + fn add_one_maybe(&mut self, tok: mbe::TokenTree) { if !self.tokens.contains(&tok) { self.tokens.push(tok); } @@ -768,10 +772,10 @@ fn check_matcher_core( features: &Features, attrs: &[ast::Attribute], first_sets: &FirstSets, - matcher: &[quoted::TokenTree], + matcher: &[mbe::TokenTree], follow: &TokenSet, ) -> TokenSet { - use quoted::TokenTree; + use mbe::TokenTree; let mut last = TokenSet::empty(); @@ -946,8 +950,8 @@ fn check_matcher_core( last } -fn token_can_be_followed_by_any(tok: "ed::TokenTree) -> bool { - if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok { +fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { + if let mbe::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok { frag_can_be_followed_by_any(frag_spec.name) } else { // (Non NT's can always be followed by anthing in matchers.) @@ -993,8 +997,8 @@ enum IsInFollow { /// break macros that were relying on that binary operator as a /// separator. // when changing this do not forget to update doc/book/macros.md! -fn is_in_follow(tok: "ed::TokenTree, frag: Symbol) -> IsInFollow { - use quoted::TokenTree; +fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow { + use mbe::TokenTree; if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok { // closing a token tree can never be matched by any fragment; @@ -1112,10 +1116,10 @@ fn has_legal_fragment_specifier( sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], - tok: "ed::TokenTree, + tok: &mbe::TokenTree, ) -> Result<(), String> { debug!("has_legal_fragment_specifier({:?})", tok); - if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok { + if let mbe::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok { let frag_span = tok.span(); if !is_legal_fragment_specifier(sess, features, attrs, frag_spec.name, frag_span) { return Err(frag_spec.to_string()); @@ -1156,14 +1160,27 @@ fn is_legal_fragment_specifier( } } -fn quoted_tt_to_string(tt: "ed::TokenTree) -> String { +fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match *tt { - quoted::TokenTree::Token(ref token) => crate::print::pprust::token_to_string(&token), - quoted::TokenTree::MetaVar(_, name) => format!("${}", name), - quoted::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), + mbe::TokenTree::Token(ref token) => crate::print::pprust::token_to_string(&token), + mbe::TokenTree::MetaVar(_, name) => format!("${}", name), + mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), _ => panic!( - "unexpected quoted::TokenTree::{{Sequence or Delimited}} \ + "unexpected mbe::TokenTree::{{Sequence or Delimited}} \ in follow set checker" ), } } + +impl TokenTree { + /// Use this token tree as a matcher to parse given tts. + fn parse(cx: &ExtCtxt<'_>, mtch: &[mbe::TokenTree], tts: TokenStream) + -> NamedParseResult { + // `None` is because we're not interpolating + let directory = Directory { + path: Cow::from(cx.current_expansion.module.directory.as_path()), + ownership: cx.current_expansion.directory_ownership, + }; + parse(cx.parse_sess(), tts, mtch, Some(directory), true) + } +} diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/mbe/quoted.rs similarity index 67% rename from src/libsyntax/ext/tt/quoted.rs rename to src/libsyntax/ext/mbe/quoted.rs index cad94a0e4c120..3952e29a5f0d1 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/mbe/quoted.rs @@ -1,179 +1,19 @@ use crate::ast; use crate::ast::NodeId; -use crate::ext::tt::macro_parser; +use crate::ext::mbe::macro_parser; +use crate::ext::mbe::{TokenTree, KleeneOp, KleeneToken, SequenceRepetition, Delimited}; use crate::feature_gate::Features; -use crate::parse::token::{self, Token, TokenKind}; +use crate::parse::token::{self, Token}; use crate::parse::ParseSess; use crate::print::pprust; use crate::symbol::kw; -use crate::tokenstream::{self, DelimSpan}; +use crate::tokenstream; -use syntax_pos::{edition::Edition, BytePos, Span}; +use syntax_pos::{edition::Edition, Span}; use rustc_data_structures::sync::Lrc; use std::iter::Peekable; -/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note -/// that the delimiter itself might be `NoDelim`. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] -pub struct Delimited { - pub delim: token::DelimToken, - pub tts: Vec, -} - -impl Delimited { - /// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter. - pub fn open_tt(&self, span: Span) -> TokenTree { - let open_span = if span.is_dummy() { - span - } else { - span.with_hi(span.lo() + BytePos(self.delim.len() as u32)) - }; - TokenTree::token(token::OpenDelim(self.delim), open_span) - } - - /// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter. - pub fn close_tt(&self, span: Span) -> TokenTree { - let close_span = if span.is_dummy() { - span - } else { - span.with_lo(span.hi() - BytePos(self.delim.len() as u32)) - }; - TokenTree::token(token::CloseDelim(self.delim), close_span) - } -} - -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug)] -pub struct SequenceRepetition { - /// The sequence of token trees - pub tts: Vec, - /// The optional separator - pub separator: Option, - /// Whether the sequence can be repeated zero (*), or one or more times (+) - pub kleene: KleeneToken, - /// The number of `Match`s that appear in the sequence (and subsequences) - pub num_captures: usize, -} - -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)] -pub struct KleeneToken { - pub span: Span, - pub op: KleeneOp, -} - -impl KleeneToken { - pub fn new(op: KleeneOp, span: Span) -> KleeneToken { - KleeneToken { span, op } - } -} - -/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star) -/// for token sequences. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub enum KleeneOp { - /// Kleene star (`*`) for zero or more repetitions - ZeroOrMore, - /// Kleene plus (`+`) for one or more repetitions - OneOrMore, - /// Kleene optional (`?`) for zero or one reptitions - ZeroOrOne, -} - -/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)` -/// are "first-class" token trees. Useful for parsing macros. -#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)] -pub enum TokenTree { - Token(Token), - Delimited(DelimSpan, Lrc), - /// A kleene-style repetition sequence - Sequence(DelimSpan, Lrc), - /// e.g., `$var` - MetaVar(Span, ast::Ident), - /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. - MetaVarDecl( - Span, - ast::Ident, /* name to bind */ - ast::Ident, /* kind of nonterminal */ - ), -} - -impl TokenTree { - /// Return the number of tokens in the tree. - pub fn len(&self) -> usize { - match *self { - TokenTree::Delimited(_, ref delimed) => match delimed.delim { - token::NoDelim => delimed.tts.len(), - _ => delimed.tts.len() + 2, - }, - TokenTree::Sequence(_, ref seq) => seq.tts.len(), - _ => 0, - } - } - - /// Returns `true` if the given token tree contains no other tokens. This is vacuously true for - /// single tokens or metavar/decls, but may be false for delimited trees or sequences. - pub fn is_empty(&self) -> bool { - match *self { - TokenTree::Delimited(_, ref delimed) => match delimed.delim { - token::NoDelim => delimed.tts.is_empty(), - _ => false, - }, - TokenTree::Sequence(_, ref seq) => seq.tts.is_empty(), - _ => true, - } - } - - /// Returns `true` if the given token tree is delimited. - pub fn is_delimited(&self) -> bool { - match *self { - TokenTree::Delimited(..) => true, - _ => false, - } - } - - /// Returns `true` if the given token tree is a token of the given kind. - pub fn is_token(&self, expected_kind: &TokenKind) -> bool { - match self { - TokenTree::Token(Token { kind: actual_kind, .. }) => actual_kind == expected_kind, - _ => false, - } - } - - /// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences. - pub fn get_tt(&self, index: usize) -> TokenTree { - match (self, index) { - (&TokenTree::Delimited(_, ref delimed), _) if delimed.delim == token::NoDelim => { - delimed.tts[index].clone() - } - (&TokenTree::Delimited(span, ref delimed), _) => { - if index == 0 { - return delimed.open_tt(span.open); - } - if index == delimed.tts.len() + 1 { - return delimed.close_tt(span.close); - } - delimed.tts[index - 1].clone() - } - (&TokenTree::Sequence(_, ref seq), _) => seq.tts[index].clone(), - _ => panic!("Cannot expand a token tree"), - } - } - - /// Retrieves the `TokenTree`'s span. - pub fn span(&self) -> Span { - match *self { - TokenTree::Token(Token { span, .. }) - | TokenTree::MetaVar(span, _) - | TokenTree::MetaVarDecl(span, _, _) => span, - TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(), - } - } - - crate fn token(kind: TokenKind, span: Span) -> TokenTree { - TokenTree::Token(Token::new(kind, span)) - } -} - /// Takes a `tokenstream::TokenStream` and returns a `Vec`. Specifically, this /// takes a generic `TokenStream`, such as is used in the rest of the compiler, and returns a /// collection of `TokenTree` for use in parsing a macro. @@ -195,7 +35,7 @@ impl TokenTree { /// # Returns /// /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`. -pub fn parse( +pub(super) fn parse( input: tokenstream::TokenStream, expect_matchers: bool, sess: &ParseSess, diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/mbe/transcribe.rs similarity index 93% rename from src/libsyntax/ext/tt/transcribe.rs rename to src/libsyntax/ext/mbe/transcribe.rs index f9c07e3a2e4ff..ba818ebd35c7f 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/mbe/transcribe.rs @@ -1,7 +1,7 @@ use crate::ast::{Ident, Mac}; use crate::ext::base::ExtCtxt; -use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; -use crate::ext::tt::quoted; +use crate::ext::mbe; +use crate::ext::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; use crate::mut_visit::{self, MutVisitor}; use crate::parse::token::{self, NtTT, Token}; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; @@ -38,22 +38,22 @@ impl Marker { /// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`). enum Frame { - Delimited { forest: Lrc, idx: usize, span: DelimSpan }, - Sequence { forest: Lrc, idx: usize, sep: Option }, + Delimited { forest: Lrc, idx: usize, span: DelimSpan }, + Sequence { forest: Lrc, idx: usize, sep: Option }, } impl Frame { /// Construct a new frame around the delimited set of tokens. - fn new(tts: Vec) -> Frame { - let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts }); + fn new(tts: Vec) -> Frame { + let forest = Lrc::new(mbe::Delimited { delim: token::NoDelim, tts }); Frame::Delimited { forest, idx: 0, span: DelimSpan::dummy() } } } impl Iterator for Frame { - type Item = quoted::TokenTree; + type Item = mbe::TokenTree; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { match *self { Frame::Delimited { ref forest, ref mut idx, .. } => { *idx += 1; @@ -90,7 +90,7 @@ impl Iterator for Frame { pub(super) fn transcribe( cx: &ExtCtxt<'_>, interp: &FxHashMap, - src: Vec, + src: Vec, transparency: Transparency, ) -> TokenStream { // Nothing for us to transcribe... @@ -178,7 +178,7 @@ pub(super) fn transcribe( // We are descending into a sequence. We first make sure that the matchers in the RHS // and the matches in `interp` have the same shape. Otherwise, either the caller or the // macro writer has made a mistake. - seq @ quoted::TokenTree::Sequence(..) => { + seq @ mbe::TokenTree::Sequence(..) => { match lockstep_iter_size(&seq, interp, &repeats) { LockstepIterSize::Unconstrained => { cx.span_fatal( @@ -199,7 +199,7 @@ pub(super) fn transcribe( LockstepIterSize::Constraint(len, _) => { // We do this to avoid an extra clone above. We know that this is a // sequence already. - let (sp, seq) = if let quoted::TokenTree::Sequence(sp, seq) = seq { + let (sp, seq) = if let mbe::TokenTree::Sequence(sp, seq) = seq { (sp, seq) } else { unreachable!() @@ -207,7 +207,7 @@ pub(super) fn transcribe( // Is the repetition empty? if len == 0 { - if seq.kleene.op == quoted::KleeneOp::OneOrMore { + if seq.kleene.op == mbe::KleeneOp::OneOrMore { // FIXME: this really ought to be caught at macro definition // time... It happens when the Kleene operator in the matcher and // the body for the same meta-variable do not match. @@ -232,7 +232,7 @@ pub(super) fn transcribe( } // Replace the meta-var with the matched token tree from the invocation. - quoted::TokenTree::MetaVar(mut sp, mut ident) => { + mbe::TokenTree::MetaVar(mut sp, mut ident) => { // Find the matched nonterminal from the macro invocation, and use it to replace // the meta-var. if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { @@ -269,7 +269,7 @@ pub(super) fn transcribe( // We will produce all of the results of the inside of the `Delimited` and then we will // jump back out of the Delimited, pop the result_stack and add the new results back to // the previous results (from outside the Delimited). - quoted::TokenTree::Delimited(mut span, delimited) => { + mbe::TokenTree::Delimited(mut span, delimited) => { marker.visit_delim_span(&mut span); stack.push(Frame::Delimited { forest: delimited, idx: 0, span }); result_stack.push(mem::take(&mut result)); @@ -277,14 +277,14 @@ pub(super) fn transcribe( // Nothing much to do here. Just push the token to the result, being careful to // preserve syntax context. - quoted::TokenTree::Token(token) => { + mbe::TokenTree::Token(token) => { let mut tt = TokenTree::Token(token); marker.visit_tt(&mut tt); result.push(tt.into()); } // There should be no meta-var declarations in the invocation of a macro. - quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), + mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), } } } @@ -368,11 +368,11 @@ impl LockstepIterSize { /// `lookup_cur_matched` will return `None`, which is why this still works even in the presnece of /// multiple nested matcher sequences. fn lockstep_iter_size( - tree: "ed::TokenTree, + tree: &mbe::TokenTree, interpolations: &FxHashMap, repeats: &[(usize, usize)], ) -> LockstepIterSize { - use quoted::TokenTree; + use mbe::TokenTree; match *tree { TokenTree::Delimited(_, ref delimed) => { delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| { diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index b4ae1e87bca28..b0833010fe071 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -162,19 +162,14 @@ pub mod ext { mod proc_macro_server; pub use syntax_pos::hygiene; + pub use mbe::macro_rules::compile_declarative_macro; pub mod allocator; pub mod base; pub mod build; pub mod expand; pub mod proc_macro; - pub mod tt { - pub mod transcribe; - pub mod macro_check; - pub mod macro_parser; - pub mod macro_rules; - pub mod quoted; - } + crate mod mbe; } pub mod early_buffered_lints; diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index d702038f54ec3..26cae2a8e7c42 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -13,9 +13,6 @@ //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking //! ownership of the original. -use crate::ext::base; -use crate::ext::tt::{macro_parser, quoted}; -use crate::parse::Directory; use crate::parse::token::{self, DelimToken, Token, TokenKind}; use crate::print::pprust; @@ -26,7 +23,6 @@ use rustc_data_structures::sync::Lrc; use rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; use smallvec::{SmallVec, smallvec}; -use std::borrow::Cow; use std::{fmt, iter, mem}; #[cfg(test)] @@ -63,17 +59,6 @@ where {} impl TokenTree { - /// Use this token tree as a matcher to parse given tts. - pub fn parse(cx: &base::ExtCtxt<'_>, mtch: &[quoted::TokenTree], tts: TokenStream) - -> macro_parser::NamedParseResult { - // `None` is because we're not interpolating - let directory = Directory { - path: Cow::from(cx.current_expansion.module.directory.as_path()), - ownership: cx.current_expansion.directory_ownership, - }; - macro_parser::parse(cx.parse_sess(), tts, mtch, Some(directory), true) - } - /// Checks if this TokenTree is equal to the other, regardless of span information. pub fn eq_unspanned(&self, other: &TokenTree) -> bool { match (self, other) { diff --git a/src/libsyntax_ext/Cargo.toml b/src/libsyntax_ext/Cargo.toml index 791ee94b4fa77..73310df305b32 100644 --- a/src/libsyntax_ext/Cargo.toml +++ b/src/libsyntax_ext/Cargo.toml @@ -18,4 +18,3 @@ rustc_target = { path = "../librustc_target" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rustc_lexer = { path = "../librustc_lexer" } diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index e0bea4ce54530..d9e4abf61c3dc 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -27,6 +27,7 @@ impl JsonFormatter { ty: &str, name: &str, evt: &str, + exec_time: Option<&TestExecTime>, stdout: Option>, extra: Option<&str>, ) -> io::Result<()> { @@ -34,6 +35,12 @@ impl JsonFormatter { r#"{{ "type": "{}", "name": "{}", "event": "{}""#, ty, name, evt ))?; + if let Some(exec_time) = exec_time { + self.write_message(&*format!( + r#", "exec_time": "{}""#, + exec_time + ))?; + } if let Some(stdout) = stdout { self.write_message(&*format!( r#", "stdout": "{}""#, @@ -69,6 +76,7 @@ impl OutputFormatter for JsonFormatter { &mut self, desc: &TestDesc, result: &TestResult, + exec_time: Option<&TestExecTime>, stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()> { @@ -78,24 +86,36 @@ impl OutputFormatter for JsonFormatter { None }; match *result { - TrOk => self.write_event("test", desc.name.as_slice(), "ok", stdout, None), + TrOk => { + self.write_event("test", desc.name.as_slice(), "ok", exec_time, stdout, None) + } - TrFailed => self.write_event("test", desc.name.as_slice(), "failed", stdout, None), + TrFailed => { + self.write_event("test", desc.name.as_slice(), "failed", exec_time, stdout, None) + } TrFailedMsg(ref m) => self.write_event( "test", desc.name.as_slice(), "failed", + exec_time, stdout, Some(&*format!(r#""message": "{}""#, EscapedString(m))), ), - TrIgnored => self.write_event("test", desc.name.as_slice(), "ignored", stdout, None), - - TrAllowedFail => { - self.write_event("test", desc.name.as_slice(), "allowed_failure", stdout, None) + TrIgnored => { + self.write_event("test", desc.name.as_slice(), "ignored", exec_time, stdout, None) } + TrAllowedFail => self.write_event( + "test", + desc.name.as_slice(), + "allowed_failure", + exec_time, + stdout, + None, + ), + TrBench(ref bs) => { let median = bs.ns_iter_summ.median as usize; let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize; diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index cc30b06e5ec38..e97cda76d2318 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -16,6 +16,7 @@ pub(crate) trait OutputFormatter { &mut self, desc: &TestDesc, result: &TestResult, + exec_time: Option<&TestExecTime>, stdout: &[u8], state: &ConsoleTestState, ) -> io::Result<()>; diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 88331406a64d0..184726c67d3fb 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -30,20 +30,20 @@ impl PrettyFormatter { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { - self.write_short_result("ok", term::color::GREEN) + pub fn write_ok(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> { + self.write_short_result("ok", term::color::GREEN, exec_time) } - pub fn write_failed(&mut self) -> io::Result<()> { - self.write_short_result("FAILED", term::color::RED) + pub fn write_failed(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> { + self.write_short_result("FAILED", term::color::RED, exec_time) } - pub fn write_ignored(&mut self) -> io::Result<()> { - self.write_short_result("ignored", term::color::YELLOW) + pub fn write_ignored(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> { + self.write_short_result("ignored", term::color::YELLOW, exec_time) } - pub fn write_allowed_fail(&mut self) -> io::Result<()> { - self.write_short_result("FAILED (allowed)", term::color::YELLOW) + pub fn write_allowed_fail(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> { + self.write_short_result("FAILED (allowed)", term::color::YELLOW, exec_time) } pub fn write_bench(&mut self) -> io::Result<()> { @@ -54,8 +54,12 @@ impl PrettyFormatter { &mut self, result: &str, color: term::color::Color, + exec_time: Option<&TestExecTime>, ) -> io::Result<()> { self.write_pretty(result, color)?; + if let Some(exec_time) = exec_time { + self.write_plain(format!(" {}", exec_time))?; + } self.write_plain("\n") } @@ -166,6 +170,7 @@ impl OutputFormatter for PrettyFormatter { &mut self, desc: &TestDesc, result: &TestResult, + exec_time: Option<&TestExecTime>, _: &[u8], _: &ConsoleTestState, ) -> io::Result<()> { @@ -174,10 +179,10 @@ impl OutputFormatter for PrettyFormatter { } match *result { - TrOk => self.write_ok(), - TrFailed | TrFailedMsg(_) => self.write_failed(), - TrIgnored => self.write_ignored(), - TrAllowedFail => self.write_allowed_fail(), + TrOk => self.write_ok(exec_time), + TrFailed | TrFailedMsg(_) => self.write_failed(exec_time), + TrIgnored => self.write_ignored(exec_time), + TrAllowedFail => self.write_allowed_fail(exec_time), TrBench(ref bs) => { self.write_bench()?; self.write_plain(&format!(": {}\n", fmt_bench_samples(bs))) diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index d10b0c5807d57..1812c20904c87 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -174,6 +174,7 @@ impl OutputFormatter for TerseFormatter { &mut self, desc: &TestDesc, result: &TestResult, + _: Option<&TestExecTime>, _: &[u8], _: &ConsoleTestState, ) -> io::Result<()> { diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 09d5fcc89520e..e441514e59738 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -378,6 +378,7 @@ pub struct TestOpts { pub format: OutputFormat, pub test_threads: Option, pub skip: Vec, + pub report_time: bool, pub options: Options, } @@ -460,6 +461,11 @@ fn optgroups() -> getopts::Options { "Enable nightly-only flags: unstable-options = Allow use of experimental features", "unstable-options", + ) + .optflag( + "", + "report-time", + "Show execution time of each test. Not available for --format=terse" ); return opts; } @@ -560,6 +566,13 @@ pub fn parse_opts(args: &[String]) -> Option { )); } + let report_time = matches.opt_present("report-time"); + if !allow_unstable && report_time { + return Some(Err( + "The \"report-time\" flag is only accepted on the nightly compiler".into(), + )); + } + let run_ignored = match (include_ignored, matches.opt_present("ignored")) { (true, true) => { return Some(Err( @@ -653,6 +666,7 @@ pub fn parse_opts(args: &[String]) -> Option { format, test_threads, skip: matches.opt_strs("skip"), + report_time, options: Options::new().display_output(matches.opt_present("show-output")), }; @@ -677,6 +691,16 @@ pub enum TestResult { unsafe impl Send for TestResult {} +/// The meassured execution time of a unit test. +#[derive(Clone, PartialEq)] +pub struct TestExecTime(Duration); + +impl fmt::Display for TestExecTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:.3}s", self.0.as_secs_f64()) + } +} + enum OutputLocation { Pretty(Box), Raw(T), @@ -736,17 +760,30 @@ impl ConsoleTestState { }) } - pub fn write_log>(&mut self, msg: S) -> io::Result<()> { - let msg = msg.as_ref(); + pub fn write_log( + &mut self, + msg: F, + ) -> io::Result<()> + where + S: AsRef, + F: FnOnce() -> S, + { match self.log_out { None => Ok(()), - Some(ref mut o) => o.write_all(msg.as_bytes()), + Some(ref mut o) => { + let msg = msg(); + let msg = msg.as_ref(); + o.write_all(msg.as_bytes()) + }, } } - pub fn write_log_result(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> { - self.write_log(format!( - "{} {}\n", + pub fn write_log_result(&mut self,test: &TestDesc, + result: &TestResult, + exec_time: Option<&TestExecTime>, + ) -> io::Result<()> { + self.write_log(|| format!( + "{} {}", match *result { TrOk => "ok".to_owned(), TrFailed => "failed".to_owned(), @@ -755,8 +792,12 @@ impl ConsoleTestState { TrAllowedFail => "failed (allowed)".to_owned(), TrBench(ref bs) => fmt_bench_samples(bs), }, - test.name - )) + test.name, + ))?; + if let Some(exec_time) = exec_time { + self.write_log(|| format!(" {}", exec_time))?; + } + self.write_log(|| "\n") } fn current_test_count(&self) -> usize { @@ -843,7 +884,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Res }; writeln!(output, "{}: {}", name, fntype)?; - st.write_log(format!("{} {}\n", fntype, name))?; + st.write_log(|| format!("{} {}\n", fntype, name))?; } fn plural(count: u32, s: &str) -> String { @@ -884,9 +925,9 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu TeFilteredOut(filtered_out) => Ok(st.filtered_out = filtered_out), TeWait(ref test) => out.write_test_start(test), TeTimeout(ref test) => out.write_timeout(test), - TeResult(test, result, stdout) => { - st.write_log_result(&test, &result)?; - out.write_result(&test, &result, &*stdout, &st)?; + TeResult(test, result, exec_time, stdout) => { + st.write_log_result(&test, &result, exec_time.as_ref())?; + out.write_result(&test, &result, exec_time.as_ref(), &*stdout, &st)?; match result { TrOk => { st.passed += 1; @@ -1004,12 +1045,12 @@ fn stdout_isatty() -> bool { pub enum TestEvent { TeFiltered(Vec), TeWait(TestDesc), - TeResult(TestDesc, TestResult, Vec), + TeResult(TestDesc, TestResult, Option, Vec), TeTimeout(TestDesc), TeFilteredOut(usize), } -pub type MonitorMsg = (TestDesc, TestResult, Vec); +pub type MonitorMsg = (TestDesc, TestResult, Option, Vec); struct Sink(Arc>>); impl Write for Sink { @@ -1105,8 +1146,8 @@ where let test = remaining.pop().unwrap(); callback(TeWait(test.desc.clone()))?; run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::No); - let (test, result, stdout) = rx.recv().unwrap(); - callback(TeResult(test, result, stdout))?; + let (test, result, exec_time, stdout) = rx.recv().unwrap(); + callback(TeResult(test, result, exec_time, stdout))?; } } else { while pending > 0 || !remaining.is_empty() { @@ -1135,10 +1176,10 @@ where } } - let (desc, result, stdout) = res.unwrap(); + let (desc, result, exec_time, stdout) = res.unwrap(); running_tests.remove(&desc); - callback(TeResult(desc, result, stdout))?; + callback(TeResult(desc, result, exec_time, stdout))?; pending -= 1; } } @@ -1148,8 +1189,8 @@ where for b in filtered_benchs { callback(TeWait(b.desc.clone()))?; run_test(opts, false, b, tx.clone(), Concurrent::No); - let (test, result, stdout) = rx.recv().unwrap(); - callback(TeResult(test, result, stdout))?; + let (test, result, exec_time, stdout) = rx.recv().unwrap(); + callback(TeResult(test, result, exec_time, stdout))?; } } Ok(()) @@ -1384,7 +1425,7 @@ pub fn run_test( && desc.should_panic != ShouldPanic::No; if force_ignore || desc.ignore || ignore_because_panic_abort { - monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap(); + monitor_ch.send((desc, TrIgnored, None, Vec::new())).unwrap(); return; } @@ -1392,6 +1433,7 @@ pub fn run_test( desc: TestDesc, monitor_ch: Sender, nocapture: bool, + report_time: bool, testfn: Box, concurrency: Concurrent, ) { @@ -1410,7 +1452,16 @@ pub fn run_test( None }; + let start = if report_time { + Some(Instant::now()) + } else { + None + }; let result = catch_unwind(AssertUnwindSafe(testfn)); + let exec_time = start.map(|start| { + let duration = start.elapsed(); + TestExecTime(duration) + }); if let Some((printio, panicio)) = oldio { io::set_print(printio); @@ -1420,7 +1471,7 @@ pub fn run_test( let test_result = calc_result(&desc, result); let stdout = data.lock().unwrap().to_vec(); monitor_ch - .send((desc.clone(), test_result, stdout)) + .send((desc.clone(), test_result, exec_time, stdout)) .unwrap(); }; @@ -1449,12 +1500,20 @@ pub fn run_test( } DynTestFn(f) => { let cb = move || __rust_begin_short_backtrace(f); - run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(cb), concurrency) + run_test_inner( + desc, + monitor_ch, + opts.nocapture, + opts.report_time, + Box::new(cb), + concurrency, + ) } StaticTestFn(f) => run_test_inner( desc, monitor_ch, opts.nocapture, + opts.report_time, Box::new(move || __rust_begin_short_backtrace(f)), concurrency, ), @@ -1702,7 +1761,7 @@ pub mod bench { }; let stdout = data.lock().unwrap().to_vec(); - monitor_ch.send((desc, test_result, stdout)).unwrap(); + monitor_ch.send((desc, test_result, None, stdout)).unwrap(); } pub fn run_once(f: F) diff --git a/src/libtest/tests.rs b/src/libtest/tests.rs index afc4217ec1ba2..13ac8eb91f411 100644 --- a/src/libtest/tests.rs +++ b/src/libtest/tests.rs @@ -23,6 +23,7 @@ impl TestOpts { format: OutputFormat::Pretty, test_threads: None, skip: vec![], + report_time: false, options: Options::new(), } } @@ -67,7 +68,7 @@ pub fn do_not_run_ignored_tests() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res != TrOk); } @@ -85,7 +86,7 @@ pub fn ignored_tests_result_in_ignored() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res == TrIgnored); } @@ -105,7 +106,7 @@ fn test_should_panic() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res == TrOk); } @@ -125,7 +126,7 @@ fn test_should_panic_good_message() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res == TrOk); } @@ -147,7 +148,7 @@ fn test_should_panic_bad_message() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected))); } @@ -165,10 +166,43 @@ fn test_should_panic_but_succeeds() { }; let (tx, rx) = channel(); run_test(&TestOpts::new(), false, desc, tx, Concurrent::No); - let (_, res, _) = rx.recv().unwrap(); + let (_, res, _, _) = rx.recv().unwrap(); assert!(res == TrFailed); } +fn report_time_test_template(report_time: bool) -> Option { + fn f() {} + let desc = TestDescAndFn { + desc: TestDesc { + name: StaticTestName("whatever"), + ignore: false, + should_panic: ShouldPanic::No, + allow_fail: false, + }, + testfn: DynTestFn(Box::new(f)), + }; + let test_opts = TestOpts { + report_time, + ..TestOpts::new() + }; + let (tx, rx) = channel(); + run_test(&test_opts, false, desc, tx, Concurrent::No); + let (_, _, exec_time, _) = rx.recv().unwrap(); + exec_time +} + +#[test] +fn test_should_not_report_time() { + let exec_time = report_time_test_template(false); + assert!(exec_time.is_none()); +} + +#[test] +fn test_should_report_time() { + let exec_time = report_time_test_template(true); + assert!(exec_time.is_some()); +} + #[test] fn parse_ignored_flag() { let args = vec![ diff --git a/src/test/ui/borrowck/borrowck-closures-mut-of-imm.rs b/src/test/ui/borrowck/borrowck-closures-mut-of-imm.rs index 24e06e3c4e666..d7e187a2b3958 100644 --- a/src/test/ui/borrowck/borrowck-closures-mut-of-imm.rs +++ b/src/test/ui/borrowck/borrowck-closures-mut-of-imm.rs @@ -1,10 +1,6 @@ // Tests that two closures cannot simultaneously have mutable // and immutable access to the variable. Issue #6801. -fn get(x: &isize) -> isize { - *x -} - fn set(x: &mut isize) { *x = 4; } diff --git a/src/test/ui/borrowck/borrowck-closures-mut-of-imm.stderr b/src/test/ui/borrowck/borrowck-closures-mut-of-imm.stderr index 3be7d725eda3a..784b903a5896a 100644 --- a/src/test/ui/borrowck/borrowck-closures-mut-of-imm.stderr +++ b/src/test/ui/borrowck/borrowck-closures-mut-of-imm.stderr @@ -1,17 +1,17 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-closures-mut-of-imm.rs:13:25 + --> $DIR/borrowck-closures-mut-of-imm.rs:9:25 | LL | let mut c1 = || set(&mut *x); | ^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-closures-mut-of-imm.rs:15:25 + --> $DIR/borrowck-closures-mut-of-imm.rs:11:25 | LL | let mut c2 = || set(&mut *x); | ^^^^^^^ cannot borrow as mutable error[E0524]: two closures require unique access to `x` at the same time - --> $DIR/borrowck-closures-mut-of-imm.rs:15:18 + --> $DIR/borrowck-closures-mut-of-imm.rs:11:18 | LL | let mut c1 = || set(&mut *x); | -- - first borrow occurs due to use of `x` in closure @@ -28,4 +28,5 @@ LL | c2(); c1(); error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0596`. +Some errors have detailed explanations: E0524, E0596. +For more information about an error, try `rustc --explain E0524`. diff --git a/src/test/ui/borrowck/borrowck-closures-mut-of-mut.stderr b/src/test/ui/borrowck/borrowck-closures-mut-of-mut.stderr index a174388712158..471173e595f47 100644 --- a/src/test/ui/borrowck/borrowck-closures-mut-of-mut.stderr +++ b/src/test/ui/borrowck/borrowck-closures-mut-of-mut.stderr @@ -15,3 +15,4 @@ LL | c2(); c1(); error: aborting due to previous error +For more information about this error, try `rustc --explain E0524`. diff --git a/src/test/ui/borrowck/borrowck-closures-unique.stderr b/src/test/ui/borrowck/borrowck-closures-unique.stderr index 9b53af4c01f59..2ed08b83c58b9 100644 --- a/src/test/ui/borrowck/borrowck-closures-unique.stderr +++ b/src/test/ui/borrowck/borrowck-closures-unique.stderr @@ -50,4 +50,5 @@ LL | let c1 = |y: &'static mut isize| x = y; error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0500`. +Some errors have detailed explanations: E0500, E0524. +For more information about an error, try `rustc --explain E0500`. diff --git a/src/test/ui/nll/closures-in-loops.stderr b/src/test/ui/nll/closures-in-loops.stderr index 7603f9650b54a..0b15d9bcfe68c 100644 --- a/src/test/ui/nll/closures-in-loops.stderr +++ b/src/test/ui/nll/closures-in-loops.stderr @@ -27,5 +27,5 @@ LL | v.push(|| *x = String::new()); error: aborting due to 3 previous errors -Some errors have detailed explanations: E0382, E0499. +Some errors have detailed explanations: E0382, E0499, E0524. For more information about an error, try `rustc --explain E0382`.