diff options
21 files changed, 385 insertions, 309 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 066e3e9eceb..f0f5cc4db07 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -240,6 +240,9 @@ pub enum AttributeKind { /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). + PubTransparent(Span), + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr(ThinVec<(ReprAttr, Span)>), diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index d4c846de56e..4cfd9a82ce8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -19,3 +19,16 @@ impl<S: Stage> SingleAttributeParser<S> for AsPtrParser { Some(AttributeKind::AsPtr(cx.attr_span)) } } + +pub(crate) struct PubTransparentParser; +impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser { + const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> { + // FIXME: check that there's no args (this is currently checked elsewhere) + Some(AttributeKind::PubTransparent(cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1bcf500459d..b95ea598e72 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -19,7 +19,7 @@ use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser}; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::lint_helpers::AsPtrParser; +use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -113,6 +113,7 @@ attribute_parsers!( Single<InlineParser>, Single<MayDangleParser>, Single<OptimizeParser>, + Single<PubTransparentParser>, Single<RustcForceInlineParser>, Single<TransparencyParser>, // tidy-alphabetical-end diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5b1f1684d54..280b33f0723 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -710,7 +710,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( rustc_pub_transparent, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes, + ErrorFollowing, EncodeCrossCrate::Yes, "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", ), diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 32fec0604c0..752cc2eff97 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,6 +2,7 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; +use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; @@ -1384,7 +1385,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), ty::Adt(def, args) => { - if !def.did().is_local() && !tcx.has_attr(def.did(), sym::rustc_pub_transparent) + if !def.did().is_local() + && !attrs::find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::PubTransparent(_) + ) { let non_exhaustive = def.is_variant_list_non_exhaustive() || def diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 48db536c122..b45bff2af44 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -89,7 +89,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { opportunities: Vec::new(), }; - for bb in body.basic_blocks.indices() { + for (bb, _) in traversal::preorder(body) { finder.start_from_switch(bb); } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 7ead0a6d6b7..00fd3ba8046 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -430,7 +430,7 @@ where canonical_input, step_kind_from_parent, &mut canonical_goal_evaluation, - |search_graph, canonical_goal_evaluation| { + |search_graph, cx, canonical_input, canonical_goal_evaluation| { EvalCtxt::enter_canonical( cx, search_graph, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 30ac1bbe94b..d802bf4df19 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -150,6 +150,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ } + + &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => { + self.check_rustc_pub_transparent(attr_span, span, attrs) + } Attribute::Parsed(AttributeKind::Cold(attr_span)) => { self.check_cold(hir_id, *attr_span, span, target) } @@ -291,7 +295,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_type_const(hir_id,attr, target); } [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs), [ // ok sym::allow diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index a2442660259..1b99cc820f1 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -2,6 +2,7 @@ use derive_where::derive_where; use super::{AvailableDepth, Cx, NestedGoals}; use crate::data_structures::HashMap; +use crate::search_graph::EvaluationResult; struct Success<X: Cx> { required_depth: usize, @@ -43,28 +44,26 @@ impl<X: Cx> GlobalCache<X> { &mut self, cx: X, input: X::Input, - - origin_result: X::Result, + evaluation_result: EvaluationResult<X>, dep_node: X::DepNodeIndex, - - required_depth: usize, - encountered_overflow: bool, - nested_goals: NestedGoals<X>, ) { - let result = cx.mk_tracked(origin_result, dep_node); + let EvaluationResult { encountered_overflow, required_depth, heads, nested_goals, result } = + evaluation_result; + debug_assert!(heads.is_empty()); + let result = cx.mk_tracked(result, dep_node); let entry = self.map.entry(input).or_default(); if encountered_overflow { let with_overflow = WithOverflow { nested_goals, result }; let prev = entry.with_overflow.insert(required_depth, with_overflow); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); - assert_eq!(cx.get_tracked(&prev.result), origin_result); + assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } else { let prev = entry.success.replace(Success { required_depth, nested_goals, result }); if let Some(prev) = &prev { assert!(cx.evaluation_is_concurrent()); - assert_eq!(cx.get_tracked(&prev.result), origin_result); + assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result); } } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index b59b4f92854..a857da2fcd5 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -1,16 +1,16 @@ -/// The search graph is responsible for caching and cycle detection in the trait -/// solver. Making sure that caching doesn't result in soundness bugs or unstable -/// query results is very challenging and makes this one of the most-involved -/// self-contained components of the compiler. -/// -/// We added fuzzing support to test its correctness. The fuzzers used to verify -/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz. -/// -/// This is just a quick overview of the general design, please check out the relevant -/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for -/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`. -/// The global cache has to be completely unobservable, while the per-cycle cache may impact -/// behavior as long as the resulting behavior is still correct. +//! The search graph is responsible for caching and cycle detection in the trait +//! solver. Making sure that caching doesn't result in soundness bugs or unstable +//! query results is very challenging and makes this one of the most-involved +//! self-contained components of the compiler. +//! +//! We added fuzzing support to test its correctness. The fuzzers used to verify +//! the current implementation can be found in <https://github.com/lcnr/search_graph_fuzz>. +//! +//! This is just a quick overview of the general design, please check out the relevant +//! [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for +//! more details. Caching is split between a global cache and the per-cycle `provisional_cache`. +//! The global cache has to be completely unobservable, while the per-cycle cache may impact +//! behavior as long as the resulting behavior is still correct. use std::cmp::Ordering; use std::collections::BTreeMap; use std::collections::hash_map::Entry; @@ -381,18 +381,16 @@ impl PathsToNested { /// The nested goals of each stack entry and the path from the /// stack entry to that nested goal. /// +/// They are used when checking whether reevaluating a global cache +/// would encounter a cycle or use a provisional cache entry given the +/// currentl search graph state. We need to disable the global cache +/// in this case as it could otherwise result in behaviorial differences. +/// Cycles can impact behavior. The cycle ABA may have different final +/// results from a the cycle BAB depending on the cycle root. +/// /// We only start tracking nested goals once we've either encountered /// overflow or a solver cycle. This is a performance optimization to /// avoid tracking nested goals on the happy path. -/// -/// We use nested goals for two reasons: -/// - when rebasing provisional cache entries -/// - when checking whether we have to ignore a global cache entry as reevaluating -/// it would encounter a cycle or use a provisional cache entry. -/// -/// We need to disable the global cache if using it would hide a cycle, as -/// cycles can impact behavior. The cycle ABA may have different final -/// results from a the cycle BAB depending on the cycle root. #[derive_where(Debug, Default, Clone; X: Cx)] struct NestedGoals<X: Cx> { nested_goals: HashMap<X::Input, PathsToNested>, @@ -450,6 +448,43 @@ struct ProvisionalCacheEntry<X: Cx> { result: X::Result, } +/// The final result of evaluating a goal. +/// +/// We reset `encountered_overflow` when reevaluating a goal, +/// but need to track whether we've hit the recursion limit at +/// all for correctness. +/// +/// We've previously simply returned the final `StackEntry` but this +/// made it easy to accidentally drop information from the previous +/// evaluation. +#[derive_where(Debug; X: Cx)] +struct EvaluationResult<X: Cx> { + encountered_overflow: bool, + required_depth: usize, + heads: CycleHeads, + nested_goals: NestedGoals<X>, + result: X::Result, +} + +impl<X: Cx> EvaluationResult<X> { + fn finalize( + final_entry: StackEntry<X>, + encountered_overflow: bool, + result: X::Result, + ) -> EvaluationResult<X> { + EvaluationResult { + encountered_overflow, + // Unlike `encountered_overflow`, we share `heads`, `required_depth`, + // and `nested_goals` between evaluations. + required_depth: final_entry.required_depth, + heads: final_entry.heads, + nested_goals: final_entry.nested_goals, + // We only care about the final result. + result, + } + } +} + pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> { root_depth: AvailableDepth, /// The stack of goals currently being computed. @@ -562,7 +597,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input: X::Input, step_kind_from_parent: PathKind, inspect: &mut D::ProofTreeBuilder, - mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy, ) -> X::Result { let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack) @@ -616,12 +651,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { input, step_kind_from_parent, available_depth, + provisional_result: None, required_depth: 0, heads: Default::default(), encountered_overflow: false, has_been_used: None, nested_goals: Default::default(), - provisional_result: None, }); // This is for global caching, so we properly track query dependencies. @@ -630,35 +665,41 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // not tracked by the cache key and from outside of this anon task, it // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. - let ((final_entry, result), dep_node) = cx.with_cached_task(|| { - self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal) - }); + let (evaluation_result, dep_node) = + cx.with_cached_task(|| self.evaluate_goal_in_task(cx, input, inspect, evaluate_goal)); // We've finished computing the goal and have popped it from the stack, // lazily update its parent goal. Self::update_parent_goal( &mut self.stack, - final_entry.step_kind_from_parent, - final_entry.required_depth, - &final_entry.heads, - final_entry.encountered_overflow, - UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals), + step_kind_from_parent, + evaluation_result.required_depth, + &evaluation_result.heads, + evaluation_result.encountered_overflow, + UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals), ); + let result = evaluation_result.result; // We're now done with this goal. We only add the root of cycles to the global cache. // In case this goal is involved in a larger cycle add it to the provisional cache. - if final_entry.heads.is_empty() { + if evaluation_result.heads.is_empty() { if let Some((_scope, expected)) = validate_cache { // Do not try to move a goal into the cache again if we're testing // the global cache. - assert_eq!(result, expected, "input={input:?}"); + assert_eq!(evaluation_result.result, expected, "input={input:?}"); } else if D::inspect_is_noop(inspect) { - self.insert_global_cache(cx, final_entry, result, dep_node) + self.insert_global_cache(cx, input, evaluation_result, dep_node) } } else if D::ENABLE_PROVISIONAL_CACHE { debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}"); let entry = self.provisional_cache.entry(input).or_default(); - let StackEntry { heads, encountered_overflow, .. } = final_entry; + let EvaluationResult { + encountered_overflow, + required_depth: _, + heads, + nested_goals: _, + result, + } = evaluation_result; let path_from_head = Self::cycle_path_kind( &self.stack, step_kind_from_parent, @@ -1023,19 +1064,25 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, - ) -> (StackEntry<X>, X::Result) { + evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy, + ) -> EvaluationResult<X> { + // We reset `encountered_overflow` each time we rerun this goal + // but need to make sure we currently propagate it to the global + // cache even if only some of the evaluations actually reach the + // recursion limit. + let mut encountered_overflow = false; let mut i = 0; loop { - let result = evaluate_goal(self, inspect); + let result = evaluate_goal(self, cx, input, inspect); let stack_entry = self.stack.pop(); + encountered_overflow |= stack_entry.encountered_overflow; debug_assert_eq!(stack_entry.input, input); // If the current goal is not the root of a cycle, we are done. // // There are no provisional cache entries which depend on this goal. let Some(usage_kind) = stack_entry.has_been_used else { - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; // If it is a cycle head, we have to keep trying to prove it until @@ -1051,7 +1098,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // final result is equal to the initial response for that case. if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } // If computing this goal results in ambiguity with no constraints, @@ -1070,7 +1117,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::propagate_ambiguity(cx, input, result) }); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; // If we've reached the fixpoint step limit, we bail with overflow and taint all @@ -1082,7 +1129,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::on_fixpoint_overflow(cx, input) }); - return (stack_entry, result); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } // Clear all provisional cache entries which depend on a previous provisional @@ -1091,9 +1138,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { debug!(?result, "fixpoint changed provisional results"); self.stack.push(StackEntry { - has_been_used: None, + input, + step_kind_from_parent: stack_entry.step_kind_from_parent, + available_depth: stack_entry.available_depth, provisional_result: Some(result), - ..stack_entry + // We can keep these goals from previous iterations as they are only + // ever read after finalizing this evaluation. + required_depth: stack_entry.required_depth, + heads: stack_entry.heads, + nested_goals: stack_entry.nested_goals, + // We reset these two fields when rerunning this goal. We could + // keep `encountered_overflow` as it's only used as a performance + // optimization. However, given that the proof tree will likely look + // similar to the previous iterations when reevaluating, it's better + // for caching if the reevaluation also starts out with `false`. + encountered_overflow: false, + has_been_used: None, }); } } @@ -1109,21 +1169,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { fn insert_global_cache( &mut self, cx: X, - final_entry: StackEntry<X>, - result: X::Result, + input: X::Input, + evaluation_result: EvaluationResult<X>, dep_node: X::DepNodeIndex, ) { - debug!(?final_entry, ?result, "insert global cache"); - cx.with_global_cache(|cache| { - cache.insert( - cx, - final_entry.input, - result, - dep_node, - final_entry.required_depth, - final_entry.encountered_overflow, - final_entry.nested_goals, - ) - }) + debug!(?evaluation_result, "insert global cache"); + cx.with_global_cache(|cache| cache.insert(cx, input, evaluation_result, dep_node)) } } diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 8bb247bf055..e0fd934df69 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -26,6 +26,10 @@ pub(super) struct StackEntry<X: Cx> { /// The available depth of a given goal, immutable. pub available_depth: AvailableDepth, + /// Starts out as `None` and gets set when rerunning this + /// goal in case we encounter a cycle. + pub provisional_result: Option<X::Result>, + /// The maximum depth required while evaluating this goal. pub required_depth: usize, @@ -42,10 +46,6 @@ pub(super) struct StackEntry<X: Cx> { /// The nested goals of this goal, see the doc comment of the type. pub nested_goals: NestedGoals<X>, - - /// Starts out as `None` and gets set when rerunning this - /// goal in case we encounter a cycle. - pub provisional_result: Option<X::Result>, } #[derive_where(Default; X: Cx)] diff --git a/library/Cargo.toml b/library/Cargo.toml index 35480b9319d..c66f621ffde 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["profile-rustflags"] + [workspace] resolver = "1" members = [ @@ -44,6 +46,14 @@ object.debug = 0 rustc-demangle.debug = 0 rustc-demangle.opt-level = "s" +# panic_abort must always be compiled with panic=abort, even when the rest of the +# sysroot is panic=unwind. +[profile.dev.package.panic_abort] +rustflags = ["-Cpanic=abort"] + +[profile.release.package.panic_abort] +rustflags = ["-Cpanic=abort"] + [patch.crates-io] # See comments in `library/rustc-std-workspace-core/README.md` for what's going on # here diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 0671a8467e8..0364c664ba5 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -151,18 +151,6 @@ fn main() { cmd.arg("--sysroot").arg(&sysroot); } - // If we're compiling specifically the `panic_abort` crate then we pass - // the `-C panic=abort` option. Note that we do not do this for any - // other crate intentionally as this is the only crate for now that we - // ship with panic=abort. - // - // This... is a bit of a hack how we detect this. Ideally this - // information should be encoded in the crate I guess? Would likely - // require an RFC amendment to RFC 1513, however. - if crate_name == Some("panic_abort") { - cmd.arg("-C").arg("panic=abort"); - } - let crate_type = parse_value_from_args(&orig_args, "--crate-type"); // `-Ztls-model=initial-exec` must not be applied to proc-macros, see // issue https://github.com/rust-lang/rust/issues/100530 diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 408ef611ee5..c0a9d8c84f6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2432,20 +2432,6 @@ pub(crate) enum ConstantKind { Infer, } -impl Constant { - pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String { - self.kind.expr(tcx) - } - - pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option<String> { - self.kind.value(tcx) - } - - pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { - self.kind.is_literal(tcx) - } -} - impl ConstantKind { pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String { match *self { diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fb2b45802a6..606a9113908 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -439,24 +439,20 @@ impl CratesIndexPart { let content = format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>"); let template = layout::render(layout, &page, "", content, style_files); - match SortedTemplate::from_template(&template, DELIMITER) { - Ok(template) => template, - Err(e) => panic!( - "Object Replacement Character (U+FFFC) should not appear in the --index-page: {e}" - ), - } + SortedTemplate::from_template(&template, DELIMITER) + .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page") } /// Might return parts that are duplicate with ones in prexisting index.html fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> { let mut ret = PartsAndLocations::default(); - let path = PathBuf::from("index.html"); + let path = Path::new("index.html"); for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) { let part = format!( "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>", trailing_slash = ensure_trailing_slash(crate_name), ); - ret.push(path.clone(), part); + ret.push(path.to_path_buf(), part); } Ok(ret) } @@ -737,7 +733,7 @@ impl TraitAliasPart { }, }; - let implementors = imps + let mut implementors = imps .iter() .filter_map(|imp| { // If the trait and implementation are in the same crate, then @@ -759,12 +755,12 @@ impl TraitAliasPart { }) } }) - .collect::<Vec<_>>(); + .peekable(); // Only create a js file if we have impls to add to it. If the trait is // documented locally though we always create the file to avoid dead // links. - if implementors.is_empty() && !cache.paths.contains_key(&did) { + if implementors.peek().is_none() && !cache.paths.contains_key(&did) { continue; } @@ -775,11 +771,7 @@ impl TraitAliasPart { path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1])); let part = OrderedJson::array_sorted( - implementors - .iter() - .map(OrderedJson::serialize) - .collect::<Result<Vec<_>, _>>() - .unwrap(), + implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()), ); path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part])); } @@ -874,9 +866,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { let impl_ = cache .impls .get(&target_did) - .map(|v| &v[..]) - .unwrap_or_default() - .iter() + .into_iter() + .flatten() .map(|impl_| { (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() }) }) @@ -891,14 +882,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> { // Exclude impls that are directly on this type. They're already in the HTML. // Some inlining scenarios can cause there to be two versions of the same // impl: one on the type alias and one on the underlying target type. - let mut seen_impls: FxHashSet<ItemId> = cache - .impls - .get(&self_did) - .map(|s| &s[..]) - .unwrap_or_default() - .iter() - .map(|i| i.impl_item.item_id) - .collect(); + let mut seen_impls: FxHashSet<ItemId> = + cache.impls.get(&self_did).into_iter().flatten().map(|i| i.impl_item.item_id).collect(); for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ { // Only include this impl if it actually unifies with this alias. // Synthetic impls are not included; those are also included in the HTML. diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index cb4c1f7fbc0..abad6e48029 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -11,7 +11,7 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_metadata::rendered_const; use rustc_middle::{bug, ty}; -use rustc_span::{Pos, Symbol, kw}; +use rustc_span::{Pos, kw, sym}; use rustdoc_json_types::*; use thin_vec::ThinVec; @@ -66,47 +66,16 @@ impl JsonRenderer<'_> { id, crate_id: item_id.krate().as_u32(), name: name.map(|sym| sym.to_string()), - span: span.and_then(|span| self.convert_span(span)), - visibility: self.convert_visibility(visibility), + span: span.and_then(|span| span.into_json(self)), + visibility: visibility.into_json(self), docs, attrs, - deprecation: deprecation.map(from_deprecation), + deprecation: deprecation.into_json(self), inner, links, }) } - fn convert_span(&self, span: clean::Span) -> Option<Span> { - match span.filename(self.sess()) { - rustc_span::FileName::Real(name) => { - if let Some(local_path) = name.into_local_path() { - let hi = span.hi(self.sess()); - let lo = span.lo(self.sess()); - Some(Span { - filename: local_path, - begin: (lo.line, lo.col.to_usize() + 1), - end: (hi.line, hi.col.to_usize() + 1), - }) - } else { - None - } - } - _ => None, - } - } - - fn convert_visibility(&self, v: Option<ty::Visibility<DefId>>) -> Visibility { - match v { - None => Visibility::Default, - Some(ty::Visibility::Public) => Visibility::Public, - Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate, - Some(ty::Visibility::Restricted(did)) => Visibility::Restricted { - parent: self.id_from_item_default(did.into()), - path: self.tcx.def_path(did).to_string_no_crate_verbose(), - }, - } - } - fn ids(&self, items: &[clean::Item]) -> Vec<Id> { items .iter() @@ -140,11 +109,29 @@ where } } +impl<T, U> FromClean<Box<T>> for U +where + U: FromClean<T>, +{ + fn from_clean(opt: &Box<T>, renderer: &JsonRenderer<'_>) -> Self { + opt.as_ref().into_json(renderer) + } +} + +impl<T, U> FromClean<Option<T>> for Option<U> +where + U: FromClean<T>, +{ + fn from_clean(opt: &Option<T>, renderer: &JsonRenderer<'_>) -> Self { + opt.as_ref().map(|x| x.into_json(renderer)) + } +} + impl<T, U> FromClean<Vec<T>> for Vec<U> where U: FromClean<T>, { - fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> { + fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Self { items.iter().map(|i| i.into_json(renderer)).collect() } } @@ -153,20 +140,57 @@ impl<T, U> FromClean<ThinVec<T>> for Vec<U> where U: FromClean<T>, { - fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> { + fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Self { items.iter().map(|i| i.into_json(renderer)).collect() } } -pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation { - let attrs::Deprecation { since, note, suggestion: _ } = deprecation; - let since = match since { - DeprecatedSince::RustcVersion(version) => Some(version.to_string()), - DeprecatedSince::Future => Some("TBD".to_owned()), - DeprecatedSince::NonStandard(since) => Some(since.to_string()), - DeprecatedSince::Unspecified | DeprecatedSince::Err => None, - }; - Deprecation { since, note: note.map(|s| s.to_string()) } +impl FromClean<clean::Span> for Option<Span> { + fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self { + match span.filename(renderer.sess()) { + rustc_span::FileName::Real(name) => { + if let Some(local_path) = name.into_local_path() { + let hi = span.hi(renderer.sess()); + let lo = span.lo(renderer.sess()); + Some(Span { + filename: local_path, + begin: (lo.line, lo.col.to_usize() + 1), + end: (hi.line, hi.col.to_usize() + 1), + }) + } else { + None + } + } + _ => None, + } + } +} + +impl FromClean<Option<ty::Visibility<DefId>>> for Visibility { + fn from_clean(v: &Option<ty::Visibility<DefId>>, renderer: &JsonRenderer<'_>) -> Self { + match v { + None => Visibility::Default, + Some(ty::Visibility::Public) => Visibility::Public, + Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate, + Some(ty::Visibility::Restricted(did)) => Visibility::Restricted { + parent: renderer.id_from_item_default((*did).into()), + path: renderer.tcx.def_path(*did).to_string_no_crate_verbose(), + }, + } + } +} + +impl FromClean<attrs::Deprecation> for Deprecation { + fn from_clean(deprecation: &attrs::Deprecation, _renderer: &JsonRenderer<'_>) -> Self { + let attrs::Deprecation { since, note, suggestion: _ } = deprecation; + let since = match since { + DeprecatedSince::RustcVersion(version) => Some(version.to_string()), + DeprecatedSince::Future => Some("TBD".to_string()), + DeprecatedSince::NonStandard(since) => Some(since.to_string()), + DeprecatedSince::Unspecified | DeprecatedSince::Err => None, + }; + Deprecation { since, note: note.map(|sym| sym.to_string()) } + } } impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> { @@ -182,7 +206,7 @@ impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> { }, Parenthesized { inputs, output } => GenericArgs::Parenthesized { inputs: inputs.into_json(renderer), - output: output.as_ref().map(|a| a.as_ref().into_json(renderer)), + output: output.into_json(renderer), }, ReturnTypeNotation => GenericArgs::ReturnTypeNotation, })) @@ -193,7 +217,7 @@ impl FromClean<clean::GenericArg> for GenericArg { fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArg::*; match arg { - Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)), + Lifetime(l) => GenericArg::Lifetime(l.into_json(renderer)), Type(t) => GenericArg::Type(t.into_json(renderer)), Const(box c) => GenericArg::Const(c.into_json(renderer)), Infer => GenericArg::Infer, @@ -201,17 +225,6 @@ impl FromClean<clean::GenericArg> for GenericArg { } } -impl FromClean<clean::Constant> for Constant { - // FIXME(generic_const_items): Add support for generic const items. - fn from_clean(constant: &clean::Constant, renderer: &JsonRenderer<'_>) -> Self { - let tcx = renderer.tcx; - let expr = constant.expr(tcx); - let value = constant.value(tcx); - let is_literal = constant.is_literal(tcx); - Constant { expr, value, is_literal } - } -} - impl FromClean<clean::ConstantKind> for Constant { // FIXME(generic_const_items): Add support for generic const items. fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self { @@ -259,21 +272,25 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)), EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), - FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)), + FunctionItem(f) => { + ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) } - TraitItem(t) => ItemEnum::Trait(t.as_ref().into_json(renderer)), + TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)), + MethodItem(m, _) => { + ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) + } RequiredMethodItem(m) => { - ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) } - ImplItem(i) => ItemEnum::Impl(i.as_ref().into_json(renderer)), - StaticItem(s) => ItemEnum::Static(convert_static(s, &rustc_hir::Safety::Safe, renderer)), - ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)), + ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), + StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), + ForeignStaticItem(s, safety) => ItemEnum::Static(from_clean_static(s, *safety, renderer)), ForeignTypeItem => ItemEnum::ExternType, - TypeAliasItem(t) => ItemEnum::TypeAlias(t.as_ref().into_json(renderer)), + TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)), // FIXME(generic_const_items): Add support for generic free consts ConstantItem(ci) => ItemEnum::Constant { type_: ci.type_.into_json(renderer), @@ -289,7 +306,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum } // FIXME(generic_const_items): Add support for generic associated consts. RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.as_ref().into_json(renderer), value: None } + ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } } // FIXME(generic_const_items): Add support for generic associated consts. ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { @@ -361,32 +378,38 @@ impl FromClean<clean::Union> for Union { } } -pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader { - FunctionHeader { - is_async: header.is_async(), - is_const: header.is_const(), - is_unsafe: header.is_unsafe(), - abi: convert_abi(header.abi), +impl FromClean<rustc_hir::FnHeader> for FunctionHeader { + fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self { + FunctionHeader { + is_async: header.is_async(), + is_const: header.is_const(), + is_unsafe: header.is_unsafe(), + abi: header.abi.into_json(renderer), + } } } -fn convert_abi(a: ExternAbi) -> Abi { - match a { - ExternAbi::Rust => Abi::Rust, - ExternAbi::C { unwind } => Abi::C { unwind }, - ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind }, - ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind }, - ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind }, - ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind }, - ExternAbi::Win64 { unwind } => Abi::Win64 { unwind }, - ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind }, - ExternAbi::System { unwind } => Abi::System { unwind }, - _ => Abi::Other(a.to_string()), +impl FromClean<ExternAbi> for Abi { + fn from_clean(a: &ExternAbi, _renderer: &JsonRenderer<'_>) -> Self { + match *a { + ExternAbi::Rust => Abi::Rust, + ExternAbi::C { unwind } => Abi::C { unwind }, + ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind }, + ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind }, + ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind }, + ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind }, + ExternAbi::Win64 { unwind } => Abi::Win64 { unwind }, + ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind }, + ExternAbi::System { unwind } => Abi::System { unwind }, + _ => Abi::Other(a.to_string()), + } } } -fn convert_lifetime(l: &clean::Lifetime) -> String { - l.0.to_string() +impl FromClean<clean::Lifetime> for String { + fn from_clean(l: &clean::Lifetime, _renderer: &JsonRenderer<'_>) -> String { + l.0.to_string() + } } impl FromClean<clean::Generics> for Generics { @@ -411,16 +434,16 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind { fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericParamDefKind::*; match kind { - Lifetime { outlives } => GenericParamDefKind::Lifetime { - outlives: outlives.into_iter().map(convert_lifetime).collect(), - }, + Lifetime { outlives } => { + GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) } + } Type { bounds, default, synthetic } => GenericParamDefKind::Type { bounds: bounds.into_json(renderer), - default: default.as_ref().map(|x| x.as_ref().into_json(renderer)), + default: default.into_json(renderer), is_synthetic: *synthetic, }, Const { ty, default, synthetic: _ } => GenericParamDefKind::Const { - type_: ty.as_ref().into_json(renderer), + type_: ty.into_json(renderer), default: default.as_ref().map(|x| x.as_ref().clone()), }, } @@ -434,45 +457,14 @@ impl FromClean<clean::WherePredicate> for WherePredicate { BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate { type_: ty.into_json(renderer), bounds: bounds.into_json(renderer), - generic_params: bound_params - .iter() - .map(|x| { - let name = x.name.to_string(); - let kind = match &x.kind { - clean::GenericParamDefKind::Lifetime { outlives } => { - GenericParamDefKind::Lifetime { - outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(), - } - } - clean::GenericParamDefKind::Type { bounds, default, synthetic } => { - GenericParamDefKind::Type { - bounds: bounds - .into_iter() - .map(|bound| bound.into_json(renderer)) - .collect(), - default: default - .as_ref() - .map(|ty| ty.as_ref().into_json(renderer)), - is_synthetic: *synthetic, - } - } - clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => { - GenericParamDefKind::Const { - type_: ty.as_ref().into_json(renderer), - default: default.as_ref().map(|d| d.as_ref().clone()), - } - } - }; - GenericParamDef { name, kind } - }) - .collect(), + generic_params: bound_params.into_json(renderer), }, RegionPredicate { lifetime, bounds } => WherePredicate::LifetimePredicate { - lifetime: convert_lifetime(lifetime), + lifetime: lifetime.into_json(renderer), outlives: bounds .iter() .map(|bound| match bound { - clean::GenericBound::Outlives(lt) => convert_lifetime(lt), + clean::GenericBound::Outlives(lt) => lt.into_json(renderer), _ => bug!("found non-outlives-bound on lifetime predicate"), }) .collect(), @@ -496,15 +488,15 @@ impl FromClean<clean::GenericBound> for GenericBound { GenericBound::TraitBound { trait_: trait_.into_json(renderer), generic_params: generic_params.into_json(renderer), - modifier: from_trait_bound_modifier(modifier), + modifier: modifier.into_json(renderer), } } - Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)), + Outlives(lifetime) => GenericBound::Outlives(lifetime.into_json(renderer)), Use(args) => GenericBound::Use( args.iter() .map(|arg| match arg { clean::PreciseCapturingArg::Lifetime(lt) => { - PreciseCapturingArg::Lifetime(convert_lifetime(lt)) + PreciseCapturingArg::Lifetime(lt.into_json(renderer)) } clean::PreciseCapturingArg::Param(param) => { PreciseCapturingArg::Param(param.to_string()) @@ -516,19 +508,22 @@ impl FromClean<clean::GenericBound> for GenericBound { } } -pub(crate) fn from_trait_bound_modifier( - modifiers: &rustc_hir::TraitBoundModifiers, -) -> TraitBoundModifier { - use rustc_hir as hir; - let hir::TraitBoundModifiers { constness, polarity } = modifiers; - match (constness, polarity) { - (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None, - (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe, - (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => { - TraitBoundModifier::MaybeConst +impl FromClean<rustc_hir::TraitBoundModifiers> for TraitBoundModifier { + fn from_clean( + modifiers: &rustc_hir::TraitBoundModifiers, + _renderer: &JsonRenderer<'_>, + ) -> Self { + use rustc_hir as hir; + let hir::TraitBoundModifiers { constness, polarity } = modifiers; + match (constness, polarity) { + (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None, + (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe, + (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => { + TraitBoundModifier::MaybeConst + } + // FIXME: Fill out the rest of this matrix. + _ => TraitBoundModifier::None, } - // FIXME: Fill out the rest of this matrix. - _ => TraitBoundModifier::None, } } @@ -542,35 +537,35 @@ impl FromClean<clean::Type> for Type { match ty { clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)), clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait { - lifetime: lt.as_ref().map(convert_lifetime), + lifetime: lt.into_json(renderer), traits: bounds.into_json(renderer), }), Generic(s) => Type::Generic(s.to_string()), // FIXME: add dedicated variant to json Type? SelfTy => Type::Generic("Self".to_owned()), Primitive(p) => Type::Primitive(p.as_sym().to_string()), - BareFunction(f) => Type::FunctionPointer(Box::new(f.as_ref().into_json(renderer))), + BareFunction(f) => Type::FunctionPointer(Box::new(f.into_json(renderer))), Tuple(t) => Type::Tuple(t.into_json(renderer)), - Slice(t) => Type::Slice(Box::new(t.as_ref().into_json(renderer))), + Slice(t) => Type::Slice(Box::new(t.into_json(renderer))), Array(t, s) => { - Type::Array { type_: Box::new(t.as_ref().into_json(renderer)), len: s.to_string() } + Type::Array { type_: Box::new(t.into_json(renderer)), len: s.to_string() } } clean::Type::Pat(t, p) => Type::Pat { - type_: Box::new(t.as_ref().into_json(renderer)), + type_: Box::new(t.into_json(renderer)), __pat_unstable_do_not_use: p.to_string(), }, ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)), Infer => Type::Infer, RawPointer(mutability, type_) => Type::RawPointer { is_mutable: *mutability == ast::Mutability::Mut, - type_: Box::new(type_.as_ref().into_json(renderer)), + type_: Box::new(type_.into_json(renderer)), }, BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef { - lifetime: lifetime.as_ref().map(convert_lifetime), + lifetime: lifetime.into_json(renderer), is_mutable: *mutability == ast::Mutability::Mut, - type_: Box::new(type_.as_ref().into_json(renderer)), + type_: Box::new(type_.into_json(renderer)), }, - QPath(qpath) => qpath.as_ref().into_json(renderer), + QPath(qpath) => qpath.into_json(renderer), // FIXME(unsafe_binder): Implement rustdoc-json. UnsafeBinder(_) => todo!(), } @@ -578,7 +573,7 @@ impl FromClean<clean::Type> for Type { } impl FromClean<clean::Path> for Path { - fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Path { + fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Self { Path { path: path.whole_name(), id: renderer.id_from_item_default(path.def_id().into()), @@ -608,13 +603,13 @@ impl FromClean<clean::QPathData> for Type { name: assoc.name.to_string(), args: assoc.args.into_json(renderer), self_type: Box::new(self_type.into_json(renderer)), - trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)), + trait_: trait_.into_json(renderer), } } } impl FromClean<clean::Term> for Term { - fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Term { + fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Self { match term { clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)), clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)), @@ -630,7 +625,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer { is_unsafe: safety.is_unsafe(), is_const: false, is_async: false, - abi: convert_abi(*abi), + abi: abi.into_json(renderer), }, generic_params: generic_params.into_json(renderer), sig: decl.into_json(renderer), @@ -709,17 +704,17 @@ impl FromClean<clean::Impl> for Impl { .into_iter() .map(|x| x.to_string()) .collect(), - trait_: trait_.as_ref().map(|path| path.into_json(renderer)), + trait_: trait_.into_json(renderer), for_: for_.into_json(renderer), items: renderer.ids(&items), is_negative, is_synthetic, - blanket_impl: blanket_impl.map(|x| x.as_ref().into_json(renderer)), + blanket_impl: blanket_impl.map(|x| x.into_json(renderer)), } } } -pub(crate) fn from_function( +pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, header: rustc_hir::FnHeader, @@ -728,7 +723,7 @@ pub(crate) fn from_function( Function { sig: decl.into_json(renderer), generics: generics.into_json(renderer), - header: from_fn_header(&header), + header: header.into_json(renderer), has_body, } } @@ -750,7 +745,7 @@ impl FromClean<clean::Variant> for Variant { fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self { use clean::VariantKind::*; - let discriminant = variant.discriminant.as_ref().map(|d| d.into_json(renderer)); + let discriminant = variant.discriminant.into_json(renderer); let kind = match &variant.kind { CLike => VariantKind::Plain, @@ -783,10 +778,7 @@ impl FromClean<clean::Import> for Use { use clean::ImportKind::*; let (name, is_glob) = match import.kind { Simple(s) => (s.to_string(), false), - Glob => ( - import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(), - true, - ), + Glob => (import.source.path.last_opt().unwrap_or(sym::asterisk).to_string(), true), }; Use { source: import.source.path.whole_name(), @@ -798,20 +790,22 @@ impl FromClean<clean::Import> for Use { } impl FromClean<clean::ProcMacro> for ProcMacro { - fn from_clean(mac: &clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self { + fn from_clean(mac: &clean::ProcMacro, renderer: &JsonRenderer<'_>) -> Self { ProcMacro { - kind: from_macro_kind(mac.kind), + kind: mac.kind.into_json(renderer), helpers: mac.helpers.iter().map(|x| x.to_string()).collect(), } } } -pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind { - use rustc_span::hygiene::MacroKind::*; - match kind { - Bang => MacroKind::Bang, - Attr => MacroKind::Attr, - Derive => MacroKind::Derive, +impl FromClean<rustc_span::hygiene::MacroKind> for MacroKind { + fn from_clean(kind: &rustc_span::hygiene::MacroKind, _renderer: &JsonRenderer<'_>) -> Self { + use rustc_span::hygiene::MacroKind::*; + match kind { + Bang => MacroKind::Bang, + Attr => MacroKind::Attr, + Derive => MacroKind::Derive, + } } } @@ -822,9 +816,9 @@ impl FromClean<clean::TypeAlias> for TypeAlias { } } -fn convert_static( +fn from_clean_static( stat: &clean::Static, - safety: &rustc_hir::Safety, + safety: rustc_hir::Safety, renderer: &JsonRenderer<'_>, ) -> Static { let tcx = renderer.tcx; diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 029da1c1898..60e8e16e25e 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -221,6 +221,10 @@ impl TestCx<'_> { cmd.env("REMOTE_TEST_CLIENT", remote_test_client); } + if let Some(runner) = &self.config.runner { + cmd.env("RUNNER", runner); + } + // We don't want RUSTFLAGS set from the outside to interfere with // compiler flags set in the test cases: cmd.env_remove("RUSTFLAGS"); diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 60e711d3402..b95f3a5cfe5 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -1,4 +1,4 @@ -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::path::PathBuf; use std::{env, panic}; @@ -22,6 +22,20 @@ fn run_common(name: &str, args: Option<&[&str]>) -> Command { cmd.arg("0"); cmd.arg(bin_path); cmd + } else if let Ok(runner) = std::env::var("RUNNER") { + let mut args = split_maybe_args(&runner); + + let prog = args.remove(0); + let mut cmd = Command::new(prog); + + for arg in args { + cmd.arg(arg); + } + + cmd.arg("--"); + cmd.arg(bin_path); + + cmd } else { Command::new(bin_path) }; @@ -92,3 +106,12 @@ pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command { command.env("LC_ALL", "C"); // force english locale command } + +fn split_maybe_args(s: &str) -> Vec<OsString> { + // FIXME(132599): implement proper env var/shell argument splitting. + s.split(' ') + .filter_map(|s| { + if s.chars().all(|c| c.is_whitespace()) { None } else { Some(OsString::from(s)) } + }) + .collect() +} diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 63904bea622..426d65b7af3 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -3,7 +3,8 @@ // prevent the creation of a functional binary. // See https://github.com/rust-lang/rust/pull/49878 -//@ ignore-cross-compile +//@ needs-target-std +//@ ignore-android: FIXME(#142855) use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; diff --git a/tests/crashes/131451.rs b/tests/ui/mir/unreachable-loop-jump-threading.rs index cd5b44bad8a..8403906bb5c 100644 --- a/tests/crashes/131451.rs +++ b/tests/ui/mir/unreachable-loop-jump-threading.rs @@ -1,9 +1,10 @@ -//@ known-bug: #131451 +//@ build-pass //@ needs-rustc-debug-assertions //@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+JumpThreading --crate-type=lib pub fn fun(terminate: bool) { while true {} + //~^ WARN denote infinite loops with `loop { ... }` while !terminate {} } diff --git a/tests/ui/mir/unreachable-loop-jump-threading.stderr b/tests/ui/mir/unreachable-loop-jump-threading.stderr new file mode 100644 index 00000000000..21b174c8021 --- /dev/null +++ b/tests/ui/mir/unreachable-loop-jump-threading.stderr @@ -0,0 +1,10 @@ +warning: denote infinite loops with `loop { ... }` + --> $DIR/unreachable-loop-jump-threading.rs:6:5 + | +LL | while true {} + | ^^^^^^^^^^ help: use `loop` + | + = note: `#[warn(while_true)]` on by default + +warning: 1 warning emitted + |
