From 1add949ef0e424e3b144e33d55938c873ac5050e Mon Sep 17 00:00:00 2001 From: Paul Emmerich Date: Thu, 11 Jul 2019 22:23:00 +0200 Subject: [PATCH 01/11] libtest: add --show-output option this new flag enables printing the captured stdout of successful tests utilizing the already existing display_output test runner option --- src/librustdoc/markdown.rs | 2 +- src/librustdoc/test.rs | 2 +- src/libtest/lib.rs | 16 +++++++++++----- src/libtest/tests.rs | 11 +++++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 50a647f244db5..21a7c39f72e92 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -154,6 +154,6 @@ pub fn test(mut options: Options, diag: &errors::Handler) -> i32 { options.test_args.insert(0, "rustdoctest".to_string()); testing::test_main(&options.test_args, collector.tests, - testing::Options::new().display_output(options.display_warnings)); + Some(testing::Options::new().display_output(options.display_warnings))); 0 } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 63545ab45bf64..562d98a4a4327 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -120,7 +120,7 @@ pub fn run(options: Options) -> i32 { testing::test_main( &test_args, tests, - testing::Options::new().display_output(display_warnings) + Some(testing::Options::new().display_output(display_warnings)) ); 0 diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 53bf67bdf671f..a1ce45dad4e9e 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -272,7 +272,7 @@ impl Options { // The default console test runner. It accepts the command line // arguments and a vector of test_descs. -pub fn test_main(args: &[String], tests: Vec, options: Options) { +pub fn test_main(args: &[String], tests: Vec, options: Option) { let mut opts = match parse_opts(args) { Some(Ok(o)) => o, Some(Err(msg)) => { @@ -281,8 +281,9 @@ pub fn test_main(args: &[String], tests: Vec, options: Options) { } None => return, }; - - opts.options = options; + if let Some(options) = options { + opts.options = options; + } if opts.list { if let Err(e) = list_tests_console(&opts, tests) { eprintln!("error: io error when listing tests: {:?}", e); @@ -323,7 +324,7 @@ pub fn test_main_static(tests: &[&TestDescAndFn]) { _ => panic!("non-static tests passed to test::test_main_static"), }) .collect(); - test_main(&args, owned_tests, Options::new()) + test_main(&args, owned_tests, None) } /// Invoked when unit tests terminate. Should panic if the unit @@ -468,6 +469,11 @@ fn optgroups() -> getopts::Options { json = Output a json document", "pretty|terse|json", ) + .optflag( + "", + "show-output", + "Show captured stdout of successful tests" + ) .optopt( "Z", "", @@ -667,7 +673,7 @@ pub fn parse_opts(args: &[String]) -> Option { format, test_threads, skip: matches.opt_strs("skip"), - options: Options::new(), + options: Options::new().display_output(matches.opt_present("show-output")), }; Some(Ok(test_opts)) diff --git a/src/libtest/tests.rs b/src/libtest/tests.rs index d8734d8caa03e..5956526e89403 100644 --- a/src/libtest/tests.rs +++ b/src/libtest/tests.rs @@ -160,6 +160,17 @@ fn parse_ignored_flag() { assert_eq!(opts.run_ignored, RunIgnored::Only); } +#[test] +fn parse_show_output_flag() { + let args = vec![ + "progname".to_string(), + "filter".to_string(), + "--show-output".to_string(), + ]; + let opts = parse_opts(&args).unwrap().unwrap(); + assert!(opts.options.display_output); +} + #[test] fn parse_include_ignored_flag() { let args = vec![ From 409a41dc24fe72e6d8bf3c2252efb13c94d921c5 Mon Sep 17 00:00:00 2001 From: Paul Emmerich Date: Fri, 12 Jul 2019 01:56:43 +0200 Subject: [PATCH 02/11] libtest: support display_output in JSON formatter --- src/libtest/formatters/json.rs | 71 +++++++++++-------- src/libtest/formatters/mod.rs | 1 + src/libtest/formatters/pretty.rs | 8 ++- src/libtest/formatters/terse.rs | 8 ++- src/libtest/lib.rs | 2 +- .../run-make-fulldeps/libtest-json/Makefile | 12 ++-- src/test/run-make-fulldeps/libtest-json/f.rs | 3 +- .../{output.json => output-default.json} | 2 +- .../libtest-json/output-stdout-success.json | 10 +++ 9 files changed, 77 insertions(+), 40 deletions(-) rename src/test/run-make-fulldeps/libtest-json/{output.json => output-default.json} (91%) create mode 100644 src/test/run-make-fulldeps/libtest-json/output-stdout-success.json diff --git a/src/libtest/formatters/json.rs b/src/libtest/formatters/json.rs index a06497f98626a..e0bea4ce54530 100644 --- a/src/libtest/formatters/json.rs +++ b/src/libtest/formatters/json.rs @@ -9,44 +9,57 @@ impl JsonFormatter { Self { out } } - fn write_message(&mut self, s: &str) -> io::Result<()> { + fn writeln_message(&mut self, s: &str) -> io::Result<()> { assert!(!s.contains('\n')); self.out.write_all(s.as_ref())?; self.out.write_all(b"\n") } + fn write_message(&mut self, s: &str) -> io::Result<()> { + assert!(!s.contains('\n')); + + self.out.write_all(s.as_ref()) + } + fn write_event( &mut self, ty: &str, name: &str, evt: &str, - extra: Option, + stdout: Option>, + extra: Option<&str>, ) -> io::Result<()> { - if let Some(extras) = extra { + self.write_message(&*format!( + r#"{{ "type": "{}", "name": "{}", "event": "{}""#, + ty, name, evt + ))?; + if let Some(stdout) = stdout { self.write_message(&*format!( - r#"{{ "type": "{}", "name": "{}", "event": "{}", {} }}"#, - ty, name, evt, extras - )) - } else { + r#", "stdout": "{}""#, + EscapedString(stdout) + ))?; + } + if let Some(extra) = extra { self.write_message(&*format!( - r#"{{ "type": "{}", "name": "{}", "event": "{}" }}"#, - ty, name, evt - )) + r#", {}"#, + extra + ))?; } + self.writeln_message(" }") } } impl OutputFormatter for JsonFormatter { fn write_run_start(&mut self, test_count: usize) -> io::Result<()> { - self.write_message(&*format!( + self.writeln_message(&*format!( r#"{{ "type": "suite", "event": "started", "test_count": {} }}"#, test_count )) } fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> { - self.write_message(&*format!( + self.writeln_message(&*format!( r#"{{ "type": "test", "event": "started", "name": "{}" }}"#, desc.name )) @@ -57,34 +70,30 @@ impl OutputFormatter for JsonFormatter { desc: &TestDesc, result: &TestResult, stdout: &[u8], + state: &ConsoleTestState, ) -> io::Result<()> { + let stdout = if (state.options.display_output || *result != TrOk) && stdout.len() > 0 { + Some(String::from_utf8_lossy(stdout)) + } else { + None + }; match *result { - TrOk => self.write_event("test", desc.name.as_slice(), "ok", None), - - TrFailed => { - let extra_data = if stdout.len() > 0 { - Some(format!( - r#""stdout": "{}""#, - EscapedString(String::from_utf8_lossy(stdout)) - )) - } else { - None - }; + TrOk => self.write_event("test", desc.name.as_slice(), "ok", stdout, None), - self.write_event("test", desc.name.as_slice(), "failed", extra_data) - } + TrFailed => self.write_event("test", desc.name.as_slice(), "failed", stdout, None), TrFailedMsg(ref m) => self.write_event( "test", desc.name.as_slice(), "failed", - Some(format!(r#""message": "{}""#, EscapedString(m))), + stdout, + Some(&*format!(r#""message": "{}""#, EscapedString(m))), ), - TrIgnored => self.write_event("test", desc.name.as_slice(), "ignored", None), + TrIgnored => self.write_event("test", desc.name.as_slice(), "ignored", stdout, None), TrAllowedFail => { - self.write_event("test", desc.name.as_slice(), "allowed_failure", None) + self.write_event("test", desc.name.as_slice(), "allowed_failure", stdout, None) } TrBench(ref bs) => { @@ -105,20 +114,20 @@ impl OutputFormatter for JsonFormatter { desc.name, median, deviation, mbps ); - self.write_message(&*line) + self.writeln_message(&*line) } } } fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> { - self.write_message(&*format!( + self.writeln_message(&*format!( r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"#, desc.name )) } fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result { - self.write_message(&*format!( + self.writeln_message(&*format!( "{{ \"type\": \"suite\", \ \"event\": \"{}\", \ \"passed\": {}, \ diff --git a/src/libtest/formatters/mod.rs b/src/libtest/formatters/mod.rs index be5f6a65039b7..cc30b06e5ec38 100644 --- a/src/libtest/formatters/mod.rs +++ b/src/libtest/formatters/mod.rs @@ -17,6 +17,7 @@ pub(crate) trait OutputFormatter { desc: &TestDesc, result: &TestResult, stdout: &[u8], + state: &ConsoleTestState, ) -> io::Result<()>; fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result; } diff --git a/src/libtest/formatters/pretty.rs b/src/libtest/formatters/pretty.rs index 4af00428ca87e..88331406a64d0 100644 --- a/src/libtest/formatters/pretty.rs +++ b/src/libtest/formatters/pretty.rs @@ -162,7 +162,13 @@ impl OutputFormatter for PrettyFormatter { Ok(()) } - fn write_result(&mut self, desc: &TestDesc, result: &TestResult, _: &[u8]) -> io::Result<()> { + fn write_result( + &mut self, + desc: &TestDesc, + result: &TestResult, + _: &[u8], + _: &ConsoleTestState, + ) -> io::Result<()> { if self.is_multithreaded { self.write_test_name(desc)?; } diff --git a/src/libtest/formatters/terse.rs b/src/libtest/formatters/terse.rs index 1400fba5d6092..d10b0c5807d57 100644 --- a/src/libtest/formatters/terse.rs +++ b/src/libtest/formatters/terse.rs @@ -170,7 +170,13 @@ impl OutputFormatter for TerseFormatter { Ok(()) } - fn write_result(&mut self, desc: &TestDesc, result: &TestResult, _: &[u8]) -> io::Result<()> { + fn write_result( + &mut self, + desc: &TestDesc, + result: &TestResult, + _: &[u8], + _: &ConsoleTestState, + ) -> io::Result<()> { match *result { TrOk => self.write_ok(), TrFailed | TrFailedMsg(_) => self.write_failed(), diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index a1ce45dad4e9e..6e00e02b3c809 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -906,7 +906,7 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu TeTimeout(ref test) => out.write_timeout(test), TeResult(test, result, stdout) => { st.write_log_result(&test, &result)?; - out.write_result(&test, &result, &*stdout)?; + out.write_result(&test, &result, &*stdout, &st)?; match result { TrOk => { st.passed += 1; diff --git a/src/test/run-make-fulldeps/libtest-json/Makefile b/src/test/run-make-fulldeps/libtest-json/Makefile index a0bc8cf6688bc..8339e230bbe92 100644 --- a/src/test/run-make-fulldeps/libtest-json/Makefile +++ b/src/test/run-make-fulldeps/libtest-json/Makefile @@ -2,13 +2,17 @@ # Test expected libtest's JSON output -OUTPUT_FILE := $(TMPDIR)/libtest-json-output.json +OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-json-output-default.json +OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-json-output-stdout-success.json all: $(RUSTC) --test f.rs - RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json > $(OUTPUT_FILE) || true + RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json > $(OUTPUT_FILE_DEFAULT) || true + RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true - cat $(OUTPUT_FILE) | "$(PYTHON)" validate_json.py + cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_json.py + cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_json.py # Compare to output file - diff output.json $(OUTPUT_FILE) + diff output-default.json $(OUTPUT_FILE_DEFAULT) + diff output-stdout-success.json $(OUTPUT_FILE_STDOUT_SUCCESS) diff --git a/src/test/run-make-fulldeps/libtest-json/f.rs b/src/test/run-make-fulldeps/libtest-json/f.rs index f5e44c2c24407..95ff36bd764ec 100644 --- a/src/test/run-make-fulldeps/libtest-json/f.rs +++ b/src/test/run-make-fulldeps/libtest-json/f.rs @@ -1,11 +1,12 @@ #[test] fn a() { + println!("print from successful test"); // Should pass } #[test] fn b() { - assert!(false) + assert!(false); } #[test] diff --git a/src/test/run-make-fulldeps/libtest-json/output.json b/src/test/run-make-fulldeps/libtest-json/output-default.json similarity index 91% rename from src/test/run-make-fulldeps/libtest-json/output.json rename to src/test/run-make-fulldeps/libtest-json/output-default.json index 0caf268aa007f..8046d72221703 100644 --- a/src/test/run-make-fulldeps/libtest-json/output.json +++ b/src/test/run-make-fulldeps/libtest-json/output-default.json @@ -2,7 +2,7 @@ { "type": "test", "event": "started", "name": "a" } { "type": "test", "name": "a", "event": "ok" } { "type": "test", "event": "started", "name": "b" } -{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:8:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.\n" } +{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.\n" } { "type": "test", "event": "started", "name": "c" } { "type": "test", "name": "c", "event": "ok" } { "type": "test", "event": "started", "name": "d" } diff --git a/src/test/run-make-fulldeps/libtest-json/output-stdout-success.json b/src/test/run-make-fulldeps/libtest-json/output-stdout-success.json new file mode 100644 index 0000000000000..303316278d8ab --- /dev/null +++ b/src/test/run-make-fulldeps/libtest-json/output-stdout-success.json @@ -0,0 +1,10 @@ +{ "type": "suite", "event": "started", "test_count": 4 } +{ "type": "test", "event": "started", "name": "a" } +{ "type": "test", "name": "a", "event": "ok", "stdout": "print from successful test\n" } +{ "type": "test", "event": "started", "name": "b" } +{ "type": "test", "name": "b", "event": "failed", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:9:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.\n" } +{ "type": "test", "event": "started", "name": "c" } +{ "type": "test", "name": "c", "event": "ok", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:15:5\n" } +{ "type": "test", "event": "started", "name": "d" } +{ "type": "test", "name": "d", "event": "ignored" } +{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "allowed_fail": 0, "ignored": 1, "measured": 0, "filtered_out": 0 } From c4569347b260fa1ae00ede021e39f2a3227da4ad Mon Sep 17 00:00:00 2001 From: Phosphorus15 Date: Mon, 19 Aug 2019 17:22:08 +0800 Subject: [PATCH 03/11] Added negative cases for `asinh` according to IEEE-754. --- src/libstd/f32.rs | 15 +++++++++++---- src/libstd/f64.rs | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index f649170c40372..653108cbeceae 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -908,10 +908,17 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - if self == NEG_INFINITY { - NEG_INFINITY - } else { - (self + ((self * self) + 1.0).sqrt()).ln() + match self { + x if x == NEG_INFINITY => NEG_INFINITY, + x if x.is_sign_negative() => { + let v = (x + ((x * x) + 1.0).sqrt()).ln(); + if v.is_sign_negative() { + v + } else { + -v + } + } + x => (x + ((x * x) + 1.0).sqrt()).ln() } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index f61630997dcdb..e5f963d873672 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -831,10 +831,17 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - if self == NEG_INFINITY { - NEG_INFINITY - } else { - (self + ((self * self) + 1.0).sqrt()).ln() + match self { + x if x == NEG_INFINITY => NEG_INFINITY, + x if x.is_sign_negative() => { + let v = (x + ((x * x) + 1.0).sqrt()).ln(); + if v.is_sign_negative() { + v + } else { + -v + } + } + x => (x + ((x * x) + 1.0).sqrt()).ln() } } From 64e3a10a82e6abad20f4a56750dac4cdd5df19ab Mon Sep 17 00:00:00 2001 From: Phosphorus15 Date: Mon, 19 Aug 2019 17:29:37 +0800 Subject: [PATCH 04/11] test cases for both `f32` and `f64` on asinh(-0.0) --- src/libstd/f32.rs | 1 + src/libstd/f64.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 653108cbeceae..ba75650fc4cb0 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -1495,6 +1495,7 @@ mod tests { assert_eq!(inf.asinh(), inf); assert_eq!(neg_inf.asinh(), neg_inf); assert!(nan.asinh().is_nan()); + assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index e5f963d873672..62c659739de79 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -1441,6 +1441,7 @@ mod tests { assert_eq!(inf.asinh(), inf); assert_eq!(neg_inf.asinh(), neg_inf); assert!(nan.asinh().is_nan()); + assert!((-0.0f64).asinh().is_sign_negative()); // issue 63271 assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); } From 535efa4afd3bb5a141eae8579f54aa641ccedaa2 Mon Sep 17 00:00:00 2001 From: Phosphorus15 Date: Tue, 20 Aug 2019 12:39:12 +0800 Subject: [PATCH 05/11] Used `copysign` to avoid unnecessary branches. --- src/libstd/f32.rs | 10 +--------- src/libstd/f64.rs | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index ba75650fc4cb0..dcb035993ae39 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -910,15 +910,7 @@ impl f32 { pub fn asinh(self) -> f32 { match self { x if x == NEG_INFINITY => NEG_INFINITY, - x if x.is_sign_negative() => { - let v = (x + ((x * x) + 1.0).sqrt()).ln(); - if v.is_sign_negative() { - v - } else { - -v - } - } - x => (x + ((x * x) + 1.0).sqrt()).ln() + x => (x + ((x * x) + 1.0).sqrt()).ln().copysign(self) } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 62c659739de79..076b6340d89bc 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -833,15 +833,7 @@ impl f64 { pub fn asinh(self) -> f64 { match self { x if x == NEG_INFINITY => NEG_INFINITY, - x if x.is_sign_negative() => { - let v = (x + ((x * x) + 1.0).sqrt()).ln(); - if v.is_sign_negative() { - v - } else { - -v - } - } - x => (x + ((x * x) + 1.0).sqrt()).ln() + x => (x + ((x * x) + 1.0).sqrt()).ln().copysign(self) } } From e33d8707c8ed516fd798c835acf7e1567293cf9a Mon Sep 17 00:00:00 2001 From: Phosphorus15 Date: Tue, 20 Aug 2019 15:12:41 +0800 Subject: [PATCH 06/11] Refined implementations of `asinh` and `acosh` --- src/libstd/f32.rs | 14 ++++++++------ src/libstd/f64.rs | 39 +++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index dcb035993ae39..f8f38cb75ab77 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -908,9 +908,10 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - match self { - x if x == NEG_INFINITY => NEG_INFINITY, - x => (x + ((x * x) + 1.0).sqrt()).ln().copysign(self) + if self == NEG_INFINITY { + NEG_INFINITY + } else { + (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) } } @@ -931,9 +932,10 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f32 { - match self { - x if x < 1.0 => crate::f32::NAN, - x => (x + ((x * x) - 1.0).sqrt()).ln(), + if self < 1.0 { + crate::f32::NAN + } else { + (self + ((self * self) - 1.0).sqrt()).ln() } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 076b6340d89bc..06f68bc72e764 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -244,7 +244,7 @@ impl f64 { pub fn div_euclid(self, rhs: f64) -> f64 { let q = (self / rhs).trunc(); if self % rhs < 0.0 { - return if rhs > 0.0 { q - 1.0 } else { q + 1.0 } + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; } q } @@ -437,9 +437,9 @@ impl f64 { pub fn log2(self) -> f64 { self.log_wrapper(|n| { #[cfg(target_os = "android")] - return crate::sys::android::log2f64(n); + return crate::sys::android::log2f64(n); #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f64(n) }; + return unsafe { intrinsics::log2f64(n) }; }) } @@ -481,16 +481,16 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] #[rustc_deprecated(since = "1.10.0", - reason = "you probably meant `(self - other).abs()`: \ + reason = "you probably meant `(self - other).abs()`: \ this operation is `(self - other).max(0.0)` \ except that `abs_sub` also propagates NaNs (also \ known as `fdim` in C). If you truly need the positive \ difference, consider using that expression or the C function \ `fdim`, depending on how you wish to handle NaN (please consider \ filing an issue describing your use-case too).")] - pub fn abs_sub(self, other: f64) -> f64 { - unsafe { cmath::fdim(self, other) } - } + pub fn abs_sub(self, other: f64) -> f64 { + unsafe { cmath::fdim(self, other) } + } /// Takes the cubic root of a number. /// @@ -831,9 +831,10 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - match self { - x if x == NEG_INFINITY => NEG_INFINITY, - x => (x + ((x * x) + 1.0).sqrt()).ln().copysign(self) + if self == NEG_INFINITY { + NEG_INFINITY + } else { + (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) } } @@ -852,9 +853,10 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f64 { - match self { - x if x < 1.0 => NAN, - x => (x + ((x * x) - 1.0).sqrt()).ln(), + if self < 1.0 { + NAN + } else { + (self + ((self * self) - 1.0).sqrt()).ln() } } @@ -1187,7 +1189,7 @@ mod tests { assert_eq!((-0f64).abs(), 0f64); assert_eq!((-1f64).abs(), 1f64); assert_eq!(NEG_INFINITY.abs(), INFINITY); - assert_eq!((1f64/NEG_INFINITY).abs(), 0f64); + assert_eq!((1f64 / NEG_INFINITY).abs(), 0f64); assert!(NAN.abs().is_nan()); } @@ -1199,7 +1201,7 @@ mod tests { assert_eq!((-0f64).signum(), -1f64); assert_eq!((-1f64).signum(), -1f64); assert_eq!(NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64/NEG_INFINITY).signum(), -1f64); + assert_eq!((1f64 / NEG_INFINITY).signum(), -1f64); assert!(NAN.signum().is_nan()); } @@ -1211,7 +1213,7 @@ mod tests { assert!(!(-0f64).is_sign_positive()); assert!(!(-1f64).is_sign_positive()); assert!(!NEG_INFINITY.is_sign_positive()); - assert!(!(1f64/NEG_INFINITY).is_sign_positive()); + assert!(!(1f64 / NEG_INFINITY).is_sign_positive()); assert!(NAN.is_sign_positive()); assert!(!(-NAN).is_sign_positive()); } @@ -1224,7 +1226,7 @@ mod tests { assert!((-0f64).is_sign_negative()); assert!((-1f64).is_sign_negative()); assert!(NEG_INFINITY.is_sign_negative()); - assert!((1f64/NEG_INFINITY).is_sign_negative()); + assert!((1f64 / NEG_INFINITY).is_sign_negative()); assert!(!NAN.is_sign_negative()); assert!((-NAN).is_sign_negative()); } @@ -1433,7 +1435,8 @@ mod tests { assert_eq!(inf.asinh(), inf); assert_eq!(neg_inf.asinh(), neg_inf); assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); // issue 63271 + assert!((-0.0f64).asinh().is_sign_negative()); + // issue 63271 assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); } From fa7ea104b232fbc2b72f7717393181e849d39dfe Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 26 Aug 2019 17:51:52 +0000 Subject: [PATCH 07/11] Error when generator trait is not found --- src/librustc_typeck/check/closure.rs | 4 ++-- src/test/ui/lang-item-missing-generator.rs | 19 +++++++++++++++++++ .../ui/lang-item-missing-generator.stderr | 4 ++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/lang-item-missing-generator.rs create mode 100644 src/test/ui/lang-item-missing-generator.stderr diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index d2f2f89cf0b1c..06b1e7bfd4eaf 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -3,7 +3,7 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; use crate::astconv::AstConv; -use crate::middle::region; +use crate::middle::{lang_items, region}; use rustc::hir::def_id::DefId; use rustc::infer::{InferOk, InferResult}; use rustc::infer::LateBoundRegionConversionTime; @@ -266,7 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_ref = projection.to_poly_trait_ref(tcx); let is_fn = tcx.lang_items().fn_trait_kind(trait_ref.def_id()).is_some(); - let gen_trait = tcx.lang_items().gen_trait().unwrap(); + let gen_trait = tcx.require_lang_item(lang_items::GeneratorTraitLangItem); let is_gen = gen_trait == trait_ref.def_id(); if !is_fn && !is_gen { debug!("deduce_sig_from_projection: not fn or generator"); diff --git a/src/test/ui/lang-item-missing-generator.rs b/src/test/ui/lang-item-missing-generator.rs new file mode 100644 index 0000000000000..0c329542928c5 --- /dev/null +++ b/src/test/ui/lang-item-missing-generator.rs @@ -0,0 +1,19 @@ +// error-pattern: requires `generator` lang_item +#![feature(no_core, lang_items, unboxed_closures)] +#![no_core] + +#[lang = "sized"] pub trait Sized { } + +#[lang = "fn_once"] +#[rustc_paren_sugar] +pub trait FnOnce { + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +pub fn abc() -> impl FnOnce(f32) { + |_| {} +} + +fn main() {} diff --git a/src/test/ui/lang-item-missing-generator.stderr b/src/test/ui/lang-item-missing-generator.stderr new file mode 100644 index 0000000000000..d0cc4b81be685 --- /dev/null +++ b/src/test/ui/lang-item-missing-generator.stderr @@ -0,0 +1,4 @@ +error: requires `generator` lang_item + +error: aborting due to previous error + From 32e5acb3eba7a92029488b9684d7bb27bc716294 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 20 Aug 2019 02:26:40 +0300 Subject: [PATCH 08/11] proc_macro: Turn `quote` into a regular built-in macro Previously in was implemented using a special hack in the metadata loader --- src/libproc_macro/lib.rs | 10 ++++++---- src/libproc_macro/quote.rs | 4 ++-- src/librustc_metadata/cstore_impl.rs | 12 +----------- src/libsyntax_ext/lib.rs | 8 ++++++++ src/test/ui/auxiliary/cond_plugin.rs | 1 + src/test/ui/auxiliary/proc_macro_def.rs | 1 + 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index c0f7714ca211a..d408fef75153e 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -19,12 +19,15 @@ #![feature(nll)] #![feature(staged_api)] +#![feature(allow_internal_unstable)] #![feature(const_fn)] +#![feature(decl_macro)] #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(optin_builtin_traits)] #![feature(mem_take)] #![feature(non_exhaustive)] +#![feature(rustc_attrs)] #![feature(specialization)] #![recursion_limit="256"] @@ -222,11 +225,10 @@ pub mod token_stream { /// /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. -/// -/// This is a dummy macro, the actual implementation is in `quote::quote`.` #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[macro_export] -macro_rules! quote { () => {} } +#[allow_internal_unstable(proc_macro_def_site)] +#[cfg_attr(not(bootstrap), rustc_builtin_macro)] +pub macro quote ($($t:tt)*) { /* compiler built-in */ } #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs index e3d31b78f4a09..144e2d6bac43b 100644 --- a/src/libproc_macro/quote.rs +++ b/src/libproc_macro/quote.rs @@ -57,9 +57,9 @@ macro_rules! quote { } /// Quote a `TokenStream` into a `TokenStream`. -/// This is the actual `quote!()` proc macro. +/// This is the actual implementation of the `quote!()` proc macro. /// -/// It is manually loaded in `CStore::load_macro_untracked`. +/// It is loaded by the compiler in `register_builtin_macros`. #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 7aeeef00ea934..f4c04770c1505 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -30,11 +30,9 @@ use syntax::ast; use syntax::attr; use syntax::source_map; use syntax::edition::Edition; -use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind}; -use syntax::ext::proc_macro::BangProcMacro; use syntax::parse::source_file_to_stream; use syntax::parse::parser::emit_unclosed_delims; -use syntax::symbol::{Symbol, sym}; +use syntax::symbol::Symbol; use syntax_pos::{Span, FileName}; use rustc_data_structures::bit_set::BitSet; @@ -437,14 +435,6 @@ impl cstore::CStore { let data = self.get_crate_data(id.krate); if data.is_proc_macro_crate() { return LoadedMacro::ProcMacro(data.get_proc_macro(id.index, sess).ext); - } else if data.name == sym::proc_macro && data.item_name(id.index) == sym::quote { - let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); - let kind = SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })); - let ext = SyntaxExtension { - allow_internal_unstable: Some([sym::proc_macro_def_site][..].into()), - ..SyntaxExtension::default(kind, data.root.edition) - }; - return LoadedMacro::ProcMacro(Lrc::new(ext)); } let def = data.get_macro(id.index); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 4add2261c6cda..1a6176916623b 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -7,13 +7,18 @@ #![feature(decl_macro)] #![feature(mem_take)] #![feature(nll)] +#![feature(proc_macro_internals)] +#![feature(proc_macro_quote)] #![feature(rustc_diagnostic_macros)] +extern crate proc_macro; + use crate::deriving::*; use syntax::ast::Ident; use syntax::edition::Edition; use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, MacroExpanderFn}; +use syntax::ext::proc_macro::BangProcMacro; use syntax::symbol::sym; mod error_codes; @@ -100,4 +105,7 @@ pub fn register_builtin_macros(resolver: &mut dyn syntax::ext::base::Resolver, e RustcDecodable: decodable::expand_deriving_rustc_decodable, RustcEncodable: encodable::expand_deriving_rustc_encodable, } + + let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); + register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client }))); } diff --git a/src/test/ui/auxiliary/cond_plugin.rs b/src/test/ui/auxiliary/cond_plugin.rs index 1f97b556a0767..2819541bf6966 100644 --- a/src/test/ui/auxiliary/cond_plugin.rs +++ b/src/test/ui/auxiliary/cond_plugin.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(proc_macro_hygiene)] +#![feature(proc_macro_quote)] extern crate proc_macro; diff --git a/src/test/ui/auxiliary/proc_macro_def.rs b/src/test/ui/auxiliary/proc_macro_def.rs index dfc5a42d19c69..49cfb5518ba9c 100644 --- a/src/test/ui/auxiliary/proc_macro_def.rs +++ b/src/test/ui/auxiliary/proc_macro_def.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(proc_macro_hygiene)] +#![feature(proc_macro_quote)] extern crate proc_macro; From 52c62eaae42ebc0efe370848a142c2858ac7fffc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 20 Aug 2019 21:22:32 +0300 Subject: [PATCH 09/11] Respect attributes on proc macro definitions --- src/librustc_metadata/decoder.rs | 18 ++--- src/libsyntax/ext/base.rs | 67 +++++++++++++++++- src/libsyntax/ext/tt/macro_rules.rs | 70 ++++--------------- .../proc-macro/attributes-on-definitions.rs | 12 ++++ .../attributes-on-definitions.stderr | 8 +++ .../auxiliary/attributes-on-definitions.rs | 23 ++++++ 6 files changed, 131 insertions(+), 67 deletions(-) create mode 100644 src/test/ui/proc-macro/attributes-on-definitions.rs create mode 100644 src/test/ui/proc-macro/attributes-on-definitions.stderr create mode 100644 src/test/ui/proc-macro/auxiliary/attributes-on-definitions.rs diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 5b9cb966af235..fffeee82d8d24 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -530,7 +530,6 @@ impl<'a, 'tcx> CrateMetadata { id: DefIndex, sess: &Session) -> FullProcMacro { - let raw_macro = self.raw_proc_macro(id); let (name, kind, helper_attrs) = match *raw_macro { ProcMacro::CustomDerive { trait_name, attributes, client } => { @@ -551,16 +550,19 @@ impl<'a, 'tcx> CrateMetadata { name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new() ) }; - - let span = self.get_span(id, sess); + let name = Symbol::intern(name); FullProcMacro { - name: Symbol::intern(name), - ext: Lrc::new(SyntaxExtension { - span, + name, + ext: Lrc::new(SyntaxExtension::new( + &sess.parse_sess, + kind, + self.get_span(id, sess), helper_attrs, - ..SyntaxExtension::default(kind, root.edition) - }) + root.edition, + name, + &self.get_attributes(&self.entry(id), sess), + )), } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a63c4181d5e03..10ff1b17285fe 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -1,11 +1,11 @@ use crate::ast::{self, NodeId, Attribute, Name, PatKind}; -use crate::attr::{HasAttrs, Stability, Deprecation}; +use crate::attr::{self, HasAttrs, Stability, Deprecation}; use crate::source_map::SourceMap; use crate::edition::Edition; use crate::ext::expand::{self, AstFragment, Invocation}; use crate::ext::hygiene::{ExpnId, Transparency}; use crate::mut_visit::{self, MutVisitor}; -use crate::parse::{self, parser, DirectoryOwnership}; +use crate::parse::{self, parser, ParseSess, DirectoryOwnership}; use crate::parse::token; use crate::ptr::P; use crate::symbol::{kw, sym, Ident, Symbol}; @@ -601,6 +601,69 @@ impl SyntaxExtension { } } + /// Constructs a syntax extension with the given properties + /// and other properties converted from attributes. + pub fn new( + sess: &ParseSess, + kind: SyntaxExtensionKind, + span: Span, + helper_attrs: Vec, + edition: Edition, + name: Name, + attrs: &[ast::Attribute], + ) -> SyntaxExtension { + let allow_internal_unstable = + attr::find_by_name(attrs, sym::allow_internal_unstable).map(|attr| { + attr.meta_item_list() + .map(|list| { + list.iter() + .filter_map(|it| { + let name = it.ident().map(|ident| ident.name); + if name.is_none() { + sess.span_diagnostic.span_err( + it.span(), "allow internal unstable expects feature names" + ) + } + name + }) + .collect::>() + .into() + }) + .unwrap_or_else(|| { + sess.span_diagnostic.span_warn( + attr.span, + "allow_internal_unstable expects list of feature names. In the future \ + this will become a hard error. Please use `allow_internal_unstable(\ + foo, bar)` to only allow the `foo` and `bar` features", + ); + vec![sym::allow_internal_unstable_backcompat_hack].into() + }) + }); + + let mut local_inner_macros = false; + if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) { + if let Some(l) = macro_export.meta_item_list() { + local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros); + } + } + + let is_builtin = attr::contains_name(attrs, sym::rustc_builtin_macro); + + SyntaxExtension { + kind, + span, + allow_internal_unstable, + allow_internal_unsafe: attr::contains_name(attrs, sym::allow_internal_unsafe), + local_inner_macros, + stability: attr::find_stability(&sess, attrs, span), + deprecation: attr::find_deprecation(&sess, attrs, span), + helper_attrs, + edition, + is_builtin, + is_derive_copy: is_builtin && name == sym::Copy, + } + } + pub fn dummy_bang(edition: Edition) -> SyntaxExtension { fn expander<'cx>(_: &'cx mut ExtCtxt<'_>, span: Span, _: &[TokenTree]) -> Box { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 37cb8467ff5ee..46ffa52f7f572 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -1,3 +1,5 @@ +use crate::ast; +use crate::attr::{self, TransparencyError}; use crate::edition::Edition; use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander}; use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind}; @@ -15,7 +17,6 @@ use crate::parse::token::{self, NtTT, Token}; use crate::parse::{Directory, ParseSess}; use crate::symbol::{kw, sym, Symbol}; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; -use crate::{ast, attr, attr::TransparencyError}; use errors::{DiagnosticBuilder, FatalError}; use log::debug; @@ -290,6 +291,7 @@ pub fn compile( def: &ast::Item, edition: Edition, ) -> SyntaxExtension { + let diag = &sess.span_diagnostic; let lhs_nm = ast::Ident::new(sym::lhs, def.span); let rhs_nm = ast::Ident::new(sym::rhs, def.span); let tt_spec = ast::Ident::new(sym::tt, def.span); @@ -423,13 +425,9 @@ pub fn compile( let (transparency, transparency_error) = attr::find_transparency(&def.attrs, body.legacy); match transparency_error { Some(TransparencyError::UnknownTransparency(value, span)) => - sess.span_diagnostic.span_err( - span, &format!("unknown macro transparency: `{}`", value) - ), + diag.span_err(span, &format!("unknown macro transparency: `{}`", value)), Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => - sess.span_diagnostic.span_err( - vec![old_span, new_span], "multiple macro transparency attributes" - ), + diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes"), None => {} } @@ -437,57 +435,15 @@ pub fn compile( name: def.ident, span: def.span, transparency, lhses, rhses, valid }); - let allow_internal_unstable = - attr::find_by_name(&def.attrs, sym::allow_internal_unstable).map(|attr| { - attr.meta_item_list() - .map(|list| { - list.iter() - .filter_map(|it| { - let name = it.ident().map(|ident| ident.name); - if name.is_none() { - sess.span_diagnostic.span_err( - it.span(), - "allow internal unstable expects feature names", - ) - } - name - }) - .collect::>() - .into() - }) - .unwrap_or_else(|| { - sess.span_diagnostic.span_warn( - attr.span, - "allow_internal_unstable expects list of feature names. In the \ - future this will become a hard error. Please use `allow_internal_unstable(\ - foo, bar)` to only allow the `foo` and `bar` features", - ); - vec![sym::allow_internal_unstable_backcompat_hack].into() - }) - }); - - let mut local_inner_macros = false; - if let Some(macro_export) = attr::find_by_name(&def.attrs, sym::macro_export) { - if let Some(l) = macro_export.meta_item_list() { - local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros); - } - } - - let is_builtin = attr::contains_name(&def.attrs, sym::rustc_builtin_macro); - - SyntaxExtension { - kind: SyntaxExtensionKind::LegacyBang(expander), - span: def.span, - allow_internal_unstable, - allow_internal_unsafe: attr::contains_name(&def.attrs, sym::allow_internal_unsafe), - local_inner_macros, - stability: attr::find_stability(&sess, &def.attrs, def.span), - deprecation: attr::find_deprecation(&sess, &def.attrs, def.span), - helper_attrs: Vec::new(), + SyntaxExtension::new( + sess, + SyntaxExtensionKind::LegacyBang(expander), + def.span, + Vec::new(), edition, - is_builtin, - is_derive_copy: is_builtin && def.ident.name == sym::Copy, - } + def.ident.name, + &def.attrs, + ) } fn check_lhs_nt_follows( diff --git a/src/test/ui/proc-macro/attributes-on-definitions.rs b/src/test/ui/proc-macro/attributes-on-definitions.rs new file mode 100644 index 0000000000000..055781d2c6048 --- /dev/null +++ b/src/test/ui/proc-macro/attributes-on-definitions.rs @@ -0,0 +1,12 @@ +// check-pass +// aux-build:attributes-on-definitions.rs + +#![forbid(unsafe_code)] + +extern crate attributes_on_definitions; + +attributes_on_definitions::with_attrs!(); +//~^ WARN use of deprecated item +// No errors about the use of unstable and unsafe code inside the macro. + +fn main() {} diff --git a/src/test/ui/proc-macro/attributes-on-definitions.stderr b/src/test/ui/proc-macro/attributes-on-definitions.stderr new file mode 100644 index 0000000000000..c61e043b22971 --- /dev/null +++ b/src/test/ui/proc-macro/attributes-on-definitions.stderr @@ -0,0 +1,8 @@ +warning: use of deprecated item 'attributes_on_definitions::with_attrs': test + --> $DIR/attributes-on-definitions.rs:8:1 + | +LL | attributes_on_definitions::with_attrs!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(deprecated)]` on by default + diff --git a/src/test/ui/proc-macro/auxiliary/attributes-on-definitions.rs b/src/test/ui/proc-macro/auxiliary/attributes-on-definitions.rs new file mode 100644 index 0000000000000..93a339840d621 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/attributes-on-definitions.rs @@ -0,0 +1,23 @@ +// force-host +// no-prefer-dynamic + +#![feature(allow_internal_unsafe)] +#![feature(allow_internal_unstable)] + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +#[allow_internal_unstable(proc_macro_internals)] +#[allow_internal_unsafe] +#[deprecated(since = "1.0.0", note = "test")] +pub fn with_attrs(_: TokenStream) -> TokenStream { + " + extern crate proc_macro; + use ::proc_macro::bridge; + + fn contains_unsafe() { unsafe {} } + ".parse().unwrap() +} From 2065ee9accc87a461897d06414195cfa3e92fb89 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 20 Aug 2019 23:18:55 +0300 Subject: [PATCH 10/11] metadata: Eliminate `FullProcMacro` Fix caching of loaded proc macros --- src/librustc_metadata/cstore.rs | 7 +-- src/librustc_metadata/cstore_impl.rs | 2 +- src/librustc_metadata/decoder.rs | 53 +++++++-------------- src/librustc_resolve/build_reduced_graph.rs | 12 ++--- src/librustc_resolve/macros.rs | 4 +- 5 files changed, 28 insertions(+), 50 deletions(-) diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index efc77699313e0..5bf4067431f24 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -95,11 +95,6 @@ pub struct CrateMetadata { pub raw_proc_macros: Option<&'static [ProcMacro]>, } -pub struct FullProcMacro { - pub name: ast::Name, - pub ext: Lrc -} - pub struct CStore { metas: RwLock>>>, /// Map from NodeId's of local extern crate statements to crate numbers @@ -109,7 +104,7 @@ pub struct CStore { pub enum LoadedMacro { MacroDef(ast::Item), - ProcMacro(Lrc), + ProcMacro(SyntaxExtension), } impl CStore { diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index f4c04770c1505..5bfb315da473f 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -434,7 +434,7 @@ impl cstore::CStore { pub fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro { let data = self.get_crate_data(id.krate); if data.is_proc_macro_crate() { - return LoadedMacro::ProcMacro(data.get_proc_macro(id.index, sess).ext); + return LoadedMacro::ProcMacro(data.load_proc_macro(id.index, sess)); } let def = data.get_macro(id.index); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index fffeee82d8d24..c777b5ea409a4 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -1,6 +1,6 @@ // Decoding metadata from a single crate's metadata -use crate::cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule, FullProcMacro}; +use crate::cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule}; use crate::schema::*; use rustc_data_structures::indexed_vec::IndexVec; @@ -512,26 +512,8 @@ impl<'a, 'tcx> CrateMetadata { self.entry(index).span.decode((self, sess)) } - - pub fn get_proc_macro(&self, id: DefIndex, sess: &Session) -> FullProcMacro { - if sess.opts.debugging_opts.dual_proc_macros { - let host_lib = self.host_lib.as_ref().unwrap(); - self.load_proc_macro( - &host_lib.metadata.get_root(), - id, - sess - ) - } else { - self.load_proc_macro(&self.root, id, sess) - } - } - - fn load_proc_macro(&self, root: &CrateRoot<'_>, - id: DefIndex, - sess: &Session) - -> FullProcMacro { - let raw_macro = self.raw_proc_macro(id); - let (name, kind, helper_attrs) = match *raw_macro { + crate fn load_proc_macro(&self, id: DefIndex, sess: &Session) -> SyntaxExtension { + let (name, kind, helper_attrs) = match *self.raw_proc_macro(id) { ProcMacro::CustomDerive { trait_name, attributes, client } => { let helper_attrs = attributes.iter().cloned().map(Symbol::intern).collect::>(); @@ -550,20 +532,21 @@ impl<'a, 'tcx> CrateMetadata { name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new() ) }; - let name = Symbol::intern(name); - - FullProcMacro { - name, - ext: Lrc::new(SyntaxExtension::new( - &sess.parse_sess, - kind, - self.get_span(id, sess), - helper_attrs, - root.edition, - name, - &self.get_attributes(&self.entry(id), sess), - )), - } + let edition = if sess.opts.debugging_opts.dual_proc_macros { + self.host_lib.as_ref().unwrap().metadata.get_root().edition + } else { + self.root.edition + }; + + SyntaxExtension::new( + &sess.parse_sess, + kind, + self.get_span(id, sess), + helper_attrs, + edition, + Symbol::intern(name), + &self.get_attributes(&self.entry(id), sess), + ) } pub fn get_trait_def(&self, item_id: DefIndex, sess: &Session) -> ty::TraitDef { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 9a794ade729c2..165a4c707bb6d 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -150,12 +150,12 @@ impl<'a> Resolver<'a> { return Some(ext.clone()); } - let macro_def = match self.cstore.load_macro_untracked(def_id, &self.session) { - LoadedMacro::MacroDef(macro_def) => macro_def, - LoadedMacro::ProcMacro(ext) => return Some(ext), - }; + let ext = Lrc::new(match self.cstore.load_macro_untracked(def_id, &self.session) { + LoadedMacro::MacroDef(item) => + self.compile_macro(&item, self.cstore.crate_edition_untracked(def_id.krate)), + LoadedMacro::ProcMacro(ext) => ext, + }); - let ext = self.compile_macro(¯o_def, self.cstore.crate_edition_untracked(def_id.krate)); self.macro_map.insert(def_id, ext.clone()); Some(ext) } @@ -1104,7 +1104,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let expansion = parent_scope.expansion; let (ext, ident, span, is_legacy) = match &item.node { ItemKind::MacroDef(def) => { - let ext = self.r.compile_macro(item, self.r.session.edition()); + let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition())); (ext, item.ident, item.span, def.legacy) } ItemKind::Fn(..) => match Self::proc_macro_stub(item) { diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 719167eb057b2..cc78e928380a8 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -800,7 +800,7 @@ impl<'a> Resolver<'a> { /// Compile the macro into a `SyntaxExtension` and possibly replace it with a pre-defined /// extension partially or entirely for built-in macros and legacy plugin macros. - crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> Lrc { + crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension { let mut result = macro_rules::compile( &self.session.parse_sess, self.session.features_untracked(), item, edition ); @@ -822,6 +822,6 @@ impl<'a> Resolver<'a> { } } - Lrc::new(result) + result } } From c476b55e528ce854b6198de5bcfdd20b08440c9d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 20 Aug 2019 23:35:03 +0300 Subject: [PATCH 11/11] proc_macro: Update `Span::def_site` to use the proc macro definition location Which is no longer dummy and is available from metadata now. --- src/libsyntax/ext/proc_macro_server.rs | 7 +- src/libsyntax/parse/parser.rs | 9 +- .../macros/auxiliary/proc_macro_sequence.rs | 20 ++- src/test/ui/macros/same-sequence-span.stderr | 12 +- src/test/ui/proc-macro/multispan.stderr | 133 ++++++++++++++---- src/test/ui/proc-macro/three-equals.stderr | 19 ++- 6 files changed, 151 insertions(+), 49 deletions(-) diff --git a/src/libsyntax/ext/proc_macro_server.rs b/src/libsyntax/ext/proc_macro_server.rs index b1bbd2aaac971..1a26b17dac782 100644 --- a/src/libsyntax/ext/proc_macro_server.rs +++ b/src/libsyntax/ext/proc_macro_server.rs @@ -360,12 +360,11 @@ pub(crate) struct Rustc<'a> { impl<'a> Rustc<'a> { pub fn new(cx: &'a ExtCtxt<'_>) -> Self { - // No way to determine def location for a proc macro right now, so use call location. - let location = cx.current_expansion.id.expn_data().call_site; + let expn_data = cx.current_expansion.id.expn_data(); Rustc { sess: cx.parse_sess, - def_site: cx.with_def_site_ctxt(location), - call_site: cx.with_call_site_ctxt(location), + def_site: cx.with_def_site_ctxt(expn_data.def_site), + call_site: cx.with_call_site_ctxt(expn_data.call_site), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 89725d8b3395c..47243b1ac5678 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -375,10 +375,11 @@ impl<'a> Parser<'a> { if let Some(directory) = directory { parser.directory = directory; } else if !parser.token.span.is_dummy() { - if let FileName::Real(mut path) = - sess.source_map().span_to_unmapped_path(parser.token.span) { - path.pop(); - parser.directory.path = Cow::from(path); + if let Some(FileName::Real(path)) = + &sess.source_map().lookup_char_pos(parser.token.span.lo()).file.unmapped_path { + if let Some(directory_path) = path.parent() { + parser.directory.path = Cow::from(directory_path.to_path_buf()); + } } } diff --git a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs index b50ed7ca92ad5..c460db36f1aa3 100644 --- a/src/test/ui/macros/auxiliary/proc_macro_sequence.rs +++ b/src/test/ui/macros/auxiliary/proc_macro_sequence.rs @@ -6,7 +6,7 @@ extern crate proc_macro; -use proc_macro::{quote, Span, TokenStream}; +use proc_macro::{quote, Span, TokenStream, TokenTree}; fn assert_same_span(a: Span, b: Span) { assert_eq!(a.start(), b.start()); @@ -24,12 +24,22 @@ pub fn make_foo(_: TokenStream) -> TokenStream { }; // Check that all spans are equal. - let mut span = None; + // FIXME: `quote!` gives def-site spans to idents and literals, + // but leaves (default) call-site spans on groups and punctuation. + let mut span_call = None; + let mut span_def = None; for tt in result.clone() { - match span { - None => span = Some(tt.span()), - Some(span) => assert_same_span(tt.span(), span), + match tt { + TokenTree::Ident(..) | TokenTree::Literal(..) => match span_def { + None => span_def = Some(tt.span()), + Some(span) => assert_same_span(tt.span(), span), + } + TokenTree::Punct(..) | TokenTree::Group(..) => match span_call { + None => span_call = Some(tt.span()), + Some(span) => assert_same_span(tt.span(), span), + } } + } result diff --git a/src/test/ui/macros/same-sequence-span.stderr b/src/test/ui/macros/same-sequence-span.stderr index 250773a1853ec..0eef4a2a678b6 100644 --- a/src/test/ui/macros/same-sequence-span.stderr +++ b/src/test/ui/macros/same-sequence-span.stderr @@ -17,11 +17,15 @@ LL | $(= $z:tt)* error: `$x:expr` may be followed by `$y:tt`, which is not allowed for `expr` fragments --> $DIR/same-sequence-span.rs:20:1 | -LL | proc_macro_sequence::make_foo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | proc_macro_sequence::make_foo!(); + | ^-------------------------------- + | | + | _in this macro invocation | | - | not allowed after `expr` fragments - | in this macro invocation +LL | | +LL | | +LL | | fn main() {} +... | | = note: allowed there are: `=>`, `,` or `;` diff --git a/src/test/ui/proc-macro/multispan.stderr b/src/test/ui/proc-macro/multispan.stderr index a0c1f9cd5c05f..e7f705c7feb67 100644 --- a/src/test/ui/proc-macro/multispan.stderr +++ b/src/test/ui/proc-macro/multispan.stderr @@ -1,8 +1,19 @@ error: hello to you, too! - --> $DIR/multispan.rs:14:5 - | -LL | hello!(hi); - | ^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:14:5 + | +LL | hello!(hi); + | ----------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:14:12 @@ -11,10 +22,21 @@ LL | hello!(hi); | ^^ error: hello to you, too! - --> $DIR/multispan.rs:17:5 - | -LL | hello!(hi hi); - | ^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:17:5 + | +LL | hello!(hi hi); + | -------------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:17:12 @@ -23,10 +45,21 @@ LL | hello!(hi hi); | ^^ ^^ error: hello to you, too! - --> $DIR/multispan.rs:20:5 - | -LL | hello!(hi hi hi); - | ^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:20:5 + | +LL | hello!(hi hi hi); + | ----------------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:20:12 @@ -35,10 +68,21 @@ LL | hello!(hi hi hi); | ^^ ^^ ^^ error: hello to you, too! - --> $DIR/multispan.rs:23:5 - | -LL | hello!(hi hey hi yo hi beep beep hi hi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:23:5 + | +LL | hello!(hi hey hi yo hi beep beep hi hi); + | ---------------------------------------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:23:12 @@ -47,10 +91,21 @@ LL | hello!(hi hey hi yo hi beep beep hi hi); | ^^ ^^ ^^ ^^ ^^ error: hello to you, too! - --> $DIR/multispan.rs:24:5 - | -LL | hello!(hi there, hi how are you? hi... hi.); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:24:5 + | +LL | hello!(hi there, hi how are you? hi... hi.); + | -------------------------------------------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:24:12 @@ -59,10 +114,21 @@ LL | hello!(hi there, hi how are you? hi... hi.); | ^^ ^^ ^^ ^^ error: hello to you, too! - --> $DIR/multispan.rs:25:5 - | -LL | hello!(whoah. hi di hi di ho); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:25:5 + | +LL | hello!(whoah. hi di hi di ho); + | ------------------------------ in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:25:19 @@ -71,10 +137,21 @@ LL | hello!(whoah. hi di hi di ho); | ^^ ^^ error: hello to you, too! - --> $DIR/multispan.rs:26:5 - | -LL | hello!(hi good hi and good bye); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/multispan.rs:31:1 + | +LL | / pub fn hello(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | } +LL | | +LL | | TokenStream::new() +LL | | } + | |_^ + | + ::: $DIR/multispan.rs:26:5 + | +LL | hello!(hi good hi and good bye); + | -------------------------------- in this macro invocation | note: found these 'hi's --> $DIR/multispan.rs:26:12 diff --git a/src/test/ui/proc-macro/three-equals.stderr b/src/test/ui/proc-macro/three-equals.stderr index 0a6cbe13098a8..0698b0f475424 100644 --- a/src/test/ui/proc-macro/three-equals.stderr +++ b/src/test/ui/proc-macro/three-equals.stderr @@ -1,8 +1,19 @@ error: found 2 equal signs, need exactly 3 - --> $DIR/three-equals.rs:15:5 - | -LL | three_equals!(==); - | ^^^^^^^^^^^^^^^^^^ in this macro invocation + --> $DIR/auxiliary/three-equals.rs:42:1 + | +LL | / pub fn three_equals(input: TokenStream) -> TokenStream { +LL | | if let Err(diag) = parse(input) { +LL | | diag.emit(); +LL | | return TokenStream::new(); +... | +LL | | "3".parse().unwrap() +LL | | } + | |_^ + | + ::: $DIR/three-equals.rs:15:5 + | +LL | three_equals!(==); + | ------------------ in this macro invocation | = help: input must be: `===`