diff options
Diffstat (limited to 'src/librustdoc/passes')
| -rw-r--r-- | src/librustdoc/passes/calculate_doc_coverage.rs | 13 | ||||
| -rw-r--r-- | src/librustdoc/passes/check_code_block_syntax.rs | 15 | ||||
| -rw-r--r-- | src/librustdoc/passes/collapse_docs.rs | 72 | ||||
| -rw-r--r-- | src/librustdoc/passes/collect_intra_doc_links.rs | 542 | ||||
| -rw-r--r-- | src/librustdoc/passes/collect_trait_impls.rs | 8 | ||||
| -rw-r--r-- | src/librustdoc/passes/doc_test_lints.rs | 6 | ||||
| -rw-r--r-- | src/librustdoc/passes/html_tags.rs | 2 | ||||
| -rw-r--r-- | src/librustdoc/passes/mod.rs | 5 | ||||
| -rw-r--r-- | src/librustdoc/passes/strip_hidden.rs | 8 | ||||
| -rw-r--r-- | src/librustdoc/passes/stripper.rs | 12 | ||||
| -rw-r--r-- | src/librustdoc/passes/unindent_comments.rs | 18 | ||||
| -rw-r--r-- | src/librustdoc/passes/unindent_comments/tests.rs | 14 |
12 files changed, 345 insertions, 370 deletions
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 52f6a97089b..05a3a15adac 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -5,7 +5,7 @@ use crate::html::markdown::{find_testable_code, ErrorCodes}; use crate::passes::doc_test_lints::{should_have_doc_example, Tests}; use crate::passes::Pass; use rustc_lint::builtin::MISSING_DOCS; -use rustc_middle::lint::LintSource; +use rustc_middle::lint::LintLevelSource; use rustc_session::lint; use rustc_span::symbol::sym; use rustc_span::FileName; @@ -187,7 +187,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> { impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> { - match i.kind { + match *i.kind { _ if !i.def_id.is_local() => { // non-local items are skipped because they can be out of the users control, // especially in the case of trait impls, which rustdoc eagerly inlines @@ -235,12 +235,7 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { let mut tests = Tests { found_tests: 0 }; find_testable_code( - &i.attrs - .doc_strings - .iter() - .map(|d| d.doc.as_str()) - .collect::<Vec<_>>() - .join("\n"), + &i.attrs.collapsed_doc_value().unwrap_or_default(), &mut tests, ErrorCodes::No, false, @@ -254,7 +249,7 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { // `missing_docs` is allow-by-default, so don't treat this as ignoring the item // unless the user had an explicit `allow` let should_have_docs = - level != lint::Level::Allow || matches!(source, LintSource::Default); + level != lint::Level::Allow || matches!(source, LintLevelSource::Default); debug!("counting {:?} {:?} in {}", i.type_(), i.name, filename); self.items.entry(filename).or_default().count_item( has_docs, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 0c76dc571be..554392c213e 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -51,10 +51,10 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { let mut diag = if let Some(sp) = super::source_span_for_markdown_range(self.cx, &dox, &code_block.range, &item.attrs) { - let warning_message = if buffer.has_errors { - "could not parse code block as Rust code" + let (warning_message, suggest_using_text) = if buffer.has_errors { + ("could not parse code block as Rust code", true) } else { - "Rust code block is empty" + ("Rust code block is empty", false) }; let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); @@ -67,6 +67,15 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { String::from("```text"), Applicability::MachineApplicable, ); + } else if suggest_using_text && code_block.is_ignore { + let sp = sp.from_inner(InnerSpan::new(0, 3)); + diag.span_suggestion( + sp, + "`ignore` code blocks require valid Rust code for syntax highlighting. \ + Mark blocks that do not contain Rust code as text", + String::from("```text,"), + Applicability::MachineApplicable, + ); } diag diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs deleted file mode 100644 index e1ba75baa0f..00000000000 --- a/src/librustdoc/passes/collapse_docs.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::clean::{self, DocFragment, DocFragmentKind, Item}; -use crate::core::DocContext; -use crate::fold; -use crate::fold::DocFolder; -use crate::passes::Pass; - -use std::mem::take; - -crate const COLLAPSE_DOCS: Pass = Pass { - name: "collapse-docs", - run: collapse_docs, - description: "concatenates all document attributes into one document attribute", -}; - -crate fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { - let mut krate = Collapser.fold_crate(krate); - krate.collapsed = true; - krate -} - -struct Collapser; - -impl fold::DocFolder for Collapser { - fn fold_item(&mut self, mut i: Item) -> Option<Item> { - i.attrs.collapse_doc_comments(); - Some(self.fold_item_recur(i)) - } -} - -fn collapse(doc_strings: &mut Vec<DocFragment>) { - let mut docs = vec![]; - let mut last_frag: Option<DocFragment> = None; - - for frag in take(doc_strings) { - if let Some(mut curr_frag) = last_frag.take() { - let curr_kind = &curr_frag.kind; - let new_kind = &frag.kind; - - if matches!(*curr_kind, DocFragmentKind::Include { .. }) - || curr_kind != new_kind - || curr_frag.parent_module != frag.parent_module - { - if *curr_kind == DocFragmentKind::SugaredDoc - || *curr_kind == DocFragmentKind::RawDoc - { - // add a newline for extra padding between segments - curr_frag.doc.push('\n'); - } - docs.push(curr_frag); - last_frag = Some(frag); - } else { - curr_frag.doc.push('\n'); - curr_frag.doc.push_str(&frag.doc); - curr_frag.span = curr_frag.span.to(frag.span); - last_frag = Some(curr_frag); - } - } else { - last_frag = Some(frag); - } - } - - if let Some(frag) = last_frag.take() { - docs.push(frag); - } - *doc_strings = docs; -} - -impl clean::Attributes { - crate fn collapse_doc_comments(&mut self) { - collapse(&mut self.doc_strings); - } -} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a8adfe08b25..11ee59b2401 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -10,31 +10,33 @@ use rustc_hir as hir; use rustc_hir::def::{ DefKind, Namespace::{self, *}, - PerNS, Res, + PerNS, }; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_middle::ty; +use rustc_middle::{bug, ty}; use rustc_resolve::ParentScope; use rustc_session::lint::{ builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}, Lint, }; use rustc_span::hygiene::MacroKind; -use rustc_span::symbol::sym; use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; +use pulldown_cmark::LinkType; + use std::borrow::Cow; use std::cell::Cell; +use std::convert::{TryFrom, TryInto}; use std::mem; use std::ops::Range; -use crate::clean::{self, Crate, Item, ItemLink, PrimitiveType}; +use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType}; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::html::markdown::markdown_links; +use crate::html::markdown::{markdown_links, MarkdownLink}; use crate::passes::Pass; use super::span_of_attrs; @@ -61,6 +63,71 @@ impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> { } } +#[derive(Copy, Clone, Debug, Hash)] +enum Res { + Def(DefKind, DefId), + Primitive(PrimitiveType), +} + +type ResolveRes = rustc_hir::def::Res<rustc_ast::NodeId>; + +impl Res { + fn descr(self) -> &'static str { + match self { + Res::Def(kind, id) => ResolveRes::Def(kind, id).descr(), + Res::Primitive(_) => "builtin type", + } + } + + fn article(self) -> &'static str { + match self { + Res::Def(kind, id) => ResolveRes::Def(kind, id).article(), + Res::Primitive(_) => "a", + } + } + + fn name(self, tcx: ty::TyCtxt<'_>) -> String { + match self { + Res::Def(_, id) => tcx.item_name(id).to_string(), + Res::Primitive(prim) => prim.as_str().to_string(), + } + } + + fn def_id(self) -> DefId { + self.opt_def_id().expect("called def_id() on a primitive") + } + + fn opt_def_id(self) -> Option<DefId> { + match self { + Res::Def(_, id) => Some(id), + Res::Primitive(_) => None, + } + } + + fn as_hir_res(self) -> Option<rustc_hir::def::Res> { + match self { + Res::Def(kind, id) => Some(rustc_hir::def::Res::Def(kind, id)), + // FIXME: maybe this should handle the subset of PrimitiveType that fits into hir::PrimTy? + Res::Primitive(_) => None, + } + } +} + +impl TryFrom<ResolveRes> for Res { + type Error = (); + + fn try_from(res: ResolveRes) -> Result<Self, ()> { + use rustc_hir::def::Res::*; + match res { + Def(kind, id) => Ok(Res::Def(kind, id)), + PrimTy(prim) => Ok(Res::Primitive(PrimitiveType::from_hir(prim))), + // e.g. `#[derive]` + NonMacroAttr(..) | Err => Result::Err(()), + other => bug!("unrecognized res {:?}", other), + } + } +} + #[derive(Debug)] /// A link failed to resolve. enum ResolutionFailure<'a> { @@ -200,8 +267,9 @@ struct LinkCollector<'a, 'tcx> { /// because `clean` and the disambiguator code expect them to be different. /// See the code for associated items on inherent impls for details. kind_side_channel: Cell<Option<(DefKind, DefId)>>, - /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link - visited_links: FxHashMap<ResolutionInfo, CachedLink>, + /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link. + /// The link will be `None` if it could not be resolved (i.e. the error was cached). + visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>, } impl<'a, 'tcx> LinkCollector<'a, 'tcx> { @@ -253,12 +321,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) - .map(|(_, res)| res) - .unwrap_or(Res::Err); - if let Res::Err = ty_res { - return Err(no_res().into()); - } - let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); + .and_then(|(_, res)| res.try_into()) + .map_err(|()| no_res())?; + match ty_res { Res::Def(DefKind::Enum, did) => { if cx @@ -309,7 +374,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// lifetimes on `&'path` will work. fn resolve_primitive_associated_item( &self, - prim_ty: hir::PrimTy, + prim_ty: PrimitiveType, ns: Namespace, module_id: DefId, item_name: Symbol, @@ -317,7 +382,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ) -> Result<(Res, Option<String>), ErrorKind<'path>> { let cx = self.cx; - PrimitiveType::from_hir(prim_ty) + prim_ty .impls(cx.tcx) .into_iter() .find_map(|&impl_| { @@ -329,28 +394,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ns, impl_, ) - .map(|item| match item.kind { - ty::AssocKind::Fn => "method", - ty::AssocKind::Const => "associatedconstant", - ty::AssocKind::Type => "associatedtype", + .map(|item| { + let kind = item.kind; + self.kind_side_channel.set(Some((kind.as_def_kind(), item.def_id))); + match kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + } }) .map(|out| { ( - Res::PrimTy(prim_ty), - Some(format!("{}#{}.{}", prim_ty.name(), out, item_str)), + Res::Primitive(prim_ty), + Some(format!("{}#{}.{}", prim_ty.as_str(), out, item_str)), ) }) }) .ok_or_else(|| { debug!( "returning primitive error for {}::{} in {} namespace", - prim_ty.name(), + prim_ty.as_str(), item_name, ns.descr() ); ResolutionFailure::NotResolved { module_id, - partial_res: Some(Res::PrimTy(prim_ty)), + partial_res: Some(Res::Primitive(prim_ty)), unresolved: item_str.into(), } .into() @@ -377,19 +446,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { false, ) { if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Ok(res.map_id(|_| panic!("unexpected id"))); + return Ok(res.try_into().unwrap()); } } - if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { - return Ok(res.map_id(|_| panic!("unexpected id"))); + if let Some(&res) = resolver.all_macros().get(&Symbol::intern(path_str)) { + return Ok(res.try_into().unwrap()); } debug!("resolving {} as a macro in the module {:?}", path_str, module_id); if let Ok((_, res)) = resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id) { // don't resolve builtins like `#[derive]` - if let Res::Def(..) = res { - let res = res.map_id(|_| panic!("unexpected node_id")); + if let Ok(res) = res.try_into() { return Ok(res); } } @@ -408,14 +476,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// Associated items will never be resolved by this function. fn resolve_path(&self, path_str: &str, ns: Namespace, module_id: DefId) -> Option<Res> { let result = self.cx.enter_resolver(|resolver| { - resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) + resolver + .resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) + .and_then(|(_, res)| res.try_into()) }); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); - match result.map(|(_, res)| res) { - // resolver doesn't know about true and false so we'll have to resolve them + match result { + // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`) // manually as bool - Ok(Res::Err) | Err(()) => is_bool_value(path_str, ns).map(|(_, res)| res), - Ok(res) => Some(res.map_id(|_| panic!("unexpected node_id"))), + Err(()) => resolve_primitive(path_str, ns), + Ok(res) => Some(res), } } @@ -444,13 +514,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return handle_variant(cx, res, extra_fragment); } // Not a trait item; just return what we found. - Res::PrimTy(ty) => { + Res::Primitive(ty) => { if extra_fragment.is_some() { return Err(ErrorKind::AnchorFailure( AnchorFailure::RustdocAnchorConflict(res), )); } - return Ok((res, Some(ty.name_str().to_owned()))); + return Ok((res, Some(ty.as_str().to_owned()))); } Res::Def(DefKind::Mod, _) => { return Ok((res, extra_fragment.clone())); @@ -483,7 +553,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // FIXME: are these both necessary? let ty_res = if let Some(ty_res) = resolve_primitive(&path_root, TypeNS) - .map(|(_, res)| res) .or_else(|| self.resolve_path(&path_root, TypeNS, module_id)) { ty_res @@ -502,7 +571,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; let res = match ty_res { - Res::PrimTy(prim) => Some( + Res::Primitive(prim) => Some( self.resolve_primitive_associated_item(prim, ns, module_id, item_name, item_str), ), Res::Def( @@ -768,31 +837,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { use rustc_middle::ty::DefIdTree; let parent_node = if item.is_fake() { - // FIXME: is this correct? None - // If we're documenting the crate root itself, it has no parent. Use the root instead. - } else if item.def_id.is_top_level_module() { - Some(item.def_id) } else { - let mut current = item.def_id; - // The immediate parent might not always be a module. - // Find the first parent which is. - loop { - if let Some(parent) = self.cx.tcx.parent(current) { - if self.cx.tcx.def_kind(parent) == DefKind::Mod { - break Some(parent); - } - current = parent; - } else { - debug!( - "{:?} has no parent (kind={:?}, original was {:?})", - current, - self.cx.tcx.def_kind(current), - item.def_id - ); - break None; - } - } + find_nearest_parent_module(self.cx.tcx, item.def_id) }; if parent_node.is_some() { @@ -851,43 +898,18 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { // In the presence of re-exports, this is not the same as the module of the item. // Rather than merging all documentation into one, resolve it one attribute at a time // so we know which module it came from. - let mut attrs = item.attrs.doc_strings.iter().peekable(); - while let Some(attr) = attrs.next() { - // `collapse_docs` does not have the behavior we want: - // we want `///` and `#[doc]` to count as the same attribute, - // but currently it will treat them as separate. - // As a workaround, combine all attributes with the same parent module into the same attribute. - let mut combined_docs = attr.doc.clone(); - loop { - match attrs.peek() { - Some(next) if next.parent_module == attr.parent_module => { - combined_docs.push('\n'); - combined_docs.push_str(&attrs.next().unwrap().doc); - } - _ => break, - } - } - debug!("combined_docs={}", combined_docs); + for (parent_module, doc) in item.attrs.collapsed_doc_value_by_module_level() { + debug!("combined_docs={}", doc); - let (krate, parent_node) = if let Some(id) = attr.parent_module { - trace!("docs {:?} came from {:?}", attr.doc, id); + let (krate, parent_node) = if let Some(id) = parent_module { (id.krate, Some(id)) } else { - trace!("no parent found for {:?}", attr.doc); (item.def_id.krate, parent_node) }; // NOTE: if there are links that start in one crate and end in another, this will not resolve them. // This is a degenerate case and it's not supported by rustdoc. - for (ori_link, link_range) in markdown_links(&combined_docs) { - let link = self.resolve_link( - &item, - &combined_docs, - &self_name, - parent_node, - krate, - ori_link, - link_range, - ); + for md_link in markdown_links(&doc) { + let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link); if let Some(link) = link { item.attrs.links.push(link); } @@ -919,27 +941,26 @@ impl LinkCollector<'_, '_> { self_name: &Option<String>, parent_node: Option<DefId>, krate: CrateNum, - ori_link: String, - link_range: Range<usize>, + ori_link: MarkdownLink, ) -> Option<ItemLink> { - trace!("considering link '{}'", ori_link); + trace!("considering link '{}'", ori_link.link); // Bail early for real links. - if ori_link.contains('/') { + if ori_link.link.contains('/') { return None; } // [] is mostly likely not supposed to be a link - if ori_link.is_empty() { + if ori_link.link.is_empty() { return None; } let cx = self.cx; - let link = ori_link.replace("`", ""); + let link = ori_link.link.replace("`", ""); let parts = link.split('#').collect::<Vec<_>>(); let (link, extra_fragment) = if parts.len() > 2 { // A valid link can't have multiple #'s - anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors); + anchor_failure(cx, &item, &link, dox, ori_link.range, AnchorFailure::MultipleAnchors); return None; } else if parts.len() == 2 { if parts[0].trim().is_empty() { @@ -958,7 +979,7 @@ impl LinkCollector<'_, '_> { (link.trim(), None) }; - if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !".contains(ch))) { + if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, !*&;".contains(ch))) { return None; } @@ -995,7 +1016,7 @@ impl LinkCollector<'_, '_> { path_str, disambiguator, dox, - link_range, + ori_link.range, smallvec![ResolutionFailure::NoParentItem], ); return None; @@ -1035,7 +1056,7 @@ impl LinkCollector<'_, '_> { path_str, disambiguator, dox, - link_range, + ori_link.range, smallvec![err_kind], ); return None; @@ -1046,21 +1067,27 @@ impl LinkCollector<'_, '_> { // Sanity check to make sure we don't have any angle brackets after stripping generics. assert!(!path_str.contains(['<', '>'].as_slice())); - // The link is not an intra-doc link if it still contains commas or spaces after - // stripping generics. - if path_str.contains([',', ' '].as_slice()) { + // The link is not an intra-doc link if it still contains spaces after stripping generics. + if path_str.contains(' ') { return None; } - let key = ResolutionInfo { - module_id, - dis: disambiguator, - path_str: path_str.to_owned(), - extra_fragment, + let diag_info = DiagnosticInfo { + item, + dox, + ori_link: &ori_link.link, + link_range: ori_link.range.clone(), }; - let diag = - DiagnosticInfo { item, dox, ori_link: &ori_link, link_range: link_range.clone() }; - let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(key, diag)?; + let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( + ResolutionInfo { + module_id, + dis: disambiguator, + path_str: path_str.to_owned(), + extra_fragment, + }, + diag_info, + matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), + )?; // Check for a primitive which might conflict with a module // Report the ambiguity and require that the user specify which one they meant. @@ -1068,9 +1095,9 @@ impl LinkCollector<'_, '_> { if matches!( disambiguator, None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive) - ) && !matches!(res, Res::PrimTy(_)) + ) && !matches!(res, Res::Primitive(_)) { - if let Some((path, prim)) = resolve_primitive(path_str, TypeNS) { + if let Some(prim) = resolve_primitive(path_str, TypeNS) { // `prim@char` if matches!(disambiguator, Some(Disambiguator::Primitive)) { if fragment.is_some() { @@ -1079,17 +1106,17 @@ impl LinkCollector<'_, '_> { &item, path_str, dox, - link_range, + ori_link.range, AnchorFailure::RustdocAnchorConflict(prim), ); return None; } res = prim; - fragment = Some(path.as_str().to_string()); + fragment = Some(prim.name(self.cx.tcx)); } else { // `[char]` when a `char` module is in scope let candidates = vec![res, prim]; - ambiguity_error(cx, &item, path_str, dox, link_range, candidates); + ambiguity_error(cx, &item, path_str, dox, ori_link.range, candidates); return None; } } @@ -1107,49 +1134,45 @@ impl LinkCollector<'_, '_> { specified.descr() ); diag.note(¬e); - suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range); + suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range); }; - report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, &item, dox, &link_range, callback); + report_diagnostic( + cx, + BROKEN_INTRA_DOC_LINKS, + &msg, + &item, + dox, + &ori_link.range, + callback, + ); }; - if let Res::PrimTy(..) = res { - match disambiguator { - Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => { - Some(ItemLink { link: ori_link, link_text, did: None, fragment }) - } - Some(other) => { - report_mismatch(other, Disambiguator::Primitive); - None - } - } - } else { + + let verify = |kind: DefKind, id: DefId| { debug!("intra-doc link to {} resolved to {:?}", path_str, res); // Disallow e.g. linking to enums with `struct@` - if let Res::Def(kind, _) = res { - debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); - match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { - | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) - // NOTE: this allows 'method' to mean both normal functions and associated functions - // This can't cause ambiguity because both are in the same namespace. - | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) - // These are namespaces; allow anything in the namespace to match - | (_, Some(Disambiguator::Namespace(_))) - // If no disambiguator given, allow anything - | (_, None) - // All of these are valid, so do nothing - => {} - (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} - (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { - report_mismatch(specified, Disambiguator::Kind(kind)); - return None; - } + debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); + match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { + | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) + // NOTE: this allows 'method' to mean both normal functions and associated functions + // This can't cause ambiguity because both are in the same namespace. + | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) + // These are namespaces; allow anything in the namespace to match + | (_, Some(Disambiguator::Namespace(_))) + // If no disambiguator given, allow anything + | (_, None) + // All of these are valid, so do nothing + => {} + (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} + (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { + report_mismatch(specified, Disambiguator::Kind(kind)); + return None; } } // item can be non-local e.g. when using #[doc(primitive = "pointer")] - if let Some((src_id, dst_id)) = res - .opt_def_id() - .and_then(|def_id| def_id.as_local()) + if let Some((src_id, dst_id)) = id + .as_local() .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id))) { use rustc_hir::def_id::LOCAL_CRATE; @@ -1160,11 +1183,41 @@ impl LinkCollector<'_, '_> { if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src) && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst) { - privacy_error(cx, &item, &path_str, dox, link_range); + privacy_error(cx, &item, &path_str, dox, &ori_link); } } - let id = clean::register_res(cx, res); - Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment }) + + Some((kind, id)) + }; + + match res { + Res::Primitive(_) => { + if let Some((kind, id)) = self.kind_side_channel.take() { + // We're actually resolving an associated item of a primitive, so we need to + // verify the disambiguator (if any) matches the type of the associated item. + // This case should really follow the same flow as the `Res::Def` branch below, + // but attempting to add a call to `clean::register_res` causes an ICE. @jyn514 + // thinks `register_res` is only needed for cross-crate re-exports, but Rust + // doesn't allow statements like `use str::trim;`, making this a (hopefully) + // valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677 + // for discussion on the matter. + verify(kind, id)?; + } else { + match disambiguator { + Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {} + Some(other) => { + report_mismatch(other, Disambiguator::Primitive); + return None; + } + } + } + Some(ItemLink { link: ori_link.link, link_text, did: None, fragment }) + } + Res::Def(kind, id) => { + let (kind, id) = verify(kind, id)?; + let id = clean::register_res(cx, rustc_hir::def::Res::Def(kind, id)); + Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment }) + } } } @@ -1172,28 +1225,47 @@ impl LinkCollector<'_, '_> { &mut self, key: ResolutionInfo, diag: DiagnosticInfo<'_>, + cache_resolution_failure: bool, ) -> Option<(Res, Option<String>)> { // Try to look up both the result and the corresponding side channel value if let Some(ref cached) = self.visited_links.get(&key) { - self.kind_side_channel.set(cached.side_channel.clone()); - return Some(cached.res.clone()); + match cached { + Some(cached) => { + self.kind_side_channel.set(cached.side_channel.clone()); + return Some(cached.res.clone()); + } + None if cache_resolution_failure => return None, + None => { + // Although we hit the cache and found a resolution error, this link isn't + // supposed to cache those. Run link resolution again to emit the expected + // resolution error. + } + } } let res = self.resolve_with_disambiguator(&key, diag); // Cache only if resolved successfully - don't silence duplicate errors - if let Some(res) = &res { + if let Some(res) = res { // Store result for the actual namespace self.visited_links.insert( key, - CachedLink { + Some(CachedLink { res: res.clone(), side_channel: self.kind_side_channel.clone().into_inner(), - }, + }), ); - } - res + Some(res) + } else { + if cache_resolution_failure { + // For reference-style links we only want to report one resolution error + // so let's cache them as well. + self.visited_links.insert(key, None); + } + + None + } } /// After parsing the disambiguator, resolve the main part of the link. @@ -1239,7 +1311,7 @@ impl LinkCollector<'_, '_> { // This could just be a normal link or a broken link // we could potentially check if something is // "intra-doc-link-like" and warn in that case. - return None; + None } Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure( @@ -1250,7 +1322,7 @@ impl LinkCollector<'_, '_> { diag.link_range, msg, ); - return None; + None } } } @@ -1296,16 +1368,18 @@ impl LinkCollector<'_, '_> { .and_then(|(res, fragment)| { // Constructors are picked up in the type namespace. match res { - Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => { + Res::Def(DefKind::Ctor(..), _) => { Err(ResolutionFailure::WrongNamespace(res, TypeNS)) } - _ => match (fragment, extra_fragment.clone()) { - (Some(fragment), Some(_)) => { - // Shouldn't happen but who knows? - Ok((res, Some(fragment))) + _ => { + match (fragment, extra_fragment.clone()) { + (Some(fragment), Some(_)) => { + // Shouldn't happen but who knows? + Ok((res, Some(fragment))) + } + (fragment, None) | (None, fragment) => Ok((res, fragment)), } - (fragment, None) | (None, fragment) => Ok((res, fragment)), - }, + } } }), }; @@ -1344,7 +1418,7 @@ impl LinkCollector<'_, '_> { diag.link_range, candidates.present_items().collect(), ); - return None; + None } } Some(MacroNS) => { @@ -1369,7 +1443,7 @@ impl LinkCollector<'_, '_> { diag.link_range, smallvec![kind], ); - return None; + None } } } @@ -1414,8 +1488,11 @@ impl Disambiguator { ("!", DefKind::Macro(MacroKind::Bang)), ]; for &(suffix, kind) in &suffixes { - if link.ends_with(suffix) { - return Ok((Kind(kind), link.trim_end_matches(suffix))); + if let Some(link) = link.strip_suffix(suffix) { + // Avoid turning `!` or `()` into an empty string + if !link.is_empty() { + return Ok((Kind(kind), link)); + } } } Err(()) @@ -1445,12 +1522,10 @@ impl Disambiguator { } } - /// WARNING: panics on `Res::Err` fn from_res(res: Res) -> Self { match res { Res::Def(kind, _) => Disambiguator::Kind(kind), - Res::PrimTy(_) => Disambiguator::Primitive, - _ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")), + Res::Primitive(_) => Disambiguator::Primitive, } } @@ -1585,6 +1660,7 @@ fn report_diagnostic( let mut diag = lint.build(msg); let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs); + if let Some(sp) = span { diag.set_span(sp); } else { @@ -1598,7 +1674,7 @@ fn report_diagnostic( // Print the line containing the `link_range` and manually mark it with '^'s. diag.note(&format!( "the link appears in this line:\n\n{line}\n\ - {indicator: <before$}{indicator:^<found$}", + {indicator: <before$}{indicator:^<found$}", line = line, indicator = "", before = link_range.start - last_new_line_offset, @@ -1626,6 +1702,7 @@ fn resolution_failure( link_range: Range<usize>, kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { + let tcx = collector.cx.tcx; report_diagnostic( collector.cx, BROKEN_INTRA_DOC_LINKS, @@ -1634,16 +1711,9 @@ fn resolution_failure( dox, &link_range, |diag, sp| { - let item = |res: Res| { - format!( - "the {} `{}`", - res.descr(), - collector.cx.tcx.item_name(res.def_id()).to_string() - ) - }; + let item = |res: Res| format!("the {} `{}`", res.descr(), res.name(tcx),); let assoc_item_not_allowed = |res: Res| { - let def_id = res.def_id(); - let name = collector.cx.tcx.item_name(def_id); + let name = res.name(tcx); format!( "`{}` is {} {}, not a module or type, and cannot have associated items", name, @@ -1709,7 +1779,7 @@ fn resolution_failure( if let Some(module) = last_found_module { let note = if partial_res.is_some() { // Part of the link resolved; e.g. `std::io::nonexistent` - let module_name = collector.cx.tcx.item_name(module); + let module_name = tcx.item_name(module); format!("no item named `{}` in module `{}`", unresolved, module_name) } else { // None of the link resolved; e.g. `Notimported` @@ -1733,14 +1803,10 @@ fn resolution_failure( // Otherwise, it must be an associated item or variant let res = partial_res.expect("None case was handled by `last_found_module`"); - let diagnostic_name; - let (kind, name) = match res { - Res::Def(kind, def_id) => { - diagnostic_name = collector.cx.tcx.item_name(def_id).as_str(); - (Some(kind), &*diagnostic_name) - } - Res::PrimTy(ty) => (None, ty.name_str()), - _ => unreachable!("only ADTs and primitives are in scope at module level"), + let name = res.name(tcx); + let kind = match res { + Res::Def(kind, _) => Some(kind), + Res::Primitive(_) => None, }; let path_description = if let Some(kind) = kind { match kind { @@ -1950,13 +2016,7 @@ fn suggest_disambiguator( } /// Report a link from a public item to a private one. -fn privacy_error( - cx: &DocContext<'_>, - item: &Item, - path_str: &str, - dox: &str, - link_range: Range<usize>, -) { +fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link: &MarkdownLink) { let sym; let item_name = match item.name { Some(name) => { @@ -1968,7 +2028,7 @@ fn privacy_error( let msg = format!("public documentation for `{}` links to private item `{}`", item_name, path_str); - report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| { + report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link.range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "this item is private"); } @@ -1997,53 +2057,49 @@ fn handle_variant( .parent(res.def_id()) .map(|parent| { let parent_def = Res::Def(DefKind::Enum, parent); - let variant = cx.tcx.expect_variant_res(res); + let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap()); (parent_def, Some(format!("variant.{}", variant.ident.name))) }) .ok_or_else(|| ResolutionFailure::NoParentItem.into()) } -// FIXME: At this point, this is basically a copy of the PrimitiveTypeTable -const PRIMITIVES: &[(Symbol, Res)] = &[ - (sym::u8, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))), - (sym::u16, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))), - (sym::u32, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))), - (sym::u64, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))), - (sym::u128, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))), - (sym::usize, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))), - (sym::i8, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))), - (sym::i16, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))), - (sym::i32, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))), - (sym::i64, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))), - (sym::i128, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))), - (sym::isize, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))), - (sym::f32, Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))), - (sym::f64, Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))), - (sym::str, Res::PrimTy(hir::PrimTy::Str)), - (sym::bool, Res::PrimTy(hir::PrimTy::Bool)), - (sym::char, Res::PrimTy(hir::PrimTy::Char)), -]; - /// Resolve a primitive type or value. -fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<(Symbol, Res)> { - is_bool_value(path_str, ns).or_else(|| { - if ns == TypeNS { - // FIXME: this should be replaced by a lookup in PrimitiveTypeTable - let maybe_primitive = Symbol::intern(path_str); - PRIMITIVES.iter().find(|x| x.0 == maybe_primitive).copied() - } else { - None - } - }) -} - -/// Resolve a primitive value. -fn is_bool_value(path_str: &str, ns: Namespace) -> Option<(Symbol, Res)> { - if ns == TypeNS && (path_str == "true" || path_str == "false") { - Some((sym::bool, Res::PrimTy(hir::PrimTy::Bool))) - } else { - None +fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> { + if ns != TypeNS { + return None; } + use PrimitiveType::*; + let prim = match path_str { + "isize" => Isize, + "i8" => I8, + "i16" => I16, + "i32" => I32, + "i64" => I64, + "i128" => I128, + "usize" => Usize, + "u8" => U8, + "u16" => U16, + "u32" => U32, + "u64" => U64, + "u128" => U128, + "f32" => F32, + "f64" => F64, + "char" => Char, + "bool" | "true" | "false" => Bool, + "str" => Str, + // See #80181 for why these don't have symbols associated. + "slice" => Slice, + "array" => Array, + "tuple" => Tuple, + "unit" => Unit, + "pointer" | "*" | "*const" | "*mut" => RawPointer, + "reference" | "&" | "&mut" => Reference, + "fn" => Fn, + "never" | "!" => Never, + _ => return None, + }; + debug!("resolved primitives {:?}", prim); + Some(Res::Primitive(prim)) } fn strip_generics_from_path(path_str: &str) -> Result<String, ResolutionFailure<'static>> { diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 60fcbe74872..9b0ae09cb3f 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -58,11 +58,11 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in &new_items { - if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.kind { + if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind { if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() { let target = items .iter() - .find_map(|item| match item.kind { + .find_map(|item| match *item.kind { TypedefItem(ref t, true) => Some(&t.type_), _ => None, }) @@ -78,7 +78,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } new_items.retain(|it| { - if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.kind { + if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind { cleaner.keep_item(for_) || trait_.as_ref().map_or(false, |t| cleaner.keep_item(t)) || blanket_impl.is_some() @@ -124,7 +124,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } if let Some(ref mut it) = krate.module { - if let ModuleItem(Module { ref mut items, .. }) = it.kind { + if let ModuleItem(Module { ref mut items, .. }) = *it.kind { items.extend(synth.impls); items.extend(new_items); } else { diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 1c1141e7c81..a513c2abc87 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -9,7 +9,7 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; -use rustc_middle::lint::LintSource; +use rustc_middle::lint::LintLevelSource; use rustc_session::lint; crate const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { @@ -59,7 +59,7 @@ impl crate::doctest::Tester for Tests { crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { if matches!( - item.kind, + *item.kind, clean::StructFieldItem(_) | clean::VariantItem(_) | clean::AssocConstItem(_, _) @@ -77,7 +77,7 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local()); let (level, source) = cx.tcx.lint_level_at_node(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id); - level != lint::Level::Allow || matches!(source, LintSource::Default) + level != lint::Level::Allow || matches!(source, LintLevelSource::Default) } crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs index a7a1ba1118d..38ec2bef0ad 100644 --- a/src/librustdoc/passes/html_tags.rs +++ b/src/librustdoc/passes/html_tags.rs @@ -59,7 +59,7 @@ fn drop_tag( continue; } let last_tag_name_low = last_tag_name.to_lowercase(); - if ALLOWED_UNCLOSED.iter().any(|&at| at == &last_tag_name_low) { + if ALLOWED_UNCLOSED.iter().any(|&at| at == last_tag_name_low) { continue; } // `tags` is used as a queue, meaning that everything after `pos` is included inside it. diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 51818d7faf0..7ac42c75992 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -14,9 +14,6 @@ crate use stripper::*; mod non_autolinks; crate use self::non_autolinks::CHECK_NON_AUTOLINKS; -mod collapse_docs; -crate use self::collapse_docs::COLLAPSE_DOCS; - mod strip_hidden; crate use self::strip_hidden::STRIP_HIDDEN; @@ -84,7 +81,6 @@ crate const PASSES: &[Pass] = &[ CHECK_PRIVATE_ITEMS_DOC_TESTS, STRIP_HIDDEN, UNINDENT_COMMENTS, - COLLAPSE_DOCS, STRIP_PRIVATE, STRIP_PRIV_IMPORTS, PROPAGATE_DOC_CFG, @@ -99,7 +95,6 @@ crate const PASSES: &[Pass] = &[ /// The list of passes run by default. crate const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::always(COLLECT_TRAIT_IMPLS), - ConditionalPass::always(COLLAPSE_DOCS), ConditionalPass::always(UNINDENT_COMMENTS), ConditionalPass::always(CHECK_PRIVATE_ITEMS_DOC_TESTS), ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 6b59eb8cf28..a276b7a6337 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -26,9 +26,7 @@ crate fn strip_hidden(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { // strip all impls referencing stripped items let mut stripper = ImplStripper { retained: &retained }; - let krate = stripper.fold_crate(krate); - - krate + stripper.fold_crate(krate) } struct Stripper<'a> { @@ -41,7 +39,7 @@ impl<'a> DocFolder for Stripper<'a> { if i.attrs.lists(sym::doc).has_word(sym::hidden) { debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name); // use a dedicated hidden item for given item type if any - match i.kind { + match *i.kind { clean::StructFieldItem(..) | clean::ModuleItem(..) => { // We need to recurse into stripped modules to // strip things like impl methods but when doing so @@ -49,7 +47,7 @@ impl<'a> DocFolder for Stripper<'a> { let old = mem::replace(&mut self.update_retained, false); let ret = StripItem(self.fold_item_recur(i)).strip(); self.update_retained = old; - return ret; + return Some(ret); } _ => return None, } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 444fd593ec9..a1924422f0e 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -13,7 +13,7 @@ crate struct Stripper<'a> { impl<'a> DocFolder for Stripper<'a> { fn fold_item(&mut self, i: Item) -> Option<Item> { - match i.kind { + match *i.kind { clean::StrippedItem(..) => { // We need to recurse into stripped modules to strip things // like impl methods but when doing so we must not add any @@ -51,7 +51,7 @@ impl<'a> DocFolder for Stripper<'a> { clean::StructFieldItem(..) => { if !i.visibility.is_public() { - return StripItem(i).strip(); + return Some(StripItem(i).strip()); } } @@ -61,7 +61,7 @@ impl<'a> DocFolder for Stripper<'a> { let old = mem::replace(&mut self.update_retained, false); let ret = StripItem(self.fold_item_recur(i)).strip(); self.update_retained = old; - return ret; + return Some(ret); } } @@ -86,7 +86,7 @@ impl<'a> DocFolder for Stripper<'a> { clean::KeywordItem(..) => {} } - let fastreturn = match i.kind { + let fastreturn = match *i.kind { // nothing left to do for traits (don't want to filter their // methods out, visibility controlled by the trait) clean::TraitItem(..) => true, @@ -121,7 +121,7 @@ crate struct ImplStripper<'a> { impl<'a> DocFolder for ImplStripper<'a> { fn fold_item(&mut self, i: Item) -> Option<Item> { - if let clean::ImplItem(ref imp) = i.kind { + if let clean::ImplItem(ref imp) = *i.kind { // emptied none trait impls can be stripped if imp.trait_.is_none() && imp.items.is_empty() { return None; @@ -160,7 +160,7 @@ crate struct ImportStripper; impl DocFolder for ImportStripper { fn fold_item(&mut self, i: Item) -> Option<Item> { - match i.kind { + match *i.kind { clean::ExternCrateItem(..) | clean::ImportItem(..) if !i.visibility.is_public() => None, _ => Some(self.fold_item_recur(i)), } diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index d0345d1e48c..1cad480d4e8 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -68,7 +68,7 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { let min_indent = match docs .iter() .map(|fragment| { - fragment.doc.lines().fold(usize::MAX, |min_indent, line| { + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { if line.chars().all(|c| c.is_whitespace()) { min_indent } else { @@ -87,7 +87,7 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { }; for fragment in docs { - if fragment.doc.lines().count() == 0 { + if fragment.doc.as_str().lines().count() == 0 { continue; } @@ -97,18 +97,6 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { min_indent }; - fragment.doc = fragment - .doc - .lines() - .map(|line| { - if line.chars().all(|c| c.is_whitespace()) { - line.to_string() - } else { - assert!(line.len() >= min_indent); - line[min_indent..].to_string() - } - }) - .collect::<Vec<_>>() - .join("\n"); + fragment.indent = min_indent; } } diff --git a/src/librustdoc/passes/unindent_comments/tests.rs b/src/librustdoc/passes/unindent_comments/tests.rs index 9dec71f7683..9c9924841b9 100644 --- a/src/librustdoc/passes/unindent_comments/tests.rs +++ b/src/librustdoc/passes/unindent_comments/tests.rs @@ -1,21 +1,27 @@ use super::*; use rustc_span::source_map::DUMMY_SP; +use rustc_span::symbol::Symbol; +use rustc_span::with_default_session_globals; fn create_doc_fragment(s: &str) -> Vec<DocFragment> { vec![DocFragment { line: 0, span: DUMMY_SP, parent_module: None, - doc: s.to_string(), + doc: Symbol::intern(s), kind: DocFragmentKind::SugaredDoc, + need_backline: false, + indent: 0, }] } #[track_caller] fn run_test(input: &str, expected: &str) { - let mut s = create_doc_fragment(input); - unindent_fragments(&mut s); - assert_eq!(s[0].doc, expected); + with_default_session_globals(|| { + let mut s = create_doc_fragment(input); + unindent_fragments(&mut s); + assert_eq!(&s.iter().collect::<String>(), expected); + }); } #[test] |
