about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs47
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs51
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs23
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_expand/src/base.rs41
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs53
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs1
-rw-r--r--compiler/rustc_middle/src/query/mod.rs6
-rw-r--r--compiler/rustc_passes/Cargo.toml1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs60
-rw-r--r--compiler/rustc_passes/src/debugger_visualizer.rs137
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_span/src/lib.rs23
-rw-r--r--compiler/rustc_span/src/symbol.rs2
19 files changed, 404 insertions, 76 deletions
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 2817fce463b..8bf3a0799b6 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -3,17 +3,15 @@ use rustc_ast::ptr::P;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
-use rustc_errors::PResult;
 use rustc_expand::base::{self, *};
 use rustc_expand::module::DirOwnership;
 use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_parse::{self, new_parser_from_file};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
 use rustc_span::symbol::Symbol;
-use rustc_span::{self, FileName, Pos, Span};
+use rustc_span::{self, Pos, Span};
 
 use smallvec::SmallVec;
-use std::path::PathBuf;
 use std::rc::Rc;
 
 // These macros all relate to the file system; they either return
@@ -104,7 +102,7 @@ pub fn expand_include<'cx>(
         return DummyResult::any(sp);
     };
     // The file will be added to the code map by the parser
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -176,7 +174,7 @@ pub fn expand_include_str(
     let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
         return DummyResult::any(sp);
     };
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -210,7 +208,7 @@ pub fn expand_include_bytes(
     let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
         return DummyResult::any(sp);
     };
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -225,40 +223,3 @@ pub fn expand_include_bytes(
         }
     }
 }
-
-/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
-///
-/// This unifies the logic used for resolving `include_X!`.
-fn resolve_path<'a>(
-    cx: &mut ExtCtxt<'a>,
-    path: impl Into<PathBuf>,
-    span: Span,
-) -> PResult<'a, PathBuf> {
-    let path = path.into();
-
-    // Relative paths are resolved relative to the file in which they are found
-    // after macro expansion (that is, they are unhygienic).
-    if !path.is_absolute() {
-        let callsite = span.source_callsite();
-        let mut result = match cx.source_map().span_to_filename(callsite) {
-            FileName::Real(name) => name
-                .into_local_path()
-                .expect("attempting to resolve a file path in an external file"),
-            FileName::DocTest(path, _) => path,
-            other => {
-                return Err(cx.struct_span_err(
-                    span,
-                    &format!(
-                        "cannot resolve relative path in non-file source `{}`",
-                        cx.source_map().filename_for_diagnostics(&other)
-                    ),
-                ));
-            }
-        };
-        result.pop();
-        result.push(path);
-        Ok(result)
-    } else {
-        Ok(path)
-    }
-}
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 6887f0666a4..04ec1e7f3c1 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use rustc_hir::def_id::CrateNum;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
@@ -2099,8 +2099,14 @@ fn add_order_independent_options(
     // Pass optimization flags down to the linker.
     cmd.optimize();
 
+    let debugger_visualizer_paths = if sess.target.is_like_msvc {
+        collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
+    } else {
+        Vec::new()
+    };
+
     // Pass debuginfo and strip flags down to the linker.
-    cmd.debuginfo(strip_value(sess));
+    cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
 
     // We want to prevent the compiler from accidentally leaking in any system libraries,
     // so by default we tell linkers not to link to any default libraries.
@@ -2119,6 +2125,47 @@ fn add_order_independent_options(
     add_rpath_args(cmd, sess, codegen_results, out_filename);
 }
 
+// Write the debugger visualizer files for each crate to the temp directory and gather the file paths.
+fn collect_debugger_visualizers(
+    tmpdir: &Path,
+    sess: &Session,
+    crate_info: &CrateInfo,
+) -> Vec<PathBuf> {
+    let mut visualizer_paths = Vec::new();
+    let debugger_visualizers = &crate_info.debugger_visualizers;
+    let mut index = 0;
+
+    for (&cnum, visualizers) in debugger_visualizers {
+        let crate_name = if cnum == LOCAL_CRATE {
+            crate_info.local_crate_name.as_str()
+        } else {
+            crate_info.crate_name[&cnum].as_str()
+        };
+
+        for visualizer in visualizers {
+            let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index));
+
+            match fs::write(&visualizer_out_file, &visualizer.src) {
+                Ok(()) => {
+                    visualizer_paths.push(visualizer_out_file.clone());
+                    index += 1;
+                }
+                Err(error) => {
+                    sess.warn(
+                        format!(
+                            "Unable to write debugger visualizer file `{}`: {} ",
+                            visualizer_out_file.display(),
+                            error
+                        )
+                        .as_str(),
+                    );
+                }
+            };
+        }
+    }
+    visualizer_paths
+}
+
 /// # Native library linking
 ///
 /// User-supplied library search paths (-L on the command line). These are the same paths used to
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 50db2c22ae6..2a71377d2f1 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -183,7 +183,7 @@ pub trait Linker {
     fn optimize(&mut self);
     fn pgo_gen(&mut self);
     fn control_flow_guard(&mut self);
-    fn debuginfo(&mut self, strip: Strip);
+    fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]);
     fn no_crt_objects(&mut self);
     fn no_default_libraries(&mut self);
     fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
@@ -611,7 +611,7 @@ impl<'a> Linker for GccLinker<'a> {
 
     fn control_flow_guard(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         // MacOS linker doesn't support stripping symbols directly anymore.
         if self.sess.target.is_like_osx {
             return;
@@ -915,7 +915,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         self.cmd.arg("/guard:cf");
     }
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
         match strip {
             Strip::None => {
                 // This will cause the Microsoft linker to generate a PDB file
@@ -942,6 +942,13 @@ impl<'a> Linker for MsvcLinker<'a> {
                         }
                     }
                 }
+
+                // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
+                for path in debugger_visualizers {
+                    let mut arg = OsString::from("/NATVIS:");
+                    arg.push(path);
+                    self.cmd.arg(arg);
+                }
             }
             Strip::Debuginfo | Strip::Symbols => {
                 self.cmd.arg("/DEBUG:NONE");
@@ -1124,7 +1131,7 @@ impl<'a> Linker for EmLinker<'a> {
 
     fn control_flow_guard(&mut self) {}
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         // Preserve names or generate source maps depending on debug info
         self.cmd.arg(match self.sess.opts.debuginfo {
             DebugInfo::None => "-g0",
@@ -1315,7 +1322,7 @@ impl<'a> Linker for WasmLd<'a> {
 
     fn pgo_gen(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
@@ -1450,7 +1457,7 @@ impl<'a> Linker for L4Bender<'a> {
 
     fn pgo_gen(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
@@ -1600,7 +1607,7 @@ impl<'a> Linker for PtxLinker<'a> {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         self.cmd.arg("--debug");
     }
 
@@ -1699,7 +1706,7 @@ impl<'a> Linker for BpfLinker<'a> {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         self.cmd.arg("--debug");
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 5bc95614c19..7b7e09208a2 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -847,7 +847,13 @@ impl CrateInfo {
             missing_lang_items: Default::default(),
             dependency_formats: tcx.dependency_formats(()).clone(),
             windows_subsystem,
+            debugger_visualizers: Default::default(),
         };
+        let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone();
+        if !debugger_visualizers.is_empty() {
+            info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers);
+        }
+
         let lang_items = tcx.lang_items();
 
         let crates = tcx.crates(());
@@ -862,7 +868,9 @@ impl CrateInfo {
             info.native_libraries
                 .insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
             info.crate_name.insert(cnum, tcx.crate_name(cnum));
-            info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());
+
+            let used_crate_source = tcx.used_crate_source(cnum);
+            info.used_crate_source.insert(cnum, used_crate_source.clone());
             if tcx.is_compiler_builtins(cnum) {
                 info.compiler_builtins = Some(cnum);
             }
@@ -883,6 +891,14 @@ impl CrateInfo {
             let missing =
                 missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
             info.missing_lang_items.insert(cnum, missing);
+
+            // Only include debugger visualizer files from crates that will be statically linked.
+            if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
+                let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
+                if !debugger_visualizers.is_empty() {
+                    info.debugger_visualizers.insert(cnum, debugger_visualizers);
+                }
+            }
         }
 
         info
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 05d32972dab..d6979bb625c 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -36,6 +36,7 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT
 use rustc_session::cstore::{self, CrateSource};
 use rustc_session::utils::NativeLibKind;
 use rustc_span::symbol::Symbol;
+use rustc_span::DebuggerVisualizerFile;
 use std::path::{Path, PathBuf};
 
 pub mod back;
@@ -157,6 +158,7 @@ pub struct CrateInfo {
     pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
     pub dependency_formats: Lrc<Dependencies>,
     pub windows_subsystem: Option<String>,
+    pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
 }
 
 #[derive(Encodable, Decodable)]
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index ae1b50a4176..2b30ec601a0 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
@@ -20,7 +20,7 @@ use rustc_span::edition::Edition;
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{FileName, Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
 use std::default::Default;
@@ -1136,6 +1136,43 @@ impl<'a> ExtCtxt<'a> {
     }
 }
 
+/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
+///
+/// This unifies the logic used for resolving `include_X!`.
+pub fn resolve_path(
+    parse_sess: &ParseSess,
+    path: impl Into<PathBuf>,
+    span: Span,
+) -> PResult<'_, PathBuf> {
+    let path = path.into();
+
+    // Relative paths are resolved relative to the file in which they are found
+    // after macro expansion (that is, they are unhygienic).
+    if !path.is_absolute() {
+        let callsite = span.source_callsite();
+        let mut result = match parse_sess.source_map().span_to_filename(callsite) {
+            FileName::Real(name) => name
+                .into_local_path()
+                .expect("attempting to resolve a file path in an external file"),
+            FileName::DocTest(path, _) => path,
+            other => {
+                return Err(parse_sess.span_diagnostic.struct_span_err(
+                    span,
+                    &format!(
+                        "cannot resolve relative path in non-file source `{}`",
+                        parse_sess.source_map().filename_for_diagnostics(&other)
+                    ),
+                ));
+            }
+        };
+        result.pop();
+        result.push(path);
+        Ok(result)
+    } else {
+        Ok(path)
+    }
+}
+
 /// Extracts a string literal from the macro expanded version of `expr`,
 /// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
 /// The returned bool indicates whether an applicable suggestion has already been
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 9159d60463c..5c07d9121cc 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -358,6 +358,8 @@ declare_features! (
     (active, custom_inner_attributes, "1.30.0", Some(54726), None),
     /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
     (active, custom_test_frameworks, "1.30.0", Some(50297), None),
+    /// Allows using `#[debugger_visualizer]`.
+    (active, debugger_visualizer, "1.62.0", Some(95939), None),
     /// Allows declarative macros 2.0 (`macro`).
     (active, decl_macro, "1.17.0", Some(39412), None),
     /// Allows rustc to inject a default alloc_error_handler
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index e588385cfca..bdbda8bf20c 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -379,6 +379,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Unstable attributes:
     // ==========================================================================
 
+    // RFC #3191: #[debugger_visualizer] support
+    gated!(
+        debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
+        DuplicatesOk, experimental!(debugger_visualizer)
+    ),
+
     // Linking:
     gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
     gated!(
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 1edb62e189f..b25522cfd96 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1023,6 +1023,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
         self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess))
     }
 
+    fn get_debugger_visualizers(self) -> Vec<rustc_span::DebuggerVisualizerFile> {
+        self.root.debugger_visualizers.decode(self).collect::<Vec<_>>()
+    }
+
     /// Iterates over all the stability attributes in the given crate.
     fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] {
         tcx.arena.alloc_from_iter(self.root.lib_features.decode(self))
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index da1dd6af65a..c00c6ce2f71 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -233,6 +233,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     }
 
     used_crate_source => { Lrc::clone(&cdata.source) }
+    debugger_visualizers => { cdata.get_debugger_visualizers() }
 
     exported_symbols => {
         let syms = cdata.exported_symbols(tcx);
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index b46ea955a3a..b2eafa035db 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -35,7 +35,9 @@ use rustc_serialize::{opaque, Encodable, Encoder};
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext};
+use rustc_span::{
+    self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext,
+};
 use rustc_span::{
     hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind},
     RealFileName,
@@ -672,6 +674,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let tables = self.tables.encode(&mut self.opaque);
         let tables_bytes = self.position() - i;
 
+        i = self.position();
+        let debugger_visualizers = self.encode_debugger_visualizers();
+        let debugger_visualizers_bytes = self.position() - i;
+
         // Encode exported symbols info. This is prefetched in `encode_metadata` so we encode
         // this as late as possible to give the prefetching as much time as possible to complete.
         i = self.position();
@@ -717,6 +723,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
             has_default_lib_allocator,
             proc_macro_data,
+            debugger_visualizers,
             compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins),
             needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator),
             needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime),
@@ -757,25 +764,26 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
 
             eprintln!("metadata stats:");
-            eprintln!("             dep bytes: {}", dep_bytes);
-            eprintln!("     lib feature bytes: {}", lib_feature_bytes);
-            eprintln!("       lang item bytes: {}", lang_item_bytes);
-            eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes);
-            eprintln!("          native bytes: {}", native_lib_bytes);
-            eprintln!("      source_map bytes: {}", source_map_bytes);
-            eprintln!("          traits bytes: {}", traits_bytes);
-            eprintln!("           impls bytes: {}", impls_bytes);
-            eprintln!("incoherent_impls bytes: {}", incoherent_impls_bytes);
-            eprintln!("    exp. symbols bytes: {}", exported_symbols_bytes);
-            eprintln!("  def-path table bytes: {}", def_path_table_bytes);
-            eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes);
-            eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
-            eprintln!("             mir bytes: {}", mir_bytes);
-            eprintln!("            item bytes: {}", item_bytes);
-            eprintln!("           table bytes: {}", tables_bytes);
-            eprintln!("         hygiene bytes: {}", hygiene_bytes);
-            eprintln!("            zero bytes: {}", zero_bytes);
-            eprintln!("           total bytes: {}", total_bytes);
+            eprintln!("                  dep bytes: {}", dep_bytes);
+            eprintln!("          lib feature bytes: {}", lib_feature_bytes);
+            eprintln!("            lang item bytes: {}", lang_item_bytes);
+            eprintln!("      diagnostic item bytes: {}", diagnostic_item_bytes);
+            eprintln!("               native bytes: {}", native_lib_bytes);
+            eprintln!(" debugger visualizers bytes: {}", debugger_visualizers_bytes);
+            eprintln!("           source_map bytes: {}", source_map_bytes);
+            eprintln!("               traits bytes: {}", traits_bytes);
+            eprintln!("                impls bytes: {}", impls_bytes);
+            eprintln!("     incoherent_impls bytes: {}", incoherent_impls_bytes);
+            eprintln!("         exp. symbols bytes: {}", exported_symbols_bytes);
+            eprintln!("       def-path table bytes: {}", def_path_table_bytes);
+            eprintln!("      def-path hashes bytes: {}", def_path_hash_map_bytes);
+            eprintln!("      proc-macro-data-bytes: {}", proc_macro_data_bytes);
+            eprintln!("                  mir bytes: {}", mir_bytes);
+            eprintln!("                 item bytes: {}", item_bytes);
+            eprintln!("                table bytes: {}", tables_bytes);
+            eprintln!("              hygiene bytes: {}", hygiene_bytes);
+            eprintln!("                 zero bytes: {}", zero_bytes);
+            eprintln!("                total bytes: {}", total_bytes);
         }
 
         root
@@ -1716,6 +1724,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
     }
 
+    fn encode_debugger_visualizers(&mut self) -> Lazy<[DebuggerVisualizerFile]> {
+        empty_proc_macro!(self);
+        self.lazy(self.tcx.debugger_visualizers(LOCAL_CRATE).iter())
+    }
+
     fn encode_crate_deps(&mut self) -> Lazy<[CrateDep]> {
         empty_proc_macro!(self);
 
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index cdbed90e6b9..a0fd9ef4f87 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -219,6 +219,7 @@ crate struct CrateRoot<'tcx> {
     proc_macro_data: Option<ProcMacroData>,
 
     tables: LazyTables<'tcx>,
+    debugger_visualizers: Lazy<[rustc_span::DebuggerVisualizerFile]>,
 
     exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]),
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index cc80ab8f16e..e439d128dbc 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1628,6 +1628,12 @@ rustc_queries! {
         desc { "looking at the source for a crate" }
         separate_provide_extern
     }
+    /// Returns the debugger visualizers defined for this crate.
+    query debugger_visualizers(_: CrateNum) -> Vec<rustc_span::DebuggerVisualizerFile> {
+        storage(ArenaCacheSelector<'tcx>)
+        desc { "looking up the debugger visualizers for this crate" }
+        separate_provide_extern
+    }
     query postorder_cnums(_: ()) -> &'tcx [CrateNum] {
         eval_always
         desc { "generating a postorder list of CrateNums" }
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index 39e578bce7e..a3ef1981e84 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -9,6 +9,7 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_expand = { path = "../rustc_expand" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_parse = { path = "../rustc_parse" }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index a9444972130..fd7e2901ee2 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -100,6 +100,7 @@ impl CheckAttrVisitor<'_> {
                 sym::allow_internal_unstable => {
                     self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
                 }
+                sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
                 sym::rustc_allow_const_fn_unstable => {
                     self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
                 }
@@ -1860,6 +1861,65 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
+    /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
+    fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
+        match target {
+            Target::Mod => {}
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "attribute should be applied to a module")
+                    .emit();
+                return false;
+            }
+        }
+
+        let hints = match attr.meta_item_list() {
+            Some(meta_item_list) => meta_item_list,
+            None => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        let hint = match hints.len() {
+            1 => &hints[0],
+            _ => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        if !hint.has_name(sym::natvis_file) {
+            self.emit_debugger_visualizer_err(attr);
+            return false;
+        }
+
+        let meta_item = match hint.meta_item() {
+            Some(meta_item) => meta_item,
+            None => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        match (meta_item.name_or_empty(), meta_item.value_str()) {
+            (sym::natvis_file, Some(_)) => true,
+            (_, _) => {
+                self.emit_debugger_visualizer_err(attr);
+                false
+            }
+        }
+    }
+
+    fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
+        self.tcx
+            .sess
+            .struct_span_err(attr.span, "invalid argument")
+            .note(r#"expected: `natvis_file = "..."`"#)
+            .emit();
+    }
+
     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
     /// (Allows proc_macro functions)
     fn check_rustc_allow_const_fn_unstable(
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
new file mode 100644
index 00000000000..09a73cc282e
--- /dev/null
+++ b/compiler/rustc_passes/src/debugger_visualizer.rs
@@ -0,0 +1,137 @@
+//! Detecting usage of the #[debugger_visualizer] attribute.
+
+use hir::CRATE_HIR_ID;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_expand::base::resolve_path;
+use rustc_hir as hir;
+use rustc_hir::def_id::CrateNum;
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_hir::{HirId, Target};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
+
+use std::sync::Arc;
+
+struct DebuggerVisualizerCollector<'tcx> {
+    debugger_visualizers: FxHashSet<DebuggerVisualizerFile>,
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> {
+    fn visit_item(&mut self, item: &hir::Item<'_>) {
+        let target = Target::from_item(item);
+        match target {
+            Target::Mod => {
+                self.check_for_debugger_visualizer(item.hir_id());
+            }
+            _ => {}
+        }
+    }
+
+    fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {}
+
+    fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {}
+
+    fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
+}
+
+impl<'tcx> DebuggerVisualizerCollector<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> {
+        DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() }
+    }
+
+    fn check_for_debugger_visualizer(&mut self, hir_id: HirId) {
+        let attrs = self.tcx.hir().attrs(hir_id);
+        for attr in attrs {
+            if attr.has_name(sym::debugger_visualizer) {
+                let list = match attr.meta_item_list() {
+                    Some(list) => list,
+                    _ => continue,
+                };
+
+                let meta_item = match list.len() {
+                    1 => match list[0].meta_item() {
+                        Some(meta_item) => meta_item,
+                        _ => continue,
+                    },
+                    _ => continue,
+                };
+
+                let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
+                    (sym::natvis_file, Some(value)) => {
+                        match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) {
+                            Ok(file) => file,
+                            Err(mut err) => {
+                                err.emit();
+                                continue;
+                            }
+                        }
+                    }
+                    (_, _) => continue,
+                };
+
+                if file.is_file() {
+                    let contents = match std::fs::read(&file) {
+                        Ok(contents) => contents,
+                        Err(err) => {
+                            self.tcx
+                                .sess
+                                .struct_span_err(
+                                    attr.span,
+                                    &format!(
+                                        "Unable to read contents of file `{}`. {}",
+                                        file.display(),
+                                        err
+                                    ),
+                                )
+                                .emit();
+                            continue;
+                        }
+                    };
+
+                    self.debugger_visualizers.insert(DebuggerVisualizerFile::new(
+                        Arc::from(contents),
+                        DebuggerVisualizerType::Natvis,
+                    ));
+                } else {
+                    self.tcx
+                        .sess
+                        .struct_span_err(
+                            attr.span,
+                            &format!("{} is not a valid file", file.display()),
+                        )
+                        .emit();
+                }
+            }
+        }
+    }
+}
+
+/// Traverses and collects the debugger visualizers for a specific crate.
+fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> {
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    // Initialize the collector.
+    let mut collector = DebuggerVisualizerCollector::new(tcx);
+
+    // Collect debugger visualizers in this crate.
+    tcx.hir().visit_all_item_likes(&mut collector);
+
+    // Collect debugger visualizers on the crate attributes.
+    collector.check_for_debugger_visualizer(CRATE_HIR_ID);
+
+    // Extract out the found debugger_visualizer items.
+    let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector;
+
+    let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>();
+
+    // Sort the visualizers so we always get a deterministic query result.
+    visualizers.sort();
+    visualizers
+}
+
+pub fn provide(providers: &mut Providers) {
+    providers.debugger_visualizers = debugger_visualizers;
+}
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 71d49d8b7ea..d9d08488d28 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -26,6 +26,7 @@ use rustc_middle::ty::query::Providers;
 mod check_attr;
 mod check_const;
 pub mod dead;
+mod debugger_visualizer;
 mod diagnostic_items;
 pub mod entry;
 pub mod hir_id_validator;
@@ -47,6 +48,7 @@ pub fn provide(providers: &mut Providers) {
     check_attr::provide(providers);
     check_const::provide(providers);
     dead::provide(providers);
+    debugger_visualizer::provide(providers);
     diagnostic_items::provide(providers);
     entry::provide(providers);
     lang_items::provide(providers);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f22faef2580..14668a3d411 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -71,6 +71,7 @@ use std::hash::Hash;
 use std::ops::{Add, Range, Sub};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
+use std::sync::Arc;
 
 use md5::Digest;
 use md5::Md5;
@@ -1200,6 +1201,28 @@ impl SourceFileHash {
     }
 }
 
+#[derive(HashStable_Generic)]
+#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
+pub enum DebuggerVisualizerType {
+    Natvis,
+}
+
+/// A single debugger visualizer file.
+#[derive(HashStable_Generic)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
+pub struct DebuggerVisualizerFile {
+    /// The complete debugger visualizer source.
+    pub src: Arc<[u8]>,
+    /// Indicates which visualizer type this targets.
+    pub visualizer_type: DebuggerVisualizerType,
+}
+
+impl DebuggerVisualizerFile {
+    pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType) -> Self {
+        DebuggerVisualizerFile { src, visualizer_type }
+    }
+}
+
 /// A single source in the [`SourceMap`].
 #[derive(Clone)]
 pub struct SourceFile {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c1299c94c4b..e7149f4a672 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -556,6 +556,7 @@ symbols! {
         debug_struct,
         debug_trait_builder,
         debug_tuple,
+        debugger_visualizer,
         decl_macro,
         declare_lint_pass,
         decode,
@@ -927,6 +928,7 @@ symbols! {
         native_link_modifiers_bundle,
         native_link_modifiers_verbatim,
         native_link_modifiers_whole_archive,
+        natvis_file,
         ne,
         nearbyintf32,
         nearbyintf64,