Skip to content

Commit a576514

Browse files
committed
Introduce -Zterminal-urls to use OSC8 for error codes
Terminals supporting the OSC8 Hyperlink Extension can support inline anchors where the text is user defineable but clicking on it opens a browser to a specified URLs, just like `<a href="URL">` does in HTML. https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
1 parent a00e24d commit a576514

File tree

16 files changed

+99
-9
lines changed

16 files changed

+99
-9
lines changed

compiler/rustc_driver_impl/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
2323
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
2424
use rustc_data_structures::sync::SeqCst;
2525
use rustc_errors::registry::{InvalidErrorCode, Registry};
26-
use rustc_errors::{ErrorGuaranteed, PResult};
26+
use rustc_errors::{ErrorGuaranteed, PResult, TerminalUrl};
2727
use rustc_feature::find_gated_cfg;
2828
use rustc_hir::def_id::LOCAL_CRATE;
2929
use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
@@ -1192,6 +1192,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
11921192
None,
11931193
false,
11941194
false,
1195+
TerminalUrl::No,
11951196
));
11961197
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
11971198

compiler/rustc_errors/src/emitter.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::translation::{to_fluent_args, Translate};
1818
use crate::{
1919
diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
2020
FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
21-
SubstitutionHighlight, SuggestionStyle,
21+
SubstitutionHighlight, SuggestionStyle, TerminalUrl,
2222
};
2323
use rustc_lint_defs::pluralize;
2424

@@ -66,6 +66,7 @@ impl HumanReadableErrorType {
6666
diagnostic_width: Option<usize>,
6767
macro_backtrace: bool,
6868
track_diagnostics: bool,
69+
terminal_url: TerminalUrl,
6970
) -> EmitterWriter {
7071
let (short, color_config) = self.unzip();
7172
let color = color_config.suggests_using_colors();
@@ -80,6 +81,7 @@ impl HumanReadableErrorType {
8081
diagnostic_width,
8182
macro_backtrace,
8283
track_diagnostics,
84+
terminal_url,
8385
)
8486
}
8587
}
@@ -652,6 +654,7 @@ pub struct EmitterWriter {
652654

653655
macro_backtrace: bool,
654656
track_diagnostics: bool,
657+
terminal_url: TerminalUrl,
655658
}
656659

657660
#[derive(Debug)]
@@ -672,6 +675,7 @@ impl EmitterWriter {
672675
diagnostic_width: Option<usize>,
673676
macro_backtrace: bool,
674677
track_diagnostics: bool,
678+
terminal_url: TerminalUrl,
675679
) -> EmitterWriter {
676680
let dst = Destination::from_stderr(color_config);
677681
EmitterWriter {
@@ -685,6 +689,7 @@ impl EmitterWriter {
685689
diagnostic_width,
686690
macro_backtrace,
687691
track_diagnostics,
692+
terminal_url,
688693
}
689694
}
690695

@@ -699,6 +704,7 @@ impl EmitterWriter {
699704
diagnostic_width: Option<usize>,
700705
macro_backtrace: bool,
701706
track_diagnostics: bool,
707+
terminal_url: TerminalUrl,
702708
) -> EmitterWriter {
703709
EmitterWriter {
704710
dst: Raw(dst, colored),
@@ -711,6 +717,7 @@ impl EmitterWriter {
711717
diagnostic_width,
712718
macro_backtrace,
713719
track_diagnostics,
720+
terminal_url,
714721
}
715722
}
716723

@@ -1378,7 +1385,13 @@ impl EmitterWriter {
13781385
// only render error codes, not lint codes
13791386
if let Some(DiagnosticId::Error(ref code)) = *code {
13801387
buffer.append(0, "[", Style::Level(*level));
1381-
buffer.append(0, code, Style::Level(*level));
1388+
let code = if let TerminalUrl::Yes = self.terminal_url {
1389+
let path = "https://doc.rust-lang.org/error_codes";
1390+
format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07")
1391+
} else {
1392+
code.clone()
1393+
};
1394+
buffer.append(0, &code, Style::Level(*level));
13821395
buffer.append(0, "]", Style::Level(*level));
13831396
label_width += 2 + code.len();
13841397
}

compiler/rustc_errors/src/json.rs

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::translation::{to_fluent_args, Translate};
1717
use crate::DiagnosticId;
1818
use crate::{
1919
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
20+
TerminalUrl,
2021
};
2122
use rustc_lint_defs::Applicability;
2223

@@ -47,6 +48,7 @@ pub struct JsonEmitter {
4748
diagnostic_width: Option<usize>,
4849
macro_backtrace: bool,
4950
track_diagnostics: bool,
51+
terminal_url: TerminalUrl,
5052
}
5153

5254
impl JsonEmitter {
@@ -60,6 +62,7 @@ impl JsonEmitter {
6062
diagnostic_width: Option<usize>,
6163
macro_backtrace: bool,
6264
track_diagnostics: bool,
65+
terminal_url: TerminalUrl,
6366
) -> JsonEmitter {
6467
JsonEmitter {
6568
dst: Box::new(io::BufWriter::new(io::stderr())),
@@ -73,6 +76,7 @@ impl JsonEmitter {
7376
diagnostic_width,
7477
macro_backtrace,
7578
track_diagnostics,
79+
terminal_url,
7680
}
7781
}
7882

@@ -84,6 +88,7 @@ impl JsonEmitter {
8488
diagnostic_width: Option<usize>,
8589
macro_backtrace: bool,
8690
track_diagnostics: bool,
91+
terminal_url: TerminalUrl,
8792
) -> JsonEmitter {
8893
let file_path_mapping = FilePathMapping::empty();
8994
JsonEmitter::stderr(
@@ -96,6 +101,7 @@ impl JsonEmitter {
96101
diagnostic_width,
97102
macro_backtrace,
98103
track_diagnostics,
104+
terminal_url,
99105
)
100106
}
101107

@@ -110,6 +116,7 @@ impl JsonEmitter {
110116
diagnostic_width: Option<usize>,
111117
macro_backtrace: bool,
112118
track_diagnostics: bool,
119+
terminal_url: TerminalUrl,
113120
) -> JsonEmitter {
114121
JsonEmitter {
115122
dst,
@@ -123,6 +130,7 @@ impl JsonEmitter {
123130
diagnostic_width,
124131
macro_backtrace,
125132
track_diagnostics,
133+
terminal_url,
126134
}
127135
}
128136

@@ -360,6 +368,7 @@ impl Diagnostic {
360368
je.diagnostic_width,
361369
je.macro_backtrace,
362370
je.track_diagnostics,
371+
je.terminal_url,
363372
)
364373
.ui_testing(je.ui_testing)
365374
.emit_diagnostic(diag);

compiler/rustc_errors/src/json/tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::json::JsonEmitter;
44
use rustc_span::source_map::{FilePathMapping, SourceMap};
55

66
use crate::emitter::{ColorConfig, HumanReadableErrorType};
7-
use crate::Handler;
7+
use crate::{Handler, TerminalUrl};
88
use rustc_span::{BytePos, Span};
99

1010
use std::str;
@@ -60,6 +60,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
6060
None,
6161
false,
6262
false,
63+
TerminalUrl::No,
6364
);
6465

6566
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));

compiler/rustc_errors/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ impl Handler {
573573
None,
574574
flags.macro_backtrace,
575575
flags.track_diagnostics,
576+
TerminalUrl::No,
576577
));
577578
Self::with_emitter_and_flags(emitter, flags)
578579
}
@@ -1838,6 +1839,13 @@ pub fn add_elided_lifetime_in_path_suggestion(
18381839
);
18391840
}
18401841

1842+
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
1843+
pub enum TerminalUrl {
1844+
No,
1845+
Yes,
1846+
Auto,
1847+
}
1848+
18411849
/// Useful type to use with `Result<>` indicate that an error has already
18421850
/// been reported to the user, so no need to continue checking.
18431851
#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)]

compiler/rustc_expand/src/tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_span::{BytePos, Span};
88

99
use rustc_data_structures::sync::Lrc;
1010
use rustc_errors::emitter::EmitterWriter;
11-
use rustc_errors::{Handler, MultiSpan, PResult};
11+
use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl};
1212

1313
use std::io;
1414
use std::io::prelude::*;
@@ -152,6 +152,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
152152
None,
153153
false,
154154
false,
155+
TerminalUrl::No,
155156
);
156157
let handler = Handler::with_emitter(true, None, Box::new(emitter));
157158
#[allow(rustc::untranslatable_diagnostic)]

compiler/rustc_session/src/options.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::early_error;
44
use crate::lint;
55
use crate::search_paths::SearchPath;
66
use crate::utils::NativeLib;
7-
use rustc_errors::LanguageIdentifier;
7+
use rustc_errors::{LanguageIdentifier, TerminalUrl};
88
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
99
use rustc_target::spec::{
1010
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
@@ -399,6 +399,8 @@ mod desc {
399399
pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)";
400400
pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)";
401401
pub const parse_target_feature: &str = parse_string;
402+
pub const parse_terminal_url: &str =
403+
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`";
402404
pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
403405
pub const parse_split_debuginfo: &str =
404406
"one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)";
@@ -979,6 +981,16 @@ mod parse {
979981
true
980982
}
981983

984+
pub(crate) fn parse_terminal_url(slot: &mut TerminalUrl, v: Option<&str>) -> bool {
985+
*slot = match v {
986+
Some("on" | "" | "yes" | "y") | None => TerminalUrl::Yes,
987+
Some("off" | "no" | "n") => TerminalUrl::No,
988+
Some("auto") => TerminalUrl::Auto,
989+
_ => return false,
990+
};
991+
true
992+
}
993+
982994
pub(crate) fn parse_symbol_mangling_version(
983995
slot: &mut Option<SymbolManglingVersion>,
984996
v: Option<&str>,
@@ -1602,6 +1614,8 @@ options! {
16021614
"show extended diagnostic help (default: no)"),
16031615
temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
16041616
"the directory the intermediate files are written to"),
1617+
terminal_urls: TerminalUrl = (TerminalUrl::No, parse_terminal_url, [UNTRACKED],
1618+
"use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output"),
16051619
#[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")]
16061620
thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
16071621
"enable ThinLTO when possible"),

compiler/rustc_session/src/session.rs

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_errors::registry::Registry;
2424
use rustc_errors::{
2525
error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
2626
ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
27+
TerminalUrl,
2728
};
2829
use rustc_macros::HashStable_Generic;
2930
pub use rustc_span::def_id::StableCrateId;
@@ -1273,6 +1274,19 @@ fn default_emitter(
12731274
) -> Box<dyn Emitter + sync::Send> {
12741275
let macro_backtrace = sopts.unstable_opts.macro_backtrace;
12751276
let track_diagnostics = sopts.unstable_opts.track_diagnostics;
1277+
let terminal_url = match sopts.unstable_opts.terminal_urls {
1278+
TerminalUrl::Auto => {
1279+
match (std::env::var("COLORTERM").as_deref(), std::env::var("TERM").as_deref()) {
1280+
(Ok("truecolor"), Ok("xterm-256color"))
1281+
if sopts.unstable_features.is_nightly_build() =>
1282+
{
1283+
TerminalUrl::Yes
1284+
}
1285+
_ => TerminalUrl::No,
1286+
}
1287+
}
1288+
t => t,
1289+
};
12761290
match sopts.error_format {
12771291
config::ErrorOutputType::HumanReadable(kind) => {
12781292
let (short, color_config) = kind.unzip();
@@ -1297,6 +1311,7 @@ fn default_emitter(
12971311
sopts.diagnostic_width,
12981312
macro_backtrace,
12991313
track_diagnostics,
1314+
terminal_url,
13001315
);
13011316
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
13021317
}
@@ -1312,6 +1327,7 @@ fn default_emitter(
13121327
sopts.diagnostic_width,
13131328
macro_backtrace,
13141329
track_diagnostics,
1330+
terminal_url,
13151331
)
13161332
.ui_testing(sopts.unstable_opts.ui_testing),
13171333
),
@@ -1624,6 +1640,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
16241640
None,
16251641
false,
16261642
false,
1643+
TerminalUrl::No,
16271644
))
16281645
}
16291646
config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic(
@@ -1634,6 +1651,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
16341651
None,
16351652
false,
16361653
false,
1654+
TerminalUrl::No,
16371655
)),
16381656
};
16391657
rustc_errors::Handler::with_emitter(true, None, emitter)

src/librustdoc/core.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_data_structures::sync::{self, Lrc};
44
use rustc_data_structures::unord::UnordSet;
55
use rustc_errors::emitter::{Emitter, EmitterWriter};
66
use rustc_errors::json::JsonEmitter;
7+
use rustc_errors::TerminalUrl;
78
use rustc_feature::UnstableFeatures;
89
use rustc_hir::def::{Namespace, Res};
910
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@@ -164,6 +165,7 @@ pub(crate) fn new_handler(
164165
diagnostic_width,
165166
false,
166167
unstable_opts.track_diagnostics,
168+
TerminalUrl::No,
167169
)
168170
.ui_testing(unstable_opts.ui_testing),
169171
)
@@ -183,6 +185,7 @@ pub(crate) fn new_handler(
183185
diagnostic_width,
184186
false,
185187
unstable_opts.track_diagnostics,
188+
TerminalUrl::No,
186189
)
187190
.ui_testing(unstable_opts.ui_testing),
188191
)

src/librustdoc/doctest.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_ast as ast;
22
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
33
use rustc_data_structures::sync::Lrc;
4-
use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
4+
use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl};
55
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
66
use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
77
use rustc_interface::interface;
@@ -557,6 +557,7 @@ pub(crate) fn make_test(
557557
Some(80),
558558
false,
559559
false,
560+
TerminalUrl::No,
560561
)
561562
.supports_color();
562563

@@ -571,6 +572,7 @@ pub(crate) fn make_test(
571572
None,
572573
false,
573574
false,
575+
TerminalUrl::No,
574576
);
575577

576578
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
@@ -756,6 +758,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
756758
None,
757759
false,
758760
false,
761+
TerminalUrl::No,
759762
);
760763

761764
let handler = Handler::with_emitter(false, None, Box::new(emitter));

0 commit comments

Comments
 (0)