diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 85c4731c6bff7..d79fcdd5fcee5 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1943,14 +1943,18 @@ crate enum Variant { crate struct Span(rustc_span::Span); impl Span { + /// Wraps a [`rustc_span::Span`]. In case this span is the result of a macro expansion, the + /// span will be updated to point to the macro invocation instead of the macro definition. + /// + /// (See rust-lang/rust#39726) crate fn from_rustc_span(sp: rustc_span::Span) -> Self { - // Get the macro invocation instead of the definition, - // in case the span is result of a macro expansion. - // (See rust-lang/rust#39726) Self(sp.source_callsite()) } /// Unless you know what you're doing, use [`Self::from_rustc_span`] instead! + /// + /// Contrary to [`Self::from_rustc_span`], this constructor wraps the span as is and don't + /// perform any operation on it, even if it's from a macro expansion. crate fn wrap(sp: rustc_span::Span) -> Span { Self(sp) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 8b5c147b56cae..8ab6aa775d2e4 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -494,10 +494,7 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec (fqp, shortty, { let module_fqp = to_module_fqp(shortty, fqp); diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 579f22052adc6..62f8618b8c6e7 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -63,6 +63,24 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option Option<(u32, u32)> { match self { Self::Ident(sp) | Self::Self_(sp) => Some(sp), @@ -166,7 +186,7 @@ impl Iterator for TokenIter<'a> { } } -/// Returns `None` if this is a `Class::Ident`. +/// Classifies into identifier class; returns `None` if this is a non-keyword identifier. fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option { let ignore: &[&str] = if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] }; @@ -181,7 +201,20 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) }) } -fn move_span(file_span_lo: u32, start: u32, end: u32) -> (u32, u32) { +/// Before explaining what this function does, some global explanations on rust's `Span`: +/// +/// Each source code file is stored in the source map in the compiler and has a +/// `lo` and a `hi` (lowest and highest bytes in this source map which can be seen as one huge +/// string to simplify things). So in this case, this represents the starting byte of the current +/// file. It'll be used later on to retrieve the "definition span" from the +/// `span_correspondance_map` (which is inside `context`). +/// +/// This when we transform the "span" we have from reading the input into a "span" which can be +/// used as index to the `span_correspondance_map` to get the definition of this item. +/// +/// So in here, `file_span_lo` is representing the "lo" byte in the global source map, and to make +/// our "span" works in there, we simply add `file_span_lo` to our values. +fn local_span_to_global_span(file_span_lo: u32, start: u32, end: u32) -> (u32, u32) { (start + file_span_lo, end + file_span_lo) } @@ -199,6 +232,9 @@ struct Classifier<'a> { } impl<'a> Classifier<'a> { + /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code + /// file "lo" byte which we be used later on by the `span_correspondance_map`. More explanations + /// are provided in the [`local_span_to_global_span`] function documentation about how it works. fn new(src: &str, edition: Edition, file_span_lo: u32) -> Classifier<'_> { let tokens = TokenIter { src }.peekable(); Classifier { @@ -263,7 +299,10 @@ impl<'a> Classifier<'a> { } } - /// Wraps the tokens iteration to ensure that the byte_pos is always correct. + /// Wraps the tokens iteration to ensure that the `byte_pos` is always correct. + /// + /// It returns the token's kind, the token as a string and its byte position in the source + /// string. fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> { if let Some((kind, text)) = self.tokens.next() { let before = self.byte_pos; @@ -306,8 +345,12 @@ impl<'a> Classifier<'a> { } } - /// Single step of highlighting. This will classify `token`, but maybe also - /// a couple of following ones as well. + /// Single step of highlighting. This will classify `token`, but maybe also a couple of + /// following ones as well. + /// + /// `before` is the position of the given token in the `source` string and is used as "lo" byte + /// in case we want to try to generate a link for this token using the + /// `span_correspondance_map`. fn advance( &mut self, token: TokenKind, @@ -453,12 +496,12 @@ impl<'a> Classifier<'a> { self.in_macro_nonterminal = false; Class::MacroNonTerminal } - "self" | "Self" => Class::Self_(move_span( + "self" | "Self" => Class::Self_(local_span_to_global_span( self.file_span_lo, before, before + text.len() as u32, )), - _ => Class::Ident(move_span( + _ => Class::Ident(local_span_to_global_span( self.file_span_lo, before, before + text.len() as u32, @@ -466,9 +509,11 @@ impl<'a> Classifier<'a> { }, Some(c) => c, }, - TokenKind::RawIdent | TokenKind::UnknownPrefix => { - Class::Ident(move_span(self.file_span_lo, before, before + text.len() as u32)) - } + TokenKind::RawIdent | TokenKind::UnknownPrefix => Class::Ident(local_span_to_global_span( + self.file_span_lo, + before, + before + text.len() as u32, + )), TokenKind::Lifetime { .. } => Class::Lifetime, }; // Anything that didn't return above is the simple case where we the @@ -501,8 +546,13 @@ fn exit_span(out: &mut Buffer) { /// enter_span(Foo), string("text", None), exit_span() /// string("text", Foo) /// ``` +/// /// The latter can be thought of as a shorthand for the former, which is more /// flexible. +/// +/// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function +/// will then try to find this `span` in the `span_correspondance_map`. If found, it'll then +/// generate a link for this element (which corresponds to where its definition is located). fn string( out: &mut Buffer, text: T, diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 6100f45e998e1..9e16c451626f7 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -9,12 +9,29 @@ use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; +/// This enum allows us to store two different kinds of information: +/// +/// In case the `span` definition comes from the same crate, we can simply get the `span` and use +/// it as is. +/// +/// Otherwise, we store the definition `DefId` and will generate a link to the documentation page +/// instead of the source code directly. #[derive(Debug)] crate enum LinkFromSrc { Local(Span), External(DefId), } +/// This function will do at most two things: +/// +/// 1. Generate a `span` correspondance map which links an item `span` to its definition `span`. +/// 2. Collect the source code files. +/// +/// It returns the `krate`, the source code files and the `span` correspondance map. +/// +/// Note about the `span` correspondance map: the keys are actually `(lo, hi)` of `span`s. We don't +/// need the `span` context later on, only their position, so instead of keep a whole `Span`, we +/// only keep the `lo` and `hi`. crate fn collect_spans_and_sources( tcx: TyCtxt<'_>, krate: clean::Crate,