diff options
Diffstat (limited to 'src/librustdoc/clean/types.rs')
-rw-r--r-- | src/librustdoc/clean/types.rs | 248 |
1 files changed, 27 insertions, 221 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 87de41fde..27d18aad7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,13 +5,12 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use std::sync::OnceLock as OnceCell; -use std::{cmp, fmt, iter}; +use std::{fmt, iter}; use arrayvec::ArrayVec; use thin_vec::ThinVec; -use rustc_ast::util::comments::beautify_doc_string; -use rustc_ast::{self as ast, AttrStyle}; +use rustc_ast as ast; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -24,6 +23,7 @@ use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::vec::IndexVec; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, DefIdTree, TyCtxt, Visibility}; +use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -182,10 +182,8 @@ impl ExternalCrate { return Local; } - if extern_url_takes_precedence { - if let Some(url) = extern_url { - return to_remote(url); - } + if extern_url_takes_precedence && let Some(url) = extern_url { + return to_remote(url); } // Failing that, see if there's an attribute specifying where to find this @@ -405,7 +403,7 @@ impl Item { pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { self.item_id .as_def_id() - .map(|did| tcx.get_attrs_unchecked(did).inner_docs()) + .map(|did| inner_docs(tcx.get_attrs_unchecked(did))) .unwrap_or(false) } @@ -439,17 +437,6 @@ impl Item { self.attrs.doc_value() } - /// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts - /// `hir_id` to a [`DefId`] - pub(crate) fn from_hir_id_and_parts( - hir_id: hir::HirId, - name: Option<Symbol>, - kind: ItemKind, - cx: &mut DocContext<'_>, - ) -> Item { - Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx) - } - pub(crate) fn from_def_id_and_parts( def_id: DefId, name: Option<Symbol>, @@ -493,16 +480,16 @@ impl Item { } pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> { - use crate::html::format::href; + use crate::html::format::{href, link_tooltip}; cx.cache() .intra_doc_links .get(&self.item_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| { - debug!(?did); - if let Ok((mut href, ..)) = href(*did, cx) { + .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| { + debug!(?id); + if let Ok((mut href, ..)) = href(*id, cx) { debug!(?href); if let Some(ref fragment) = *fragment { fragment.render(&mut href, cx.tcx()) @@ -510,6 +497,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), + tooltip: link_tooltip(*id, fragment, cx), href, }) } else { @@ -534,6 +522,7 @@ impl Item { original_text: s.clone(), new_text: link_text.clone(), href: String::new(), + tooltip: String::new(), }) .collect() } @@ -665,7 +654,7 @@ impl Item { tcx: TyCtxt<'_>, asyncness: hir::IsAsync, ) -> hir::FnHeader { - let sig = tcx.fn_sig(def_id); + let sig = tcx.fn_sig(def_id).skip_binder(); let constness = if tcx.is_const_fn(def_id) && is_unstable_const_fn(tcx, def_id).is_none() { hir::Constness::Const @@ -677,7 +666,7 @@ impl Item { let header = match *self.kind { ItemKind::ForeignFunctionItem(_) => { let def_id = self.item_id.as_def_id().unwrap(); - let abi = tcx.fn_sig(def_id).abi(); + let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap()) @@ -885,8 +874,6 @@ pub(crate) trait AttributesExt { fn span(&self) -> Option<rustc_span::Span>; - fn inner_docs(&self) -> bool; - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>; } @@ -905,14 +892,6 @@ impl AttributesExt for [ast::Attribute] { self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span) } - /// Returns whether the first doc-comment is an inner attribute. - /// - //// If there are no doc-comments, return true. - /// FIXME(#78591): Support both inner and outer attributes on the same item. - fn inner_docs(&self) -> bool { - self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner) - } - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> { let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg; @@ -1021,58 +1000,6 @@ impl<I: Iterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I { } } -/// A portion of documentation, extracted from a `#[doc]` attribute. -/// -/// Each variant contains the line number within the complete doc-comment where the fragment -/// starts, as well as the Span where the corresponding doc comment or attribute is located. -/// -/// Included files are kept separate from inline doc comments so that proper line-number -/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are -/// kept separate because of issue #42760. -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) struct DocFragment { - pub(crate) span: rustc_span::Span, - /// The module this doc-comment came from. - /// - /// This allows distinguishing between the original documentation and a pub re-export. - /// If it is `None`, the item was not re-exported. - pub(crate) parent_module: Option<DefId>, - pub(crate) doc: Symbol, - pub(crate) kind: DocFragmentKind, - pub(crate) indent: usize, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) enum DocFragmentKind { - /// A doc fragment created from a `///` or `//!` doc comment. - SugaredDoc, - /// A doc fragment created from a "raw" `#[doc=""]` attribute. - RawDoc, -} - -/// The goal of this function is to apply the `DocFragment` transformation that is required when -/// transforming into the final Markdown, which is applying the computed indent to each line in -/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). -/// -/// Note: remove the trailing newline where appropriate -fn add_doc_fragment(out: &mut String, frag: &DocFragment) { - let s = frag.doc.as_str(); - let mut iter = s.lines(); - if s.is_empty() { - out.push('\n'); - return; - } - while let Some(line) = iter.next() { - if line.chars().any(|c| !c.is_whitespace()) { - assert!(line.len() >= frag.indent); - out.push_str(&line[frag.indent..]); - } else { - out.push_str(line); - } - out.push('\n'); - } -} - /// Collapse a collection of [`DocFragment`]s into one string, /// handling indentation and newlines as needed. pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { @@ -1084,98 +1011,18 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { acc } -/// Removes excess indentation on comments in order for the Markdown -/// to be parsed correctly. This is necessary because the convention for -/// writing documentation is to provide a space between the /// or //! marker -/// and the doc text, but Markdown is whitespace-sensitive. For example, -/// a block of text with four-space indentation is parsed as a code block, -/// so if we didn't unindent comments, these list items -/// -/// /// A list: -/// /// -/// /// - Foo -/// /// - Bar -/// -/// would be parsed as if they were in a code block, which is likely not what the user intended. -fn unindent_doc_fragments(docs: &mut Vec<DocFragment>) { - // `add` is used in case the most common sugared doc syntax is used ("/// "). The other - // fragments kind's lines are never starting with a whitespace unless they are using some - // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, - // we need to take into account the fact that the minimum indent minus one (to take this - // whitespace into account). - // - // For example: - // - // /// hello! - // #[doc = "another"] - // - // In this case, you want "hello! another" and not "hello! another". - let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) - && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) - { - // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to - // "decide" how much the minimum indent will be. - 1 - } else { - 0 - }; - - // `min_indent` is used to know how much whitespaces from the start of each lines must be - // removed. Example: - // - // /// hello! - // #[doc = "another"] - // - // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum - // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 - // (5 - 1) whitespaces. - let Some(min_indent) = docs - .iter() - .map(|fragment| { - fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } else { - // Compare against either space or tab, ignoring whether they are - // mixed or not. - let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); - cmp::min(min_indent, whitespace) - + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } - } - }) - }) - .min() - else { - return; - }; - - for fragment in docs { - if fragment.doc == kw::Empty { - continue; - } - - let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { - min_indent - add - } else { - min_indent - }; - - fragment.indent = min_indent; - } -} - /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct ItemLink { /// The original link written in the markdown - pub(crate) link: String, + pub(crate) link: Box<str>, /// The link text displayed in the HTML. /// /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) - pub(crate) link_text: String, + pub(crate) link_text: Box<str>, /// The `DefId` of the Item whose **HTML Page** contains the item being /// linked to. This will be different to `item_id` on item's that don't /// have their own page, such as struct fields and enum variants. @@ -1188,11 +1035,13 @@ pub struct RenderedLink { /// The text the link was original written as. /// /// This could potentially include disambiguators and backticks. - pub(crate) original_text: String, + pub(crate) original_text: Box<str>, /// The text to display in the HTML - pub(crate) new_text: String, + pub(crate) new_text: Box<str>, /// The URL to put in the `href` pub(crate) href: String, + /// The tooltip. + pub(crate) tooltip: String, } /// The attributes on an [`Item`], including attributes like `#[derive(...)]` and `#[inline]`, @@ -1242,26 +1091,7 @@ impl Attributes { attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>, doc_only: bool, ) -> Attributes { - let mut doc_strings = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); - for (attr, parent_module) in attrs { - if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { - trace!("got doc_str={doc_str:?}"); - let doc = beautify_doc_string(doc_str, comment_kind); - let kind = if attr.is_doc_comment() { - DocFragmentKind::SugaredDoc - } else { - DocFragmentKind::RawDoc - }; - let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 }; - doc_strings.push(fragment); - } else if !doc_only { - other_attrs.push(attr.clone()); - } - } - - unindent_doc_fragments(&mut doc_strings); - + let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); Attributes { doc_strings, other_attrs } } @@ -1280,20 +1110,6 @@ impl Attributes { if out.is_empty() { None } else { Some(out) } } - /// Return the doc-comments on this item, grouped by the module they came from. - /// The module can be different if this is a re-export with added documentation. - /// - /// The last newline is not trimmed so the produced strings are reusable between - /// early and late doc link resolution regardless of their position. - pub(crate) fn prepare_to_doc_link_resolution(&self) -> FxHashMap<Option<DefId>, String> { - let mut res = FxHashMap::default(); - for fragment in &self.doc_strings { - let out_str = res.entry(fragment.parent_module).or_default(); - add_doc_fragment(out_str, fragment); - } - res - } - /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined /// with newlines. pub(crate) fn collapsed_doc_value(&self) -> Option<String> { @@ -1358,10 +1174,10 @@ impl GenericBound { pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { use rustc_hir::TraitBoundModifier as TBM; - if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { - if Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() { - return true; - } + if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self && + Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() + { + return true; } false } @@ -2416,10 +2232,7 @@ impl ConstantKind { pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { match *self { - ConstantKind::TyConst { .. } => false, - ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| { - is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id)) - }), + ConstantKind::TyConst { .. } | ConstantKind::Extern { .. } => false, ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { is_literal_expr(tcx, body.hir_id) } @@ -2498,14 +2311,7 @@ impl Import { } pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool { - match self.source.did { - Some(did) => tcx - .get_attrs(did, sym::doc) - .filter_map(ast::Attribute::meta_item_list) - .flatten() - .has_word(sym::hidden), - None => false, - } + self.source.did.map_or(false, |did| tcx.is_doc_hidden(did)) } } |