diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index fe27498b900f3..853c4bfc9ca3f 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -283,6 +283,12 @@ pub(crate) unsafe fn create_module<'ll>( llvm::LLVMRustSetModuleCodeModel(llmod, to_llvm_code_model(sess.code_model())); } + if let Some(large_data_threshold) = sess.opts.unstable_opts.large_data_threshold { + unsafe { + llvm::LLVMRustSetModuleLargeDataThreshold(llmod, large_data_threshold); + } + } + // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1a60b59a93525..8c9bf55b14e45 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2508,6 +2508,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); + pub(crate) fn LLVMRustSetModuleLargeDataThreshold(M: &Module, Threshold: u64); pub(crate) fn LLVMRustBufferPtr(p: &Buffer) -> *const u8; pub(crate) fn LLVMRustBufferLen(p: &Buffer) -> usize; pub(crate) fn LLVMRustBufferFree(p: &'static mut Buffer); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 81a506c63a8ee..6fd78c6bde4be 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -529,6 +529,17 @@ extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)( extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)( void *); // LlvmSelfProfiler +#if LLVM_VERSION_GE(24, 0) +std::string LLVMRustwrappedIrGetName(const llvm::IRUnitRef &WrappedIr) { + if (const auto *Cast = dyn_cast(WrappedIr)) + return Cast->getName().str(); + if (const auto *Cast = dyn_cast(WrappedIr)) + return Cast->getName().str(); + if (const auto *Cast = dyn_cast(WrappedIr)) + return Cast->getName().str(); + if (const auto *Cast = dyn_cast(WrappedIr)) + return Cast->getName(); +#else std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { if (const auto *Cast = any_cast(&WrappedIr)) return (*Cast)->getName().str(); @@ -538,6 +549,7 @@ std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { return (*Cast)->getName().str(); if (const auto *Cast = any_cast(&WrappedIr)) return (*Cast)->getName(); +#endif return ""; } @@ -546,15 +558,26 @@ void LLVMSelfProfileInitializeCallbacks( LLVMRustSelfProfileBeforePassCallback BeforePassCallback, LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { PIC.registerBeforeNonSkippedPassCallback( +#if LLVM_VERSION_GE(24, 0) + [LlvmSelfProfiler, BeforePassCallback](StringRef Pass, + llvm::IRUnitRef Ir) { +#else [LlvmSelfProfiler, BeforePassCallback](StringRef Pass, llvm::Any Ir) { +#endif std::string PassName = Pass.str(); std::string IrName = LLVMRustwrappedIrGetName(Ir); BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); }); PIC.registerAfterPassCallback( +#if LLVM_VERSION_GE(24, 0) + [LlvmSelfProfiler, + AfterPassCallback](StringRef Pass, llvm::IRUnitRef IR, + const PreservedAnalyses &Preserved) { +#else [LlvmSelfProfiler, AfterPassCallback]( StringRef Pass, llvm::Any IR, const PreservedAnalyses &Preserved) { +#endif AfterPassCallback(LlvmSelfProfiler); }); @@ -564,17 +587,27 @@ void LLVMSelfProfileInitializeCallbacks( AfterPassCallback(LlvmSelfProfiler); }); +#if LLVM_VERSION_GE(24, 0) + PIC.registerBeforeAnalysisCallback([LlvmSelfProfiler, BeforePassCallback]( + StringRef Pass, llvm::IRUnitRef Ir) { +#else PIC.registerBeforeAnalysisCallback( [LlvmSelfProfiler, BeforePassCallback](StringRef Pass, llvm::Any Ir) { - std::string PassName = Pass.str(); - std::string IrName = LLVMRustwrappedIrGetName(Ir); - BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); - }); +#endif + std::string PassName = Pass.str(); + std::string IrName = LLVMRustwrappedIrGetName(Ir); + BeforePassCallback(LlvmSelfProfiler, PassName.c_str(), IrName.c_str()); + }); +#if LLVM_VERSION_GE(24, 0) + PIC.registerAfterAnalysisCallback([LlvmSelfProfiler, AfterPassCallback]( + StringRef Pass, llvm::IRUnitRef Ir) { +#else PIC.registerAfterAnalysisCallback( [LlvmSelfProfiler, AfterPassCallback](StringRef Pass, llvm::Any Ir) { - AfterPassCallback(LlvmSelfProfiler); - }); +#endif + AfterPassCallback(LlvmSelfProfiler); + }); } enum class LLVMRustOptStage { @@ -1185,6 +1218,11 @@ extern "C" void LLVMRustSetModuleCodeModel(LLVMModuleRef M, unwrap(M)->setCodeModel(*CM); } +extern "C" void LLVMRustSetModuleLargeDataThreshold(LLVMModuleRef M, + uint64_t Threshold) { + unwrap(M)->setLargeDataThreshold(Threshold); +} + // Here you'll find an implementation of ThinLTO as used by the Rust compiler // right now. This ThinLTO support is only enabled on "recent ish" versions of // LLVM, and otherwise it's just blanket rejected from other compilers. diff --git a/library/std/src/sys/path/windows/tests.rs b/library/std/src/sys/path/windows/tests.rs index 830f48d7bfc94..4ca47fc10c2e1 100644 --- a/library/std/src/sys/path/windows/tests.rs +++ b/library/std/src/sys/path/windows/tests.rs @@ -83,6 +83,9 @@ fn verbatim() { // Make sure opening a drive will work. check("Z:", "Z:"); + // Verbatim drive paths begin with `LETTER:\`. `/` is just a regular character here + check(r"\\?\C:/path\somewhere", r"\\?\C:/path\somewhere"); + // A path that contains null is not a valid path. assert!(maybe_verbatim(Path::new("\0")).is_err()); } @@ -93,9 +96,23 @@ fn parse_prefix(path: &str) -> Option> { #[test] fn test_parse_prefix_verbatim() { - let prefix = Some(Prefix::VerbatimDisk(b'C')); - assert_eq!(prefix, parse_prefix(r"\\?\C:/windows/system32/notepad.exe")); - assert_eq!(prefix, parse_prefix(r"\\?\C:\windows\system32\notepad.exe")); + assert_eq!( + parse_prefix(r"\\?\C:\windows\system32\notepad.exe"), + Some(Prefix::VerbatimDisk(b'C')), + ); +} + +#[test] +fn test_verbatim_disk_issue_161651() { + use crate::path::Path; + + // This is not a `VerbatimDisk` path, because `/` is not a separator in verbatim paths! + assert_eq!( + parse_prefix(r"\\?\C:/windows\system32"), + Some(Prefix::Verbatim(OsStr::new("C:/windows"))), + ); + + assert_ne!(Path::new(r"\\?\C:/foo"), Path::new(r"\\?\C:\foo")); } #[test] diff --git a/library/std/src/sys/path/windows_prefix.rs b/library/std/src/sys/path/windows_prefix.rs index b9dfe754485ab..5413269e9edee 100644 --- a/library/std/src/sys/path/windows_prefix.rs +++ b/library/std/src/sys/path/windows_prefix.rs @@ -142,7 +142,7 @@ fn parse_drive(path: &OsStr) -> Option { // Parses a drive prefix exactly, e.g. "C:" fn parse_drive_exact(path: &OsStr) -> Option { // only parse two bytes: the drive letter and the drive separator - if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { + if path.as_encoded_bytes().get(2).map(|&x| is_verbatim_sep(x)).unwrap_or(true) { parse_drive(path) } else { None diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 8997b8ad192dc..4d42437fbd871 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -989,14 +989,14 @@ pub fn test_decompositions_windows() { ); t!("\\\\?\\C:/foo/bar", - iter: ["\\\\?\\C:", "\\", "foo/bar"], + iter: ["\\\\?\\C:/foo/bar"], has_root: true, is_absolute: true, - parent: Some("\\\\?\\C:/"), - file_name: Some("foo/bar"), - file_stem: Some("foo/bar"), + parent: None, + file_name: None, + file_stem: None, extension: None, - file_prefix: Some("foo/bar") + file_prefix: None ); t!("\\\\.\\foo\\bar", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0bee8e8855c1c..b216075c56d52 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1102,6 +1102,14 @@ impl CommandLineStep for IntrinsicTest { builder.info(&format!("Skipping intrinsic-test, as it is not available for {host}")); return; } + // intrinsic-test shells out to `cargo` and `rustfmt` make bootstrap's + // managed binaries findable by prepending their dirs to PATH. + let Some(rustfmt_path) = builder.ensure(InternalRustfmt) else { + eprintln!( + "WARNING: intrinsic-test skipped because rustfmt is required but not available on this channel" + ); + return; + }; let (input_file, skip_file, cflags, sde_runner) = if host.contains("x86_64-unknown-linux") { let Some(sde) = &builder.config.sde else { @@ -1172,14 +1180,6 @@ impl CommandLineStep for IntrinsicTest { cmd.arg("--cc-arg-style").arg("gcc"); cmd.env("CC", builder.cc(host)); cmd.env("CFLAGS", cflags); - // intrinsic-test shells out to `cargo` and `rustfmt` make bootstrap's - // managed binaries findable by prepending their dirs to PATH. - let Some(rustfmt_path) = builder.ensure(InternalRustfmt) else { - eprintln!( - "WARNING: intrinsic-test skipped because rustfmt is required but not available on this channel" - ); - return; - }; let mut path_dirs: Vec = Vec::new(); if let Some(cargo_dir) = builder.initial_cargo.parent() { diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 0e51a7c105478..c02fd567ac6c9 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -464,6 +464,13 @@ impl Cargo { let lto_cflag = if matches!(self.mode, Mode::Rustc | Mode::ToolRustcPrivate) && is_lto_stage(&self.compiler) && builder.cc_tool(target).is_like_clang() + // Exclude aarch64-linux as we can't assume the user has an LTO-capable linker + // (and these files get distributed in the rustc-dev component). + // FIXME: this means the argument above about doing this for rustc but not std makes + // no sense. We distribute rlibs for both, so both need to be linkable by users. I + // guess we just don't want to risk this for std, but are less worried about + // breaking rustc-dev. + && !target.starts_with("aarch64-unknown-linux") { match builder.config.rust_lto { RustcLto::Thin => Some("-flto=thin"), @@ -474,7 +481,7 @@ impl Cargo { None }; - // Extend `CXXFLAGS_$TARGET` with our extra flags. + // Extend `CFLAGS_$TARGET` with our extra flags. let env = format!("CFLAGS_{triple_underscored}"); let mut cflags = builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C).join(" "); diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 9dee33ef6eb85..abd436bb5561c 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -456,3 +456,31 @@ note: the lint level is defined here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: Remove explicit link instead ``` + +## `invalid_markdown_table` + +This lint is **warn-by-default**. It detects unescaped pipes (`|`) in table rows which +lead to some row cells being ignored. For example: + +```rust +//! | col1 | +//! | ---- | +//! | `code_with(|arg| arg)` | +``` + +Which will give: + +```text +error: table row has too many columns + --> $DIR/foo.rs:5:18 + | +5 | //! | `code_with(|arg| arg)` | + | ^ help: any content after this column divider is discarded + | + = help: to escape `|` characters in tables, add a `\` before them like `\|` +note: the lint level is defined here + --> $DIR/foo.rs:1:9 + | +1 | #![deny(rustdoc::invalid_markdown_table)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``` diff --git a/src/librustdoc/calculate_doc_coverage.rs b/src/librustdoc/calculate_doc_coverage.rs index ae05e362f2383..3c180d9afd25d 100644 --- a/src/librustdoc/calculate_doc_coverage.rs +++ b/src/librustdoc/calculate_doc_coverage.rs @@ -1,4 +1,7 @@ -//! Calculates information used for the --show-coverage flag. +//! Calculates information used for the `--show-coverage` flag. +//! +//! More specifically, it counts the number of items with documentation, ones with +//! "examples" (i.e., non-ignored Rust code blocks) and various totals. use std::collections::BTreeMap; use std::fs::{File, create_dir_all}; @@ -17,7 +20,7 @@ use crate::core::DocContext; use crate::docfs::PathError; use crate::error::Error; use crate::html::markdown::{ErrorCodes, find_testable_code}; -use crate::passes::{Tests, should_have_doc_example}; +use crate::passes::check_doc_test_visibility::{Tests, should_have_doc_example}; use crate::visit::DocVisitor; use crate::{clean, try_err}; diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 3ad07dd35ccf0..941632f0d283a 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -26,7 +26,6 @@ use crate::externalfiles::ExternalHtml; use crate::html::markdown::IdMap; use crate::html::render::StylePath; use crate::html::static_files; -use crate::passes::{self, Condition}; use crate::scrape_examples::{AllCallLocations, ScrapeExamplesOptions}; use crate::{html, opts, theme}; @@ -428,40 +427,7 @@ impl Options { // check for deprecated options check_deprecated_options(matches, dcx); - if matches.opt_strs("passes") == ["list"] { - println!("Available passes for running rustdoc:"); - for pass in passes::PASSES { - println!("{:>20} - {}", pass.name, pass.description); - } - println!("\nDefault passes for rustdoc:"); - for p in passes::DEFAULT_PASSES { - print!("{:>20}", p.pass.name); - println_condition(p.condition); - } - - if nightly_options::match_is_nightly_build(matches) { - println!("\nPasses run with `--show-coverage`:"); - for p in passes::COVERAGE_PASSES { - print!("{:>20}", p.pass.name); - println_condition(p.condition); - } - } - - fn println_condition(condition: Condition) { - use Condition::*; - match condition { - Always => println!(), - WhenDocumentPrivate => println!(" (when --document-private-items)"), - WhenNotDocumentPrivate => println!(" (when not --document-private-items)"), - WhenNotDocumentHidden => println!(" (when not --document-hidden-items)"), - } - } - - return None; - } - let should_test = matches.opt_present("test"); - let show_coverage = matches.opt_present("show-coverage"); let output_format_s = matches.opt_str("output-format"); let output_format = match output_format_s.as_deref() { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c30c68a1fa5c8..ad6718e75466e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -26,7 +26,7 @@ pub(crate) use rustc_session::config::{Options, UnstableOptions}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_structures::CrateType; -use tracing::{debug, info}; +use tracing::debug; use crate::clean::inline::build_trait; use crate::clean::{self, ItemId}; @@ -34,8 +34,6 @@ use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; use crate::html::macro_expansion::{ExpandedCode, source_macro_expansion}; use crate::passes; -use crate::passes::Condition::*; -use crate::passes::collect_intra_doc_links::LinkCollector; pub(crate) struct DocContext<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, @@ -428,31 +426,8 @@ pub(crate) fn run_global_ctxt( ); } - info!("Executing passes"); - - let mut visited = FxHashMap::default(); - let mut ambiguous = FxIndexMap::default(); - - for p in passes::defaults(show_coverage) { - let run = match p.condition { - Always => true, - WhenDocumentPrivate => ctxt.document_private(), - WhenNotDocumentPrivate => !ctxt.document_private(), - WhenNotDocumentHidden => !ctxt.document_hidden(), - }; - if run { - debug!("running pass {}", p.pass.name); - if let Some(run_fn) = p.pass.run { - krate = tcx.sess.time(p.pass.name, || run_fn(krate, &mut ctxt)); - } else { - let (k, LinkCollector { visited_links, ambiguous_links, .. }) = - passes::collect_intra_doc_links::collect_intra_doc_links(krate, &mut ctxt); - krate = k; - visited = visited_links; - ambiguous = ambiguous_links; - } - } - } + let store; + (krate, store) = passes::run(krate, &mut ctxt, show_coverage); if show_coverage && let Err(error) = crate::calculate_doc_coverage::run(&krate, &mut ctxt, &render_options) @@ -466,9 +441,7 @@ pub(crate) fn run_global_ctxt( krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate, &render_options)); - let mut collector = - LinkCollector { cx: &mut ctxt, visited_links: visited, ambiguous_links: ambiguous }; - collector.resolve_ambiguities(); + passes::finalize(&mut ctxt, store); tcx.dcx().abort_if_errors(); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 5e020ece6e6b9..7d3e80fccb2c7 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1595,8 +1595,8 @@ impl MarkdownSummaryLine<'_> { html::push_html(&mut s, without_paragraphs); - let has_more_content = - matches!(summary.inner.peek(), Some(Event::Start(_))) || summary.skipped_tags > 0; + let has_more_content = matches!(summary.inner.peek(), Some(Event::Start(_) | Event::Rule)) + || summary.skipped_tags > 0; (s, has_more_content) } diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 1c3d1c421b545..5d8675aecb86a 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -209,6 +209,17 @@ declare_rustdoc_lint! { "detects unused footnote definitions" } +declare_rustdoc_lint! { + /// This lint is **warn-by-default**. It detects unescaped pipes in table rows which + /// lead to some row cells being ignored. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_markdown_table + INVALID_MARKDOWN_TABLE, + Warn, + "detects unescaped pipe in table rows in doc comments" +} + pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -224,6 +235,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { REDUNDANT_EXPLICIT_LINKS, BROKEN_FOOTNOTE, UNUSED_FOOTNOTE_DEFINITION, + INVALID_MARKDOWN_TABLE, ] }); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 13452539c8295..17f69b68b537d 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -10,27 +10,19 @@ use rustc_macros::Diagnostic; use rustc_middle::lint::LintLevelSource; use tracing::debug; -use super::Pass; -use crate::clean; use crate::clean::utils::inherits_doc_hidden; -use crate::clean::*; +use crate::clean::{self, *}; use crate::core::DocContext; use crate::html::markdown::{ CodeLineMapping, ErrorCodes, Ignore, LangString, MdRelLine, find_testable_code, }; use crate::visit::DocVisitor; -pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass { - name: "check_doc_test_visibility", - run: Some(check_doc_test_visibility), - description: "run various visibility-related lints on doctests", -}; - struct DocTestVisibilityLinter<'a, 'tcx> { cx: &'a mut DocContext<'tcx>, } -pub(crate) fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate { +pub(super) fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate { let mut coll = DocTestVisibilityLinter { cx }; coll.visit_crate(&krate); krate diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 38d285f4fe86d..7aee8dba82e1a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,6 +1,6 @@ -//! This module implements [RFC 1946]: Intra-rustdoc-links +//! Resolves intra-doc links ([RFC 1946]). //! -//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md +//! [RFC 1946]: https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html use std::borrow::Cow; use std::fmt::Display; @@ -36,23 +36,19 @@ use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType, reexport_ use crate::core::DocContext; use crate::html::markdown::{MarkdownLink, MarkdownLinkRange, markdown_links}; use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}; -use crate::passes::Pass; use crate::visit::DocVisitor; -pub(crate) const COLLECT_INTRA_DOC_LINKS: Pass = - Pass { name: "collect-intra-doc-links", run: None, description: "resolves intra-doc links" }; - -pub(crate) fn collect_intra_doc_links<'a, 'tcx>( +pub(super) fn collect_intra_doc_links( krate: Crate, - cx: &'a mut DocContext<'tcx>, -) -> (Crate, LinkCollector<'a, 'tcx>) { - let mut collector = LinkCollector { - cx, - visited_links: FxHashMap::default(), - ambiguous_links: FxIndexMap::default(), - }; + cx: &mut DocContext<'_>, +) -> (Crate, LinkCollection) { + let mut collector = LinkCollector { cx, links: LinkCollection::default() }; collector.visit_crate(&krate); - (krate, collector) + (krate, collector.links) +} + +pub(super) fn resolve_ambiguous_links(links: LinkCollection, cx: &mut DocContext<'_>) { + LinkCollector { cx, links }.resolve_ambiguities(); } fn filter_assoc_items_by_name_and_namespace( @@ -252,11 +248,16 @@ impl OwnedDiagnosticInfo { } } -pub(crate) struct LinkCollector<'a, 'tcx> { - pub(crate) cx: &'a mut DocContext<'tcx>, +struct LinkCollector<'a, 'tcx> { + cx: &'a mut DocContext<'tcx>, + links: LinkCollection, +} + +#[derive(Default)] +pub(super) struct LinkCollection { /// 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). - pub(crate) visited_links: FxHashMap)>>, + visited: FxHashMap)>>, /// According to `rustc_resolve`, these links are ambiguous. /// /// However, we cannot link to an item that has been stripped from the documentation. If all @@ -267,7 +268,7 @@ pub(crate) struct LinkCollector<'a, 'tcx> { /// We could get correct results by simply delaying everything. This would have fewer happy /// codepaths, but we want to distinguish different kinds of error conditions, and this is easy /// to do by resolving links as soon as possible. - pub(crate) ambiguous_links: FxIndexMap<(ItemId, String), Vec>, + ambiguous: FxIndexMap<(ItemId, String), Vec>, } pub(crate) struct AmbiguousLinks { @@ -1216,7 +1217,8 @@ impl LinkCollector<'_, '_> { resolved, }; - self.ambiguous_links + self.links + .ambiguous .entry((item.item_id, path_str.to_string())) .or_default() .push(links); @@ -1272,8 +1274,8 @@ impl LinkCollector<'_, '_> { || !did.is_local() } - pub(crate) fn resolve_ambiguities(&mut self) { - let mut ambiguous_links = mem::take(&mut self.ambiguous_links); + fn resolve_ambiguities(&mut self) { + let mut ambiguous_links = mem::take(&mut self.links.ambiguous); for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() { for info in info_items { info.resolved.retain(|(res, _)| match res { @@ -1523,7 +1525,7 @@ impl LinkCollector<'_, '_> { // which we want in some cases but not in others. cache_errors: bool, ) -> Option)>> { - if let Some(res) = self.visited_links.get(&key) + if let Some(res) = self.links.visited.get(&key) && (res.is_some() || cache_errors) { return res.clone().map(|r| vec![r]); @@ -1570,9 +1572,9 @@ impl LinkCollector<'_, '_> { out.push((res, fragment)); } if let [r] = out.as_slice() { - self.visited_links.insert(key, Some(r.clone())); + self.links.visited.insert(key, Some(r.clone())); } else if cache_errors { - self.visited_links.insert(key, None); + self.links.visited.insert(key, None); } Some(out) } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 1651690653786..1bfac8e67748c 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -1,6 +1,7 @@ -//! Collects trait impls for each item in the crate. For example, if a crate -//! defines a struct that implements a trait, this pass will note that the -//! struct implements that trait. +//! Collects trait impls for each item in the crate. +//! +//! For example, if a crate defines a struct that implements a trait, +//! this pass will note that the struct implements that trait. use rustc_data_structures::fx::FxHashSet; use rustc_errors::FatalError; @@ -11,19 +12,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::kw; use tracing::debug; -use super::Pass; use crate::clean::*; use crate::core::DocContext; use crate::formats::cache::Cache; use crate::visit::DocVisitor; -pub(crate) const COLLECT_TRAIT_IMPLS: Pass = Pass { - name: "collect-trait-impls", - run: Some(collect_trait_impls), - description: "retrieves trait impls for items in the crate", -}; - -pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate { +pub(super) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate { let tcx = cx.tcx; // We need to check if there are errors before running this pass because it would crash when // we try to get auto and blanket implementations. diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index bb952b32393cf..9988aa683d6d6 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -1,26 +1,22 @@ -//! Runs several rustdoc lints, consolidating them into a single pass for -//! efficiency and simplicity. +//! Runs several rustdoc lints, consolidating them into a single pass for efficiency and simplicity. mod bare_urls; mod check_code_block_syntax; mod footnotes; mod html_tags; +mod invalid_markdown_table; mod redundant_explicit_links; mod unescaped_backticks; -use super::Pass; use crate::clean::*; use crate::core::DocContext; use crate::visit::DocVisitor; -pub(crate) const RUN_LINTS: Pass = - Pass { name: "run-lints", run: Some(run_lints), description: "runs some of rustdoc's lints" }; - struct Linter<'a, 'tcx> { cx: &'a mut DocContext<'tcx>, } -pub(crate) fn run_lints(krate: Crate, cx: &mut DocContext<'_>) -> Crate { +pub(super) fn lint(krate: Crate, cx: &mut DocContext<'_>) -> Crate { Linter { cx }.visit_crate(&krate); krate } @@ -35,6 +31,7 @@ impl DocVisitor<'_> for Linter<'_, '_> { if !dox.is_empty() { let may_have_link = dox.contains(&[':', '['][..]); let may_have_block_comment_or_html = dox.contains(['<', '>']); + let may_have_table = dox.contains(&['|'][..]); // ~~~rust // // This is a real, supported commonmark syntax for block code // ~~~ @@ -51,6 +48,9 @@ impl DocVisitor<'_> for Linter<'_, '_> { if may_have_block_comment_or_html { html_tags::visit_item(self.cx, item, hir_id, &dox); } + if may_have_table { + invalid_markdown_table::visit_item(self.cx, item, hir_id, &dox); + } } self.visit_item_recur(item) diff --git a/src/librustdoc/passes/lint/invalid_markdown_table.rs b/src/librustdoc/passes/lint/invalid_markdown_table.rs new file mode 100644 index 0000000000000..dd44f2ec92445 --- /dev/null +++ b/src/librustdoc/passes/lint/invalid_markdown_table.rs @@ -0,0 +1,120 @@ +//! Detects table rows where some content seems to have been discarded because there are too many +//! pipe characters. + +use std::ops::Range; + +use rustc_hir::HirId; +use rustc_macros::Diagnostic; +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Parser, Tag, TagEnd}; +use rustc_resolve::rustdoc::source_span_for_markdown_range; + +use crate::clean::*; +use crate::core::DocContext; +use crate::html::markdown::main_body_opts; + +#[derive(Diagnostic)] +#[diag("table row has too many columns")] +#[help(r"to escape `|` characters in tables, add a `\` before them like `\|`")] +struct UnescapedPipeInTableCell { + #[primary_span] + #[label("any content after this column divider is discarded")] + span: rustc_span::Span, +} + +#[derive(Diagnostic)] +#[diag("unused content after last table cell")] +struct ContentAfterLastPipe { + #[primary_span] + #[label("this content is discarded")] + span: rustc_span::Span, +} + +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { + let mut p = Parser::new_ext(dox, main_body_opts()).into_offset_iter(); + + while let Some((event, _range)) = p.next() { + if Event::Start(Tag::TableRow) == event { + let mut prev_range = None; + while let Some((event, range)) = p.next() { + match event { + Event::End(TagEnd::TableCell) => { + prev_range = Some(range); + } + Event::End(TagEnd::TableRow) => { + if let Some(prev_range) = &prev_range + // So here what is happening: when `pulldown-cmark` is parsing a table + // and a table row has too many cells, it doesn't emit events for the + // extra cells. So the only way for us to know these extra cells exist + // is to compare the row's span with the last emitted cell event's span. + // If the span ends don't match, then there are extra cells. + && prev_range.end + 1 < range.end + { + // Something seems wrong, the range diff doesn't match, some content + // was left out. + let mut after_last_cell_range = + Range { start: prev_range.end + 1, end: range.end }; + if dox[after_last_cell_range.clone()].trim().is_empty() { + // Seems all good so let's ignore it and continue;. + continue; + } + // Check if any pipes appear after the end of the row. + let mut iter = dox[after_last_cell_range.clone()].bytes().peekable(); + let mut found_divider = false; + while let Some(c) = iter.next() { + // the sequence `\\|` still escapes the pipe because GFM + // processes block structures like tables in its own pass + if c == b'\\' && iter.peek() == Some(&b'|') { + iter.next(); + } else if c == b'|' { + found_divider = true; + break; + } + } + if found_divider { + // Seems like a pipe was not escaped as it should have been. + let last_cell_separator = + Range { start: prev_range.end, end: prev_range.end + 1 }; + + if let Some((span, _)) = source_span_for_markdown_range( + cx.tcx, + dox, + &last_cell_separator, + &item.attrs.doc_strings, + ) { + cx.tcx.emit_node_span_lint( + crate::lint::INVALID_MARKDOWN_TABLE, + hir_id, + span, + UnescapedPipeInTableCell { span }, + ); + } + } else { + // An unclosed cell maybe? There is content after the last cell so + // let's lint about it. + let content = &dox[after_last_cell_range.clone()]; + after_last_cell_range.end -= + content.len() - content.trim_end().len(); + + if let Some((span, _)) = source_span_for_markdown_range( + cx.tcx, + dox, + &after_last_cell_range, + &item.attrs.doc_strings, + ) { + cx.tcx.emit_node_span_lint( + crate::lint::INVALID_MARKDOWN_TABLE, + hir_id, + span, + ContentAfterLastPipe { span }, + ); + } + } + } + } + Event::End(TagEnd::Table) => break, + _ => {} + } + } + } + } +} diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index b5f7d342e889a..35e254c754d68 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -12,9 +12,11 @@ use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, source_span_for_mar use rustc_span::def_id::{DefId, ModId}; use rustc_span::{Span, Symbol}; -use crate::clean::Item; use crate::clean::utils::{find_nearest_parent_module, inherits_doc_hidden}; +use crate::clean::{Item, inline}; use crate::core::DocContext; +use crate::formats::item_type::ItemType; +use crate::html::format::href_relative_parts; use crate::html::markdown::main_body_opts; #[derive(Debug)] @@ -71,12 +73,13 @@ fn check_redundant_explicit_link_for_did( return; }; - check_redundant_explicit_link(cx, item, hir_id, doc, resolutions); + check_redundant_explicit_link(cx, item, module_id.into(), hir_id, doc, resolutions); } fn check_redundant_explicit_link<'md>( cx: &DocContext<'_>, item: &Item, + module_id: DefId, hir_id: HirId, doc: &'md str, resolutions: &DocLinkResMap, @@ -114,41 +117,41 @@ fn check_redundant_explicit_link<'md>( continue; } - if dest_url.ends_with(resolvable_link) || resolvable_link.ends_with(&*dest_url) { - let check_result = match link_type { - LinkType::Inline | LinkType::ReferenceUnknown => { - check_inline_or_reference_unknown_redundancy( - cx, - item, - hir_id, - doc, - resolutions, - link_range, - dest_url.to_string(), - link_data, - if link_type == LinkType::Inline { (b'(', b')') } else { (b'[', b']') }, - ) - } - LinkType::Reference => check_reference_redundancy( + let check_result = match link_type { + LinkType::Inline | LinkType::ReferenceUnknown => { + check_inline_or_reference_unknown_redundancy( cx, item, + module_id, hir_id, doc, resolutions, link_range, - &dest_url, + dest_url.to_string(), link_data, - ), - _ => Ok(()), - }; - if let Err(lint) = check_result { - cx.tcx.emit_node_span_lint( - crate::lint::REDUNDANT_EXPLICIT_LINKS, - hir_id, - item.attr_span(cx.tcx), - lint, - ); + if link_type == LinkType::Inline { (b'(', b')') } else { (b'[', b']') }, + ) } + LinkType::Reference => check_reference_redundancy( + cx, + item, + module_id, + hir_id, + doc, + resolutions, + link_range, + &dest_url, + link_data, + ), + _ => Ok(()), + }; + if let Err(lint) = check_result { + cx.tcx.emit_node_span_lint( + crate::lint::REDUNDANT_EXPLICIT_LINKS, + hir_id, + item.attr_span(cx.tcx), + lint, + ); } } } @@ -179,6 +182,7 @@ impl<'a> Diagnostic<'a, ()> for RedundantExplicitLinksWithoutSuggestion { fn check_inline_or_reference_unknown_redundancy( cx: &DocContext<'_>, item: &Item, + module_id: DefId, hir_id: HirId, doc: &str, resolutions: &DocLinkResMap, @@ -226,13 +230,8 @@ fn check_inline_or_reference_unknown_redundancy( else { return Ok(()); }; - let (Some(dest_res), Some(display_res)) = - (find_resolution(resolutions, &dest), find_resolution(resolutions, resolvable_link)) - else { - return Ok(()); - }; - if dest_res == display_res { + if explicit_link_is_redundant(cx, module_id, resolutions, &dest, resolvable_link) { let attr_span = item.attr_span(cx.tcx); let link_span = match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings) @@ -302,6 +301,7 @@ fn check_inline_or_reference_unknown_redundancy( fn check_reference_redundancy( cx: &DocContext<'_>, item: &Item, + module_id: DefId, hir_id: HirId, doc: &str, resolutions: &DocLinkResMap, @@ -347,13 +347,8 @@ fn check_reference_redundancy( else { return Ok(()); }; - let (Some(dest_res), Some(display_res)) = - (find_resolution(resolutions, dest), find_resolution(resolutions, resolvable_link)) - else { - return Ok(()); - }; - if dest_res == display_res { + if explicit_link_is_redundant(cx, module_id, resolutions, dest, resolvable_link) { let attr_span = item.attr_span(cx.tcx); let link_span = match source_span_for_markdown_range(cx.tcx, doc, &link_range, &item.attrs.doc_strings) @@ -437,6 +432,61 @@ fn check_reference_redundancy( Ok(()) } +fn explicit_link_is_redundant( + cx: &DocContext<'_>, + module_id: DefId, + resolutions: &DocLinkResMap, + dest: &str, + resolvable_link: &str, +) -> bool { + let Some(display_res) = find_resolution(resolutions, resolvable_link) else { + return false; + }; + + if (dest.ends_with(resolvable_link) || resolvable_link.ends_with(dest)) + && find_resolution(resolutions, dest).is_some_and(|dest_res| dest_res == display_res) + { + return true; + } + + if dest.contains('#') || !dest.ends_with(".html") { + return false; + } + + local_href_for_res(cx, module_id, display_res).is_some_and(|href| href == dest) +} + +fn local_href_for_res(cx: &DocContext<'_>, module_id: DefId, res: Res) -> Option { + let mut did = res.opt_def_id()?; + if matches!(cx.tcx.def_kind(did), DefKind::Ctor(..)) { + did = cx.tcx.parent(did); + } + + if matches!( + cx.tcx.def_kind(did), + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::Variant + ) || !did.is_local() + { + return None; + } + + let item_type = ItemType::from_def_id(did, cx.tcx); + let fqp = inline::get_item_path(cx.tcx, did, item_type); + let module_fqp = if item_type == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] }; + let current_fqp = inline::get_item_path(cx.tcx, module_id, ItemType::Module); + + let mut url_parts = href_relative_parts(module_fqp, ¤t_fqp); + match item_type { + ItemType::Module => url_parts.push("index.html"), + _ => url_parts.push_fmt(format_args!( + "{}.{last}.html", + item_type.as_str(), + last = fqp.last()? + )), + } + Some(url_parts.finish()) +} + fn find_resolution(resolutions: &DocLinkResMap, path: &str) -> Option> { [Namespace::TypeNS, Namespace::ValueNS, Namespace::MacroNS] .into_iter() diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 725c2be4e2121..0793ce88a1066 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -1,119 +1,63 @@ -//! Contains information about "passes", used to modify crate information during the documentation -//! process. +//! The definitions of *passes* which transform crate information. -use self::Condition::*; -use crate::clean; +use crate::clean::Crate; use crate::core::DocContext; mod stripper; pub(crate) use stripper::*; -mod strip_aliased_non_local; -pub(crate) use self::strip_aliased_non_local::STRIP_ALIASED_NON_LOCAL; - -mod strip_hidden; -pub(crate) use self::strip_hidden::STRIP_HIDDEN; - -mod strip_private; -pub(crate) use self::strip_private::STRIP_PRIVATE; - -mod strip_priv_imports; -pub(crate) use self::strip_priv_imports::STRIP_PRIV_IMPORTS; - -mod propagate_doc_cfg; -pub(crate) use self::propagate_doc_cfg::PROPAGATE_DOC_CFG; - -mod propagate_stability; -pub(crate) use self::propagate_stability::PROPAGATE_STABILITY; - +pub(crate) mod check_doc_test_visibility; pub(crate) mod collect_intra_doc_links; -pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS; - -mod check_doc_test_visibility; -pub(crate) use self::check_doc_test_visibility::{ - CHECK_DOC_TEST_VISIBILITY, Tests, should_have_doc_example, -}; - mod collect_trait_impls; -pub(crate) use self::collect_trait_impls::COLLECT_TRAIT_IMPLS; - mod lint; -pub(crate) use self::lint::RUN_LINTS; - -/// A single pass over the cleaned documentation. -/// -/// Runs in the compiler context, so it has access to types and traits and the like. -#[derive(Copy, Clone)] -pub(crate) struct Pass { - pub(crate) name: &'static str, - pub(crate) run: Option) -> clean::Crate>, - pub(crate) description: &'static str, -} +mod propagate_doc_cfg; +mod propagate_stability; +mod strip_aliased_non_local; +mod strip_hidden; +mod strip_priv_imports; +mod strip_private; -/// In a list of passes, a pass that may or may not need to be run depending on options. -#[derive(Copy, Clone)] -pub(crate) struct ConditionalPass { - pub(crate) pass: Pass, - pub(crate) condition: Condition, +#[derive(Default)] +pub(crate) struct Store { + links: collect_intra_doc_links::LinkCollection, } -/// How to decide whether to run a conditional pass. -#[derive(Copy, Clone)] -pub(crate) enum Condition { - Always, - /// When `--document-private-items` is passed. - WhenDocumentPrivate, - /// When `--document-private-items` is not passed. - WhenNotDocumentPrivate, - /// When `--document-hidden-items` is not passed. - WhenNotDocumentHidden, -} +#[tracing::instrument(level = "info", skip_all)] +pub(crate) fn run( + mut krate: Crate, + cx: &mut DocContext<'_>, + show_coverage: bool, +) -> (Crate, Store) { + macro_rules! run { + ($name:ident($( $args:tt )*)) => {{ + tracing::debug!("running pass `{}`", stringify!($name)); + cx.tcx.sess.time(stringify!($name), || $name::$name($( $args )*)) + }}; + } -/// The full list of passes. -pub(crate) const PASSES: &[Pass] = &[ - CHECK_DOC_TEST_VISIBILITY, - PROPAGATE_DOC_CFG, - STRIP_ALIASED_NON_LOCAL, - STRIP_HIDDEN, - STRIP_PRIVATE, - STRIP_PRIV_IMPORTS, - PROPAGATE_STABILITY, - COLLECT_INTRA_DOC_LINKS, - COLLECT_TRAIT_IMPLS, - RUN_LINTS, -]; + let mut store = Store::default(); -/// The list of passes run by default. -pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[ - ConditionalPass::always(COLLECT_TRAIT_IMPLS), - ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY), - ConditionalPass::always(STRIP_ALIASED_NON_LOCAL), - ConditionalPass::always(PROPAGATE_DOC_CFG), - ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), - ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate), - ConditionalPass::new(STRIP_PRIV_IMPORTS, WhenDocumentPrivate), - ConditionalPass::always(COLLECT_INTRA_DOC_LINKS), - ConditionalPass::always(PROPAGATE_STABILITY), - ConditionalPass::always(RUN_LINTS), -]; + if !show_coverage { + krate = run!(collect_trait_impls(krate, cx)); + krate = run!(check_doc_test_visibility(krate, cx)); + krate = run!(strip_aliased_non_local(krate, cx)); + krate = run!(propagate_doc_cfg(krate, cx)); + } -/// The list of default passes run when `--doc-coverage` is passed to rustdoc. -pub(crate) const COVERAGE_PASSES: &[ConditionalPass] = &[ - ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), - ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate), -]; + krate = run!(strip_hidden(krate, cx)); + krate = run!(strip_private(krate, cx)); -impl ConditionalPass { - pub(crate) const fn always(pass: Pass) -> Self { - Self::new(pass, Always) + if !show_coverage { + krate = run!(strip_priv_imports(krate, cx)); + (krate, store.links) = run!(collect_intra_doc_links(krate, cx)); + krate = run!(propagate_stability(krate, cx)); + krate = run!(lint(krate, cx)); } - pub(crate) const fn new(pass: Pass, condition: Condition) -> Self { - ConditionalPass { pass, condition } - } + (krate, store) } -/// Returns the given default set of passes. -pub(crate) fn defaults(show_coverage: bool) -> &'static [ConditionalPass] { - if show_coverage { COVERAGE_PASSES } else { DEFAULT_PASSES } +/// To be run after the cache in [`DocContext`] has been fully populated. +pub(crate) fn finalize(cx: &mut DocContext<'_>, store: Store) { + collect_intra_doc_links::resolve_ambiguous_links(store.links, cx); } diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index e15bb657b7867..0d16141fa09a1 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -1,4 +1,6 @@ -//! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items. +//! Propagates `#[doc(cfg(…))]` ([RFC 3631]) to child items. +//! +//! [RFC 3631]: https://rust-lang.github.io/rfcs/3631-rustdoc-cfgs-handling.html use rustc_data_structures::fx::FxHashMap; use rustc_hir::attrs::{AttributeKind, DocAttribute}; @@ -9,15 +11,8 @@ use crate::clean::inline::{load_attrs, merge_attrs}; use crate::clean::{CfgInfo, Crate, Item, ItemId, ItemKind}; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::passes::Pass; -pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { - name: "propagate-doc-cfg", - run: Some(propagate_doc_cfg), - description: "propagates `#[doc(cfg(...))]` to child items", -}; - -pub(crate) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { +pub(super) fn propagate_doc_cfg(cr: Crate, cx: &mut DocContext<'_>) -> Crate { if cx.tcx.features().doc_cfg() { CfgPropagator { cx, cfg_info: CfgInfo::default(), impl_cfg_info: FxHashMap::default() } .fold_crate(cr) diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index 6700ca649d7be..9afde1e6195e7 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -12,15 +12,8 @@ use rustc_hir::{Stability, StabilityLevel}; use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::passes::Pass; -pub(crate) const PROPAGATE_STABILITY: Pass = Pass { - name: "propagate-stability", - run: Some(propagate_stability), - description: "propagates stability to child items", -}; - -pub(crate) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate { +pub(super) fn propagate_stability(cr: Crate, cx: &mut DocContext<'_>) -> Crate { let crate_stability = cx.tcx.lookup_stability(CRATE_DEF_ID); StabilityPropagator { parent_stability: crate_stability, cx }.fold_crate(cr) } diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs index 18865f90e9031..06418bf97ed2b 100644 --- a/src/librustdoc/passes/strip_aliased_non_local.rs +++ b/src/librustdoc/passes/strip_aliased_non_local.rs @@ -1,18 +1,16 @@ +//! Strips all non-local private aliases items from the output. + use rustc_middle::ty::{TyCtxt, Visibility}; use crate::clean; use crate::clean::Item; use crate::core::DocContext; use crate::fold::{DocFolder, strip_item}; -use crate::passes::Pass; - -pub(crate) const STRIP_ALIASED_NON_LOCAL: Pass = Pass { - name: "strip-aliased-non-local", - run: Some(strip_aliased_non_local), - description: "strips all non-local private aliased items from the output", -}; -fn strip_aliased_non_local(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { +pub(super) fn strip_aliased_non_local( + krate: clean::Crate, + cx: &mut DocContext<'_>, +) -> clean::Crate { let mut stripper = AliasedNonLocalStripper { tcx: cx.tcx }; stripper.fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index e4c8a7b82a16d..95219d5e10a63 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -1,4 +1,4 @@ -//! Strip all doc(hidden) items from the output. +//! Strip all `#[doc(hidden)]` items from the output. use std::mem; @@ -10,16 +10,13 @@ use crate::clean::utils::inherits_doc_hidden; use crate::clean::{self, Item, ItemIdSet, reexport_chain}; use crate::core::DocContext; use crate::fold::{DocFolder, strip_item}; -use crate::passes::{ImplStripper, Pass}; +use crate::passes::ImplStripper; -pub(crate) const STRIP_HIDDEN: Pass = Pass { - name: "strip-hidden", - run: Some(strip_hidden), - description: "strips all `#[doc(hidden)]` items from the output", -}; +pub(super) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { + if cx.document_hidden() { + return krate; + } -/// Strip items marked `#[doc(hidden)]` -pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let mut retained = ItemIdSet::default(); let is_json_output = cx.is_json_output(); diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index a169797e00b2a..9a432c3bad343 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -1,18 +1,16 @@ -//! Strips all private import statements (use, extern crate) from a -//! crate. +//! Strips all private imports (`use`, `extern crate`) from a crate. use crate::clean; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::passes::{ImportStripper, Pass}; +use crate::passes::ImportStripper; -pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass { - name: "strip-priv-imports", - run: Some(strip_priv_imports), - description: "strips all private import statements (`use`, `extern crate`) from a crate", -}; +pub(super) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { + if !cx.document_private() { + // We don't need to do anything since it'll be handled by the `strip_private` pass. + return krate; + } -pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let is_json_output = cx.is_json_output(); ImportStripper { tcx: cx.tcx, is_json_output, document_hidden: cx.document_hidden() } .fold_crate(krate) diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 045bf0c0be029..54c8e9daedd17 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -1,21 +1,17 @@ -//! Strip all private items from the output. Additionally implies strip_priv_imports. -//! Basically, the goal is to remove items that are not relevant for public documentation. +//! Strip all private items from the output. +//! +//! Implies `strip_priv_imports`. use crate::clean::{self, ItemIdSet}; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::passes::{ImplStripper, ImportStripper, Pass, Stripper}; +use crate::passes::{ImplStripper, ImportStripper, Stripper}; -pub(crate) const STRIP_PRIVATE: Pass = Pass { - name: "strip-private", - run: Some(strip_private), - description: "strips all private items from a crate which cannot be seen externally, \ - implies strip-priv-imports", -}; +pub(super) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { + if cx.document_private() { + return krate; + } -/// Strip private items from the point of view of a crate or externally from a -/// crate, specified by the `xcrate` flag. -pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { // This stripper collects all *retained* nodes. let mut retained = ItemIdSet::default(); let is_json_output = cx.is_json_output(); diff --git a/tests/codegen-llvm/large_data_threshold.rs b/tests/codegen-llvm/large_data_threshold.rs new file mode 100644 index 0000000000000..a63a333985cca --- /dev/null +++ b/tests/codegen-llvm/large_data_threshold.rs @@ -0,0 +1,9 @@ +//@ only-x86_64 +//@ revisions: DEFAULT EXPLICIT +//@[DEFAULT] compile-flags: -C code-model=medium +//@[EXPLICIT] compile-flags: -C code-model=medium -Z large-data-threshold=1024 + +#![crate_type = "lib"] + +// DEFAULT-NOT: !"Large Data Threshold" +// EXPLICIT: !{{[0-9]+}} = !{i32 1, !"Large Data Threshold", i64 1024} diff --git a/tests/rustdoc-html/trait-read-more-161300.rs b/tests/rustdoc-html/trait-read-more-161300.rs new file mode 100644 index 0000000000000..47aac5a2770a4 --- /dev/null +++ b/tests/rustdoc-html/trait-read-more-161300.rs @@ -0,0 +1,27 @@ +// regression test for +// ensures the "read more" link exists in various circumstances. +#![crate_name = "foo"] + +//@ has 'foo/struct.MyStruct.html' +pub trait MyTrait { + /// First + /// + /// Next paragraph + //@ has - '//a[@href="trait.MyTrait.html#method.first"]' 'Read more' + fn first() {} + + /// Second + /// + /// --- + /// + /// This method is experimental! + /// + /// --- + /// + /// Next paragraph + //@ has - '//a[@href="trait.MyTrait.html#method.second"]' 'Read more' + fn second() {} +} + +pub struct MyStruct; +impl MyTrait for MyStruct {} diff --git a/tests/rustdoc-ui/issues/issue-91713.stdout b/tests/rustdoc-ui/issues/issue-91713.stdout deleted file mode 100644 index 0243f6cd533a0..0000000000000 --- a/tests/rustdoc-ui/issues/issue-91713.stdout +++ /dev/null @@ -1,27 +0,0 @@ -Available passes for running rustdoc: -check_doc_test_visibility - run various visibility-related lints on doctests - propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items -strip-aliased-non-local - strips all non-local private aliased items from the output - strip-hidden - strips all `#[doc(hidden)]` items from the output - strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports - strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate - propagate-stability - propagates stability to child items -collect-intra-doc-links - resolves intra-doc links - collect-trait-impls - retrieves trait impls for items in the crate - run-lints - runs some of rustdoc's lints - -Default passes for rustdoc: - collect-trait-impls -check_doc_test_visibility -strip-aliased-non-local - propagate-doc-cfg - strip-hidden (when not --document-hidden-items) - strip-private (when not --document-private-items) - strip-priv-imports (when --document-private-items) -collect-intra-doc-links - propagate-stability - run-lints - -Passes run with `--show-coverage`: - strip-hidden (when not --document-hidden-items) - strip-private (when not --document-private-items) diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.rs b/tests/rustdoc-ui/lints/invalid-html-tags.rs index d0aa97c9e4074..7a244e6cc58f5 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.rs +++ b/tests/rustdoc-ui/lints/invalid-html-tags.rs @@ -1,5 +1,6 @@ #![deny(rustdoc::invalid_html_tags)] //~^ NOTE the lint level is defined here +#![allow(rustdoc::invalid_markdown_table)] //!

💩

//~^ ERROR unclosed HTML tag `p` diff --git a/tests/rustdoc-ui/lints/invalid-html-tags.stderr b/tests/rustdoc-ui/lints/invalid-html-tags.stderr index 15b88496b7557..d0830321536dd 100644 --- a/tests/rustdoc-ui/lints/invalid-html-tags.stderr +++ b/tests/rustdoc-ui/lints/invalid-html-tags.stderr @@ -1,5 +1,5 @@ error: unclosed HTML tag `p` - --> $DIR/invalid-html-tags.rs:4:5 + --> $DIR/invalid-html-tags.rs:5:5 | LL | //!

💩

| ^^^ @@ -11,115 +11,115 @@ LL | #![deny(rustdoc::invalid_html_tags)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unclosed HTML tag `p` - --> $DIR/invalid-html-tags.rs:4:9 + --> $DIR/invalid-html-tags.rs:5:9 | LL | //!

💩

| ^^^ error: unclosed HTML tag `unknown` - --> $DIR/invalid-html-tags.rs:12:5 + --> $DIR/invalid-html-tags.rs:13:5 | LL | /// | ^^^^^^^^^ error: unclosed HTML tag `script` - --> $DIR/invalid-html-tags.rs:15:5 + --> $DIR/invalid-html-tags.rs:16:5 | LL | ///