From 3790ce8ccd32d075620f208b45cd872e535d5238 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Tue, 24 Jul 2018 22:18:59 -0400 Subject: [PATCH 1/9] Update sandbox tests to use defaults to avoid annoying changes --- ui/src/sandbox.rs | 114 +++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 73 deletions(-) diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index f1e4366b6..7d3537024 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -628,16 +628,36 @@ mod test { } "#; + impl Default for ExecuteRequest { + fn default() -> Self { + ExecuteRequest { + channel: Channel::Stable, + crate_type: CrateType::Binary, + mode: Mode::Debug, + tests: false, + code: HELLO_WORLD_CODE.to_string(), + edition: None, + } + } + } + + impl Default for CompileRequest { + fn default() -> Self { + CompileRequest { + target: CompileTarget::LlvmIr, + channel: Channel::Stable, + crate_type: CrateType::Binary, + mode: Mode::Debug, + tests: false, + code: HELLO_WORLD_CODE.to_string(), + edition: None, + } + } + } + #[test] fn basic_functionality() { - let req = ExecuteRequest { - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, - code: HELLO_WORLD_CODE.to_string(), - edition: None, - }; + let req = ExecuteRequest::default(); let sb = Sandbox::new().expect("Unable to create sandbox"); let resp = sb.execute(&req).expect("Unable to execute code"); @@ -660,12 +680,8 @@ mod test { #[test] fn debug_mode() { let req = ExecuteRequest { - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, code: COMPILATION_MODE_CODE.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -677,12 +693,9 @@ mod test { #[test] fn release_mode() { let req = ExecuteRequest { - channel: Channel::Stable, - crate_type: CrateType::Binary, mode: Mode::Release, - tests: false, code: COMPILATION_MODE_CODE.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -705,11 +718,8 @@ mod test { fn stable_channel() { let req = ExecuteRequest { channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, code: VERSION_CODE.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -724,11 +734,8 @@ mod test { fn beta_channel() { let req = ExecuteRequest { channel: Channel::Beta, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, code: VERSION_CODE.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -743,11 +750,8 @@ mod test { fn nightly_channel() { let req = ExecuteRequest { channel: Channel::Nightly, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, code: VERSION_CODE.to_string(), - edition: Some(Edition::Rust2018), + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -762,12 +766,7 @@ mod test { fn output_llvm_ir() { let req = CompileRequest { target: CompileTarget::LlvmIr, - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, - code: HELLO_WORLD_CODE.to_string(), - edition: None, + ..CompileRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -782,12 +781,7 @@ mod test { fn output_assembly() { let req = CompileRequest { target: CompileTarget::Assembly(AssemblyFlavor::Att, DemangleAssembly::Mangle, ProcessAssembly::Raw), - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, - code: HELLO_WORLD_CODE.to_string(), - edition: None, + ..CompileRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -803,12 +797,7 @@ mod test { fn output_demangled_assembly() { let req = CompileRequest { target: CompileTarget::Assembly(AssemblyFlavor::Att, DemangleAssembly::Demangle, ProcessAssembly::Raw), - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, - code: HELLO_WORLD_CODE.to_string(), - edition: None, + ..CompileRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -823,12 +812,7 @@ mod test { fn output_filtered_assembly() { let req = CompileRequest { target: CompileTarget::Assembly(AssemblyFlavor::Att, DemangleAssembly::Mangle, ProcessAssembly::Filter), - channel: Channel::Stable, - crate_type: CrateType::Binary, - mode: Mode::Debug, - tests: false, - code: HELLO_WORLD_CODE.to_string(), - edition: None, + ..CompileRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -886,12 +870,8 @@ mod test { "#; let req = ExecuteRequest { - channel: Channel::Stable, - mode: Mode::Debug, - crate_type: CrateType::Binary, - tests: false, code: code.to_string(), - edition: None + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -911,12 +891,8 @@ mod test { "#; let req = ExecuteRequest { - channel: Channel::Stable, - mode: Mode::Debug, - crate_type: CrateType::Binary, - tests: false, code: code.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -935,12 +911,8 @@ mod test { "#; let req = ExecuteRequest { - channel: Channel::Stable, - mode: Mode::Debug, - crate_type: CrateType::Binary, - tests: false, code: code.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); @@ -964,12 +936,8 @@ mod test { "##; let req = ExecuteRequest { - channel: Channel::Stable, - mode: Mode::Debug, - crate_type: CrateType::Binary, - tests: false, code: forkbomb.to_string(), - edition: None, + ..ExecuteRequest::default() }; let sb = Sandbox::new().expect("Unable to create sandbox"); From ce7b9bb6d1d27e67f6624ba83f6635b7cc9df78b Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Tue, 24 Jul 2018 22:19:05 -0400 Subject: [PATCH 2/9] Update sandbox memory test to touch all memory pages --- ui/src/sandbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index 7d3537024..0dc526e05 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -886,7 +886,7 @@ mod test { fn main() { let megabyte = 1024 * 1024; let mut big = vec![0u8; 384 * megabyte]; - *big.last_mut().unwrap() += 1; + for i in &mut big { *i += 1; } } "#; From b03f37a8ea7a5bdb0d4637a9b9b9d99fd0e4cedf Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Tue, 24 Jul 2018 22:48:57 -0400 Subject: [PATCH 3/9] Add sandbox tests for editions --- ui/src/sandbox.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index 0dc526e05..c01d66440 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -762,6 +762,63 @@ mod test { assert!(resp.stdout.contains("nightly")); } + const EDITION_CODE: &str = r#" + mod foo { + pub fn bar() {} + } + + fn main() { + crate::foo::bar(); + } + "#; + + #[test] + fn rust_edition_default() -> Result<()> { + let req = ExecuteRequest { + channel: Channel::Nightly, + code: EDITION_CODE.to_string(), + ..ExecuteRequest::default() + }; + + let sb = Sandbox::new()?; + let resp = sb.execute(&req)?; + + assert!(resp.stderr.contains("`crate` in paths is experimental")); + Ok(()) + } + + #[test] + fn rust_edition_2015() -> Result<()> { + let req = ExecuteRequest { + channel: Channel::Nightly, + code: EDITION_CODE.to_string(), + edition: Some(Edition::Rust2015), + ..ExecuteRequest::default() + }; + + let sb = Sandbox::new()?; + let resp = sb.execute(&req)?; + + assert!(resp.stderr.contains("`crate` in paths is experimental")); + Ok(()) + } + + #[test] + fn rust_edition_2018() -> Result<()> { + let req = ExecuteRequest { + channel: Channel::Nightly, + code: EDITION_CODE.to_string(), + edition: Some(Edition::Rust2018), + ..ExecuteRequest::default() + }; + + let sb = Sandbox::new()?; + let resp = sb.execute(&req)?; + + assert!(!resp.stderr.contains("`crate` in paths is experimental")); + Ok(()) + } + #[test] fn output_llvm_ir() { let req = CompileRequest { From 670f78667daa8e70de98855e3530afe2ee801408 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 22:19:34 -0400 Subject: [PATCH 4/9] Add backtrace to the state --- ui/frontend/AdvancedOptionsMenu.tsx | 21 ++++++++++++++++++--- ui/frontend/actions.ts | 6 ++++++ ui/frontend/reducers/configuration.ts | 5 +++++ ui/frontend/selectors/index.ts | 17 ++++++++++++----- ui/frontend/types.ts | 5 +++++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/ui/frontend/AdvancedOptionsMenu.tsx b/ui/frontend/AdvancedOptionsMenu.tsx index 63acbcd46..27d906705 100644 --- a/ui/frontend/AdvancedOptionsMenu.tsx +++ b/ui/frontend/AdvancedOptionsMenu.tsx @@ -1,19 +1,22 @@ import React, { Fragment } from 'react'; import { connect } from 'react-redux'; -import { changeEdition } from './actions'; +import { changeBacktrace, changeEdition } from './actions'; import { changeNightlyEdition } from './actions'; import { Either as EitherConfig } from './ConfigElement'; import MenuGroup from './MenuGroup'; import { State } from './reducers'; -import { getEditionSet, isEditionAvailable } from './selectors'; -import { Edition } from './types'; +import { getBacktraceSet, getEditionSet, isEditionAvailable } from './selectors'; +import { Backtrace, Edition } from './types'; interface AdvancedOptionsMenuProps { edition: Edition; isEditionSet: boolean; isEditionAvailable: boolean; changeEdition: (_: Edition) => any; + backtrace: Backtrace; + isBacktraceSet: boolean; + changeBacktrace: (_: Backtrace) => any; } const AdvancedOptionsMenu: React.SFC = props => ( @@ -28,6 +31,15 @@ const AdvancedOptionsMenu: React.SFC = props => ( isNotDefault={props.isEditionSet} onChange={props.changeEdition} /> {!props.isEditionAvailable && } + + ); @@ -43,10 +55,13 @@ const mapStateToProps = (state: State) => ({ isEditionSet: getEditionSet(state), edition: state.configuration.edition, isEditionAvailable: isEditionAvailable(state), + isBacktraceSet: getBacktraceSet(state), + backtrace: state.configuration.backtrace, }); const mapDispatchToProps = ({ changeEdition: changeNightlyEdition, + changeBacktrace, }); export default connect(mapStateToProps, mapDispatchToProps)(AdvancedOptionsMenu); diff --git a/ui/frontend/actions.ts b/ui/frontend/actions.ts index 094a99758..9b4a57498 100644 --- a/ui/frontend/actions.ts +++ b/ui/frontend/actions.ts @@ -6,6 +6,7 @@ import { getCrateType, isEditionAvailable, runAsTest } from './selectors'; import State from './state'; import { AssemblyFlavor, + Backtrace, Channel, DemangleAssembly, Edition, @@ -51,6 +52,7 @@ export enum ActionType { ChangeProcessAssembly = 'CHANGE_PROCESS_ASSEMBLY', ChangeMode = 'CHANGE_MODE', ChangeEdition = 'CHANGE_EDITION', + ChangeBacktrace = 'CHANGE_BACKTRACE', ChangeFocus = 'CHANGE_FOCUS', ExecuteRequest = 'EXECUTE_REQUEST', ExecuteSucceeded = 'EXECUTE_SUCCEEDED', @@ -131,6 +133,9 @@ export const changeNightlyEdition = (edition: Edition): ThunkAction => dispatch dispatch(changeEdition(edition)); }; +export const changeBacktrace = (backtrace: Backtrace) => + createAction(ActionType.ChangeBacktrace, { backtrace }); + export const changeFocus = focus => createAction(ActionType.ChangeFocus, { focus }); @@ -569,6 +574,7 @@ export type Action = | ReturnType | ReturnType | ReturnType + | ReturnType | ReturnType | ReturnType | ReturnType diff --git a/ui/frontend/reducers/configuration.ts b/ui/frontend/reducers/configuration.ts index 1a9ea3457..74e14927f 100644 --- a/ui/frontend/reducers/configuration.ts +++ b/ui/frontend/reducers/configuration.ts @@ -1,6 +1,7 @@ import { Action, ActionType } from '../actions'; import { AssemblyFlavor, + Backtrace, Channel, DemangleAssembly, Edition, @@ -22,6 +23,7 @@ export interface State { channel: Channel; mode: Mode; edition: Edition; + backtrace: Backtrace; } export const DEFAULT: State = { @@ -36,6 +38,7 @@ export const DEFAULT: State = { channel: Channel.Stable, mode: Mode.Debug, edition: Edition.Rust2015, + backtrace: Backtrace.Disabled, }; export default function configuration(state = DEFAULT, action: Action): State { @@ -75,6 +78,8 @@ export default function configuration(state = DEFAULT, action: Action): State { } return { ...state, edition: action.edition }; } + case ActionType.ChangeBacktrace: + return { ...state, backtrace: action.backtrace }; default: return state; } diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index 6486a7727..ebdb46fb7 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -3,7 +3,7 @@ import { createSelector } from 'reselect'; import * as url from 'url'; import { State } from '../reducers'; -import { Channel, Edition } from '../types'; +import { Backtrace, Channel, Edition } from '../types'; const getCode = state => state.code; @@ -53,14 +53,21 @@ export const getChannelLabel = (state: State) => { return `${channel}`; }; -export const getAdvancedOptionsSet = (state: State) => ( - getEditionSet(state) -); - export const getEditionSet = (state: State) => ( state.configuration.edition !== Edition.Rust2015 ); +export const getBacktraceSet = (state: State) => ( + state.configuration.backtrace !== Backtrace.Disabled +); + +export const getAdvancedOptionsSet = createSelector( + getEditionSet, getBacktraceSet, + (editionSet, backtraceSet) => ( + editionSet || backtraceSet + ), +); + const baseUrlSelector = (state: State) => state.globalConfiguration.baseUrl; diff --git a/ui/frontend/types.ts b/ui/frontend/types.ts index 4dd1c0ff3..4598271ca 100644 --- a/ui/frontend/types.ts +++ b/ui/frontend/types.ts @@ -66,3 +66,8 @@ export enum Edition { Rust2015 = '2015', Rust2018 = '2018', } + +export enum Backtrace { + Disabled = 'disabled', + Enabled = 'enabled', +} From cf4e2417a799ce982d03c619b6042974e5b24844 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 22:33:03 -0400 Subject: [PATCH 5/9] Enable backtraces when passed to the backend --- ui/src/main.rs | 5 ++++ ui/src/sandbox.rs | 66 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/ui/src/main.rs b/ui/src/main.rs index dc6db240a..67a98a620 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -525,6 +525,7 @@ struct CompileRequest { #[serde(rename = "crateType")] crate_type: String, tests: bool, + backtrace: bool, code: String, } @@ -545,6 +546,7 @@ struct ExecuteRequest { #[serde(rename = "crateType")] crate_type: String, tests: bool, + backtrace: bool, code: String, } @@ -657,6 +659,7 @@ impl TryFrom for sandbox::CompileRequest { edition: parse_edition(&me.edition)?, crate_type: parse_crate_type(&me.crate_type)?, tests: me.tests, + backtrace: me.backtrace, code: me.code, }) } @@ -683,6 +686,7 @@ impl TryFrom for sandbox::ExecuteRequest { edition: parse_edition(&me.edition)?, crate_type: try!(parse_crate_type(&me.crate_type)), tests: me.tests, + backtrace: me.backtrace, code: me.code, }) } @@ -779,6 +783,7 @@ impl TryFrom for sandbox::ExecuteRequest { edition: None, // FIXME: What should this be? crate_type: sandbox::CrateType::Binary, tests: false, + backtrace: false, code: me.code, }) } diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index c01d66440..c63c15202 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -113,7 +113,7 @@ impl Sandbox { pub fn compile(&self, req: &CompileRequest) -> Result { try!(self.write_source_code(&req.code)); - let mut command = self.compile_command(req.target, req.channel, req.mode, req.crate_type, req.tests, req.edition); + let mut command = self.compile_command(req.target, req.channel, req.mode, req.crate_type, req.tests, req.backtrace, req.edition); let output = try!(command.output().map_err(Error::UnableToExecuteCompiler)); @@ -167,7 +167,7 @@ impl Sandbox { pub fn execute(&self, req: &ExecuteRequest) -> Result { try!(self.write_source_code(&req.code)); - let mut command = self.execute_command(req.channel, req.mode, req.crate_type, req.tests, req.edition); + let mut command = self.execute_command(req.channel, req.mode, req.crate_type, req.tests, req.backtrace, req.edition); let output = try!(command.output().map_err(Error::UnableToExecuteCompiler)); @@ -257,9 +257,9 @@ impl Sandbox { Ok(()) } - fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, crate_type: CrateType, tests: bool, edition: Option) -> Command { + fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, crate_type: CrateType, tests: bool, backtrace: bool, edition: Option) -> Command { let mut cmd = self.docker_command(Some(crate_type)); - set_execution_environment(&mut cmd, Some(target), crate_type, edition); + set_execution_environment(&mut cmd, Some(target), crate_type, edition, backtrace); let execution_cmd = build_execution_command(Some(target), mode, crate_type, tests); @@ -270,9 +270,9 @@ impl Sandbox { cmd } - fn execute_command(&self, channel: Channel, mode: Mode, crate_type: CrateType, tests: bool, edition: Option) -> Command { + fn execute_command(&self, channel: Channel, mode: Mode, crate_type: CrateType, tests: bool, backtrace: bool, edition: Option) -> Command { let mut cmd = self.docker_command(Some(crate_type)); - set_execution_environment(&mut cmd, None, crate_type, edition); + set_execution_environment(&mut cmd, None, crate_type, edition, backtrace); let execution_cmd = build_execution_command(None, mode, crate_type, tests); @@ -392,7 +392,7 @@ fn build_execution_command(target: Option, mode: Mode, crate_type cmd } -fn set_execution_environment(cmd: &mut Command, target: Option, crate_type: CrateType, edition: Option) { +fn set_execution_environment(cmd: &mut Command, target: Option, crate_type: CrateType, edition: Option, backtrace: bool) { use self::CompileTarget::*; use self::CrateType::*; @@ -408,6 +408,10 @@ fn set_execution_environment(cmd: &mut Command, target: Option, c if let Some(edition) = edition { cmd.args(&["--env", &format!("PLAYGROUND_EDITION={}", edition.cargo_ident())]); } + + if backtrace { + cmd.args(&["--env", "RUST_BACKTRACE=1"]); + } } fn read(path: &Path) -> Result> { @@ -565,6 +569,7 @@ pub struct CompileRequest { pub mode: Mode, pub edition: Option, pub tests: bool, + pub backtrace: bool, pub code: String, } @@ -583,6 +588,7 @@ pub struct ExecuteRequest { pub edition: Option, pub crate_type: CrateType, pub tests: bool, + pub backtrace: bool, pub code: String, } @@ -637,6 +643,7 @@ mod test { tests: false, code: HELLO_WORLD_CODE.to_string(), edition: None, + backtrace: false, } } } @@ -651,6 +658,7 @@ mod test { tests: false, code: HELLO_WORLD_CODE.to_string(), edition: None, + backtrace: false, } } } @@ -819,6 +827,50 @@ mod test { Ok(()) } + const BACKTRACE_CODE: &str = r#" + fn trigger_the_problem() { + None::.unwrap(); + } + + fn main() { + trigger_the_problem() + } + "#; + + #[test] + fn backtrace_disabled() -> Result<()> { + let req = ExecuteRequest { + code: BACKTRACE_CODE.to_string(), + backtrace: false, + ..ExecuteRequest::default() + }; + + let sb = Sandbox::new()?; + let resp = sb.execute(&req)?; + + assert!(resp.stderr.contains("Run with `RUST_BACKTRACE=1` for a backtrace")); + assert!(!resp.stderr.contains("stack backtrace:")); + + Ok(()) + } + + #[test] + fn backtrace_enabled() -> Result<()> { + let req = ExecuteRequest { + code: BACKTRACE_CODE.to_string(), + backtrace: true, + ..ExecuteRequest::default() + }; + + let sb = Sandbox::new()?; + let resp = sb.execute(&req)?; + + assert!(!resp.stderr.contains("Run with `RUST_BACKTRACE=1` for a backtrace")); + assert!(resp.stderr.contains("stack backtrace:")); + + Ok(()) + } + #[test] fn output_llvm_ir() { let req = CompileRequest { From a518816caaa2f8e63277558fbc696b9bfd68edd1 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 22:33:28 -0400 Subject: [PATCH 6/9] Pass the backtrace flag to the backend --- ui/frontend/actions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/frontend/actions.ts b/ui/frontend/actions.ts index 9b4a57498..cfddce411 100644 --- a/ui/frontend/actions.ts +++ b/ui/frontend/actions.ts @@ -189,6 +189,7 @@ interface ExecuteRequestBody { tests: boolean; code: string; edition?: string; + backtrace: boolean; } export function performExecute(): ThunkAction { @@ -200,8 +201,9 @@ export function performExecute(): ThunkAction { const { code, configuration: { channel, mode, edition } } = state; const crateType = getCrateType(state); const tests = runAsTest(state); + const backtrace = state.configuration.backtrace === Backtrace.Enabled; - const body: ExecuteRequestBody = { channel, mode, crateType, tests, code }; + const body: ExecuteRequestBody = { channel, mode, crateType, tests, code, backtrace }; if (isEditionAvailable(state)) { body.edition = edition; } @@ -235,6 +237,7 @@ function performCompile(target, { request, success, failure }): ThunkAction { } } = state; const crateType = getCrateType(state); const tests = runAsTest(state); + const backtrace = state.configuration.backtrace === Backtrace.Enabled; const body: CompileRequestBody = { channel, mode, @@ -245,6 +248,7 @@ function performCompile(target, { request, success, failure }): ThunkAction { assemblyFlavor, demangleAssembly, processAssembly, + backtrace, }; if (isEditionAvailable(state)) { body.edition = edition; From 9b2e8dabcebe9edfe6b5c3ff0f6ee8291d39b2bf Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 22:45:33 -0400 Subject: [PATCH 7/9] Restore the backtrace go-to-line links --- ui/frontend/highlighting.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/frontend/highlighting.ts b/ui/frontend/highlighting.ts index 760eca366..ec808ff08 100644 --- a/ui/frontend/highlighting.ts +++ b/ui/frontend/highlighting.ts @@ -11,7 +11,12 @@ export function configureRustErrors({ gotoPosition, getChannel }) { }, }, 'error-location': /-->.*\n/, - 'stack-trace-location': /at \/playground.*\n/, + 'backtrace': { + pattern: /at src\/.*\n/, + inside: { + 'backtrace-location': /src\/main.rs:(\d+)/, + }, + }, }; Prism.hooks.add('wrap', env => { @@ -37,8 +42,8 @@ export function configureRustErrors({ gotoPosition, getChannel }) { env.attributes['data-line'] = line; env.attributes['data-col'] = col; } - if (env.type === 'stack-trace-location') { - const errorMatch = /main.rs:(\d+)/.exec(env.content); + if (env.type === 'backtrace-location') { + const errorMatch = /:(\d+)/.exec(env.content); const [_, line] = errorMatch; env.tag = 'a'; env.attributes.href = '#'; @@ -48,7 +53,7 @@ export function configureRustErrors({ gotoPosition, getChannel }) { }); Prism.hooks.add('after-highlight', env => { - const links = env.element.querySelectorAll('.error-location, .stack-trace-location'); + const links = env.element.querySelectorAll('.error-location, .backtrace-location'); Array.from(links).forEach((link: HTMLAnchorElement) => { const { line, col } = link.dataset; link.onclick = e => { From 14c5051f65b502beb20ef98a4b5a2ed104b36ff8 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 22:58:47 -0400 Subject: [PATCH 8/9] Add a link to rerun with backtraces enabled --- ui/frontend/actions.ts | 5 +++++ ui/frontend/highlighting.ts | 16 +++++++++++++++- ui/frontend/index.tsx | 4 +++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ui/frontend/actions.ts b/ui/frontend/actions.ts index cfddce411..2002ab5cf 100644 --- a/ui/frontend/actions.ts +++ b/ui/frontend/actions.ts @@ -136,6 +136,11 @@ export const changeNightlyEdition = (edition: Edition): ThunkAction => dispatch export const changeBacktrace = (backtrace: Backtrace) => createAction(ActionType.ChangeBacktrace, { backtrace }); +export const reExecuteWithBacktrace = (): ThunkAction => dispatch => { + dispatch(changeBacktrace(Backtrace.Enabled)); + dispatch(performExecute()); +}; + export const changeFocus = focus => createAction(ActionType.ChangeFocus, { focus }); diff --git a/ui/frontend/highlighting.ts b/ui/frontend/highlighting.ts index ec808ff08..3c842f54b 100644 --- a/ui/frontend/highlighting.ts +++ b/ui/frontend/highlighting.ts @@ -1,6 +1,6 @@ import Prism from 'prismjs'; -export function configureRustErrors({ gotoPosition, getChannel }) { +export function configureRustErrors({ gotoPosition, reExecuteWithBacktrace, getChannel }) { Prism.languages.rust_errors = { // eslint-disable-line camelcase 'warning': /^warning:.*$/m, 'error': { @@ -17,6 +17,7 @@ export function configureRustErrors({ gotoPosition, getChannel }) { 'backtrace-location': /src\/main.rs:(\d+)/, }, }, + 'backtrace-enable': /Run with `RUST_BACKTRACE=1` for a backtrace/, }; Prism.hooks.add('wrap', env => { @@ -42,6 +43,11 @@ export function configureRustErrors({ gotoPosition, getChannel }) { env.attributes['data-line'] = line; env.attributes['data-col'] = col; } + if (env.type === 'backtrace-enable') { + env.tag = 'a'; + env.attributes.href = '#'; + env.attributes['data-backtrace-enable'] = 'true'; + } if (env.type === 'backtrace-location') { const errorMatch = /:(\d+)/.exec(env.content); const [_, line] = errorMatch; @@ -61,5 +67,13 @@ export function configureRustErrors({ gotoPosition, getChannel }) { gotoPosition(line, col); }; }); + + const backtraceEnablers = env.element.querySelectorAll('.backtrace-enable'); + Array.from(backtraceEnablers).forEach((link: HTMLAnchorElement) => { + link.onclick = e => { + e.preventDefault(); + reExecuteWithBacktrace(); + }; + }); }); } diff --git a/ui/frontend/index.tsx b/ui/frontend/index.tsx index f974df92e..4ab36092d 100644 --- a/ui/frontend/index.tsx +++ b/ui/frontend/index.tsx @@ -8,13 +8,14 @@ import persistState from 'redux-localstorage'; import thunk, { ThunkDispatch } from 'redux-thunk'; import * as url from 'url'; -import { Action, gotoPosition, performCratesLoad, performVersionsLoad } from './actions'; +import { Action, gotoPosition, performCratesLoad, performVersionsLoad, reExecuteWithBacktrace } from './actions'; import { configureRustErrors } from './highlighting'; import { deserialize, serialize } from './local_storage'; import PageSwitcher from './PageSwitcher'; import playgroundApp from './reducers'; import { State } from './reducers'; import Router from './Router'; +import { Backtrace } from './types'; const baseUrl = url.resolve(window.location.href, '/'); @@ -31,6 +32,7 @@ const store = createStore(playgroundApp, initialState, enhancers); configureRustErrors({ gotoPosition: (line, col) => store.dispatch(gotoPosition(line, col)), + reExecuteWithBacktrace: () => store.dispatch(reExecuteWithBacktrace()), getChannel: () => store.getState().configuration.channel, }); From 0d18b6aecb7c480d6a41213e857dd62416af4a2d Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 23 Jul 2018 23:11:37 -0400 Subject: [PATCH 9/9] Restore the backtrace tests --- tests/spec/features/backtrace_spec.rb | 39 ++++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/tests/spec/features/backtrace_spec.rb b/tests/spec/features/backtrace_spec.rb index 76795dc47..11f906918 100644 --- a/tests/spec/features/backtrace_spec.rb +++ b/tests/spec/features/backtrace_spec.rb @@ -1,26 +1,45 @@ require 'spec_helper' require 'support/editor' +require 'support/playground_actions' RSpec.feature "A backtrace is shown for certain errors", type: :feature, js: true do - before { pending "Backtracing has a large performance penalty" } + include PlaygroundActions before do visit '/' editor.set(code) - within('.header') { click_on("Run") } end - scenario "a stack trace is shown" do - within('.output-stderr') do - expect(page).to have_content 'stack backtrace:' - expect(page).to have_content 'rust_begin_unwind' + context "backtraces are enabled" do + before do + in_advanced_options_menu { choose 'enabled' } + within('.header') { click_on("Run") } + end + + scenario "a backtrace is shown" do + within('.output-stderr') do + expect(page).to have_content 'stack backtrace:' + expect(page).to have_content 'rust_begin_unwind' + end + end + + scenario "filenames link to that line of code" do + within('.output-stderr') do + expect(page).to have_link('main.rs:2') + expect(page).to have_link('main.rs:6') + end end end - scenario "filenames link to that line of code" do - within('.output-stderr') do - expect(page).to have_link('main.rs:2') - expect(page).to have_link('main.rs:6') + context "backtraces are disabled" do + before do + within('.header') { click_on("Run") } + end + + scenario "the backtrace suggestion is a link" do + within('.output-stderr') do + expect(page).to have_link(text: /Run with .* a backtrace/) + end end end