about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs19
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs8
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs112
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs16
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs33
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs22
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs37
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs2
-rw-r--r--src/librustdoc/html/highlight.rs5
-rw-r--r--src/librustdoc/html/render/context.rs7
-rw-r--r--src/librustdoc/html/render/span_map.rs97
-rw-r--r--tests/rustdoc-gui/source-anchor-scroll.goml6
-rw-r--r--tests/rustdoc/check-source-code-urls-to-def.rs19
-rw-r--r--tests/rustdoc/jump-to-def-doc-links.rs51
18 files changed, 310 insertions, 137 deletions
diff --git a/.editorconfig b/.editorconfig
index d065fa46469..eadd72e4993 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -11,6 +11,8 @@ trim_trailing_whitespace = true
 insert_final_newline = true
 indent_style = space
 indent_size = 4
+
+[*.rs]
 max_line_length = 100
 
 [*.md]
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index c7925be0d7d..b603a878746 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -2970,10 +2970,25 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
         return;
     }
 
+    let self_contained_linker = sess.opts.cg.link_self_contained.linker();
+
+    // FIXME: some targets default to using `lld`, but users can only override the linker on the CLI
+    // and cannot yet select the precise linker flavor to opt out of that. See for example issue
+    // #113597 for the `thumbv6m-none-eabi` target: a driver is used, and its default linker
+    // conflicts with the target's flavor, causing unexpected arguments being passed.
+    //
+    // Until the new `LinkerFlavor`-like CLI options are stabilized, we only adopt MCP510's behavior
+    // if its dedicated unstable CLI flags are used, to keep the current sub-optimal stable
+    // behavior.
+    let using_mcp510 =
+        self_contained_linker || sess.opts.cg.linker_flavor.is_some_and(|f| f.is_unstable());
+    if !using_mcp510 && !unstable_use_lld {
+        return;
+    }
+
     // 1. Implement the "self-contained" part of this feature by adding rustc distribution
     //    directories to the tool's search path.
-    let self_contained_linker = sess.opts.cg.link_self_contained.linker() || unstable_use_lld;
-    if self_contained_linker {
+    if self_contained_linker || unstable_use_lld {
         for path in sess.get_tools_search_paths(false) {
             cmd.arg({
                 let mut arg = OsString::from("-B");
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index 527afa005b9..8698cf86022 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -32,7 +32,7 @@ pub enum GoalEvaluationKind<'tcx> {
 }
 impl Debug for GoalEvaluation<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter { f, on_newline: true }.format_goal_evaluation(self)
+        ProofTreeFormatter::new(f).format_goal_evaluation(self)
     }
 }
 
@@ -43,7 +43,7 @@ pub struct AddedGoalsEvaluation<'tcx> {
 }
 impl Debug for AddedGoalsEvaluation<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter { f, on_newline: true }.format_nested_goal_evaluation(self)
+        ProofTreeFormatter::new(f).format_nested_goal_evaluation(self)
     }
 }
 
@@ -58,7 +58,7 @@ pub struct GoalEvaluationStep<'tcx> {
 }
 impl Debug for GoalEvaluationStep<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter { f, on_newline: true }.format_evaluation_step(self)
+        ProofTreeFormatter::new(f).format_evaluation_step(self)
     }
 }
 
@@ -78,6 +78,6 @@ pub enum CandidateKind<'tcx> {
 }
 impl Debug for GoalCandidate<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter { f, on_newline: true }.format_candidate(self)
+        ProofTreeFormatter::new(f).format_candidate(self)
     }
 }
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index 2ee625674fa..f19f1189e44 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -1,11 +1,19 @@
 use super::*;
 
 pub(super) struct ProofTreeFormatter<'a, 'b> {
-    pub(super) f: &'a mut (dyn Write + 'b),
-    pub(super) on_newline: bool,
+    f: &'a mut (dyn Write + 'b),
 }
 
-impl Write for ProofTreeFormatter<'_, '_> {
+/// A formatter which adds 4 spaces of indentation to its input before
+/// passing it on to its nested formatter.
+///
+/// We can use this for arbitrary levels of indentation by nesting it.
+struct Indentor<'a, 'b> {
+    f: &'a mut (dyn Write + 'b),
+    on_newline: bool,
+}
+
+impl Write for Indentor<'_, '_> {
     fn write_str(&mut self, s: &str) -> std::fmt::Result {
         for line in s.split_inclusive("\n") {
             if self.on_newline {
@@ -19,49 +27,52 @@ impl Write for ProofTreeFormatter<'_, '_> {
     }
 }
 
-impl ProofTreeFormatter<'_, '_> {
-    fn nested(&mut self) -> ProofTreeFormatter<'_, '_> {
-        ProofTreeFormatter { f: self, on_newline: true }
+impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
+    pub(super) fn new(f: &'a mut (dyn Write + 'b)) -> Self {
+        ProofTreeFormatter { f }
     }
 
-    pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
-        let f = &mut *self.f;
+    fn nested<F, R>(&mut self, func: F) -> R
+    where
+        F: FnOnce(&mut ProofTreeFormatter<'_, '_>) -> R,
+    {
+        func(&mut ProofTreeFormatter { f: &mut Indentor { f: self.f, on_newline: true } })
+    }
 
+    pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
         let goal_text = match goal.is_normalizes_to_hack {
             IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
             IsNormalizesToHack::No => "GOAL",
         };
 
-        writeln!(f, "{}: {:?}", goal_text, goal.uncanonicalized_goal,)?;
-        writeln!(f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
+        writeln!(self.f, "{}: {:?}", goal_text, goal.uncanonicalized_goal)?;
+        writeln!(self.f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
 
         match &goal.kind {
             GoalEvaluationKind::CacheHit(CacheHit::Global) => {
-                writeln!(f, "GLOBAL CACHE HIT: {:?}", goal.result)
+                writeln!(self.f, "GLOBAL CACHE HIT: {:?}", goal.result)
             }
             GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
-                writeln!(f, "PROVISIONAL CACHE HIT: {:?}", goal.result)
+                writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", goal.result)
             }
             GoalEvaluationKind::Uncached { revisions } => {
                 for (n, step) in revisions.iter().enumerate() {
-                    let f = &mut *self.f;
-                    writeln!(f, "REVISION {n}: {:?}", step.result)?;
-                    let mut f = self.nested();
-                    f.format_evaluation_step(step)?;
+                    writeln!(self.f, "REVISION {n}: {:?}", step.result)?;
+                    self.nested(|this| this.format_evaluation_step(step))?;
                 }
-
-                let f = &mut *self.f;
-                writeln!(f, "RESULT: {:?}", goal.result)
+                writeln!(self.f, "RESULT: {:?}", goal.result)
             }
         }?;
 
         if goal.returned_goals.len() > 0 {
-            let f = &mut *self.f;
-            writeln!(f, "NESTED GOALS ADDED TO CALLER: [")?;
-            let mut f = self.nested();
-            for goal in goal.returned_goals.iter() {
-                writeln!(f, "ADDED GOAL: {:?},", goal)?;
-            }
+            writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?;
+            self.nested(|this| {
+                for goal in goal.returned_goals.iter() {
+                    writeln!(this.f, "ADDED GOAL: {:?},", goal)?;
+                }
+                Ok(())
+            })?;
+
             writeln!(self.f, "]")?;
         }
 
@@ -72,58 +83,53 @@ impl ProofTreeFormatter<'_, '_> {
         &mut self,
         evaluation_step: &GoalEvaluationStep<'_>,
     ) -> std::fmt::Result {
-        let f = &mut *self.f;
-        writeln!(f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
+        writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
 
         for candidate in &evaluation_step.candidates {
-            let mut f = self.nested();
-            f.format_candidate(candidate)?;
+            self.nested(|this| this.format_candidate(candidate))?;
         }
-        for nested_goal_evaluation in &evaluation_step.nested_goal_evaluations {
-            let mut f = self.nested();
-            f.format_nested_goal_evaluation(nested_goal_evaluation)?;
+        for nested in &evaluation_step.nested_goal_evaluations {
+            self.nested(|this| this.format_nested_goal_evaluation(nested))?;
         }
 
         Ok(())
     }
 
     pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
-        let f = &mut *self.f;
-
         match &candidate.kind {
             CandidateKind::NormalizedSelfTyAssembly => {
-                writeln!(f, "NORMALIZING SELF TY FOR ASSEMBLY:")
+                writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
             }
             CandidateKind::Candidate { name, result } => {
-                writeln!(f, "CANDIDATE {}: {:?}", name, result)
+                writeln!(self.f, "CANDIDATE {}: {:?}", name, result)
             }
         }?;
 
-        let mut f = self.nested();
-        for candidate in &candidate.candidates {
-            f.format_candidate(candidate)?;
-        }
-        for nested_evaluations in &candidate.nested_goal_evaluations {
-            f.format_nested_goal_evaluation(nested_evaluations)?;
-        }
-
-        Ok(())
+        self.nested(|this| {
+            for candidate in &candidate.candidates {
+                this.format_candidate(candidate)?;
+            }
+            for nested in &candidate.nested_goal_evaluations {
+                this.format_nested_goal_evaluation(nested)?;
+            }
+            Ok(())
+        })
     }
 
     pub(super) fn format_nested_goal_evaluation(
         &mut self,
         nested_goal_evaluation: &AddedGoalsEvaluation<'_>,
     ) -> std::fmt::Result {
-        let f = &mut *self.f;
-        writeln!(f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?;
+        writeln!(self.f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?;
 
         for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() {
-            let f = &mut *self.f;
-            writeln!(f, "REVISION {n}")?;
-            let mut f = self.nested();
-            for goal_evaluation in revision {
-                f.format_goal_evaluation(goal_evaluation)?;
-            }
+            writeln!(self.f, "REVISION {n}")?;
+            self.nested(|this| {
+                for goal_evaluation in revision {
+                    this.format_goal_evaluation(goal_evaluation)?;
+                }
+                Ok(())
+            })?;
         }
 
         Ok(())
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 87e0b211556..527d5220564 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -27,21 +27,33 @@ pub fn crate_item(did: DefId) -> stable_mir::CrateItem {
     with_tables(|t| t.crate_item(did))
 }
 
+pub fn adt_def(did: DefId) -> stable_mir::ty::AdtDef {
+    with_tables(|t| t.adt_def(did))
+}
+
 impl<'tcx> Tables<'tcx> {
     pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId {
         self.def_ids[item.0]
     }
 
     pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem {
+        stable_mir::CrateItem(self.create_def_id(did))
+    }
+
+    pub fn adt_def(&mut self, did: DefId) -> stable_mir::ty::AdtDef {
+        stable_mir::ty::AdtDef(self.create_def_id(did))
+    }
+
+    fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
         // FIXME: this becomes inefficient when we have too many ids
         for (i, &d) in self.def_ids.iter().enumerate() {
             if d == did {
-                return stable_mir::CrateItem(i);
+                return i;
             }
         }
         let id = self.def_ids.len();
         self.def_ids.push(did);
-        stable_mir::CrateItem(id)
+        id
     }
 }
 
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 7fb31df84d0..df682cd8f3a 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -8,7 +8,7 @@
 //! For now, we are developing everything inside `rustc`, thus, we keep this module private.
 
 use crate::rustc_internal::{self, opaque};
-use crate::stable_mir::ty::{FloatTy, IntTy, RigidTy, TyKind, UintTy};
+use crate::stable_mir::ty::{AdtSubsts, FloatTy, GenericArgKind, IntTy, RigidTy, TyKind, UintTy};
 use crate::stable_mir::{self, Context};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -94,7 +94,25 @@ impl<'tcx> Tables<'tcx> {
                 ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)),
                 ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)),
             },
-            ty::Adt(_, _) => todo!(),
+            ty::Adt(adt_def, substs) => TyKind::RigidTy(RigidTy::Adt(
+                rustc_internal::adt_def(adt_def.did()),
+                AdtSubsts(
+                    substs
+                        .iter()
+                        .map(|arg| match arg.unpack() {
+                            ty::GenericArgKind::Lifetime(region) => {
+                                GenericArgKind::Lifetime(opaque(&region))
+                            }
+                            ty::GenericArgKind::Type(ty) => {
+                                GenericArgKind::Type(self.intern_ty(ty))
+                            }
+                            ty::GenericArgKind::Const(const_) => {
+                                GenericArgKind::Const(opaque(&const_))
+                            }
+                        })
+                        .collect(),
+                ),
+            )),
             ty::Foreign(_) => todo!(),
             ty::Str => todo!(),
             ty::Array(_, _) => todo!(),
@@ -149,13 +167,6 @@ pub(crate) trait Stable {
     fn stable(&self) -> Self::T;
 }
 
-impl Stable for DefId {
-    type T = stable_mir::CrateItem;
-    fn stable(&self) -> Self::T {
-        rustc_internal::crate_item(*self)
-    }
-}
-
 impl<'tcx> Stable for mir::Statement<'tcx> {
     type T = stable_mir::mir::Statement;
     fn stable(&self) -> Self::T {
@@ -190,7 +201,9 @@ impl<'tcx> Stable for mir::Rvalue<'tcx> {
             Ref(region, kind, place) => {
                 stable_mir::mir::Rvalue::Ref(opaque(region), kind.stable(), place.stable())
             }
-            ThreadLocalRef(def_id) => stable_mir::mir::Rvalue::ThreadLocalRef(def_id.stable()),
+            ThreadLocalRef(def_id) => {
+                stable_mir::mir::Rvalue::ThreadLocalRef(rustc_internal::crate_item(*def_id))
+            }
             AddressOf(mutability, place) => {
                 stable_mir::mir::Rvalue::AddressOf(mutability.stable(), place.stable())
             }
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 3181af46e9c..389e3364117 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -1,4 +1,5 @@
-use super::with;
+use super::{with, DefId};
+use crate::rustc_internal::Opaque;
 
 #[derive(Copy, Clone, Debug)]
 pub struct Ty(pub usize);
@@ -9,6 +10,9 @@ impl Ty {
     }
 }
 
+type Const = Opaque;
+type Region = Opaque;
+
 #[derive(Clone, Debug)]
 pub enum TyKind {
     RigidTy(RigidTy),
@@ -21,6 +25,7 @@ pub enum RigidTy {
     Int(IntTy),
     Uint(UintTy),
     Float(FloatTy),
+    Adt(AdtDef, AdtSubsts),
     Tuple(Vec<Ty>),
 }
 
@@ -49,3 +54,18 @@ pub enum FloatTy {
     F32,
     F64,
 }
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct AdtDef(pub(crate) DefId);
+
+#[derive(Clone, Debug)]
+pub struct AdtSubsts(pub Vec<GenericArgKind>);
+
+#[derive(Clone, Debug)]
+pub enum GenericArgKind {
+    // FIXME add proper region
+    Lifetime(Region),
+    Type(Ty),
+    // FIXME add proper const
+    Const(Const),
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 74dfbdddbab..7b41e975474 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -116,7 +116,8 @@ impl NestedGoals<'_> {
 #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
 pub enum GenerateProofTree {
     Yes(UseGlobalCache),
-    No,
+    IfEnabled,
+    Never,
 }
 
 #[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
@@ -202,7 +203,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
         {
             let mut lock = std::io::stdout().lock();
-            let _ = lock.write_fmt(format_args!("{tree:?}"));
+            let _ = lock.write_fmt(format_args!("{tree:?}\n"));
             let _ = lock.flush();
         }
 
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 03ec198c01e..141369b0337 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -41,7 +41,7 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
             self.instantiate_binder_with_placeholders(obligation.predicate),
         );
 
-        let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+        let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| {
             let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
             let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
             let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 88ee14c4db5..f1d3091225c 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -57,7 +57,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
             .map(|obligation| {
                 let code = infcx.probe(|_| {
                     match infcx
-                        .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No)
+                        .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled)
                         .0
                     {
                         Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
@@ -96,7 +96,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
             for obligation in mem::take(&mut self.obligations) {
                 let goal = obligation.clone().into();
                 let (changed, certainty, nested_goals) =
-                    match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 {
+                    match infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0 {
                         Ok(result) => result,
                         Err(NoSolution) => {
                             errors.push(FulfillmentError {
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 2d6717fdad9..cda68396321 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -200,30 +200,23 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         tcx: TyCtxt<'tcx>,
         generate_proof_tree: GenerateProofTree,
     ) -> ProofTreeBuilder<'tcx> {
-        let generate_proof_tree = match (
-            tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
-            tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache,
-            generate_proof_tree,
-        ) {
-            (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
-                GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
-            }
-
-            (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => {
-                let use_cache = use_cache.unwrap_or(true);
-                GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
-            }
-
-            (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
-            (DumpSolverProofTree::Never, _, _) => generate_proof_tree,
-            (DumpSolverProofTree::OnError, _, _) => generate_proof_tree,
-        };
-
         match generate_proof_tree {
-            GenerateProofTree::No => ProofTreeBuilder::new_noop(),
-            GenerateProofTree::Yes(global_cache_disabled) => {
-                ProofTreeBuilder::new_root(global_cache_disabled)
+            GenerateProofTree::Never => ProofTreeBuilder::new_noop(),
+            GenerateProofTree::IfEnabled => {
+                let opts = &tcx.sess.opts.unstable_opts;
+                match opts.dump_solver_proof_tree {
+                    DumpSolverProofTree::Always => {
+                        let use_cache = opts.dump_solver_proof_tree_use_cache.unwrap_or(true);
+                        ProofTreeBuilder::new_root(UseGlobalCache::from_bool(use_cache))
+                    }
+                    // `OnError` is handled by reevaluating goals in error
+                    // reporting with `GenerateProofTree::Yes`.
+                    DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
+                        ProofTreeBuilder::new_noop()
+                    }
+                }
             }
+            GenerateProofTree::Yes(use_cache) => ProofTreeBuilder::new_root(use_cache),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index a0facd94bfd..18d48b3b95f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -3569,7 +3569,7 @@ pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &
             .1
             .expect("proof tree should have been generated");
         let mut lock = std::io::stdout().lock();
-        let _ = lock.write_fmt(format_args!("{tree:?}"));
+        let _ = lock.write_fmt(format_args!("{tree:?}\n"));
         let _ = lock.flush();
     });
 }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 0aee79a6947..a99ac0f4e05 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -986,6 +986,11 @@ fn string_without_closing_tag<T: Display>(
                     )
                     .ok()
                     .map(|(url, _, _)| url),
+                    LinkFromSrc::Doc(def_id) => {
+                        format::href_with_root_path(*def_id, context, Some(&href_context.root_path))
+                            .ok()
+                            .map(|(doc_link, _, _)| doc_link)
+                    }
                 }
             })
         {
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index aa72af001df..d0a1e223eb8 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -349,7 +349,12 @@ impl<'tcx> Context<'tcx> {
                     let e = ExternalCrate { crate_num: cnum };
                     (e.name(self.tcx()), e.src_root(self.tcx()))
                 }
-                ExternalLocation::Unknown => return None,
+                ExternalLocation::Unknown => {
+                    let e = ExternalCrate { crate_num: cnum };
+                    let name = e.name(self.tcx());
+                    root = name.to_string();
+                    (name, e.src_root(self.tcx()))
+                }
             };
 
             let href = RefCell::new(PathBuf::new());
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index eb9262f472b..ac587bf6008 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -1,11 +1,11 @@
-use crate::clean::{self, PrimitiveType};
+use crate::clean::{self, rustc_span, PrimitiveType};
 use crate::html::sources;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{ExprKind, HirId, Mod, Node};
+use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::hygiene::MacroKind;
@@ -25,6 +25,7 @@ pub(crate) enum LinkFromSrc {
     Local(clean::Span),
     External(DefId),
     Primitive(PrimitiveType),
+    Doc(DefId),
 }
 
 /// This function will do at most two things:
@@ -65,24 +66,43 @@ struct SpanMapVisitor<'tcx> {
 impl<'tcx> SpanMapVisitor<'tcx> {
     /// This function is where we handle `hir::Path` elements and add them into the "span map".
     fn handle_path(&mut self, path: &rustc_hir::Path<'_>) {
-        let info = match path.res {
+        match path.res {
             // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
             // Would be nice to support them too alongside the other `DefKind`
             // (such as primitive types!).
-            Res::Def(kind, def_id) if kind != DefKind::TyParam => Some(def_id),
-            Res::Local(_) => None,
+            Res::Def(kind, def_id) if kind != DefKind::TyParam => {
+                let link = if def_id.as_local().is_some() {
+                    LinkFromSrc::Local(rustc_span(def_id, self.tcx))
+                } else {
+                    LinkFromSrc::External(def_id)
+                };
+                self.matches.insert(path.span, link);
+            }
+            Res::Local(_) => {
+                if let Some(span) = self.tcx.hir().res_span(path.res) {
+                    self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
+                }
+            }
             Res::PrimTy(p) => {
                 // FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
                 self.matches.insert(path.span, LinkFromSrc::Primitive(PrimitiveType::from(p)));
-                return;
             }
-            Res::Err => return,
-            _ => return,
-        };
-        if let Some(span) = self.tcx.hir().res_span(path.res) {
-            self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
-        } else if let Some(def_id) = info {
-            self.matches.insert(path.span, LinkFromSrc::External(def_id));
+            Res::Err => {}
+            _ => {}
+        }
+    }
+
+    /// Used to generate links on items' definition to go to their documentation page.
+    pub(crate) fn extract_info_from_hir_id(&mut self, hir_id: HirId) {
+        if let Some(Node::Item(item)) = self.tcx.hir().find(hir_id) {
+            if let Some(span) = self.tcx.def_ident_span(item.owner_id) {
+                let cspan = clean::Span::new(span);
+                // If the span isn't from the current crate, we ignore it.
+                if cspan.inner().is_dummy() || cspan.cnum(self.tcx.sess) != LOCAL_CRATE {
+                    return;
+                }
+                self.matches.insert(span, LinkFromSrc::Doc(item.owner_id.to_def_id()));
+            }
         }
     }
 
@@ -117,10 +137,13 @@ impl<'tcx> SpanMapVisitor<'tcx> {
             _ => return true,
         };
         let link_from_src = match data.macro_def_id {
-            Some(macro_def_id) if macro_def_id.is_local() => {
-                LinkFromSrc::Local(clean::Span::new(data.def_site))
+            Some(macro_def_id) => {
+                if macro_def_id.is_local() {
+                    LinkFromSrc::Local(clean::Span::new(data.def_site))
+                } else {
+                    LinkFromSrc::External(macro_def_id)
+                }
             }
-            Some(macro_def_id) => LinkFromSrc::External(macro_def_id),
             None => return true,
         };
         let new_span = data.call_site;
@@ -160,6 +183,9 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
                     LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
                 );
             }
+        } else {
+            // If it's a "mod foo {}", we want to look to its documentation page.
+            self.extract_info_from_hir_id(id);
         }
         intravisit::walk_mod(self, m, id);
     }
@@ -176,13 +202,12 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
                 .tcx
                 .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"));
             if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
-                self.matches.insert(
-                    segment.ident.span,
-                    match hir.span_if_local(def_id) {
-                        Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
-                        None => LinkFromSrc::External(def_id),
-                    },
-                );
+                let link = if def_id.as_local().is_some() {
+                    LinkFromSrc::Local(rustc_span(def_id, self.tcx))
+                } else {
+                    LinkFromSrc::External(def_id)
+                };
+                self.matches.insert(segment.ident.span, link);
             }
         } else if self.handle_macro(expr.span) {
             // We don't want to go deeper into the macro.
@@ -190,4 +215,28 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
         }
         intravisit::walk_expr(self, expr);
     }
+
+    fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
+        match item.kind {
+            ItemKind::Static(_, _, _)
+            | ItemKind::Const(_, _)
+            | ItemKind::Fn(_, _, _)
+            | ItemKind::Macro(_, _)
+            | ItemKind::TyAlias(_, _)
+            | ItemKind::Enum(_, _)
+            | ItemKind::Struct(_, _)
+            | ItemKind::Union(_, _)
+            | ItemKind::Trait(_, _, _, _, _)
+            | ItemKind::TraitAlias(_, _) => self.extract_info_from_hir_id(item.hir_id()),
+            ItemKind::Impl(_)
+            | ItemKind::Use(_, _)
+            | ItemKind::ExternCrate(_)
+            | ItemKind::ForeignMod { .. }
+            | ItemKind::GlobalAsm(_)
+            | ItemKind::OpaqueTy(_)
+            // We already have "visit_mod" above so no need to check it here.
+            | ItemKind::Mod(_) => {}
+        }
+        intravisit::walk_item(self, item);
+    }
 }
diff --git a/tests/rustdoc-gui/source-anchor-scroll.goml b/tests/rustdoc-gui/source-anchor-scroll.goml
index 67f1497e70c..0e4913cafb2 100644
--- a/tests/rustdoc-gui/source-anchor-scroll.goml
+++ b/tests/rustdoc-gui/source-anchor-scroll.goml
@@ -7,11 +7,11 @@ set-window-size: (600, 800)
 // We check that the scroll is at the top first.
 assert-property: ("html", {"scrollTop": "0"})
 
-click: '//a[text() = "barbar"]'
+click: '//a[text() = "barbar" and @href="#5-7"]'
 assert-property: ("html", {"scrollTop": "149"})
-click: '//a[text() = "bar"]'
+click: '//a[text() = "bar" and @href="#28-36"]'
 assert-property: ("html", {"scrollTop": "180"})
-click: '//a[text() = "sub_fn"]'
+click: '//a[text() = "sub_fn" and @href="#2-4"]'
 assert-property: ("html", {"scrollTop": "77"})
 
 // We now check that clicking on lines doesn't change the scroll
diff --git a/tests/rustdoc/check-source-code-urls-to-def.rs b/tests/rustdoc/check-source-code-urls-to-def.rs
index 41b9d41fa44..b803c7e9e86 100644
--- a/tests/rustdoc/check-source-code-urls-to-def.rs
+++ b/tests/rustdoc/check-source-code-urls-to-def.rs
@@ -14,10 +14,10 @@ extern crate source_code;
 #[path = "auxiliary/source-code-bar.rs"]
 pub mod bar;
 
-// @count - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#5"]' 4
+// @count - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#5-7"]' 4
 use bar::Bar;
-// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#13"]' 'self'
-// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'Trait'
+// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#13-17"]' 'self'
+// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14-16"]' 'Trait'
 use bar::sub::{self, Trait};
 
 pub struct Foo;
@@ -32,7 +32,8 @@ fn babar() {}
 // @has - '//pre[@class="rust"]//a/@href' '/primitive.u32.html'
 // @has - '//pre[@class="rust"]//a/@href' '/primitive.str.html'
 // @count - '//pre[@class="rust"]//a[@href="#23"]' 5
-// @has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' 'source_code::SourceCode'
+// @has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \
+//        'source_code::SourceCode'
 pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
     let x = 12;
     let y: Foo = Foo;
@@ -42,15 +43,15 @@ pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::Sour
     y.hello();
 }
 
-// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'bar::sub::Trait'
-// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'Trait'
+// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14-16"]' 'bar::sub::Trait'
+// @has - '//pre[@class="rust"]//a[@href="auxiliary/source-code-bar.rs.html#14-16"]' 'Trait'
 pub fn foo2<T: bar::sub::Trait, V: Trait>(t: &T, v: &V, b: bool) {}
 
 pub trait AnotherTrait {}
 pub trait WhyNot {}
 
-// @has - '//pre[@class="rust"]//a[@href="#49"]' 'AnotherTrait'
-// @has - '//pre[@class="rust"]//a[@href="#50"]' 'WhyNot'
+// @has - '//pre[@class="rust"]//a[@href="#50"]' 'AnotherTrait'
+// @has - '//pre[@class="rust"]//a[@href="#51"]' 'WhyNot'
 pub fn foo3<T, V>(t: &T, v: &V)
 where
     T: AnotherTrait,
@@ -59,7 +60,7 @@ where
 
 pub trait AnotherTrait2 {}
 
-// @has - '//pre[@class="rust"]//a[@href="#60"]' 'AnotherTrait2'
+// @has - '//pre[@class="rust"]//a[@href="#61"]' 'AnotherTrait2'
 pub fn foo4() {
     let x: Vec<AnotherTrait2> = Vec::new();
 }
diff --git a/tests/rustdoc/jump-to-def-doc-links.rs b/tests/rustdoc/jump-to-def-doc-links.rs
new file mode 100644
index 00000000000..014d5803299
--- /dev/null
+++ b/tests/rustdoc/jump-to-def-doc-links.rs
@@ -0,0 +1,51 @@
+// compile-flags: -Zunstable-options --generate-link-to-definition
+
+#![crate_name = "foo"]
+
+// @has 'src/foo/jump-to-def-doc-links.rs.html'
+
+// @has - '//a[@href="../../foo/struct.Bar.html"]' 'Bar'
+// @has - '//a[@href="../../foo/struct.Foo.html"]' 'Foo'
+pub struct Bar; pub struct Foo;
+
+// @has - '//a[@href="../../foo/enum.Enum.html"]' 'Enum'
+pub enum Enum {
+    Variant1(String),
+    Variant2(u8),
+}
+
+// @has - '//a[@href="../../foo/struct.Struct.html"]' 'Struct'
+pub struct Struct {
+    pub a: u8,
+    b: Foo,
+}
+
+impl Struct {
+    pub fn foo() {}
+    pub fn foo2(&self) {}
+    fn bar() {}
+    fn bar(&self) {}
+}
+
+// @has - '//a[@href="../../foo/trait.Trait.html"]' 'Trait'
+pub trait Trait {
+    fn foo();
+}
+
+impl Trait for Struct {
+    fn foo() {}
+}
+
+// @has - '//a[@href="../../foo/union.Union.html"]' 'Union'
+pub union Union {
+    pub a: u16,
+    pub f: u32,
+}
+
+// @has - '//a[@href="../../foo/fn.bar.html"]' 'bar'
+pub fn bar(b: Bar) {
+     let x = Foo;
+}
+
+// @has - '//a[@href="../../foo/bar/index.html"]' 'bar'
+pub mod bar {}