From 8f76f931c243dce31e2121a265ab0501b19504d5 Mon Sep 17 00:00:00 2001 From: Isaac Andrade Date: Mon, 2 May 2016 16:15:46 -0700 Subject: [PATCH 01/53] Add mention to RFC 940 in the Rust Reference. - It also keeps mentions to RFCs consistent with the format "RFC XXX" --- src/doc/reference.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index fcf9aefaba84..87eede0b7373 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -845,6 +845,20 @@ extern crate std; // equivalent to: extern crate std as std; extern crate std as ruststd; // linking to 'std' under another name ``` +When naming Rust crates, hyphens are disallowed. However, Cargo packages may +make use of them. In such case, when `cargo.toml` doesn't specify a crate name, +Cargo will transparently replace `-` with `_` (Refer to [RFC 940] for more +details). + +Here is an example: + +```{.ignore} +// Importing the Cargo package hello-world/ +extern crate hello_world; // The crate name replaced the hyphen +``` + +[RFC 940]: https://github.com/rust-lang/rfcs/blob/master/text/0940-hyphens-considered-harmful.md + #### Use declarations A _use declaration_ creates one or more local name bindings synonymous with @@ -3720,9 +3734,9 @@ Since `'static` "lives longer" than `'a`, `&'static str` is a subtype of ## Type coercions -Coercions are defined in [RFC401]. A coercion is implicit and has no syntax. +Coercions are defined in [RFC 401]. A coercion is implicit and has no syntax. -[RFC401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md +[RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md ### Coercion sites @@ -3862,7 +3876,7 @@ Coercion is allowed between the following types: In the future, coerce_inner will be recursively extended to tuples and structs. In addition, coercions from sub-traits to super-traits will be - added. See [RFC401] for more details. + added. See [RFC 401] for more details. # Special traits From ba6ce123bac969ee896a776699a8fad651a0a1cd Mon Sep 17 00:00:00 2001 From: Isaac Andrade Date: Tue, 3 May 2016 08:29:44 -0700 Subject: [PATCH 02/53] Improve language. --- src/doc/reference.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index 87eede0b7373..862e57d01605 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -846,15 +846,15 @@ extern crate std as ruststd; // linking to 'std' under another name ``` When naming Rust crates, hyphens are disallowed. However, Cargo packages may -make use of them. In such case, when `cargo.toml` doesn't specify a crate name, +make use of them. In such case, when `Cargo.toml` doesn't specify a crate name, Cargo will transparently replace `-` with `_` (Refer to [RFC 940] for more details). Here is an example: ```{.ignore} -// Importing the Cargo package hello-world/ -extern crate hello_world; // The crate name replaced the hyphen +// Importing the Cargo package hello-world +extern crate hello_world; // hyphen replaced with an underscore ``` [RFC 940]: https://github.com/rust-lang/rfcs/blob/master/text/0940-hyphens-considered-harmful.md From ad335c2d50ba140392807c167839c3869c95a3d1 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sun, 19 Jun 2016 11:45:26 +0200 Subject: [PATCH 03/53] Add `is_empty` function to `ExactSizeIterator` All other types implementing a `len` functions have `is_empty` already. --- src/libcore/iter/traits.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 67503984450a..41c34d932621 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -485,8 +485,6 @@ impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait ExactSizeIterator: Iterator { - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] /// Returns the exact number of times the iterator will iterate. /// /// This method has a default implementation, so you usually should not @@ -510,6 +508,8 @@ pub trait ExactSizeIterator: Iterator { /// /// assert_eq!(5, five.len()); /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] fn len(&self) -> usize { let (lower, upper) = self.size_hint(); // Note: This assertion is overly defensive, but it checks the invariant @@ -519,6 +519,31 @@ pub trait ExactSizeIterator: Iterator { assert_eq!(upper, Some(lower)); lower } + + /// + /// Returns whether the iterator is empty. + /// + /// This method has a default implementation using `self.len()`, so you + /// don't need to implement it yourself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut one_element = [0].iter(); + /// assert!(!one_element.is_empty()); + /// + /// assert_eq!(one_element.next(), Some(0)); + /// assert!(one_element.is_empty()); + /// + /// assert_eq!(one_element.next(), None); + /// ``` + #[inline] + #[unstable(feature = "exact_size_is_empty", issue = "0")] + fn is_empty(&self) -> bool { + self.len() == 0 + } } #[stable(feature = "rust1", since = "1.0.0")] From 208de46f8b7c7338f7d19057e748a45a72fc7d73 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sun, 19 Jun 2016 18:52:33 +0200 Subject: [PATCH 04/53] Remove first empty line of doc comment --- src/libcore/iter/traits.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 41c34d932621..b7ee9ba55f59 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -520,7 +520,6 @@ pub trait ExactSizeIterator: Iterator { lower } - /// /// Returns whether the iterator is empty. /// /// This method has a default implementation using `self.len()`, so you From 1bcd60682d5b01aa170790f98ddd96372105b4b9 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 1 Jul 2016 19:47:30 -0400 Subject: [PATCH 05/53] llvm, rt: build using the Ninja generator if available The Ninja generator generally builds much faster than make. It may also be used on Windows to have a vast speed improvement over the Visual Studio generators. Currently hidden behind an `--enable-ninja` flag because it does not obey the top-level `-j` or `-l` flags given to `make`. --- configure | 17 ++++++++++++++++- mk/llvm.mk | 10 ++++++++-- src/bootstrap/config.rs | 1 + 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 6451b87673f6..3ff728b86a9f 100755 --- a/configure +++ b/configure @@ -612,6 +612,7 @@ opt rustbuild 0 "use the rust and cargo based build system" opt orbit 0 "get MIR where it belongs - everywhere; most importantly, in orbit" opt codegen-tests 1 "run the src/test/codegen tests" opt option-checking 1 "complain about unrecognized options in this configure script" +opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)" # Optimization and debugging options. These may be overridden by the release channel, etc. opt_nosave optimize 1 "build optimized rust code" @@ -775,6 +776,17 @@ probe CFG_BISON bison probe CFG_GDB gdb probe CFG_LLDB lldb +if [ -n "$CFG_ENABLE_NINJA" ] +then + probe CFG_NINJA ninja + if [ -z "$CFG_NINJA" ] + then + # On Debian and Fedora, the `ninja` binary is an IRC bot, so the build tool was + # renamed. Handle this case. + probe CFG_NINJA ninja-build + fi +fi + # For building LLVM probe_need CFG_CMAKE cmake @@ -1524,7 +1536,10 @@ do fi # We need the generator later on for compiler-rt even if LLVM's not built - if [ ${is_msvc} -ne 0 ] + if [ -n "$CFG_NINJA" ] + then + generator="Ninja" + elif [ ${is_msvc} -ne 0 ] then case "$CFG_MSVC_ROOT" in *14.0*) diff --git a/mk/llvm.mk b/mk/llvm.mk index 229856522107..602c20430298 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -43,7 +43,9 @@ $$(LLVM_CONFIG_$(1)): $$(LLVM_DONE_$(1)) $$(LLVM_DONE_$(1)): $$(LLVM_DEPS_TARGET_$(1)) $$(LLVM_STAMP_$(1)) @$$(call E, cmake: llvm) -ifeq ($$(findstring msvc,$(1)),msvc) +ifneq ($$(CFG_NINJA),) + $$(Q)$$(CFG_NINJA) -C $$(CFG_LLVM_BUILD_DIR_$(1)) +else ifeq ($$(findstring msvc,$(1)),msvc) $$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \ --config $$(LLVM_BUILD_CONFIG_MODE) else @@ -51,7 +53,11 @@ else endif $$(Q)touch $$@ -ifeq ($$(findstring msvc,$(1)),msvc) +ifneq ($$(CFG_NINJA),) +clean-llvm$(1): + @$$(call E, clean: llvm) + $$(Q)$$(CFG_NINJA) -C $$(CFG_LLVM_BUILD_DIR_$(1)) -t clean +else ifeq ($$(findstring msvc,$(1)),msvc) clean-llvm$(1): else clean-llvm$(1): diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 498196e9b6df..e64d7e5a437e 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -317,6 +317,7 @@ impl Config { ("OPTIMIZE_TESTS", self.rust_optimize_tests), ("DEBUGINFO_TESTS", self.rust_debuginfo_tests), ("LOCAL_REBUILD", self.local_rebuild), + ("NINJA", self.ninja), } match key { From b9a35902a20f264aa0b516f7f3a4ff2490a3cf06 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 1 Jul 2016 20:19:23 -0400 Subject: [PATCH 06/53] llvm: allow cleaning LLVM's Visual Studio builds The Visual Studio generators create a `clean` target that we can use. --- mk/llvm.mk | 4 ++++ mk/rt.mk | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/mk/llvm.mk b/mk/llvm.mk index 602c20430298..d6f812049e03 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -59,6 +59,10 @@ clean-llvm$(1): $$(Q)$$(CFG_NINJA) -C $$(CFG_LLVM_BUILD_DIR_$(1)) -t clean else ifeq ($$(findstring msvc,$(1)),msvc) clean-llvm$(1): + @$$(call E, clean: llvm) + $$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \ + --config $$(LLVM_BUILD_CONFIG_MODE) \ + --target clean else clean-llvm$(1): @$$(call E, clean: llvm) diff --git a/mk/rt.mk b/mk/rt.mk index d0ab3102d7d7..8113b6838074 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -350,10 +350,17 @@ $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD $$(COMPRT_DEFINES_$(1)) \ $$(COMPRT_BUILD_CC_$(1)) \ -G"$$(CFG_CMAKE_GENERATOR)" +ifneq ($$(CFG_NINJA),) + $$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \ + --target $$(COMPRT_BUILD_TARGET_$(1)) \ + --config $$(LLVM_BUILD_CONFIG_MODE) \ + -- $$(COMPRT_BUILD_ARGS_$(1)) +else $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \ --target $$(COMPRT_BUILD_TARGET_$(1)) \ --config $$(LLVM_BUILD_CONFIG_MODE) \ -- $$(COMPRT_BUILD_ARGS_$(1)) $$(MFLAGS) +endif $$(Q)cp "$$(COMPRT_OUTPUT_$(1))" $$@ endif From 767e14983e8b93fe111fb53fe802b4e7753ffbe6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 Jul 2016 13:09:44 -0700 Subject: [PATCH 07/53] std: Correct tracking issue for SipHash{13,24} The referenced tracking issue was closed and was actually about changing the algorithm. cc #34767 --- src/libcore/hash/sip.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libcore/hash/sip.rs b/src/libcore/hash/sip.rs index c52c0b0730be..4a806a3c9860 100644 --- a/src/libcore/hash/sip.rs +++ b/src/libcore/hash/sip.rs @@ -18,7 +18,7 @@ use ptr; /// An implementation of SipHash 1-3. /// /// See: https://131002.net/siphash/ -#[unstable(feature = "sip_hash_13", issue = "29754")] +#[unstable(feature = "sip_hash_13", issue = "34767")] #[derive(Debug, Clone, Default)] pub struct SipHasher13 { hasher: Hasher, @@ -27,7 +27,7 @@ pub struct SipHasher13 { /// An implementation of SipHash 2-4. /// /// See: https://131002.net/siphash/ -#[unstable(feature = "sip_hash_13", issue = "29754")] +#[unstable(feature = "sip_hash_13", issue = "34767")] #[derive(Debug, Clone, Default)] pub struct SipHasher24 { hasher: Hasher, @@ -154,14 +154,14 @@ impl SipHasher { impl SipHasher13 { /// Creates a new `SipHasher13` with the two initial keys set to 0. #[inline] - #[unstable(feature = "sip_hash_13", issue = "29754")] + #[unstable(feature = "sip_hash_13", issue = "34767")] pub fn new() -> SipHasher13 { SipHasher13::new_with_keys(0, 0) } /// Creates a `SipHasher13` that is keyed off the provided keys. #[inline] - #[unstable(feature = "sip_hash_13", issue = "29754")] + #[unstable(feature = "sip_hash_13", issue = "34767")] pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) @@ -172,14 +172,14 @@ impl SipHasher13 { impl SipHasher24 { /// Creates a new `SipHasher24` with the two initial keys set to 0. #[inline] - #[unstable(feature = "sip_hash_13", issue = "29754")] + #[unstable(feature = "sip_hash_13", issue = "34767")] pub fn new() -> SipHasher24 { SipHasher24::new_with_keys(0, 0) } /// Creates a `SipHasher24` that is keyed off the provided keys. #[inline] - #[unstable(feature = "sip_hash_13", issue = "29754")] + #[unstable(feature = "sip_hash_13", issue = "34767")] pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher24 { SipHasher24 { hasher: Hasher::new_with_keys(key0, key1) @@ -232,7 +232,7 @@ impl super::Hasher for SipHasher { } } -#[unstable(feature = "sip_hash_13", issue = "29754")] +#[unstable(feature = "sip_hash_13", issue = "34767")] impl super::Hasher for SipHasher13 { #[inline] fn write(&mut self, msg: &[u8]) { @@ -245,7 +245,7 @@ impl super::Hasher for SipHasher13 { } } -#[unstable(feature = "sip_hash_13", issue = "29754")] +#[unstable(feature = "sip_hash_13", issue = "34767")] impl super::Hasher for SipHasher24 { #[inline] fn write(&mut self, msg: &[u8]) { From 4e74c181a4f16adf72ff0f9adbf6faca8fe5d1df Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 14 Jul 2016 08:55:48 +0000 Subject: [PATCH 08/53] Make `ext::base::expr_to_string` work correctly with `include!` macro invocations --- src/libsyntax/ext/base.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 92670cd9def9..70d924cf46d0 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -820,6 +820,12 @@ impl<'a> ExtCtxt<'a> { /// compilation on error, merely emits a non-fatal error and returns None. pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) -> Option<(InternedString, ast::StrStyle)> { + // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. + let expr = expr.map(|mut expr| { + expr.span.expn_id = cx.backtrace; + expr + }); + // we want to be able to handle e.g. concat("foo", "bar") let expr = cx.expander().fold_expr(expr); match expr.node { From 11f24a93c79a5ff5ecd2c238c603bdab30926bb3 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 14 Jul 2016 08:59:25 +0000 Subject: [PATCH 09/53] Add regression test --- src/test/compile-fail/macro-expanded-include/foo/mod.rs | 4 ++++ src/test/compile-fail/macro-expanded-include/test.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/macro-expanded-include/foo/mod.rs b/src/test/compile-fail/macro-expanded-include/foo/mod.rs index 57b7b72a1d43..888bdf5179a2 100644 --- a/src/test/compile-fail/macro-expanded-include/foo/mod.rs +++ b/src/test/compile-fail/macro-expanded-include/foo/mod.rs @@ -13,3 +13,7 @@ macro_rules! m { () => { include!("file.txt"); } } + +macro_rules! n { + () => { unsafe { asm!(include_str!("file.txt")); } } +} diff --git a/src/test/compile-fail/macro-expanded-include/test.rs b/src/test/compile-fail/macro-expanded-include/test.rs index 7ab9dd19b1b7..e1e85ddb2c1b 100644 --- a/src/test/compile-fail/macro-expanded-include/test.rs +++ b/src/test/compile-fail/macro-expanded-include/test.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] +#![feature(asm, rustc_attrs)] +#![allow(unused)] #[macro_use] mod foo; m!(); +fn f() { n!(); } #[rustc_error] fn main() {} //~ ERROR compilation successful From 8f044fae36b73ec4593c127ec2a7c28716208591 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 5 Jul 2016 15:24:23 -0400 Subject: [PATCH 10/53] Remove BasicEmitter --- src/librustc/session/mod.rs | 15 +++- src/librustc_driver/lib.rs | 17 +++- src/librustc_errors/emitter.rs | 132 ++++++++++++++----------------- src/librustc_errors/lib.rs | 2 +- src/librustdoc/core.rs | 2 +- src/librustdoc/test.rs | 4 +- src/libsyntax/codemap.rs | 6 +- src/libsyntax/parse/lexer/mod.rs | 2 +- src/libsyntax/parse/mod.rs | 6 +- 9 files changed, 99 insertions(+), 87 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 0e516bdc2119..3939f5f5880a 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -22,7 +22,8 @@ use mir::transform as mir_pass; use syntax::ast::{NodeId, Name}; use errors::{self, DiagnosticBuilder}; -use errors::emitter::{Emitter, BasicEmitter, EmitterWriter}; +use errors::emitter::{Emitter, EmitterWriter}; +use errors::snippet::FormatMode; use syntax::json::JsonEmitter; use syntax::feature_gate; use syntax::parse; @@ -439,7 +440,7 @@ pub fn build_session_with_codemap(sopts: config::Options, config::ErrorOutputType::HumanReadable(color_config) => { Box::new(EmitterWriter::stderr(color_config, Some(registry), - codemap.clone(), + Some(codemap.clone()), errors::snippet::FormatMode::EnvironmentSelected)) } config::ErrorOutputType::Json => { @@ -577,7 +578,10 @@ unsafe fn configure_llvm(sess: &Session) { pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { let mut emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(BasicEmitter::stderr(color_config)) + Box::new(EmitterWriter::stderr(color_config, + None, + None, + FormatMode::EnvironmentSelected)) } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; @@ -588,7 +592,10 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { pub fn early_warn(output: config::ErrorOutputType, msg: &str) { let mut emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(BasicEmitter::stderr(color_config)) + Box::new(EmitterWriter::stderr(color_config, + None, + None, + FormatMode::EnvironmentSelected)) } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 84e040319231..695e9062db33 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -100,6 +100,7 @@ use syntax::feature_gate::{GatedCfg, UnstableFeatures}; use syntax::parse::{self, PResult}; use syntax_pos::MultiSpan; use errors::emitter::Emitter; +use errors::snippet::FormatMode; #[cfg(test)] pub mod test; @@ -139,7 +140,10 @@ pub fn run(args: Vec) -> isize { Some(sess) => sess.fatal(&abort_msg(err_count)), None => { let mut emitter = - errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto); + errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, + None, + None, + FormatMode::EnvironmentSelected); emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None, errors::Level::Fatal); exit_on_err(); @@ -375,7 +379,10 @@ fn check_cfg(sopts: &config::Options, output: ErrorOutputType) { let mut emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(errors::emitter::BasicEmitter::stderr(color_config)) + Box::new(errors::emitter::EmitterWriter::stderr(color_config, + None, + None, + FormatMode::EnvironmentSelected)) } config::ErrorOutputType::Json => Box::new(json::JsonEmitter::basic()), }; @@ -1046,7 +1053,11 @@ pub fn monitor(f: F) { if let Err(value) = thread.unwrap().join() { // Thread panicked without emitting a fatal diagnostic if !value.is::() { - let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto); + let mut emitter = + errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, + None, + None, + FormatMode::EnvironmentSelected); // a .span_bug or .bug call has already printed what // it wants to print. diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index a7c68e3a87b3..75c35869e9e9 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -117,42 +117,10 @@ impl ColorConfig { } } -/// A basic emitter for when we don't have access to a codemap or registry. Used -/// for reporting very early errors, etc. -pub struct BasicEmitter { - dst: Destination, -} - -impl CoreEmitter for BasicEmitter { - fn emit_message(&mut self, - _rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - _is_header: bool, - _show_snippet: bool) { - // we ignore the span as we have no access to a codemap at this point - if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) { - panic!("failed to print diagnostics: {:?}", e); - } - } -} - -impl BasicEmitter { - pub fn stderr(color_config: ColorConfig) -> BasicEmitter { - if color_config.use_color() { - let dst = Destination::from_stderr(); - BasicEmitter { dst: dst } - } else { - BasicEmitter { dst: Raw(Box::new(io::stderr())) } - } - } -} - pub struct EmitterWriter { dst: Destination, registry: Option, - cm: Rc, + cm: Option>, /// Is this the first error emitted thus far? If not, we emit a /// `\n` before the top-level errors. @@ -194,7 +162,7 @@ macro_rules! println_maybe_styled { impl EmitterWriter { pub fn stderr(color_config: ColorConfig, registry: Option, - code_map: Rc, + code_map: Option>, format_mode: FormatMode) -> EmitterWriter { if color_config.use_color() { @@ -215,7 +183,7 @@ impl EmitterWriter { pub fn new(dst: Box, registry: Option, - code_map: Rc, + code_map: Option>, format_mode: FormatMode) -> EmitterWriter { EmitterWriter { dst: Raw(dst), @@ -257,7 +225,11 @@ impl EmitterWriter { if old_school { let loc = match rsp.span().primary_span() { Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), + Some(ps) => if let Some(ref cm) = self.cm { + cm.span_to_string(ps) + } else { + "".to_string() + }, None => "".to_string() }; print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))? @@ -270,7 +242,11 @@ impl EmitterWriter { if old_school { let loc = match rsp.span().primary_span() { Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), + Some(ps) => if let Some(ref cm) = self.cm { + cm.span_to_string(ps) + } else { + "".to_string() + }, None => "".to_string() }; print_diagnostic(&mut self.dst, &loc, lvl, msg, code)? @@ -316,7 +292,11 @@ impl EmitterWriter { .is_some() => { let loc = match rsp.span().primary_span() { Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => self.cm.span_to_string(ps), + Some(ps) => if let Some(ref cm) = self.cm { + cm.span_to_string(ps) + } else { + "".to_string() + }, None => "".to_string() }; let msg = "run `rustc --explain ".to_string() + &code.to_string() + @@ -335,32 +315,34 @@ impl EmitterWriter { use std::borrow::Borrow; let primary_span = suggestion.msp.primary_span().unwrap(); - let lines = self.cm.span_to_lines(primary_span).unwrap(); - assert!(!lines.lines.is_empty()); + if let Some(ref cm) = self.cm { + let lines = cm.span_to_lines(primary_span).unwrap(); - let complete = suggestion.splice_lines(self.cm.borrow()); - let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); - let display_lines = &lines.lines[..line_count]; + assert!(!lines.lines.is_empty()); - let fm = &*lines.file; - // Calculate the widest number to format evenly - let max_digits = line_num_max_digits(display_lines.last().unwrap()); + let complete = suggestion.splice_lines(cm.borrow()); + let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); + let display_lines = &lines.lines[..line_count]; - // print the suggestion without any line numbers, but leave - // space for them. This helps with lining up with previous - // snippets from the actual error being reported. - let mut lines = complete.lines(); - for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { - write!(&mut self.dst, "{0}:{1:2$} {3}\n", - fm.name, "", max_digits, line)?; + let fm = &*lines.file; + // Calculate the widest number to format evenly + let max_digits = line_num_max_digits(display_lines.last().unwrap()); + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let mut lines = complete.lines(); + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + write!(&mut self.dst, "{0}:{1:2$} {3}\n", + fm.name, "", max_digits, line)?; + } + + // if we elided some lines, add an ellipsis + if let Some(_) = lines.next() { + write!(&mut self.dst, "{0:1$} {0:2$} ...\n", + "", fm.name.len(), max_digits)?; + } } - - // if we elided some lines, add an ellipsis - if let Some(_) = lines.next() { - write!(&mut self.dst, "{0:1$} {0:2$} ...\n", - "", fm.name.len(), max_digits)?; - } - Ok(()) } @@ -369,20 +351,26 @@ impl EmitterWriter { lvl: Level) -> io::Result<()> { + // Check to see if we have any lines to highlight, exit early if not + match self.cm { + None => return Ok(()), + _ => () + } + let old_school = match self.format_mode { FormatMode::NewErrorFormat => false, FormatMode::OriginalErrorFormat => true, FormatMode::EnvironmentSelected => check_old_skool() }; - let mut snippet_data = SnippetData::new(self.cm.clone(), + let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(), msp.primary_span(), self.format_mode.clone()); if old_school { let mut output_vec = vec![]; for span_label in msp.span_labels() { - let mut snippet_data = SnippetData::new(self.cm.clone(), + let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(), Some(span_label.span), self.format_mode.clone()); @@ -431,16 +419,18 @@ impl EmitterWriter { fn print_macro_backtrace(&mut self, sp: Span) -> io::Result<()> { - for trace in self.cm.macro_backtrace(sp) { - let mut diag_string = - format!("in this expansion of {}", trace.macro_decl_name); - if let Some(def_site_span) = trace.def_site_span { - diag_string.push_str( - &format!(" (defined in {})", - self.cm.span_to_filename(def_site_span))); + if let Some(ref cm) = self.cm { + for trace in cm.macro_backtrace(sp) { + let mut diag_string = + format!("in this expansion of {}", trace.macro_decl_name); + if let Some(def_site_span) = trace.def_site_span { + diag_string.push_str( + &format!(" (defined in {})", + cm.span_to_filename(def_site_span))); + } + let snippet = cm.span_to_string(trace.call_site); + print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; } - let snippet = self.cm.span_to_string(trace.call_site); - print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; } Ok(()) } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 18fc826f9aa4..100e79b1954d 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -423,7 +423,7 @@ impl Handler { registry: Option, can_emit_warnings: bool, treat_err_as_bug: bool, - cm: Rc) + cm: Option>) -> Handler { let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm, snippet::FormatMode::EnvironmentSelected)); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 49a3991ecbe0..fc9ae73f5ce7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -131,7 +131,7 @@ pub fn run_core(search_paths: SearchPaths, None, true, false, - codemap.clone()); + Some(codemap.clone())); let dep_graph = DepGraph::new(false); let _ignore = dep_graph.in_ignore(); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index bb69ba6e568d..f9d0df9981a1 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -77,7 +77,7 @@ pub fn run(input: &str, None, true, false, - codemap.clone()); + Some(codemap.clone())); let dep_graph = DepGraph::new(false); let _ignore = dep_graph.in_ignore(); @@ -229,7 +229,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, let codemap = Rc::new(CodeMap::new()); let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()), None, - codemap.clone(), + Some(codemap.clone()), errors::snippet::FormatMode::EnvironmentSelected); let old = io::set_panic(box Sink(data.clone())); let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout())); diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 743f96d737e2..3b93dc0c4d37 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1235,7 +1235,7 @@ r"blork2.rs:2:1: 2:12 let cm = Rc::new(CodeMap::new()); let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, - cm.clone(), + Some(cm.clone()), FormatMode::NewErrorFormat); let content = "abcdefg koksi @@ -1321,7 +1321,7 @@ r"blork2.rs:2:1: 2:12 let cm = Rc::new(CodeMap::new()); let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, - cm.clone(), + Some(cm.clone()), FormatMode::NewErrorFormat); let inp = "_____aaaaaa____bbbbbb__cccccdd_"; @@ -1377,7 +1377,7 @@ r"blork2.rs:2:1: 2:12 let cm = Rc::new(CodeMap::new()); let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, - cm.clone(), + Some(cm.clone()), FormatMode::NewErrorFormat); let inp = "aaaaa\n\ diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 77b5c10899a3..5ea1d6be9fec 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1686,7 +1686,7 @@ mod tests { // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), None, - cm, + Some(cm), errors::snippet::FormatMode::EnvironmentSelected); errors::Handler::with_emitter(true, false, Box::new(emitter)) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 9502bc48a3e1..6af4d95e888a 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -50,7 +50,11 @@ pub struct ParseSess { impl ParseSess { pub fn new() -> ParseSess { let cm = Rc::new(CodeMap::new()); - let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone()); + let handler = Handler::with_tty_emitter(ColorConfig::Auto, + None, + true, + false, + Some(cm.clone())); ParseSess::with_span_handler(handler, cm) } From 55f06883b83bb652d7a8f036c15136abeb933048 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 6 Jul 2016 12:08:16 -0400 Subject: [PATCH 11/53] Remove emit from emitter, leaving emit_struct --- src/librustc/session/mod.rs | 10 +++--- src/librustc_driver/lib.rs | 34 +++++++++++--------- src/librustc_errors/emitter.rs | 16 ---------- src/librustc_errors/lib.rs | 57 ++++++++++++++++++++++++++-------- src/libsyntax/json.rs | 25 +-------------- 5 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3939f5f5880a..fa9bc7c83680 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -576,7 +576,7 @@ unsafe fn configure_llvm(sess: &Session) { } pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { - let mut emitter: Box = match output { + let emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, @@ -585,12 +585,13 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; - emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Fatal); + let handler = errors::Handler::with_emitter(true, false, emitter); + handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal); panic!(errors::FatalError); } pub fn early_warn(output: config::ErrorOutputType, msg: &str) { - let mut emitter: Box = match output { + let emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, @@ -599,7 +600,8 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) { } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), }; - emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Warning); + let handler = errors::Handler::with_emitter(true, false, emitter); + handler.emit(&MultiSpan::new(), msg, errors::Level::Warning); } // Err(0) means compilation was stopped, but no errors were found. diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 695e9062db33..0a8df923b846 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -139,13 +139,15 @@ pub fn run(args: Vec) -> isize { match session { Some(sess) => sess.fatal(&abort_msg(err_count)), None => { - let mut emitter = + let emitter = errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None, None, FormatMode::EnvironmentSelected); - emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None, - errors::Level::Fatal); + let handler = errors::Handler::with_emitter(true, false, Box::new(emitter)); + handler.emit(&MultiSpan::new(), + &abort_msg(err_count), + errors::Level::Fatal); exit_on_err(); } } @@ -377,7 +379,7 @@ fn handle_explain(code: &str, fn check_cfg(sopts: &config::Options, output: ErrorOutputType) { - let mut emitter: Box = match output { + let emitter: Box = match output { config::ErrorOutputType::HumanReadable(color_config) => { Box::new(errors::emitter::EmitterWriter::stderr(color_config, None, @@ -386,17 +388,17 @@ fn check_cfg(sopts: &config::Options, } config::ErrorOutputType::Json => Box::new(json::JsonEmitter::basic()), }; + let handler = errors::Handler::with_emitter(true, false, emitter); let mut saw_invalid_predicate = false; for item in sopts.cfg.iter() { match item.node { ast::MetaItemKind::List(ref pred, _) => { saw_invalid_predicate = true; - emitter.emit(&MultiSpan::new(), + handler.emit(&MultiSpan::new(), &format!("invalid predicate in --cfg command line argument: `{}`", pred), - None, - errors::Level::Fatal); + errors::Level::Fatal); } _ => {}, } @@ -1053,30 +1055,34 @@ pub fn monitor(f: F) { if let Err(value) = thread.unwrap().join() { // Thread panicked without emitting a fatal diagnostic if !value.is::() { - let mut emitter = - errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, + let emitter = + Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None, None, - FormatMode::EnvironmentSelected); + FormatMode::EnvironmentSelected)); + let handler = errors::Handler::with_emitter(true, false, emitter); // a .span_bug or .bug call has already printed what // it wants to print. if !value.is::() { - emitter.emit(&MultiSpan::new(), "unexpected panic", None, errors::Level::Bug); + handler.emit(&MultiSpan::new(), + "unexpected panic", + errors::Level::Bug); } let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(), format!("we would appreciate a bug report: {}", BUG_REPORT_URL)]; for note in &xs { - emitter.emit(&MultiSpan::new(), ¬e[..], None, errors::Level::Note) + handler.emit(&MultiSpan::new(), + ¬e[..], + errors::Level::Note); } if match env::var_os("RUST_BACKTRACE") { Some(val) => &val != "0", None => false, } { - emitter.emit(&MultiSpan::new(), + handler.emit(&MultiSpan::new(), "run with `RUST_BACKTRACE=1` for a backtrace", - None, errors::Level::Note); } diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 75c35869e9e9..ab331239cdba 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -28,9 +28,6 @@ use term; /// Emitter trait for emitting errors. Do not implement this directly: /// implement `CoreEmitter` instead. pub trait Emitter { - /// Emit a standalone diagnostic message. - fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level); - /// Emit a structured diagnostic. fn emit_struct(&mut self, db: &DiagnosticBuilder); } @@ -46,19 +43,6 @@ pub trait CoreEmitter { } impl Emitter for T { - fn emit(&mut self, - msp: &MultiSpan, - msg: &str, - code: Option<&str>, - lvl: Level) { - self.emit_message(&FullSpan(msp.clone()), - msg, - code, - lvl, - true, - true); - } - fn emit_struct(&mut self, db: &DiagnosticBuilder) { let old_school = check_old_skool(); let db_span = FullSpan(db.span.clone()); diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 100e79b1954d..e3e63341ee90 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -359,11 +359,20 @@ impl<'a> DiagnosticBuilder<'a> { fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { + DiagnosticBuilder::new_with_code(handler, level, None, message) + } + + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + fn new_with_code(handler: &'a Handler, + level: Level, + code: Option, + message: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder { handler: handler, level: level, message: message.to_owned(), - code: None, + code: code, span: MultiSpan::new(), children: vec![], } @@ -397,10 +406,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> { impl<'a> Drop for DiagnosticBuilder<'a> { fn drop(&mut self) { if !panicking() && !self.cancelled() { - self.handler.emit.borrow_mut().emit(&MultiSpan::new(), - "Error constructed but not emitted", - None, - Bug); + let mut db = DiagnosticBuilder::new(self.handler, + Bug, + "Error constructed but not emitted"); + db.emit(); panic!(); } } @@ -588,7 +597,7 @@ impl Handler { self.bump_err_count(); } pub fn span_note_without_error>(&self, sp: S, msg: &str) { - self.emit.borrow_mut().emit(&sp.into(), msg, None, Note); + self.emit(&sp.into(), msg, Note); } pub fn span_unimpl>(&self, sp: S, msg: &str) -> ! { self.span_bug(sp, &format!("unimplemented {}", msg)); @@ -597,7 +606,10 @@ impl Handler { if self.treat_err_as_bug { self.bug(msg); } - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal); + let mut db = DiagnosticBuilder::new(self, + Fatal, + msg); + db.emit(); self.bump_err_count(); FatalError } @@ -605,17 +617,29 @@ impl Handler { if self.treat_err_as_bug { self.bug(msg); } - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error); + let mut db = DiagnosticBuilder::new(self, + Error, + msg); + db.emit(); self.bump_err_count(); } pub fn warn(&self, msg: &str) { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning); + let mut db = DiagnosticBuilder::new(self, + Warning, + msg); + db.emit(); } pub fn note_without_error(&self, msg: &str) { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note); + let mut db = DiagnosticBuilder::new(self, + Note, + msg); + db.emit(); } pub fn bug(&self, msg: &str) -> ! { - self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug); + let mut db = DiagnosticBuilder::new(self, + Bug, + msg); + db.emit(); panic!(ExplicitBug); } pub fn unimpl(&self, msg: &str) -> ! { @@ -661,7 +685,9 @@ impl Handler { msg: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(&msp, msg, None, lvl); + let mut db = DiagnosticBuilder::new(self, lvl, msg); + db.set_span(msp.clone()); + db.emit(); if !self.continue_after_error.get() { self.abort_if_errors(); } } pub fn emit_with_code(&self, @@ -670,7 +696,12 @@ impl Handler { code: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl); + let mut db = DiagnosticBuilder::new_with_code(self, + lvl, + Some(code.to_owned()), + msg); + db.set_span(msp.clone()); + db.emit(); if !self.continue_after_error.get() { self.abort_if_errors(); } } } diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs index dc9a5ee46645..20908453e73e 100644 --- a/src/libsyntax/json.rs +++ b/src/libsyntax/json.rs @@ -22,7 +22,7 @@ use codemap::CodeMap; use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan}; use errors::registry::Registry; -use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper}; +use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper}; use errors::emitter::Emitter; use std::rc::Rc; @@ -53,13 +53,6 @@ impl JsonEmitter { } impl Emitter for JsonEmitter { - fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) { - let data = Diagnostic::new(span, msg, code, level, self); - if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { - panic!("failed to print diagnostics: {:?}", e); - } - } - fn emit_struct(&mut self, db: &DiagnosticBuilder) { let data = Diagnostic::from_diagnostic_builder(db, self); if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { @@ -146,22 +139,6 @@ struct DiagnosticCode { } impl<'a> Diagnostic<'a> { - fn new(msp: &MultiSpan, - msg: &'a str, - code: Option<&str>, - level: Level, - je: &JsonEmitter) - -> Diagnostic<'a> { - Diagnostic { - message: msg, - code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je), - level: level.to_str(), - spans: DiagnosticSpan::from_multispan(msp, je), - children: vec![], - rendered: None, - } - } - fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder, je: &JsonEmitter) -> Diagnostic<'c> { From a6e7239e7b5db3a96fb6b84c41ecbced64b0ad2e Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 7 Jul 2016 07:50:49 -0400 Subject: [PATCH 12/53] Rename emit_struct->emit --- src/librustc_errors/emitter.rs | 4 ++-- src/librustc_errors/lib.rs | 6 +++--- src/libsyntax/json.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index ab331239cdba..697f9687b0a1 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -29,7 +29,7 @@ use term; /// implement `CoreEmitter` instead. pub trait Emitter { /// Emit a structured diagnostic. - fn emit_struct(&mut self, db: &DiagnosticBuilder); + fn emit(&mut self, db: &DiagnosticBuilder); } pub trait CoreEmitter { @@ -43,7 +43,7 @@ pub trait CoreEmitter { } impl Emitter for T { - fn emit_struct(&mut self, db: &DiagnosticBuilder) { + fn emit(&mut self, db: &DiagnosticBuilder) { let old_school = check_old_skool(); let db_span = FullSpan(db.span.clone()); self.emit_message(&FullSpan(db.span.clone()), diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index e3e63341ee90..7c14c132382e 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -238,7 +238,7 @@ impl<'a> DiagnosticBuilder<'a> { return; } - self.handler.emit.borrow_mut().emit_struct(&self); + self.handler.emitter.borrow_mut().emit(&self); self.cancel(); self.handler.panic_if_treat_err_as_bug(); @@ -420,7 +420,7 @@ impl<'a> Drop for DiagnosticBuilder<'a> { /// others log errors for later reporting. pub struct Handler { err_count: Cell, - emit: RefCell>, + emitter: RefCell>, pub can_emit_warnings: bool, treat_err_as_bug: bool, continue_after_error: Cell, @@ -444,7 +444,7 @@ impl Handler { e: Box) -> Handler { Handler { err_count: Cell::new(0), - emit: RefCell::new(e), + emitter: RefCell::new(e), can_emit_warnings: can_emit_warnings, treat_err_as_bug: treat_err_as_bug, continue_after_error: Cell::new(true), diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs index 20908453e73e..a40c30b3e339 100644 --- a/src/libsyntax/json.rs +++ b/src/libsyntax/json.rs @@ -53,7 +53,7 @@ impl JsonEmitter { } impl Emitter for JsonEmitter { - fn emit_struct(&mut self, db: &DiagnosticBuilder) { + fn emit(&mut self, db: &DiagnosticBuilder) { let data = Diagnostic::from_diagnostic_builder(db, self); if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { panic!("failed to print diagnostics: {:?}", e); From a019c2c6bad2bae7e0f5c527ea8a11615acc037f Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 7 Jul 2016 14:57:09 -0400 Subject: [PATCH 13/53] Remove CoreEmitter and focus on Emitter --- src/librustc_driver/test.rs | 19 ++++---- src/librustc_errors/emitter.rs | 79 +++++++++++++++----------------- src/librustc_trans/back/write.rs | 28 +++++------ 3 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 911becd3f569..ace469680df2 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -33,8 +33,8 @@ use syntax::ast; use syntax::abi::Abi; use syntax::codemap::CodeMap; use errors; -use errors::emitter::{CoreEmitter, Emitter}; -use errors::{Level, RenderSpan}; +use errors::emitter::Emitter; +use errors::{Level, RenderSpan, DiagnosticBuilder}; use syntax::parse::token; use syntax::feature_gate::UnstableFeatures; use syntax_pos::DUMMY_SP; @@ -76,15 +76,12 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { } } -impl CoreEmitter for ExpectErrorEmitter { - fn emit_message(&mut self, - _sp: &RenderSpan, - msg: &str, - _: Option<&str>, - lvl: Level, - _is_header: bool, - _show_snippet: bool) { - remove_message(self, msg, lvl); +impl Emitter for ExpectErrorEmitter { + fn emit(&mut self, db: &DiagnosticBuilder) { + remove_message(self, db.message, lvl); + for child in &db.children { + remove_message(self, &child.message, child.level); + } } } diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 697f9687b0a1..a0d7120dd4f4 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -25,47 +25,29 @@ use std::io; use std::rc::Rc; use term; -/// Emitter trait for emitting errors. Do not implement this directly: -/// implement `CoreEmitter` instead. +/// Emitter trait for emitting errors. pub trait Emitter { /// Emit a structured diagnostic. fn emit(&mut self, db: &DiagnosticBuilder); } -pub trait CoreEmitter { - fn emit_message(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool); -} - -impl Emitter for T { +impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { - let old_school = check_old_skool(); - let db_span = FullSpan(db.span.clone()); self.emit_message(&FullSpan(db.span.clone()), &db.message, db.code.as_ref().map(|s| &**s), db.level, true, true); - for child in &db.children { - let render_span = child.render_span - .clone() - .unwrap_or_else( - || FullSpan(child.span.clone())); - if !old_school { - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - true); - } else { + if check_old_skool() { + let db_span = FullSpan(db.span.clone()); + + for child in &db.children { + let render_span = child.render_span + .clone() + .unwrap_or_else( + || FullSpan(child.span.clone())); let (render_span, show_snippet) = match render_span.span().primary_span() { None => (db_span.clone(), false), _ => (render_span, true) @@ -77,6 +59,19 @@ impl Emitter for T { false, show_snippet); } + } else { + for child in &db.children { + let render_span = child.render_span + .clone() + .unwrap_or_else( + || FullSpan(child.span.clone())); + self.emit_message(&render_span, + &child.message, + None, + child.level, + false, + true); + } } } } @@ -114,21 +109,6 @@ pub struct EmitterWriter { format_mode: FormatMode } -impl CoreEmitter for EmitterWriter { - fn emit_message(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool) { - match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) { - Ok(()) => { } - Err(e) => panic!("failed to emit error: {}", e) - } - } -} - /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See /// `EmitterWriter::print_maybe_styled` for details. macro_rules! print_maybe_styled { @@ -177,6 +157,19 @@ impl EmitterWriter { format_mode: format_mode.clone() } } + fn emit_message(&mut self, + rsp: &RenderSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + is_header: bool, + show_snippet: bool) { + match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) { + Ok(()) => { } + Err(e) => panic!("failed to emit error: {}", e) + } + } + fn emit_message_(&mut self, rsp: &RenderSpan, msg: &str, diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 071960f1944c..33cffa8a4801 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -19,8 +19,8 @@ use llvm::SMDiagnosticRef; use {CrateTranslation, ModuleTranslation}; use util::common::time; use util::common::path2cstr; -use errors::{self, Handler, Level, RenderSpan}; -use errors::emitter::CoreEmitter; +use errors::{self, Handler, Level, DiagnosticBuilder}; +use errors::emitter::Emitter; use syntax_pos::MultiSpan; use std::collections::HashMap; @@ -100,23 +100,23 @@ impl SharedEmitter { } } -impl CoreEmitter for SharedEmitter { - fn emit_message(&mut self, - _rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - _is_header: bool, - _show_snippet: bool) { +impl Emitter for SharedEmitter { + fn emit(&mut self, db: &DiagnosticBuilder) { self.buffer.lock().unwrap().push(Diagnostic { - msg: msg.to_string(), - code: code.map(|s| s.to_string()), - lvl: lvl, + msg: db.message.to_string(), + code: db.code.clone(), + lvl: db.level, }); + for child in &db.children { + self.buffer.lock().unwrap().push(Diagnostic { + msg: child.message.to_string(), + code: None, + lvl: child.level, + }); + } } } - // On android, we by default compile for armv7 processors. This enables // things like double word CAS instructions (rather than emulating them) // which are *far* more efficient. This is obviously undesirable in some From 71ec2867e3c4cc448cb64890daffd1d5ffbe353b Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 11 Jul 2016 16:02:03 -0400 Subject: [PATCH 14/53] Implement latest rfc style using simpler rendering --- src/librustc_errors/emitter.rs | 666 +++++++++++++++++++++++++++++++-- src/librustc_errors/lib.rs | 1 + src/librustc_errors/snippet.rs | 224 ++++------- src/libsyntax/test.rs | 2 +- src/libsyntax_pos/lib.rs | 2 +- 5 files changed, 697 insertions(+), 198 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index a0d7120dd4f4..52c0ea931408 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -10,14 +10,15 @@ use self::Destination::*; -use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, Span, MultiSpan, LineInfo}; +use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, LineInfo, CharPos}; use registry; use check_old_skool; use {Level, RenderSpan, CodeSuggestion, DiagnosticBuilder, CodeMapper}; use RenderSpan::*; use Level::*; -use snippet::{RenderedLineKind, SnippetData, Style, FormatMode}; +use snippet::{SnippetData, StyledString, Style, FormatMode, Annotation, Line}; +use styled_buffer::StyledBuffer; use std::{cmp, fmt}; use std::io::prelude::*; @@ -33,14 +34,13 @@ pub trait Emitter { impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { - self.emit_message(&FullSpan(db.span.clone()), - &db.message, - db.code.as_ref().map(|s| &**s), - db.level, - true, - true); - if check_old_skool() { + self.emit_message(&FullSpan(db.span.clone()), + &db.message, + db.code.as_ref().map(|s| &**s), + db.level, + true, + true); let db_span = FullSpan(db.span.clone()); for child in &db.children { @@ -60,18 +60,7 @@ impl Emitter for EmitterWriter { show_snippet); } } else { - for child in &db.children { - let render_span = child.render_span - .clone() - .unwrap_or_else( - || FullSpan(child.span.clone())); - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - true); - } + self.emit_messages_default(db); } } } @@ -109,6 +98,12 @@ pub struct EmitterWriter { format_mode: FormatMode } +struct FileWithAnnotatedLines { + file: Rc, + lines: Vec, +} + + /// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See /// `EmitterWriter::print_maybe_styled` for details. macro_rules! print_maybe_styled { @@ -170,6 +165,560 @@ impl EmitterWriter { } } + fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec { + fn add_annotation_to_file(file_vec: &mut Vec, + file: Rc, + line_index: usize, + ann: Annotation) { + + for slot in file_vec.iter_mut() { + // Look through each of our files for the one we're adding to + if slot.file.name == file.name { + // See if we already have a line for it + for line_slot in &mut slot.lines { + if line_slot.line_index == line_index { + line_slot.annotations.push(ann); + return; + } + } + // We don't have a line yet, create one + slot.lines.push(Line { + line_index: line_index, + annotations: vec![ann], + }); + slot.lines.sort(); + return; + } + } + // This is the first time we're seeing the file + file_vec.push(FileWithAnnotatedLines { + file: file, + lines: vec![Line { + line_index: line_index, + annotations: vec![ann], + }], + }); + } + + let mut output = vec![]; + + if let Some(ref cm) = self.cm { + for span_label in msp.span_labels() { + let lo = cm.lookup_char_pos(span_label.span.lo); + let hi = cm.lookup_char_pos(span_label.span.hi); + + // If the span is multi-line, simplify down to the span of one character + let (start_col, mut end_col, is_minimized) = if lo.line != hi.line { + (lo.col, CharPos(lo.col.0 + 1), true) + } else { + (lo.col, hi.col, false) + }; + + // Watch out for "empty spans". If we get a span like 6..6, we + // want to just display a `^` at 6, so convert that to + // 6..7. This is degenerate input, but it's best to degrade + // gracefully -- and the parser likes to supply a span like + // that for EOF, in particular. + if start_col == end_col { + end_col.0 += 1; + } + + add_annotation_to_file(&mut output, + lo.file, + lo.line, + Annotation { + start_col: lo.col.0, + end_col: hi.col.0, + is_primary: span_label.is_primary, + is_minimized: is_minimized, + label: span_label.label.clone(), + }); + } + } + output + } + + fn render_source_line(&self, + buffer: &mut StyledBuffer, + file: Rc, + line: &Line, + width_offset: usize) { + let source_string = file.get_line(line.line_index - 1) + .unwrap_or(""); + + let line_offset = buffer.num_lines(); + + // First create the source line we will highlight. + buffer.puts(line_offset, width_offset, &source_string, Style::Quotation); + buffer.puts(line_offset, + 0, + &(line.line_index.to_string()), + Style::LineNumber); + + draw_col_separator(buffer, line_offset, width_offset - 2); + + if line.annotations.is_empty() { + return; + } + + // We want to display like this: + // + // vec.push(vec.pop().unwrap()); + // --- ^^^ _ previous borrow ends here + // | | + // | error occurs here + // previous borrow of `vec` occurs here + // + // But there are some weird edge cases to be aware of: + // + // vec.push(vec.pop().unwrap()); + // -------- - previous borrow ends here + // || + // |this makes no sense + // previous borrow of `vec` occurs here + // + // For this reason, we group the lines into "highlight lines" + // and "annotations lines", where the highlight lines have the `~`. + + // let mut highlight_line = Self::whitespace(&source_string); + let old_school = check_old_skool(); + + // Sort the annotations by (start, end col) + let mut annotations = line.annotations.clone(); + annotations.sort(); + + // Next, create the highlight line. + for annotation in &annotations { + if old_school { + for p in annotation.start_col..annotation.end_col { + if p == annotation.start_col { + buffer.putc(line_offset + 1, + width_offset + p, + '^', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } else { + buffer.putc(line_offset + 1, + width_offset + p, + '~', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } + } + } else { + for p in annotation.start_col..annotation.end_col { + if annotation.is_primary { + buffer.putc(line_offset + 1, + width_offset + p, + '^', + Style::UnderlinePrimary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlinePrimary); + } + } else { + buffer.putc(line_offset + 1, + width_offset + p, + '-', + Style::UnderlineSecondary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlineSecondary); + } + } + } + } + } + draw_col_separator(buffer, line_offset + 1, width_offset - 2); + + // Now we are going to write labels in. To start, we'll exclude + // the annotations with no labels. + let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = annotations.into_iter() + .partition(|a| a.label.is_some()); + + // If there are no annotations that need text, we're done. + if labeled_annotations.is_empty() { + return; + } + if old_school { + return; + } + + // Now add the text labels. We try, when possible, to stick the rightmost + // annotation at the end of the highlight line: + // + // vec.push(vec.pop().unwrap()); + // --- --- - previous borrow ends here + // + // But sometimes that's not possible because one of the other + // annotations overlaps it. For example, from the test + // `span_overlap_label`, we have the following annotations + // (written on distinct lines for clarity): + // + // fn foo(x: u32) { + // -------------- + // - + // + // In this case, we can't stick the rightmost-most label on + // the highlight line, or we would get: + // + // fn foo(x: u32) { + // -------- x_span + // | + // fn_span + // + // which is totally weird. Instead we want: + // + // fn foo(x: u32) { + // -------------- + // | | + // | x_span + // fn_span + // + // which is...less weird, at least. In fact, in general, if + // the rightmost span overlaps with any other span, we should + // use the "hang below" version, so we can at least make it + // clear where the span *starts*. + let mut labeled_annotations = &labeled_annotations[..]; + match labeled_annotations.split_last().unwrap() { + (last, previous) => { + if previous.iter() + .chain(&unlabeled_annotations) + .all(|a| !overlaps(a, last)) { + // append the label afterwards; we keep it in a separate + // string + let highlight_label: String = format!(" {}", last.label.as_ref().unwrap()); + if last.is_primary { + buffer.append(line_offset + 1, &highlight_label, Style::LabelPrimary); + } else { + buffer.append(line_offset + 1, &highlight_label, Style::LabelSecondary); + } + labeled_annotations = previous; + } + } + } + + // If that's the last annotation, we're done + if labeled_annotations.is_empty() { + return; + } + + for (index, annotation) in labeled_annotations.iter().enumerate() { + // Leave: + // - 1 extra line + // - One line for each thing that comes after + let comes_after = labeled_annotations.len() - index - 1; + let blank_lines = 3 + comes_after; + + // For each blank line, draw a `|` at our column. The + // text ought to be long enough for this. + for index in 2..blank_lines { + if annotation.is_primary { + buffer.putc(line_offset + index, + width_offset + annotation.start_col, + '|', + Style::UnderlinePrimary); + } else { + buffer.putc(line_offset + index, + width_offset + annotation.start_col, + '|', + Style::UnderlineSecondary); + } + draw_col_separator(buffer, line_offset + index, width_offset - 2); + } + + if annotation.is_primary { + buffer.puts(line_offset + blank_lines, + width_offset + annotation.start_col, + annotation.label.as_ref().unwrap(), + Style::LabelPrimary); + } else { + buffer.puts(line_offset + blank_lines, + width_offset + annotation.start_col, + annotation.label.as_ref().unwrap(), + Style::LabelSecondary); + } + draw_col_separator(buffer, line_offset + blank_lines, width_offset - 2); + } + } + + fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize { + let mut max = 0; + if let Some(ref cm) = self.cm { + for primary_span in msp.primary_spans() { + let hi = cm.lookup_char_pos(primary_span.hi); + if hi.line > max { + max = hi.line; + } + } + for span_label in msp.span_labels() { + let hi = cm.lookup_char_pos(span_label.span.hi); + if hi.line > max { + max = hi.line; + } + } + } + max + } + + fn get_max_line_num(&mut self, db: &DiagnosticBuilder) -> usize { + let mut max = 0; + + let primary = self.get_multispan_max_line_num(&db.span); + max = if primary > max { primary } else { max }; + + for sub in &db.children { + let sub_result = self.get_multispan_max_line_num(&sub.span); + max = if sub_result > max { primary } else { max }; + } + max + } + + fn emit_message_default(&mut self, + msp: &MultiSpan, + msg: &str, + code: &Option, + level: &Level, + max_line_num_len: usize, + is_secondary: bool) + -> io::Result<()> { + let mut buffer = StyledBuffer::new(); + + if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary { + // This is a secondary message with no span info + for i in 0..max_line_num_len { + buffer.prepend(0, " ", Style::NoStyle); + } + draw_note_separator(&mut buffer, 0, max_line_num_len + 1); + buffer.append(0, &level.to_string(), Style::HeaderMsg); + buffer.append(0, ": ", Style::NoStyle); + buffer.append(0, msg, Style::NoStyle); + } + else { + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + match code { + &Some(ref code) => { + buffer.append(0, "[", Style::Level(level.clone())); + buffer.append(0, &code, Style::Level(level.clone())); + buffer.append(0, "]", Style::Level(level.clone())); + } + _ => {} + } + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + } + + // Preprocess all the annotations so that they are grouped by file and by line number + // This helps us quickly iterate over the whole message (including secondary file spans) + let mut annotated_files = self.preprocess_annotations(msp); + + // Make sure our primary file comes first + let primary_lo = + if let (Some(ref cm), Some(ref primary_span)) = (self.cm.as_ref(), + msp.primary_span().as_ref()) { + cm.lookup_char_pos(primary_span.lo) + } else { + // If we don't have span information, emit and exit + emit_to_destination(&buffer.render(), level, &mut self.dst); + return Ok(()); + }; + if let Ok(pos) = + annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) { + annotated_files.swap(0, pos); + } + + // Print out the annotate source lines that correspond with the error + for annotated_file in annotated_files { + // print out the span location and spacer before we print the annotated source + // to do this, we need to know if this span will be primary + let is_primary = primary_lo.file.name == annotated_file.file.name; + if is_primary { + // remember where we are in the output buffer for easy reference + let mut buffer_msg_line_offset = buffer.num_lines(); + + buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); + let loc = primary_lo.clone(); + buffer.append(buffer_msg_line_offset, + &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0), + Style::LineAndColumn); + for i in 0..max_line_num_len { + buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); + } + } else { + // remember where we are in the output buffer for easy reference + let mut buffer_msg_line_offset = buffer.num_lines(); + + // Add spacing line + draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + + // Then, the secondary file indicator + buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); + buffer.append(buffer_msg_line_offset + 1, + &annotated_file.file.name, + Style::LineAndColumn); + for i in 0..max_line_num_len { + buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle); + } + } + + // Put in the spacer between the location and annotated source + let mut buffer_msg_line_offset = buffer.num_lines(); + draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + + // Next, output the annotate source for this file + for line_idx in 0..annotated_file.lines.len() { + self.render_source_line(&mut buffer, + annotated_file.file.clone(), + &annotated_file.lines[line_idx], + 3 + max_line_num_len); + + // check to see if we need to print out or elide lines that come between + // this annotated line and the next one + if line_idx < (annotated_file.lines.len() - 1) { + let line_idx_delta = annotated_file.lines[line_idx + 1].line_index - + annotated_file.lines[line_idx].line_index; + if line_idx_delta > 2 { + let last_buffer_line_num = buffer.num_lines(); + buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); + } else if line_idx_delta == 2 { + let unannotated_line = annotated_file.file + .get_line(annotated_file.lines[line_idx].line_index) + .unwrap_or(""); + + let last_buffer_line_num = buffer.num_lines(); + + buffer.puts(last_buffer_line_num, + 0, + &(annotated_file.lines[line_idx + 1].line_index - 1) + .to_string(), + Style::LineNumber); + draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len); + buffer.puts(last_buffer_line_num, + 3 + max_line_num_len, + &unannotated_line, + Style::Quotation); + } + } + } + } + + // final step: take our styled buffer, render it, then output it + emit_to_destination(&buffer.render(), level, &mut self.dst); + + Ok(()) + } + fn emit_suggestion_default(&mut self, + suggestion: &CodeSuggestion, + level: &Level, + msg: &str, + max_line_num_len: usize) + -> io::Result<()> { + use std::borrow::Borrow; + + let primary_span = suggestion.msp.primary_span().unwrap(); + if let Some(ref cm) = self.cm { + let mut buffer = StyledBuffer::new(); + + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + + let lines = cm.span_to_lines(primary_span).unwrap(); + + assert!(!lines.lines.is_empty()); + + let complete = suggestion.splice_lines(cm.borrow()); + let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); + let display_lines = &lines.lines[..line_count]; + + let fm = &*lines.file; + // Calculate the widest number to format evenly + let max_digits = line_num_max_digits(display_lines.last().unwrap()); + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let mut lines = complete.lines(); + let mut row_num = 1; + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + buffer.append(row_num, line, Style::NoStyle); + row_num += 1; + } + + // if we elided some lines, add an ellipsis + if let Some(_) = lines.next() { + buffer.append(row_num, "...", Style::NoStyle); + } + emit_to_destination(&buffer.render(), level, &mut self.dst); + } + Ok(()) + } + fn emit_messages_default(&mut self, db: &DiagnosticBuilder) { + let max_line_num = self.get_max_line_num(db); + let max_line_num_len = max_line_num.to_string().len(); + + match self.emit_message_default(&db.span, + &db.message, + &db.code, + &db.level, + max_line_num_len, + false) { + Ok(()) => { + if !db.children.is_empty() { + let mut buffer = StyledBuffer::new(); + draw_col_separator(&mut buffer, 0, max_line_num_len + 1); + emit_to_destination(&buffer.render(), &db.level, &mut self.dst); + } + for child in &db.children { + match child.render_span { + Some(FullSpan(ref msp)) => { + match self.emit_message_default(msp, + &child.message, + &None, + &child.level, + max_line_num_len, + true) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + Some(Suggestion(ref cs)) => { + match self.emit_suggestion_default(cs, + &child.level, + &child.message, + max_line_num_len) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + None => { + match self.emit_message_default(&child.span, + &child.message, + &None, + &child.level, + max_line_num_len, + true) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } + } + } + } + Err(e) => panic!("failed to emit error: {}", e) + } + write!(&mut self.dst, "\n"); + } + fn emit_message_(&mut self, rsp: &RenderSpan, msg: &str, @@ -363,6 +912,7 @@ impl EmitterWriter { } for snippet_data in output_vec.iter() { + /* let rendered_lines = snippet_data.render_lines(); for rendered_line in &rendered_lines { for styled_string in &rendered_line.text { @@ -372,6 +922,8 @@ impl EmitterWriter { } write!(&mut self.dst, "\n")?; } + */ + emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); } } else { @@ -380,6 +932,8 @@ impl EmitterWriter { span_label.is_primary, span_label.label); } + emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); + /* let rendered_lines = snippet_data.render_lines(); for rendered_line in &rendered_lines { for styled_string in &rendered_line.text { @@ -389,6 +943,7 @@ impl EmitterWriter { } write!(&mut self.dst, "\n")?; } + */ } Ok(()) } @@ -413,6 +968,33 @@ impl EmitterWriter { } } +fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { + buffer.puts(line, col, "| ", Style::LineNumber); +} + +fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { + buffer.puts(line, col, "= ", Style::LineNumber); +} + +fn overlaps(a1: &Annotation, a2: &Annotation) -> bool { + (a2.start_col..a2.end_col).contains(a1.start_col) || + (a1.start_col..a1.end_col).contains(a2.start_col) +} + +fn emit_to_destination(rendered_buffer: &Vec>, + lvl: &Level, + dst: &mut Destination) -> io::Result<()> { + for line in rendered_buffer { + for part in line { + dst.apply_style(lvl.clone(), part.style); + write!(dst, "{}", part.text); + dst.reset_attrs()?; + } + write!(dst, "\n"); + } + Ok(()) +} + fn line_num_max_digits(line: &LineInfo) -> usize { let mut max_line_num = line.line_index + 1; let mut digits = 0; @@ -480,7 +1062,7 @@ fn stderr_isatty() -> bool { } } -enum Destination { +pub enum Destination { Terminal(Box), Raw(Box), } @@ -495,35 +1077,39 @@ impl Destination { fn apply_style(&mut self, lvl: Level, - _kind: &RenderedLineKind, style: Style) -> io::Result<()> { match style { - Style::FileNameStyle | - Style::LineAndColumn => { - } + Style::FileNameStyle | Style::LineAndColumn => {} Style::LineNumber => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))); } - Style::Quotation => { + Style::ErrorCode => { + try!(self.start_attr(term::Attr::Bold)); + //try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))); } - Style::OldSkoolNote => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?; + Style::Quotation => {} + Style::OldSchoolNote => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))); } - Style::OldSkoolNoteText => { - self.start_attr(term::Attr::Bold)?; + Style::OldSchoolNoteText | Style::HeaderMsg => { + try!(self.start_attr(term::Attr::Bold)); } Style::UnderlinePrimary | Style::LabelPrimary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(lvl.color()))); } - Style::UnderlineSecondary | Style::LabelSecondary => { - self.start_attr(term::Attr::Bold)?; - self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + Style::UnderlineSecondary | + Style::LabelSecondary => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))); } - Style::NoStyle => { + Style::NoStyle => {} + Style::Level(l) => { + try!(self.start_attr(term::Attr::Bold)); + try!(self.start_attr(term::Attr::ForegroundColor(l.color()))); } } Ok(()) diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 7c14c132382e..33781bed759f 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -49,6 +49,7 @@ use std::thread::panicking; pub mod emitter; pub mod snippet; pub mod registry; +pub mod styled_buffer; use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION }; use syntax_pos::{MacroBacktrace}; diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 33f40ffc71a9..525c83499fbe 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -13,9 +13,11 @@ use syntax_pos::{Span, FileMap, CharPos, LineInfo}; use check_old_skool; use CodeMapper; +use styled_buffer::StyledBuffer; use std::cmp; use std::rc::Rc; use std::mem; +use {Level}; #[derive(Clone)] pub enum FormatMode { @@ -49,38 +51,40 @@ pub struct FileInfo { format_mode: FormatMode, } -#[derive(Clone, Debug)] -struct Line { - line_index: usize, - annotations: Vec, +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +pub struct Line { + pub line_index: usize, + pub annotations: Vec, } #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -struct Annotation { +pub struct Annotation { /// Start column, 0-based indexing -- counting *characters*, not /// utf-8 bytes. Note that it is important that this field goes /// first, so that when we sort, we sort orderings by start /// column. - start_col: usize, + pub start_col: usize, /// End column within the line (exclusive) - end_col: usize, + pub end_col: usize, /// Is this annotation derived from primary span - is_primary: bool, + pub is_primary: bool, /// Is this a large span minimized down to a smaller span - is_minimized: bool, + pub is_minimized: bool, /// Optional label to display adjacent to the annotation. - label: Option, + pub label: Option, } +/* #[derive(Debug)] pub struct RenderedLine { pub text: Vec, pub kind: RenderedLineKind, } +*/ #[derive(Debug)] pub struct StyledString { @@ -88,14 +92,9 @@ pub struct StyledString { pub style: Style, } -#[derive(Debug)] -pub struct StyledBuffer { - text: Vec>, - styles: Vec> -} - #[derive(Copy, Clone, Debug, PartialEq)] pub enum Style { + HeaderMsg, FileNameStyle, LineAndColumn, LineNumber, @@ -104,11 +103,14 @@ pub enum Style { UnderlineSecondary, LabelPrimary, LabelSecondary, - OldSkoolNoteText, - OldSkoolNote, + OldSchoolNoteText, + OldSchoolNote, NoStyle, + ErrorCode, + Level(Level), } +/* #[derive(Debug, Clone)] pub enum RenderedLineKind { PrimaryFileName, @@ -120,6 +122,7 @@ pub enum RenderedLineKind { Annotations, Elision, } +*/ impl SnippetData { pub fn new(codemap: Rc, @@ -186,15 +189,15 @@ impl SnippetData { self.files.last_mut().unwrap() } - pub fn render_lines(&self) -> Vec { + pub fn render_lines(&self) -> Vec> { debug!("SnippetData::render_lines()"); let mut rendered_lines: Vec<_> = self.files.iter() .flat_map(|f| f.render_file_lines(&self.codemap)) .collect(); - prepend_prefixes(&mut rendered_lines, &self.format_mode); - trim_lines(&mut rendered_lines); + //prepend_prefixes(&mut rendered_lines, &self.format_mode); + //trim_lines(&mut rendered_lines); rendered_lines } } @@ -215,6 +218,7 @@ impl StringSource for Vec { } } +/* impl From<(S, Style, RenderedLineKind)> for RenderedLine where S: StringSource { @@ -282,96 +286,20 @@ impl RenderedLineKind { } } } - -impl StyledBuffer { - fn new() -> StyledBuffer { - StyledBuffer { text: vec![], styles: vec![] } - } - - fn render(&self, source_kind: RenderedLineKind) -> Vec { - let mut output: Vec = vec![]; - let mut styled_vec: Vec = vec![]; - - for (row, row_style) in self.text.iter().zip(&self.styles) { - let mut current_style = Style::NoStyle; - let mut current_text = String::new(); - - for (&c, &s) in row.iter().zip(row_style) { - if s != current_style { - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - current_style = s; - current_text = String::new(); - } - current_text.push(c); - } - if !current_text.is_empty() { - styled_vec.push(StyledString { text: current_text, style: current_style }); - } - - if output.is_empty() { - //We know our first output line is source and the rest are highlights and labels - output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() }); - } else { - output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations }); - } - styled_vec = vec![]; - } - - output - } - - fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { - while line >= self.text.len() { - self.text.push(vec![]); - self.styles.push(vec![]); - } - - if col < self.text[line].len() { - self.text[line][col] = chr; - self.styles[line][col] = style; - } else { - let mut i = self.text[line].len(); - while i < col { - let s = match self.text[0].get(i) { - Some(&'\t') => '\t', - _ => ' ' - }; - self.text[line].push(s); - self.styles[line].push(Style::NoStyle); - i += 1; - } - self.text[line].push(chr); - self.styles[line].push(style); - } - } - - fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) { - let mut n = col; - for c in string.chars() { - self.putc(line, n, c, style); - n += 1; - } - } - - fn set_style(&mut self, line: usize, col: usize, style: Style) { - if self.styles.len() > line && self.styles[line].len() > col { - self.styles[line][col] = style; - } - } - - fn append(&mut self, line: usize, string: &str, style: Style) { - if line >= self.text.len() { - self.puts(line, 0, string, style); - } else { - let col = self.text[line].len(); - self.puts(line, col, string, style); - } - } -} +*/ impl FileInfo { + fn get_max_line_num(&self) -> usize { + let mut max = 0; + + for line in &self.lines { + if line.line_index > max { + max = line.line_index; + } + } + max + } + fn push_lines(&mut self, lines: &[LineInfo], is_primary: bool, @@ -469,16 +397,13 @@ impl FileInfo { return line_index - first_line_index; } - fn render_file_lines(&self, codemap: &Rc) -> Vec { + fn render_file_lines(&self, codemap: &Rc) -> Vec> { let old_school = match self.format_mode { FormatMode::OriginalErrorFormat => true, FormatMode::NewErrorFormat => false, FormatMode::EnvironmentSelected => check_old_skool() }; - // As a first step, we elide any instance of more than one - // continuous unannotated line. - let mut lines_iter = self.lines.iter(); let mut output = vec![]; @@ -487,39 +412,27 @@ impl FileInfo { match self.primary_span { Some(span) => { let lo = codemap.lookup_char_pos(span.lo); - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }, StyledString { text: format!(":{}:{}", lo.line, lo.col.0 + 1), style: Style::LineAndColumn, - }], - kind: RenderedLineKind::PrimaryFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { + }]); + output.push(vec![StyledString { text: "".to_string(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); + }]); } None => { - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: self.file.name.clone(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::OtherFileName, - }); - output.push(RenderedLine { - text: vec![StyledString { + }]); + output.push(vec![StyledString { text: "".to_string(), style: Style::FileNameStyle, - }], - kind: RenderedLineKind::Annotations, - }); + }]); } } } @@ -541,8 +454,7 @@ impl FileInfo { //as an old-style note if !line.annotations[0].is_primary { if let Some(ann) = line.annotations[0].label.clone() { - output.push(RenderedLine { - text: vec![StyledString { + output.push(vec![StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }, StyledString { @@ -551,31 +463,29 @@ impl FileInfo { style: Style::LineAndColumn, }, StyledString { text: format!("note: "), - style: Style::OldSkoolNote, + style: Style::OldSchoolNote, }, StyledString { text: format!("{}", ann), - style: Style::OldSkoolNoteText, - }], - kind: RenderedLineKind::Annotations, - }); + style: Style::OldSchoolNoteText, + }]); } } - rendered_lines[0].text.insert(0, StyledString { + rendered_lines[0].insert(0, StyledString { text: format!(":{} ", lo.line), style: Style::LineAndColumn, }); - rendered_lines[0].text.insert(0, StyledString { + rendered_lines[0].insert(0, StyledString { text: lo.file.name.clone(), style: Style::FileNameStyle, }); let gap_amount = - rendered_lines[0].text[0].text.len() + - rendered_lines[0].text[1].text.len(); + rendered_lines[0][0].text.len() + + rendered_lines[0][1].text.len(); assert!(rendered_lines.len() >= 2, "no annotations resulted from: {:?}", line); for i in 1..rendered_lines.len() { - rendered_lines[i].text.insert(0, StyledString { + rendered_lines[i].insert(0, StyledString { text: vec![" "; gap_amount].join(""), style: Style::NoStyle }); @@ -598,9 +508,7 @@ impl FileInfo { next_line = lines_iter.next(); } if unannotated_lines > 1 { - output.push(RenderedLine::from((String::new(), - Style::NoStyle, - RenderedLineKind::Elision))); + output.push(vec![StyledString{ text: String::new(), style: Style::NoStyle}]); } else if let Some(line) = unannotated_line { output.append(&mut self.render_line(line)); } @@ -609,7 +517,7 @@ impl FileInfo { output } - fn render_line(&self, line: &Line) -> Vec { + fn render_line(&self, line: &Line) -> Vec> { let old_school = match self.format_mode { FormatMode::OriginalErrorFormat => true, FormatMode::NewErrorFormat => false, @@ -618,10 +526,12 @@ impl FileInfo { let source_string = self.file.get_line(line.line_index) .unwrap_or(""); + /* let source_kind = RenderedLineKind::SourceText { file: self.file.clone(), line_index: line.line_index, }; + */ let mut styled_buffer = StyledBuffer::new(); @@ -629,7 +539,7 @@ impl FileInfo { styled_buffer.append(0, &source_string, Style::Quotation); if line.annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } // We want to display like this: @@ -666,7 +576,7 @@ impl FileInfo { if annotation.is_primary { Style::UnderlinePrimary } else { - Style::OldSkoolNote + Style::OldSchoolNote }); } else { @@ -674,7 +584,7 @@ impl FileInfo { if annotation.is_primary { Style::UnderlinePrimary } else { - Style::OldSkoolNote + Style::OldSchoolNote }); } } @@ -704,10 +614,10 @@ impl FileInfo { // If there are no annotations that need text, we're done. if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } if old_school { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } // Now add the text labels. We try, when possible, to stick the rightmost @@ -767,7 +677,7 @@ impl FileInfo { // If that's the last annotation, we're done if labeled_annotations.is_empty() { - return styled_buffer.render(source_kind); + return styled_buffer.render(); } for (index, annotation) in labeled_annotations.iter().enumerate() { @@ -796,10 +706,11 @@ impl FileInfo { } } - styled_buffer.render(source_kind) + styled_buffer.render() } } +/* fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) { let old_school = match *format_mode { FormatMode::OriginalErrorFormat => true, @@ -882,6 +793,7 @@ fn trim_lines(rendered_lines: &mut [RenderedLine]) { } } } +*/ impl Line { fn new(line_index: usize) -> Line { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 0a60b7fd430c..570c0a09bc41 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -26,7 +26,7 @@ use std::rc::Rc; use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute}; use errors; -use errors::snippet::{RenderedLine, SnippetData}; +use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; use ext::base::{ExtCtxt, DummyMacroLoader}; diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 39bb5956312b..7dfe19452a2a 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -568,7 +568,7 @@ impl Sub for CharPos { // /// A source code location used for error reporting -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Loc { /// Information about the original source pub file: Rc, From 8612838dd04ae9343088955136626b1c1b579999 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 12 Jul 2016 10:41:26 -0400 Subject: [PATCH 15/53] Add back in old school mode --- src/librustc_errors/emitter.rs | 289 ++++++++++++++++++++++++++++++--- 1 file changed, 264 insertions(+), 25 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 52c0ea931408..037d64ba128f 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -34,31 +34,14 @@ pub trait Emitter { impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { - if check_old_skool() { - self.emit_message(&FullSpan(db.span.clone()), - &db.message, - db.code.as_ref().map(|s| &**s), - db.level, - true, - true); - let db_span = FullSpan(db.span.clone()); + let old_school = match self.format_mode { + FormatMode::NewErrorFormat => false, + FormatMode::OriginalErrorFormat => true, + FormatMode::EnvironmentSelected => check_old_skool() + }; - for child in &db.children { - let render_span = child.render_span - .clone() - .unwrap_or_else( - || FullSpan(child.span.clone())); - let (render_span, show_snippet) = match render_span.span().primary_span() { - None => (db_span.clone(), false), - _ => (render_span, true) - }; - self.emit_message(&render_span, - &child.message, - None, - child.level, - false, - show_snippet); - } + if old_school { + self.emit_messages_old_school(db); } else { self.emit_messages_default(db); } @@ -718,6 +701,239 @@ impl EmitterWriter { } write!(&mut self.dst, "\n"); } + fn emit_message_old_school(&mut self, + msp: &MultiSpan, + msg: &str, + code: &Option, + level: &Level, + show_snippet: bool) + -> io::Result<()> { + let mut buffer = StyledBuffer::new(); + + let loc = match msp.primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), + Some(ps) => if let Some(ref cm) = self.cm { + cm.span_to_string(ps) + } else { + "".to_string() + }, + None => { + "".to_string() + } + }; + if loc != "" { + buffer.append(0, &loc, Style::NoStyle); + buffer.append(0, " ", Style::NoStyle); + } + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + buffer.append(0, " ", Style::NoStyle); + match code { + &Some(ref code) => { + buffer.append(0, "[", Style::ErrorCode); + buffer.append(0, &code, Style::ErrorCode); + buffer.append(0, "]", Style::ErrorCode); + } + _ => {} + } + + if !show_snippet { + emit_to_destination(&buffer.render(), level, &mut self.dst); + return Ok(()); + } + + // Watch out for various nasty special spans; don't try to + // print any filename or anything for those. + match msp.primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => { + emit_to_destination(&buffer.render(), level, &mut self.dst); + return Ok(()); + } + _ => { } + } + + let mut annotated_files = self.preprocess_annotations(msp); + + if let (Some(ref cm), Some(ann_file), Some(ref primary_span)) = + (self.cm.as_ref(), annotated_files.first(), msp.primary_span().as_ref()) { + + // Next, print the source line and its squiggle + // for old school mode, we will render them to the buffer, then insert the file loc + // (or space the same amount) in front of the line and the squiggle + let source_string = ann_file.file.get_line(ann_file.lines[0].line_index - 1) + .unwrap_or(""); + + let line_offset = buffer.num_lines(); + + let lo = cm.lookup_char_pos(primary_span.lo); + //Before each secondary line in old skool-mode, print the label + //as an old-style note + let file_pos = format!("{}:{} ", lo.file.name.clone(), lo.line); + let file_pos_len = file_pos.len(); + + // First create the source line we will highlight. + buffer.puts(line_offset, 0, &file_pos, Style::FileNameStyle); + buffer.puts(line_offset, file_pos_len, &source_string, Style::Quotation); + // Sort the annotations by (start, end col) + let mut annotations = ann_file.lines[0].annotations.clone(); + + // Next, create the highlight line. + for annotation in &annotations { + for p in annotation.start_col..annotation.end_col { + if p == annotation.start_col { + buffer.putc(line_offset + 1, + file_pos_len + p, + '^', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } else { + buffer.putc(line_offset + 1, + file_pos_len + p, + '~', + if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::OldSchoolNote + }); + } + } + } + } + if let Some(ref primary_span) = msp.primary_span().as_ref() { + self.render_macro_backtrace_old_school(primary_span, &mut buffer)?; + } + + match code { + &Some(ref code) if self.registry.as_ref() + .and_then(|registry| registry.find_description(code)) + .is_some() => { + let msg = "run `rustc --explain ".to_string() + &code.to_string() + + "` to see a detailed explanation"; + + let line_offset = buffer.num_lines(); + buffer.append(line_offset, &loc, Style::NoStyle); + buffer.append(line_offset, " ", Style::NoStyle); + buffer.append(line_offset, &Level::Help.to_string(), Style::Level(Level::Help)); + buffer.append(line_offset, ": ", Style::HeaderMsg); + buffer.append(line_offset, &msg, Style::HeaderMsg); + } + _ => () + } + + // final step: take our styled buffer, render it, then output it + emit_to_destination(&buffer.render(), level, &mut self.dst); + Ok(()) + } + fn emit_suggestion_old_school(&mut self, + suggestion: &CodeSuggestion, + level: &Level, + msg: &str) + -> io::Result<()> { + use std::borrow::Borrow; + + let primary_span = suggestion.msp.primary_span().unwrap(); + if let Some(ref cm) = self.cm { + let mut buffer = StyledBuffer::new(); + + let loc = cm.span_to_string(primary_span); + + if loc != "" { + buffer.append(0, &loc, Style::NoStyle); + buffer.append(0, " ", Style::NoStyle); + } + + buffer.append(0, &level.to_string(), Style::Level(level.clone())); + buffer.append(0, ": ", Style::HeaderMsg); + buffer.append(0, msg, Style::HeaderMsg); + + let lines = cm.span_to_lines(primary_span).unwrap(); + + assert!(!lines.lines.is_empty()); + + let complete = suggestion.splice_lines(cm.borrow()); + let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); + let display_lines = &lines.lines[..line_count]; + + let fm = &*lines.file; + // Calculate the widest number to format evenly + let max_digits = line_num_max_digits(display_lines.last().unwrap()); + + // print the suggestion without any line numbers, but leave + // space for them. This helps with lining up with previous + // snippets from the actual error being reported. + let mut lines = complete.lines(); + let mut row_num = 1; + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { + buffer.append(row_num, &fm.name, Style::FileNameStyle); + for i in 0..max_digits+2 { + buffer.append(row_num, &" ", Style::NoStyle); + } + buffer.append(row_num, line, Style::NoStyle); + row_num += 1; + } + + // if we elided some lines, add an ellipsis + if let Some(_) = lines.next() { + buffer.append(row_num, "...", Style::NoStyle); + } + emit_to_destination(&buffer.render(), level, &mut self.dst); + } + Ok(()) + } + + fn emit_messages_old_school(&mut self, db: &DiagnosticBuilder) { + match self.emit_message_old_school(&db.span, + &db.message, + &db.code, + &db.level, + true) { + Ok(()) => { + for child in &db.children { + let (span, show_snippet) = if child.span.primary_spans().is_empty() { + (db.span.clone(), false) + } else { + (child.span.clone(), true) + }; + + match child.render_span { + Some(FullSpan(ref msp)) => { + match self.emit_message_old_school(&span, + &child.message, + &None, + &child.level, + show_snippet) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + Some(Suggestion(ref cs)) => { + match self.emit_suggestion_old_school(cs, + &child.level, + &child.message) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + }, + None => { + match self.emit_message_old_school(&span, + &child.message, + &None, + &child.level, + show_snippet) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } + } + } + } + Err(e) => panic!("failed to emit error: {}", e) + } + } fn emit_message_(&mut self, rsp: &RenderSpan, @@ -948,6 +1164,29 @@ impl EmitterWriter { Ok(()) } + fn render_macro_backtrace_old_school(&mut self, + sp: &Span, + buffer: &mut StyledBuffer) -> io::Result<()> { + if let Some(ref cm) = self.cm { + for trace in cm.macro_backtrace(sp.clone()) { + let line_offset = buffer.num_lines(); + + let mut diag_string = + format!("in this expansion of {}", trace.macro_decl_name); + if let Some(def_site_span) = trace.def_site_span { + diag_string.push_str( + &format!(" (defined in {})", + cm.span_to_filename(def_site_span))); + } + let snippet = cm.span_to_string(trace.call_site); + buffer.append(line_offset, &format!("{} ", snippet), Style::NoStyle); + buffer.append(line_offset, "Note", Style::Level(Level::Note)); + buffer.append(line_offset, ": ", Style::NoStyle); + buffer.append(line_offset, &diag_string, Style::OldSchoolNoteText); + } + } + Ok(()) + } fn print_macro_backtrace(&mut self, sp: Span) -> io::Result<()> { @@ -1087,7 +1326,7 @@ impl Destination { } Style::ErrorCode => { try!(self.start_attr(term::Attr::Bold)); - //try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))); + try!(self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA))); } Style::Quotation => {} Style::OldSchoolNote => { From f481879d2ae82a6f6ebe33202d4a1cfa8aece5ed Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 12 Jul 2016 12:37:57 -0400 Subject: [PATCH 16/53] Fix a couple UI test failures --- src/librustc_errors/emitter.rs | 27 +++++++++++-------- .../ui/mismatched_types/issue-26480.stderr | 19 ++++++------- src/test/ui/mismatched_types/main.stderr | 14 +++++----- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 037d64ba128f..5147318d4a63 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -187,23 +187,24 @@ impl EmitterWriter { if let Some(ref cm) = self.cm { for span_label in msp.span_labels() { - let lo = cm.lookup_char_pos(span_label.span.lo); - let hi = cm.lookup_char_pos(span_label.span.hi); + let mut lo = cm.lookup_char_pos(span_label.span.lo); + let mut hi = cm.lookup_char_pos(span_label.span.hi); + let mut is_minimized = false; // If the span is multi-line, simplify down to the span of one character - let (start_col, mut end_col, is_minimized) = if lo.line != hi.line { - (lo.col, CharPos(lo.col.0 + 1), true) - } else { - (lo.col, hi.col, false) - }; + if lo.line != hi.line { + hi.line = lo.line; + hi.col = CharPos(lo.col.0 + 1); + is_minimized = true; + } // Watch out for "empty spans". If we get a span like 6..6, we // want to just display a `^` at 6, so convert that to // 6..7. This is degenerate input, but it's best to degrade // gracefully -- and the parser likes to supply a span like // that for EOF, in particular. - if start_col == end_col { - end_col.0 += 1; + if lo.col == hi.col { + hi.col = CharPos(lo.col.0 + 1); } add_annotation_to_file(&mut output, @@ -530,7 +531,7 @@ impl EmitterWriter { buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); let loc = primary_lo.clone(); buffer.append(buffer_msg_line_offset, - &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0), + &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1), Style::LineAndColumn); for i in 0..max_line_num_len { buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); @@ -593,6 +594,10 @@ impl EmitterWriter { } } + if let Some(ref primary_span) = msp.primary_span().as_ref() { + self.render_macro_backtrace_old_school(primary_span, &mut buffer)?; + } + // final step: take our styled buffer, render it, then output it emit_to_destination(&buffer.render(), level, &mut self.dst); @@ -1180,7 +1185,7 @@ impl EmitterWriter { } let snippet = cm.span_to_string(trace.call_site); buffer.append(line_offset, &format!("{} ", snippet), Style::NoStyle); - buffer.append(line_offset, "Note", Style::Level(Level::Note)); + buffer.append(line_offset, "note", Style::Level(Level::Note)); buffer.append(line_offset, ": ", Style::NoStyle); buffer.append(line_offset, &diag_string, Style::OldSchoolNoteText); } diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index c00594a59c11..63f2ab4d7c6b 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -1,15 +1,16 @@ -error: mismatched types [--explain E0308] +error[E0308]: mismatched types --> $DIR/issue-26480.rs:27:19 - |> -27 |> $arr.len() * size_of($arr[0])); - |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize -$DIR/issue-26480.rs:38:5: 38:19: note: in this expansion of write! (defined in $DIR/issue-26480.rs) + | +27 | $arr.len() * size_of($arr[0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize +$DIR/issue-26480.rs:38:5: 38:19 note: in this expansion of write! (defined in $DIR/issue-26480.rs) error: non-scalar cast: `_` as `()` --> $DIR/issue-26480.rs:33:19 - |> -33 |> ($x:expr) => ($x as ()) - |> ^^^^^^^^ -$DIR/issue-26480.rs:39:5: 39:14: note: in this expansion of cast! (defined in $DIR/issue-26480.rs) + | +33 | ($x:expr) => ($x as ()) + | ^^^^^^^^ +$DIR/issue-26480.rs:39:5: 39:14 note: in this expansion of cast! (defined in $DIR/issue-26480.rs) error: aborting due to 2 previous errors + diff --git a/src/test/ui/mismatched_types/main.stderr b/src/test/ui/mismatched_types/main.stderr index 1af332ee5bea..ae1d417eca9e 100644 --- a/src/test/ui/mismatched_types/main.stderr +++ b/src/test/ui/mismatched_types/main.stderr @@ -1,9 +1,11 @@ -error: mismatched types [--explain E0308] +error[E0308]: mismatched types --> $DIR/main.rs:14:18 - |> -14 |> let x: u32 = ( - |> ^ expected u32, found () -note: expected type `u32` -note: found type `()` + | +14 | let x: u32 = ( + | ^ expected u32, found () + | + = note: expected type `u32` + = note: found type `()` error: aborting due to previous error + From 2f2c3e178325dc1837badcd7573c2c0905fab979 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 12 Jul 2016 15:18:16 -0400 Subject: [PATCH 17/53] DCE and fixing some internal tests --- src/librustc_errors/emitter.rs | 406 ++---------------- src/librustc_errors/lib.rs | 10 - src/librustc_errors/snippet.rs | 733 +-------------------------------- src/libsyntax/codemap.rs | 196 ++++----- 4 files changed, 132 insertions(+), 1213 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 5147318d4a63..901cd3403c1d 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -14,13 +14,12 @@ use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, LineInfo, use registry; use check_old_skool; -use {Level, RenderSpan, CodeSuggestion, DiagnosticBuilder, CodeMapper}; +use {Level, CodeSuggestion, DiagnosticBuilder, CodeMapper}; use RenderSpan::*; -use Level::*; -use snippet::{SnippetData, StyledString, Style, FormatMode, Annotation, Line}; +use snippet::{StyledString, Style, FormatMode, Annotation, Line}; use styled_buffer::StyledBuffer; -use std::{cmp, fmt}; +use std::cmp; use std::io::prelude::*; use std::io; use std::rc::Rc; @@ -73,10 +72,6 @@ pub struct EmitterWriter { registry: Option, cm: Option>, - /// Is this the first error emitted thus far? If not, we emit a - /// `\n` before the top-level errors. - first: bool, - // For now, allow an old-school mode while we transition format_mode: FormatMode } @@ -112,13 +107,11 @@ impl EmitterWriter { EmitterWriter { dst: dst, registry: registry, cm: code_map, - first: true, format_mode: format_mode.clone() } } else { EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map, - first: true, format_mode: format_mode.clone() } } } @@ -131,23 +124,9 @@ impl EmitterWriter { EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map, - first: true, format_mode: format_mode.clone() } } - fn emit_message(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool) { - match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) { - Ok(()) => { } - Err(e) => panic!("failed to emit error: {}", e) - } - } - fn preprocess_annotations(&self, msp: &MultiSpan) -> Vec { fn add_annotation_to_file(file_vec: &mut Vec, file: Rc, @@ -187,7 +166,7 @@ impl EmitterWriter { if let Some(ref cm) = self.cm { for span_label in msp.span_labels() { - let mut lo = cm.lookup_char_pos(span_label.span.lo); + let lo = cm.lookup_char_pos(span_label.span.lo); let mut hi = cm.lookup_char_pos(span_label.span.hi); let mut is_minimized = false; @@ -478,7 +457,7 @@ impl EmitterWriter { if msp.primary_spans().is_empty() && msp.span_labels().is_empty() && is_secondary { // This is a secondary message with no span info - for i in 0..max_line_num_len { + for _ in 0..max_line_num_len { buffer.prepend(0, " ", Style::NoStyle); } draw_note_separator(&mut buffer, 0, max_line_num_len + 1); @@ -511,7 +490,7 @@ impl EmitterWriter { cm.lookup_char_pos(primary_span.lo) } else { // If we don't have span information, emit and exit - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; return Ok(()); }; if let Ok(pos) = @@ -526,19 +505,19 @@ impl EmitterWriter { let is_primary = primary_lo.file.name == annotated_file.file.name; if is_primary { // remember where we are in the output buffer for easy reference - let mut buffer_msg_line_offset = buffer.num_lines(); + let buffer_msg_line_offset = buffer.num_lines(); buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); let loc = primary_lo.clone(); buffer.append(buffer_msg_line_offset, &format!("{}:{}:{}", loc.file.name, loc.line, loc.col.0 + 1), Style::LineAndColumn); - for i in 0..max_line_num_len { + for _ in 0..max_line_num_len { buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle); } } else { // remember where we are in the output buffer for easy reference - let mut buffer_msg_line_offset = buffer.num_lines(); + let buffer_msg_line_offset = buffer.num_lines(); // Add spacing line draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); @@ -548,13 +527,13 @@ impl EmitterWriter { buffer.append(buffer_msg_line_offset + 1, &annotated_file.file.name, Style::LineAndColumn); - for i in 0..max_line_num_len { + for _ in 0..max_line_num_len { buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle); } } // Put in the spacer between the location and annotated source - let mut buffer_msg_line_offset = buffer.num_lines(); + let buffer_msg_line_offset = buffer.num_lines(); draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); // Next, output the annotate source for this file @@ -599,7 +578,7 @@ impl EmitterWriter { } // final step: take our styled buffer, render it, then output it - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; Ok(()) } @@ -624,12 +603,6 @@ impl EmitterWriter { assert!(!lines.lines.is_empty()); let complete = suggestion.splice_lines(cm.borrow()); - let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); - let display_lines = &lines.lines[..line_count]; - - let fm = &*lines.file; - // Calculate the widest number to format evenly - let max_digits = line_num_max_digits(display_lines.last().unwrap()); // print the suggestion without any line numbers, but leave // space for them. This helps with lining up with previous @@ -646,7 +619,7 @@ impl EmitterWriter { if let Some(_) = lines.next() { buffer.append(row_num, "...", Style::NoStyle); } - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; } Ok(()) } @@ -664,7 +637,10 @@ impl EmitterWriter { if !db.children.is_empty() { let mut buffer = StyledBuffer::new(); draw_col_separator(&mut buffer, 0, max_line_num_len + 1); - emit_to_destination(&buffer.render(), &db.level, &mut self.dst); + match emit_to_destination(&buffer.render(), &db.level, &mut self.dst) { + Ok(()) => (), + Err(e) => panic!("failed to emit error: {}", e) + } } for child in &db.children { match child.render_span { @@ -704,7 +680,10 @@ impl EmitterWriter { } Err(e) => panic!("failed to emit error: {}", e) } - write!(&mut self.dst, "\n"); + match write!(&mut self.dst, "\n") { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } } fn emit_message_old_school(&mut self, msp: &MultiSpan, @@ -744,7 +723,7 @@ impl EmitterWriter { } if !show_snippet { - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; return Ok(()); } @@ -752,13 +731,13 @@ impl EmitterWriter { // print any filename or anything for those. match msp.primary_span() { Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => { - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; return Ok(()); } _ => { } } - let mut annotated_files = self.preprocess_annotations(msp); + let annotated_files = self.preprocess_annotations(msp); if let (Some(ref cm), Some(ann_file), Some(ref primary_span)) = (self.cm.as_ref(), annotated_files.first(), msp.primary_span().as_ref()) { @@ -781,7 +760,7 @@ impl EmitterWriter { buffer.puts(line_offset, 0, &file_pos, Style::FileNameStyle); buffer.puts(line_offset, file_pos_len, &source_string, Style::Quotation); // Sort the annotations by (start, end col) - let mut annotations = ann_file.lines[0].annotations.clone(); + let annotations = ann_file.lines[0].annotations.clone(); // Next, create the highlight line. for annotation in &annotations { @@ -830,7 +809,7 @@ impl EmitterWriter { } // final step: take our styled buffer, render it, then output it - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; Ok(()) } fn emit_suggestion_old_school(&mut self, @@ -874,7 +853,7 @@ impl EmitterWriter { let mut row_num = 1; for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { buffer.append(row_num, &fm.name, Style::FileNameStyle); - for i in 0..max_digits+2 { + for _ in 0..max_digits+2 { buffer.append(row_num, &" ", Style::NoStyle); } buffer.append(row_num, line, Style::NoStyle); @@ -885,7 +864,7 @@ impl EmitterWriter { if let Some(_) = lines.next() { buffer.append(row_num, "...", Style::NoStyle); } - emit_to_destination(&buffer.render(), level, &mut self.dst); + emit_to_destination(&buffer.render(), level, &mut self.dst)?; } Ok(()) } @@ -905,7 +884,7 @@ impl EmitterWriter { }; match child.render_span { - Some(FullSpan(ref msp)) => { + Some(FullSpan(_)) => { match self.emit_message_old_school(&span, &child.message, &None, @@ -940,235 +919,6 @@ impl EmitterWriter { } } - fn emit_message_(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level, - is_header: bool, - show_snippet: bool) - -> io::Result<()> { - let old_school = match self.format_mode { - FormatMode::NewErrorFormat => false, - FormatMode::OriginalErrorFormat => true, - FormatMode::EnvironmentSelected => check_old_skool() - }; - - if is_header { - if self.first { - self.first = false; - } else { - if !old_school { - write!(self.dst, "\n")?; - } - } - } - - match code { - Some(code) if self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) - .is_some() => { - let code_with_explain = String::from("--explain ") + code; - if old_school { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => if let Some(ref cm) = self.cm { - cm.span_to_string(ps) - } else { - "".to_string() - }, - None => "".to_string() - }; - print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))? - } - else { - print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))? - } - } - _ => { - if old_school { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => if let Some(ref cm) = self.cm { - cm.span_to_string(ps) - } else { - "".to_string() - }, - None => "".to_string() - }; - print_diagnostic(&mut self.dst, &loc, lvl, msg, code)? - } - else { - print_diagnostic(&mut self.dst, "", lvl, msg, code)? - } - } - } - - if !show_snippet { - return Ok(()); - } - - // Watch out for various nasty special spans; don't try to - // print any filename or anything for those. - match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => { - return Ok(()); - } - _ => { } - } - - // Otherwise, print out the snippet etc as needed. - match *rsp { - FullSpan(ref msp) => { - self.highlight_lines(msp, lvl)?; - if let Some(primary_span) = msp.primary_span() { - self.print_macro_backtrace(primary_span)?; - } - } - Suggestion(ref suggestion) => { - self.highlight_suggestion(suggestion)?; - if let Some(primary_span) = rsp.span().primary_span() { - self.print_macro_backtrace(primary_span)?; - } - } - } - if old_school { - match code { - Some(code) if self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) - .is_some() => { - let loc = match rsp.span().primary_span() { - Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), - Some(ps) => if let Some(ref cm) = self.cm { - cm.span_to_string(ps) - } else { - "".to_string() - }, - None => "".to_string() - }; - let msg = "run `rustc --explain ".to_string() + &code.to_string() + - "` to see a detailed explanation"; - print_diagnostic(&mut self.dst, &loc, Level::Help, &msg, - None)? - } - _ => () - } - } - Ok(()) - } - - fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()> - { - use std::borrow::Borrow; - - let primary_span = suggestion.msp.primary_span().unwrap(); - if let Some(ref cm) = self.cm { - let lines = cm.span_to_lines(primary_span).unwrap(); - - assert!(!lines.lines.is_empty()); - - let complete = suggestion.splice_lines(cm.borrow()); - let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); - let display_lines = &lines.lines[..line_count]; - - let fm = &*lines.file; - // Calculate the widest number to format evenly - let max_digits = line_num_max_digits(display_lines.last().unwrap()); - - // print the suggestion without any line numbers, but leave - // space for them. This helps with lining up with previous - // snippets from the actual error being reported. - let mut lines = complete.lines(); - for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { - write!(&mut self.dst, "{0}:{1:2$} {3}\n", - fm.name, "", max_digits, line)?; - } - - // if we elided some lines, add an ellipsis - if let Some(_) = lines.next() { - write!(&mut self.dst, "{0:1$} {0:2$} ...\n", - "", fm.name.len(), max_digits)?; - } - } - Ok(()) - } - - pub fn highlight_lines(&mut self, - msp: &MultiSpan, - lvl: Level) - -> io::Result<()> - { - // Check to see if we have any lines to highlight, exit early if not - match self.cm { - None => return Ok(()), - _ => () - } - - let old_school = match self.format_mode { - FormatMode::NewErrorFormat => false, - FormatMode::OriginalErrorFormat => true, - FormatMode::EnvironmentSelected => check_old_skool() - }; - - let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(), - msp.primary_span(), - self.format_mode.clone()); - if old_school { - let mut output_vec = vec![]; - - for span_label in msp.span_labels() { - let mut snippet_data = SnippetData::new(self.cm.as_ref().unwrap().clone(), - Some(span_label.span), - self.format_mode.clone()); - - snippet_data.push(span_label.span, - span_label.is_primary, - span_label.label); - if span_label.is_primary { - output_vec.insert(0, snippet_data); - } - else { - output_vec.push(snippet_data); - } - } - - for snippet_data in output_vec.iter() { - /* - let rendered_lines = snippet_data.render_lines(); - for rendered_line in &rendered_lines { - for styled_string in &rendered_line.text { - self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; - write!(&mut self.dst, "{}", styled_string.text)?; - self.dst.reset_attrs()?; - } - write!(&mut self.dst, "\n")?; - } - */ - emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); - } - } - else { - for span_label in msp.span_labels() { - snippet_data.push(span_label.span, - span_label.is_primary, - span_label.label); - } - emit_to_destination(&snippet_data.render_lines(), &lvl, &mut self.dst); - /* - let rendered_lines = snippet_data.render_lines(); - for rendered_line in &rendered_lines { - for styled_string in &rendered_line.text { - self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; - write!(&mut self.dst, "{}", styled_string.text)?; - self.dst.reset_attrs()?; - } - write!(&mut self.dst, "\n")?; - } - */ - } - Ok(()) - } - fn render_macro_backtrace_old_school(&mut self, sp: &Span, buffer: &mut StyledBuffer) -> io::Result<()> { @@ -1192,24 +942,6 @@ impl EmitterWriter { } Ok(()) } - fn print_macro_backtrace(&mut self, - sp: Span) - -> io::Result<()> { - if let Some(ref cm) = self.cm { - for trace in cm.macro_backtrace(sp) { - let mut diag_string = - format!("in this expansion of {}", trace.macro_decl_name); - if let Some(def_site_span) = trace.def_site_span { - diag_string.push_str( - &format!(" (defined in {})", - cm.span_to_filename(def_site_span))); - } - let snippet = cm.span_to_string(trace.call_site); - print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; - } - } - Ok(()) - } } fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { @@ -1230,11 +962,11 @@ fn emit_to_destination(rendered_buffer: &Vec>, dst: &mut Destination) -> io::Result<()> { for line in rendered_buffer { for part in line { - dst.apply_style(lvl.clone(), part.style); - write!(dst, "{}", part.text); + dst.apply_style(lvl.clone(), part.style)?; + write!(dst, "{}", part.text)?; dst.reset_attrs()?; } - write!(dst, "\n"); + write!(dst, "\n")?; } Ok(()) } @@ -1249,40 +981,6 @@ fn line_num_max_digits(line: &LineInfo) -> usize { digits } -fn print_diagnostic(dst: &mut Destination, - topic: &str, - lvl: Level, - msg: &str, - code: Option<&str>) - -> io::Result<()> { - if !topic.is_empty() { - let old_school = check_old_skool(); - if !old_school { - write!(dst, "{}: ", topic)?; - } - else { - write!(dst, "{} ", topic)?; - } - dst.reset_attrs()?; - } - dst.start_attr(term::Attr::Bold)?; - dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?; - write!(dst, "{}", lvl.to_string())?; - dst.reset_attrs()?; - write!(dst, ": ")?; - dst.start_attr(term::Attr::Bold)?; - write!(dst, "{}", msg)?; - - if let Some(code) = code { - let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); - print_maybe_styled!(dst, style, " [{}]", code.clone())?; - } - - dst.reset_attrs()?; - write!(dst, "\n")?; - Ok(()) -} - #[cfg(unix)] fn stderr_isatty() -> bool { use libc; @@ -1374,46 +1072,6 @@ impl Destination { } Ok(()) } - - fn print_maybe_styled(&mut self, - args: fmt::Arguments, - color: term::Attr, - print_newline_at_end: bool) - -> io::Result<()> { - match *self { - Terminal(ref mut t) => { - t.attr(color)?; - // If `msg` ends in a newline, we need to reset the color before - // the newline. We're making the assumption that we end up writing - // to a `LineBufferedWriter`, which means that emitting the reset - // after the newline ends up buffering the reset until we print - // another line or exit. Buffering the reset is a problem if we're - // sharing the terminal with any other programs (e.g. other rustc - // instances via `make -jN`). - // - // Note that if `msg` contains any internal newlines, this will - // result in the `LineBufferedWriter` flushing twice instead of - // once, which still leaves the opportunity for interleaved output - // to be miscolored. We assume this is rare enough that we don't - // have to worry about it. - t.write_fmt(args)?; - t.reset()?; - if print_newline_at_end { - t.write_all(b"\n") - } else { - Ok(()) - } - } - Raw(ref mut w) => { - w.write_fmt(args)?; - if print_newline_at_end { - w.write_all(b"\n") - } else { - Ok(()) - } - } - } - } } impl Write for Destination { diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 33781bed759f..d5340e66ff0c 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -82,16 +82,6 @@ pub trait CodeMapper { fn macro_backtrace(&self, span: Span) -> Vec; } -impl RenderSpan { - fn span(&self) -> &MultiSpan { - match *self { - FullSpan(ref msp) | - Suggestion(CodeSuggestion { ref msp, .. }) => - msp - } - } -} - impl CodeSuggestion { /// Returns the assembled code suggestion. pub fn splice_lines(&self, cm: &CodeMapper) -> String { diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs index 525c83499fbe..2f94a7f6832f 100644 --- a/src/librustc_errors/snippet.rs +++ b/src/librustc_errors/snippet.rs @@ -10,13 +10,9 @@ // Code for annotating snippets. -use syntax_pos::{Span, FileMap, CharPos, LineInfo}; -use check_old_skool; +use syntax_pos::{Span, FileMap}; use CodeMapper; -use styled_buffer::StyledBuffer; -use std::cmp; use std::rc::Rc; -use std::mem; use {Level}; #[derive(Clone)] @@ -78,14 +74,6 @@ pub struct Annotation { pub label: Option, } -/* -#[derive(Debug)] -pub struct RenderedLine { - pub text: Vec, - pub kind: RenderedLineKind, -} -*/ - #[derive(Debug)] pub struct StyledString { pub text: String, @@ -108,721 +96,4 @@ pub enum Style { NoStyle, ErrorCode, Level(Level), -} - -/* -#[derive(Debug, Clone)] -pub enum RenderedLineKind { - PrimaryFileName, - OtherFileName, - SourceText { - file: Rc, - line_index: usize, - }, - Annotations, - Elision, -} -*/ - -impl SnippetData { - pub fn new(codemap: Rc, - primary_span: Option, - format_mode: FormatMode) // (*) - -> Self { - // (*) The primary span indicates the file that must appear - // first, and which will have a line number etc in its - // name. Outside of tests, this is always `Some`, but for many - // tests it's not relevant to test this portion of the logic, - // and it's tedious to pick a primary span (read: tedious to - // port older tests that predate the existence of a primary - // span). - - debug!("SnippetData::new(primary_span={:?})", primary_span); - - let mut data = SnippetData { - codemap: codemap.clone(), - files: vec![], - format_mode: format_mode.clone() - }; - if let Some(primary_span) = primary_span { - let lo = codemap.lookup_char_pos(primary_span.lo); - data.files.push( - FileInfo { - file: lo.file, - primary_span: Some(primary_span), - lines: vec![], - format_mode: format_mode.clone(), - }); - } - data - } - - pub fn push(&mut self, span: Span, is_primary: bool, label: Option) { - debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})", - span, is_primary, label); - - let file_lines = match self.codemap.span_to_lines(span) { - Ok(file_lines) => file_lines, - Err(_) => { - // ignore unprintable spans completely. - return; - } - }; - - self.file(&file_lines.file) - .push_lines(&file_lines.lines, is_primary, label); - } - - fn file(&mut self, file_map: &Rc) -> &mut FileInfo { - let index = self.files.iter().position(|f| f.file.name == file_map.name); - if let Some(index) = index { - return &mut self.files[index]; - } - - self.files.push( - FileInfo { - file: file_map.clone(), - lines: vec![], - primary_span: None, - format_mode: self.format_mode.clone() - }); - self.files.last_mut().unwrap() - } - - pub fn render_lines(&self) -> Vec> { - debug!("SnippetData::render_lines()"); - - let mut rendered_lines: Vec<_> = - self.files.iter() - .flat_map(|f| f.render_file_lines(&self.codemap)) - .collect(); - //prepend_prefixes(&mut rendered_lines, &self.format_mode); - //trim_lines(&mut rendered_lines); - rendered_lines - } -} - -pub trait StringSource { - fn make_string(self) -> String; -} - -impl StringSource for String { - fn make_string(self) -> String { - self - } -} - -impl StringSource for Vec { - fn make_string(self) -> String { - self.into_iter().collect() - } -} - -/* -impl From<(S, Style, RenderedLineKind)> for RenderedLine - where S: StringSource -{ - fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self { - RenderedLine { - text: vec![StyledString { - text: text.make_string(), - style: style, - }], - kind: kind, - } - } -} - -impl From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine - where S1: StringSource, S2: StringSource -{ - fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self { - let (text1, style1, text2, style2, kind) = tuple; - RenderedLine { - text: vec![ - StyledString { - text: text1.make_string(), - style: style1, - }, - StyledString { - text: text2.make_string(), - style: style2, - } - ], - kind: kind, - } - } -} - -impl RenderedLine { - fn trim_last(&mut self) { - if let Some(last_text) = self.text.last_mut() { - let len = last_text.text.trim_right().len(); - last_text.text.truncate(len); - } - } -} - -impl RenderedLineKind { - fn prefix(&self) -> StyledString { - match *self { - RenderedLineKind::SourceText { file: _, line_index } => - StyledString { - text: format!("{}", line_index + 1), - style: Style::LineNumber, - }, - RenderedLineKind::Elision => - StyledString { - text: String::from("..."), - style: Style::LineNumber, - }, - RenderedLineKind::PrimaryFileName | - RenderedLineKind::OtherFileName | - RenderedLineKind::Annotations => - StyledString { - text: String::from(""), - style: Style::LineNumber, - }, - } - } -} -*/ - -impl FileInfo { - fn get_max_line_num(&self) -> usize { - let mut max = 0; - - for line in &self.lines { - if line.line_index > max { - max = line.line_index; - } - } - max - } - - fn push_lines(&mut self, - lines: &[LineInfo], - is_primary: bool, - label: Option) { - assert!(lines.len() > 0); - - // If a span covers multiple lines, we reduce it to a single - // point at the start of the span. This means that instead - // of producing output like this: - // - // ``` - // --> foo.rs:2:1 - // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>) - // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // 3 |> -> Set> - // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // (and so on) - // ``` - // - // we produce: - // - // ``` - // --> foo.rs:2:1 - // 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>) - // ^ - // ``` - // - // Basically, although this loses information, multi-line spans just - // never look good. - - let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 { - (lines[0].line_index, lines[0].start_col, lines[0].end_col, false) - } else { - (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), true) - }; - - // Watch out for "empty spans". If we get a span like 6..6, we - // want to just display a `^` at 6, so convert that to - // 6..7. This is degenerate input, but it's best to degrade - // gracefully -- and the parser likes to suply a span like - // that for EOF, in particular. - if start_col == end_col { - end_col.0 += 1; - } - - let index = self.ensure_source_line(line); - self.lines[index].push_annotation(start_col, - end_col, - is_primary, - is_minimized, - label); - } - - /// Ensure that we have a `Line` struct corresponding to - /// `line_index` in the file. If we already have some other lines, - /// then this will add the intervening lines to ensure that we - /// have a complete snippet. (Note that when we finally display, - /// some of those lines may be elided.) - fn ensure_source_line(&mut self, line_index: usize) -> usize { - if self.lines.is_empty() { - self.lines.push(Line::new(line_index)); - return 0; - } - - // Find the range of lines we have thus far. - let first_line_index = self.lines.first().unwrap().line_index; - let last_line_index = self.lines.last().unwrap().line_index; - assert!(first_line_index <= last_line_index); - - // If the new line is lower than all the lines we have thus - // far, then insert the new line and any intervening lines at - // the front. In a silly attempt at micro-optimization, we - // don't just call `insert` repeatedly, but instead make a new - // (empty) vector, pushing the new lines onto it, and then - // appending the old vector. - if line_index < first_line_index { - let lines = mem::replace(&mut self.lines, vec![]); - self.lines.extend( - (line_index .. first_line_index) - .map(|line| Line::new(line)) - .chain(lines)); - return 0; - } - - // If the new line comes after the ones we have so far, insert - // lines for it. - if line_index > last_line_index { - self.lines.extend( - (last_line_index+1 .. line_index+1) - .map(|line| Line::new(line))); - return self.lines.len() - 1; - } - - // Otherwise it should already exist. - return line_index - first_line_index; - } - - fn render_file_lines(&self, codemap: &Rc) -> Vec> { - let old_school = match self.format_mode { - FormatMode::OriginalErrorFormat => true, - FormatMode::NewErrorFormat => false, - FormatMode::EnvironmentSelected => check_old_skool() - }; - - let mut lines_iter = self.lines.iter(); - let mut output = vec![]; - - // First insert the name of the file. - if !old_school { - match self.primary_span { - Some(span) => { - let lo = codemap.lookup_char_pos(span.lo); - output.push(vec![StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }, StyledString { - text: format!(":{}:{}", lo.line, lo.col.0 + 1), - style: Style::LineAndColumn, - }]); - output.push(vec![StyledString { - text: "".to_string(), - style: Style::FileNameStyle, - }]); - } - None => { - output.push(vec![StyledString { - text: self.file.name.clone(), - style: Style::FileNameStyle, - }]); - output.push(vec![StyledString { - text: "".to_string(), - style: Style::FileNameStyle, - }]); - } - } - } - - let mut next_line = lines_iter.next(); - while next_line.is_some() { - // Consume lines with annotations. - while let Some(line) = next_line { - if line.annotations.is_empty() { break; } - - let mut rendered_lines = self.render_line(line); - assert!(!rendered_lines.is_empty()); - if old_school { - match self.primary_span { - Some(span) => { - let lo = codemap.lookup_char_pos(span.lo); - let hi = codemap.lookup_char_pos(span.hi); - //Before each secondary line in old skool-mode, print the label - //as an old-style note - if !line.annotations[0].is_primary { - if let Some(ann) = line.annotations[0].label.clone() { - output.push(vec![StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }, StyledString { - text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1, - hi.line, hi.col.0+1), - style: Style::LineAndColumn, - }, StyledString { - text: format!("note: "), - style: Style::OldSchoolNote, - }, StyledString { - text: format!("{}", ann), - style: Style::OldSchoolNoteText, - }]); - } - } - rendered_lines[0].insert(0, StyledString { - text: format!(":{} ", lo.line), - style: Style::LineAndColumn, - }); - rendered_lines[0].insert(0, StyledString { - text: lo.file.name.clone(), - style: Style::FileNameStyle, - }); - let gap_amount = - rendered_lines[0][0].text.len() + - rendered_lines[0][1].text.len(); - assert!(rendered_lines.len() >= 2, - "no annotations resulted from: {:?}", - line); - for i in 1..rendered_lines.len() { - rendered_lines[i].insert(0, StyledString { - text: vec![" "; gap_amount].join(""), - style: Style::NoStyle - }); - } - } - _ =>() - } - } - output.append(&mut rendered_lines); - next_line = lines_iter.next(); - } - - // Emit lines without annotations, but only if they are - // followed by a line with an annotation. - let unannotated_line = next_line; - let mut unannotated_lines = 0; - while let Some(line) = next_line { - if !line.annotations.is_empty() { break; } - unannotated_lines += 1; - next_line = lines_iter.next(); - } - if unannotated_lines > 1 { - output.push(vec![StyledString{ text: String::new(), style: Style::NoStyle}]); - } else if let Some(line) = unannotated_line { - output.append(&mut self.render_line(line)); - } - } - - output - } - - fn render_line(&self, line: &Line) -> Vec> { - let old_school = match self.format_mode { - FormatMode::OriginalErrorFormat => true, - FormatMode::NewErrorFormat => false, - FormatMode::EnvironmentSelected => check_old_skool() - }; - - let source_string = self.file.get_line(line.line_index) - .unwrap_or(""); - /* - let source_kind = RenderedLineKind::SourceText { - file: self.file.clone(), - line_index: line.line_index, - }; - */ - - let mut styled_buffer = StyledBuffer::new(); - - // First create the source line we will highlight. - styled_buffer.append(0, &source_string, Style::Quotation); - - if line.annotations.is_empty() { - return styled_buffer.render(); - } - - // We want to display like this: - // - // vec.push(vec.pop().unwrap()); - // --- ^^^ _ previous borrow ends here - // | | - // | error occurs here - // previous borrow of `vec` occurs here - // - // But there are some weird edge cases to be aware of: - // - // vec.push(vec.pop().unwrap()); - // -------- - previous borrow ends here - // || - // |this makes no sense - // previous borrow of `vec` occurs here - // - // For this reason, we group the lines into "highlight lines" - // and "annotations lines", where the highlight lines have the `~`. - - //let mut highlight_line = Self::whitespace(&source_string); - - // Sort the annotations by (start, end col) - let mut annotations = line.annotations.clone(); - annotations.sort(); - - // Next, create the highlight line. - for annotation in &annotations { - if old_school { - for p in annotation.start_col .. annotation.end_col { - if p == annotation.start_col { - styled_buffer.putc(1, p, '^', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSchoolNote - }); - } - else { - styled_buffer.putc(1, p, '~', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSchoolNote - }); - } - } - } - else { - for p in annotation.start_col .. annotation.end_col { - if annotation.is_primary { - styled_buffer.putc(1, p, '^', Style::UnderlinePrimary); - if !annotation.is_minimized { - styled_buffer.set_style(0, p, Style::UnderlinePrimary); - } - } else { - styled_buffer.putc(1, p, '-', Style::UnderlineSecondary); - if !annotation.is_minimized { - styled_buffer.set_style(0, p, Style::UnderlineSecondary); - } - } - } - } - } - - // Now we are going to write labels in. To start, we'll exclude - // the annotations with no labels. - let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) = - annotations.into_iter() - .partition(|a| a.label.is_some()); - - // If there are no annotations that need text, we're done. - if labeled_annotations.is_empty() { - return styled_buffer.render(); - } - if old_school { - return styled_buffer.render(); - } - - // Now add the text labels. We try, when possible, to stick the rightmost - // annotation at the end of the highlight line: - // - // vec.push(vec.pop().unwrap()); - // --- --- - previous borrow ends here - // - // But sometimes that's not possible because one of the other - // annotations overlaps it. For example, from the test - // `span_overlap_label`, we have the following annotations - // (written on distinct lines for clarity): - // - // fn foo(x: u32) { - // -------------- - // - - // - // In this case, we can't stick the rightmost-most label on - // the highlight line, or we would get: - // - // fn foo(x: u32) { - // -------- x_span - // | - // fn_span - // - // which is totally weird. Instead we want: - // - // fn foo(x: u32) { - // -------------- - // | | - // | x_span - // fn_span - // - // which is...less weird, at least. In fact, in general, if - // the rightmost span overlaps with any other span, we should - // use the "hang below" version, so we can at least make it - // clear where the span *starts*. - let mut labeled_annotations = &labeled_annotations[..]; - match labeled_annotations.split_last().unwrap() { - (last, previous) => { - if previous.iter() - .chain(&unlabeled_annotations) - .all(|a| !overlaps(a, last)) - { - // append the label afterwards; we keep it in a separate - // string - let highlight_label: String = format!(" {}", last.label.as_ref().unwrap()); - if last.is_primary { - styled_buffer.append(1, &highlight_label, Style::LabelPrimary); - } else { - styled_buffer.append(1, &highlight_label, Style::LabelSecondary); - } - labeled_annotations = previous; - } - } - } - - // If that's the last annotation, we're done - if labeled_annotations.is_empty() { - return styled_buffer.render(); - } - - for (index, annotation) in labeled_annotations.iter().enumerate() { - // Leave: - // - 1 extra line - // - One line for each thing that comes after - let comes_after = labeled_annotations.len() - index - 1; - let blank_lines = 3 + comes_after; - - // For each blank line, draw a `|` at our column. The - // text ought to be long enough for this. - for index in 2..blank_lines { - if annotation.is_primary { - styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary); - } else { - styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary); - } - } - - if annotation.is_primary { - styled_buffer.puts(blank_lines, annotation.start_col, - annotation.label.as_ref().unwrap(), Style::LabelPrimary); - } else { - styled_buffer.puts(blank_lines, annotation.start_col, - annotation.label.as_ref().unwrap(), Style::LabelSecondary); - } - } - - styled_buffer.render() - } -} - -/* -fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) { - let old_school = match *format_mode { - FormatMode::OriginalErrorFormat => true, - FormatMode::NewErrorFormat => false, - FormatMode::EnvironmentSelected => check_old_skool() - }; - if old_school { - return; - } - - let prefixes: Vec<_> = - rendered_lines.iter() - .map(|rl| rl.kind.prefix()) - .collect(); - - // find the max amount of spacing we need; add 1 to - // p.text.len() to leave space between the prefix and the - // source text - let padding_len = - prefixes.iter() - .map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 }) - .max() - .unwrap_or(0); - - // Ensure we insert at least one character of padding, so that the - // `-->` arrows can fit etc. - let padding_len = cmp::max(padding_len, 1); - - for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) { - let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' '); - prefix.text.extend(extra_spaces); - match line.kind { - RenderedLineKind::Elision => { - line.text.insert(0, prefix); - } - RenderedLineKind::PrimaryFileName => { - // --> filename - // 22 |> - // ^ - // padding_len - let dashes = (0..padding_len - 1).map(|_| ' ') - .chain(Some('-')) - .chain(Some('-')) - .chain(Some('>')) - .chain(Some(' ')); - line.text.insert(0, StyledString {text: dashes.collect(), - style: Style::LineNumber}) - } - RenderedLineKind::OtherFileName => { - // ::: filename - // 22 |> - // ^ - // padding_len - let dashes = (0..padding_len - 1).map(|_| ' ') - .chain(Some(':')) - .chain(Some(':')) - .chain(Some(':')) - .chain(Some(' ')); - line.text.insert(0, StyledString {text: dashes.collect(), - style: Style::LineNumber}) - } - _ => { - line.text.insert(0, prefix); - line.text.insert(1, StyledString {text: String::from("|> "), - style: Style::LineNumber}) - } - } - } -} - -fn trim_lines(rendered_lines: &mut [RenderedLine]) { - for line in rendered_lines { - while !line.text.is_empty() { - line.trim_last(); - if line.text.last().unwrap().text.is_empty() { - line.text.pop(); - } else { - break; - } - } - } -} -*/ - -impl Line { - fn new(line_index: usize) -> Line { - Line { - line_index: line_index, - annotations: vec![] - } - } - - fn push_annotation(&mut self, - start: CharPos, - end: CharPos, - is_primary: bool, - is_minimized: bool, - label: Option) { - self.annotations.push(Annotation { - start_col: start.0, - end_col: end.0, - is_primary: is_primary, - is_minimized: is_minimized, - label: label, - }); - } -} - -fn overlaps(a1: &Annotation, - a2: &Annotation) - -> bool -{ - (a2.start_col .. a2.end_col).contains(a1.start_col) || - (a1.start_col .. a1.end_col).contains(a2.start_col) -} +} \ No newline at end of file diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 3b93dc0c4d37..80b38a8e48ef 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1265,9 +1265,9 @@ r"blork2.rs:2:1: 2:12 println!("r#\"\n{}\"#", str); assert_eq!(str, &r#" --> dummy.txt:11:1 - |> -11 |> e-lä-vän - |> ^ + | +11 | e-lä-vän + | ^ "#[1..]); } @@ -1333,9 +1333,9 @@ r"blork2.rs:2:1: 2:12 let expect_start = &r#" --> dummy.txt:1:6 - |> -1 |> _____aaaaaa____bbbbbb__cccccdd_ - |> ^^^^^^ ^^^^^^ ^^^^^^^ + | +1 | _____aaaaaa____bbbbbb__cccccdd_ + | ^^^^^^ ^^^^^^ ^^^^^^^ "#[1..]; let span = |sp, expected| { @@ -1409,28 +1409,28 @@ r"blork2.rs:2:1: 2:12 let expect0 = &r#" --> dummy.txt:5:1 - |> -5 |> ccccc - |> ^ + | +5 | ccccc + | ^ ... -9 |> ddd__eee_ - |> ^^^ ^^^ -10 |> elided -11 |> __f_gg - |> ^ ^^ +9 | ddd__eee_ + | ^^^ ^^^ +10 | elided +11 | __f_gg + | ^ ^^ "#[1..]; let expect = &r#" --> dummy.txt:1:1 - |> -1 |> aaaaa - |> ^ + | +1 | aaaaa + | ^ ... -9 |> ddd__eee_ - |> ^^^ ^^^ -10 |> elided -11 |> __f_gg - |> ^ ^^ +9 | ddd__eee_ + | ^^^ ^^^ +10 | elided +11 | __f_gg + | ^ ^^ "#[1..]; macro_rules! test { @@ -1477,9 +1477,9 @@ fn foo() { let text = make_string(&lines); assert_eq!(&text[..], &" --> foo.rs:3:2 - |> -3 |> \tbar; - |> \t^^^ + | +3 | \tbar; + | \t^^^ "[1..]); } @@ -1510,12 +1510,12 @@ fn foo() { println!("text=\n{}", text); assert_eq!(&text[..], &r#" ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- --- - previous borrow ends here - |> | | - |> | error occurs here - |> previous borrow of `vec` occurs here + | +3 | vec.push(vec.pop().unwrap()); + | --- --- - previous borrow ends here + | | | + | | error occurs here + | previous borrow of `vec` occurs here "#[1..]); } @@ -1577,24 +1577,24 @@ fn bar() { println!("text=\n{}", text); - // Note that the `|>` remain aligned across both files: + // Note that the `|` remain aligned across both files: assert_eq!(&text[..], &r#" --> foo.rs:3:14 - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- ^^^ - c - |> | | - |> | b - |> a + | +3 | vec.push(vec.pop().unwrap()); + | --- ^^^ - c + | | | + | | b + | a ::: bar.rs - |> -17 |> vec.push(); - |> --- - f - |> | - |> d + | +17 | vec.push(); + | --- - f + | | + | d ... -21 |> vec.pop().unwrap()); - |> --- e +21 | vec.pop().unwrap()); + | --- e "#[1..]); } @@ -1632,15 +1632,15 @@ fn foo() { println!("text=\n{}", text); assert_eq!(&text[..], &r#" ::: foo.rs - |> -3 |> let name = find_id(&data, 22).unwrap(); - |> ---- immutable borrow begins here + | +3 | let name = find_id(&data, 22).unwrap(); + | ---- immutable borrow begins here ... -6 |> data.push(Data { name: format!("Hera"), id: 66 }); - |> ---- mutable borrow occurs here +6 | data.push(Data { name: format!("Hera"), id: 66 }); + | ---- mutable borrow occurs here ... -11 |> } - |> - immutable borrow ends here +11 | } + | - immutable borrow ends here "#[1..]); } @@ -1672,13 +1672,13 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> -------- ------ D - |> || - |> |C - |> A - |> B + | +3 | vec.push(vec.pop().unwrap()); + | -------- ------ D + | || + | |C + | A + | B "#[1..]); } @@ -1709,12 +1709,12 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - |> -3 |> vec.push(vec.pop().unwrap()); - |> --- --- - previous borrow ends here - |> | | - |> | error occurs here - |> previous borrow of `vec` occurs here + | +3 | vec.push(vec.pop().unwrap()); + | --- --- - previous borrow ends here + | | | + | | error occurs here + | previous borrow of `vec` occurs here "#[1..]); } @@ -1748,12 +1748,12 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - |> -4 |> let mut vec2 = vec; - |> --- `vec` moved here because it has type `collections::vec::Vec` + | +4 | let mut vec2 = vec; + | --- `vec` moved here because it has type `collections::vec::Vec` ... -9 |> vec.push(7); - |> --- use of moved value: `vec` +9 | vec.push(7); + | --- use of moved value: `vec` "#[1..]); } @@ -1785,11 +1785,11 @@ fn foo() { println!("text=&r#\"\n{}\n\"#[1..]", text); assert_eq!(text, &r#" ::: foo.rs - |> -3 |> let mut vec = vec![0, 1, 2]; - |> --- --- -4 |> let mut vec2 = vec; - |> --- --- + | +3 | let mut vec = vec![0, 1, 2]; + | --- --- +4 | let mut vec2 = vec; + | --- --- "#[1..]); } @@ -1817,9 +1817,9 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - |> -3 |> fn foo(x: u32) { - |> - + | +3 | fn foo(x: u32) { + | - "#[1..]); } @@ -1847,12 +1847,12 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - |> -2 |> fn foo(x: u32) { - |> -------------- - |> | | - |> | x_span - |> fn_span + | +2 | fn foo(x: u32) { + | -------------- + | | | + | | x_span + | fn_span "#[1..]); } @@ -1882,12 +1882,12 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - |> -2 |> fn foo(x: u32) { - |> -------------- - |> | | - |> | x_span - |> fn_span + | +2 | fn foo(x: u32) { + | -------------- + | | | + | | x_span + | fn_span "#[1..]); } @@ -1928,11 +1928,11 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - |> -3 |> let closure = || { - |> - foo -4 |> inner - |> ----- bar + | +3 | let closure = || { + | - foo +4 | inner + | ----- bar "#[1..]); } @@ -1971,9 +1971,9 @@ fn main() { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" --> foo.rs:11:2 - |> -11 |> } - |> - + | +11 | } + | - "#[1..]); } } From 012ff15c94ff44f45c07641bdd0957da6a625591 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 12 Jul 2016 16:10:01 -0400 Subject: [PATCH 18/53] Fix up some tidy-unfriendly spacing --- src/librustc_errors/emitter.rs | 10 ++++-- src/libsyntax/codemap.rs | 36 +++++++++---------- .../ui/mismatched_types/issue-26480.stderr | 4 +-- src/test/ui/mismatched_types/main.stderr | 4 +-- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 901cd3403c1d..8a5aea853f1c 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -534,7 +534,7 @@ impl EmitterWriter { // Put in the spacer between the location and annotated source let buffer_msg_line_offset = buffer.num_lines(); - draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); // Next, output the annotate source for this file for line_idx in 0..annotated_file.lines.len() { @@ -636,7 +636,7 @@ impl EmitterWriter { Ok(()) => { if !db.children.is_empty() { let mut buffer = StyledBuffer::new(); - draw_col_separator(&mut buffer, 0, max_line_num_len + 1); + draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); match emit_to_destination(&buffer.render(), &db.level, &mut self.dst) { Ok(()) => (), Err(e) => panic!("failed to emit error: {}", e) @@ -948,6 +948,10 @@ fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "| ", Style::LineNumber); } +fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) { + buffer.puts(line, col, "|", Style::LineNumber); +} + fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "= ", Style::LineNumber); } @@ -1087,4 +1091,4 @@ impl Write for Destination { Raw(ref mut w) => w.flush(), } } -} +} \ No newline at end of file diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 80b38a8e48ef..9f8e08e06121 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1265,7 +1265,7 @@ r"blork2.rs:2:1: 2:12 println!("r#\"\n{}\"#", str); assert_eq!(str, &r#" --> dummy.txt:11:1 - | + | 11 | e-lä-vän | ^ "#[1..]); @@ -1333,7 +1333,7 @@ r"blork2.rs:2:1: 2:12 let expect_start = &r#" --> dummy.txt:1:6 - | + | 1 | _____aaaaaa____bbbbbb__cccccdd_ | ^^^^^^ ^^^^^^ ^^^^^^^ "#[1..]; @@ -1409,7 +1409,7 @@ r"blork2.rs:2:1: 2:12 let expect0 = &r#" --> dummy.txt:5:1 - | + | 5 | ccccc | ^ ... @@ -1422,7 +1422,7 @@ r"blork2.rs:2:1: 2:12 let expect = &r#" --> dummy.txt:1:1 - | + | 1 | aaaaa | ^ ... @@ -1477,7 +1477,7 @@ fn foo() { let text = make_string(&lines); assert_eq!(&text[..], &" --> foo.rs:3:2 - | + | 3 | \tbar; | \t^^^ "[1..]); @@ -1510,7 +1510,7 @@ fn foo() { println!("text=\n{}", text); assert_eq!(&text[..], &r#" ::: foo.rs - | + | 3 | vec.push(vec.pop().unwrap()); | --- --- - previous borrow ends here | | | @@ -1580,14 +1580,14 @@ fn bar() { // Note that the `|` remain aligned across both files: assert_eq!(&text[..], &r#" --> foo.rs:3:14 - | + | 3 | vec.push(vec.pop().unwrap()); | --- ^^^ - c | | | | | b | a ::: bar.rs - | + | 17 | vec.push(); | --- - f | | @@ -1632,7 +1632,7 @@ fn foo() { println!("text=\n{}", text); assert_eq!(&text[..], &r#" ::: foo.rs - | + | 3 | let name = find_id(&data, 22).unwrap(); | ---- immutable borrow begins here ... @@ -1672,7 +1672,7 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - | + | 3 | vec.push(vec.pop().unwrap()); | -------- ------ D | || @@ -1709,7 +1709,7 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - | + | 3 | vec.push(vec.pop().unwrap()); | --- --- - previous borrow ends here | | | @@ -1748,7 +1748,7 @@ fn foo() { println!("text=r#\"\n{}\".trim_left()", text); assert_eq!(&text[..], &r#" ::: foo.rs - | + | 4 | let mut vec2 = vec; | --- `vec` moved here because it has type `collections::vec::Vec` ... @@ -1785,7 +1785,7 @@ fn foo() { println!("text=&r#\"\n{}\n\"#[1..]", text); assert_eq!(text, &r#" ::: foo.rs - | + | 3 | let mut vec = vec![0, 1, 2]; | --- --- 4 | let mut vec2 = vec; @@ -1817,7 +1817,7 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - | + | 3 | fn foo(x: u32) { | - "#[1..]); @@ -1847,7 +1847,7 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - | + | 2 | fn foo(x: u32) { | -------------- | | | @@ -1882,7 +1882,7 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - | + | 2 | fn foo(x: u32) { | -------------- | | | @@ -1928,7 +1928,7 @@ impl SomeTrait for () { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" ::: foo.rs - | + | 3 | let closure = || { | - foo 4 | inner @@ -1971,7 +1971,7 @@ fn main() { println!("r#\"\n{}\"", text); assert_eq!(text, &r#" --> foo.rs:11:2 - | + | 11 | } | - "#[1..]); diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 63f2ab4d7c6b..ff6920d28ccf 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types --> $DIR/issue-26480.rs:27:19 - | + | 27 | $arr.len() * size_of($arr[0])); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize $DIR/issue-26480.rs:38:5: 38:19 note: in this expansion of write! (defined in $DIR/issue-26480.rs) error: non-scalar cast: `_` as `()` --> $DIR/issue-26480.rs:33:19 - | + | 33 | ($x:expr) => ($x as ()) | ^^^^^^^^ $DIR/issue-26480.rs:39:5: 39:14 note: in this expansion of cast! (defined in $DIR/issue-26480.rs) diff --git a/src/test/ui/mismatched_types/main.stderr b/src/test/ui/mismatched_types/main.stderr index ae1d417eca9e..2903aa08c0a9 100644 --- a/src/test/ui/mismatched_types/main.stderr +++ b/src/test/ui/mismatched_types/main.stderr @@ -1,9 +1,9 @@ error[E0308]: mismatched types --> $DIR/main.rs:14:18 - | + | 14 | let x: u32 = ( | ^ expected u32, found () - | + | = note: expected type `u32` = note: found type `()` From 2e8e73cb951e2095e444c5cc91403f7e9c5f1065 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 13 Jul 2016 05:44:29 -0400 Subject: [PATCH 19/53] Add in styled_buffer.rs and remove some unused code --- src/librustc/infer/error_reporting.rs | 4 +- src/librustc_errors/emitter.rs | 73 ++++---------- src/librustc_errors/lib.rs | 4 +- src/librustc_errors/styled_buffer.rs | 133 ++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 56 deletions(-) create mode 100644 src/librustc_errors/styled_buffer.rs diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 894044296cbd..96ecad629f54 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -94,7 +94,7 @@ use syntax::ast; use syntax::parse::token; use syntax::ptr::P; use syntax_pos::{self, Pos, Span}; -use errors::{DiagnosticBuilder, check_old_skool}; +use errors::{DiagnosticBuilder, check_old_school}; impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn note_and_explain_region(self, @@ -485,7 +485,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { "{}", trace.origin); - if !is_simple_error || check_old_skool() { + if !is_simple_error || check_old_school() { err.note_expected_found(&"type", &expected, &found); } diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 8a5aea853f1c..161f2f7bb1c3 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -13,7 +13,7 @@ use self::Destination::*; use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, LineInfo, CharPos}; use registry; -use check_old_skool; +use check_old_school; use {Level, CodeSuggestion, DiagnosticBuilder, CodeMapper}; use RenderSpan::*; use snippet::{StyledString, Style, FormatMode, Annotation, Line}; @@ -36,7 +36,7 @@ impl Emitter for EmitterWriter { let old_school = match self.format_mode { FormatMode::NewErrorFormat => false, FormatMode::OriginalErrorFormat => true, - FormatMode::EnvironmentSelected => check_old_skool() + FormatMode::EnvironmentSelected => check_old_school() }; if old_school { @@ -243,59 +243,32 @@ impl EmitterWriter { // For this reason, we group the lines into "highlight lines" // and "annotations lines", where the highlight lines have the `~`. - // let mut highlight_line = Self::whitespace(&source_string); - let old_school = check_old_skool(); - // Sort the annotations by (start, end col) let mut annotations = line.annotations.clone(); annotations.sort(); // Next, create the highlight line. for annotation in &annotations { - if old_school { - for p in annotation.start_col..annotation.end_col { - if p == annotation.start_col { - buffer.putc(line_offset + 1, - width_offset + p, - '^', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSchoolNote - }); - } else { - buffer.putc(line_offset + 1, - width_offset + p, - '~', - if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::OldSchoolNote - }); + for p in annotation.start_col..annotation.end_col { + if annotation.is_primary { + buffer.putc(line_offset + 1, + width_offset + p, + '^', + Style::UnderlinePrimary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlinePrimary); } - } - } else { - for p in annotation.start_col..annotation.end_col { - if annotation.is_primary { - buffer.putc(line_offset + 1, - width_offset + p, - '^', - Style::UnderlinePrimary); - if !annotation.is_minimized { - buffer.set_style(line_offset, - width_offset + p, - Style::UnderlinePrimary); - } - } else { - buffer.putc(line_offset + 1, - width_offset + p, - '-', - Style::UnderlineSecondary); - if !annotation.is_minimized { - buffer.set_style(line_offset, - width_offset + p, - Style::UnderlineSecondary); - } + } else { + buffer.putc(line_offset + 1, + width_offset + p, + '-', + Style::UnderlineSecondary); + if !annotation.is_minimized { + buffer.set_style(line_offset, + width_offset + p, + Style::UnderlineSecondary); } } } @@ -311,10 +284,6 @@ impl EmitterWriter { if labeled_annotations.is_empty() { return; } - if old_school { - return; - } - // Now add the text labels. We try, when possible, to stick the rightmost // annotation at the end of the highlight line: // diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index d5340e66ff0c..6a48f65714cc 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -756,13 +756,13 @@ pub fn expect(diag: &Handler, opt: Option, msg: M) -> T where /// /// FIXME(#33240) #[cfg(not(test))] -pub fn check_old_skool() -> bool { +pub fn check_old_school() -> bool { use std::env; env::var("RUST_NEW_ERROR_FORMAT").is_err() } /// For unit tests, use the new format. #[cfg(test)] -pub fn check_old_skool() -> bool { +pub fn check_old_school() -> bool { false } diff --git a/src/librustc_errors/styled_buffer.rs b/src/librustc_errors/styled_buffer.rs new file mode 100644 index 000000000000..238caf93b83f --- /dev/null +++ b/src/librustc_errors/styled_buffer.rs @@ -0,0 +1,133 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Code for creating styled buffers + +use snippet::{Style, StyledString}; + +#[derive(Debug)] +pub struct StyledBuffer { + text: Vec>, + styles: Vec>, +} + +impl StyledBuffer { + pub fn new() -> StyledBuffer { + StyledBuffer { + text: vec![], + styles: vec![], + } + } + + pub fn render(&self) -> Vec> { + let mut output: Vec> = vec![]; + let mut styled_vec: Vec = vec![]; + + for (row, row_style) in self.text.iter().zip(&self.styles) { + let mut current_style = Style::NoStyle; + let mut current_text = String::new(); + + for (&c, &s) in row.iter().zip(row_style) { + if s != current_style { + if !current_text.is_empty() { + styled_vec.push(StyledString { + text: current_text, + style: current_style, + }); + } + current_style = s; + current_text = String::new(); + } + current_text.push(c); + } + if !current_text.is_empty() { + styled_vec.push(StyledString { + text: current_text, + style: current_style, + }); + } + + // We're done with the row, push and keep going + output.push(styled_vec); + + styled_vec = vec![]; + } + + output + } + + fn ensure_lines(&mut self, line: usize) { + while line >= self.text.len() { + self.text.push(vec![]); + self.styles.push(vec![]); + } + } + + pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) { + self.ensure_lines(line); + if col < self.text[line].len() { + self.text[line][col] = chr; + self.styles[line][col] = style; + } else { + let mut i = self.text[line].len(); + while i < col { + let s = match self.text[0].get(i) { + Some(&'\t') => '\t', + _ => ' ', + }; + self.text[line].push(s); + self.styles[line].push(Style::NoStyle); + i += 1; + } + self.text[line].push(chr); + self.styles[line].push(style); + } + } + + pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) { + let mut n = col; + for c in string.chars() { + self.putc(line, n, c, style); + n += 1; + } + } + + pub fn set_style(&mut self, line: usize, col: usize, style: Style) { + if self.styles.len() > line && self.styles[line].len() > col { + self.styles[line][col] = style; + } + } + + pub fn prepend(&mut self, line: usize, string: &str, style: Style) { + self.ensure_lines(line); + let string_len = string.len(); + + // Push the old content over to make room for new content + for _ in 0..string_len { + self.styles[line].insert(0, Style::NoStyle); + self.text[line].insert(0, ' '); + } + + self.puts(line, 0, string, style); + } + + pub fn append(&mut self, line: usize, string: &str, style: Style) { + if line >= self.text.len() { + self.puts(line, 0, string, style); + } else { + let col = self.text[line].len(); + self.puts(line, col, string, style); + } + } + + pub fn num_lines(&self) -> usize { + self.text.len() + } +} From 1fd014a9654e11ae1bc7c0793c6b01b157d825cd Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 13 Jul 2016 17:53:42 -0400 Subject: [PATCH 20/53] Add fix for tabs. Move error unit tests->ui tests --- src/librustc_errors/styled_buffer.rs | 25 +- src/libsyntax/codemap.rs | 770 +----------------- src/test/ui/codemap_tests/empty_span.rs | 19 + src/test/ui/codemap_tests/empty_span.stderr | 8 + .../codemap_tests/huge_multispan_highlight.rs | 104 +++ .../huge_multispan_highlight.stderr | 11 + src/test/ui/codemap_tests/issue-11715.rs | 104 +++ src/test/ui/codemap_tests/issue-11715.stderr | 12 + src/test/ui/codemap_tests/one_line.rs | 16 + src/test/ui/codemap_tests/one_line.stderr | 11 + .../ui/codemap_tests/overlapping_spans.rs | 24 + .../ui/codemap_tests/overlapping_spans.stderr | 11 + src/test/ui/codemap_tests/tab.rs | 16 + src/test/ui/codemap_tests/tab.stderr | 8 + src/test/ui/codemap_tests/two_files.rs | 18 + src/test/ui/codemap_tests/two_files.stderr | 13 + src/test/ui/codemap_tests/two_files_data.rs | 16 + 17 files changed, 414 insertions(+), 772 deletions(-) create mode 100644 src/test/ui/codemap_tests/empty_span.rs create mode 100644 src/test/ui/codemap_tests/empty_span.stderr create mode 100644 src/test/ui/codemap_tests/huge_multispan_highlight.rs create mode 100644 src/test/ui/codemap_tests/huge_multispan_highlight.stderr create mode 100644 src/test/ui/codemap_tests/issue-11715.rs create mode 100644 src/test/ui/codemap_tests/issue-11715.stderr create mode 100644 src/test/ui/codemap_tests/one_line.rs create mode 100644 src/test/ui/codemap_tests/one_line.stderr create mode 100644 src/test/ui/codemap_tests/overlapping_spans.rs create mode 100644 src/test/ui/codemap_tests/overlapping_spans.stderr create mode 100644 src/test/ui/codemap_tests/tab.rs create mode 100644 src/test/ui/codemap_tests/tab.stderr create mode 100644 src/test/ui/codemap_tests/two_files.rs create mode 100644 src/test/ui/codemap_tests/two_files.stderr create mode 100644 src/test/ui/codemap_tests/two_files_data.rs diff --git a/src/librustc_errors/styled_buffer.rs b/src/librustc_errors/styled_buffer.rs index 238caf93b83f..9768b68619e7 100644 --- a/src/librustc_errors/styled_buffer.rs +++ b/src/librustc_errors/styled_buffer.rs @@ -26,10 +26,27 @@ impl StyledBuffer { } } - pub fn render(&self) -> Vec> { + pub fn copy_tabs(&mut self, row: usize) { + if row < self.text.len() { + for i in row+1..self.text.len() { + for j in 0..self.text[i].len() { + if self.text[row].len() > j && + self.text[row][j] == '\t' && + self.text[i][j] == ' ' { + self.text[i][j] = '\t'; + } + } + } + } + } + + pub fn render(&mut self) -> Vec> { let mut output: Vec> = vec![]; let mut styled_vec: Vec = vec![]; + //before we render, do a little patch-up work to support tabs + self.copy_tabs(3); + for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); @@ -78,11 +95,7 @@ impl StyledBuffer { } else { let mut i = self.text[line].len(); while i < col { - let s = match self.text[0].get(i) { - Some(&'\t') => '\t', - _ => ' ', - }; - self.text[line].push(s); + self.text[line].push(' '); self.styles[line].push(Style::NoStyle); i += 1; } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 9f8e08e06121..afa5a3b38ba2 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -827,12 +827,7 @@ impl CodeMapper for CodeMap { #[cfg(test)] mod tests { use super::*; - use errors::{Level, CodeSuggestion}; - use errors::emitter::EmitterWriter; - use errors::snippet::{SnippetData, RenderedLine, FormatMode}; - use std::sync::{Arc, Mutex}; use std::io::{self, Write}; - use std::str::from_utf8; use std::rc::Rc; #[test] @@ -1130,12 +1125,12 @@ mod tests { } } - fn make_string(lines: &[RenderedLine]) -> String { + fn make_string(lines: Vec>) -> String { lines.iter() .flat_map(|rl| { - rl.text.iter() - .map(|s| &s.text[..]) - .chain(Some("\n")) + rl.iter() + .map(|s| &s.text[..]) + .chain(Some("\n")) }) .collect() } @@ -1219,761 +1214,4 @@ r"blork2.rs:2:1: 2:12 "; assert_eq!(sstr, res_str); } - - struct Sink(Arc>>); - impl Write for Sink { - fn write(&mut self, data: &[u8]) -> io::Result { - Write::write(&mut *self.0.lock().unwrap(), data) - } - fn flush(&mut self) -> io::Result<()> { Ok(()) } - } - - // Diagnostic doesn't align properly in span where line number increases by one digit - #[test] - fn test_hilight_suggestion_issue_11715() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), - None, - Some(cm.clone()), - FormatMode::NewErrorFormat); - let content = "abcdefg - koksi - line3 - line4 - cinq - line6 - line7 - line8 - line9 - line10 - e-lä-vän - tolv - dreizehn - "; - let file = cm.new_filemap_and_lines("dummy.txt", None, content); - let start = file.lines.borrow()[10]; - let end = file.lines.borrow()[11]; - let sp = mk_sp(start, end); - let lvl = Level::Error; - println!("highlight_lines"); - ew.highlight_lines(&sp.into(), lvl).unwrap(); - println!("done"); - let vec = data.lock().unwrap().clone(); - let vec: &[u8] = &vec; - let str = from_utf8(vec).unwrap(); - println!("r#\"\n{}\"#", str); - assert_eq!(str, &r#" - --> dummy.txt:11:1 - | -11 | e-lä-vän - | ^ -"#[1..]); - } - - #[test] - fn test_single_span_splice() { - // Test that a `MultiSpan` containing a single span splices a substition correctly - let cm = CodeMap::new(); - let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection = " \n ~~\n~~~\n~~~~~ \n \n"; - cm.new_filemap_and_lines("blork.rs", None, inputtext); - let sp = span_from_selection(inputtext, selection); - let msp: MultiSpan = sp.into(); - - // check that we are extracting the text we thought we were extracting - assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD"); - - let substitute = "ZZZZZZ".to_owned(); - let expected = "bbbbZZZZZZddddd"; - let suggest = CodeSuggestion { - msp: msp, - substitutes: vec![substitute], - }; - assert_eq!(suggest.splice_lines(&cm), expected); - } - - #[test] - fn test_multi_span_splice() { - // Test that a `MultiSpan` containing multiple spans splices a substition correctly - let cm = CodeMap::new(); - let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; - let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order - let selection2 = " \n ~~\n~~~\n~~~~~ \n \n"; - cm.new_filemap_and_lines("blork.rs", None, inputtext); - let sp1 = span_from_selection(inputtext, selection1); - let sp2 = span_from_selection(inputtext, selection2); - let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]); - - let expected = "bbbbZZZZZZddddd\neXYZe"; - let suggest = CodeSuggestion { - msp: msp, - substitutes: vec!["ZZZZZZ".to_owned(), - "XYZ".to_owned()] - }; - - assert_eq!(suggest.splice_lines(&cm), expected); - } - - #[test] - fn test_multispan_highlight() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), - None, - Some(cm.clone()), - FormatMode::NewErrorFormat); - - let inp = "_____aaaaaa____bbbbbb__cccccdd_"; - let sp1 = " ~~~~~~ "; - let sp2 = " ~~~~~~ "; - let sp3 = " ~~~~~ "; - let sp4 = " ~~~~ "; - let sp34 = " ~~~~~~~ "; - - let expect_start = &r#" - --> dummy.txt:1:6 - | -1 | _____aaaaaa____bbbbbb__cccccdd_ - | ^^^^^^ ^^^^^^ ^^^^^^^ -"#[1..]; - - let span = |sp, expected| { - let sp = span_from_selection(inp, sp); - assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); - sp - }; - cm.new_filemap_and_lines("dummy.txt", None, inp); - let sp1 = span(sp1, "aaaaaa"); - let sp2 = span(sp2, "bbbbbb"); - let sp3 = span(sp3, "ccccc"); - let sp4 = span(sp4, "ccdd"); - let sp34 = span(sp34, "cccccdd"); - - let spans = vec![sp1, sp2, sp3, sp4]; - - let test = |expected, highlight: &mut FnMut()| { - data.lock().unwrap().clear(); - highlight(); - let vec = data.lock().unwrap().clone(); - let actual = from_utf8(&vec[..]).unwrap(); - println!("actual=\n{}", actual); - assert_eq!(actual, expected); - }; - - let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]); - test(expect_start, &mut || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - test(expect_start, &mut || { - let msp = MultiSpan::from_spans(spans.clone()); - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } - - #[test] - fn test_huge_multispan_highlight() { - let data = Arc::new(Mutex::new(Vec::new())); - let cm = Rc::new(CodeMap::new()); - let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), - None, - Some(cm.clone()), - FormatMode::NewErrorFormat); - - let inp = "aaaaa\n\ - aaaaa\n\ - aaaaa\n\ - bbbbb\n\ - ccccc\n\ - xxxxx\n\ - yyyyy\n\ - _____\n\ - ddd__eee_\n\ - elided\n\ - __f_gg"; - let file = cm.new_filemap_and_lines("dummy.txt", None, inp); - - let span = |lo, hi, (off_lo, off_hi)| { - let lines = file.lines.borrow(); - let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]); - lo.0 += off_lo; - hi.0 += off_hi; - mk_sp(lo, hi) - }; - let sp0 = span(4, 6, (0, 5)); - let sp1 = span(0, 6, (0, 5)); - let sp2 = span(8, 8, (0, 3)); - let sp3 = span(8, 8, (5, 8)); - let sp4 = span(10, 10, (2, 3)); - let sp5 = span(10, 10, (4, 6)); - - let expect0 = &r#" - --> dummy.txt:5:1 - | -5 | ccccc - | ^ -... -9 | ddd__eee_ - | ^^^ ^^^ -10 | elided -11 | __f_gg - | ^ ^^ -"#[1..]; - - let expect = &r#" - --> dummy.txt:1:1 - | -1 | aaaaa - | ^ -... -9 | ddd__eee_ - | ^^^ ^^^ -10 | elided -11 | __f_gg - | ^ ^^ -"#[1..]; - - macro_rules! test { - ($expected: expr, $highlight: expr) => ({ - data.lock().unwrap().clear(); - $highlight(); - let vec = data.lock().unwrap().clone(); - let actual = from_utf8(&vec[..]).unwrap(); - println!("actual:"); - println!("{}", actual); - println!("expected:"); - println!("{}", $expected); - assert_eq!(&actual[..], &$expected[..]); - }); - } - - let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]); - let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]); - - test!(expect0, || { - diag.highlight_lines(&msp0, Level::Error).unwrap(); - }); - test!(expect, || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } - - #[test] - fn tab() { - let file_text = " -fn foo() { -\tbar; -} -"; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_bar = cm.span_substr(&foo, file_text, "bar", 0); - - let mut snippet = SnippetData::new(cm, Some(span_bar), FormatMode::NewErrorFormat); - snippet.push(span_bar, true, None); - - let lines = snippet.render_lines(); - let text = make_string(&lines); - assert_eq!(&text[..], &" - --> foo.rs:3:2 - | -3 | \tbar; - | \t^^^ -"[1..]); - } - - #[test] - fn one_line() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); - let span_semi = cm.span_substr(&foo, file_text, ";", 0); - - let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); - snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); - snippet.push(span_vec1, false, Some(format!("error occurs here"))); - snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - | -3 | vec.push(vec.pop().unwrap()); - | --- --- - previous borrow ends here - | | | - | | error occurs here - | previous borrow of `vec` occurs here -"#[1..]); - } - - #[test] - fn two_files() { - let file_text_foo = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let file_text_bar = r#" -fn bar() { - // these blank links here - // serve to ensure that the line numbers - // from bar.rs - // require more digits - - - - - - - - - - - vec.push(); - - // this line will get elided - - vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo); - let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0); - let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1); - let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0); - - let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar); - let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0); - let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1); - let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0); - - let mut snippet = SnippetData::new(cm, Some(span_foo_vec1), FormatMode::NewErrorFormat); - snippet.push(span_foo_vec0, false, Some(format!("a"))); - snippet.push(span_foo_vec1, true, Some(format!("b"))); - snippet.push(span_foo_semi, false, Some(format!("c"))); - snippet.push(span_bar_vec0, false, Some(format!("d"))); - snippet.push(span_bar_vec1, false, Some(format!("e"))); - snippet.push(span_bar_semi, false, Some(format!("f"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - - // Note that the `|` remain aligned across both files: - assert_eq!(&text[..], &r#" - --> foo.rs:3:14 - | -3 | vec.push(vec.pop().unwrap()); - | --- ^^^ - c - | | | - | | b - | a - ::: bar.rs - | -17 | vec.push(); - | --- - f - | | - | d -... -21 | vec.pop().unwrap()); - | --- e -"#[1..]); - } - - #[test] - fn multi_line() { - let file_text = r#" -fn foo() { - let name = find_id(&data, 22).unwrap(); - - // Add one more item we forgot to the vector. Silly us. - data.push(Data { name: format!("Hera"), id: 66 }); - - // Print everything out. - println!("Name: {:?}", name); - println!("Data: {:?}", data); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_data0 = cm.span_substr(&foo, file_text, "data", 0); - let span_data1 = cm.span_substr(&foo, file_text, "data", 1); - let span_rbrace = cm.span_substr(&foo, file_text, "}", 3); - - let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); - snippet.push(span_data0, false, Some(format!("immutable borrow begins here"))); - snippet.push(span_data1, false, Some(format!("mutable borrow occurs here"))); - snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - - let text: String = make_string(&lines); - - println!("text=\n{}", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - | -3 | let name = find_id(&data, 22).unwrap(); - | ---- immutable borrow begins here -... -6 | data.push(Data { name: format!("Hera"), id: 66 }); - | ---- mutable borrow occurs here -... -11 | } - | - immutable borrow ends here -"#[1..]); - } - - #[test] - fn overlapping() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span0 = cm.span_substr(&foo, file_text, "vec.push", 0); - let span1 = cm.span_substr(&foo, file_text, "vec", 0); - let span2 = cm.span_substr(&foo, file_text, "ec.push", 0); - let span3 = cm.span_substr(&foo, file_text, "unwrap", 0); - - let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); - snippet.push(span0, false, Some(format!("A"))); - snippet.push(span1, false, Some(format!("B"))); - snippet.push(span2, false, Some(format!("C"))); - snippet.push(span3, false, Some(format!("D"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - | -3 | vec.push(vec.pop().unwrap()); - | -------- ------ D - | || - | |C - | A - | B -"#[1..]); - } - - #[test] - fn one_line_out_of_order() { - let file_text = r#" -fn foo() { - vec.push(vec.pop().unwrap()); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1); - let span_semi = cm.span_substr(&foo, file_text, ";", 0); - - // intentionally don't push the snippets left to right - let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); - snippet.push(span_vec1, false, Some(format!("error occurs here"))); - snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here"))); - snippet.push(span_semi, false, Some(format!("previous borrow ends here"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - | -3 | vec.push(vec.pop().unwrap()); - | --- --- - previous borrow ends here - | | | - | | error occurs here - | previous borrow of `vec` occurs here -"#[1..]); - } - - #[test] - fn elide_unnecessary_lines() { - let file_text = r#" -fn foo() { - let mut vec = vec![0, 1, 2]; - let mut vec2 = vec; - vec2.push(3); - vec2.push(4); - vec2.push(5); - vec2.push(6); - vec.push(7); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3); - let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8); - - let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat); - snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \ - has type `collections::vec::Vec`"))); - snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`"))); - - let lines = snippet.render_lines(); - println!("{:#?}", lines); - let text: String = make_string(&lines); - println!("text=r#\"\n{}\".trim_left()", text); - assert_eq!(&text[..], &r#" - ::: foo.rs - | -4 | let mut vec2 = vec; - | --- `vec` moved here because it has type `collections::vec::Vec` -... -9 | vec.push(7); - | --- use of moved value: `vec` -"#[1..]); - } - - #[test] - fn spans_without_labels() { - let file_text = r#" -fn foo() { - let mut vec = vec![0, 1, 2]; - let mut vec2 = vec; - vec2.push(3); - vec2.push(4); - vec2.push(5); - vec2.push(6); - vec.push(7); -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); - for i in 0..4 { - let span_veci = cm.span_substr(&foo, file_text, "vec", i); - snippet.push(span_veci, false, None); - } - - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("text=&r#\"\n{}\n\"#[1..]", text); - assert_eq!(text, &r#" - ::: foo.rs - | -3 | let mut vec = vec![0, 1, 2]; - | --- --- -4 | let mut vec2 = vec; - | --- --- -"#[1..]); - } - - #[test] - fn span_long_selection() { - let file_text = r#" -impl SomeTrait for () { - fn foo(x: u32) { - // impl 1 - // impl 2 - // impl 3 - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); - let fn_span = cm.span_substr(&foo, file_text, "fn", 0); - let rbrace_span = cm.span_substr(&foo, file_text, "}", 0); - snippet.push(splice(fn_span, rbrace_span), false, None); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - | -3 | fn foo(x: u32) { - | - -"#[1..]); - } - - #[test] - fn span_overlap_label() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. - - let file_text = r#" - fn foo(x: u32) { - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); - let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0); - let x_span = cm.span_substr(&foo, file_text, "x", 0); - snippet.push(fn_span, false, Some(format!("fn_span"))); - snippet.push(x_span, false, Some(format!("x_span"))); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - | -2 | fn foo(x: u32) { - | -------------- - | | | - | | x_span - | fn_span -"#[1..]); - } - - #[test] - fn span_overlap_label2() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. In this - // case, the overlap is only at the beginning, but it's still - // better to show the beginning more clearly. - - let file_text = r#" - fn foo(x: u32) { - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); - let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0); - let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0); - snippet.push(fn_span, false, Some(format!("fn_span"))); - snippet.push(x_span, false, Some(format!("x_span"))); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - | -2 | fn foo(x: u32) { - | -------------- - | | | - | | x_span - | fn_span -"#[1..]); - } - - #[test] - fn span_overlap_label3() { - // Test that we don't put `x_span` to the right of its highlight, - // since there is another highlight that overlaps it. In this - // case, the overlap is only at the beginning, but it's still - // better to show the beginning more clearly. - - let file_text = r#" - fn foo() { - let closure = || { - inner - }; - } -} -"#; - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat); - - let closure_span = { - let closure_start_span = cm.span_substr(&foo, file_text, "||", 0); - let closure_end_span = cm.span_substr(&foo, file_text, "}", 0); - splice(closure_start_span, closure_end_span) - }; - - let inner_span = cm.span_substr(&foo, file_text, "inner", 0); - - snippet.push(closure_span, false, Some(format!("foo"))); - snippet.push(inner_span, false, Some(format!("bar"))); - - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - ::: foo.rs - | -3 | let closure = || { - | - foo -4 | inner - | ----- bar -"#[1..]); - } - - #[test] - fn span_empty() { - // In one of the unit tests, we found that the parser sometimes - // gives empty spans, and in particular it supplied an EOF span - // like this one, which points at the very end. We want to - // fallback gracefully in this case. - - let file_text = r#" -fn main() { - struct Foo; - - impl !Sync for Foo {} - - unsafe impl Send for &'static Foo { - // error: cross-crate traits with a default impl, like `core::marker::Send`, - // can only be implemented for a struct/enum type, not - // `&'static Foo` -}"#; - - - let cm = Rc::new(CodeMap::new()); - let foo = cm.new_filemap_and_lines("foo.rs", None, file_text); - - let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1); - rbrace_span.lo = rbrace_span.hi; - - let mut snippet = SnippetData::new(cm.clone(), - Some(rbrace_span), - FormatMode::NewErrorFormat); - snippet.push(rbrace_span, false, None); - let lines = snippet.render_lines(); - let text: String = make_string(&lines); - println!("r#\"\n{}\"", text); - assert_eq!(text, &r#" - --> foo.rs:11:2 - | -11 | } - | - -"#[1..]); - } } diff --git a/src/test/ui/codemap_tests/empty_span.rs b/src/test/ui/codemap_tests/empty_span.rs new file mode 100644 index 000000000000..c78a58676342 --- /dev/null +++ b/src/test/ui/codemap_tests/empty_span.rs @@ -0,0 +1,19 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +#![feature(optin_builtin_traits)] +fn main() { + struct Foo; + + impl !Sync for Foo {} + + unsafe impl Send for &'static Foo { } +} diff --git a/src/test/ui/codemap_tests/empty_span.stderr b/src/test/ui/codemap_tests/empty_span.stderr new file mode 100644 index 000000000000..f3e04ef02409 --- /dev/null +++ b/src/test/ui/codemap_tests/empty_span.stderr @@ -0,0 +1,8 @@ +error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static main::Foo` + --> $DIR/empty_span.rs:18:5 + | +18 | unsafe impl Send for &'static Foo { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.rs b/src/test/ui/codemap_tests/huge_multispan_highlight.rs new file mode 100644 index 000000000000..b06832c7628e --- /dev/null +++ b/src/test/ui/codemap_tests/huge_multispan_highlight.rs @@ -0,0 +1,104 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT + +fn main() { + let x = "foo"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + let y = &mut x; +} + + + diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.stderr b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr new file mode 100644 index 000000000000..6a898a434778 --- /dev/null +++ b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr @@ -0,0 +1,11 @@ +error: cannot borrow immutable local variable `x` as mutable + --> $DIR/huge_multispan_highlight.rs:100:18 + | +14 | let x = "foo"; + | - use `mut x` here to make mutable +... +100 | let y = &mut x; + | ^ cannot borrow mutably + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/issue-11715.rs b/src/test/ui/codemap_tests/issue-11715.rs new file mode 100644 index 000000000000..7ea497a25c83 --- /dev/null +++ b/src/test/ui/codemap_tests/issue-11715.rs @@ -0,0 +1,104 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +fn main() { + let mut x = "foo"; + let y = &mut x; + let z = &mut x; +} + + + diff --git a/src/test/ui/codemap_tests/issue-11715.stderr b/src/test/ui/codemap_tests/issue-11715.stderr new file mode 100644 index 000000000000..4947cbedd200 --- /dev/null +++ b/src/test/ui/codemap_tests/issue-11715.stderr @@ -0,0 +1,12 @@ +error[E0499]: cannot borrow `x` as mutable more than once at a time + --> $DIR/issue-11715.rs:100:18 + | +99 | let y = &mut x; + | - first mutable borrow occurs here +100 | let z = &mut x; + | ^ second mutable borrow occurs here +101 | } + | - first borrow ends here + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/one_line.rs b/src/test/ui/codemap_tests/one_line.rs new file mode 100644 index 000000000000..2a5ee6f8711e --- /dev/null +++ b/src/test/ui/codemap_tests/one_line.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT + +fn main() { + let mut v = vec![Some("foo"), Some("bar")]; + v.push(v.pop().unwrap()); +} diff --git a/src/test/ui/codemap_tests/one_line.stderr b/src/test/ui/codemap_tests/one_line.stderr new file mode 100644 index 000000000000..8f80489ea1ae --- /dev/null +++ b/src/test/ui/codemap_tests/one_line.stderr @@ -0,0 +1,11 @@ +error[E0499]: cannot borrow `v` as mutable more than once at a time + --> $DIR/one_line.rs:15:12 + | +15 | v.push(v.pop().unwrap()); + | - ^ - first borrow ends here + | | | + | | second mutable borrow occurs here + | first mutable borrow occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/overlapping_spans.rs b/src/test/ui/codemap_tests/overlapping_spans.rs new file mode 100644 index 000000000000..5a90852392c0 --- /dev/null +++ b/src/test/ui/codemap_tests/overlapping_spans.rs @@ -0,0 +1,24 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +#[derive(Debug)] +struct Foo { } + +struct S {f:String} +impl Drop for S { + fn drop(&mut self) { println!("{}", self.f); } +} + +fn main() { + match (S {f:"foo".to_string()}) { + S {f:_s} => {} + } +} diff --git a/src/test/ui/codemap_tests/overlapping_spans.stderr b/src/test/ui/codemap_tests/overlapping_spans.stderr new file mode 100644 index 000000000000..cbcf154eaba5 --- /dev/null +++ b/src/test/ui/codemap_tests/overlapping_spans.stderr @@ -0,0 +1,11 @@ +error[E0509]: cannot move out of type `S`, which implements the `Drop` trait + --> $DIR/overlapping_spans.rs:22:9 + | +22 | S {f:_s} => {} + | ^^^^^--^ + | | | + | | hint: to prevent move, use `ref _s` or `ref mut _s` + | cannot move out of here + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/tab.rs b/src/test/ui/codemap_tests/tab.rs new file mode 100644 index 000000000000..aaaee8c5577f --- /dev/null +++ b/src/test/ui/codemap_tests/tab.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +// ignore-tidy-tab +fn main() { + bar; +} + diff --git a/src/test/ui/codemap_tests/tab.stderr b/src/test/ui/codemap_tests/tab.stderr new file mode 100644 index 000000000000..543c02fb701f --- /dev/null +++ b/src/test/ui/codemap_tests/tab.stderr @@ -0,0 +1,8 @@ +error[E0425]: unresolved name `bar` + --> $DIR/tab.rs:14:2 + | +14 | \tbar; + | \t^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/codemap_tests/two_files.rs b/src/test/ui/codemap_tests/two_files.rs new file mode 100644 index 000000000000..53e240e8c473 --- /dev/null +++ b/src/test/ui/codemap_tests/two_files.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +include!("two_files_data.rs"); + +struct Baz { } + +impl Bar for Baz { } + +fn main() { } diff --git a/src/test/ui/codemap_tests/two_files.stderr b/src/test/ui/codemap_tests/two_files.stderr new file mode 100644 index 000000000000..6c388cd69395 --- /dev/null +++ b/src/test/ui/codemap_tests/two_files.stderr @@ -0,0 +1,13 @@ +error[E0404]: `Bar` is not a trait + --> $DIR/two_files.rs:16:6 + | +16 | impl Bar for Baz { } + | ^^^ `Bar` is not a trait + | + ::: $DIR/two_files_data.rs + | +15 | type Bar = Foo; + | --------------- type aliases cannot be used for traits + +error: cannot continue compilation due to previous error + diff --git a/src/test/ui/codemap_tests/two_files_data.rs b/src/test/ui/codemap_tests/two_files_data.rs new file mode 100644 index 000000000000..412c40f8e811 --- /dev/null +++ b/src/test/ui/codemap_tests/two_files_data.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +// ignore-test +trait Foo { } + +type Bar = Foo; + From d162b97c4bb63302d19f4d6ae61a369ba278ff42 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 06:50:50 -0400 Subject: [PATCH 21/53] Teach EmitterWriter about the dangers of quasi-quoting --- src/librustc_errors/emitter.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 161f2f7bb1c3..6c47c01d0fb1 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -166,6 +166,9 @@ impl EmitterWriter { if let Some(ref cm) = self.cm { for span_label in msp.span_labels() { + if span_label.span == DUMMY_SP || span_label.span == COMMAND_LINE_SP { + continue; + } let lo = cm.lookup_char_pos(span_label.span.lo); let mut hi = cm.lookup_char_pos(span_label.span.hi); let mut is_minimized = false; @@ -386,15 +389,19 @@ impl EmitterWriter { let mut max = 0; if let Some(ref cm) = self.cm { for primary_span in msp.primary_spans() { - let hi = cm.lookup_char_pos(primary_span.hi); - if hi.line > max { - max = hi.line; + if primary_span != &DUMMY_SP && primary_span != &COMMAND_LINE_SP { + let hi = cm.lookup_char_pos(primary_span.hi); + if hi.line > max { + max = hi.line; + } } } for span_label in msp.span_labels() { - let hi = cm.lookup_char_pos(span_label.span.hi); - if hi.line > max { - max = hi.line; + if span_label.span != DUMMY_SP && span_label.span != COMMAND_LINE_SP { + let hi = cm.lookup_char_pos(span_label.span.hi); + if hi.line > max { + max = hi.line; + } } } } @@ -456,7 +463,13 @@ impl EmitterWriter { let primary_lo = if let (Some(ref cm), Some(ref primary_span)) = (self.cm.as_ref(), msp.primary_span().as_ref()) { - cm.lookup_char_pos(primary_span.lo) + if primary_span != &&DUMMY_SP && primary_span != &&COMMAND_LINE_SP { + cm.lookup_char_pos(primary_span.lo) + } + else { + emit_to_destination(&buffer.render(), level, &mut self.dst)?; + return Ok(()); + } } else { // If we don't have span information, emit and exit emit_to_destination(&buffer.render(), level, &mut self.dst)?; From a9dfac8725bb8a550318034a577c9b273545fd27 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 09:02:48 -0400 Subject: [PATCH 22/53] Add back in import needed for codemap tests --- src/libsyntax/codemap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index afa5a3b38ba2..1e5f88dac49b 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -828,6 +828,7 @@ impl CodeMapper for CodeMap { mod tests { use super::*; use std::io::{self, Write}; + use errors::snippet::StyledString; use std::rc::Rc; #[test] From c38d5df4a2a65ed11fefaf792b6535451c2fd767 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 10:36:23 -0400 Subject: [PATCH 23/53] Remove unused imports --- src/libsyntax/codemap.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 1e5f88dac49b..42bb804cff2b 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -827,7 +827,6 @@ impl CodeMapper for CodeMap { #[cfg(test)] mod tests { use super::*; - use std::io::{self, Write}; use errors::snippet::StyledString; use std::rc::Rc; @@ -1118,24 +1117,6 @@ mod tests { } } - fn splice(start: Span, end: Span) -> Span { - Span { - lo: start.lo, - hi: end.hi, - expn_id: NO_EXPANSION, - } - } - - fn make_string(lines: Vec>) -> String { - lines.iter() - .flat_map(|rl| { - rl.iter() - .map(|s| &s.text[..]) - .chain(Some("\n")) - }) - .collect() - } - fn init_expansion_chain(cm: &CodeMap) -> Span { // Creates an expansion chain containing two recursive calls // root -> expA -> expA -> expB -> expB -> end From 0b727f080108607188db8b56e47548a277cde0ff Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 10:55:09 -0400 Subject: [PATCH 24/53] Add unicode test to ui tests --- src/test/ui/codemap_tests/unicode.rs | 14 ++++++++++++++ src/test/ui/codemap_tests/unicode.stderr | 8 ++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/test/ui/codemap_tests/unicode.rs create mode 100644 src/test/ui/codemap_tests/unicode.stderr diff --git a/src/test/ui/codemap_tests/unicode.rs b/src/test/ui/codemap_tests/unicode.rs new file mode 100644 index 000000000000..19660133d622 --- /dev/null +++ b/src/test/ui/codemap_tests/unicode.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustc-env:RUST_NEW_ERROR_FORMAT +extern "路濫狼á́́" fn foo() {} + +fn main() { } diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr new file mode 100644 index 000000000000..178bee7b7f3a --- /dev/null +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -0,0 +1,8 @@ +error: invalid ABI: expected one of [cdecl, stdcall, fastcall, vectorcall, aapcs, win64, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic], found `路濫狼á́́` + --> $DIR/unicode.rs:12:8 + | +12 | extern "路濫狼á́́" fn foo() {} + | ^^^^^^^^ + +error: aborting due to previous error + From 65fb7be72820f9be7ee7bb8e626e1d0fbb03968d Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Thu, 14 Jul 2016 17:13:13 +0200 Subject: [PATCH 25/53] mk: Move some definitions after their dependencies, to be visually less confusing --- mk/main.mk | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/mk/main.mk b/mk/main.mk index 4c72597f0c5c..e446b64a6f5a 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -20,29 +20,6 @@ CFG_RELEASE_NUM=1.12.0 # versions (section 9) CFG_PRERELEASE_VERSION=.1 -# Append a version-dependent hash to each library, so we can install different -# versions in the same place -CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND)) - -# A magic value that allows the compiler to use unstable features during the -# bootstrap even when doing so would normally be an error because of feature -# staging or because the build turns on warnings-as-errors and unstable features -# default to warnings. The build has to match this key in an env var. -# -# This value is keyed off the release to ensure that all compilers for one -# particular release have the same bootstrap key. Note that this is -# intentionally not "secure" by any definition, this is largely just a deterrent -# from users enabling unstable features on the stable compiler. -CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA) - -# The stage0 compiler needs to use the previous key recorded in src/stage0.txt, -# except for local-rebuild when it just uses the same current key. -ifdef CFG_ENABLE_LOCAL_REBUILD -CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY) -else -CFG_BOOTSTRAP_KEY_STAGE0=$(shell grep 'rustc_key' $(S)src/stage0.txt | sed 's/rustc_key: '//) -endif - ifeq ($(CFG_RELEASE_CHANNEL),stable) # This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly" CFG_RELEASE=$(CFG_RELEASE_NUM) @@ -72,6 +49,29 @@ CFG_RELEASE=$(CFG_RELEASE_NUM)-dev CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-dev endif +# Append a version-dependent hash to each library, so we can install different +# versions in the same place +CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND)) + +# A magic value that allows the compiler to use unstable features during the +# bootstrap even when doing so would normally be an error because of feature +# staging or because the build turns on warnings-as-errors and unstable features +# default to warnings. The build has to match this key in an env var. +# +# This value is keyed off the release to ensure that all compilers for one +# particular release have the same bootstrap key. Note that this is +# intentionally not "secure" by any definition, this is largely just a deterrent +# from users enabling unstable features on the stable compiler. +CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA) + +# The stage0 compiler needs to use the previous key recorded in src/stage0.txt, +# except for local-rebuild when it just uses the same current key. +ifdef CFG_ENABLE_LOCAL_REBUILD +CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY) +else +CFG_BOOTSTRAP_KEY_STAGE0=$(shell grep 'rustc_key' $(S)src/stage0.txt | sed 's/rustc_key: '//) +endif + # The name of the package to use for creating tarballs, installers etc. CFG_PACKAGE_NAME=rustc-$(CFG_PACKAGE_VERS) From 01c87d77632ef9045f4c600a4b81711398372b9b Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 13:25:06 -0400 Subject: [PATCH 26/53] Remove more unused imports --- src/libsyntax/codemap.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 42bb804cff2b..a8aca90e6238 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -827,7 +827,6 @@ impl CodeMapper for CodeMap { #[cfg(test)] mod tests { use super::*; - use errors::snippet::StyledString; use std::rc::Rc; #[test] From bf66a4840be3b7366b34454eb5573dca28ed8830 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 14 Jul 2016 15:43:53 -0400 Subject: [PATCH 27/53] Fix up more tests that I missed --- src/librustc_driver/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index ace469680df2..a607d602a57e 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -78,7 +78,7 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { impl Emitter for ExpectErrorEmitter { fn emit(&mut self, db: &DiagnosticBuilder) { - remove_message(self, db.message, lvl); + remove_message(self, &db.message, db.level); for child in &db.children { remove_message(self, &child.message, child.level); } From f2e73d9d48dec8ae3103ba46a35586a2a5435a4f Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Tue, 12 Jul 2016 10:43:37 +0200 Subject: [PATCH 28/53] Fix compile-fail test for `ExactSizeIterator::is_empty` --- src/test/compile-fail/method-suggestion-no-duplication.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/method-suggestion-no-duplication.rs b/src/test/compile-fail/method-suggestion-no-duplication.rs index c8c1447fea38..390b8f07b2fb 100644 --- a/src/test/compile-fail/method-suggestion-no-duplication.rs +++ b/src/test/compile-fail/method-suggestion-no-duplication.rs @@ -18,7 +18,8 @@ fn foo(f: F) where F: FnMut(Foo) {} fn main() { foo(|s| s.is_empty()); //~^ ERROR no method named `is_empty` found - //~^^ HELP #1: `core::slice::SliceExt` - //~^^^ HELP #2: `core::str::StrExt` - //~^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them: + //~^^ HELP #1: `std::iter::ExactSizeIterator` + //~^^^ HELP #2: `core::slice::SliceExt` + //~^^^^ HELP #3: `core::str::StrExt` + //~^^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them: } From dae8d073d4c5723099792f31769abcb3575bbda7 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 15 Jul 2016 06:52:19 -0400 Subject: [PATCH 29/53] Nudge travis by commenting a little --- src/librustc_errors/emitter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 6c47c01d0fb1..893f8a6e4ddb 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -33,6 +33,7 @@ pub trait Emitter { impl Emitter for EmitterWriter { fn emit(&mut self, db: &DiagnosticBuilder) { + // Pick old school mode either from env or let the test dictate the format let old_school = match self.format_mode { FormatMode::NewErrorFormat => false, FormatMode::OriginalErrorFormat => true, From c7158a143ae639081084755038c5ce1f8c398e93 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 15 Jul 2016 08:47:12 -0400 Subject: [PATCH 30/53] Remove unused import --- src/librustc_driver/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index a607d602a57e..39763bfa0eb6 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -34,7 +34,7 @@ use syntax::abi::Abi; use syntax::codemap::CodeMap; use errors; use errors::emitter::Emitter; -use errors::{Level, RenderSpan, DiagnosticBuilder}; +use errors::{Level, DiagnosticBuilder}; use syntax::parse::token; use syntax::feature_gate::UnstableFeatures; use syntax_pos::DUMMY_SP; From b10ac8a4501201d3209129906757b107ad6357f8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Jul 2016 17:51:27 +0200 Subject: [PATCH 31/53] Improve float number example --- src/libcore/num/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index fcdbde0d19f4..97648cc34699 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2318,7 +2318,7 @@ impl usize { /// let num = 12.4_f32; /// let inf = f32::INFINITY; /// let zero = 0f32; -/// let sub: f32 = 0.000000000000000000000000000000000000011754942; +/// let sub: f32 = 1.1754942e-38; /// let nan = f32::NAN; /// /// assert_eq!(num.classify(), FpCategory::Normal); From c850470f73b21d55842801f69e83546d7dd727a7 Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Thu, 14 Jul 2016 19:32:53 +0200 Subject: [PATCH 32/53] mk: If local-rust is the same as the current version, then force a local-rebuild --- configure | 2 +- mk/main.mk | 11 ++++++++++- mk/stage0.mk | 1 + src/etc/local_stage0.sh | 9 +++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 16496eb89d4c..fd009a757a4b 100755 --- a/configure +++ b/configure @@ -600,7 +600,7 @@ opt debug-assertions 0 "build with debugging assertions" opt fast-make 0 "use .gitmodules as timestamp for submodule deps" opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds" opt local-rust 0 "use an installed rustc rather than downloading a snapshot" -opt local-rebuild 0 "use an installed rustc matching the current version, for rebuilds" +opt local-rebuild 0 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version" opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM" opt rpath 1 "build rpaths into rustc itself" opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0" diff --git a/mk/main.mk b/mk/main.mk index e446b64a6f5a..fd12bf26dfc7 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -64,12 +64,21 @@ CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(C # from users enabling unstable features on the stable compiler. CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA) +# If local-rust is the same as the current version, then force a local-rebuild +ifdef CFG_ENABLE_LOCAL_RUST +ifeq ($(CFG_RELEASE),\ + $(shell $(S)src/etc/local_stage0.sh --print-rustc-release $(CFG_LOCAL_RUST_ROOT))) + CFG_INFO := $(info cfg: auto-detected local-rebuild $(CFG_RELEASE)) + CFG_ENABLE_LOCAL_REBUILD = 1 +endif +endif + # The stage0 compiler needs to use the previous key recorded in src/stage0.txt, # except for local-rebuild when it just uses the same current key. ifdef CFG_ENABLE_LOCAL_REBUILD CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY) else -CFG_BOOTSTRAP_KEY_STAGE0=$(shell grep 'rustc_key' $(S)src/stage0.txt | sed 's/rustc_key: '//) +CFG_BOOTSTRAP_KEY_STAGE0=$(shell sed -ne 's/^rustc_key: //p' $(S)src/stage0.txt) endif # The name of the package to use for creating tarballs, installers etc. diff --git a/mk/stage0.mk b/mk/stage0.mk index d0191874cb3d..8a2bf2ebbde6 100644 --- a/mk/stage0.mk +++ b/mk/stage0.mk @@ -11,6 +11,7 @@ endif $(SNAPSHOT_RUSTC_POST_CLEANUP): \ $(S)src/stage0.txt \ + $(S)src/etc/local_stage0.sh \ $(S)src/etc/get-stage0.py $(MKFILE_DEPS) \ | $(HBIN0_H_$(CFG_BUILD))/ @$(call E, fetch: $@) diff --git a/src/etc/local_stage0.sh b/src/etc/local_stage0.sh index fb455441910b..354be34b6a29 100755 --- a/src/etc/local_stage0.sh +++ b/src/etc/local_stage0.sh @@ -49,6 +49,13 @@ if [ -z $TARG_DIR ]; then exit 1 fi +case "$TARG_DIR" in +--print-rustc-release) + # not actually copying to TARG_DIR, just print the local rustc version and exit + ${PREFIX}/bin/rustc${BIN_SUF} --version --verbose | sed -ne 's/^release: //p' +;; +*) + cp ${PREFIX}/bin/rustc${BIN_SUF} ${TARG_DIR}/stage0/bin/ cp ${PREFIX}/${LIB_DIR}/${RUSTLIBDIR}/${TARG_DIR}/${LIB_DIR}/* ${TARG_DIR}/stage0/${LIB_DIR}/ cp ${PREFIX}/${LIB_DIR}/${LIB_PREFIX}extra*${LIB_SUF} ${TARG_DIR}/stage0/${LIB_DIR}/ @@ -66,3 +73,5 @@ cp ${PREFIX}/${LIB_DIR}/${LIB_PREFIX}term*${LIB_SUF} ${TARG_DIR}/stage0/${LIB_DI # do not fail if one of the above fails, as all we need is a working rustc! exit 0 + +esac From bbff3367fe0355dbdee466bd7d3e0ea241799a9f Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Thu, 14 Jul 2016 19:39:55 +0200 Subject: [PATCH 33/53] rustbuild: If local-rust is the same as the current version, then force a local-rebuild --- src/bootstrap/lib.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 943271fc8a64..caa6ea17ea05 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -118,6 +118,7 @@ pub struct Build { ver_date: Option, version: String, package_vers: String, + local_rebuild: bool, bootstrap_key: String, bootstrap_key_stage0: String, @@ -174,6 +175,7 @@ impl Build { Some(ref s) => PathBuf::from(s), None => stage0_root.join(exe("cargo", &config.build)), }; + let local_rebuild = config.local_rebuild; Build { flags: flags, @@ -189,6 +191,7 @@ impl Build { short_ver_hash: None, ver_date: None, version: String::new(), + local_rebuild: local_rebuild, bootstrap_key: String::new(), bootstrap_key_stage0: String::new(), package_vers: String::new(), @@ -219,6 +222,16 @@ impl Build { sanity::check(self); self.verbose("collecting channel variables"); channel::collect(self); + // If local-rust is the same as the current version, then force a local-rebuild + let local_version_verbose = output( + Command::new(&self.rustc).arg("--version").arg("--verbose")); + let local_release = local_version_verbose + .lines().filter(|x| x.starts_with("release:")) + .next().unwrap().trim_left_matches("release:").trim(); + if local_release == self.release { + self.verbose(&format!("auto-detected local-rebuild {}", self.release)); + self.local_rebuild = true; + } self.verbose("updating submodules"); self.update_submodules(); @@ -525,7 +538,7 @@ impl Build { .arg("--target").arg(target); let stage; - if compiler.stage == 0 && self.config.local_rebuild { + if compiler.stage == 0 && self.local_rebuild { // Assume the local-rebuild rustc already has stage1 features. stage = 1; } else { @@ -766,7 +779,7 @@ impl Build { // In stage0 we're using a previously released stable compiler, so we // use the stage0 bootstrap key. Otherwise we use our own build's // bootstrap key. - let bootstrap_key = if compiler.is_snapshot(self) && !self.config.local_rebuild { + let bootstrap_key = if compiler.is_snapshot(self) && !self.local_rebuild { &self.bootstrap_key_stage0 } else { &self.bootstrap_key From b28e9dcae8f0f1930bbd11c826dd7357093ea7ae Mon Sep 17 00:00:00 2001 From: Nikhil Shagrithaya Date: Fri, 15 Jul 2016 20:47:45 +0530 Subject: [PATCH 34/53] Refactored code to access TLS only in case of panic --- src/libstd/panicking.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index d73e9542d212..a7de9d3a0c5d 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -232,10 +232,9 @@ fn default_hook(info: &PanicInfo) { pub unsafe fn try R>(f: F) -> Result> { let mut slot = None; let mut f = Some(f); - let ret = PANIC_COUNT.with(|s| { - let prev = s.get(); - s.set(0); + let ret; + { let mut to_run = || { slot = Some(f.take().unwrap()()); }; @@ -248,18 +247,21 @@ pub unsafe fn try R>(f: F) -> Result> { dataptr, &mut any_data, &mut any_vtable); - s.set(prev); - if r == 0 { - Ok(()) + ret = Ok(()); } else { - Err(mem::transmute(raw::TraitObject { + PANIC_COUNT.with(|s| { + let prev = s.get(); + s.set(prev - 1); + }); + ret = Err(mem::transmute(raw::TraitObject { data: any_data as *mut _, vtable: any_vtable as *mut _, - })) + })); } - }); + } + debug_assert!(PANIC_COUNT.with(|c| c.get() == 0)); return ret.map(|()| { slot.take().unwrap() }); From fcecdac96d718b90849f210e1ccd6be45dc6ff1e Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 15 Jul 2016 13:17:35 -0400 Subject: [PATCH 35/53] Fix up documentation around no_std 1. Fix the sections in the book to have the correct signatures. I've also marked them as `ignore`; there's no way to set the `no_std` feature for libc, so it pulls in the stdlib, so this wasn't even testing the actual thing it was testing. Better to just ignore. 2. Correcting libcore's docs for factual inaccuracy, and add a note about language items. Fixes #33677 --- src/doc/book/no-stdlib.md | 73 +++++++++++++++++++++++++-------------- src/libcore/lib.rs | 13 ++++--- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/doc/book/no-stdlib.md b/src/doc/book/no-stdlib.md index c5c139e6580b..6fd7cf66920d 100644 --- a/src/doc/book/no-stdlib.md +++ b/src/doc/book/no-stdlib.md @@ -21,7 +21,7 @@ this using our `Cargo.toml` file: ```toml [dependencies] -libc = { version = "0.2.11", default-features = false } +libc = { version = "0.2.14", default-features = false } ``` Note that the default features have been disabled. This is a critical step - @@ -36,8 +36,7 @@ or overriding the default shim for the C `main` function with your own. The function marked `#[start]` is passed the command line parameters in the same format as C: -```rust -# #![feature(libc)] +```rust,ignore #![feature(lang_items)] #![feature(start)] #![no_std] @@ -51,15 +50,21 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } -// These functions and traits are used by the compiler, but not +// These functions are used by the compiler, but not // for a bare-bones hello world. These are normally // provided by libstd. -#[lang = "eh_personality"] extern fn eh_personality() {} -#[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop {} } -# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} -# #[no_mangle] pub extern fn rust_eh_register_frames () {} -# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} -# // fn main() {} tricked you, rustdoc! +#[lang = "eh_personality"] +#[no_mangle] +pub extern fn eh_personality() { +} + +#[lang = "panic_fmt"] +#[no_mangle] +pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, + _file: &'static str, + _line: u32) -> ! { + loop {} +} ``` To override the compiler-inserted `main` shim, one has to disable it @@ -67,37 +72,55 @@ with `#![no_main]` and then create the appropriate symbol with the correct ABI and the correct name, which requires overriding the compiler's name mangling too: -```rust -# #![feature(libc)] +```rust,ignore #![feature(lang_items)] #![feature(start)] #![no_std] #![no_main] +// Pull in the system libc library for what crt0.o likely requires extern crate libc; +// Entry point for this program #[no_mangle] // ensure that this symbol is called `main` in the output -pub extern fn main(argc: i32, argv: *const *const u8) -> i32 { +pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { 0 } -#[lang = "eh_personality"] extern fn eh_personality() {} -#[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop {} } -# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} -# #[no_mangle] pub extern fn rust_eh_register_frames () {} -# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} -# // fn main() {} tricked you, rustdoc! +// These functions and traits are used by the compiler, but not +// for a bare-bones hello world. These are normally +// provided by libstd. +#[lang = "eh_personality"] +#[no_mangle] +pub extern fn eh_personality() { +} + +#[lang = "panic_fmt"] +#[no_mangle] +pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, + _file: &'static str, + _line: u32) -> ! { + loop {} +} ``` -The compiler currently makes a few assumptions about symbols which are available -in the executable to call. Normally these functions are provided by the standard -library, but without it you must define your own. +## More about the langauge items + +The compiler currently makes a few assumptions about symbols which are +available in the executable to call. Normally these functions are provided by +the standard library, but without it you must define your own. These symbols +are called "language items", and they each have an internal name, and then a +signature that an implementation must conform to. The first of these two functions, `eh_personality`, is used by the failure mechanisms of the compiler. This is often mapped to GCC's personality function (see the [libstd implementation][unwind] for more information), but crates which do not trigger a panic can be assured that this function is never -called. The second function, `panic_fmt`, is also used by the failure -mechanisms of the compiler. - +called. Both the language item and the symbol name are `eh_personality`. + [unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs + +The second function, `panic_fmt`, is also used by the failure mechanisms of the +compiler. When a panic happens, this controls the message that's displayed on +the screen. While the language item's name is `panic_fmt`, the symbol name is +`rust_begin_panic`. diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index e849369d647c..0ad1f671f155 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -25,6 +25,8 @@ //! //! # How to use the core library //! +//! Please note that all of these details are currently not considered stable. +//! // FIXME: Fill me in with more detail when the interface settles //! This library is built on the assumption of a few existing symbols: //! @@ -34,11 +36,12 @@ //! These functions are often provided by the system libc, but can also be //! provided by the [rlibc crate](https://crates.io/crates/rlibc). //! -//! * `rust_begin_unwind` - This function takes three arguments, a -//! `fmt::Arguments`, a `&str`, and a `u32`. These three arguments dictate -//! the panic message, the file at which panic was invoked, and the line. -//! It is up to consumers of this core library to define this panic -//! function; it is only required to never return. +//! * `rust_begin_panic` - This function takes three arguments, a +//! `fmt::Arguments`, a `&'static str`, and a `u32`. These three arguments +//! dictate the panic message, the file at which panic was invoked, and the +//! line. It is up to consumers of this core library to define this panic +//! function; it is only required to never return. This requires a `lang` +//! attribute named `panic_fmt`. // Since libcore defines many fundamental lang items, all tests live in a // separate crate, libcoretest, to avoid bizarre issues. From 5f438171425cd424647ce3499c2a1d006c577365 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 14 Jul 2016 21:48:41 -0700 Subject: [PATCH 36/53] mk: Don't pass -msoft-float on mips-gnu Soon the LLVM upgrade (#34743) will require an updated CMake installation, and the easiest way to do this was to upgrade the Ubuntu version of the bots to 16.04. This in turn brings in a new MIPS compiler on the linux-cross builder, which is now from the "official" ubuntu repositories. Unfortunately these new compilers don't support compiling with the `-msoft-float` flag like we're currently passing, causing compiles to fail. This commit removes these flags as it's not clear *why* they're being passed, as the mipsel targets also don't have it. At least if it's not supported by a debian default compiler, perhaps it's not too relevant to support? --- mk/cfg/mips-unknown-linux-gnu.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mk/cfg/mips-unknown-linux-gnu.mk b/mk/cfg/mips-unknown-linux-gnu.mk index 9e7042befa97..0783a4c17a4f 100644 --- a/mk/cfg/mips-unknown-linux-gnu.mk +++ b/mk/cfg/mips-unknown-linux-gnu.mk @@ -7,10 +7,10 @@ CFG_LIB_NAME_mips-unknown-linux-gnu=lib$(1).so CFG_STATIC_LIB_NAME_mips-unknown-linux-gnu=lib$(1).a CFG_LIB_GLOB_mips-unknown-linux-gnu=lib$(1)-*.so CFG_LIB_DSYM_GLOB_mips-unknown-linux-gnu=lib$(1)-*.dylib.dSYM -CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -msoft-float -mabi=32 $(CFLAGS) -CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -msoft-float -mabi=32 $(CFLAGS) +CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -mabi=32 $(CFLAGS) +CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -mabi=32 $(CFLAGS) CFG_GCCISH_CXXFLAGS_mips-unknown-linux-gnu := -fno-rtti $(CXXFLAGS) -CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -msoft-float -mabi=32 +CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -mabi=32 CFG_GCCISH_DEF_FLAG_mips-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list= CFG_LLC_FLAGS_mips-unknown-linux-gnu := CFG_INSTALL_NAME_mips-unknown-linux-gnu = From fecf3b6a2c192d52ae7f69551f76b1ac4b01a85c Mon Sep 17 00:00:00 2001 From: cgswords Date: Thu, 14 Jul 2016 09:52:43 -0700 Subject: [PATCH 37/53] Added tokenstream parser procedure --- src/libsyntax/parse/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 9502bc48a3e1..80ffd8775188 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -224,10 +224,18 @@ pub fn filemap_to_parser<'a>(sess: &'a ParseSess, // compiler expands into it pub fn new_parser_from_tts<'a>(sess: &'a ParseSess, cfg: ast::CrateConfig, - tts: Vec) -> Parser<'a> { + tts: Vec) + -> Parser<'a> { tts_to_parser(sess, tts, cfg) } +pub fn new_parser_from_ts<'a>(sess: &'a ParseSess, + cfg: ast::CrateConfig, + ts: tokenstream::TokenStream) + -> Parser<'a> { + tts_to_parser(sess, ts.tts, cfg) +} + // base abstractions From ff95ba3a8c1cc2cf9b1901a18ba73434f3380c0d Mon Sep 17 00:00:00 2001 From: Aravind Gollakota Date: Tue, 5 Jul 2016 21:35:12 -0700 Subject: [PATCH 38/53] syntax: Better error message for inner attr following doc comment --- src/libsyntax/parse/attr.rs | 70 +++++++++++++++---- .../inner-attr-after-doc-comment.rs | 20 ++++++ src/test/parse-fail/inner-attr.rs | 16 +++++ 3 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 src/test/parse-fail/inner-attr-after-doc-comment.rs create mode 100644 src/test/parse-fail/inner-attr.rs diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 15344cef1dbc..2ae3236cd5aa 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -18,23 +18,43 @@ use parse::token; use parse::parser::{Parser, TokenType}; use ptr::P; +#[derive(PartialEq, Eq, Debug)] +enum InnerAttributeParsePolicy<'a> { + Permitted, + NotPermitted { reason: &'a str }, +} + +const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute is not \ + permitted in this context"; + impl<'a> Parser<'a> { /// Parse attributes that appear before an item pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = Vec::new(); + let mut just_parsed_doc_comment = false; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); match self.token { token::Pound => { - attrs.push(self.parse_attribute(false)?); + let inner_error_reason = if just_parsed_doc_comment { + "an inner attribute is not permitted following an outer doc comment" + } else if !attrs.is_empty() { + "an inner attribute is not permitted following an outer attribute" + } else { + DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG + }; + let inner_parse_policy = + InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason }; + attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?); + just_parsed_doc_comment = false; } token::DocComment(s) => { let attr = ::attr::mk_sugared_doc_attr( - attr::mk_attr_id(), - self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), - self.span.lo, - self.span.hi - ); + attr::mk_attr_id(), + self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)), + self.span.lo, + self.span.hi + ); if attr.node.style != ast::AttrStyle::Outer { let mut err = self.fatal("expected outer doc comment"); err.note("inner doc comments like this (starting with \ @@ -43,6 +63,7 @@ impl<'a> Parser<'a> { } attrs.push(attr); self.bump(); + just_parsed_doc_comment = true; } _ => break, } @@ -55,26 +76,46 @@ impl<'a> Parser<'a> { /// If permit_inner is true, then a leading `!` indicates an inner /// attribute pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> { - debug!("parse_attributes: permit_inner={:?} self.token={:?}", + debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token); + let inner_parse_policy = if permit_inner { + InnerAttributeParsePolicy::Permitted + } else { + InnerAttributeParsePolicy::NotPermitted + { reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG } + }; + self.parse_attribute_with_inner_parse_policy(inner_parse_policy) + } + + /// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy` + /// that prescribes how to handle inner attributes. + fn parse_attribute_with_inner_parse_policy(&mut self, + inner_parse_policy: InnerAttributeParsePolicy) + -> PResult<'a, ast::Attribute> { + debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", + inner_parse_policy, + self.token); let (span, value, mut style) = match self.token { token::Pound => { let lo = self.span.lo; self.bump(); - if permit_inner { + if inner_parse_policy == InnerAttributeParsePolicy::Permitted { self.expected_tokens.push(TokenType::Token(token::Not)); } let style = if self.token == token::Not { self.bump(); - if !permit_inner { + if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy + { let span = self.span; self.diagnostic() - .struct_span_err(span, - "an inner attribute is not permitted in this context") - .help("place inner attribute at the top of the module or \ - block") + .struct_span_err(span, reason) + .note("inner attributes and doc comments, like `#![no_std]` or \ + `//! My crate`, annotate the item enclosing them, and are \ + usually found at the beginning of source files. Outer \ + attributes and doc comments, like `#[test]` and + `/// My function`, annotate the item following them.") .emit() } ast::AttrStyle::Inner @@ -95,7 +136,8 @@ impl<'a> Parser<'a> { } }; - if permit_inner && self.token == token::Semi { + if inner_parse_policy == InnerAttributeParsePolicy::Permitted && + self.token == token::Semi { self.bump(); self.span_warn(span, "this inner attribute syntax is deprecated. The new syntax is \ diff --git a/src/test/parse-fail/inner-attr-after-doc-comment.rs b/src/test/parse-fail/inner-attr-after-doc-comment.rs new file mode 100644 index 000000000000..ed8342d9f5aa --- /dev/null +++ b/src/test/parse-fail/inner-attr-after-doc-comment.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +#![feature(lang_items)] +/** + * My module + */ + +#![recursion_limit="100"] +//~^ ERROR an inner attribute is not permitted following an outer doc comment +fn main() {} diff --git a/src/test/parse-fail/inner-attr.rs b/src/test/parse-fail/inner-attr.rs new file mode 100644 index 000000000000..8cebda664456 --- /dev/null +++ b/src/test/parse-fail/inner-attr.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +#[feature(lang_items)] + +#![recursion_limit="100"] //~ ERROR an inner attribute is not permitted following an outer attribute +fn main() {} From d1486108bc517edf8fb401da587e1fac22ffffb0 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 16 Jul 2016 10:45:13 +0200 Subject: [PATCH 39/53] Erase regions before computing type layout Fixes #34839 --- src/librustc_lint/types.rs | 3 ++- src/test/compile-fail/issue-34839.rs | 31 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/issue-34839.rs diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 9b08ddcafab8..f688bd80ee9d 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -698,7 +698,8 @@ impl LateLintPass for VariantSizeDifferences { if gens.ty_params.is_empty() { // sizes only make sense for non-generic types let t = cx.tcx.node_id_to_type(it.id); let layout = cx.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| { - t.layout(&infcx).unwrap_or_else(|e| { + let ty = cx.tcx.erase_regions(&t); + ty.layout(&infcx).unwrap_or_else(|e| { bug!("failed to get layout for `{}`: {}", t, e) }) }); diff --git a/src/test/compile-fail/issue-34839.rs b/src/test/compile-fail/issue-34839.rs new file mode 100644 index 000000000000..be7e78200204 --- /dev/null +++ b/src/test/compile-fail/issue-34839.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +trait RegularExpression: Sized { + type Text; +} + +struct ExecNoSyncStr<'a>(&'a u8); + +impl<'c> RegularExpression for ExecNoSyncStr<'c> { + type Text = u8; +} + +struct FindCaptures<'t, R>(&'t R::Text) where R: RegularExpression, R::Text: 't; + +enum FindCapturesInner<'r, 't> { + Dynamic(FindCaptures<'t, ExecNoSyncStr<'r>>), +} + +#[rustc_error] +fn main() {} //~ ERROR compilation successful From fc0d037265beee18458b671cd46a7a9a53706a16 Mon Sep 17 00:00:00 2001 From: Bastien Dejean Date: Sat, 16 Jul 2016 10:50:19 +0200 Subject: [PATCH 40/53] Add missing inline code delimiters around Vec --- src/doc/nomicon/phantom-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/nomicon/phantom-data.md b/src/doc/nomicon/phantom-data.md index 0d7ec7f16179..2be14c150165 100644 --- a/src/doc/nomicon/phantom-data.md +++ b/src/doc/nomicon/phantom-data.md @@ -56,7 +56,7 @@ Good to go! Nope. -The drop checker will generously determine that Vec does not own any values +The drop checker will generously determine that `Vec` does not own any values of type T. This will in turn make it conclude that it doesn't need to worry about Vec dropping any T's in its destructor for determining drop check soundness. This will in turn allow people to create unsoundness using From cc2176d3fea15b5a67acc959ca92e0ee6521515c Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Sat, 16 Jul 2016 11:10:05 +0200 Subject: [PATCH 41/53] doc: remove stray comma --- src/doc/nomicon/phantom-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/nomicon/phantom-data.md b/src/doc/nomicon/phantom-data.md index 0d7ec7f16179..ba9ef19830b6 100644 --- a/src/doc/nomicon/phantom-data.md +++ b/src/doc/nomicon/phantom-data.md @@ -81,7 +81,7 @@ Raw pointers that own an allocation is such a pervasive pattern that the standard library made a utility for itself called `Unique` which: * wraps a `*const T` for variance -* includes a `PhantomData`, +* includes a `PhantomData` * auto-derives Send/Sync as if T was contained * marks the pointer as NonZero for the null-pointer optimization From 29ca456dfce27667341f4f26eec2b4efd13ffa17 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Sat, 16 Jul 2016 11:13:29 +0200 Subject: [PATCH 42/53] doc: remove extraneous word --- src/doc/nomicon/phantom-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/nomicon/phantom-data.md b/src/doc/nomicon/phantom-data.md index 0d7ec7f16179..4843cd420df1 100644 --- a/src/doc/nomicon/phantom-data.md +++ b/src/doc/nomicon/phantom-data.md @@ -51,7 +51,7 @@ struct Vec { ``` Unlike the previous example it *appears* that everything is exactly as we -want. Every generic argument to Vec shows up in the at least one field. +want. Every generic argument to Vec shows up in at least one field. Good to go! Nope. From dd72785af537c30aaf70f49cf7abdb9700085997 Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Sat, 16 Jul 2016 22:19:43 +0200 Subject: [PATCH 43/53] Revert "Refactored code to access TLS only in case of panic" --- src/libstd/panicking.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index a7de9d3a0c5d..d73e9542d212 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -232,9 +232,10 @@ fn default_hook(info: &PanicInfo) { pub unsafe fn try R>(f: F) -> Result> { let mut slot = None; let mut f = Some(f); - let ret; + let ret = PANIC_COUNT.with(|s| { + let prev = s.get(); + s.set(0); - { let mut to_run = || { slot = Some(f.take().unwrap()()); }; @@ -247,21 +248,18 @@ pub unsafe fn try R>(f: F) -> Result> { dataptr, &mut any_data, &mut any_vtable); + s.set(prev); + if r == 0 { - ret = Ok(()); + Ok(()) } else { - PANIC_COUNT.with(|s| { - let prev = s.get(); - s.set(prev - 1); - }); - ret = Err(mem::transmute(raw::TraitObject { + Err(mem::transmute(raw::TraitObject { data: any_data as *mut _, vtable: any_vtable as *mut _, - })); + })) } - } + }); - debug_assert!(PANIC_COUNT.with(|c| c.get() == 0)); return ret.map(|()| { slot.take().unwrap() }); From f66da5e7942954730fe19f8bba957759fa13984b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 17 Jul 2016 00:15:15 +0300 Subject: [PATCH 44/53] Do not resolve inherent static methods from other crates prematurely --- src/librustc_metadata/decoder.rs | 26 ---------------------- src/test/compile-fail/use-from-trait-xc.rs | 2 +- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 3f5c9a6d3bd8..409cec282bce 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -687,32 +687,6 @@ fn each_child_of_item_or_crate(cdata: Cmd, } } - // As a special case, iterate over all static methods of - // associated implementations too. This is a bit of a botch. - // --pcwalton - for inherent_impl_def_id_doc in reader::tagged_docs(item_doc, - tag_items_data_item_inherent_impl) { - let inherent_impl_def_id = item_def_id(inherent_impl_def_id_doc, cdata); - if let Some(inherent_impl_doc) = cdata.get_item(inherent_impl_def_id.index) { - for impl_item_def_id_doc in reader::tagged_docs(inherent_impl_doc, - tag_item_impl_item) { - let impl_item_def_id = item_def_id(impl_item_def_id_doc, - cdata); - if let Some(impl_method_doc) = cdata.get_item(impl_item_def_id.index) { - if let StaticMethod = item_family(impl_method_doc) { - // Hand off the static method to the callback. - let static_method_name = item_name(impl_method_doc); - let static_method_def_like = item_to_def_like(cdata, impl_method_doc, - impl_item_def_id); - callback(static_method_def_like, - static_method_name, - item_visibility(impl_method_doc)); - } - } - } - } - } - for reexport_doc in reexports(item_doc) { let def_id_doc = reader::get_doc(reexport_doc, tag_items_data_item_reexport_def_id); diff --git a/src/test/compile-fail/use-from-trait-xc.rs b/src/test/compile-fail/use-from-trait-xc.rs index e6c9b1b41c04..687cdba1542b 100644 --- a/src/test/compile-fail/use-from-trait-xc.rs +++ b/src/test/compile-fail/use-from-trait-xc.rs @@ -31,6 +31,6 @@ use use_from_trait_xc::Bar::new as bnew; //~^ ERROR unresolved import `use_from_trait_xc::Bar::new` use use_from_trait_xc::Baz::new as baznew; -//~^ ERROR `baznew` is not directly importable +//~^ ERROR unresolved import `use_from_trait_xc::Baz::new` fn main() {} From f80165dfa2253bb8da054296391c8bfe5012b69f Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sat, 16 Jul 2016 22:09:55 -0400 Subject: [PATCH 45/53] Remove unnecessary indexing and deref in `Vec::as_mut_slice`. --- src/libcollections/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index da56b21cf0c0..7a3c9bc3bb21 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -520,7 +520,7 @@ impl Vec { #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { - &mut self[..] + self } /// Sets the length of a vector. From 9c5039a128ba2fa69eb979c314f158aa680ff6a2 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 17 Jul 2016 00:15:15 +0300 Subject: [PATCH 46/53] Implement traits for variadic function pointers --- src/libcore/ptr.rs | 13 +++++++++++-- src/libcoretest/ptr.rs | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 8b3a14b24df2..925cdfec900d 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -571,12 +571,21 @@ macro_rules! fnptr_impls_safety_abi { } macro_rules! fnptr_impls_args { - ($($Arg: ident),*) => { + ($($Arg: ident),+) => { fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* } fnptr_impls_safety_abi! { extern "C" fn($($Arg),*) -> Ret, $($Arg),* } + fnptr_impls_safety_abi! { extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* } fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* } fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),*) -> Ret, $($Arg),* } - } + fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* } + }; + () => { + // No variadic functions with 0 parameters + fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { extern "C" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, } + fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, } + }; } fnptr_impls_args! { } diff --git a/src/libcoretest/ptr.rs b/src/libcoretest/ptr.rs index 343db93d4a97..e0a9f4e5d422 100644 --- a/src/libcoretest/ptr.rs +++ b/src/libcoretest/ptr.rs @@ -171,3 +171,17 @@ fn test_unsized_unique() { let zs: &mut [i32] = &mut [1, 2, 3]; assert!(ys == zs); } + +#[test] +fn test_variadic_fnptr() { + use core::hash::{Hash, SipHasher}; + extern "C" { + fn printf(_: *const u8, ...); + } + let p: unsafe extern "C" fn(*const u8, ...) = printf; + let q = p.clone(); + assert_eq!(p, q); + assert!(!(p < q)); + let mut s = SipHasher::new(); + assert_eq!(p.hash(&mut s), q.hash(&mut s)); +} From 27a18b127f4f02dbbdf12eec03b817ed3c3f0f68 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 16 Jul 2016 20:41:43 +0000 Subject: [PATCH 47/53] macros: Fix bug in statement matchers --- src/libsyntax/parse/parser.rs | 80 ++++++++++++++--------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4656ba03e21c..125f1abb062b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3789,13 +3789,8 @@ impl<'a> Parser<'a> { /// Parse a statement. This stops just before trailing semicolons on everything but items. /// e.g. a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. - /// - /// Also, if a macro begins an expression statement, this only parses the macro. For example, - /// ```rust - /// vec![1].into_iter(); //< `parse_stmt` only parses the "vec![1]" - /// ``` pub fn parse_stmt(&mut self) -> PResult<'a, Option> { - Ok(self.parse_stmt_()) + Ok(self.parse_stmt_(true)) } // Eat tokens until we can be relatively sure we reached the end of the @@ -3859,15 +3854,15 @@ impl<'a> Parser<'a> { } } - fn parse_stmt_(&mut self) -> Option { - self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { + fn parse_stmt_(&mut self, macro_expanded: bool) -> Option { + self.parse_stmt_without_recovery(macro_expanded).unwrap_or_else(|mut e| { e.emit(); self.recover_stmt_(SemiColonMode::Break); None }) } - fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option> { + fn parse_stmt_without_recovery(&mut self, macro_expanded: bool) -> PResult<'a, Option> { maybe_whole!(Some deref self, NtStmt); let attrs = self.parse_outer_attributes()?; @@ -3930,10 +3925,34 @@ impl<'a> Parser<'a> { if id.name == keywords::Invalid.name() { let mac = spanned(lo, hi, Mac_ { path: pth, tts: tts }); + let node = if delim == token::Brace || + self.token == token::Semi || self.token == token::Eof { + StmtKind::Mac(P((mac, style, attrs.into()))) + } + // We used to incorrectly stop parsing macro-expanded statements here. + // If the next token will be an error anyway but could have parsed with the + // earlier behavior, stop parsing here and emit a warning to avoid breakage. + else if macro_expanded && self.token.can_begin_expr() && match self.token { + // These can continue an expression, so we can't stop parsing and warn. + token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) | + token::BinOp(token::Minus) | token::BinOp(token::Star) | + token::BinOp(token::And) | token::BinOp(token::Or) | + token::AndAnd | token::OrOr | + token::DotDot | token::DotDotDot => false, + _ => true, + } { + self.warn_missing_semicolon(); + StmtKind::Mac(P((mac, style, attrs.into()))) + } else { + let e = self.mk_mac_expr(lo, hi, mac.node, ThinVec::new()); + let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?; + let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; + StmtKind::Expr(e) + }; Stmt { id: ast::DUMMY_NODE_ID, - node: StmtKind::Mac(P((mac, style, attrs.into()))), span: mk_sp(lo, hi), + node: node, } } else { // if it has a special ident, it's definitely an item @@ -4061,49 +4080,12 @@ impl<'a> Parser<'a> { } /// Parse a statement, including the trailing semicolon. - /// This parses expression statements that begin with macros correctly (c.f. `parse_stmt`). pub fn parse_full_stmt(&mut self, macro_expanded: bool) -> PResult<'a, Option> { - let mut stmt = match self.parse_stmt_() { + let mut stmt = match self.parse_stmt_(macro_expanded) { Some(stmt) => stmt, None => return Ok(None), }; - if let StmtKind::Mac(mac) = stmt.node { - if mac.1 != MacStmtStyle::NoBraces || - self.token == token::Semi || self.token == token::Eof { - stmt.node = StmtKind::Mac(mac); - } else { - // We used to incorrectly stop parsing macro-expanded statements here. - // If the next token will be an error anyway but could have parsed with the - // earlier behavior, stop parsing here and emit a warning to avoid breakage. - if macro_expanded && self.token.can_begin_expr() && match self.token { - // These tokens can continue an expression, so we can't stop parsing and warn. - token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) | - token::BinOp(token::Minus) | token::BinOp(token::Star) | - token::BinOp(token::And) | token::BinOp(token::Or) | - token::AndAnd | token::OrOr | - token::DotDot | token::DotDotDot => false, - _ => true, - } { - self.warn_missing_semicolon(); - stmt.node = StmtKind::Mac(mac); - return Ok(Some(stmt)); - } - - let (mac, _style, attrs) = mac.unwrap(); - let e = self.mk_mac_expr(stmt.span.lo, stmt.span.hi, mac.node, ThinVec::new()); - let e = self.parse_dot_or_call_expr_with(e, stmt.span.lo, attrs)?; - let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; - stmt.node = StmtKind::Expr(e); - } - } - - stmt = self.handle_trailing_semicolon(stmt, macro_expanded)?; - Ok(Some(stmt)) - } - - fn handle_trailing_semicolon(&mut self, mut stmt: Stmt, macro_expanded: bool) - -> PResult<'a, Stmt> { match stmt.node { StmtKind::Expr(ref expr) if self.token != token::Eof => { // expression without semicolon @@ -4133,7 +4115,7 @@ impl<'a> Parser<'a> { } stmt.span.hi = self.last_span.hi; - Ok(stmt) + Ok(Some(stmt)) } fn warn_missing_semicolon(&self) { From bd1ad762b7ab068ac2e3e1bfc413e80aac2dc7fd Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 17 Jul 2016 15:46:21 +0000 Subject: [PATCH 48/53] Add regression test --- src/test/compile-fail/macro-stmt-matchers.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/compile-fail/macro-stmt-matchers.rs diff --git a/src/test/compile-fail/macro-stmt-matchers.rs b/src/test/compile-fail/macro-stmt-matchers.rs new file mode 100644 index 000000000000..8f46d3301eb3 --- /dev/null +++ b/src/test/compile-fail/macro-stmt-matchers.rs @@ -0,0 +1,17 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#[rustc_error] +fn main() { //~ ERROR compilation successful + macro_rules! m { ($s:stmt;) => { $s } } + m!(vec![].push(0);); +} From 76ed4456224f6bf7dc35dc72cc1c2cc93186285a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 22 Jun 2016 08:03:42 +0000 Subject: [PATCH 49/53] Clean up and encapsulate `syntax::ext::mtwt` --- src/librustc_driver/driver.rs | 7 +- src/librustc_driver/pretty.rs | 2 +- src/librustc_resolve/assign_ids.rs | 6 +- src/librustc_resolve/lib.rs | 20 ++-- src/libsyntax/ast.rs | 22 +--- src/libsyntax/ext/expand.rs | 42 +++----- src/libsyntax/ext/mtwt.rs | 157 ++++++++++++++--------------- src/libsyntax/parse/token.rs | 5 - 8 files changed, 110 insertions(+), 151 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 3b1124a911e5..6b141ca15c05 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -479,9 +479,8 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { // These may be left in an incoherent state after a previous compile. - // `clear_tables` and `clear_ident_interner` can be used to free - // memory, but they do not restore the initial state. - syntax::ext::mtwt::reset_tables(); + syntax::ext::mtwt::reset_hygiene_data(); + // `clear_ident_interner` can be used to free memory, but it does not restore the initial state. token::reset_ident_interner(); let continue_after_error = sess.opts.continue_parse_after_error; sess.diagnostic().set_continue_after_error(continue_after_error); @@ -763,7 +762,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, // Discard MTWT tables that aren't required past lowering to HIR. if !keep_mtwt_tables(sess) { - syntax::ext::mtwt::clear_tables(); + syntax::ext::mtwt::reset_hygiene_data(); } Ok(ExpansionResult { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index baac455a25f3..14476cc997ff 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -456,7 +456,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { pp::space(&mut s.s)?; // FIXME #16420: this doesn't display the connections // between syntax contexts - s.synth_comment(format!("{}#{}", nm, ctxt.0)) + s.synth_comment(format!("{}{:?}", nm, ctxt)) } pprust::NodeName(&ast::Name(nm)) => { pp::space(&mut s.s)?; diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs index d4465822229e..77facbfb6179 100644 --- a/src/librustc_resolve/assign_ids.rs +++ b/src/librustc_resolve/assign_ids.rs @@ -11,7 +11,7 @@ use Resolver; use rustc::session::Session; use syntax::ast; -use syntax::ext::mtwt; +use syntax::ext::mtwt::Mark; use syntax::fold::{self, Folder}; use syntax::ptr::P; use syntax::util::move_map::MoveMap; @@ -31,7 +31,7 @@ impl<'a> Resolver<'a> { struct NodeIdAssigner<'a> { sess: &'a Session, - macros_at_scope: &'a mut HashMap>, + macros_at_scope: &'a mut HashMap>, } impl<'a> Folder for NodeIdAssigner<'a> { @@ -49,7 +49,7 @@ impl<'a> Folder for NodeIdAssigner<'a> { block.stmts = block.stmts.move_flat_map(|stmt| { if let ast::StmtKind::Item(ref item) = stmt.node { if let ast::ItemKind::Mac(..) = item.node { - macros.push(mtwt::outer_mark(item.ident.ctxt)); + macros.push(item.ident.ctxt.data().outer_mark); return None; } } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2535c264ef8f..36cd2ef6002a 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -53,7 +53,7 @@ use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace}; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; -use syntax::ext::mtwt; +use syntax::ext::mtwt::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; use syntax::parse::token::{self, keywords}; @@ -654,7 +654,7 @@ enum RibKind<'a> { ModuleRibKind(Module<'a>), // We passed through a `macro_rules!` statement with the given expansion - MacroDefinition(ast::Mrk), + MacroDefinition(Mark), } #[derive(Copy, Clone)] @@ -933,7 +933,7 @@ pub struct Resolver<'a> { // Maps the node id of a statement to the expansions of the `macro_rules!`s // immediately above the statement (if appropriate). - macros_at_scope: HashMap>, + macros_at_scope: HashMap>, graph_root: Module<'a>, @@ -1434,10 +1434,9 @@ impl<'a> Resolver<'a> { if let MacroDefinition(mac) = self.get_ribs(ns)[i].kind { // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. - if let Some((source_ident, source_macro)) = mtwt::source(ident) { - if mac == source_macro { - ident = source_ident; - } + let (source_ctxt, source_macro) = ident.ctxt.source(); + if source_macro == mac { + ident.ctxt = source_ctxt; } } } @@ -1585,10 +1584,9 @@ impl<'a> Resolver<'a> { MacroDefinition(mac) => { // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. - if let Some((source_ident, source_macro)) = mtwt::source(ident) { - if mac == source_macro { - ident = source_ident; - } + let (source_ctxt, source_macro) = ident.ctxt.source(); + if source_macro == mac { + ident.ctxt = source_ctxt; } } _ => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6b662c6779a4..1f716923a16e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -19,6 +19,7 @@ pub use util::ThinVec; use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId}; use codemap::{respan, Spanned}; use abi::Abi; +use ext::mtwt::SyntaxContext; use parse::token::{self, keywords, InternedString}; use print::pprust; use ptr::P; @@ -33,15 +34,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Name(pub u32); -/// A SyntaxContext represents a chain of macro-expandings -/// and renamings. Each macro expansion corresponds to -/// a fresh u32. This u32 is a reference to a table stored -/// in thread-local storage. -/// The special value EMPTY_CTXT is used to indicate an empty -/// syntax context. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -pub struct SyntaxContext(pub u32); - /// An identifier contains a Name (index into the interner /// table) and a SyntaxContext to track renaming and /// macro expansion per Flatt et al., "Macros That Work Together" @@ -81,20 +73,15 @@ impl Decodable for Name { } } -pub const EMPTY_CTXT : SyntaxContext = SyntaxContext(0); - impl Ident { - pub fn new(name: Name, ctxt: SyntaxContext) -> Ident { - Ident {name: name, ctxt: ctxt} - } pub const fn with_empty_ctxt(name: Name) -> Ident { - Ident {name: name, ctxt: EMPTY_CTXT} + Ident { name: name, ctxt: SyntaxContext::empty() } } } impl fmt::Debug for Ident { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}#{}", self.name, self.ctxt.0) + write!(f, "{}{:?}", self.name, self.ctxt) } } @@ -116,9 +103,6 @@ impl Decodable for Ident { } } -/// A mark represents a unique id associated with a macro expansion -pub type Mrk = u32; - #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] pub struct Lifetime { pub id: NodeId, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3e9837a6995c..767d1ddb8e24 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -9,20 +9,19 @@ // except according to those terms. use ast::{Block, Crate, Ident, Mac_, Name, PatKind}; -use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind}; +use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind}; use ast; -use attr::HasAttrs; -use ext::mtwt; -use attr; +use ext::mtwt::Mark; +use attr::{self, HasAttrs}; use attr::AttrMetaMethods; -use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; +use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use syntax_pos::{self, Span, ExpnId}; use config::StripUnconfigured; use ext::base::*; use feature_gate::{self, Features}; use fold; use fold::*; -use parse::token::{fresh_mark, intern, keywords}; +use parse::token::{intern, keywords}; use ptr::P; use tokenstream::TokenTree; use util::small_vector::SmallVector; @@ -130,9 +129,9 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec(path: &ast::Path, ident: Option, tts: Vec, mark: Mrk, + fn mac_result<'a>(path: &ast::Path, ident: Option, tts: Vec, mark: Mark, attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) -> Option> { // Detect use of feature-gated or invalid attributes on macro invoations @@ -708,30 +707,17 @@ pub fn expand_crate(mut cx: ExtCtxt, return (ret, cx.syntax_env.names); } -// HYGIENIC CONTEXT EXTENSION: -// all of these functions are for walking over -// ASTs and making some change to the context of every -// element that has one. a CtxtFn is a trait-ified -// version of a closure in (SyntaxContext -> SyntaxContext). -// the ones defined here include: -// Marker - add a mark to a context - // A Marker adds the given mark to the syntax context and // sets spans' `expn_id` to the given expn_id (unless it is `None`). -struct Marker { mark: Mrk, expn_id: Option } +struct Marker { mark: Mark, expn_id: Option } impl Folder for Marker { - fn fold_ident(&mut self, id: Ident) -> Ident { - ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt)) + fn fold_ident(&mut self, mut ident: Ident) -> Ident { + ident.ctxt = ident.ctxt.apply_mark(self.mark); + ident } - fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac { - Spanned { - node: Mac_ { - path: self.fold_path(node.path), - tts: self.fold_tts(&node.tts), - }, - span: self.new_span(span), - } + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + noop_fold_mac(mac, self) } fn new_span(&mut self, mut span: Span) -> Span { @@ -743,7 +729,7 @@ impl Folder for Marker { } // apply a given mark to the given token trees. Used prior to expansion of a macro. -fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec { +fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None}) } diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index d2f6df9d5dbd..c4af4a1f85b9 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -15,107 +15,104 @@ //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. //! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093 -pub use self::SyntaxContext_::*; - -use ast::{Ident, Mrk, SyntaxContext}; - use std::cell::RefCell; use std::collections::HashMap; +use std::fmt; -/// The SCTable contains a table of SyntaxContext_'s. It -/// represents a flattened tree structure, to avoid having -/// managed pointers everywhere (that caused an ICE). -/// The `marks` ensures that adding the same mark to the -/// same context gives you back the same context as before. -pub struct SCTable { - table: RefCell>, - marks: RefCell>, +/// A SyntaxContext represents a chain of macro expansions (represented by marks). +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Default)] +pub struct SyntaxContext(u32); + +#[derive(Copy, Clone)] +pub struct SyntaxContextData { + pub outer_mark: Mark, + pub prev_ctxt: SyntaxContext, } -#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)] -pub enum SyntaxContext_ { - EmptyCtxt, - Mark (Mrk,SyntaxContext), -} +/// A mark represents a unique id associated with a macro expansion. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct Mark(u32); -/// Extend a syntax context with a given mark -pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext { - with_sctable(|table| apply_mark_internal(m, ctxt, table)) -} - -/// Extend a syntax context with a given mark and sctable (explicit memoization) -fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext { - let ctxts = &mut *table.table.borrow_mut(); - match ctxts[ctxt.0 as usize] { - // Applying the same mark twice is a no-op. - Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt, - _ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| { - SyntaxContext(idx_push(ctxts, Mark(m, ctxt))) - }), +impl Mark { + pub fn fresh() -> Self { + HygieneData::with(|data| { + let next_mark = Mark(data.next_mark.0 + 1); + ::std::mem::replace(&mut data.next_mark, next_mark) + }) } } -/// Fetch the SCTable from TLS, create one if it doesn't yet exist. -pub fn with_sctable(op: F) -> T where - F: FnOnce(&SCTable) -> T, -{ - thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal()); - SCTABLE_KEY.with(move |slot| op(slot)) +struct HygieneData { + syntax_contexts: Vec, + markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, + next_mark: Mark, } -// Make a fresh syntax context table with EmptyCtxt in slot zero. -fn new_sctable_internal() -> SCTable { - SCTable { - table: RefCell::new(vec![EmptyCtxt]), - marks: RefCell::new(HashMap::new()), +impl HygieneData { + fn new() -> Self { + HygieneData { + syntax_contexts: vec![SyntaxContextData { + outer_mark: Mark(0), // the null mark + prev_ctxt: SyntaxContext(0), // the empty context + }], + markings: HashMap::new(), + next_mark: Mark(1), + } + } + + fn with T>(f: F) -> T { + thread_local! { + static HYGIENE_DATA: RefCell = RefCell::new(HygieneData::new()); + } + HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut())) } } -/// Clear the tables from TLD to reclaim memory. -pub fn clear_tables() { - with_sctable(|table| { - *table.table.borrow_mut() = Vec::new(); - *table.marks.borrow_mut() = HashMap::new(); - }); +pub fn reset_hygiene_data() { + HygieneData::with(|data| *data = HygieneData::new()) } -/// Reset the tables to their initial state -pub fn reset_tables() { - with_sctable(|table| { - *table.table.borrow_mut() = vec![EmptyCtxt]; - *table.marks.borrow_mut() = HashMap::new(); - }); -} +impl SyntaxContext { + pub const fn empty() -> Self { + SyntaxContext(0) + } -/// Add a value to the end of a vec, return its index -fn idx_push(vec: &mut Vec, val: T) -> u32 { - vec.push(val); - (vec.len() - 1) as u32 -} + pub fn data(self) -> SyntaxContextData { + HygieneData::with(|data| data.syntax_contexts[self.0 as usize]) + } -/// Return the outer mark for a context with a mark at the outside. -/// FAILS when outside is not a mark. -pub fn outer_mark(ctxt: SyntaxContext) -> Mrk { - with_sctable(|sctable| { - match (*sctable.table.borrow())[ctxt.0 as usize] { - Mark(mrk, _) => mrk, - _ => panic!("can't retrieve outer mark when outside is not a mark") + /// Extend a syntax context with a given mark + pub fn apply_mark(self, mark: Mark) -> SyntaxContext { + // Applying the same mark twice is a no-op + let ctxt_data = self.data(); + if mark == ctxt_data.outer_mark { + return ctxt_data.prev_ctxt; } - }) + + HygieneData::with(|data| { + let syntax_contexts = &mut data.syntax_contexts; + *data.markings.entry((self, mark)).or_insert_with(|| { + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + prev_ctxt: self, + }); + SyntaxContext(syntax_contexts.len() as u32 - 1) + }) + }) + } + + /// If `ident` is macro expanded, return the source ident from the macro definition + /// and the mark of the expansion that created the macro definition. + pub fn source(self) -> (Self /* source context */, Mark /* source macro */) { + let macro_def_ctxt = self.data().prev_ctxt.data(); + (macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark) + } } -/// If `ident` is macro expanded, return the source ident from the macro definition -/// and the mark of the expansion that created the macro definition. -pub fn source(ident: Ident) -> Option<(Ident /* source ident */, Mrk /* source macro */)> { - with_sctable(|sctable| { - let ctxts = sctable.table.borrow(); - if let Mark(_expansion_mark, macro_ctxt) = ctxts[ident.ctxt.0 as usize] { - if let Mark(definition_mark, orig_ctxt) = ctxts[macro_ctxt.0 as usize] { - return Some((Ident::new(ident.name, orig_ctxt), definition_mark)); - } - } - None - }) +impl fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{}", self.0) + } } #[cfg(test)] diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index fe9d3ef7c234..f0a6f8edeec7 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -633,8 +633,3 @@ pub fn fresh_name(src: ast::Ident) -> ast::Name { /*let num = rand::thread_rng().gen_uint_range(0,0xffff); gensym(format!("{}_{}",ident_to_string(src),num))*/ } - -// create a fresh mark. -pub fn fresh_mark() -> ast::Mrk { - gensym("mark").0 -} From 44575f708aed13f4260c6ce27b35ab102e5dd9f2 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 16 Jul 2016 19:11:28 +0000 Subject: [PATCH 50/53] Rename `mtwt` to `hygiene` --- src/librustc/session/config.rs | 4 ++-- src/librustc_driver/driver.rs | 12 ++++++------ src/librustc_resolve/assign_ids.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/ext/{mtwt.rs => hygiene.rs} | 2 +- src/libsyntax/lib.rs | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) rename src/libsyntax/ext/{mtwt.rs => hygiene.rs} (98%) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index bc271e9ecc8e..65b97abfccbd 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -735,8 +735,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "for every macro invocation, print its name and arguments"), enable_nonzeroing_move_hints: bool = (false, parse_bool, "force nonzeroing move optimization on"), - keep_mtwt_tables: bool = (false, parse_bool, - "don't clear the resolution tables after analysis"), + keep_hygiene_data: bool = (false, parse_bool, + "don't clear the hygiene data after analysis"), keep_ast: bool = (false, parse_bool, "keep the AST after lowering it to HIR"), show_span: Option = (None, parse_opt_string, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6b141ca15c05..ab3b20e08c80 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -236,8 +236,8 @@ pub fn compile_input(sess: &Session, Ok(()) } -fn keep_mtwt_tables(sess: &Session) -> bool { - sess.opts.debugging_opts.keep_mtwt_tables +fn keep_hygiene_data(sess: &Session) -> bool { + sess.opts.debugging_opts.keep_hygiene_data } fn keep_ast(sess: &Session) -> bool { @@ -479,7 +479,7 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { // These may be left in an incoherent state after a previous compile. - syntax::ext::mtwt::reset_hygiene_data(); + syntax::ext::hygiene::reset_hygiene_data(); // `clear_ident_interner` can be used to free memory, but it does not restore the initial state. token::reset_ident_interner(); let continue_after_error = sess.opts.continue_parse_after_error; @@ -760,9 +760,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, hir_map::Forest::new(lower_crate(sess, &krate, &mut resolver), &sess.dep_graph) }); - // Discard MTWT tables that aren't required past lowering to HIR. - if !keep_mtwt_tables(sess) { - syntax::ext::mtwt::reset_hygiene_data(); + // Discard hygiene data, which isn't required past lowering to HIR. + if !keep_hygiene_data(sess) { + syntax::ext::hygiene::reset_hygiene_data(); } Ok(ExpansionResult { diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs index 77facbfb6179..70e566de8a7b 100644 --- a/src/librustc_resolve/assign_ids.rs +++ b/src/librustc_resolve/assign_ids.rs @@ -11,7 +11,7 @@ use Resolver; use rustc::session::Session; use syntax::ast; -use syntax::ext::mtwt::Mark; +use syntax::ext::hygiene::Mark; use syntax::fold::{self, Folder}; use syntax::ptr::P; use syntax::util::move_map::MoveMap; diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 36cd2ef6002a..aa8c706ea1e2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -53,7 +53,7 @@ use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace}; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; -use syntax::ext::mtwt::Mark; +use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; use syntax::parse::token::{self, keywords}; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 1f716923a16e..a8bb255fba4a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -19,7 +19,7 @@ pub use util::ThinVec; use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId}; use codemap::{respan, Spanned}; use abi::Abi; -use ext::mtwt::SyntaxContext; +use ext::hygiene::SyntaxContext; use parse::token::{self, keywords, InternedString}; use print::pprust; use ptr::P; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 767d1ddb8e24..18342f2e38c1 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -11,7 +11,7 @@ use ast::{Block, Crate, Ident, Mac_, Name, PatKind}; use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind}; use ast; -use ext::mtwt::Mark; +use ext::hygiene::Mark; use attr::{self, HasAttrs}; use attr::AttrMetaMethods; use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/hygiene.rs similarity index 98% rename from src/libsyntax/ext/mtwt.rs rename to src/libsyntax/ext/hygiene.rs index c4af4a1f85b9..521930f69a93 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Machinery for hygienic macros, as described in the MTWT[1] paper. +//! Machinery for hygienic macros, inspired by the MTWT[1] paper. //! //! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler. //! 2012. *Macros that work together: Compile-time bindings, partial expansion, diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 8febf1c49ec2..5ad174441889 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -127,7 +127,7 @@ pub mod ext { pub mod base; pub mod build; pub mod expand; - pub mod mtwt; + pub mod hygiene; pub mod quote; pub mod source_util; From 275d321ab099b4d7b0de71aa72a87eb27de120af Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 16 Jul 2016 19:26:16 +0000 Subject: [PATCH 51/53] Remove some unit tests and that are redundant with `run-pass/hygiene.rs` and that would be painful to rewrite. --- src/libsyntax/ext/hygiene.rs | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index 521930f69a93..ade165e0ef9c 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -114,38 +114,3 @@ impl fmt::Debug for SyntaxContext { write!(f, "#{}", self.0) } } - -#[cfg(test)] -mod tests { - use ast::{EMPTY_CTXT, Mrk, SyntaxContext}; - use super::{apply_mark_internal, new_sctable_internal, Mark, SCTable}; - - // extend a syntax context with a sequence of marks given - // in a vector. v[0] will be the outermost mark. - fn unfold_marks(mrks: Vec , tail: SyntaxContext, table: &SCTable) - -> SyntaxContext { - mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk| - {apply_mark_internal(*mrk,tail,table)}) - } - - #[test] fn unfold_marks_test() { - let mut t = new_sctable_internal(); - - assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),SyntaxContext(2)); - { - let table = t.table.borrow(); - assert!((*table)[1] == Mark(7,EMPTY_CTXT)); - assert!((*table)[2] == Mark(3,SyntaxContext(1))); - } - } - - #[test] - fn hashing_tests () { - let mut t = new_sctable_internal(); - assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1)); - assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),SyntaxContext(2)); - // using the same one again should result in the same index: - assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1)); - // I'm assuming that the rename table will behave the same.... - } -} From 22f77a917147f232d7fa96522ae539fe5b00d7f6 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 18 Jul 2016 10:16:19 -0400 Subject: [PATCH 52/53] Run base::internalize_symbols() even for single-codegen-unit crates. The initial linkage-assignment (especially for closures) is a conservative one that makes some symbols more visible than they need to be. While this is not a correctness problem, it does force the LLVM inliner to be more conservative too, which results in poor performance. Once translation is based solely on MIR, it will be easier to also make the initial linkage assignment a better fitting one. Until then `internalize_symbols()` does a good job of preventing most performance regressions. --- src/librustc_trans/base.rs | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 70d142e50edb..c8b9fea15ba8 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -2270,12 +2270,9 @@ fn internalize_symbols(cx: &CrateContextList, reachable: &HashSet<&str>) { let is_decl = llvm::LLVMIsDeclaration(val) != 0; if is_decl || is_available_externally { - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)) - .to_bytes() - .to_vec(); + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); declared.insert(name); } - } } @@ -2286,21 +2283,21 @@ fn internalize_symbols(cx: &CrateContextList, reachable: &HashSet<&str>) { for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) { let linkage = llvm::LLVMGetLinkage(val); - let is_external = linkage == llvm::ExternalLinkage as c_uint; - let is_weak_odr = linkage == llvm::WeakODRLinkage as c_uint; - let is_decl = llvm::LLVMIsDeclaration(val) != 0; + let is_externally_visible = (linkage == llvm::ExternalLinkage as c_uint) || + (linkage == llvm::LinkOnceODRLinkage as c_uint) || + (linkage == llvm::WeakODRLinkage as c_uint); + let is_definition = llvm::LLVMIsDeclaration(val) != 0; - // We only care about external definitions. - if (is_external || is_weak_odr) && !is_decl { + // If this is a definition (as opposed to just a declaration) + // and externally visible, check if we can internalize it + if is_definition && is_externally_visible { + let name_cstr = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let name_str = name_cstr.to_str().unwrap(); - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)) - .to_bytes() - .to_vec(); + let is_referenced_somewhere = declared.contains(&name_cstr); + let is_reachable = reachable.contains(name_str); - let is_declared = declared.contains(&name); - let reachable = reachable.contains(str::from_utf8(&name).unwrap()); - - if !is_declared && !reachable { + if !is_referenced_somewhere && !is_reachable { llvm::SetLinkage(val, llvm::InternalLinkage); llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass); llvm::UnsetComdat(val); @@ -2488,7 +2485,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Run the translation item collector and partition the collected items into // codegen units. let (codegen_units, symbol_map) = collect_and_partition_translation_items(&shared_ccx); - let codegen_unit_count = codegen_units.len(); let symbol_map = Rc::new(symbol_map); @@ -2620,10 +2616,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, })); } - if codegen_unit_count > 1 { - internalize_symbols(&crate_context_list, - &reachable_symbols.iter().map(|x| &x[..]).collect()); - } + internalize_symbols(&crate_context_list, + &reachable_symbols.iter().map(|x| &x[..]).collect()); if sess.target.target.options.is_like_msvc && sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) { From 7b2a03f08e930f6eaac419e203630b06e3d491e6 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 18 Jul 2016 12:08:37 +0200 Subject: [PATCH 53/53] Fix doctest of `ExactSizeIterator::is_empty` --- src/libcore/iter/traits.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index b7ee9ba55f59..b1af30559328 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -530,7 +530,9 @@ pub trait ExactSizeIterator: Iterator { /// Basic usage: /// /// ``` - /// let mut one_element = [0].iter(); + /// #![feature(exact_size_is_empty)] + /// + /// let mut one_element = 0..1; /// assert!(!one_element.is_empty()); /// /// assert_eq!(one_element.next(), Some(0));