diff --git a/Cargo.lock b/Cargo.lock index 561f29abec4a9..5840ed01796d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2511,6 +2511,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "html-checker" version = "0.1.0" @@ -3066,6 +3077,12 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -5295,6 +5312,7 @@ name = "rustc_span" version = "0.0.0" dependencies = [ "cfg-if", + "hostname", "indexmap", "md-5", "rustc_arena", diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 44b9c4718a75f..67109d72a0c80 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -400,7 +400,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, item: &ast::Item) -> (Symbol, usize, usiz cx.sess.source_map().span_to_location_info(span); let file_name = match source_file { - Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(), + Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped, false).to_string(), None => "no-location".to_string(), }; diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 9872b3bda1e06..40d761f2f5b80 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -159,7 +159,7 @@ impl AnnotateSnippetEmitterWriter { } // owned: line source, line index, annotations type Owned = (String, usize, Vec); - let filename = source_map.filename_for_diagnostics(&primary_lo.file.name); + let filename = source_map.filename_for_diagnostics(&primary_lo.file.name, false); let origin = filename.to_string_lossy(); let annotated_files: Vec = annotated_files .into_iter() diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d6fd057c5a476..a11ee7591c559 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1456,7 +1456,10 @@ impl EmitterWriter { line_idx, &format!( "{}:{}:{}", - sm.filename_for_diagnostics(&annotated_file.file.name), + sm.filename_for_diagnostics( + &annotated_file.file.name, + self.terminal_url == TerminalUrl::Yes + ), sm.doctest_offset_line( &annotated_file.file.name, line.line_index @@ -1512,7 +1515,10 @@ impl EmitterWriter { buffer_msg_line_offset, &format!( "{}:{}:{}", - sm.filename_for_diagnostics(&loc.file.name), + sm.filename_for_diagnostics( + &loc.file.name, + self.terminal_url == TerminalUrl::Yes + ), sm.doctest_offset_line(&loc.file.name, loc.line), loc.col.0 + 1, ), @@ -1526,7 +1532,10 @@ impl EmitterWriter { 0, &format!( "{}:{}:{}: ", - sm.filename_for_diagnostics(&loc.file.name), + sm.filename_for_diagnostics( + &loc.file.name, + self.terminal_url == TerminalUrl::Yes + ), sm.doctest_offset_line(&loc.file.name, loc.line), loc.col.0 + 1, ), @@ -1554,12 +1563,21 @@ impl EmitterWriter { }; format!( "{}:{}{}", - sm.filename_for_diagnostics(&annotated_file.file.name), + sm.filename_for_diagnostics( + &annotated_file.file.name, + self.terminal_url == TerminalUrl::Yes + ), sm.doctest_offset_line(&annotated_file.file.name, first_line.line_index), col ) } else { - format!("{}", sm.filename_for_diagnostics(&annotated_file.file.name)) + format!( + "{}", + sm.filename_for_diagnostics( + &annotated_file.file.name, + self.terminal_url == TerminalUrl::Yes + ) + ) }; buffer.append(buffer_msg_line_offset + 1, &loc, Style::LineAndColumn); for _ in 0..max_line_num_len { @@ -1815,7 +1833,10 @@ impl EmitterWriter { if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() { let arrow = "--> "; buffer.puts(row_num - 1, 0, arrow, Style::LineNumber); - let filename = sm.filename_for_diagnostics(&loc.file.name); + let filename = sm.filename_for_diagnostics( + &loc.file.name, + self.terminal_url == TerminalUrl::Yes, + ); let offset = sm.doctest_offset_line(&loc.file.name, loc.line); let message = format!("{}:{}:{}", filename, offset, loc.col.0 + 1); if row_num == 2 { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index f32d6b96b9b24..2f5c06a5832cb 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -475,7 +475,7 @@ impl DiagnosticSpan { }); DiagnosticSpan { - file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), + file_name: je.sm.filename_for_diagnostics(&start.file.name, false).to_string(), byte_start: start.file.original_relative_byte_pos(span.lo()).0, byte_end: start.file.original_relative_byte_pos(span.hi()).0, line_start: start.line, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index caa2a201c758d..504d7bacfb42c 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1202,7 +1202,10 @@ pub fn resolve_path( other => { return Err(errors::ResolveRelativePath { span, - path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(), + path: parse_sess + .source_map() + .filename_for_diagnostics(&other, false) + .to_string(), } .into_diagnostic(&parse_sess.span_diagnostic)); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 75c3d9f641dda..91a2d49a777f4 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1760,7 +1760,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); format!( " (opaque type at <{}:{}:{}>)", - sm.filename_for_diagnostics(&pos.file.name), + sm.filename_for_diagnostics(&pos.file.name, false), pos.line, pos.col.to_usize() + 1, ) @@ -1772,7 +1772,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let pos = sm.lookup_char_pos(self.tcx.def_span(proj.def_id).lo()); format!( " (trait associated opaque type at <{}:{}:{}>)", - sm.filename_for_diagnostics(&pos.file.name), + sm.filename_for_diagnostics(&pos.file.name, false), pos.line, pos.col.to_usize() + 1, ) diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 17466cd0e6dce..d63dd00cc7783 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -192,7 +192,7 @@ pub fn maybe_file_to_stream( let src = source_file.src.as_ref().unwrap_or_else(|| { sess.span_diagnostic.bug(&format!( "cannot lex `source_file` without source: {}", - sess.source_map().filename_for_diagnostics(&source_file.name) + sess.source_map().filename_for_diagnostics(&source_file.name, false) )); }); diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index a7c7575f392e6..6e4831e35dccc 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -19,3 +19,4 @@ sha1 = "0.10.0" sha2 = "0.10.1" md5 = { package = "md-5", version = "0.10.0" } indexmap = { version = "1.9.3" } +hostname = "0.3.1" diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index e14760aa01885..757d398d8aaff 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -323,6 +323,38 @@ pub enum FileNameDisplayPreference { pub struct FileNameDisplay<'a> { inner: &'a FileName, display_pref: FileNameDisplayPreference, + link: bool, +} + +fn resolve_file_uri(path: &Path) -> Option { + fn uri_encode(str: &str) -> String { + let mut res = String::with_capacity(str.len()); + + for &x in str.as_bytes() { + // ascii characters are encoded directly. + // ':' and ';' are used as control sequences and so can't be used. + if (32..127).contains(&x) && x != ':' as u8 && x != ';' as u8 { + res.push(x as char) + } else { + res.push('%'); + res.push_str(&format!("{x:02x}")); + } + } + + res + } + + // Absolute paths are never excepted. + let canonical_path = path.canonicalize().ok()?; + // But we can get away without a hostname. + let hostname = hostname::get().unwrap_or_default(); + + let canonical_path = uri_encode(canonical_path.to_str()?); + // Sometimes the canonical path starts with a slash otherwise we need to add one. + let separator = if canonical_path.starts_with("/") { "" } else { "/" }; + let hostname = uri_encode(hostname.to_str()?); + + Some(format!("file://{hostname}{separator}{canonical_path}")) } impl fmt::Display for FileNameDisplay<'_> { @@ -330,7 +362,22 @@ impl fmt::Display for FileNameDisplay<'_> { use FileName::*; match *self.inner { Real(ref name) => { - write!(fmt, "{}", name.to_string_lossy(self.display_pref)) + let file_uri = if self.link && let Some(path) = name.local_path() { + resolve_file_uri(path) + } else { + None + }; + + if let Some(file_uri) = file_uri { + write!( + fmt, + "\x1b]8;;{}\x07{}\x1b]8;;\x07", + file_uri, + name.to_string_lossy(self.display_pref) + ) + } else { + write!(fmt, "{}", name.to_string_lossy(self.display_pref)) + } } QuoteExpansion(_) => write!(fmt, ""), MacroExpansion(_) => write!(fmt, ""), @@ -372,17 +419,25 @@ impl FileName { } pub fn prefer_remapped(&self) -> FileNameDisplay<'_> { - FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped } + FileNameDisplay { + inner: self, + display_pref: FileNameDisplayPreference::Remapped, + link: false, + } } /// This may include transient local filesystem information. /// Must not be embedded in build outputs. pub fn prefer_local(&self) -> FileNameDisplay<'_> { - FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local } + FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local, link: false } } - pub fn display(&self, display_pref: FileNameDisplayPreference) -> FileNameDisplay<'_> { - FileNameDisplay { inner: self, display_pref } + pub fn display( + &self, + display_pref: FileNameDisplayPreference, + link: bool, + ) -> FileNameDisplay<'_> { + FileNameDisplay { inner: self, display_pref, link } } pub fn macro_expansion_source_code(src: &str) -> FileName { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index ee895f53eba9b..4baba5e1a3af3 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -451,7 +451,7 @@ impl SourceMap { let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp); let file_name = match source_file { - Some(sf) => sf.name.display(filename_display_pref).to_string(), + Some(sf) => sf.name.display(filename_display_pref, false).to_string(), None => return "no-location".to_string(), }; @@ -502,7 +502,7 @@ impl SourceMap { format!( "{}:+{}:{}: +{}:{}", - lo.file.name.display(FileNameDisplayPreference::Remapped), + lo.file.name.display(FileNameDisplayPreference::Remapped, false), lo_line, lo.col.to_usize() + 1, hi_line, @@ -521,8 +521,12 @@ impl SourceMap { self.lookup_char_pos(sp.lo()).file.name.clone() } - pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { - filename.display(self.path_mapping.filename_display_for_diagnostics) + pub fn filename_for_diagnostics<'a>( + &self, + filename: &'a FileName, + link: bool, + ) -> FileNameDisplay<'a> { + filename.display(self.path_mapping.filename_display_for_diagnostics, link) } pub fn is_multiline(&self, sp: Span) -> bool { diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index a9eb6c8d03f72..4fa9a58084146 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -141,6 +141,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "gsgdt", "hashbrown", "hermit-abi", + "hostname", "icu_list", "icu_locid", "icu_provider", @@ -161,6 +162,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "lock_api", "log", "matchers", + "match_cfg", "md-5", "measureme", "memchr", diff --git a/tests/ui/diagnostic-flags/terminal_urls.stderr b/tests/ui/diagnostic-flags/terminal_urls.stderr index 7f7e69c5d5da7..71d0fd608d1ba 100644 --- a/tests/ui/diagnostic-flags/terminal_urls.stderr +++ b/tests/ui/diagnostic-flags/terminal_urls.stderr @@ -1,7 +1,7 @@ error[]8;;https://doc.rust-lang.org/error_codes/E0308.htmlE0308]8;;]: mismatched types - --> $DIR/terminal_urls.rs:3:9 + --> ]8;;file://$DIR/terminal_urls.rs$DIR/terminal_urls.rs]8;;:3:9 | -LL | let () = 4; + | let () = 4; | ^^ - this expression has type `{integer}` | | | expected integer, found `()`