diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 8132bf577d883..0b4ec6956bd15 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -73,6 +73,7 @@ use rustc_middle::ty::{
ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility,
};
use rustc_query_system::ich::StableHashingContext;
+use rustc_session::config::CrateType;
use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym};
@@ -2430,6 +2431,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
fn resolve_main(&mut self) {
+ let any_exe = self.tcx.crate_types().contains(&CrateType::Executable);
+ // Don't try to resolve main unless it's an executable
+ if !any_exe {
+ return;
+ }
+
let module = self.graph_root;
let ident = Ident::with_dummy_span(sym::main);
let parent_scope = &ParentScope::module(module, self.arenas);
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile
index a74db2250fc67..d4113736b544b 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.04
+FROM ghcr.io/rust-lang/ubuntu:24.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
index ad2ee85c7bb51..db4fca71d6376 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
@@ -1,4 +1,4 @@
-FROM ghcr.io/rust-lang/ubuntu:22.04
+FROM ghcr.io/rust-lang/ubuntu:24.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs
index 2e8b5d9f59489..80c110ec07f56 100644
--- a/src/librustdoc/formats/mod.rs
+++ b/src/librustdoc/formats/mod.rs
@@ -76,4 +76,8 @@ impl Impl {
};
true
}
+
+ pub(crate) fn is_negative_trait_impl(&self) -> bool {
+ self.inner_impl().is_negative_trait_impl()
+ }
}
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 0c511738d7c8a..f88a5b8974fb9 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -646,6 +646,27 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
})
}
+/// Struct used to handle insertion of "negative impl" marker in the generated DOM.
+///
+/// This marker appears once in all trait impl lists to divide negative impls from positive impls.
+struct NegativeMarker {
+ inserted: bool,
+}
+
+impl NegativeMarker {
+ fn new() -> Self {
+ Self { inserted: false }
+ }
+
+ fn insert_if_needed(&mut self, w: &mut fmt::Formatter<'_>, implementor: &Impl) -> fmt::Result {
+ if !self.inserted && !implementor.is_negative_trait_impl() {
+ w.write_str("
")?;
+ self.inserted = true;
+ }
+ Ok(())
+ }
+}
+
fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt::Display {
fmt::from_fn(|w| {
let tcx = cx.tcx();
@@ -1072,7 +1093,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
"",
)
)?;
+ let mut negative_marker = NegativeMarker::new();
for implementor in concrete {
+ negative_marker.insert_if_needed(w, implementor)?;
write!(w, "{}", render_implementor(cx, implementor, it, &implementor_dups, &[]))?;
}
w.write_str("
")?;
@@ -1088,7 +1111,9 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
"",
)
)?;
+ let mut negative_marker = NegativeMarker::new();
for implementor in synthetic {
+ negative_marker.insert_if_needed(w, implementor)?;
write!(
w,
"{}",
@@ -2302,11 +2327,18 @@ where
}
#[derive(PartialEq, Eq)]
-struct ImplString(String);
+struct ImplString {
+ rendered: String,
+ is_negative: bool,
+}
impl ImplString {
fn new(i: &Impl, cx: &Context<'_>) -> ImplString {
- ImplString(format!("{}", print_impl(i.inner_impl(), false, cx)))
+ let impl_ = i.inner_impl();
+ ImplString {
+ is_negative: impl_.is_negative_trait_impl(),
+ rendered: format!("{}", print_impl(impl_, false, cx)),
+ }
}
}
@@ -2318,7 +2350,12 @@ impl PartialOrd for ImplString {
impl Ord for ImplString {
fn cmp(&self, other: &Self) -> Ordering {
- compare_names(&self.0, &other.0)
+ // We sort negative impls first.
+ match (self.is_negative, other.is_negative) {
+ (false, true) => Ordering::Greater,
+ (true, false) => Ordering::Less,
+ _ => compare_names(&self.rendered, &other.rendered),
+ }
}
}
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 6bf116c3b75ad..e0a37c95257c8 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -14,6 +14,7 @@
//! or contains "invocation-specific".
use std::cell::RefCell;
+use std::cmp::Ordering;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, Write as _};
@@ -47,6 +48,7 @@ use crate::formats::item_type::ItemType;
use crate::html::format::{print_impl, print_path};
use crate::html::layout;
use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
+use crate::html::render::print_item::compare_names;
use crate::html::render::search_index::{SerializedSearchIndex, build_index};
use crate::html::render::sorted_template::{self, FileFormat, SortedTemplate};
use crate::html::render::{AssocItemLink, ImplRenderingParameters, StylePath};
@@ -667,7 +669,7 @@ impl TraitAliasPart {
fn blank() -> SortedTemplate<::FileFormat> {
SortedTemplate::from_before_after(
r"(function() {
- var implementors = Object.fromEntries([",
+ const implementors = Object.fromEntries([",
r"]);
if (window.register_implementors) {
window.register_implementors(implementors);
@@ -720,10 +722,12 @@ impl TraitAliasPart {
{
None
} else {
+ let impl_ = imp.inner_impl();
Some(Implementor {
- text: print_impl(imp.inner_impl(), false, cx).to_string(),
+ text: print_impl(impl_, false, cx).to_string(),
synthetic: imp.inner_impl().kind.is_auto(),
types: collect_paths_for_type(&imp.inner_impl().for_, cache),
+ is_negative: impl_.is_negative_trait_impl(),
})
}
})
@@ -742,8 +746,22 @@ impl TraitAliasPart {
}
path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
- let part = OrderedJson::array_sorted(
- implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
+ let mut implementors = implementors.collect::>();
+ implementors.sort_unstable_by(|a, b| {
+ // We sort negative impls first.
+ match (a.is_negative, b.is_negative) {
+ (false, true) => Ordering::Greater,
+ (true, false) => Ordering::Less,
+ _ => compare_names(&a.text, &b.text),
+ }
+ });
+
+ let part = OrderedJson::array_unsorted(
+ implementors
+ .iter()
+ .map(OrderedJson::serialize)
+ .collect::, _>>()
+ .unwrap(),
);
path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
}
@@ -755,6 +773,7 @@ struct Implementor {
text: String,
synthetic: bool,
types: Vec,
+ is_negative: bool,
}
impl Serialize for Implementor {
@@ -764,6 +783,7 @@ impl Serialize for Implementor {
{
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&self.text)?;
+ seq.serialize_element(if self.is_negative { &1 } else { &0 })?;
if self.synthetic {
seq.serialize_element(&1)?;
seq.serialize_element(&self.types)?;
diff --git a/src/librustdoc/html/render/write_shared/tests.rs b/src/librustdoc/html/render/write_shared/tests.rs
index 1989a1f87aae6..48d592c22f6f0 100644
--- a/src/librustdoc/html/render/write_shared/tests.rs
+++ b/src/librustdoc/html/render/write_shared/tests.rs
@@ -68,7 +68,7 @@ fn trait_alias_template() {
assert_eq!(
but_last_line(&template.to_string()),
r#"(function() {
- var implementors = Object.fromEntries([]);
+ const implementors = Object.fromEntries([]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
@@ -80,7 +80,7 @@ fn trait_alias_template() {
assert_eq!(
but_last_line(&template.to_string()),
r#"(function() {
- var implementors = Object.fromEntries([["a"]]);
+ const implementors = Object.fromEntries([["a"]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
@@ -92,7 +92,7 @@ fn trait_alias_template() {
assert_eq!(
but_last_line(&template.to_string()),
r#"(function() {
- var implementors = Object.fromEntries([["a"],["b"]]);
+ const implementors = Object.fromEntries([["a"],["b"]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 5c02e2eb26a3e..6f44854b6914e 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -29,6 +29,10 @@ nav.sub {
display: none;
}
+#synthetic-implementors-list:not(.loaded), #implementors-list:not(.loaded) {
+ display: block;
+}
+
/* Begin: styles for themes
Keep the default light and dark themes synchronized with the ones
in rustdoc.css */
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 7f47856948493..69a79f2736e77 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1158,6 +1158,12 @@ div.where {
margin-left: calc(var(--docblock-indent) + var(--impl-items-indent));
}
+#synthetic-implementors-list:not(.loaded), #implementors-list:not(.loaded) {
+ /* To prevent layout shift when loading the page with extra implementors being loaded
+ from JS, we hide the list until it's complete. */
+ display: none;
+}
+
.item-info code {
font-size: 0.875rem;
}
@@ -2976,6 +2982,9 @@ in src-script.js and main.js
{
margin-bottom: 0.75em;
}
+.negative-marker {
+ display: none;
+}
.variants > .docblock,
.implementors-toggle > .docblock,
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index dff40485e9a90..476ecd42d6f91 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -800,21 +800,34 @@ function preLoadCss(cssUrl) {
//
window.register_implementors = imp => {
- const implementors = document.getElementById("implementors-list");
- const synthetic_implementors = document.getElementById("synthetic-implementors-list");
+ /** Takes an ID as input and returns a list of two elements. The first element is the DOM
+ * element with the given ID and the second is the "negative marker", meaning the location
+ * between the negative and non-negative impls.
+ *
+ * @param {string} id: ID of the DOM element.
+ *
+ * @return {[HTMLElement|null, HTMLElement|null]}
+ */
+ function implementorsElems(id) {
+ const elem = document.getElementById(id);
+ return [elem, elem ? elem.querySelector(".negative-marker") : null];
+ }
+ const implementors = implementorsElems("implementors-list");
+ const syntheticImplementors = implementorsElems("synthetic-implementors-list");
const inlined_types = new Set();
const TEXT_IDX = 0;
- const SYNTHETIC_IDX = 1;
- const TYPES_IDX = 2;
+ const IS_NEG_IDX = 1;
+ const SYNTHETIC_IDX = 2;
+ const TYPES_IDX = 3;
- if (synthetic_implementors) {
+ if (syntheticImplementors[0]) {
// This `inlined_types` variable is used to avoid having the same implementation
// showing up twice. For example "String" in the "Sync" doc page.
//
// By the way, this is only used by and useful for traits implemented automatically
// (like "Send" and "Sync").
- onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => {
+ onEachLazy(syntheticImplementors[0].getElementsByClassName("impl"), el => {
const aliases = el.getAttribute("data-aliases");
if (!aliases) {
return;
@@ -827,7 +840,7 @@ function preLoadCss(cssUrl) {
}
// @ts-expect-error
- let currentNbImpls = implementors.getElementsByClassName("impl").length;
+ let currentNbImpls = implementors[0].getElementsByClassName("impl").length;
// @ts-expect-error
const traitName = document.querySelector(".main-heading h1 > .trait").textContent;
const baseIdName = "impl-" + traitName + "-";
@@ -849,7 +862,7 @@ function preLoadCss(cssUrl) {
struct_loop:
for (const struct of structs) {
- const list = struct[SYNTHETIC_IDX] ? synthetic_implementors : implementors;
+ const list = struct[SYNTHETIC_IDX] ? syntheticImplementors : implementors;
// The types list is only used for synthetic impls.
// If this changes, `main.js` and `write_shared.rs` both need changed.
@@ -884,11 +897,25 @@ function preLoadCss(cssUrl) {
addClass(display, "impl");
display.appendChild(anchor);
display.appendChild(code);
- // @ts-expect-error
- list.appendChild(display);
+
+ // If this is a negative implementor, we put it into the right location (just
+ // before the negative impl marker).
+ if (struct[IS_NEG_IDX]) {
+ // @ts-expect-error
+ list[1].before(display);
+ } else {
+ // @ts-expect-error
+ list[0].appendChild(display);
+ }
currentNbImpls += 1;
}
}
+ if (implementors[0]) {
+ implementors[0].classList.add("loaded");
+ }
+ if (syntheticImplementors[0]) {
+ syntheticImplementors[0].classList.add("loaded");
+ }
};
if (window.pending_implementors) {
window.register_implementors(window.pending_implementors);
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 18d3b6a455d3e..60df4fc10b8c5 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -520,7 +520,7 @@ declare namespace rustdoc {
* Provided by generated `trait.impl` files.
*/
type Implementors = {
- [key: string]: Array<[string, number, Array]>
+ [key: string]: Array<[string, 0|1, number, Array]>
}
type TypeImpls = {
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 34153d93d463d..cd0e79f5ab3fa 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -9,7 +9,6 @@ use camino::{Utf8Path, Utf8PathBuf};
use semver::Version;
use crate::edition::Edition;
-use crate::executor::ColorConfig;
use crate::fatal;
use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
@@ -597,11 +596,6 @@ pub struct Config {
/// FIXME: this is *way* too coarse; the user can't select *which* info to verbosely dump.
pub verbose: bool,
- /// Whether to use colors in test output.
- ///
- /// Note: the exact control mechanism is delegated to [`colored`].
- pub color: ColorConfig,
-
/// Where to find the remote test client process, if we're using it.
///
/// Note: this is *only* used for target platform executables created by `run-make` test
@@ -623,9 +617,6 @@ pub struct Config {
/// created in `$test_suite_build_root/rustfix_missing_coverage.txt`
pub rustfix_coverage: bool,
- /// Whether to run `tidy` (html-tidy) when a rustdoc test fails.
- pub has_html_tidy: bool,
-
/// Whether to run `enzyme` autodiff tests.
pub has_enzyme: bool,
diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index 4dd4b1f85aaa7..c800d11d6b2fd 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -341,15 +341,6 @@ pub(crate) struct CollectedTestDesc {
pub(crate) should_fail: ShouldFail,
}
-/// Whether console output should be colored or not.
-#[derive(Copy, Clone, Default, Debug)]
-pub enum ColorConfig {
- #[default]
- AutoColor,
- AlwaysColor,
- NeverColor,
-}
-
/// Tests with `//@ should-fail` are tests of compiletest itself, and should
/// be reported as successful if and only if they would have _failed_.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 324ce2eaabeec..1fa818df62dea 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -24,7 +24,6 @@ use core::panic;
use std::collections::HashSet;
use std::fmt::Write;
use std::io::{self, ErrorKind};
-use std::process::{Command, Stdio};
use std::sync::{Arc, OnceLock};
use std::time::SystemTime;
use std::{env, fs, vec};
@@ -43,7 +42,7 @@ use crate::common::{
};
use crate::directives::{AuxProps, DirectivesCache, FileDirectives};
use crate::edition::parse_edition;
-use crate::executor::{CollectedTest, ColorConfig};
+use crate::executor::CollectedTest;
/// Creates the `Config` instance for this invocation of compiletest.
///
@@ -136,8 +135,9 @@ fn parse_config(args: Vec) -> Config {
"overwrite stderr/stdout files instead of complaining about a mismatch",
)
.optflag("", "fail-fast", "stop as soon as possible after any test fails")
- .optopt("", "color", "coloring: auto, always, never", "WHEN")
.optopt("", "target", "the target to build for", "TARGET")
+ // FIXME: Should be removed once `bootstrap` will be updated to not use this option.
+ .optopt("", "color", "coloring: auto, always, never", "WHEN")
.optopt("", "host", "the host to build for", "HOST")
.optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
.optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
@@ -274,12 +274,6 @@ fn parse_config(args: Vec) -> Config {
let lldb = matches.opt_str("lldb").map(Utf8PathBuf::from);
let lldb_version =
matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version);
- let color = match matches.opt_str("color").as_deref() {
- Some("auto") | None => ColorConfig::AutoColor,
- Some("always") => ColorConfig::AlwaysColor,
- Some("never") => ColorConfig::NeverColor,
- Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
- };
// FIXME: this is very questionable, we really should be obtaining LLVM version info from
// `bootstrap`, and not trying to be figuring out that in `compiletest` by running the
// `FileCheck` binary.
@@ -304,16 +298,6 @@ fn parse_config(args: Vec) -> Config {
let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions");
let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
- let has_html_tidy = if mode == TestMode::Rustdoc {
- Command::new("tidy")
- .arg("--version")
- .stdout(Stdio::null())
- .status()
- .map_or(false, |status| status.success())
- } else {
- // Avoid spawning an external command when we know html-tidy won't be used.
- false
- };
let has_enzyme = matches.opt_present("has-enzyme");
let filters = if mode == TestMode::RunMake {
matches
@@ -455,11 +439,9 @@ fn parse_config(args: Vec) -> Config {
adb_device_status,
verbose: matches.opt_present("verbose"),
only_modified: matches.opt_present("only-modified"),
- color,
remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from),
compare_mode,
rustfix_coverage: matches.opt_present("rustfix-coverage"),
- has_html_tidy,
has_enzyme,
channel: matches.opt_str("channel").unwrap(),
git_hash: matches.opt_present("git-hash"),
@@ -1144,10 +1126,6 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet) {
}
fn early_config_check(config: &Config) {
- if !config.has_html_tidy && config.mode == TestMode::Rustdoc {
- warning!("`tidy` (html-tidy.org) is not installed; diffs will not be generated");
- }
-
if !config.profiler_runtime && config.mode == TestMode::CoverageRun {
let actioned = if config.bless { "blessed" } else { "checked" };
warning!("profiler runtime is not available, so `.coverage` files won't be {actioned}");
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 79e3ecc2a9c3b..1216ba888328b 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1,12 +1,11 @@
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::ffi::OsString;
-use std::fs::{self, File, create_dir_all};
+use std::fs::{self, create_dir_all};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::io::prelude::*;
-use std::io::{self, BufReader};
use std::process::{Child, Command, ExitStatus, Output, Stdio};
-use std::{env, fmt, iter, str};
+use std::{env, fmt, io, iter, str};
use build_helper::fs::remove_and_create_dir_all;
use camino::{Utf8Path, Utf8PathBuf};
@@ -23,9 +22,9 @@ use crate::directives::TestProps;
use crate::errors::{Error, ErrorKind, load_errors};
use crate::output_capture::ConsoleOut;
use crate::read2::{Truncated, read2_abbreviated};
-use crate::runtest::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
+use crate::runtest::compute_diff::{DiffLine, make_diff, write_diff};
use crate::util::{Utf8PathBufExt, add_dylib_path, static_regex};
-use crate::{ColorConfig, help, json, stamp_file_path, warning};
+use crate::{json, stamp_file_path};
// Helper modules that implement test running logic for each test suite.
// tidy-alphabetical-start
@@ -2165,161 +2164,6 @@ impl<'test> TestCx<'test> {
if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
}
- fn compare_to_default_rustdoc(&self, out_dir: &Utf8Path) {
- if !self.config.has_html_tidy {
- return;
- }
- writeln!(self.stdout, "info: generating a diff against nightly rustdoc");
-
- let suffix =
- self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
- let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix));
- remove_and_create_dir_all(&compare_dir).unwrap_or_else(|e| {
- panic!("failed to remove and recreate output directory `{compare_dir}`: {e}")
- });
-
- // We need to create a new struct for the lifetimes on `config` to work.
- let new_rustdoc = TestCx {
- config: &Config {
- // FIXME: use beta or a user-specified rustdoc instead of
- // hardcoding the default toolchain
- rustdoc_path: Some("rustdoc".into()),
- // Needed for building auxiliary docs below
- rustc_path: "rustc".into(),
- ..self.config.clone()
- },
- ..*self
- };
-
- let output_file = TargetLocation::ThisDirectory(new_rustdoc.aux_output_dir_name());
- let mut rustc = new_rustdoc.make_compile_args(
- &new_rustdoc.testpaths.file,
- output_file,
- Emit::None,
- AllowUnused::Yes,
- LinkToAux::Yes,
- Vec::new(),
- );
- let aux_dir = new_rustdoc.aux_output_dir();
- new_rustdoc.build_all_auxiliary(&aux_dir, &mut rustc);
-
- let proc_res = new_rustdoc.document(&compare_dir, DocKind::Html);
- if !proc_res.status.success() {
- writeln!(self.stderr, "failed to run nightly rustdoc");
- return;
- }
-
- #[rustfmt::skip]
- let tidy_args = [
- "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar,rustdoc-topbar",
- "--indent", "yes",
- "--indent-spaces", "2",
- "--wrap", "0",
- "--show-warnings", "no",
- "--markup", "yes",
- "--quiet", "yes",
- "-modify",
- ];
- let tidy_dir = |dir| {
- for entry in walkdir::WalkDir::new(dir) {
- let entry = entry.expect("failed to read file");
- if entry.file_type().is_file()
- && entry.path().extension().and_then(|p| p.to_str()) == Some("html")
- {
- let status =
- Command::new("tidy").args(&tidy_args).arg(entry.path()).status().unwrap();
- // `tidy` returns 1 if it modified the file.
- assert!(status.success() || status.code() == Some(1));
- }
- }
- };
- tidy_dir(out_dir);
- tidy_dir(&compare_dir);
-
- let pager = {
- let output = Command::new("git").args(&["config", "--get", "core.pager"]).output().ok();
- output.and_then(|out| {
- if out.status.success() {
- Some(String::from_utf8(out.stdout).expect("invalid UTF8 in git pager"))
- } else {
- None
- }
- })
- };
-
- let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());
-
- if !write_filtered_diff(
- self,
- &diff_filename,
- out_dir,
- &compare_dir,
- self.config.verbose,
- |file_type, extension| {
- file_type.is_file() && (extension == Some("html") || extension == Some("js"))
- },
- ) {
- return;
- }
-
- match self.config.color {
- ColorConfig::AlwaysColor => colored::control::set_override(true),
- ColorConfig::NeverColor => colored::control::set_override(false),
- _ => {}
- }
-
- if let Some(pager) = pager {
- let pager = pager.trim();
- if self.config.verbose {
- writeln!(self.stderr, "using pager {}", pager);
- }
- let output = Command::new(pager)
- // disable paging; we want this to be non-interactive
- .env("PAGER", "")
- .stdin(File::open(&diff_filename).unwrap())
- // Capture output and print it explicitly so it will in turn be
- // captured by output-capture.
- .output()
- .unwrap();
- assert!(output.status.success());
- writeln!(self.stdout, "{}", String::from_utf8_lossy(&output.stdout));
- writeln!(self.stderr, "{}", String::from_utf8_lossy(&output.stderr));
- } else {
- warning!("no pager configured, falling back to unified diff");
- help!(
- "try configuring a git pager (e.g. `delta`) with \
- `git config --global core.pager delta`"
- );
- let mut out = io::stdout();
- let mut diff = BufReader::new(File::open(&diff_filename).unwrap());
- let mut line = Vec::new();
- loop {
- line.truncate(0);
- match diff.read_until(b'\n', &mut line) {
- Ok(0) => break,
- Ok(_) => {}
- Err(e) => writeln!(self.stderr, "ERROR: {:?}", e),
- }
- match String::from_utf8(line.clone()) {
- Ok(line) => {
- if line.starts_with('+') {
- write!(&mut out, "{}", line.green()).unwrap();
- } else if line.starts_with('-') {
- write!(&mut out, "{}", line.red()).unwrap();
- } else if line.starts_with('@') {
- write!(&mut out, "{}", line.blue()).unwrap();
- } else {
- out.write_all(line.as_bytes()).unwrap();
- }
- }
- Err(_) => {
- write!(&mut out, "{}", String::from_utf8_lossy(&line).reversed()).unwrap();
- }
- }
- }
- };
- }
-
fn get_lines(&self, path: &Utf8Path, mut other_files: Option<&mut Vec>) -> Vec {
let content = fs::read_to_string(path.as_std_path()).unwrap();
let mut ignore = false;
diff --git a/src/tools/compiletest/src/runtest/compute_diff.rs b/src/tools/compiletest/src/runtest/compute_diff.rs
index 3363127b3ea37..8418dcbaf9639 100644
--- a/src/tools/compiletest/src/runtest/compute_diff.rs
+++ b/src/tools/compiletest/src/runtest/compute_diff.rs
@@ -1,9 +1,4 @@
use std::collections::VecDeque;
-use std::fs::{File, FileType};
-
-use camino::Utf8Path;
-
-use crate::runtest::TestCx;
#[derive(Debug, PartialEq)]
pub enum DiffLine {
@@ -109,55 +104,3 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S
}
output
}
-
-/// Filters based on filetype and extension whether to diff a file.
-///
-/// Returns whether any data was actually written.
-pub(crate) fn write_filtered_diff(
- cx: &TestCx<'_>,
- diff_filename: &str,
- out_dir: &Utf8Path,
- compare_dir: &Utf8Path,
- verbose: bool,
- filter: Filter,
-) -> bool
-where
- Filter: Fn(FileType, Option<&str>) -> bool,
-{
- use std::io::{Read, Write};
- let mut diff_output = File::create(diff_filename).unwrap();
- let mut wrote_data = false;
- for entry in walkdir::WalkDir::new(out_dir.as_std_path()) {
- let entry = entry.expect("failed to read file");
- let extension = entry.path().extension().and_then(|p| p.to_str());
- if filter(entry.file_type(), extension) {
- let expected_path = compare_dir
- .as_std_path()
- .join(entry.path().strip_prefix(&out_dir.as_std_path()).unwrap());
- let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
- let actual_path = entry.path();
- let actual = std::fs::read(&actual_path).unwrap();
- let diff = unified_diff::diff(
- &expected,
- &expected_path.to_str().unwrap(),
- &actual,
- &actual_path.to_str().unwrap(),
- 3,
- );
- wrote_data |= !diff.is_empty();
- diff_output.write_all(&diff).unwrap();
- }
- }
-
- if !wrote_data {
- writeln!(cx.stdout, "note: diff is identical to nightly rustdoc");
- assert!(diff_output.metadata().unwrap().len() == 0);
- return false;
- } else if verbose {
- writeln!(cx.stderr, "printing diff:");
- let mut buf = Vec::new();
- diff_output.read_to_end(&mut buf).unwrap();
- std::io::stderr().lock().write_all(&mut buf).unwrap();
- }
- true
-}
diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs
index a4558de5f1c0c..3c80521e51ecd 100644
--- a/src/tools/compiletest/src/runtest/rustdoc.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc.rs
@@ -28,9 +28,7 @@ impl TestCx<'_> {
}
let res = self.run_command_to_procres(&mut cmd);
if !res.status.success() {
- self.fatal_proc_rec_general("htmldocck failed!", None, &res, || {
- self.compare_to_default_rustdoc(&out_dir);
- });
+ self.fatal_proc_rec("htmldocck failed!", &res);
}
}
}
diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs
index f6d026ab9cfd6..c30f3d1e10ea8 100644
--- a/src/tools/compiletest/src/rustdoc_gui_test.rs
+++ b/src/tools/compiletest/src/rustdoc_gui_test.rs
@@ -108,11 +108,9 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config {
adb_test_dir: Default::default(),
adb_device_status: Default::default(),
verbose: Default::default(),
- color: Default::default(),
remote_test_client: Default::default(),
compare_mode: Default::default(),
rustfix_coverage: Default::default(),
- has_html_tidy: Default::default(),
has_enzyme: Default::default(),
channel: Default::default(),
git_hash: Default::default(),
diff --git a/tests/rustdoc-gui/implementors.goml b/tests/rustdoc-gui/implementors.goml
index b39b95c1a9bff..d4542c6f78172 100644
--- a/tests/rustdoc-gui/implementors.goml
+++ b/tests/rustdoc-gui/implementors.goml
@@ -1,19 +1,46 @@
// The goal of this test is to check that the external trait implementors, generated with JS,
// have the same display than the "local" ones.
go-to: "file://" + |DOC_PATH| + "/implementors/trait.Whatever.html"
-assert: "#implementors-list"
-// There are supposed to be two implementors listed.
-assert-count: ("#implementors-list .impl", 2)
+wait-for-css: ("#implementors-list", {"display": "block"})
+// There are supposed to be four implementors listed.
+assert-count: ("#implementors-list .impl", 4)
+// There are supposed to be two non-negative implementors.
+assert-count: ("#implementors-list .negative-marker ~ *", 2)
// Now we check that both implementors have an anchor, an ID and a similar DOM.
-assert: ("#implementors-list .impl:nth-child(1) > a.anchor")
-assert-attribute: ("#implementors-list .impl:nth-child(1)", {"id": "impl-Whatever-for-Struct"})
-assert-attribute: ("#implementors-list .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever-for-Struct"})
-assert: "#implementors-list .impl:nth-child(1) > .code-header"
+define-function: (
+ "check-dom",
+ [id],
+ block {
+ assert-attribute: (|id| + " > a.anchor", {"href": |id|})
+ assert: |id| + " > .code-header"
+ },
+)
-assert: ("#implementors-list .impl:nth-child(2) > a.anchor")
-assert-attribute: ("#implementors-list .impl:nth-child(2)", {"id": "impl-Whatever-1"})
-assert-attribute: ("#implementors-list .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"})
-assert: "#implementors-list .impl:nth-child(2) > .code-header"
+call-function: ("check-dom", {"id": "#impl-Whatever-for-Struct2"})
+call-function: ("check-dom", {"id": "#impl-Whatever-2"})
+call-function: ("check-dom", {"id": "#impl-Whatever-for-Struct"})
+call-function: ("check-dom", {"id": "#impl-Whatever-3"})
+
+// Ensure that negative impl are sorted first.
+assert-property: (
+ "#implementors-list > *:nth-child(1) > h3",
+ {"textContent": "impl !Whatever for Struct2"},
+)
+assert-property: (
+ "#implementors-list > *:nth-child(2) > h3",
+ {"textContent": "impl !Whatever for StructToImplOnReexport"},
+)
+// Third one is the negative marker.
+assert-attribute: ("#implementors-list > *:nth-child(3)", {"class": "negative-marker"})
+// This one is a `` so the selector is a bit different.
+assert-property: (
+ "#implementors-list > *:nth-child(4) section > h3",
+ {"textContent": "impl Whatever for Struct"},
+)
+assert-property: (
+ "#implementors-list > *:nth-child(5) > h3",
+ {"textContent": "impl Whatever for Foo"},
+)
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HasEmptyTraits.html"
compare-elements-position-near-false: (
@@ -39,3 +66,8 @@ assert-count: ("#implementors-list .impl", 1)
go-to: "file://" + |DOC_PATH| + "/http/trait.HttpTrait.html"
assert-count: ("#implementors-list .impl", 1)
assert-attribute: ("#implementors-list .impl a.trait", {"href": "../http/trait.HttpTrait.html"})
+
+// Now we check that if JS is disabled, the implementors list will be visible.
+javascript: false
+reload:
+assert-css: ("#implementors-list", {"display": "block"})
diff --git a/tests/rustdoc-gui/src/lib2/implementors/lib.rs b/tests/rustdoc-gui/src/lib2/implementors/lib.rs
index 2842ac50dc1e8..f081820039275 100644
--- a/tests/rustdoc-gui/src/lib2/implementors/lib.rs
+++ b/tests/rustdoc-gui/src/lib2/implementors/lib.rs
@@ -1,3 +1,5 @@
+#![feature(negative_impls)]
+
pub trait Whatever {
type Foo;
@@ -5,11 +7,14 @@ pub trait Whatever {
}
pub struct Struct;
+pub struct Struct2;
impl Whatever for Struct {
type Foo = u8;
}
+impl !Whatever for Struct2 {}
+
impl http::HttpTrait for Struct {}
mod traits {
diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs
index 400488cbe8570..b87fdeea89da0 100644
--- a/tests/rustdoc-gui/src/lib2/lib.rs
+++ b/tests/rustdoc-gui/src/lib2/lib.rs
@@ -1,6 +1,7 @@
// ignore-tidy-linelength
#![feature(doc_cfg)]
+#![feature(negative_impls)]
pub mod another_folder;
pub mod another_mod;
@@ -60,6 +61,8 @@ impl implementors::Whatever for Foo {
type Foo = u32;
}
+impl !implementors::Whatever for StructToImplOnReexport {}
+
#[doc(inline)]
pub use implementors::TraitToReexport;
diff --git a/tests/ui/entry-point/imported_main_conflict_lib.rs b/tests/ui/entry-point/imported_main_conflict_lib.rs
new file mode 100644
index 0000000000000..b50e37951ede4
--- /dev/null
+++ b/tests/ui/entry-point/imported_main_conflict_lib.rs
@@ -0,0 +1,10 @@
+// Tests that ambiguously glob importing main doesn't fail to compile in non-executable crates
+// Regression test for #149412
+//@ check-pass
+#![crate_type = "lib"]
+
+mod m1 { pub(crate) fn main() {} }
+mod m2 { pub(crate) fn main() {} }
+
+use m1::*;
+use m2::*;