summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/clean/types.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/clean/types.rs')
-rw-r--r--src/librustdoc/clean/types.rs248
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))
}
}