diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 8b740b7774089..2267f09ec1f11 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -322,10 +322,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
},
ItemKind::ForeignMod(ref fm) => {
if fm.abi.is_none() {
- self.maybe_lint_missing_abi(span, id, abi::Abi::C);
+ self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
}
hir::ItemKind::ForeignMod {
- abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
+ abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)),
items: self
.arena
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
@@ -1322,8 +1322,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
match ext {
Extern::None => abi::Abi::Rust,
Extern::Implicit => {
- self.maybe_lint_missing_abi(span, id, abi::Abi::C);
- abi::Abi::C
+ self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
+ abi::Abi::C { unwind: false }
}
Extern::Explicit(abi) => self.lower_abi(abi),
}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 474ec2b589b71..acfbec6fa21b5 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -164,6 +164,38 @@ impl<'a> PostExpansionVisitor<'a> {
"C-cmse-nonsecure-call ABI is experimental and subject to change"
);
}
+ "C-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "C-unwind ABI is experimental and subject to change"
+ );
+ }
+ "stdcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "stdcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "system-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "system-unwind ABI is experimental and subject to change"
+ );
+ }
+ "thiscall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "thiscall-unwind ABI is experimental and subject to change"
+ );
+ }
abi => self
.sess
.parse_sess
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 24da75114a656..e58b266fdb9e1 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -176,7 +176,7 @@ pub fn find_stability(
sess: &Session,
attrs: &[Attribute],
item_sp: Span,
-) -> (Option, Option) {
+) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) {
find_stability_generic(sess, attrs.iter(), item_sp)
}
@@ -184,15 +184,16 @@ fn find_stability_generic<'a, I>(
sess: &Session,
attrs_iter: I,
item_sp: Span,
-) -> (Option, Option)
+) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>)
where
I: Iterator- ,
{
use StabilityLevel::*;
- let mut stab: Option = None;
- let mut const_stab: Option = None;
+ let mut stab: Option<(Stability, Span)> = None;
+ let mut const_stab: Option<(ConstStability, Span)> = None;
let mut promotable = false;
+
let diagnostic = &sess.parse_sess.span_diagnostic;
'outer: for attr in attrs_iter {
@@ -356,10 +357,12 @@ where
}
let level = Unstable { reason, issue: issue_num, is_soft };
if sym::unstable == meta_name {
- stab = Some(Stability { level, feature });
+ stab = Some((Stability { level, feature }, attr.span));
} else {
- const_stab =
- Some(ConstStability { level, feature, promotable: false });
+ const_stab = Some((
+ ConstStability { level, feature, promotable: false },
+ attr.span,
+ ));
}
}
(None, _, _) => {
@@ -432,10 +435,12 @@ where
(Some(feature), Some(since)) => {
let level = Stable { since };
if sym::stable == meta_name {
- stab = Some(Stability { level, feature });
+ stab = Some((Stability { level, feature }, attr.span));
} else {
- const_stab =
- Some(ConstStability { level, feature, promotable: false });
+ const_stab = Some((
+ ConstStability { level, feature, promotable: false },
+ attr.span,
+ ));
}
}
(None, _) => {
@@ -455,7 +460,7 @@ where
// Merge the const-unstable info into the stability info
if promotable {
- if let Some(ref mut stab) = const_stab {
+ if let Some((ref mut stab, _)) = const_stab {
stab.promotable = promotable;
} else {
struct_span_err!(
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index b2647e6c8d384..83bb1d0172fbb 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -542,7 +542,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
// FIXME find a cleaner way to support varargs
if fn_sig.c_variadic {
- if fn_sig.abi != Abi::C {
+ if !matches!(fn_sig.abi, Abi::C { .. }) {
fx.tcx.sess.span_fatal(
span,
&format!("Variadic call for non-C abi {:?}", fn_sig.abi),
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs
index 005d2efdd3b26..5f21046b05e47 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs
@@ -24,7 +24,7 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec {
debug!("preparing the RPATH!");
- let libs = config.used_crates.clone();
+ let libs = config.used_crates;
let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>();
let rpaths = get_rpaths(config, &libs);
let mut flags = rpaths_to_flags(&rpaths);
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index ce8103c0f850d..929cc56294d3d 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -774,10 +774,16 @@ impl SyntaxExtension {
.find_by_name(attrs, sym::rustc_builtin_macro)
.map(|a| a.value_str().unwrap_or(name));
let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
- if const_stability.is_some() {
+ if let Some((_, sp)) = const_stability {
sess.parse_sess
.span_diagnostic
- .span_err(span, "macros cannot have const stability attributes");
+ .struct_span_err(sp, "macros cannot have const stability attributes")
+ .span_label(sp, "invalid const stability attribute")
+ .span_label(
+ sess.source_map().guess_head_span(span),
+ "const stability attribute affects this macro",
+ )
+ .emit();
}
SyntaxExtension {
@@ -786,7 +792,7 @@ impl SyntaxExtension {
allow_internal_unstable,
allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe),
local_inner_macros,
- stability,
+ stability: stability.map(|(s, _)| s),
deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d),
helper_attrs,
edition,
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 8ee995a59d80d..95ae6a1ac9a26 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -644,6 +644,9 @@ declare_features! (
/// Allows associated types in inherent impls.
(active, inherent_associated_types, "1.52.0", Some(8995), None),
+ /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
+ (active, c_unwind, "1.52.0", Some(74990), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 547779dd6856a..9e61c83fda3a5 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -57,6 +57,7 @@ mod methods;
mod non_ascii_idents;
mod non_fmt_panic;
mod nonstandard_style;
+mod noop_method_call;
mod passes;
mod redundant_semicolon;
mod traits;
@@ -81,6 +82,7 @@ use methods::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
use nonstandard_style::*;
+use noop_method_call::*;
use redundant_semicolon::*;
use traits::*;
use types::*;
@@ -168,6 +170,7 @@ macro_rules! late_lint_passes {
DropTraitConstraints: DropTraitConstraints,
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
NonPanicFmt: NonPanicFmt,
+ NoopMethodCall: NoopMethodCall,
]
);
};
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
new file mode 100644
index 0000000000000..479cc00199f6a
--- /dev/null
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -0,0 +1,111 @@
+use crate::context::LintContext;
+use crate::rustc_middle::ty::TypeFoldable;
+use crate::LateContext;
+use crate::LateLintPass;
+use rustc_hir::def::DefKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_middle::ty;
+use rustc_span::symbol::sym;
+
+declare_lint! {
+ /// The `noop_method_call` lint detects specific calls to noop methods
+ /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # #![allow(unused)]
+ /// #![warn(noop_method_call)]
+ /// struct Foo;
+ /// let foo = &Foo;
+ /// let clone: &Foo = foo.clone();
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Some method calls are noops meaning that they do nothing. Usually such methods
+ /// are the result of blanket implementations that happen to create some method invocations
+ /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
+ /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
+ /// as references are copy. This lint detects these calls and warns the user about them.
+ pub NOOP_METHOD_CALL,
+ Allow,
+ "detects the use of well-known noop methods"
+}
+
+declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
+
+impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // We only care about method calls.
+ let (call, elements) = match expr.kind {
+ ExprKind::MethodCall(call, _, elements, _) => (call, elements),
+ _ => return,
+ };
+ // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
+ // traits and ignore any other method call.
+ let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
+ // Verify we are dealing with a method/associated function.
+ Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
+ // Check that we're dealing with a trait method for one of the traits we care about.
+ Some(trait_id)
+ if [sym::Clone, sym::Deref, sym::Borrow]
+ .iter()
+ .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) =>
+ {
+ (trait_id, did)
+ }
+ _ => return,
+ },
+ _ => return,
+ };
+ let substs = cx.typeck_results().node_substs(expr.hir_id);
+ if substs.needs_subst() {
+ // We can't resolve on types that require monomorphization, so we don't handle them if
+ // we need to perfom substitution.
+ return;
+ }
+ let param_env = cx.tcx.param_env(trait_id);
+ // Resolve the trait method instance.
+ let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
+ Ok(Some(i)) => i,
+ _ => return,
+ };
+ // (Re)check that it implements the noop diagnostic.
+ for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
+ if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
+ let method = &call.ident.name;
+ let receiver = &elements[0];
+ let receiver_ty = cx.typeck_results().expr_ty(receiver);
+ let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
+ if receiver_ty != expr_ty {
+ // This lint will only trigger if the receiver type and resulting expression \
+ // type are the same, implying that the method call is unnecessary.
+ return;
+ }
+ let expr_span = expr.span;
+ let note = format!(
+ "the type `{:?}` which `{}` is being called on is the same as \
+ the type returned from `{}`, so the method call does not do \
+ anything and can be removed",
+ receiver_ty, method, method,
+ );
+
+ let span = expr_span.with_lo(receiver.span.hi());
+ cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
+ let method = &call.ident.name;
+ let message = format!(
+ "call to `.{}()` on a reference in this situation does nothing",
+ &method,
+ );
+ lint.build(&message)
+ .span_label(span, "unnecessary method call")
+ .note(¬e)
+ .emit()
+ });
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index bf315c81588a9..f19cc99844926 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -12,7 +12,6 @@ use rustc_target::spec::abi;
use std::borrow::Cow;
use std::fmt;
-use std::ops::Deref;
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)]
pub struct ExpectedFound {
@@ -548,7 +547,6 @@ impl Trait for X {
TargetFeatureCast(def_id) => {
let attrs = self.get_attrs(*def_id);
let target_spans = attrs
- .deref()
.iter()
.filter(|attr| attr.has_name(sym::target_feature))
.map(|attr| attr.span);
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 6d2ab0e5f5a80..814581a6cf171 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2562,6 +2562,7 @@ fn fn_can_unwind(
panic_strategy: PanicStrategy,
codegen_fn_attr_flags: CodegenFnAttrFlags,
call_conv: Conv,
+ abi: SpecAbi,
) -> bool {
if panic_strategy != PanicStrategy::Unwind {
// In panic=abort mode we assume nothing can unwind anywhere, so
@@ -2586,17 +2587,34 @@ fn fn_can_unwind(
//
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
//
- // Foreign items (case 1) are assumed to not unwind; it is
- // UB otherwise. (At least for now; see also
- // rust-lang/rust#63909 and Rust RFC 2753.)
- //
- // Items defined in Rust with non-Rust ABIs (case 2) are also
- // not supposed to unwind. Whether this should be enforced
- // (versus stating it is UB) and *how* it would be enforced
- // is currently under discussion; see rust-lang/rust#58794.
- //
- // In either case, we mark item as explicitly nounwind.
- false
+ // In both of these cases, we should refer to the ABI to determine whether or not we
+ // should unwind. See Rust RFC 2945 for more information on this behavior, here:
+ // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
+ use SpecAbi::*;
+ match abi {
+ C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+ unwind
+ }
+ Cdecl
+ | Fastcall
+ | Vectorcall
+ | Aapcs
+ | Win64
+ | SysV64
+ | PtxKernel
+ | Msp430Interrupt
+ | X86Interrupt
+ | AmdGpuKernel
+ | EfiApi
+ | AvrInterrupt
+ | AvrNonBlockingInterrupt
+ | CCmseNonSecureCall
+ | RustIntrinsic
+ | PlatformIntrinsic
+ | Unadjusted => false,
+ // In the `if` above, we checked for functions with the Rust calling convention.
+ Rust | RustCall => unreachable!(),
+ }
}
}
}
@@ -2654,14 +2672,14 @@ where
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
// It's the ABI's job to select this, not ours.
- System => bug!("system abi should be selected elsewhere"),
+ System { .. } => bug!("system abi should be selected elsewhere"),
EfiApi => bug!("eficall abi should be selected elsewhere"),
- Stdcall => Conv::X86Stdcall,
+ Stdcall { .. } => Conv::X86Stdcall,
Fastcall => Conv::X86Fastcall,
Vectorcall => Conv::X86VectorCall,
- Thiscall => Conv::X86ThisCall,
- C => Conv::C,
+ Thiscall { .. } => Conv::X86ThisCall,
+ C { .. } => Conv::C,
Unadjusted => Conv::C,
Win64 => Conv::X86_64Win64,
SysV64 => Conv::X86_64SysV,
@@ -2823,7 +2841,12 @@ where
c_variadic: sig.c_variadic,
fixed_count: inputs.len(),
conv,
- can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv),
+ can_unwind: fn_can_unwind(
+ cx.tcx().sess.panic_strategy(),
+ codegen_fn_attr_flags,
+ conv,
+ sig.abi,
+ ),
};
fn_abi.adjust_for_abi(cx, sig.abi);
debug!("FnAbi::new_internal = {:?}", fn_abi);
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index b0b58a8d00367..24b9408ffb657 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -10,16 +10,18 @@ use rustc_middle::mir::{
FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
-use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty};
-use rustc_span::{source_map::DesugaringKind, symbol::sym, Span};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable};
+use rustc_span::source_map::DesugaringKind;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use crate::dataflow::drop_flag_effects;
use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
use crate::util::borrowck_errors;
use crate::borrow_check::{
- borrow_set::BorrowData, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt,
- PrefixSet, WriteKind,
+ borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
+ InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
};
use super::{
@@ -1267,6 +1269,29 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if return_span != borrow_span {
err.span_label(borrow_span, note);
+
+ let tcx = self.infcx.tcx;
+ let ty_params = ty::List::empty();
+
+ let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
+ let return_ty = tcx.erase_regions(return_ty);
+
+ // to avoid panics
+ if !return_ty.has_infer_types() {
+ if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
+ if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
+ {
+ if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
+ err.span_suggestion_hidden(
+ return_span,
+ "use `.collect()` to allocate the iterator",
+ format!("{}{}", snippet, ".collect::>()"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
}
Some(err)
diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs
index 8c05e6fd5d0e4..e423e449746fc 100644
--- a/compiler/rustc_mir/src/borrow_check/invalidation.rs
+++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs
@@ -165,7 +165,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
self.consume_operand(location, value);
// Invalidate all borrows of local places
- let borrow_set = self.borrow_set.clone();
+ let borrow_set = self.borrow_set;
let resume = self.location_table.start_index(resume.start_location());
for (i, data) in borrow_set.iter_enumerated() {
if borrow_of_local_data(data.borrowed_place) {
@@ -177,7 +177,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
// Invalidate all borrows of local places
- let borrow_set = self.borrow_set.clone();
+ let borrow_set = self.borrow_set;
let start = self.location_table.start_index(location);
for (i, data) in borrow_set.iter_enumerated() {
if borrow_of_local_data(data.borrowed_place) {
@@ -369,7 +369,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
);
let tcx = self.tcx;
let body = self.body;
- let borrow_set = self.borrow_set.clone();
+ let borrow_set = self.borrow_set;
let indices = self.borrow_set.indices();
each_borrow_involving_path(
self,
@@ -377,7 +377,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
body,
location,
(sd, place),
- &borrow_set.clone(),
+ borrow_set,
indices,
|this, borrow_index, borrow| {
match (rw, borrow.kind) {
diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs
index 0807949a2d91b..4aa1360d53539 100644
--- a/compiler/rustc_mir/src/interpret/terminator.rs
+++ b/compiler/rustc_mir/src/interpret/terminator.rs
@@ -248,9 +248,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
throw_ub_format!(
- "calling a function with ABI {:?} using caller ABI {:?}",
- callee_abi,
- caller_abi
+ "calling a function with ABI {} using caller ABI {}",
+ callee_abi.name(),
+ caller_abi.name()
)
}
}
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 126fb957a6a99..4db7debee7e8f 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -51,7 +51,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatKind::Constant { value } => Test {
span: match_pair.pattern.span,
- kind: TestKind::Eq { value, ty: match_pair.pattern.ty.clone() },
+ kind: TestKind::Eq { value, ty: match_pair.pattern.ty },
},
PatKind::Range(range) => {
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index b928458df8ee4..3ac9c631d031e 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -548,7 +548,7 @@ macro_rules! unpack {
}};
}
-fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
+fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
// Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
@@ -558,12 +558,42 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b
return false;
}
- // This is a special case: some functions have a C abi but are meant to
- // unwind anyway. Don't stop them.
match unwind_attr {
- None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
+ // If an `#[unwind]` attribute was found, we should adhere to it.
Some(UnwindAttr::Allowed) => false,
Some(UnwindAttr::Aborts) => true,
+ // If no attribute was found and the panic strategy is `unwind`, then we should examine
+ // the function's ABI string to determine whether it should abort upon panic.
+ None => {
+ use Abi::*;
+ match abi {
+ // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
+ // permits unwinding. If so, we should not abort. Otherwise, we should.
+ C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+ !unwind
+ }
+ // Rust and `rust-call` functions are allowed to unwind, and should not abort.
+ Rust | RustCall => false,
+ // Other ABI's should abort.
+ Cdecl
+ | Fastcall
+ | Vectorcall
+ | Aapcs
+ | Win64
+ | SysV64
+ | PtxKernel
+ | Msp430Interrupt
+ | X86Interrupt
+ | AmdGpuKernel
+ | EfiApi
+ | AvrInterrupt
+ | AvrNonBlockingInterrupt
+ | CCmseNonSecureCall
+ | RustIntrinsic
+ | PlatformIntrinsic
+ | Unadjusted => true,
+ }
+ }
}
}
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 7c62a234dbaec..02b20e45d000c 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -109,7 +109,7 @@ impl LibFeatureCollector<'tcx> {
}
fn span_feature_error(&self, span: Span, msg: &str) {
- struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg,).emit();
+ struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit();
}
}
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index f538427efd9ff..c4c1034374360 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -70,6 +70,17 @@ impl InheritConstStability {
}
}
+enum InheritStability {
+ Yes,
+ No,
+}
+
+impl InheritStability {
+ fn yes(&self) -> bool {
+ matches!(self, InheritStability::Yes)
+ }
+}
+
// A private tree-walker for producing an Index.
struct Annotator<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
@@ -91,6 +102,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
kind: AnnotationKind,
inherit_deprecation: InheritDeprecation,
inherit_const_stability: InheritConstStability,
+ inherit_from_parent: InheritStability,
visit_children: F,
) where
F: FnOnce(&mut Self),
@@ -131,12 +143,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
if self.tcx.features().staged_api {
- if let Some(..) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
- self.tcx.sess.span_err(
- item_sp,
- "`#[deprecated]` cannot be used in staged API; \
- use `#[rustc_deprecated]` instead",
- );
+ if let Some(a) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
+ self.tcx
+ .sess
+ .struct_span_err(a.span, "`#[deprecated]` cannot be used in staged API")
+ .span_label(a.span, "use `#[rustc_deprecated]` instead")
+ .span_label(item_sp, "")
+ .emit();
}
} else {
self.recurse_with_stability_attrs(
@@ -150,7 +163,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
- let const_stab = const_stab.map(|const_stab| {
+ let const_stab = const_stab.map(|(const_stab, _)| {
let const_stab = self.tcx.intern_const_stability(const_stab);
self.index.const_stab_map.insert(hir_id, const_stab);
const_stab
@@ -180,12 +193,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
- let stab = stab.map(|stab| {
+ let stab = stab.map(|(stab, span)| {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
|| (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
{
- self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
+ self.tcx.sess.struct_span_err(span,"this stability annotation is useless")
+ .span_label(span, "useless stability annotation")
+ .span_label(item_sp, "the stability attribute annotates this item")
+ .emit();
}
debug!("annotate: found {:?}", stab);
@@ -202,16 +218,19 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
{
match stab_v.parse::() {
Err(_) => {
- self.tcx.sess.span_err(item_sp, "Invalid stability version found");
+ self.tcx.sess.struct_span_err(span, "invalid stability version found")
+ .span_label(span, "invalid stability version")
+ .span_label(item_sp, "the stability attribute annotates this item")
+ .emit();
break;
}
Ok(stab_vp) => match dep_v.parse::() {
Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
Ordering::Less => {
- self.tcx.sess.span_err(
- item_sp,
- "An API can't be stabilized after it is deprecated",
- );
+ self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated")
+ .span_label(span, "invalid version")
+ .span_label(item_sp, "the stability attribute annotates this item")
+ .emit();
break;
}
Ordering::Equal => continue,
@@ -219,9 +238,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
},
Err(_) => {
if dep_v != "TBD" {
- self.tcx
- .sess
- .span_err(item_sp, "Invalid deprecation version found");
+ self.tcx.sess.struct_span_err(span, "invalid deprecation version found")
+ .span_label(span, "invalid deprecation version")
+ .span_label(item_sp, "the stability attribute annotates this item")
+ .emit();
}
break;
}
@@ -237,7 +257,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
if stab.is_none() {
debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
if let Some(stab) = self.parent_stab {
- if inherit_deprecation.yes() && stab.level.is_unstable() {
+ if inherit_deprecation.yes() && stab.level.is_unstable()
+ || inherit_from_parent.yes()
+ {
self.index.stab_map.insert(hir_id, stab);
}
}
@@ -368,6 +390,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::Yes,
|_| {},
)
}
@@ -382,6 +405,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
kind,
InheritDeprecation::Yes,
const_stab_inherit,
+ InheritStability::No,
|v| intravisit::walk_item(v, i),
);
self.in_trait_impl = orig_in_trait_impl;
@@ -395,6 +419,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|v| {
intravisit::walk_trait_item(v, ti);
},
@@ -411,6 +436,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
kind,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|v| {
intravisit::walk_impl_item(v, ii);
},
@@ -425,6 +451,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::Yes,
|v| {
if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
v.annotate(
@@ -434,6 +461,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|_| {},
);
}
@@ -451,6 +479,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::Yes,
|v| {
intravisit::walk_struct_field(v, s);
},
@@ -465,6 +494,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|v| {
intravisit::walk_foreign_item(v, i);
},
@@ -479,6 +509,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|_| {},
);
}
@@ -499,6 +530,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
kind,
InheritDeprecation::No,
InheritConstStability::No,
+ InheritStability::No,
|v| {
intravisit::walk_generic_param(v, p);
},
@@ -669,6 +701,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
AnnotationKind::Required,
InheritDeprecation::Yes,
InheritConstStability::No,
+ InheritStability::No,
|v| intravisit::walk_crate(v, krate),
);
}
@@ -729,18 +762,13 @@ impl Visitor<'tcx> for Checker<'tcx> {
// error if all involved types and traits are stable, because
// it will have no effect.
// See: https://github.com/rust-lang/rust/issues/55436
- if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) =
+ if let (Some((Stability { level: attr::Unstable { .. }, .. }, span)), _) =
attr::find_stability(&self.tcx.sess, &item.attrs, item.span)
{
let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
c.visit_ty(self_ty);
c.visit_trait_ref(t);
if c.fully_stable {
- let span = item
- .attrs
- .iter()
- .find(|a| a.has_name(sym::unstable))
- .map_or(item.span, |a| a.span);
self.tcx.struct_span_lint_hir(
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
item.hir_id(),
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index bd0296751a535..61f4c00a4ca42 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -156,6 +156,21 @@ impl<'a> NameResolution<'a> {
}
}
+// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;`
+// are permitted for backward-compatibility under a deprecation lint.
+fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBinding<'_>) -> bool {
+ match (&import.kind, &binding.kind) {
+ (
+ ImportKind::Single { .. },
+ NameBindingKind::Import {
+ import: Import { kind: ImportKind::ExternCrate { .. }, .. },
+ ..
+ },
+ ) => import.vis.get() == ty::Visibility::Public,
+ _ => false,
+ }
+}
+
impl<'a> Resolver<'a> {
crate fn resolve_ident_in_module_unadjusted(
&mut self,
@@ -263,10 +278,7 @@ impl<'a> Resolver<'a> {
return Err((Determined, Weak::No));
}
}
- // `extern crate` are always usable for backwards compatibility, see issue #37020,
- // remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`.
- let usable = this.is_accessible_from(binding.vis, parent_scope.module)
- || binding.is_extern_crate();
+ let usable = this.is_accessible_from(binding.vis, parent_scope.module);
if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
};
@@ -309,10 +321,7 @@ impl<'a> Resolver<'a> {
}
}
- if !(self.is_accessible_from(binding.vis, parent_scope.module) ||
- // Remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
- (self.last_import_segment && binding.is_extern_crate()))
- {
+ if !self.is_accessible_from(binding.vis, parent_scope.module) {
self.privacy_errors.push(PrivacyError {
ident,
binding,
@@ -455,9 +464,8 @@ impl<'a> Resolver<'a> {
binding: &'a NameBinding<'a>,
import: &'a Import<'a>,
) -> &'a NameBinding<'a> {
- let vis = if binding.vis.is_at_least(import.vis.get(), self) ||
- // cf. `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
- !import.is_glob() && binding.is_extern_crate()
+ let vis = if binding.vis.is_at_least(import.vis.get(), self)
+ || pub_use_of_private_extern_crate_hack(import, binding)
{
import.vis.get()
} else {
@@ -1188,7 +1196,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
// All namespaces must be re-exported with extra visibility for an error to occur.
if !any_successful_reexport {
let (ns, binding) = reexport_error.unwrap();
- if ns == TypeNS && binding.is_extern_crate() {
+ if pub_use_of_private_extern_crate_hack(import, binding) {
let msg = format!(
"extern crate `{}` is private, and cannot be \
re-exported (error E0365), consider declaring with \
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index f9af0886a959a..25e7b3cba9341 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -129,6 +129,7 @@ symbols! {
BTreeMap,
BTreeSet,
BinaryHeap,
+ Borrow,
C,
CString,
Center,
@@ -141,6 +142,7 @@ symbols! {
Decodable,
Decoder,
Default,
+ Deref,
Encodable,
Encoder,
Eq,
@@ -328,6 +330,7 @@ symbols! {
bridge,
bswap,
c_str,
+ c_unwind,
c_variadic,
call,
call_mut,
@@ -789,6 +792,9 @@ symbols! {
none_error,
nontemporal_store,
nontrapping_dash_fptoint: "nontrapping-fptoint",
+ noop_method_borrow,
+ noop_method_clone,
+ noop_method_deref,
noreturn,
nostack,
not,
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index bbf7ecc39cfd9..12c0a147990fa 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -440,7 +440,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
}
match sig.abi {
Abi::Rust => {}
- Abi::C => cx.push("KC"),
+ Abi::C { unwind: false } => cx.push("KC"),
abi => {
cx.push("K");
let name = abi.name();
diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs
index 65e8a4e8db2ad..17eb33b8f2eaa 100644
--- a/compiler/rustc_target/src/spec/abi.rs
+++ b/compiler/rustc_target/src/spec/abi.rs
@@ -8,24 +8,21 @@ mod tests;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
#[derive(HashStable_Generic, Encodable, Decodable)]
pub enum Abi {
- // N.B., this ordering MUST match the AbiDatas array below.
- // (This is ensured by the test indices_are_correct().)
-
// Multiplatform / generic ABIs
//
// These ABIs come first because every time we add a new ABI, we
// have to re-bless all the hashing tests. These are used in many
// places, so giving them stable values reduces test churn. The
// specific values are meaningless.
- Rust = 0,
- C = 1,
+ Rust,
+ C { unwind: bool },
// Single platform ABIs
Cdecl,
- Stdcall,
+ Stdcall { unwind: bool },
Fastcall,
Vectorcall,
- Thiscall,
+ Thiscall { unwind: bool },
Aapcs,
Win64,
SysV64,
@@ -39,7 +36,7 @@ pub enum Abi {
CCmseNonSecureCall,
// Multiplatform / generic ABIs
- System,
+ System { unwind: bool },
RustIntrinsic,
RustCall,
PlatformIntrinsic,
@@ -61,13 +58,16 @@ pub struct AbiData {
const AbiDatas: &[AbiData] = &[
// Cross-platform ABIs
AbiData { abi: Abi::Rust, name: "Rust", generic: true },
- AbiData { abi: Abi::C, name: "C", generic: true },
+ AbiData { abi: Abi::C { unwind: false }, name: "C", generic: true },
+ AbiData { abi: Abi::C { unwind: true }, name: "C-unwind", generic: true },
// Platform-specific ABIs
AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false },
- AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false },
+ AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall", generic: false },
+ AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind", generic: false },
AbiData { abi: Abi::Fastcall, name: "fastcall", generic: false },
AbiData { abi: Abi::Vectorcall, name: "vectorcall", generic: false },
- AbiData { abi: Abi::Thiscall, name: "thiscall", generic: false },
+ AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall", generic: false },
+ AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind", generic: false },
AbiData { abi: Abi::Aapcs, name: "aapcs", generic: false },
AbiData { abi: Abi::Win64, name: "win64", generic: false },
AbiData { abi: Abi::SysV64, name: "sysv64", generic: false },
@@ -84,7 +84,8 @@ const AbiDatas: &[AbiData] = &[
},
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false },
// Cross-platform ABIs
- AbiData { abi: Abi::System, name: "system", generic: true },
+ AbiData { abi: Abi::System { unwind: false }, name: "system", generic: true },
+ AbiData { abi: Abi::System { unwind: true }, name: "system-unwind", generic: true },
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },
AbiData { abi: Abi::RustCall, name: "rust-call", generic: true },
AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true },
@@ -103,7 +104,52 @@ pub fn all_names() -> Vec<&'static str> {
impl Abi {
#[inline]
pub fn index(self) -> usize {
- self as usize
+ // N.B., this ordering MUST match the AbiDatas array above.
+ // (This is ensured by the test indices_are_correct().)
+ use Abi::*;
+ let i = match self {
+ // Cross-platform ABIs
+ Rust => 0,
+ C { unwind: false } => 1,
+ C { unwind: true } => 2,
+ // Platform-specific ABIs
+ Cdecl => 3,
+ Stdcall { unwind: false } => 4,
+ Stdcall { unwind: true } => 5,
+ Fastcall => 6,
+ Vectorcall => 7,
+ Thiscall { unwind: false } => 8,
+ Thiscall { unwind: true } => 9,
+ Aapcs => 10,
+ Win64 => 11,
+ SysV64 => 12,
+ PtxKernel => 13,
+ Msp430Interrupt => 14,
+ X86Interrupt => 15,
+ AmdGpuKernel => 16,
+ EfiApi => 17,
+ AvrInterrupt => 18,
+ AvrNonBlockingInterrupt => 19,
+ CCmseNonSecureCall => 20,
+ // Cross-platform ABIs
+ System { unwind: false } => 21,
+ System { unwind: true } => 22,
+ RustIntrinsic => 23,
+ RustCall => 24,
+ PlatformIntrinsic => 25,
+ Unadjusted => 26,
+ };
+ debug_assert!(
+ AbiDatas
+ .iter()
+ .enumerate()
+ .find(|(_, AbiData { abi, .. })| *abi == self)
+ .map(|(index, _)| index)
+ .expect("abi variant has associated data")
+ == i,
+ "Abi index did not match `AbiDatas` ordering"
+ );
+ i
}
#[inline]
@@ -122,6 +168,8 @@ impl Abi {
impl fmt::Display for Abi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "\"{}\"", self.name())
+ match self {
+ abi => write!(f, "\"{}\"", abi.name()),
+ }
}
}
diff --git a/compiler/rustc_target/src/spec/arm_base.rs b/compiler/rustc_target/src/spec/arm_base.rs
index b74d80dc6bb2b..01f573313c97f 100644
--- a/compiler/rustc_target/src/spec/arm_base.rs
+++ b/compiler/rustc_target/src/spec/arm_base.rs
@@ -2,5 +2,14 @@ use crate::spec::abi::Abi;
// All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm
pub fn unsupported_abis() -> Vec {
- vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Thiscall, Abi::Win64, Abi::SysV64]
+ vec![
+ Abi::Stdcall { unwind: false },
+ Abi::Stdcall { unwind: true },
+ Abi::Fastcall,
+ Abi::Vectorcall,
+ Abi::Thiscall { unwind: false },
+ Abi::Thiscall { unwind: true },
+ Abi::Win64,
+ Abi::SysV64,
+ ]
}
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
index 0f9d3c3de1543..110c8dd80ea77 100644
--- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
@@ -23,10 +23,12 @@ pub fn target() -> Target {
panic_strategy: PanicStrategy::Abort,
relocation_model: RelocModel::Static,
unsupported_abis: vec![
- Abi::Stdcall,
+ Abi::Stdcall { unwind: false },
+ Abi::Stdcall { unwind: true },
Abi::Fastcall,
Abi::Vectorcall,
- Abi::Thiscall,
+ Abi::Thiscall { unwind: false },
+ Abi::Thiscall { unwind: true },
Abi::Win64,
Abi::SysV64,
],
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 039e9a8b2745f..4e8c0c03696d9 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1282,24 +1282,31 @@ impl Target {
/// Given a function ABI, turn it into the correct ABI for this target.
pub fn adjust_abi(&self, abi: Abi) -> Abi {
match abi {
- Abi::System => {
+ Abi::System { unwind } => {
if self.is_like_windows && self.arch == "x86" {
- Abi::Stdcall
+ Abi::Stdcall { unwind }
} else {
- Abi::C
+ Abi::C { unwind }
}
}
// These ABI kinds are ignored on non-x86 Windows targets.
// See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
// and the individual pages for __stdcall et al.
- Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => {
- if self.is_like_windows && self.arch != "x86" { Abi::C } else { abi }
+ Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => {
+ if self.is_like_windows && self.arch != "x86" { Abi::C { unwind } } else { abi }
+ }
+ Abi::Fastcall | Abi::Vectorcall => {
+ if self.is_like_windows && self.arch != "x86" {
+ Abi::C { unwind: false }
+ } else {
+ abi
+ }
}
Abi::EfiApi => {
if self.arch == "x86_64" {
Abi::Win64
} else {
- Abi::C
+ Abi::C { unwind: false }
}
}
abi => abi,
diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
index 3c9c7d578fbd4..15d8e4843f976 100644
--- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
+++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
@@ -49,10 +49,12 @@ pub fn target() -> Target {
// create the tests for this.
unsupported_abis: vec![
Abi::Cdecl,
- Abi::Stdcall,
+ Abi::Stdcall { unwind: false },
+ Abi::Stdcall { unwind: true },
Abi::Fastcall,
Abi::Vectorcall,
- Abi::Thiscall,
+ Abi::Thiscall { unwind: false },
+ Abi::Thiscall { unwind: true },
Abi::Aapcs,
Abi::Win64,
Abi::SysV64,
diff --git a/compiler/rustc_target/src/spec/riscv_base.rs b/compiler/rustc_target/src/spec/riscv_base.rs
index 64cf890037e51..5bcbb2e621bd0 100644
--- a/compiler/rustc_target/src/spec/riscv_base.rs
+++ b/compiler/rustc_target/src/spec/riscv_base.rs
@@ -5,10 +5,12 @@ use crate::spec::abi::Abi;
pub fn unsupported_abis() -> Vec {
vec![
Abi::Cdecl,
- Abi::Stdcall,
+ Abi::Stdcall { unwind: false },
+ Abi::Stdcall { unwind: true },
Abi::Fastcall,
Abi::Vectorcall,
- Abi::Thiscall,
+ Abi::Thiscall { unwind: false },
+ Abi::Thiscall { unwind: true },
Abi::Aapcs,
Abi::Win64,
Abi::SysV64,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index bfb5ebcea58b1..a3faf4cb7d4c1 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -819,7 +819,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
sig.decl
.inputs
.iter()
- .map(|arg| match arg.clone().kind {
+ .map(|arg| match arg.kind {
hir::TyKind::Tup(ref tys) => ArgKind::Tuple(
Some(arg.span),
vec![("_".to_owned(), "_".to_owned()); tys.len()],
diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs
index d98f18182c843..b7275bac19048 100644
--- a/compiler/rustc_traits/src/chalk/mod.rs
+++ b/compiler/rustc_traits/src/chalk/mod.rs
@@ -165,7 +165,7 @@ crate fn evaluate_goal<'tcx>(
// let's just ignore that
let sol = Canonical {
max_universe: ty::UniverseIndex::from_usize(0),
- variables: obligation.variables.clone(),
+ variables: obligation.variables,
value: QueryResponse {
var_values: CanonicalVarValues { var_values: IndexVec::new() }
.make_identity(tcx),
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs
index ca1e79fac73e9..6ef63bcbbbf21 100644
--- a/compiler/rustc_typeck/src/check/callee.rs
+++ b/compiler/rustc_typeck/src/check/callee.rs
@@ -465,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expected_arg_tys = self.expected_inputs_for_expected_output(
call_expr.span,
expected,
- fn_sig.output().clone(),
+ fn_sig.output(),
fn_sig.inputs(),
);
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 2faf128c491fd..48740e533da8e 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -711,7 +711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
let ret_ty = ret_coercion.borrow().expected_ty();
- let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty.clone());
+ let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
ret_coercion.borrow_mut().coerce(
self,
&self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)),
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 3881d55ef9169..a175da3270638 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2666,7 +2666,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
} else if tcx.sess.check_name(attr, sym::used) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
} else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) {
- if tcx.fn_sig(id).abi() != abi::Abi::C {
+ if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) {
struct_span_err!(
tcx.sess,
attr.span,
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index 6ddc26efeae35..fb864c5b4a7d7 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -118,14 +118,19 @@ use astconv::AstConv;
use bounds::Bounds;
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
- if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0045,
- "C-variadic function must have C or cdecl calling convention"
- );
- err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
+ match (decl.c_variadic, abi) {
+ // The function has the correct calling convention, or isn't a "C-variadic" function.
+ (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {}
+ // The function is a "C-variadic" function with an incorrect calling convention.
+ (true, _) => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0045,
+ "C-variadic function must have C or cdecl calling convention"
+ );
+ err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
+ }
}
}
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs
index 4e48db7f49305..e636e490e1bf4 100644
--- a/library/alloc/src/collections/btree/map/tests.rs
+++ b/library/alloc/src/collections/btree/map/tests.rs
@@ -1801,11 +1801,11 @@ fn test_occupied_entry_key() {
let key = "hello there";
let value = "value goes here";
assert!(a.is_empty());
- a.insert(key.clone(), value.clone());
+ a.insert(key, value);
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
- match a.entry(key.clone()) {
+ match a.entry(key) {
Vacant(_) => panic!(),
Occupied(e) => assert_eq!(key, *e.key()),
}
@@ -1821,11 +1821,11 @@ fn test_vacant_entry_key() {
let value = "value goes here";
assert!(a.is_empty());
- match a.entry(key.clone()) {
+ match a.entry(key) {
Occupied(_) => panic!(),
Vacant(e) => {
assert_eq!(key, *e.key());
- e.insert(value.clone());
+ e.insert(value);
}
}
assert_eq!(a.len(), 1);
diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs
index c9040cd0a1670..f28be20aaa1e6 100644
--- a/library/core/src/borrow.rs
+++ b/library/core/src/borrow.rs
@@ -153,6 +153,7 @@
/// [`HashMap`]: ../../std/collections/struct.HashMap.html
/// [`String`]: ../../std/string/struct.String.html
#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "Borrow"]
pub trait Borrow {
/// Immutably borrows from an owned value.
///
@@ -205,6 +206,7 @@ pub trait BorrowMut: Borrow {
#[stable(feature = "rust1", since = "1.0.0")]
impl Borrow for T {
+ #[rustc_diagnostic_item = "noop_method_borrow"]
fn borrow(&self) -> &T {
self
}
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index a953a3a4182bc..51a2dc03de318 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -104,12 +104,14 @@
/// [impls]: #implementors
#[stable(feature = "rust1", since = "1.0.0")]
#[lang = "clone"]
+#[rustc_diagnostic_item = "Clone"]
pub trait Clone: Sized {
/// Returns a copy of the value.
///
/// # Examples
///
/// ```
+ /// # #![allow(noop_method_call)]
/// let hello = "Hello"; // &str implements Clone
///
/// assert_eq!("Hello", hello.clone());
@@ -221,6 +223,7 @@ mod impls {
#[stable(feature = "rust1", since = "1.0.0")]
impl Clone for &T {
#[inline]
+ #[rustc_diagnostic_item = "noop_method_clone"]
fn clone(&self) -> Self {
*self
}
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index f28c4673cc033..e179ce01c417a 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -93,6 +93,7 @@ fn _assert_is_object_safe(_: &dyn Iterator
- ) {}
message = "`{Self}` is not an iterator"
)]
#[doc(spotlight)]
+#[rustc_diagnostic_item = "Iterator"]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub trait Iterator {
/// The type of the elements being iterated over.
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index 2419771eae212..10e3ce67448c8 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -60,6 +60,7 @@
#[doc(alias = "*")]
#[doc(alias = "&*")]
#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "Deref"]
pub trait Deref {
/// The resulting type after dereferencing.
#[stable(feature = "rust1", since = "1.0.0")]
@@ -78,6 +79,7 @@ pub trait Deref {
impl Deref for &T {
type Target = T;
+ #[rustc_diagnostic_item = "noop_method_deref"]
fn deref(&self) -> &T {
*self
}
diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs
index 9dbe232e4eec8..b336c03b5adbe 100644
--- a/library/core/tests/iter/adapters/intersperse.rs
+++ b/library/core/tests/iter/adapters/intersperse.rs
@@ -9,7 +9,7 @@ fn test_intersperse() {
assert_eq!(v, vec![1]);
let xs = ["a", "", "b", "c"];
- let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect();
+ let v: Vec<&str> = xs.iter().map(|x| *x).intersperse(", ").collect();
let text: String = v.concat();
assert_eq!(text, "a, , b, c".to_string());
@@ -24,7 +24,7 @@ fn test_intersperse_size_hint() {
assert_eq!(iter.size_hint(), (0, Some(0)));
let xs = ["a", "", "b", "c"];
- let mut iter = xs.iter().map(|x| x.clone()).intersperse(", ");
+ let mut iter = xs.iter().map(|x| *x).intersperse(", ");
assert_eq!(iter.size_hint(), (7, Some(7)));
assert_eq!(iter.next(), Some("a"));
diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
index 467968354e25d..819be14222752 100644
--- a/library/std/src/collections/hash/map/tests.rs
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -774,11 +774,11 @@ fn test_occupied_entry_key() {
let key = "hello there";
let value = "value goes here";
assert!(a.is_empty());
- a.insert(key.clone(), value.clone());
+ a.insert(key, value);
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
- match a.entry(key.clone()) {
+ match a.entry(key) {
Vacant(_) => panic!(),
Occupied(e) => assert_eq!(key, *e.key()),
}
@@ -793,11 +793,11 @@ fn test_vacant_entry_key() {
let value = "value goes here";
assert!(a.is_empty());
- match a.entry(key.clone()) {
+ match a.entry(key) {
Occupied(_) => panic!(),
Vacant(e) => {
assert_eq!(key, *e.key());
- e.insert(value.clone());
+ e.insert(value);
}
}
assert_eq!(a.len(), 1);
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 987371f50ec22..839c64ee5c443 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -1,6 +1,8 @@
use crate::cmp;
use crate::fmt;
-use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE};
+use crate::io::{
+ self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
+};
/// The `BufReader` struct adds buffering to any reader.
///
@@ -435,3 +437,9 @@ impl Seek for BufReader {
})
}
}
+
+impl SizeHint for BufReader {
+ fn lower_bound(&self) -> usize {
+ self.buffer().len()
+ }
+}
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 2291498740510..17002e3b8602d 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -2238,6 +2238,19 @@ impl BufRead for Chain {
}
}
+impl SizeHint for Chain {
+ fn lower_bound(&self) -> usize {
+ SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second)
+ }
+
+ fn upper_bound(&self) -> Option {
+ match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
+ (Some(first), Some(second)) => Some(first + second),
+ _ => None,
+ }
+ }
+}
+
/// Reader adaptor which limits the bytes read from an underlying reader.
///
/// This struct is generally created by calling [`take`] on a reader.
@@ -2464,6 +2477,30 @@ impl Iterator for Bytes {
};
}
}
+
+ fn size_hint(&self) -> (usize, Option) {
+ SizeHint::size_hint(&self.inner)
+ }
+}
+
+trait SizeHint {
+ fn lower_bound(&self) -> usize;
+
+ fn upper_bound(&self) -> Option;
+
+ fn size_hint(&self) -> (usize, Option) {
+ (self.lower_bound(), self.upper_bound())
+ }
+}
+
+impl SizeHint for T {
+ default fn lower_bound(&self) -> usize {
+ 0
+ }
+
+ default fn upper_bound(&self) -> Option {
+ None
+ }
}
/// An iterator over the contents of an instance of `BufRead` split on a
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index f176c2f088cb3..a85dd0d982715 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -1,7 +1,7 @@
use super::{repeat, Cursor, SeekFrom};
use crate::cmp::{self, min};
use crate::io::{self, IoSlice, IoSliceMut};
-use crate::io::{BufRead, Read, Seek, Write};
+use crate::io::{BufRead, BufReader, Read, Seek, Write};
use crate::ops::Deref;
#[test]
@@ -198,6 +198,53 @@ fn chain_bufread() {
cmp_bufread(chain1, chain2, &testdata[..]);
}
+#[test]
+fn bufreader_size_hint() {
+ let testdata = b"ABCDEFGHIJKL";
+ let mut buf_reader = BufReader::new(&testdata[..]);
+ assert_eq!(buf_reader.buffer().len(), 0);
+
+ let buffer_length = testdata.len();
+ buf_reader.fill_buf().unwrap();
+
+ // Check that size hint matches buffer contents
+ let mut buffered_bytes = buf_reader.bytes();
+ let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
+ assert_eq!(lower_bound, buffer_length);
+
+ // Check that size hint matches buffer contents after advancing
+ buffered_bytes.next().unwrap().unwrap();
+ let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
+ assert_eq!(lower_bound, buffer_length - 1);
+}
+
+#[test]
+fn empty_size_hint() {
+ let size_hint = io::empty().bytes().size_hint();
+ assert_eq!(size_hint, (0, Some(0)));
+}
+
+#[test]
+fn chain_empty_size_hint() {
+ let chain = io::empty().chain(io::empty());
+ let size_hint = chain.bytes().size_hint();
+ assert_eq!(size_hint, (0, Some(0)));
+}
+
+#[test]
+fn chain_size_hint() {
+ let testdata = b"ABCDEFGHIJKL";
+ let mut buf_reader_1 = BufReader::new(&testdata[..6]);
+ let mut buf_reader_2 = BufReader::new(&testdata[6..]);
+
+ buf_reader_1.fill_buf().unwrap();
+ buf_reader_2.fill_buf().unwrap();
+
+ let chain = buf_reader_1.chain(buf_reader_2);
+ let size_hint = chain.bytes().size_hint();
+ assert_eq!(size_hint, (testdata.len(), None));
+}
+
#[test]
fn chain_zero_length_read_is_not_eof() {
let a = b"A";
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index e43ce4cdb4b8e..f472361f916db 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -4,7 +4,9 @@
mod tests;
use crate::fmt;
-use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
+use crate::io::{
+ self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
+};
/// A reader which is always at EOF.
///
@@ -80,6 +82,12 @@ impl fmt::Debug for Empty {
}
}
+impl SizeHint for Empty {
+ fn upper_bound(&self) -> Option {
+ Some(0)
+ }
+}
+
/// A reader which yields one byte over and over and over and over and over and...
///
/// This struct is generally created by calling [`repeat()`]. Please
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index 943aa09f678d7..ae94527e2b4ca 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -8,7 +8,7 @@
- [Documentation tests](documentation-tests.md)
- [Linking to items by name](linking-to-items-by-name.md)
- [Lints](lints.md)
-- [Passes](passes.md)
- [Advanced features](advanced-features.md)
- [Unstable features](unstable-features.md)
+- [Passes](passes.md)
- [References](references.md)
diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md
index 0302fbecb6ed0..2e4016e24bc3f 100644
--- a/src/doc/rustdoc/src/command-line-arguments.md
+++ b/src/doc/rustdoc/src/command-line-arguments.md
@@ -57,23 +57,6 @@ release: 1.17.0
LLVM version: 3.9
```
-## `-r`/`--input-format`: input format
-
-This flag is currently ignored; the idea is that `rustdoc` would support various
-input formats, and you could specify them via this flag.
-
-Rustdoc only supports Rust source code and Markdown input formats. If the
-file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file.
-Otherwise, it assumes that the input file is Rust.
-
-
-## `-w`/`--output-format`: output format
-
-This flag is currently ignored; the idea is that `rustdoc` would support
-various output formats, and you could specify them via this flag.
-
-Rustdoc only supports HTML output, and so this flag is redundant today.
-
## `-o`/`--output`: output path
Using this flag looks like this:
@@ -100,6 +83,25 @@ By default, `rustdoc` assumes that the name of your crate is the same name
as the `.rs` file. `--crate-name` lets you override this assumption with
whatever name you choose.
+## `--document-private-items`: Show items that are not public
+
+Using this flag looks like this:
+
+```bash
+$ rustdoc src/lib.rs --document-private-items
+```
+
+By default, `rustdoc` only documents items that are publicly reachable.
+
+```rust
+pub fn public() {} // this item is public and will documented
+mod private { // this item is private and will not be documented
+ pub fn unreachable() {} // this item is public, but unreachable, so it will not be documented
+}
+```
+
+`--document-private-items` documents all items, even if they're not public.
+
## `-L`/`--library-path`: where to look for dependencies
Using this flag looks like this:
@@ -166,38 +168,6 @@ affect that.
The arguments to this flag are the same as those for the `-C` flag on rustc. Run `rustc -C help` to
get the full list.
-## `--passes`: add more rustdoc passes
-
-Using this flag looks like this:
-
-```bash
-$ rustdoc --passes list
-$ rustdoc src/lib.rs --passes strip-priv-imports
-```
-
-An argument of "list" will print a list of possible "rustdoc passes", and other
-arguments will be the name of which passes to run in addition to the defaults.
-
-For more details on passes, see [the chapter on them](passes.md).
-
-See also `--no-defaults`.
-
-## `--no-defaults`: don't run default passes
-
-Using this flag looks like this:
-
-```bash
-$ rustdoc src/lib.rs --no-defaults
-```
-
-By default, `rustdoc` will run several passes over your code. This
-removes those defaults, allowing you to use `--passes` to specify
-exactly which passes you want.
-
-For more details on passes, see [the chapter on them](passes.md).
-
-See also `--passes`.
-
## `--test`: run code examples as tests
Using this flag looks like this:
@@ -429,3 +399,21 @@ If you specify `@path` on the command-line, then it will open `path` and read
command line options from it. These options are one per line; a blank line indicates
an empty option. The file can use Unix or Windows style line endings, and must be
encoded as UTF-8.
+
+## `--passes`: add more rustdoc passes
+
+This flag is **deprecated**.
+For more details on passes, see [the chapter on them](passes.md).
+
+## `--no-defaults`: don't run default passes
+
+This flag is **deprecated**.
+For more details on passes, see [the chapter on them](passes.md).
+
+## `-r`/`--input-format`: input format
+
+This flag is **deprecated** and **has no effect**.
+
+Rustdoc only supports Rust source code and Markdown input formats. If the
+file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file.
+Otherwise, it assumes that the input file is Rust.
diff --git a/src/doc/rustdoc/src/passes.md b/src/doc/rustdoc/src/passes.md
index 140b832f19a54..c3c3fd3068ec4 100644
--- a/src/doc/rustdoc/src/passes.md
+++ b/src/doc/rustdoc/src/passes.md
@@ -3,86 +3,9 @@
Rustdoc has a concept called "passes". These are transformations that
`rustdoc` runs on your documentation before producing its final output.
-In addition to the passes below, check out the docs for these flags:
+Customizing passes is **deprecated**. The available passes are not considered stable and may
+change in any release.
-* [`--passes`](command-line-arguments.md#--passes-add-more-rustdoc-passes)
-* [`--no-defaults`](command-line-arguments.md#--no-defaults-dont-run-default-passes)
-
-## Default passes
-
-By default, rustdoc will run some passes, namely:
-
-* `strip-hidden`
-* `strip-private`
-* `collapse-docs`
-* `unindent-comments`
-
-However, `strip-private` implies `strip-priv-imports`, and so effectively,
-all passes are run by default.
-
-## `strip-hidden`
-
-This pass implements the `#[doc(hidden)]` attribute. When this pass runs, it
-checks each item, and if it is annotated with this attribute, it removes it
-from `rustdoc`'s output.
-
-Without this pass, these items will remain in the output.
-
-## `unindent-comments`
-
-When you write a doc comment like this:
-
-```rust,no_run
-/// This is a documentation comment.
-# fn f() {}
-```
-
-There's a space between the `///` and that `T`. That spacing isn't intended
-to be a part of the output; it's there for humans, to help separate the doc
-comment syntax from the text of the comment. This pass is what removes that
-space.
-
-The exact rules are left under-specified so that we can fix issues that we find.
-
-Without this pass, the exact number of spaces is preserved.
-
-## `collapse-docs`
-
-With this pass, multiple `#[doc]` attributes are converted into one single
-documentation string.
-
-For example:
-
-```rust,no_run
-#[doc = "This is the first line."]
-#[doc = "This is the second line."]
-# fn f() {}
-```
-
-Gets collapsed into a single doc string of
-
-```text
-This is the first line.
-This is the second line.
-```
-
-## `strip-private`
-
-This removes documentation for any non-public items, so for example:
-
-```rust,no_run
-/// These are private docs.
-struct Private;
-
-/// These are public docs.
-pub struct Public;
-```
-
-This pass removes the docs for `Private`, since they're not public.
-
-This pass implies `strip-priv-imports`.
-
-## `strip-priv-imports`
-
-This is the same as `strip-private`, but for `extern crate` and `use`
-statements instead of items.
+In the past the most common use case for customizing passes was to omit the `strip-private` pass.
+You can do this more easily, and without risk of the pass being changed, by passing
+[`--document-private-items`](./unstable-features.md#--document-private-items).
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index b43070510413a..7d1845dc9578e 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -340,6 +340,30 @@ Some methodology notes about what rustdoc counts in this metric:
Public items that are not documented can be seen with the built-in `missing_docs` lint. Private
items that are not documented can be seen with Clippy's `missing_docs_in_private_items` lint.
+## `-w`/`--output-format`: output format
+
+When using
+[`--show-coverage`](https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#--show-coverage-get-statistics-about-code-documentation-coverage),
+passing `--output-format json` will display the coverage information in JSON format. For example,
+here is the JSON for a file with one documented item and one undocumented item:
+
+```rust
+/// This item has documentation
+pub fn foo() {}
+
+pub fn no_documentation() {}
+```
+
+```json
+{"no_std.rs":{"total":3,"with_docs":1,"total_examples":3,"with_examples":0}}
+```
+
+Note that the third item is the crate root, which in this case is undocumented.
+
+When not using `--show-coverage`, `--output-format json` emits documentation in the experimental
+[JSON format](https://github.com/rust-lang/rfcs/pull/2963). `--output-format html` has no effect,
+and is also accepted on stable toolchains.
+
### `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests
Using this flag looks like this:
diff --git a/src/doc/unstable-book/src/language-features/c-unwind.md b/src/doc/unstable-book/src/language-features/c-unwind.md
new file mode 100644
index 0000000000000..2801d9b5e7778
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/c-unwind.md
@@ -0,0 +1,15 @@
+# `c_unwind`
+
+The tracking issue for this feature is: [#74990]
+
+[#74990]: https://github.com/rust-lang/rust/issues/74990
+
+------------------------
+
+Introduces four new ABI strings: "C-unwind", "stdcall-unwind",
+"thiscall-unwind", and "system-unwind". These enable unwinding from other
+languages (such as C++) into Rust frames and from Rust into other languages.
+
+See [RFC 2945] for more information.
+
+[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
new file mode 100644
index 0000000000000..976168a9ac407
--- /dev/null
+++ b/src/librustdoc/html/render/context.rs
@@ -0,0 +1,617 @@
+use std::cell::RefCell;
+use std::collections::BTreeMap;
+use std::io;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::sync::mpsc::{channel, Receiver};
+use std::sync::Arc;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::Session;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::FileName;
+use rustc_span::symbol::sym;
+
+use super::cache::{build_index, ExternalLocation};
+use super::print_item::{full_path, item_path, print_item};
+use super::write_shared::write_shared;
+use super::{
+ print_sidebar, settings, AllTypes, NameDoc, SharedContext, StylePath, BASIC_KEYWORDS,
+ CURRENT_DEPTH, INITIAL_IDS,
+};
+
+use crate::clean::{self, AttributesExt};
+use crate::config::RenderOptions;
+use crate::docfs::{DocFS, PathError};
+use crate::error::Error;
+use crate::formats::cache::Cache;
+use crate::formats::item_type::ItemType;
+use crate::formats::FormatRenderer;
+use crate::html::escape::Escape;
+use crate::html::format::Buffer;
+use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
+use crate::html::{layout, sources};
+
+/// Major driving force in all rustdoc rendering. This contains information
+/// about where in the tree-like hierarchy rendering is occurring and controls
+/// how the current page is being rendered.
+///
+/// It is intended that this context is a lightweight object which can be fairly
+/// easily cloned because it is cloned per work-job (about once per item in the
+/// rustdoc tree).
+#[derive(Clone)]
+crate struct Context<'tcx> {
+ /// Current hierarchy of components leading down to what's currently being
+ /// rendered
+ crate current: Vec,
+ /// The current destination folder of where HTML artifacts should be placed.
+ /// This changes as the context descends into the module hierarchy.
+ crate dst: PathBuf,
+ /// A flag, which when `true`, will render pages which redirect to the
+ /// real location of an item. This is used to allow external links to
+ /// publicly reused items to redirect to the right location.
+ crate render_redirect_pages: bool,
+ /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
+ /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
+ /// the crate.
+ crate redirections: Option>>>,
+ /// The map used to ensure all generated 'id=' attributes are unique.
+ pub(super) id_map: Rc>,
+ /// Tracks section IDs for `Deref` targets so they match in both the main
+ /// body and the sidebar.
+ pub(super) deref_id_map: Rc>>,
+ crate shared: Arc>,
+ all: Rc>,
+ /// Storage for the errors produced while generating documentation so they
+ /// can be printed together at the end.
+ crate errors: Rc>,
+ crate cache: Rc,
+}
+
+impl<'tcx> Context<'tcx> {
+ pub(super) fn path(&self, filename: &str) -> PathBuf {
+ // We use splitn vs Path::extension here because we might get a filename
+ // like `style.min.css` and we want to process that into
+ // `style-suffix.min.css`. Path::extension would just return `css`
+ // which would result in `style.min-suffix.css` which isn't what we
+ // want.
+ let (base, ext) = filename.split_once('.').unwrap();
+ let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
+ self.dst.join(&filename)
+ }
+
+ pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
+ self.shared.tcx
+ }
+
+ fn sess(&self) -> &'tcx Session {
+ &self.shared.tcx.sess
+ }
+
+ pub(super) fn derive_id(&self, id: String) -> String {
+ let mut map = self.id_map.borrow_mut();
+ map.derive(id)
+ }
+
+ /// String representation of how to get back to the root path of the 'doc/'
+ /// folder in terms of a relative URL.
+ pub(super) fn root_path(&self) -> String {
+ "../".repeat(self.current.len())
+ }
+
+ fn render_item(&self, it: &clean::Item, pushname: bool) -> String {
+ // A little unfortunate that this is done like this, but it sure
+ // does make formatting *a lot* nicer.
+ CURRENT_DEPTH.with(|slot| {
+ slot.set(self.current.len());
+ });
+
+ let mut title = if it.is_primitive() || it.is_keyword() {
+ // No need to include the namespace for primitive types and keywords
+ String::new()
+ } else {
+ self.current.join("::")
+ };
+ if pushname {
+ if !title.is_empty() {
+ title.push_str("::");
+ }
+ title.push_str(&it.name.unwrap().as_str());
+ }
+ title.push_str(" - Rust");
+ let tyname = it.type_();
+ let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc));
+ let desc = if let Some(desc) = desc {
+ desc
+ } else if it.is_crate() {
+ format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate)
+ } else {
+ format!(
+ "API documentation for the Rust `{}` {} in crate `{}`.",
+ it.name.as_ref().unwrap(),
+ tyname,
+ self.shared.layout.krate
+ )
+ };
+ let keywords = make_item_keywords(it);
+ let page = layout::Page {
+ css_class: tyname.as_str(),
+ root_path: &self.root_path(),
+ static_root_path: self.shared.static_root_path.as_deref(),
+ title: &title,
+ description: &desc,
+ keywords: &keywords,
+ resource_suffix: &self.shared.resource_suffix,
+ extra_scripts: &[],
+ static_extra_scripts: &[],
+ };
+
+ {
+ self.id_map.borrow_mut().reset();
+ self.id_map.borrow_mut().populate(&INITIAL_IDS);
+ }
+
+ if !self.render_redirect_pages {
+ layout::render(
+ &self.shared.layout,
+ &page,
+ |buf: &mut _| print_sidebar(self, it, buf),
+ |buf: &mut _| print_item(self, it, buf),
+ &self.shared.style_files,
+ )
+ } else {
+ if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) {
+ let mut path = String::new();
+ for name in &names[..names.len() - 1] {
+ path.push_str(name);
+ path.push('/');
+ }
+ path.push_str(&item_path(ty, names.last().unwrap()));
+ match self.redirections {
+ Some(ref redirections) => {
+ let mut current_path = String::new();
+ for name in &self.current {
+ current_path.push_str(name);
+ current_path.push('/');
+ }
+ current_path.push_str(&item_path(ty, names.last().unwrap()));
+ redirections.borrow_mut().insert(current_path, path);
+ }
+ None => return layout::redirect(&format!("{}{}", self.root_path(), path)),
+ }
+ }
+ String::new()
+ }
+ }
+
+ /// Construct a map of items shown in the sidebar to a plain-text summary of their docs.
+ fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> {
+ // BTreeMap instead of HashMap to get a sorted output
+ let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new();
+ for item in &m.items {
+ if item.is_stripped() {
+ continue;
+ }
+
+ let short = item.type_();
+ let myname = match item.name {
+ None => continue,
+ Some(ref s) => s.to_string(),
+ };
+ let short = short.to_string();
+ map.entry(short).or_default().push((
+ myname,
+ Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))),
+ ));
+ }
+
+ if self.shared.sort_modules_alphabetically {
+ for items in map.values_mut() {
+ items.sort();
+ }
+ }
+ map
+ }
+
+ /// Generates a url appropriate for an `href` attribute back to the source of
+ /// this item.
+ ///
+ /// The url generated, when clicked, will redirect the browser back to the
+ /// original source code.
+ ///
+ /// If `None` is returned, then a source link couldn't be generated. This
+ /// may happen, for example, with externally inlined items where the source
+ /// of their crate documentation isn't known.
+ pub(super) fn src_href(&self, item: &clean::Item) -> Option {
+ if item.source.is_dummy() {
+ return None;
+ }
+ let mut root = self.root_path();
+ let mut path = String::new();
+ let cnum = item.source.cnum(self.sess());
+
+ // We can safely ignore synthetic `SourceFile`s.
+ let file = match item.source.filename(self.sess()) {
+ FileName::Real(ref path) => path.local_path().to_path_buf(),
+ _ => return None,
+ };
+ let file = &file;
+
+ let symbol;
+ let (krate, path) = if cnum == LOCAL_CRATE {
+ if let Some(path) = self.shared.local_sources.get(file) {
+ (self.shared.layout.krate.as_str(), path)
+ } else {
+ return None;
+ }
+ } else {
+ let (krate, src_root) = match *self.cache.extern_locations.get(&cnum)? {
+ (name, ref src, ExternalLocation::Local) => (name, src),
+ (name, ref src, ExternalLocation::Remote(ref s)) => {
+ root = s.to_string();
+ (name, src)
+ }
+ (_, _, ExternalLocation::Unknown) => return None,
+ };
+
+ sources::clean_path(&src_root, file, false, |component| {
+ path.push_str(&component.to_string_lossy());
+ path.push('/');
+ });
+ let mut fname = file.file_name().expect("source has no filename").to_os_string();
+ fname.push(".html");
+ path.push_str(&fname.to_string_lossy());
+ symbol = krate.as_str();
+ (&*symbol, &path)
+ };
+
+ let loline = item.source.lo(self.sess()).line;
+ let hiline = item.source.hi(self.sess()).line;
+ let lines =
+ if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
+ Some(format!(
+ "{root}src/{krate}/{path}#{lines}",
+ root = Escape(&root),
+ krate = krate,
+ path = path,
+ lines = lines
+ ))
+ }
+}
+
+/// Generates the documentation for `crate` into the directory `dst`
+impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
+ fn descr() -> &'static str {
+ "html"
+ }
+
+ fn init(
+ mut krate: clean::Crate,
+ options: RenderOptions,
+ edition: Edition,
+ mut cache: Cache,
+ tcx: TyCtxt<'tcx>,
+ ) -> Result<(Self, clean::Crate), Error> {
+ // need to save a copy of the options for rendering the index page
+ let md_opts = options.clone();
+ let RenderOptions {
+ output,
+ external_html,
+ id_map,
+ playground_url,
+ sort_modules_alphabetically,
+ themes: style_files,
+ default_settings,
+ extension_css,
+ resource_suffix,
+ static_root_path,
+ generate_search_filter,
+ unstable_features,
+ generate_redirect_map,
+ ..
+ } = options;
+
+ let src_root = match krate.src {
+ FileName::Real(ref p) => match p.local_path().parent() {
+ Some(p) => p.to_path_buf(),
+ None => PathBuf::new(),
+ },
+ _ => PathBuf::new(),
+ };
+ // If user passed in `--playground-url` arg, we fill in crate name here
+ let mut playground = None;
+ if let Some(url) = playground_url {
+ playground =
+ Some(markdown::Playground { crate_name: Some(krate.name.to_string()), url });
+ }
+ let mut layout = layout::Layout {
+ logo: String::new(),
+ favicon: String::new(),
+ external_html,
+ default_settings,
+ krate: krate.name.to_string(),
+ css_file_extension: extension_css,
+ generate_search_filter,
+ };
+ let mut issue_tracker_base_url = None;
+ let mut include_sources = true;
+
+ // Crawl the crate attributes looking for attributes which control how we're
+ // going to emit HTML
+ if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
+ for attr in attrs.lists(sym::doc) {
+ match (attr.name_or_empty(), attr.value_str()) {
+ (sym::html_favicon_url, Some(s)) => {
+ layout.favicon = s.to_string();
+ }
+ (sym::html_logo_url, Some(s)) => {
+ layout.logo = s.to_string();
+ }
+ (sym::html_playground_url, Some(s)) => {
+ playground = Some(markdown::Playground {
+ crate_name: Some(krate.name.to_string()),
+ url: s.to_string(),
+ });
+ }
+ (sym::issue_tracker_base_url, Some(s)) => {
+ issue_tracker_base_url = Some(s.to_string());
+ }
+ (sym::html_no_source, None) if attr.is_word() => {
+ include_sources = false;
+ }
+ _ => {}
+ }
+ }
+ }
+ let (sender, receiver) = channel();
+ let mut scx = SharedContext {
+ tcx,
+ collapsed: krate.collapsed,
+ src_root,
+ include_sources,
+ local_sources: Default::default(),
+ issue_tracker_base_url,
+ layout,
+ created_dirs: Default::default(),
+ sort_modules_alphabetically,
+ style_files,
+ resource_suffix,
+ static_root_path,
+ fs: DocFS::new(sender),
+ edition,
+ codes: ErrorCodes::from(unstable_features.is_nightly_build()),
+ playground,
+ };
+
+ // Add the default themes to the `Vec` of stylepaths
+ //
+ // Note that these must be added before `sources::render` is called
+ // so that the resulting source pages are styled
+ //
+ // `light.css` is not disabled because it is the stylesheet that stays loaded
+ // by the browser as the theme stylesheet. The theme system (hackily) works by
+ // changing the href to this stylesheet. All other themes are disabled to
+ // prevent rule conflicts
+ scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
+ scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
+ scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
+
+ let dst = output;
+ scx.ensure_dir(&dst)?;
+ krate = sources::render(&dst, &mut scx, krate)?;
+
+ // Build our search index
+ let index = build_index(&krate, &mut cache, tcx);
+
+ let mut cx = Context {
+ current: Vec::new(),
+ dst,
+ render_redirect_pages: false,
+ id_map: Rc::new(RefCell::new(id_map)),
+ deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
+ shared: Arc::new(scx),
+ all: Rc::new(RefCell::new(AllTypes::new())),
+ errors: Rc::new(receiver),
+ cache: Rc::new(cache),
+ redirections: if generate_redirect_map { Some(Default::default()) } else { None },
+ };
+
+ CURRENT_DEPTH.with(|s| s.set(0));
+
+ // Write shared runs within a flock; disable thread dispatching of IO temporarily.
+ Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
+ write_shared(&cx, &krate, index, &md_opts)?;
+ Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
+ Ok((cx, krate))
+ }
+
+ fn after_krate(
+ &mut self,
+ krate: &clean::Crate,
+ diag: &rustc_errors::Handler,
+ ) -> Result<(), Error> {
+ let final_file = self.dst.join(&*krate.name.as_str()).join("all.html");
+ let settings_file = self.dst.join("settings.html");
+ let crate_name = krate.name;
+
+ let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
+ if !root_path.ends_with('/') {
+ root_path.push('/');
+ }
+ let mut page = layout::Page {
+ title: "List of all items in this crate",
+ css_class: "mod",
+ root_path: "../",
+ static_root_path: self.shared.static_root_path.as_deref(),
+ description: "List of all items in this crate",
+ keywords: BASIC_KEYWORDS,
+ resource_suffix: &self.shared.resource_suffix,
+ extra_scripts: &[],
+ static_extra_scripts: &[],
+ };
+ let sidebar = if let Some(ref version) = self.cache.crate_version {
+ format!(
+ "
Crate {}
\
+ \
+ Back to index
",
+ crate_name,
+ Escape(version),
+ )
+ } else {
+ String::new()
+ };
+ let all = self.all.replace(AllTypes::new());
+ let v = layout::render(
+ &self.shared.layout,
+ &page,
+ sidebar,
+ |buf: &mut Buffer| all.print(buf),
+ &self.shared.style_files,
+ );
+ self.shared.fs.write(&final_file, v.as_bytes())?;
+
+ // Generating settings page.
+ page.title = "Rustdoc settings";
+ page.description = "Settings of Rustdoc";
+ page.root_path = "./";
+
+ let mut style_files = self.shared.style_files.clone();
+ let sidebar = "Settings
";
+ style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
+ let v = layout::render(
+ &self.shared.layout,
+ &page,
+ sidebar,
+ settings(
+ self.shared.static_root_path.as_deref().unwrap_or("./"),
+ &self.shared.resource_suffix,
+ &self.shared.style_files,
+ )?,
+ &style_files,
+ );
+ self.shared.fs.write(&settings_file, v.as_bytes())?;
+ if let Some(redirections) = self.redirections.take() {
+ if !redirections.borrow().is_empty() {
+ let redirect_map_path =
+ self.dst.join(&*krate.name.as_str()).join("redirect-map.json");
+ let paths = serde_json::to_string(&*redirections.borrow()).unwrap();
+ self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?;
+ self.shared.fs.write(&redirect_map_path, paths.as_bytes())?;
+ }
+ }
+
+ // Flush pending errors.
+ Arc::get_mut(&mut self.shared).unwrap().fs.close();
+ let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
+ if nb_errors > 0 {
+ Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
+ } else {
+ Ok(())
+ }
+ }
+
+ fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> {
+ // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
+ // if they contain impls for public types. These modules can also
+ // contain items such as publicly re-exported structures.
+ //
+ // External crates will provide links to these structures, so
+ // these modules are recursed into, but not rendered normally
+ // (a flag on the context).
+ if !self.render_redirect_pages {
+ self.render_redirect_pages = item.is_stripped();
+ }
+ let scx = &self.shared;
+ self.dst.push(item_name);
+ self.current.push(item_name.to_owned());
+
+ info!("Recursing into {}", self.dst.display());
+
+ let buf = self.render_item(item, false);
+ // buf will be empty if the module is stripped and there is no redirect for it
+ if !buf.is_empty() {
+ self.shared.ensure_dir(&self.dst)?;
+ let joint_dst = self.dst.join("index.html");
+ scx.fs.write(&joint_dst, buf.as_bytes())?;
+ }
+
+ // Render sidebar-items.js used throughout this module.
+ if !self.render_redirect_pages {
+ let module = match *item.kind {
+ clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m,
+ _ => unreachable!(),
+ };
+ let items = self.build_sidebar_items(module);
+ let js_dst = self.dst.join("sidebar-items.js");
+ let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap());
+ scx.fs.write(&js_dst, &v)?;
+ }
+ Ok(())
+ }
+
+ fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
+ info!("Recursed; leaving {}", self.dst.display());
+
+ // Go back to where we were at
+ self.dst.pop();
+ self.current.pop();
+ Ok(())
+ }
+
+ fn item(&mut self, item: clean::Item) -> Result<(), Error> {
+ // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
+ // if they contain impls for public types. These modules can also
+ // contain items such as publicly re-exported structures.
+ //
+ // External crates will provide links to these structures, so
+ // these modules are recursed into, but not rendered normally
+ // (a flag on the context).
+ if !self.render_redirect_pages {
+ self.render_redirect_pages = item.is_stripped();
+ }
+
+ let buf = self.render_item(&item, true);
+ // buf will be empty if the item is stripped and there is no redirect for it
+ if !buf.is_empty() {
+ let name = item.name.as_ref().unwrap();
+ let item_type = item.type_();
+ let file_name = &item_path(item_type, &name.as_str());
+ self.shared.ensure_dir(&self.dst)?;
+ let joint_dst = self.dst.join(file_name);
+ self.shared.fs.write(&joint_dst, buf.as_bytes())?;
+
+ if !self.render_redirect_pages {
+ self.all.borrow_mut().append(full_path(self, &item), &item_type);
+ }
+ // If the item is a macro, redirect from the old macro URL (with !)
+ // to the new one (without).
+ if item_type == ItemType::Macro {
+ let redir_name = format!("{}.{}!.html", item_type, name);
+ if let Some(ref redirections) = self.redirections {
+ let crate_name = &self.shared.layout.krate;
+ redirections.borrow_mut().insert(
+ format!("{}/{}", crate_name, redir_name),
+ format!("{}/{}", crate_name, file_name),
+ );
+ } else {
+ let v = layout::redirect(file_name);
+ let redir_dst = self.dst.join(redir_name);
+ self.shared.fs.write(&redir_dst, v.as_bytes())?;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn cache(&self) -> &Cache {
+ &self.cache
+ }
+}
+
+fn make_item_keywords(it: &clean::Item) -> String {
+ format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())
+}
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index fbeeb2a6fa2f2..50cae50c2c3eb 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
//! Rustdoc's HTML rendering module.
//!
//! This modules contains the bulk of the logic necessary for rendering a
@@ -30,58 +28,48 @@ crate mod cache;
#[cfg(test)]
mod tests;
+mod context;
+mod print_item;
+mod write_shared;
+
+crate use context::*;
+
use std::cell::{Cell, RefCell};
-use std::cmp::Ordering;
-use std::collections::{BTreeMap, VecDeque};
+use std::collections::VecDeque;
use std::default::Default;
-use std::ffi::OsStr;
-use std::fmt::{self, Write};
-use std::fs::{self, File};
-use std::io::prelude::*;
-use std::io::{self, BufReader};
-use std::path::{Component, Path, PathBuf};
-use std::rc::Rc;
+use std::fmt;
+use std::path::{Path, PathBuf};
use std::str;
use std::string::ToString;
-use std::sync::mpsc::{channel, Receiver};
-use std::sync::Arc;
use itertools::Itertools;
use rustc_ast_pretty::pprust;
use rustc_attr::{Deprecation, StabilityLevel};
-use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::DefId;
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
use rustc_middle::ty::TyCtxt;
-use rustc_session::Session;
use rustc_span::edition::Edition;
-use rustc_span::hygiene::MacroKind;
-use rustc_span::source_map::FileName;
use rustc_span::symbol::{kw, sym, Symbol};
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
-use crate::clean::{self, AttributesExt, GetDefId, RenderedLink, SelfTy, TypeKind};
-use crate::config::RenderOptions;
+use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
use crate::html::escape::Escape;
-use crate::html::format::Function;
-use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause};
-use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace};
-use crate::html::markdown::{
- self, plain_text_summary, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine,
+use crate::html::format::{
+ href, print_abi_with_space, print_default_space, print_generic_bounds, Buffer, Function,
+ PrintWithSpace, WhereClause,
};
-use crate::html::sources;
-use crate::html::{highlight, layout, static_files};
-use cache::{build_index, ExternalLocation};
+use crate::html::layout;
+use crate::html::markdown::{self, ErrorCodes, Markdown, MarkdownHtml, MarkdownSummaryLine};
/// A pair of name and its optional document.
crate type NameDoc = (String, Option);
@@ -92,42 +80,6 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
})
}
-/// Major driving force in all rustdoc rendering. This contains information
-/// about where in the tree-like hierarchy rendering is occurring and controls
-/// how the current page is being rendered.
-///
-/// It is intended that this context is a lightweight object which can be fairly
-/// easily cloned because it is cloned per work-job (about once per item in the
-/// rustdoc tree).
-#[derive(Clone)]
-crate struct Context<'tcx> {
- /// Current hierarchy of components leading down to what's currently being
- /// rendered
- crate current: Vec,
- /// The current destination folder of where HTML artifacts should be placed.
- /// This changes as the context descends into the module hierarchy.
- crate dst: PathBuf,
- /// A flag, which when `true`, will render pages which redirect to the
- /// real location of an item. This is used to allow external links to
- /// publicly reused items to redirect to the right location.
- crate render_redirect_pages: bool,
- /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
- /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
- /// the crate.
- crate redirections: Option>>>,
- /// The map used to ensure all generated 'id=' attributes are unique.
- id_map: Rc>,
- /// Tracks section IDs for `Deref` targets so they match in both the main
- /// body and the sidebar.
- deref_id_map: Rc>>,
- crate shared: Arc>,
- all: Rc>,
- /// Storage for the errors produced while generating documentation so they
- /// can be printed together at the end.
- crate errors: Rc>,
- crate cache: Rc,
-}
-
crate struct SharedContext<'tcx> {
crate tcx: TyCtxt<'tcx>,
/// The path to the crate root source minus the file name.
@@ -169,27 +121,6 @@ crate struct SharedContext<'tcx> {
playground: Option,
}
-impl<'tcx> Context<'tcx> {
- fn path(&self, filename: &str) -> PathBuf {
- // We use splitn vs Path::extension here because we might get a filename
- // like `style.min.css` and we want to process that into
- // `style-suffix.min.css`. Path::extension would just return `css`
- // which would result in `style.min-suffix.css` which isn't what we
- // want.
- let (base, ext) = filename.split_once('.').unwrap();
- let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext);
- self.dst.join(&filename)
- }
-
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.shared.tcx
- }
-
- fn sess(&self) -> &'tcx Session {
- &self.shared.tcx.sess
- }
-}
-
impl SharedContext<'_> {
crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
let mut dirs = self.created_dirs.borrow_mut();
@@ -380,860 +311,6 @@ crate const INITIAL_IDS: [&'static str; 15] = [
"implementations",
];
-/// Generates the documentation for `crate` into the directory `dst`
-impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
- fn descr() -> &'static str {
- "html"
- }
-
- fn init(
- mut krate: clean::Crate,
- options: RenderOptions,
- edition: Edition,
- mut cache: Cache,
- tcx: TyCtxt<'tcx>,
- ) -> Result<(Self, clean::Crate), Error> {
- // need to save a copy of the options for rendering the index page
- let md_opts = options.clone();
- let RenderOptions {
- output,
- external_html,
- id_map,
- playground_url,
- sort_modules_alphabetically,
- themes: style_files,
- default_settings,
- extension_css,
- resource_suffix,
- static_root_path,
- generate_search_filter,
- unstable_features,
- generate_redirect_map,
- ..
- } = options;
-
- let src_root = match krate.src {
- FileName::Real(ref p) => match p.local_path().parent() {
- Some(p) => p.to_path_buf(),
- None => PathBuf::new(),
- },
- _ => PathBuf::new(),
- };
- // If user passed in `--playground-url` arg, we fill in crate name here
- let mut playground = None;
- if let Some(url) = playground_url {
- playground =
- Some(markdown::Playground { crate_name: Some(krate.name.to_string()), url });
- }
- let mut layout = layout::Layout {
- logo: String::new(),
- favicon: String::new(),
- external_html,
- default_settings,
- krate: krate.name.to_string(),
- css_file_extension: extension_css,
- generate_search_filter,
- };
- let mut issue_tracker_base_url = None;
- let mut include_sources = true;
-
- // Crawl the crate attributes looking for attributes which control how we're
- // going to emit HTML
- if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
- for attr in attrs.lists(sym::doc) {
- match (attr.name_or_empty(), attr.value_str()) {
- (sym::html_favicon_url, Some(s)) => {
- layout.favicon = s.to_string();
- }
- (sym::html_logo_url, Some(s)) => {
- layout.logo = s.to_string();
- }
- (sym::html_playground_url, Some(s)) => {
- playground = Some(markdown::Playground {
- crate_name: Some(krate.name.to_string()),
- url: s.to_string(),
- });
- }
- (sym::issue_tracker_base_url, Some(s)) => {
- issue_tracker_base_url = Some(s.to_string());
- }
- (sym::html_no_source, None) if attr.is_word() => {
- include_sources = false;
- }
- _ => {}
- }
- }
- }
- let (sender, receiver) = channel();
- let mut scx = SharedContext {
- tcx,
- collapsed: krate.collapsed,
- src_root,
- include_sources,
- local_sources: Default::default(),
- issue_tracker_base_url,
- layout,
- created_dirs: Default::default(),
- sort_modules_alphabetically,
- style_files,
- resource_suffix,
- static_root_path,
- fs: DocFS::new(sender),
- edition,
- codes: ErrorCodes::from(unstable_features.is_nightly_build()),
- playground,
- };
-
- // Add the default themes to the `Vec` of stylepaths
- //
- // Note that these must be added before `sources::render` is called
- // so that the resulting source pages are styled
- //
- // `light.css` is not disabled because it is the stylesheet that stays loaded
- // by the browser as the theme stylesheet. The theme system (hackily) works by
- // changing the href to this stylesheet. All other themes are disabled to
- // prevent rule conflicts
- scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
- scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
- scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
-
- let dst = output;
- scx.ensure_dir(&dst)?;
- krate = sources::render(&dst, &mut scx, krate)?;
-
- // Build our search index
- let index = build_index(&krate, &mut cache, tcx);
-
- let mut cx = Context {
- current: Vec::new(),
- dst,
- render_redirect_pages: false,
- id_map: Rc::new(RefCell::new(id_map)),
- deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
- shared: Arc::new(scx),
- all: Rc::new(RefCell::new(AllTypes::new())),
- errors: Rc::new(receiver),
- cache: Rc::new(cache),
- redirections: if generate_redirect_map { Some(Default::default()) } else { None },
- };
-
- CURRENT_DEPTH.with(|s| s.set(0));
-
- // Write shared runs within a flock; disable thread dispatching of IO temporarily.
- Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
- write_shared(&cx, &krate, index, &md_opts)?;
- Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
- Ok((cx, krate))
- }
-
- fn after_krate(
- &mut self,
- krate: &clean::Crate,
- diag: &rustc_errors::Handler,
- ) -> Result<(), Error> {
- let final_file = self.dst.join(&*krate.name.as_str()).join("all.html");
- let settings_file = self.dst.join("settings.html");
- let crate_name = krate.name;
-
- let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
- if !root_path.ends_with('/') {
- root_path.push('/');
- }
- let mut page = layout::Page {
- title: "List of all items in this crate",
- css_class: "mod",
- root_path: "../",
- static_root_path: self.shared.static_root_path.as_deref(),
- description: "List of all items in this crate",
- keywords: BASIC_KEYWORDS,
- resource_suffix: &self.shared.resource_suffix,
- extra_scripts: &[],
- static_extra_scripts: &[],
- };
- let sidebar = if let Some(ref version) = self.cache.crate_version {
- format!(
- "Crate {}
\
- \
- Back to index
",
- crate_name,
- Escape(version),
- )
- } else {
- String::new()
- };
- let all = self.all.replace(AllTypes::new());
- let v = layout::render(
- &self.shared.layout,
- &page,
- sidebar,
- |buf: &mut Buffer| all.print(buf),
- &self.shared.style_files,
- );
- self.shared.fs.write(&final_file, v.as_bytes())?;
-
- // Generating settings page.
- page.title = "Rustdoc settings";
- page.description = "Settings of Rustdoc";
- page.root_path = "./";
-
- let mut style_files = self.shared.style_files.clone();
- let sidebar = "Settings
";
- style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
- let v = layout::render(
- &self.shared.layout,
- &page,
- sidebar,
- settings(
- self.shared.static_root_path.as_deref().unwrap_or("./"),
- &self.shared.resource_suffix,
- &self.shared.style_files,
- )?,
- &style_files,
- );
- self.shared.fs.write(&settings_file, v.as_bytes())?;
- if let Some(redirections) = self.redirections.take() {
- if !redirections.borrow().is_empty() {
- let redirect_map_path =
- self.dst.join(&*krate.name.as_str()).join("redirect-map.json");
- let paths = serde_json::to_string(&*redirections.borrow()).unwrap();
- self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?;
- self.shared.fs.write(&redirect_map_path, paths.as_bytes())?;
- }
- }
-
- // Flush pending errors.
- Arc::get_mut(&mut self.shared).unwrap().fs.close();
- let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
- if nb_errors > 0 {
- Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
- } else {
- Ok(())
- }
- }
-
- fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error> {
- // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
- // if they contain impls for public types. These modules can also
- // contain items such as publicly re-exported structures.
- //
- // External crates will provide links to these structures, so
- // these modules are recursed into, but not rendered normally
- // (a flag on the context).
- if !self.render_redirect_pages {
- self.render_redirect_pages = item.is_stripped();
- }
- let scx = &self.shared;
- self.dst.push(item_name);
- self.current.push(item_name.to_owned());
-
- info!("Recursing into {}", self.dst.display());
-
- let buf = self.render_item(item, false);
- // buf will be empty if the module is stripped and there is no redirect for it
- if !buf.is_empty() {
- self.shared.ensure_dir(&self.dst)?;
- let joint_dst = self.dst.join("index.html");
- scx.fs.write(&joint_dst, buf.as_bytes())?;
- }
-
- // Render sidebar-items.js used throughout this module.
- if !self.render_redirect_pages {
- let module = match *item.kind {
- clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m,
- _ => unreachable!(),
- };
- let items = self.build_sidebar_items(module);
- let js_dst = self.dst.join("sidebar-items.js");
- let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap());
- scx.fs.write(&js_dst, &v)?;
- }
- Ok(())
- }
-
- fn mod_item_out(&mut self, _item_name: &str) -> Result<(), Error> {
- info!("Recursed; leaving {}", self.dst.display());
-
- // Go back to where we were at
- self.dst.pop();
- self.current.pop();
- Ok(())
- }
-
- fn item(&mut self, item: clean::Item) -> Result<(), Error> {
- // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
- // if they contain impls for public types. These modules can also
- // contain items such as publicly re-exported structures.
- //
- // External crates will provide links to these structures, so
- // these modules are recursed into, but not rendered normally
- // (a flag on the context).
- if !self.render_redirect_pages {
- self.render_redirect_pages = item.is_stripped();
- }
-
- let buf = self.render_item(&item, true);
- // buf will be empty if the item is stripped and there is no redirect for it
- if !buf.is_empty() {
- let name = item.name.as_ref().unwrap();
- let item_type = item.type_();
- let file_name = &item_path(item_type, &name.as_str());
- self.shared.ensure_dir(&self.dst)?;
- let joint_dst = self.dst.join(file_name);
- self.shared.fs.write(&joint_dst, buf.as_bytes())?;
-
- if !self.render_redirect_pages {
- self.all.borrow_mut().append(full_path(self, &item), &item_type);
- }
- // If the item is a macro, redirect from the old macro URL (with !)
- // to the new one (without).
- if item_type == ItemType::Macro {
- let redir_name = format!("{}.{}!.html", item_type, name);
- if let Some(ref redirections) = self.redirections {
- let crate_name = &self.shared.layout.krate;
- redirections.borrow_mut().insert(
- format!("{}/{}", crate_name, redir_name),
- format!("{}/{}", crate_name, file_name),
- );
- } else {
- let v = layout::redirect(file_name);
- let redir_dst = self.dst.join(redir_name);
- self.shared.fs.write(&redir_dst, v.as_bytes())?;
- }
- }
- }
- Ok(())
- }
-
- fn cache(&self) -> &Cache {
- &self.cache
- }
-}
-
-fn write_shared(
- cx: &Context<'_>,
- krate: &clean::Crate,
- search_index: String,
- options: &RenderOptions,
-) -> Result<(), Error> {
- // Write out the shared files. Note that these are shared among all rustdoc
- // docs placed in the output directory, so this needs to be a synchronized
- // operation with respect to all other rustdocs running around.
- let lock_file = cx.dst.join(".lock");
- let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
-
- // Add all the static files. These may already exist, but we just
- // overwrite them anyway to make sure that they're fresh and up-to-date.
-
- write_minify(
- &cx.shared.fs,
- cx.path("rustdoc.css"),
- static_files::RUSTDOC_CSS,
- options.enable_minification,
- )?;
- write_minify(
- &cx.shared.fs,
- cx.path("settings.css"),
- static_files::SETTINGS_CSS,
- options.enable_minification,
- )?;
- write_minify(
- &cx.shared.fs,
- cx.path("noscript.css"),
- static_files::NOSCRIPT_CSS,
- options.enable_minification,
- )?;
-
- // To avoid "light.css" to be overwritten, we'll first run over the received themes and only
- // then we'll run over the "official" styles.
- let mut themes: FxHashSet = FxHashSet::default();
-
- for entry in &cx.shared.style_files {
- let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
- let extension =
- try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
-
- // Handle the official themes
- match theme {
- "light" => write_minify(
- &cx.shared.fs,
- cx.path("light.css"),
- static_files::themes::LIGHT,
- options.enable_minification,
- )?,
- "dark" => write_minify(
- &cx.shared.fs,
- cx.path("dark.css"),
- static_files::themes::DARK,
- options.enable_minification,
- )?,
- "ayu" => write_minify(
- &cx.shared.fs,
- cx.path("ayu.css"),
- static_files::themes::AYU,
- options.enable_minification,
- )?,
- _ => {
- // Handle added third-party themes
- let content = try_err!(fs::read(&entry.path), &entry.path);
- cx.shared
- .fs
- .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
- }
- };
-
- themes.insert(theme.to_owned());
- }
-
- let write = |p, c| cx.shared.fs.write(p, c);
- if (*cx.shared).layout.logo.is_empty() {
- write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?;
- }
- if (*cx.shared).layout.favicon.is_empty() {
- write(cx.path("favicon.svg"), static_files::RUST_FAVICON_SVG)?;
- write(cx.path("favicon-16x16.png"), static_files::RUST_FAVICON_PNG_16)?;
- write(cx.path("favicon-32x32.png"), static_files::RUST_FAVICON_PNG_32)?;
- }
- write(cx.path("brush.svg"), static_files::BRUSH_SVG)?;
- write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?;
- write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?;
-
- let mut themes: Vec<&String> = themes.iter().collect();
- themes.sort();
- // To avoid theme switch latencies as much as possible, we put everything theme related
- // at the beginning of the html files into another js file.
- let theme_js = format!(
- r#"var themes = document.getElementById("theme-choices");
-var themePicker = document.getElementById("theme-picker");
-
-function showThemeButtonState() {{
- themes.style.display = "block";
- themePicker.style.borderBottomRightRadius = "0";
- themePicker.style.borderBottomLeftRadius = "0";
-}}
-
-function hideThemeButtonState() {{
- themes.style.display = "none";
- themePicker.style.borderBottomRightRadius = "3px";
- themePicker.style.borderBottomLeftRadius = "3px";
-}}
-
-function switchThemeButtonState() {{
- if (themes.style.display === "block") {{
- hideThemeButtonState();
- }} else {{
- showThemeButtonState();
- }}
-}};
-
-function handleThemeButtonsBlur(e) {{
- var active = document.activeElement;
- var related = e.relatedTarget;
-
- if (active.id !== "theme-picker" &&
- (!active.parentNode || active.parentNode.id !== "theme-choices") &&
- (!related ||
- (related.id !== "theme-picker" &&
- (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{
- hideThemeButtonState();
- }}
-}}
-
-themePicker.onclick = switchThemeButtonState;
-themePicker.onblur = handleThemeButtonsBlur;
-{}.forEach(function(item) {{
- var but = document.createElement("button");
- but.textContent = item;
- but.onclick = function(el) {{
- switchTheme(currentTheme, mainTheme, item, true);
- useSystemTheme(false);
- }};
- but.onblur = handleThemeButtonsBlur;
- themes.appendChild(but);
-}});"#,
- serde_json::to_string(&themes).unwrap()
- );
-
- write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?;
- write_minify(
- &cx.shared.fs,
- cx.path("main.js"),
- static_files::MAIN_JS,
- options.enable_minification,
- )?;
- write_minify(
- &cx.shared.fs,
- cx.path("settings.js"),
- static_files::SETTINGS_JS,
- options.enable_minification,
- )?;
- if cx.shared.include_sources {
- write_minify(
- &cx.shared.fs,
- cx.path("source-script.js"),
- static_files::sidebar::SOURCE_SCRIPT,
- options.enable_minification,
- )?;
- }
-
- {
- write_minify(
- &cx.shared.fs,
- cx.path("storage.js"),
- &format!(
- "var resourcesSuffix = \"{}\";{}",
- cx.shared.resource_suffix,
- static_files::STORAGE_JS
- ),
- options.enable_minification,
- )?;
- }
-
- if let Some(ref css) = cx.shared.layout.css_file_extension {
- let out = cx.path("theme.css");
- let buffer = try_err!(fs::read_to_string(css), css);
- if !options.enable_minification {
- cx.shared.fs.write(&out, &buffer)?;
- } else {
- write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?;
- }
- }
- write_minify(
- &cx.shared.fs,
- cx.path("normalize.css"),
- static_files::NORMALIZE_CSS,
- options.enable_minification,
- )?;
- write(cx.dst.join("FiraSans-Regular.woff2"), static_files::fira_sans::REGULAR2)?;
- write(cx.dst.join("FiraSans-Medium.woff2"), static_files::fira_sans::MEDIUM2)?;
- write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?;
- write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?;
- write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?;
- write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?;
- write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?;
- write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?;
- write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?;
- write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?;
- write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?;
- write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?;
- write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?;
- write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?;
- write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?;
-
- fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> {
- let mut ret = Vec::new();
- let mut krates = Vec::new();
-
- if path.exists() {
- let prefix = format!(r#"{}["{}"]"#, key, krate);
- for line in BufReader::new(File::open(path)?).lines() {
- let line = line?;
- if !line.starts_with(key) {
- continue;
- }
- if line.starts_with(&prefix) {
- continue;
- }
- ret.push(line.to_string());
- krates.push(
- line[key.len() + 2..]
- .split('"')
- .next()
- .map(|s| s.to_owned())
- .unwrap_or_else(String::new),
- );
- }
- }
- Ok((ret, krates))
- }
-
- fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> {
- let mut ret = Vec::new();
- let mut krates = Vec::new();
-
- if path.exists() {
- let prefix = format!("\"{}\"", krate);
- for line in BufReader::new(File::open(path)?).lines() {
- let line = line?;
- if !line.starts_with('"') {
- continue;
- }
- if line.starts_with(&prefix) {
- continue;
- }
- if line.ends_with(",\\") {
- ret.push(line[..line.len() - 2].to_string());
- } else {
- // Ends with "\\" (it's the case for the last added crate line)
- ret.push(line[..line.len() - 1].to_string());
- }
- krates.push(
- line.split('"')
- .find(|s| !s.is_empty())
- .map(|s| s.to_owned())
- .unwrap_or_else(String::new),
- );
- }
- }
- Ok((ret, krates))
- }
-
- use std::ffi::OsString;
-
- #[derive(Debug)]
- struct Hierarchy {
- elem: OsString,
- children: FxHashMap,
- elems: FxHashSet,
- }
-
- impl Hierarchy {
- fn new(elem: OsString) -> Hierarchy {
- Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() }
- }
-
- fn to_json_string(&self) -> String {
- let mut subs: Vec<&Hierarchy> = self.children.values().collect();
- subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem));
- let mut files = self
- .elems
- .iter()
- .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion")))
- .collect::>();
- files.sort_unstable();
- let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(",");
- let dirs =
- if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) };
- let files = files.join(",");
- let files =
- if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) };
- format!(
- "{{\"name\":\"{name}\"{dirs}{files}}}",
- name = self.elem.to_str().expect("invalid osstring conversion"),
- dirs = dirs,
- files = files
- )
- }
- }
-
- if cx.shared.include_sources {
- let mut hierarchy = Hierarchy::new(OsString::new());
- for source in cx
- .shared
- .local_sources
- .iter()
- .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
- {
- let mut h = &mut hierarchy;
- let mut elems = source
- .components()
- .filter_map(|s| match s {
- Component::Normal(s) => Some(s.to_owned()),
- _ => None,
- })
- .peekable();
- loop {
- let cur_elem = elems.next().expect("empty file path");
- if elems.peek().is_none() {
- h.elems.insert(cur_elem);
- break;
- } else {
- let e = cur_elem.clone();
- h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
- }
- }
- }
-
- let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix));
- let (mut all_sources, _krates) =
- try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst);
- all_sources.push(format!(
- "sourcesIndex[\"{}\"] = {};",
- &krate.name,
- hierarchy.to_json_string()
- ));
- all_sources.sort();
- let v = format!(
- "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n",
- all_sources.join("\n")
- );
- cx.shared.fs.write(&dst, v.as_bytes())?;
- }
-
- // Update the search index and crate list.
- let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix));
- let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst);
- all_indexes.push(search_index);
- krates.push(krate.name.to_string());
- krates.sort();
-
- // Sort the indexes by crate so the file will be generated identically even
- // with rustdoc running in parallel.
- all_indexes.sort();
- {
- let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
- v.push_str(&all_indexes.join(",\\\n"));
- v.push_str("\\\n}');\ninitSearch(searchIndex);");
- cx.shared.fs.write(&dst, &v)?;
- }
-
- let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix));
- let crate_list =
- format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(","));
- cx.shared.fs.write(&crate_list_dst, &crate_list)?;
-
- if options.enable_index_page {
- if let Some(index_page) = options.index_page.clone() {
- let mut md_opts = options.clone();
- md_opts.output = cx.dst.clone();
- md_opts.external_html = (*cx.shared).layout.external_html.clone();
-
- crate::markdown::render(&index_page, md_opts, cx.shared.edition)
- .map_err(|e| Error::new(e, &index_page))?;
- } else {
- let dst = cx.dst.join("index.html");
- let page = layout::Page {
- title: "Index of crates",
- css_class: "mod",
- root_path: "./",
- static_root_path: cx.shared.static_root_path.as_deref(),
- description: "List of crates",
- keywords: BASIC_KEYWORDS,
- resource_suffix: &cx.shared.resource_suffix,
- extra_scripts: &[],
- static_extra_scripts: &[],
- };
-
- let content = format!(
- "\
- List of all crates\
-
",
- krates
- .iter()
- .map(|s| {
- format!(
- "{}",
- ensure_trailing_slash(s),
- s
- )
- })
- .collect::()
- );
- let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
- cx.shared.fs.write(&dst, v.as_bytes())?;
- }
- }
-
- // Update the list of all implementors for traits
- let dst = cx.dst.join("implementors");
- for (&did, imps) in &cx.cache.implementors {
- // Private modules can leak through to this phase of rustdoc, which
- // could contain implementations for otherwise private types. In some
- // rare cases we could find an implementation for an item which wasn't
- // indexed, so we just skip this step in that case.
- //
- // FIXME: this is a vague explanation for why this can't be a `get`, in
- // theory it should be...
- let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) {
- Some(p) => p,
- None => match cx.cache.external_paths.get(&did) {
- Some(p) => p,
- None => continue,
- },
- };
-
- #[derive(Serialize)]
- struct Implementor {
- text: String,
- synthetic: bool,
- types: Vec,
- }
-
- let implementors = imps
- .iter()
- .filter_map(|imp| {
- // If the trait and implementation are in the same crate, then
- // there's no need to emit information about it (there's inlining
- // going on). If they're in different crates then the crate defining
- // the trait will be interested in our implementation.
- //
- // If the implementation is from another crate then that crate
- // should add it.
- if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() {
- None
- } else {
- Some(Implementor {
- text: imp.inner_impl().print(cx.cache(), false).to_string(),
- synthetic: imp.inner_impl().synthetic,
- types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()),
- })
- }
- })
- .collect::>();
-
- // 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() && !cx.cache.paths.contains_key(&did) {
- continue;
- }
-
- let implementors = format!(
- r#"implementors["{}"] = {};"#,
- krate.name,
- serde_json::to_string(&implementors).unwrap()
- );
-
- let mut mydst = dst.clone();
- for part in &remote_path[..remote_path.len() - 1] {
- mydst.push(part);
- }
- cx.shared.ensure_dir(&mydst)?;
- mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));
-
- let (mut all_implementors, _) =
- try_err!(collect(&mydst, &krate.name.as_str(), "implementors"), &mydst);
- all_implementors.push(implementors);
- // Sort the implementors by crate so the file will be generated
- // identically even with rustdoc running in parallel.
- all_implementors.sort();
-
- let mut v = String::from("(function() {var implementors = {};\n");
- for implementor in &all_implementors {
- writeln!(v, "{}", *implementor).unwrap();
- }
- v.push_str(
- "if (window.register_implementors) {\
- window.register_implementors(implementors);\
- } else {\
- window.pending_implementors = implementors;\
- }",
- );
- v.push_str("})()");
- cx.shared.fs.write(&mydst, &v)?;
- }
- Ok(())
-}
-
-fn write_minify(
- fs: &DocFS,
- dst: PathBuf,
- contents: &str,
- enable_minification: bool,
-) -> Result<(), Error> {
- if enable_minification {
- if dst.extension() == Some(&OsStr::new("css")) {
- let res = try_none!(minifier::css::minify(contents).ok(), &dst);
- fs.write(dst, res.as_bytes())
- } else {
- fs.write(dst, minifier::js::minify(contents).as_bytes())
- }
- } else {
- fs.write(dst, contents.as_bytes())
- }
-}
-
fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
if let Some(l) = cx.src_href(item) {
write!(buf, "[src]", l)
@@ -1541,390 +618,68 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result {
- fn derive_id(&self, id: String) -> String {
- let mut map = self.id_map.borrow_mut();
- map.derive(id)
- }
-
- /// String representation of how to get back to the root path of the 'doc/'
- /// folder in terms of a relative URL.
- fn root_path(&self) -> String {
- "../".repeat(self.current.len())
+fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>) {
+ if let Some(ref name) = item.name {
+ info!("Documenting {}", name);
}
+ document_item_info(w, cx, item, false, parent);
+ document_full(w, item, cx, "", false);
+}
- fn render_item(&self, it: &clean::Item, pushname: bool) -> String {
- // A little unfortunate that this is done like this, but it sure
- // does make formatting *a lot* nicer.
- CURRENT_DEPTH.with(|slot| {
- slot.set(self.current.len());
- });
+/// Render md_text as markdown.
+fn render_markdown(
+ w: &mut Buffer,
+ cx: &Context<'_>,
+ md_text: &str,
+ links: Vec,
+ prefix: &str,
+ is_hidden: bool,
+) {
+ let mut ids = cx.id_map.borrow_mut();
+ write!(
+ w,
+ "{}{}
",
+ if is_hidden { " hidden" } else { "" },
+ prefix,
+ Markdown(
+ md_text,
+ &links,
+ &mut ids,
+ cx.shared.codes,
+ cx.shared.edition,
+ &cx.shared.playground
+ )
+ .into_string()
+ )
+}
- let mut title = if it.is_primitive() || it.is_keyword() {
- // No need to include the namespace for primitive types and keywords
- String::new()
- } else {
- self.current.join("::")
- };
- if pushname {
- if !title.is_empty() {
- title.push_str("::");
- }
- title.push_str(&it.name.unwrap().as_str());
- }
- title.push_str(" - Rust");
- let tyname = it.type_();
- let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(&doc));
- let desc = if let Some(desc) = desc {
- desc
- } else if it.is_crate() {
- format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate)
- } else {
- format!(
- "API documentation for the Rust `{}` {} in crate `{}`.",
- it.name.as_ref().unwrap(),
- tyname,
- self.shared.layout.krate
- )
- };
- let keywords = make_item_keywords(it);
- let page = layout::Page {
- css_class: tyname.as_str(),
- root_path: &self.root_path(),
- static_root_path: self.shared.static_root_path.as_deref(),
- title: &title,
- description: &desc,
- keywords: &keywords,
- resource_suffix: &self.shared.resource_suffix,
- extra_scripts: &[],
- static_extra_scripts: &[],
- };
+/// Writes a documentation block containing only the first paragraph of the documentation. If the
+/// docs are longer, a "Read more" link is appended to the end.
+fn document_short(
+ w: &mut Buffer,
+ item: &clean::Item,
+ cx: &Context<'_>,
+ link: AssocItemLink<'_>,
+ prefix: &str,
+ is_hidden: bool,
+ parent: Option<&clean::Item>,
+ show_def_docs: bool,
+) {
+ document_item_info(w, cx, item, is_hidden, parent);
+ if !show_def_docs {
+ return;
+ }
+ if let Some(s) = item.doc_value() {
+ let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string();
- {
- self.id_map.borrow_mut().reset();
- self.id_map.borrow_mut().populate(&INITIAL_IDS);
- }
+ if s.contains('\n') {
+ let link =
+ format!(r#" Read more"#, naive_assoc_href(item, link, cx.cache()));
- if !self.render_redirect_pages {
- layout::render(
- &self.shared.layout,
- &page,
- |buf: &mut _| print_sidebar(self, it, buf),
- |buf: &mut _| print_item(self, it, buf),
- &self.shared.style_files,
- )
- } else {
- if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) {
- let mut path = String::new();
- for name in &names[..names.len() - 1] {
- path.push_str(name);
- path.push('/');
- }
- path.push_str(&item_path(ty, names.last().unwrap()));
- match self.redirections {
- Some(ref redirections) => {
- let mut current_path = String::new();
- for name in &self.current {
- current_path.push_str(name);
- current_path.push('/');
- }
- current_path.push_str(&item_path(ty, names.last().unwrap()));
- redirections.borrow_mut().insert(current_path, path);
- }
- None => return layout::redirect(&format!("{}{}", self.root_path(), path)),
- }
- }
- String::new()
- }
- }
-
- /// Construct a map of items shown in the sidebar to a plain-text summary of their docs.
- fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap> {
- // BTreeMap instead of HashMap to get a sorted output
- let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new();
- for item in &m.items {
- if item.is_stripped() {
- continue;
- }
-
- let short = item.type_();
- let myname = match item.name {
- None => continue,
- Some(ref s) => s.to_string(),
- };
- let short = short.to_string();
- map.entry(short).or_default().push((
- myname,
- Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))),
- ));
- }
-
- if self.shared.sort_modules_alphabetically {
- for items in map.values_mut() {
- items.sort();
- }
- }
- map
- }
-
- /// Generates a url appropriate for an `href` attribute back to the source of
- /// this item.
- ///
- /// The url generated, when clicked, will redirect the browser back to the
- /// original source code.
- ///
- /// If `None` is returned, then a source link couldn't be generated. This
- /// may happen, for example, with externally inlined items where the source
- /// of their crate documentation isn't known.
- fn src_href(&self, item: &clean::Item) -> Option {
- if item.source.is_dummy() {
- return None;
- }
- let mut root = self.root_path();
- let mut path = String::new();
- let cnum = item.source.cnum(self.sess());
-
- // We can safely ignore synthetic `SourceFile`s.
- let file = match item.source.filename(self.sess()) {
- FileName::Real(ref path) => path.local_path().to_path_buf(),
- _ => return None,
- };
- let file = &file;
-
- let symbol;
- let (krate, path) = if cnum == LOCAL_CRATE {
- if let Some(path) = self.shared.local_sources.get(file) {
- (self.shared.layout.krate.as_str(), path)
- } else {
- return None;
- }
- } else {
- let (krate, src_root) = match *self.cache.extern_locations.get(&cnum)? {
- (name, ref src, ExternalLocation::Local) => (name, src),
- (name, ref src, ExternalLocation::Remote(ref s)) => {
- root = s.to_string();
- (name, src)
- }
- (_, _, ExternalLocation::Unknown) => return None,
- };
-
- sources::clean_path(&src_root, file, false, |component| {
- path.push_str(&component.to_string_lossy());
- path.push('/');
- });
- let mut fname = file.file_name().expect("source has no filename").to_os_string();
- fname.push(".html");
- path.push_str(&fname.to_string_lossy());
- symbol = krate.as_str();
- (&*symbol, &path)
- };
-
- let loline = item.source.lo(self.sess()).line;
- let hiline = item.source.hi(self.sess()).line;
- let lines =
- if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
- Some(format!(
- "{root}src/{krate}/{path}#{lines}",
- root = Escape(&root),
- krate = krate,
- path = path,
- lines = lines
- ))
- }
-}
-
-fn wrap_into_docblock(w: &mut Buffer, f: F)
-where
- F: FnOnce(&mut Buffer),
-{
- w.write_str("");
- f(w);
- w.write_str("
")
-}
-
-fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
- debug_assert!(!item.is_stripped());
- // Write the breadcrumb trail header for the top
- buf.write_str("");
- let name = match *item.kind {
- clean::ModuleItem(ref m) => {
- if m.is_crate {
- "Crate "
- } else {
- "Module "
- }
- }
- clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
- clean::TraitItem(..) => "Trait ",
- clean::StructItem(..) => "Struct ",
- clean::UnionItem(..) => "Union ",
- clean::EnumItem(..) => "Enum ",
- clean::TypedefItem(..) => "Type Definition ",
- clean::MacroItem(..) => "Macro ",
- clean::ProcMacroItem(ref mac) => match mac.kind {
- MacroKind::Bang => "Macro ",
- MacroKind::Attr => "Attribute Macro ",
- MacroKind::Derive => "Derive Macro ",
- },
- clean::PrimitiveItem(..) => "Primitive Type ",
- clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
- clean::ConstantItem(..) => "Constant ",
- clean::ForeignTypeItem => "Foreign Type ",
- clean::KeywordItem(..) => "Keyword ",
- clean::OpaqueTyItem(..) => "Opaque Type ",
- clean::TraitAliasItem(..) => "Trait Alias ",
- _ => {
- // We don't generate pages for any other type.
- unreachable!();
- }
- };
- buf.write_str(name);
- if !item.is_primitive() && !item.is_keyword() {
- let cur = &cx.current;
- let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
- for (i, component) in cur.iter().enumerate().take(amt) {
- write!(
- buf,
- "{}::",
- "../".repeat(cur.len() - i - 1),
- component
- );
- }
- }
- write!(buf, "{}", item.type_(), item.name.as_ref().unwrap());
-
- buf.write_str(""); // in-band
- buf.write_str("");
- render_stability_since_raw(
- buf,
- item.stable_since(cx.tcx()).as_deref(),
- item.const_stable_since(cx.tcx()).as_deref(),
- None,
- None,
- );
- buf.write_str(
- "\
- \
- [−]\
- \
- ",
- );
-
- // Write `src` tag
- //
- // When this item is part of a `crate use` in a downstream crate, the
- // [src] link in the downstream documentation will actually come back to
- // this page, and this link will be auto-clicked. The `id` attribute is
- // used to find the link to auto-click.
- if cx.shared.include_sources && !item.is_primitive() {
- write_srclink(cx, item, buf);
- }
-
- buf.write_str("
"); // out-of-band
-
- match *item.kind {
- clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
- clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
- item_function(buf, cx, item, f)
- }
- clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
- clean::StructItem(ref s) => item_struct(buf, cx, item, s),
- clean::UnionItem(ref s) => item_union(buf, cx, item, s),
- clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
- clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t),
- clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
- clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
- clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
- clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i),
- clean::ConstantItem(ref c) => item_constant(buf, cx, item, c),
- clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
- clean::KeywordItem(_) => item_keyword(buf, cx, item),
- clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e),
- clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
- _ => {
- // We don't generate pages for any other type.
- unreachable!();
- }
- }
-}
-
-fn item_path(ty: ItemType, name: &str) -> String {
- match ty {
- ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
- _ => format!("{}.{}.html", ty, name),
- }
-}
-
-fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
- let mut s = cx.current.join("::");
- s.push_str("::");
- s.push_str(&item.name.unwrap().as_str());
- s
-}
-
-fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>) {
- if let Some(ref name) = item.name {
- info!("Documenting {}", name);
- }
- document_item_info(w, cx, item, false, parent);
- document_full(w, item, cx, "", false);
-}
-
-/// Render md_text as markdown.
-fn render_markdown(
- w: &mut Buffer,
- cx: &Context<'_>,
- md_text: &str,
- links: Vec,
- prefix: &str,
- is_hidden: bool,
-) {
- let mut ids = cx.id_map.borrow_mut();
- write!(
- w,
- "{}{}
",
- if is_hidden { " hidden" } else { "" },
- prefix,
- Markdown(
- md_text,
- &links,
- &mut ids,
- cx.shared.codes,
- cx.shared.edition,
- &cx.shared.playground
- )
- .into_string()
- )
-}
-
-/// Writes a documentation block containing only the first paragraph of the documentation. If the
-/// docs are longer, a "Read more" link is appended to the end.
-fn document_short(
- w: &mut Buffer,
- item: &clean::Item,
- cx: &Context<'_>,
- link: AssocItemLink<'_>,
- prefix: &str,
- is_hidden: bool,
- parent: Option<&clean::Item>,
- show_def_docs: bool,
-) {
- document_item_info(w, cx, item, is_hidden, parent);
- if !show_def_docs {
- return;
- }
- if let Some(s) = item.doc_value() {
- let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string();
-
- if s.contains('\n') {
- let link =
- format!(r#" Read more"#, naive_assoc_href(item, link, cx.cache()));
-
- if let Some(idx) = summary_html.rfind("
") {
- summary_html.insert_str(idx, &link);
- } else {
- summary_html.push_str(&link);
+ if let Some(idx) = summary_html.rfind("") {
+ summary_html.insert_str(idx, &link);
+ } else {
+ summary_html.push_str(&link);
}
}
@@ -1992,324 +747,6 @@ fn document_item_info(
}
}
-fn document_non_exhaustive_header(item: &clean::Item) -> &str {
- if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
-}
-
-fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
- if item.is_non_exhaustive() {
- write!(w, "", {
- if item.is_struct() {
- "struct"
- } else if item.is_enum() {
- "enum"
- } else if item.is_variant() {
- "variant"
- } else {
- "type"
- }
- });
-
- if item.is_struct() {
- w.write_str(
- "Non-exhaustive structs could have additional fields added in future. \
- Therefore, non-exhaustive structs cannot be constructed in external crates \
- using the traditional Struct {{ .. }}
syntax; cannot be \
- matched against without a wildcard ..
; and \
- struct update syntax will not work.",
- );
- } else if item.is_enum() {
- w.write_str(
- "Non-exhaustive enums could have additional variants added in future. \
- Therefore, when matching against variants of non-exhaustive enums, an \
- extra wildcard arm must be added to account for any future variants.",
- );
- } else if item.is_variant() {
- w.write_str(
- "Non-exhaustive enum variants could have additional fields added in future. \
- Therefore, non-exhaustive enum variants cannot be constructed in external \
- crates and cannot be matched against.",
- );
- } else {
- w.write_str(
- "This type will require a wildcard arm in any match statements or constructors.",
- );
- }
-
- w.write_str("
");
- }
-}
-
-/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
-crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
- /// Takes a non-numeric and a numeric part from the given &str.
- fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) {
- let i = s.find(|c: char| c.is_ascii_digit());
- let (a, b) = s.split_at(i.unwrap_or(s.len()));
- let i = b.find(|c: char| !c.is_ascii_digit());
- let (b, c) = b.split_at(i.unwrap_or(b.len()));
- *s = c;
- (a, b)
- }
-
- while !lhs.is_empty() || !rhs.is_empty() {
- let (la, lb) = take_parts(&mut lhs);
- let (ra, rb) = take_parts(&mut rhs);
- // First process the non-numeric part.
- match la.cmp(ra) {
- Ordering::Equal => (),
- x => return x,
- }
- // Then process the numeric part, if both sides have one (and they fit in a u64).
- if let (Ok(ln), Ok(rn)) = (lb.parse::(), rb.parse::()) {
- match ln.cmp(&rn) {
- Ordering::Equal => (),
- x => return x,
- }
- }
- // Then process the numeric part again, but this time as strings.
- match lb.cmp(rb) {
- Ordering::Equal => (),
- x => return x,
- }
- }
-
- Ordering::Equal
-}
-
-fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
- document(w, cx, item, None);
-
- let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>();
-
- // the order of item types in the listing
- fn reorder(ty: ItemType) -> u8 {
- match ty {
- ItemType::ExternCrate => 0,
- ItemType::Import => 1,
- ItemType::Primitive => 2,
- ItemType::Module => 3,
- ItemType::Macro => 4,
- ItemType::Struct => 5,
- ItemType::Enum => 6,
- ItemType::Constant => 7,
- ItemType::Static => 8,
- ItemType::Trait => 9,
- ItemType::Function => 10,
- ItemType::Typedef => 12,
- ItemType::Union => 13,
- _ => 14 + ty as u8,
- }
- }
-
- fn cmp(
- i1: &clean::Item,
- i2: &clean::Item,
- idx1: usize,
- idx2: usize,
- tcx: TyCtxt<'_>,
- ) -> Ordering {
- let ty1 = i1.type_();
- let ty2 = i2.type_();
- if ty1 != ty2 {
- return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2));
- }
- let s1 = i1.stability(tcx).as_ref().map(|s| s.level);
- let s2 = i2.stability(tcx).as_ref().map(|s| s.level);
- if let (Some(a), Some(b)) = (s1, s2) {
- match (a.is_stable(), b.is_stable()) {
- (true, true) | (false, false) => {}
- (false, true) => return Ordering::Less,
- (true, false) => return Ordering::Greater,
- }
- }
- let lhs = i1.name.unwrap_or(kw::Empty).as_str();
- let rhs = i2.name.unwrap_or(kw::Empty).as_str();
- compare_names(&lhs, &rhs)
- }
-
- if cx.shared.sort_modules_alphabetically {
- indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx()));
- }
- // This call is to remove re-export duplicates in cases such as:
- //
- // ```
- // crate mod foo {
- // crate mod bar {
- // crate trait Double { fn foo(); }
- // }
- // }
- //
- // crate use foo::bar::*;
- // crate use foo::*;
- // ```
- //
- // `Double` will appear twice in the generated docs.
- //
- // FIXME: This code is quite ugly and could be improved. Small issue: DefId
- // can be identical even if the elements are different (mostly in imports).
- // So in case this is an import, we keep everything by adding a "unique id"
- // (which is the position in the vector).
- indices.dedup_by_key(|i| {
- (
- items[*i].def_id,
- if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None },
- items[*i].type_(),
- if items[*i].is_import() { *i } else { 0 },
- )
- });
-
- debug!("{:?}", indices);
- let mut curty = None;
- for &idx in &indices {
- let myitem = &items[idx];
- if myitem.is_stripped() {
- continue;
- }
-
- let myty = Some(myitem.type_());
- if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) {
- // Put `extern crate` and `use` re-exports in the same section.
- curty = myty;
- } else if myty != curty {
- if curty.is_some() {
- w.write_str("");
- }
- curty = myty;
- let (short, name) = item_ty_to_strs(&myty.unwrap());
- write!(
- w,
- "\n",
- id = cx.derive_id(short.to_owned()),
- name = name
- );
- }
-
- match *myitem.kind {
- clean::ExternCrateItem(ref name, ref src) => {
- use crate::html::format::anchor;
-
- match *src {
- Some(ref src) => write!(
- w,
- "{}extern crate {} as {};",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- anchor(myitem.def_id, &*src.as_str(), cx.cache()),
- name
- ),
- None => write!(
- w,
- "{}extern crate {};",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- anchor(myitem.def_id, &*name.as_str(), cx.cache())
- ),
- }
- w.write_str(" | ");
- }
-
- clean::ImportItem(ref import) => {
- write!(
- w,
- "{}{} | ",
- myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
- import.print(cx.cache())
- );
- }
-
- _ => {
- if myitem.name.is_none() {
- continue;
- }
-
- let unsafety_flag = match *myitem.kind {
- clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
- if func.header.unsafety == hir::Unsafety::Unsafe =>
- {
- "⚠"
- }
- _ => "",
- };
-
- let stab = myitem.stability_class(cx.tcx());
- let add = if stab.is_some() { " " } else { "" };
-
- let doc_value = myitem.doc_value().unwrap_or_default();
- write!(
- w,
- "\
- {name}{unsafety_flag} | \
- {stab_tags}{docs} | \
- ",
- name = *myitem.name.as_ref().unwrap(),
- stab_tags = extra_info_tags(myitem, item, cx.tcx()),
- docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(),
- class = myitem.type_(),
- add = add,
- stab = stab.unwrap_or_else(String::new),
- unsafety_flag = unsafety_flag,
- href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()),
- title = [full_path(cx, myitem), myitem.type_().to_string()]
- .iter()
- .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
- .collect::>()
- .join(" "),
- );
- }
- }
- }
-
- if curty.is_some() {
- w.write_str(" |
");
- }
-}
-
-/// Render the stability, deprecation and portability tags that are displayed in the item's summary
-/// at the module level.
-fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
- let mut tags = String::new();
-
- fn tag_html(class: &str, title: &str, contents: &str) -> String {
- format!(r#"{}"#, class, Escape(title), contents)
- }
-
- // The trailing space after each tag is to space it properly against the rest of the docs.
- if let Some(depr) = &item.deprecation(tcx) {
- let mut message = "Deprecated";
- if !stability::deprecation_in_effect(
- depr.is_since_rustc_version,
- depr.since.map(|s| s.as_str()).as_deref(),
- ) {
- message = "Deprecation planned";
- }
- tags += &tag_html("deprecated", "", message);
- }
-
- // The "rustc_private" crates are permanently unstable so it makes no sense
- // to render "unstable" everywhere.
- if item
- .stability(tcx)
- .as_ref()
- .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
- == Some(true)
- {
- tags += &tag_html("unstable", "", "Experimental");
- }
-
- let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) {
- (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
- (cfg, _) => cfg.as_deref().cloned(),
- };
-
- debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg);
- if let Some(ref cfg) = cfg {
- tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
- }
-
- tags
-}
-
fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option {
let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) {
(Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
@@ -2394,504 +831,66 @@ fn short_item_info(
));
}
- message.push_str(&format!(" ({})", feature));
-
- if let Some(unstable_reason) = reason {
- let mut ids = cx.id_map.borrow_mut();
- message = format!(
- "{}
{} ",
- message,
- MarkdownHtml(
- &unstable_reason.as_str(),
- &mut ids,
- error_codes,
- cx.shared.edition,
- &cx.shared.playground,
- )
- .into_string()
- );
- }
-
- extra_info.push(format!("{}
", message));
- }
-
- if let Some(portability) = portability(item, parent) {
- extra_info.push(portability);
- }
-
- extra_info
-}
-
-fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
- w.write_str("");
- render_attributes(w, it, false);
-
- write!(
- w,
- "{vis}const {name}: {typ}",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- name = it.name.as_ref().unwrap(),
- typ = c.type_.print(cx.cache()),
- );
-
- if c.value.is_some() || c.is_literal {
- write!(w, " = {expr};", expr = Escape(&c.expr));
- } else {
- w.write_str(";");
- }
-
- if let Some(value) = &c.value {
- if !c.is_literal {
- let value_lowercase = value.to_lowercase();
- let expr_lowercase = c.expr.to_lowercase();
-
- if value_lowercase != expr_lowercase
- && value_lowercase.trim_end_matches("i32") != expr_lowercase
- {
- write!(w, " // {value}", value = Escape(value));
- }
- }
- }
-
- w.write_str("
");
- document(w, cx, it, None)
-}
-
-fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
- w.write_str("");
- render_attributes(w, it, false);
- write!(
- w,
- "{vis}static {mutability}{name}: {typ}
",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- mutability = s.mutability.print_with_space(),
- name = it.name.as_ref().unwrap(),
- typ = s.type_.print(cx.cache())
- );
- document(w, cx, it, None)
-}
-
-fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
- let header_len = format!(
- "{}{}{}{}{:#}fn {}{:#}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- f.header.constness.print_with_space(),
- f.header.asyncness.print_with_space(),
- f.header.unsafety.print_with_space(),
- print_abi_with_space(f.header.abi),
- it.name.as_ref().unwrap(),
- f.generics.print(cx.cache())
- )
- .len();
- w.write_str("");
- render_attributes(w, it, false);
- write!(
- w,
- "{vis}{constness}{asyncness}{unsafety}{abi}fn \
- {name}{generics}{decl}{spotlight}{where_clause}
",
- vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- constness = f.header.constness.print_with_space(),
- asyncness = f.header.asyncness.print_with_space(),
- unsafety = f.header.unsafety.print_with_space(),
- abi = print_abi_with_space(f.header.abi),
- name = it.name.as_ref().unwrap(),
- generics = f.generics.print(cx.cache()),
- where_clause =
- WhereClause { gens: &f.generics, indent: 0, end_newline: true }.print(cx.cache()),
- decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness }
- .print(cx.cache()),
- spotlight = spotlight_decl(&f.decl, cx.cache()),
- );
- document(w, cx, it, None)
-}
-
-fn render_implementor(
- cx: &Context<'_>,
- implementor: &Impl,
- trait_: &clean::Item,
- w: &mut Buffer,
- implementor_dups: &FxHashMap,
- aliases: &[String],
-) {
- // If there's already another implementor that has the same abbridged name, use the
- // full path, for example in `std::iter::ExactSizeIterator`
- let use_absolute = match implementor.inner_impl().for_ {
- clean::ResolvedPath { ref path, is_generic: false, .. }
- | clean::BorrowedRef {
- type_: box clean::ResolvedPath { ref path, is_generic: false, .. },
- ..
- } => implementor_dups[&path.last()].1,
- _ => false,
- };
- render_impl(
- w,
- cx,
- implementor,
- trait_,
- AssocItemLink::Anchor(None),
- RenderMode::Normal,
- trait_.stable_since(cx.tcx()).as_deref(),
- trait_.const_stable_since(cx.tcx()).as_deref(),
- false,
- Some(use_absolute),
- false,
- false,
- aliases,
- );
-}
-
-fn render_impls(
- cx: &Context<'_>,
- w: &mut Buffer,
- traits: &[&&Impl],
- containing_item: &clean::Item,
-) {
- let mut impls = traits
- .iter()
- .map(|i| {
- let did = i.trait_did_full(cx.cache()).unwrap();
- let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
- let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
- render_impl(
- &mut buffer,
- cx,
- i,
- containing_item,
- assoc_link,
- RenderMode::Normal,
- containing_item.stable_since(cx.tcx()).as_deref(),
- containing_item.const_stable_since(cx.tcx()).as_deref(),
- true,
- None,
- false,
- true,
- &[],
- );
- buffer.into_inner()
- })
- .collect::>();
- impls.sort();
- w.write_str(&impls.join(""));
-}
-
-fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cache: &Cache) -> String {
- let mut bounds = String::new();
- if !t_bounds.is_empty() {
- if !trait_alias {
- bounds.push_str(": ");
- }
- for (i, p) in t_bounds.iter().enumerate() {
- if i > 0 {
- bounds.push_str(" + ");
- }
- bounds.push_str(&p.print(cache).to_string());
- }
- }
- bounds
-}
-
-fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cache: &Cache) -> Ordering {
- let lhs = format!("{}", lhs.inner_impl().print(cache, false));
- let rhs = format!("{}", rhs.inner_impl().print(cache, false));
-
- // lhs and rhs are formatted as HTML, which may be unnecessary
- compare_names(&lhs, &rhs)
-}
-
-fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
- let bounds = bounds(&t.bounds, false, cx.cache());
- let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>();
- let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>();
- let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>();
- let provided = t.items.iter().filter(|m| m.is_method()).collect::>();
-
- // Output the trait definition
- wrap_into_docblock(w, |w| {
- w.write_str("");
- render_attributes(w, it, true);
- write!(
- w,
- "{}{}{}trait {}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- t.unsafety.print_with_space(),
- if t.is_auto { "auto " } else { "" },
- it.name.as_ref().unwrap(),
- t.generics.print(cx.cache()),
- bounds
- );
-
- if !t.generics.where_predicates.is_empty() {
- let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
- write!(w, "{}", where_.print(cx.cache()));
- } else {
- w.write_str(" ");
- }
-
- if t.items.is_empty() {
- w.write_str("{ }");
- } else {
- // FIXME: we should be using a derived_id for the Anchors here
- w.write_str("{\n");
- for t in &types {
- render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
- w.write_str(";\n");
- }
- if !types.is_empty() && !consts.is_empty() {
- w.write_str("\n");
- }
- for t in &consts {
- render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
- w.write_str(";\n");
- }
- if !consts.is_empty() && !required.is_empty() {
- w.write_str("\n");
- }
- for (pos, m) in required.iter().enumerate() {
- render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
- w.write_str(";\n");
-
- if pos < required.len() - 1 {
- w.write_str("");
- }
- }
- if !required.is_empty() && !provided.is_empty() {
- w.write_str("\n");
- }
- for (pos, m) in provided.iter().enumerate() {
- render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
- match *m.kind {
- clean::MethodItem(ref inner, _)
- if !inner.generics.where_predicates.is_empty() =>
- {
- w.write_str(",\n { ... }\n");
- }
- _ => {
- w.write_str(" { ... }\n");
- }
- }
- if pos < provided.len() - 1 {
- w.write_str("");
- }
- }
- w.write_str("}");
- }
- w.write_str("
")
- });
-
- // Trait documentation
- document(w, cx, it, None);
-
- fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
- write!(
- w,
- "{2}",
- id, title, extra_content
- )
- }
-
- fn write_loading_content(w: &mut Buffer, extra_content: &str) {
- write!(w, "{}Loading content...", extra_content)
- }
-
- fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) {
- let name = m.name.as_ref().unwrap();
- info!("Documenting {} on {:?}", name, t.name);
- let item_type = m.type_();
- let id = cx.derive_id(format!("{}.{}", item_type, name));
- write!(w, "", id = id,);
- render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
- w.write_str("
");
- render_stability_since(w, m, t, cx.tcx());
- write_srclink(cx, m, w);
- w.write_str("
");
- document(w, cx, m, Some(t));
- }
-
- if !types.is_empty() {
- write_small_section_header(
- w,
- "associated-types",
- "Associated Types",
- "",
- );
- for t in types {
- trait_item(w, cx, t, it);
- }
- write_loading_content(w, "
");
- }
-
- if !consts.is_empty() {
- write_small_section_header(
- w,
- "associated-const",
- "Associated Constants",
- "",
- );
- for t in consts {
- trait_item(w, cx, t, it);
- }
- write_loading_content(w, "
");
- }
-
- // Output the documentation for each function individually
- if !required.is_empty() {
- write_small_section_header(
- w,
- "required-methods",
- "Required methods",
- "",
- );
- for m in required {
- trait_item(w, cx, m, it);
- }
- write_loading_content(w, "
");
- }
- if !provided.is_empty() {
- write_small_section_header(
- w,
- "provided-methods",
- "Provided methods",
- "",
- );
- for m in provided {
- trait_item(w, cx, m, it);
- }
- write_loading_content(w, "
");
- }
-
- // If there are methods directly on this trait object, render them here.
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All);
-
- if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
- // The DefId is for the first Type found with that name. The bool is
- // if any Types with the same name but different DefId have been found.
- let mut implementor_dups: FxHashMap = FxHashMap::default();
- for implementor in implementors {
- match implementor.inner_impl().for_ {
- clean::ResolvedPath { ref path, did, is_generic: false, .. }
- | clean::BorrowedRef {
- type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. },
- ..
- } => {
- let &mut (prev_did, ref mut has_duplicates) =
- implementor_dups.entry(path.last()).or_insert((did, false));
- if prev_did != did {
- *has_duplicates = true;
- }
- }
- _ => {}
- }
- }
-
- let (local, foreign) = implementors.iter().partition::, _>(|i| {
- i.inner_impl()
- .for_
- .def_id_full(cx.cache())
- .map_or(true, |d| cx.cache.paths.contains_key(&d))
- });
-
- let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
- local.iter().partition(|i| i.inner_impl().synthetic);
-
- synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache()));
- concrete.sort_by(|a, b| compare_impl(a, b, cx.cache()));
-
- if !foreign.is_empty() {
- write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
-
- for implementor in foreign {
- let assoc_link = AssocItemLink::GotoSource(
- implementor.impl_item.def_id,
- &implementor.inner_impl().provided_trait_methods,
- );
- render_impl(
- w,
- cx,
- &implementor,
- it,
- assoc_link,
- RenderMode::Normal,
- implementor.impl_item.stable_since(cx.tcx()).as_deref(),
- implementor.impl_item.const_stable_since(cx.tcx()).as_deref(),
- false,
- None,
- true,
- false,
- &[],
- );
- }
- write_loading_content(w, "");
- }
-
- write_small_section_header(
- w,
- "implementors",
- "Implementors",
- "",
- );
- for implementor in concrete {
- render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
- }
- write_loading_content(w, "
");
-
- if t.is_auto {
- write_small_section_header(
- w,
- "synthetic-implementors",
- "Auto implementors",
- "",
- );
- for implementor in synthetic {
- render_implementor(
- cx,
- implementor,
- it,
- w,
- &implementor_dups,
- &collect_paths_for_type(implementor.inner_impl().for_.clone(), &cx.cache),
- );
- }
- write_loading_content(w, "
");
- }
- } else {
- // even without any implementations to write in, we still want the heading and list, so the
- // implementors javascript file pulled in below has somewhere to write the impls into
- write_small_section_header(
- w,
- "implementors",
- "Implementors",
- "",
- );
- write_loading_content(w, "
");
+ message.push_str(&format!(" ({})", feature));
- if t.is_auto {
- write_small_section_header(
- w,
- "synthetic-implementors",
- "Auto implementors",
- "",
+ if let Some(unstable_reason) = reason {
+ let mut ids = cx.id_map.borrow_mut();
+ message = format!(
+ "{}
{} ",
+ message,
+ MarkdownHtml(
+ &unstable_reason.as_str(),
+ &mut ids,
+ error_codes,
+ cx.shared.edition,
+ &cx.shared.playground,
+ )
+ .into_string()
);
- write_loading_content(w, "
");
}
+
+ extra_info.push(format!("{}
", message));
}
- write!(
- w,
- "",
- root_path = vec![".."; cx.current.len()].join("/"),
- path = if it.def_id.is_local() {
- cx.current.join("/")
- } else {
- let (ref path, _) = cx.cache.external_paths[&it.def_id];
- path[..path.len() - 1].join("/")
- },
- ty = it.type_(),
- name = *it.name.as_ref().unwrap()
- );
+ if let Some(portability) = portability(item, parent) {
+ extra_info.push(portability);
+ }
+
+ extra_info
+}
+
+fn render_impls(
+ cx: &Context<'_>,
+ w: &mut Buffer,
+ traits: &[&&Impl],
+ containing_item: &clean::Item,
+) {
+ let mut impls = traits
+ .iter()
+ .map(|i| {
+ let did = i.trait_did_full(cx.cache()).unwrap();
+ let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
+ let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
+ render_impl(
+ &mut buffer,
+ cx,
+ i,
+ containing_item,
+ assoc_link,
+ RenderMode::Normal,
+ containing_item.stable_since(cx.tcx()).as_deref(),
+ containing_item.const_stable_since(cx.tcx()).as_deref(),
+ true,
+ None,
+ false,
+ true,
+ &[],
+ );
+ buffer.into_inner()
+ })
+ .collect::>();
+ impls.sort();
+ w.write_str(&impls.join(""));
}
fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String {
@@ -2986,21 +985,6 @@ fn render_stability_since_raw(
}
}
-fn render_stability_since(
- w: &mut Buffer,
- item: &clean::Item,
- containing_item: &clean::Item,
- tcx: TyCtxt<'_>,
-) {
- render_stability_since_raw(
- w,
- item.stable_since(tcx).as_deref(),
- item.const_stable_since(tcx).as_deref(),
- containing_item.stable_since(tcx).as_deref(),
- containing_item.const_stable_since(tcx).as_deref(),
- )
-}
-
fn render_assoc_item(
w: &mut Buffer,
item: &clean::Item,
@@ -3104,227 +1088,6 @@ fn render_assoc_item(
}
}
-fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
- wrap_into_docblock(w, |w| {
- w.write_str("");
- render_attributes(w, it, true);
- render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
- w.write_str("
")
- });
-
- document(w, cx, it, None);
- let mut fields = s
- .fields
- .iter()
- .filter_map(|f| match *f.kind {
- clean::StructFieldItem(ref ty) => Some((f, ty)),
- _ => None,
- })
- .peekable();
- if let CtorKind::Fictive = s.struct_type {
- if fields.peek().is_some() {
- write!(
- w,
- "
- Fields{}
",
- document_non_exhaustive_header(it)
- );
- document_non_exhaustive(w, it);
- for (field, ty) in fields {
- let id = cx.derive_id(format!(
- "{}.{}",
- ItemType::StructField,
- field.name.as_ref().unwrap()
- ));
- write!(
- w,
- "\
- \
- {name}: {ty}
\
- ",
- item_type = ItemType::StructField,
- id = id,
- name = field.name.as_ref().unwrap(),
- ty = ty.print(cx.cache())
- );
- document(w, cx, field, Some(it));
- }
- }
- }
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
- wrap_into_docblock(w, |w| {
- w.write_str("");
- render_attributes(w, it, true);
- render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
- w.write_str("
")
- });
-
- document(w, cx, it, None);
- let mut fields = s
- .fields
- .iter()
- .filter_map(|f| match *f.kind {
- clean::StructFieldItem(ref ty) => Some((f, ty)),
- _ => None,
- })
- .peekable();
- if fields.peek().is_some() {
- write!(
- w,
- "
- Fields
"
- );
- for (field, ty) in fields {
- let name = field.name.as_ref().expect("union field name");
- let id = format!("{}.{}", ItemType::StructField, name);
- write!(
- w,
- "\
- \
- {name}: {ty}
\
- ",
- id = id,
- name = name,
- shortty = ItemType::StructField,
- ty = ty.print(cx.cache())
- );
- if let Some(stability_class) = field.stability_class(cx.tcx()) {
- write!(w, "", stab = stability_class);
- }
- document(w, cx, field, Some(it));
- }
- }
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
- wrap_into_docblock(w, |w| {
- w.write_str("");
- render_attributes(w, it, true);
- write!(
- w,
- "{}enum {}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- it.name.as_ref().unwrap(),
- e.generics.print(cx.cache()),
- WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
- );
- if e.variants.is_empty() && !e.variants_stripped {
- w.write_str(" {}");
- } else {
- w.write_str(" {\n");
- for v in &e.variants {
- w.write_str(" ");
- let name = v.name.as_ref().unwrap();
- match *v.kind {
- clean::VariantItem(ref var) => match var {
- clean::Variant::CLike => write!(w, "{}", name),
- clean::Variant::Tuple(ref tys) => {
- write!(w, "{}(", name);
- for (i, ty) in tys.iter().enumerate() {
- if i > 0 {
- w.write_str(", ")
- }
- write!(w, "{}", ty.print(cx.cache()));
- }
- w.write_str(")");
- }
- clean::Variant::Struct(ref s) => {
- render_struct(w, v, None, s.struct_type, &s.fields, " ", false, cx);
- }
- },
- _ => unreachable!(),
- }
- w.write_str(",\n");
- }
-
- if e.variants_stripped {
- w.write_str(" // some variants omitted\n");
- }
- w.write_str("}");
- }
- w.write_str("
")
- });
-
- document(w, cx, it, None);
- if !e.variants.is_empty() {
- write!(
- w,
- "
- Variants{}
\n",
- document_non_exhaustive_header(it)
- );
- document_non_exhaustive(w, it);
- for variant in &e.variants {
- let id =
- cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap()));
- write!(
- w,
- "\
-
\
-
{name}",
- id = id,
- name = variant.name.as_ref().unwrap()
- );
- if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind {
- w.write_str("(");
- for (i, ty) in tys.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- write!(w, "{}", ty.print(cx.cache()));
- }
- w.write_str(")");
- }
- w.write_str("
");
- document(w, cx, variant, Some(it));
- document_non_exhaustive(w, variant);
-
- use crate::clean::Variant;
- if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind {
- let variant_id = cx.derive_id(format!(
- "{}.{}.fields",
- ItemType::Variant,
- variant.name.as_ref().unwrap()
- ));
- write!(w, "", id = variant_id);
- write!(
- w,
- "
Fields of {name}
",
- name = variant.name.as_ref().unwrap()
- );
- for field in &s.fields {
- use crate::clean::StructFieldItem;
- if let StructFieldItem(ref ty) = *field.kind {
- let id = cx.derive_id(format!(
- "variant.{}.field.{}",
- variant.name.as_ref().unwrap(),
- field.name.as_ref().unwrap()
- ));
- write!(
- w,
- "
\
- \
- {f}: {t}
\
- ",
- id = id,
- f = field.name.as_ref().unwrap(),
- t = ty.print(cx.cache())
- );
- document(w, cx, field, Some(variant));
- }
- }
- w.write_str("
");
- }
- render_stability_since(w, variant, it, cx.tcx());
- }
- }
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
const ALLOWED_ATTRIBUTES: &[Symbol] = &[
sym::export_name,
sym::lang,
@@ -3367,147 +1130,6 @@ fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) {
}
}
-fn render_struct(
- w: &mut Buffer,
- it: &clean::Item,
- g: Option<&clean::Generics>,
- ty: CtorKind,
- fields: &[clean::Item],
- tab: &str,
- structhead: bool,
- cx: &Context<'_>,
-) {
- write!(
- w,
- "{}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- if structhead { "struct " } else { "" },
- it.name.as_ref().unwrap()
- );
- if let Some(g) = g {
- write!(w, "{}", g.print(cx.cache()))
- }
- match ty {
- CtorKind::Fictive => {
- if let Some(g) = g {
- write!(
- w,
- "{}",
- WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache())
- )
- }
- let mut has_visible_fields = false;
- w.write_str(" {");
- for field in fields {
- if let clean::StructFieldItem(ref ty) = *field.kind {
- write!(
- w,
- "\n{} {}{}: {},",
- tab,
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
- field.name.as_ref().unwrap(),
- ty.print(cx.cache())
- );
- has_visible_fields = true;
- }
- }
-
- if has_visible_fields {
- if it.has_stripped_fields().unwrap() {
- write!(w, "\n{} // some fields omitted", tab);
- }
- write!(w, "\n{}", tab);
- } else if it.has_stripped_fields().unwrap() {
- // If there are no visible fields we can just display
- // `{ /* fields omitted */ }` to save space.
- write!(w, " /* fields omitted */ ");
- }
- w.write_str("}");
- }
- CtorKind::Fn => {
- w.write_str("(");
- for (i, field) in fields.iter().enumerate() {
- if i > 0 {
- w.write_str(", ");
- }
- match *field.kind {
- clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
- clean::StructFieldItem(ref ty) => {
- write!(
- w,
- "{}{}",
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
- ty.print(cx.cache())
- )
- }
- _ => unreachable!(),
- }
- }
- w.write_str(")");
- if let Some(g) = g {
- write!(
- w,
- "{}",
- WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
- )
- }
- w.write_str(";");
- }
- CtorKind::Const => {
- // Needed for PhantomData.
- if let Some(g) = g {
- write!(
- w,
- "{}",
- WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
- )
- }
- w.write_str(";");
- }
- }
-}
-
-fn render_union(
- w: &mut Buffer,
- it: &clean::Item,
- g: Option<&clean::Generics>,
- fields: &[clean::Item],
- tab: &str,
- structhead: bool,
- cx: &Context<'_>,
-) {
- write!(
- w,
- "{}{}{}",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- if structhead { "union " } else { "" },
- it.name.as_ref().unwrap()
- );
- if let Some(g) = g {
- write!(w, "{}", g.print(cx.cache()));
- write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache()));
- }
-
- write!(w, " {{\n{}", tab);
- for field in fields {
- if let clean::StructFieldItem(ref ty) = *field.kind {
- write!(
- w,
- " {}{}: {},\n{}",
- field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
- field.name.as_ref().unwrap(),
- ty.print(cx.cache()),
- tab
- );
- }
- }
-
- if it.has_stripped_fields().unwrap() {
- write!(w, " // some fields omitted\n{}", tab);
- }
- w.write_str("}");
-}
-
#[derive(Copy, Clone)]
enum AssocItemLink<'a> {
Anchor(Option<&'a str>),
@@ -4078,86 +1700,6 @@ fn render_impl(
w.write_str("");
}
-fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
- w.write_str("");
- render_attributes(w, it, false);
- write!(
- w,
- "type {}{}{where_clause} = impl {bounds};
",
- it.name.as_ref().unwrap(),
- t.generics.print(cx.cache()),
- where_clause =
- WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
- bounds = bounds(&t.bounds, false, cx.cache())
- );
-
- document(w, cx, it, None);
-
- // Render any items associated directly to this alias, as otherwise they
- // won't be visible anywhere in the docs. It would be nice to also show
- // associated items from the aliased type (see discussion in #32077), but
- // we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
- w.write_str("");
- render_attributes(w, it, false);
- write!(
- w,
- "trait {}{}{} = {};
",
- it.name.as_ref().unwrap(),
- t.generics.print(cx.cache()),
- WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
- bounds(&t.bounds, true, cx.cache())
- );
-
- document(w, cx, it, None);
-
- // Render any items associated directly to this alias, as otherwise they
- // won't be visible anywhere in the docs. It would be nice to also show
- // associated items from the aliased type (see discussion in #32077), but
- // we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- w.write_str("");
- render_attributes(w, it, false);
- write!(
- w,
- "type {}{}{where_clause} = {type_};
",
- it.name.as_ref().unwrap(),
- t.generics.print(cx.cache()),
- where_clause =
- WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
- type_ = t.type_.print(cx.cache())
- );
-
- document(w, cx, it, None);
-
- // Render any items associated directly to this alias, as otherwise they
- // won't be visible anywhere in the docs. It would be nice to also show
- // associated items from the aliased type (see discussion in #32077), but
- // we need #14072 to make sense of the generics.
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
- w.write_str("extern {\n");
- render_attributes(w, it, false);
- write!(
- w,
- " {}type {};\n}}
",
- it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
- it.name.as_ref().unwrap(),
- );
-
- document(w, cx, it, None);
-
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 };
@@ -4850,65 +2392,8 @@ fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
}
}
-fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
- wrap_into_docblock(w, |w| {
- highlight::render_with_highlighting(
- &t.source,
- w,
- Some("macro"),
- None,
- None,
- it.source.span().edition(),
- );
- });
- document(w, cx, it, None)
-}
-
-fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
- let name = it.name.as_ref().expect("proc-macros always have names");
- match m.kind {
- MacroKind::Bang => {
- w.push_str("");
- write!(w, "{}!() {{ /* proc-macro */ }}", name);
- w.push_str("
");
- }
- MacroKind::Attr => {
- w.push_str("");
- write!(w, "#[{}]", name);
- w.push_str("
");
- }
- MacroKind::Derive => {
- w.push_str("");
- write!(w, "#[derive({})]", name);
- if !m.helpers.is_empty() {
- w.push_str("\n{\n");
- w.push_str(" // Attributes available to this derive:\n");
- for attr in &m.helpers {
- writeln!(w, " #[{}]", attr);
- }
- w.push_str("}\n");
- }
- w.push_str("
");
- }
- }
- document(w, cx, it, None)
-}
-
-fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
- document(w, cx, it, None);
- render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
-}
-
-fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
- document(w, cx, it, None)
-}
-
crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang";
-fn make_item_keywords(it: &clean::Item) -> String {
- format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())
-}
-
/// Returns a list of all paths used in the type.
/// This is used to help deduplicate imported impls
/// for reexported types. If any of the contained
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
new file mode 100644
index 0000000000000..6d61248f28ef0
--- /dev/null
+++ b/src/librustdoc/html/render/print_item.rs
@@ -0,0 +1,1420 @@
+use std::cmp::Ordering;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::def::CtorKind;
+use rustc_hir::def_id::DefId;
+use rustc_middle::middle::stability;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::hygiene::MacroKind;
+use rustc_span::symbol::{kw, sym, Symbol};
+
+use super::{
+ collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, render_assoc_item,
+ render_assoc_items, render_attributes, render_impl, render_stability_since_raw, spotlight_decl,
+ write_srclink, AssocItemLink, Context,
+};
+use crate::clean::{self, GetDefId};
+use crate::formats::cache::Cache;
+use crate::formats::item_type::ItemType;
+use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode};
+use crate::html::escape::Escape;
+use crate::html::format::{print_abi_with_space, Buffer, Function, PrintWithSpace, WhereClause};
+use crate::html::highlight;
+use crate::html::markdown::MarkdownSummaryLine;
+
+pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
+ debug_assert!(!item.is_stripped());
+ // Write the breadcrumb trail header for the top
+ buf.write_str("");
+ let name = match *item.kind {
+ clean::ModuleItem(ref m) => {
+ if m.is_crate {
+ "Crate "
+ } else {
+ "Module "
+ }
+ }
+ clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
+ clean::TraitItem(..) => "Trait ",
+ clean::StructItem(..) => "Struct ",
+ clean::UnionItem(..) => "Union ",
+ clean::EnumItem(..) => "Enum ",
+ clean::TypedefItem(..) => "Type Definition ",
+ clean::MacroItem(..) => "Macro ",
+ clean::ProcMacroItem(ref mac) => match mac.kind {
+ MacroKind::Bang => "Macro ",
+ MacroKind::Attr => "Attribute Macro ",
+ MacroKind::Derive => "Derive Macro ",
+ },
+ clean::PrimitiveItem(..) => "Primitive Type ",
+ clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ",
+ clean::ConstantItem(..) => "Constant ",
+ clean::ForeignTypeItem => "Foreign Type ",
+ clean::KeywordItem(..) => "Keyword ",
+ clean::OpaqueTyItem(..) => "Opaque Type ",
+ clean::TraitAliasItem(..) => "Trait Alias ",
+ _ => {
+ // We don't generate pages for any other type.
+ unreachable!();
+ }
+ };
+ buf.write_str(name);
+ if !item.is_primitive() && !item.is_keyword() {
+ let cur = &cx.current;
+ let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() };
+ for (i, component) in cur.iter().enumerate().take(amt) {
+ write!(
+ buf,
+ "{}::",
+ "../".repeat(cur.len() - i - 1),
+ component
+ );
+ }
+ }
+ write!(buf, "{}", item.type_(), item.name.as_ref().unwrap());
+
+ buf.write_str(""); // in-band
+ buf.write_str("");
+ render_stability_since_raw(
+ buf,
+ item.stable_since(cx.tcx()).as_deref(),
+ item.const_stable_since(cx.tcx()).as_deref(),
+ None,
+ None,
+ );
+ buf.write_str(
+ "\
+ \
+ [−]\
+ \
+ ",
+ );
+
+ // Write `src` tag
+ //
+ // When this item is part of a `crate use` in a downstream crate, the
+ // [src] link in the downstream documentation will actually come back to
+ // this page, and this link will be auto-clicked. The `id` attribute is
+ // used to find the link to auto-click.
+ if cx.shared.include_sources && !item.is_primitive() {
+ write_srclink(cx, item, buf);
+ }
+
+ buf.write_str("
"); // out-of-band
+
+ match *item.kind {
+ clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
+ clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
+ item_function(buf, cx, item, f)
+ }
+ clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
+ clean::StructItem(ref s) => item_struct(buf, cx, item, s),
+ clean::UnionItem(ref s) => item_union(buf, cx, item, s),
+ clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
+ clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t),
+ clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
+ clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
+ clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
+ clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i),
+ clean::ConstantItem(ref c) => item_constant(buf, cx, item, c),
+ clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
+ clean::KeywordItem(_) => item_keyword(buf, cx, item),
+ clean::OpaqueTyItem(ref e) => item_opaque_ty(buf, cx, item, e),
+ clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
+ _ => {
+ // We don't generate pages for any other type.
+ unreachable!();
+ }
+ }
+}
+
+fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) {
+ document(w, cx, item, None);
+
+ let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>();
+
+ // the order of item types in the listing
+ fn reorder(ty: ItemType) -> u8 {
+ match ty {
+ ItemType::ExternCrate => 0,
+ ItemType::Import => 1,
+ ItemType::Primitive => 2,
+ ItemType::Module => 3,
+ ItemType::Macro => 4,
+ ItemType::Struct => 5,
+ ItemType::Enum => 6,
+ ItemType::Constant => 7,
+ ItemType::Static => 8,
+ ItemType::Trait => 9,
+ ItemType::Function => 10,
+ ItemType::Typedef => 12,
+ ItemType::Union => 13,
+ _ => 14 + ty as u8,
+ }
+ }
+
+ fn cmp(
+ i1: &clean::Item,
+ i2: &clean::Item,
+ idx1: usize,
+ idx2: usize,
+ tcx: TyCtxt<'_>,
+ ) -> Ordering {
+ let ty1 = i1.type_();
+ let ty2 = i2.type_();
+ if ty1 != ty2 {
+ return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2));
+ }
+ let s1 = i1.stability(tcx).as_ref().map(|s| s.level);
+ let s2 = i2.stability(tcx).as_ref().map(|s| s.level);
+ if let (Some(a), Some(b)) = (s1, s2) {
+ match (a.is_stable(), b.is_stable()) {
+ (true, true) | (false, false) => {}
+ (false, true) => return Ordering::Less,
+ (true, false) => return Ordering::Greater,
+ }
+ }
+ let lhs = i1.name.unwrap_or(kw::Empty).as_str();
+ let rhs = i2.name.unwrap_or(kw::Empty).as_str();
+ compare_names(&lhs, &rhs)
+ }
+
+ if cx.shared.sort_modules_alphabetically {
+ indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2, cx.tcx()));
+ }
+ // This call is to remove re-export duplicates in cases such as:
+ //
+ // ```
+ // crate mod foo {
+ // crate mod bar {
+ // crate trait Double { fn foo(); }
+ // }
+ // }
+ //
+ // crate use foo::bar::*;
+ // crate use foo::*;
+ // ```
+ //
+ // `Double` will appear twice in the generated docs.
+ //
+ // FIXME: This code is quite ugly and could be improved. Small issue: DefId
+ // can be identical even if the elements are different (mostly in imports).
+ // So in case this is an import, we keep everything by adding a "unique id"
+ // (which is the position in the vector).
+ indices.dedup_by_key(|i| {
+ (
+ items[*i].def_id,
+ if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None },
+ items[*i].type_(),
+ if items[*i].is_import() { *i } else { 0 },
+ )
+ });
+
+ debug!("{:?}", indices);
+ let mut curty = None;
+ for &idx in &indices {
+ let myitem = &items[idx];
+ if myitem.is_stripped() {
+ continue;
+ }
+
+ let myty = Some(myitem.type_());
+ if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) {
+ // Put `extern crate` and `use` re-exports in the same section.
+ curty = myty;
+ } else if myty != curty {
+ if curty.is_some() {
+ w.write_str("");
+ }
+ curty = myty;
+ let (short, name) = item_ty_to_strs(&myty.unwrap());
+ write!(
+ w,
+ "\n",
+ id = cx.derive_id(short.to_owned()),
+ name = name
+ );
+ }
+
+ match *myitem.kind {
+ clean::ExternCrateItem(ref name, ref src) => {
+ use crate::html::format::anchor;
+
+ match *src {
+ Some(ref src) => write!(
+ w,
+ "{}extern crate {} as {};",
+ myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
+ anchor(myitem.def_id, &*src.as_str(), cx.cache()),
+ name
+ ),
+ None => write!(
+ w,
+ "{}extern crate {};",
+ myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
+ anchor(myitem.def_id, &*name.as_str(), cx.cache())
+ ),
+ }
+ w.write_str(" | ");
+ }
+
+ clean::ImportItem(ref import) => {
+ write!(
+ w,
+ "{}{} | ",
+ myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()),
+ import.print(cx.cache())
+ );
+ }
+
+ _ => {
+ if myitem.name.is_none() {
+ continue;
+ }
+
+ let unsafety_flag = match *myitem.kind {
+ clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
+ if func.header.unsafety == hir::Unsafety::Unsafe =>
+ {
+ "⚠"
+ }
+ _ => "",
+ };
+
+ let stab = myitem.stability_class(cx.tcx());
+ let add = if stab.is_some() { " " } else { "" };
+
+ let doc_value = myitem.doc_value().unwrap_or_default();
+ write!(
+ w,
+ "\
+ {name}{unsafety_flag} | \
+ {stab_tags}{docs} | \
+ ",
+ name = *myitem.name.as_ref().unwrap(),
+ stab_tags = extra_info_tags(myitem, item, cx.tcx()),
+ docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(),
+ class = myitem.type_(),
+ add = add,
+ stab = stab.unwrap_or_else(String::new),
+ unsafety_flag = unsafety_flag,
+ href = item_path(myitem.type_(), &myitem.name.unwrap().as_str()),
+ title = [full_path(cx, myitem), myitem.type_().to_string()]
+ .iter()
+ .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
+ .collect::>()
+ .join(" "),
+ );
+ }
+ }
+ }
+
+ if curty.is_some() {
+ w.write_str(" |
");
+ }
+}
+
+/// Render the stability, deprecation and portability tags that are displayed in the item's summary
+/// at the module level.
+fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
+ let mut tags = String::new();
+
+ fn tag_html(class: &str, title: &str, contents: &str) -> String {
+ format!(r#"{}"#, class, Escape(title), contents)
+ }
+
+ // The trailing space after each tag is to space it properly against the rest of the docs.
+ if let Some(depr) = &item.deprecation(tcx) {
+ let mut message = "Deprecated";
+ if !stability::deprecation_in_effect(
+ depr.is_since_rustc_version,
+ depr.since.map(|s| s.as_str()).as_deref(),
+ ) {
+ message = "Deprecation planned";
+ }
+ tags += &tag_html("deprecated", "", message);
+ }
+
+ // The "rustc_private" crates are permanently unstable so it makes no sense
+ // to render "unstable" everywhere.
+ if item
+ .stability(tcx)
+ .as_ref()
+ .map(|s| s.level.is_unstable() && s.feature != sym::rustc_private)
+ == Some(true)
+ {
+ tags += &tag_html("unstable", "", "Experimental");
+ }
+
+ let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) {
+ (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
+ (cfg, _) => cfg.as_deref().cloned(),
+ };
+
+ debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg);
+ if let Some(ref cfg) = cfg {
+ tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
+ }
+
+ tags
+}
+
+fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) {
+ let header_len = format!(
+ "{}{}{}{}{:#}fn {}{:#}",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ f.header.constness.print_with_space(),
+ f.header.asyncness.print_with_space(),
+ f.header.unsafety.print_with_space(),
+ print_abi_with_space(f.header.abi),
+ it.name.as_ref().unwrap(),
+ f.generics.print(cx.cache())
+ )
+ .len();
+ w.write_str("");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ "{vis}{constness}{asyncness}{unsafety}{abi}fn \
+ {name}{generics}{decl}{spotlight}{where_clause}
",
+ vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ constness = f.header.constness.print_with_space(),
+ asyncness = f.header.asyncness.print_with_space(),
+ unsafety = f.header.unsafety.print_with_space(),
+ abi = print_abi_with_space(f.header.abi),
+ name = it.name.as_ref().unwrap(),
+ generics = f.generics.print(cx.cache()),
+ where_clause =
+ WhereClause { gens: &f.generics, indent: 0, end_newline: true }.print(cx.cache()),
+ decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness }
+ .print(cx.cache()),
+ spotlight = spotlight_decl(&f.decl, cx.cache()),
+ );
+ document(w, cx, it, None)
+}
+
+fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
+ let bounds = bounds(&t.bounds, false, cx.cache());
+ let types = t.items.iter().filter(|m| m.is_associated_type()).collect::>();
+ let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::>();
+ let required = t.items.iter().filter(|m| m.is_ty_method()).collect::>();
+ let provided = t.items.iter().filter(|m| m.is_method()).collect::>();
+
+ // Output the trait definition
+ wrap_into_docblock(w, |w| {
+ w.write_str("");
+ render_attributes(w, it, true);
+ write!(
+ w,
+ "{}{}{}trait {}{}{}",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ t.unsafety.print_with_space(),
+ if t.is_auto { "auto " } else { "" },
+ it.name.as_ref().unwrap(),
+ t.generics.print(cx.cache()),
+ bounds
+ );
+
+ if !t.generics.where_predicates.is_empty() {
+ let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
+ write!(w, "{}", where_.print(cx.cache()));
+ } else {
+ w.write_str(" ");
+ }
+
+ if t.items.is_empty() {
+ w.write_str("{ }");
+ } else {
+ // FIXME: we should be using a derived_id for the Anchors here
+ w.write_str("{\n");
+ for t in &types {
+ render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+ w.write_str(";\n");
+ }
+ if !types.is_empty() && !consts.is_empty() {
+ w.write_str("\n");
+ }
+ for t in &consts {
+ render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+ w.write_str(";\n");
+ }
+ if !consts.is_empty() && !required.is_empty() {
+ w.write_str("\n");
+ }
+ for (pos, m) in required.iter().enumerate() {
+ render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+ w.write_str(";\n");
+
+ if pos < required.len() - 1 {
+ w.write_str("");
+ }
+ }
+ if !required.is_empty() && !provided.is_empty() {
+ w.write_str("\n");
+ }
+ for (pos, m) in provided.iter().enumerate() {
+ render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
+ match *m.kind {
+ clean::MethodItem(ref inner, _)
+ if !inner.generics.where_predicates.is_empty() =>
+ {
+ w.write_str(",\n { ... }\n");
+ }
+ _ => {
+ w.write_str(" { ... }\n");
+ }
+ }
+ if pos < provided.len() - 1 {
+ w.write_str("");
+ }
+ }
+ w.write_str("}");
+ }
+ w.write_str("
")
+ });
+
+ // Trait documentation
+ document(w, cx, it, None);
+
+ fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
+ write!(
+ w,
+ "{2}",
+ id, title, extra_content
+ )
+ }
+
+ fn write_loading_content(w: &mut Buffer, extra_content: &str) {
+ write!(w, "{}Loading content...", extra_content)
+ }
+
+ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) {
+ let name = m.name.as_ref().unwrap();
+ info!("Documenting {} on {:?}", name, t.name);
+ let item_type = m.type_();
+ let id = cx.derive_id(format!("{}.{}", item_type, name));
+ write!(w, "", id = id,);
+ render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
+ w.write_str("
");
+ render_stability_since(w, m, t, cx.tcx());
+ write_srclink(cx, m, w);
+ w.write_str("
");
+ document(w, cx, m, Some(t));
+ }
+
+ if !types.is_empty() {
+ write_small_section_header(
+ w,
+ "associated-types",
+ "Associated Types",
+ "",
+ );
+ for t in types {
+ trait_item(w, cx, t, it);
+ }
+ write_loading_content(w, "
");
+ }
+
+ if !consts.is_empty() {
+ write_small_section_header(
+ w,
+ "associated-const",
+ "Associated Constants",
+ "",
+ );
+ for t in consts {
+ trait_item(w, cx, t, it);
+ }
+ write_loading_content(w, "
");
+ }
+
+ // Output the documentation for each function individually
+ if !required.is_empty() {
+ write_small_section_header(
+ w,
+ "required-methods",
+ "Required methods",
+ "",
+ );
+ for m in required {
+ trait_item(w, cx, m, it);
+ }
+ write_loading_content(w, "
");
+ }
+ if !provided.is_empty() {
+ write_small_section_header(
+ w,
+ "provided-methods",
+ "Provided methods",
+ "",
+ );
+ for m in provided {
+ trait_item(w, cx, m, it);
+ }
+ write_loading_content(w, "
");
+ }
+
+ // If there are methods directly on this trait object, render them here.
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All);
+
+ if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
+ // The DefId is for the first Type found with that name. The bool is
+ // if any Types with the same name but different DefId have been found.
+ let mut implementor_dups: FxHashMap = FxHashMap::default();
+ for implementor in implementors {
+ match implementor.inner_impl().for_ {
+ clean::ResolvedPath { ref path, did, is_generic: false, .. }
+ | clean::BorrowedRef {
+ type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. },
+ ..
+ } => {
+ let &mut (prev_did, ref mut has_duplicates) =
+ implementor_dups.entry(path.last()).or_insert((did, false));
+ if prev_did != did {
+ *has_duplicates = true;
+ }
+ }
+ _ => {}
+ }
+ }
+
+ let (local, foreign) = implementors.iter().partition::, _>(|i| {
+ i.inner_impl()
+ .for_
+ .def_id_full(cx.cache())
+ .map_or(true, |d| cx.cache.paths.contains_key(&d))
+ });
+
+ let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) =
+ local.iter().partition(|i| i.inner_impl().synthetic);
+
+ synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache()));
+ concrete.sort_by(|a, b| compare_impl(a, b, cx.cache()));
+
+ if !foreign.is_empty() {
+ write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
+
+ for implementor in foreign {
+ let assoc_link = AssocItemLink::GotoSource(
+ implementor.impl_item.def_id,
+ &implementor.inner_impl().provided_trait_methods,
+ );
+ render_impl(
+ w,
+ cx,
+ &implementor,
+ it,
+ assoc_link,
+ RenderMode::Normal,
+ implementor.impl_item.stable_since(cx.tcx()).as_deref(),
+ implementor.impl_item.const_stable_since(cx.tcx()).as_deref(),
+ false,
+ None,
+ true,
+ false,
+ &[],
+ );
+ }
+ write_loading_content(w, "");
+ }
+
+ write_small_section_header(
+ w,
+ "implementors",
+ "Implementors",
+ "",
+ );
+ for implementor in concrete {
+ render_implementor(cx, implementor, it, w, &implementor_dups, &[]);
+ }
+ write_loading_content(w, "
");
+
+ if t.is_auto {
+ write_small_section_header(
+ w,
+ "synthetic-implementors",
+ "Auto implementors",
+ "",
+ );
+ for implementor in synthetic {
+ render_implementor(
+ cx,
+ implementor,
+ it,
+ w,
+ &implementor_dups,
+ &collect_paths_for_type(implementor.inner_impl().for_.clone(), &cx.cache),
+ );
+ }
+ write_loading_content(w, "
");
+ }
+ } else {
+ // even without any implementations to write in, we still want the heading and list, so the
+ // implementors javascript file pulled in below has somewhere to write the impls into
+ write_small_section_header(
+ w,
+ "implementors",
+ "Implementors",
+ "",
+ );
+ write_loading_content(w, "
");
+
+ if t.is_auto {
+ write_small_section_header(
+ w,
+ "synthetic-implementors",
+ "Auto implementors",
+ "",
+ );
+ write_loading_content(w, "
");
+ }
+ }
+
+ write!(
+ w,
+ "",
+ root_path = vec![".."; cx.current.len()].join("/"),
+ path = if it.def_id.is_local() {
+ cx.current.join("/")
+ } else {
+ let (ref path, _) = cx.cache.external_paths[&it.def_id];
+ path[..path.len() - 1].join("/")
+ },
+ ty = it.type_(),
+ name = *it.name.as_ref().unwrap()
+ );
+}
+
+fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
+ w.write_str("");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ "trait {}{}{} = {};
",
+ it.name.as_ref().unwrap(),
+ t.generics.print(cx.cache()),
+ WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
+ bounds(&t.bounds, true, cx.cache())
+ );
+
+ document(w, cx, it, None);
+
+ // Render any items associated directly to this alias, as otherwise they
+ // won't be visible anywhere in the docs. It would be nice to also show
+ // associated items from the aliased type (see discussion in #32077), but
+ // we need #14072 to make sense of the generics.
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
+ w.write_str("");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ "type {}{}{where_clause} = impl {bounds};
",
+ it.name.as_ref().unwrap(),
+ t.generics.print(cx.cache()),
+ where_clause =
+ WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
+ bounds = bounds(&t.bounds, false, cx.cache())
+ );
+
+ document(w, cx, it, None);
+
+ // Render any items associated directly to this alias, as otherwise they
+ // won't be visible anywhere in the docs. It would be nice to also show
+ // associated items from the aliased type (see discussion in #32077), but
+ // we need #14072 to make sense of the generics.
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+ w.write_str("");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ "type {}{}{where_clause} = {type_};
",
+ it.name.as_ref().unwrap(),
+ t.generics.print(cx.cache()),
+ where_clause =
+ WhereClause { gens: &t.generics, indent: 0, end_newline: true }.print(cx.cache()),
+ type_ = t.type_.print(cx.cache())
+ );
+
+ document(w, cx, it, None);
+
+ // Render any items associated directly to this alias, as otherwise they
+ // won't be visible anywhere in the docs. It would be nice to also show
+ // associated items from the aliased type (see discussion in #32077), but
+ // we need #14072 to make sense of the generics.
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) {
+ wrap_into_docblock(w, |w| {
+ w.write_str("");
+ render_attributes(w, it, true);
+ render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
+ w.write_str("
")
+ });
+
+ document(w, cx, it, None);
+ let mut fields = s
+ .fields
+ .iter()
+ .filter_map(|f| match *f.kind {
+ clean::StructFieldItem(ref ty) => Some((f, ty)),
+ _ => None,
+ })
+ .peekable();
+ if fields.peek().is_some() {
+ write!(
+ w,
+ "
+ Fields
"
+ );
+ for (field, ty) in fields {
+ let name = field.name.as_ref().expect("union field name");
+ let id = format!("{}.{}", ItemType::StructField, name);
+ write!(
+ w,
+ "\
+ \
+ {name}: {ty}
\
+ ",
+ id = id,
+ name = name,
+ shortty = ItemType::StructField,
+ ty = ty.print(cx.cache())
+ );
+ if let Some(stability_class) = field.stability_class(cx.tcx()) {
+ write!(w, "", stab = stability_class);
+ }
+ document(w, cx, field, Some(it));
+ }
+ }
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) {
+ wrap_into_docblock(w, |w| {
+ w.write_str("");
+ render_attributes(w, it, true);
+ write!(
+ w,
+ "{}enum {}{}{}",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.name.as_ref().unwrap(),
+ e.generics.print(cx.cache()),
+ WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
+ );
+ if e.variants.is_empty() && !e.variants_stripped {
+ w.write_str(" {}");
+ } else {
+ w.write_str(" {\n");
+ for v in &e.variants {
+ w.write_str(" ");
+ let name = v.name.as_ref().unwrap();
+ match *v.kind {
+ clean::VariantItem(ref var) => match var {
+ clean::Variant::CLike => write!(w, "{}", name),
+ clean::Variant::Tuple(ref tys) => {
+ write!(w, "{}(", name);
+ for (i, ty) in tys.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ")
+ }
+ write!(w, "{}", ty.print(cx.cache()));
+ }
+ w.write_str(")");
+ }
+ clean::Variant::Struct(ref s) => {
+ render_struct(w, v, None, s.struct_type, &s.fields, " ", false, cx);
+ }
+ },
+ _ => unreachable!(),
+ }
+ w.write_str(",\n");
+ }
+
+ if e.variants_stripped {
+ w.write_str(" // some variants omitted\n");
+ }
+ w.write_str("}");
+ }
+ w.write_str("
")
+ });
+
+ document(w, cx, it, None);
+ if !e.variants.is_empty() {
+ write!(
+ w,
+ "
+ Variants{}
\n",
+ document_non_exhaustive_header(it)
+ );
+ document_non_exhaustive(w, it);
+ for variant in &e.variants {
+ let id =
+ cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap()));
+ write!(
+ w,
+ "\
+
\
+
{name}",
+ id = id,
+ name = variant.name.as_ref().unwrap()
+ );
+ if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind {
+ w.write_str("(");
+ for (i, ty) in tys.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ");
+ }
+ write!(w, "{}", ty.print(cx.cache()));
+ }
+ w.write_str(")");
+ }
+ w.write_str("
");
+ document(w, cx, variant, Some(it));
+ document_non_exhaustive(w, variant);
+
+ use crate::clean::Variant;
+ if let clean::VariantItem(Variant::Struct(ref s)) = *variant.kind {
+ let variant_id = cx.derive_id(format!(
+ "{}.{}.fields",
+ ItemType::Variant,
+ variant.name.as_ref().unwrap()
+ ));
+ write!(w, "", id = variant_id);
+ write!(
+ w,
+ "
Fields of {name}
",
+ name = variant.name.as_ref().unwrap()
+ );
+ for field in &s.fields {
+ use crate::clean::StructFieldItem;
+ if let StructFieldItem(ref ty) = *field.kind {
+ let id = cx.derive_id(format!(
+ "variant.{}.field.{}",
+ variant.name.as_ref().unwrap(),
+ field.name.as_ref().unwrap()
+ ));
+ write!(
+ w,
+ "
\
+ \
+ {f}: {t}
\
+ ",
+ id = id,
+ f = field.name.as_ref().unwrap(),
+ t = ty.print(cx.cache())
+ );
+ document(w, cx, field, Some(variant));
+ }
+ }
+ w.write_str("
");
+ }
+ render_stability_since(w, variant, it, cx.tcx());
+ }
+ }
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) {
+ wrap_into_docblock(w, |w| {
+ highlight::render_with_highlighting(
+ &t.source,
+ w,
+ Some("macro"),
+ None,
+ None,
+ it.source.span().edition(),
+ );
+ });
+ document(w, cx, it, None)
+}
+
+fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) {
+ let name = it.name.as_ref().expect("proc-macros always have names");
+ match m.kind {
+ MacroKind::Bang => {
+ w.push_str("");
+ write!(w, "{}!() {{ /* proc-macro */ }}", name);
+ w.push_str("
");
+ }
+ MacroKind::Attr => {
+ w.push_str("");
+ write!(w, "#[{}]", name);
+ w.push_str("
");
+ }
+ MacroKind::Derive => {
+ w.push_str("");
+ write!(w, "#[derive({})]", name);
+ if !m.helpers.is_empty() {
+ w.push_str("\n{\n");
+ w.push_str(" // Attributes available to this derive:\n");
+ for attr in &m.helpers {
+ writeln!(w, " #[{}]", attr);
+ }
+ w.push_str("}\n");
+ }
+ w.push_str("
");
+ }
+ }
+ document(w, cx, it, None)
+}
+
+fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
+ document(w, cx, it, None);
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) {
+ w.write_str("");
+ render_attributes(w, it, false);
+
+ write!(
+ w,
+ "{vis}const {name}: {typ}",
+ vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ name = it.name.as_ref().unwrap(),
+ typ = c.type_.print(cx.cache()),
+ );
+
+ if c.value.is_some() || c.is_literal {
+ write!(w, " = {expr};", expr = Escape(&c.expr));
+ } else {
+ w.write_str(";");
+ }
+
+ if let Some(value) = &c.value {
+ if !c.is_literal {
+ let value_lowercase = value.to_lowercase();
+ let expr_lowercase = c.expr.to_lowercase();
+
+ if value_lowercase != expr_lowercase
+ && value_lowercase.trim_end_matches("i32") != expr_lowercase
+ {
+ write!(w, " // {value}", value = Escape(value));
+ }
+ }
+ }
+
+ w.write_str("
");
+ document(w, cx, it, None)
+}
+
+fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) {
+ wrap_into_docblock(w, |w| {
+ w.write_str("");
+ render_attributes(w, it, true);
+ render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
+ w.write_str("
")
+ });
+
+ document(w, cx, it, None);
+ let mut fields = s
+ .fields
+ .iter()
+ .filter_map(|f| match *f.kind {
+ clean::StructFieldItem(ref ty) => Some((f, ty)),
+ _ => None,
+ })
+ .peekable();
+ if let CtorKind::Fictive = s.struct_type {
+ if fields.peek().is_some() {
+ write!(
+ w,
+ "
+ Fields{}
",
+ document_non_exhaustive_header(it)
+ );
+ document_non_exhaustive(w, it);
+ for (field, ty) in fields {
+ let id = cx.derive_id(format!(
+ "{}.{}",
+ ItemType::StructField,
+ field.name.as_ref().unwrap()
+ ));
+ write!(
+ w,
+ "\
+ \
+ {name}: {ty}
\
+ ",
+ item_type = ItemType::StructField,
+ id = id,
+ name = field.name.as_ref().unwrap(),
+ ty = ty.print(cx.cache())
+ );
+ document(w, cx, field, Some(it));
+ }
+ }
+ }
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) {
+ w.write_str("");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ "{vis}static {mutability}{name}: {typ}
",
+ vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ mutability = s.mutability.print_with_space(),
+ name = it.name.as_ref().unwrap(),
+ typ = s.type_.print(cx.cache())
+ );
+ document(w, cx, it, None)
+}
+
+fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
+ w.write_str("extern {\n");
+ render_attributes(w, it, false);
+ write!(
+ w,
+ " {}type {};\n}}
",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ it.name.as_ref().unwrap(),
+ );
+
+ document(w, cx, it, None);
+
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
+ document(w, cx, it, None)
+}
+
+/// Compare two strings treating multi-digit numbers as single units (i.e. natural sort order).
+crate fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering {
+ /// Takes a non-numeric and a numeric part from the given &str.
+ fn take_parts<'a>(s: &mut &'a str) -> (&'a str, &'a str) {
+ let i = s.find(|c: char| c.is_ascii_digit());
+ let (a, b) = s.split_at(i.unwrap_or(s.len()));
+ let i = b.find(|c: char| !c.is_ascii_digit());
+ let (b, c) = b.split_at(i.unwrap_or(b.len()));
+ *s = c;
+ (a, b)
+ }
+
+ while !lhs.is_empty() || !rhs.is_empty() {
+ let (la, lb) = take_parts(&mut lhs);
+ let (ra, rb) = take_parts(&mut rhs);
+ // First process the non-numeric part.
+ match la.cmp(ra) {
+ Ordering::Equal => (),
+ x => return x,
+ }
+ // Then process the numeric part, if both sides have one (and they fit in a u64).
+ if let (Ok(ln), Ok(rn)) = (lb.parse::(), rb.parse::()) {
+ match ln.cmp(&rn) {
+ Ordering::Equal => (),
+ x => return x,
+ }
+ }
+ // Then process the numeric part again, but this time as strings.
+ match lb.cmp(rb) {
+ Ordering::Equal => (),
+ x => return x,
+ }
+ }
+
+ Ordering::Equal
+}
+
+pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
+ let mut s = cx.current.join("::");
+ s.push_str("::");
+ s.push_str(&item.name.unwrap().as_str());
+ s
+}
+
+pub(super) fn item_path(ty: ItemType, name: &str) -> String {
+ match ty {
+ ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
+ _ => format!("{}.{}.html", ty, name),
+ }
+}
+
+fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cache: &Cache) -> String {
+ let mut bounds = String::new();
+ if !t_bounds.is_empty() {
+ if !trait_alias {
+ bounds.push_str(": ");
+ }
+ for (i, p) in t_bounds.iter().enumerate() {
+ if i > 0 {
+ bounds.push_str(" + ");
+ }
+ bounds.push_str(&p.print(cache).to_string());
+ }
+ }
+ bounds
+}
+
+fn wrap_into_docblock(w: &mut Buffer, f: F)
+where
+ F: FnOnce(&mut Buffer),
+{
+ w.write_str("");
+ f(w);
+ w.write_str("
")
+}
+
+fn render_stability_since(
+ w: &mut Buffer,
+ item: &clean::Item,
+ containing_item: &clean::Item,
+ tcx: TyCtxt<'_>,
+) {
+ render_stability_since_raw(
+ w,
+ item.stable_since(tcx).as_deref(),
+ item.const_stable_since(tcx).as_deref(),
+ containing_item.stable_since(tcx).as_deref(),
+ containing_item.const_stable_since(tcx).as_deref(),
+ )
+}
+
+fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cache: &Cache) -> Ordering {
+ let lhs = format!("{}", lhs.inner_impl().print(cache, false));
+ let rhs = format!("{}", rhs.inner_impl().print(cache, false));
+
+ // lhs and rhs are formatted as HTML, which may be unnecessary
+ compare_names(&lhs, &rhs)
+}
+
+fn render_implementor(
+ cx: &Context<'_>,
+ implementor: &Impl,
+ trait_: &clean::Item,
+ w: &mut Buffer,
+ implementor_dups: &FxHashMap,
+ aliases: &[String],
+) {
+ // If there's already another implementor that has the same abbridged name, use the
+ // full path, for example in `std::iter::ExactSizeIterator`
+ let use_absolute = match implementor.inner_impl().for_ {
+ clean::ResolvedPath { ref path, is_generic: false, .. }
+ | clean::BorrowedRef {
+ type_: box clean::ResolvedPath { ref path, is_generic: false, .. },
+ ..
+ } => implementor_dups[&path.last()].1,
+ _ => false,
+ };
+ render_impl(
+ w,
+ cx,
+ implementor,
+ trait_,
+ AssocItemLink::Anchor(None),
+ RenderMode::Normal,
+ trait_.stable_since(cx.tcx()).as_deref(),
+ trait_.const_stable_since(cx.tcx()).as_deref(),
+ false,
+ Some(use_absolute),
+ false,
+ false,
+ aliases,
+ );
+}
+
+fn render_union(
+ w: &mut Buffer,
+ it: &clean::Item,
+ g: Option<&clean::Generics>,
+ fields: &[clean::Item],
+ tab: &str,
+ structhead: bool,
+ cx: &Context<'_>,
+) {
+ write!(
+ w,
+ "{}{}{}",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ if structhead { "union " } else { "" },
+ it.name.as_ref().unwrap()
+ );
+ if let Some(g) = g {
+ write!(w, "{}", g.print(cx.cache()));
+ write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache()));
+ }
+
+ write!(w, " {{\n{}", tab);
+ for field in fields {
+ if let clean::StructFieldItem(ref ty) = *field.kind {
+ write!(
+ w,
+ " {}{}: {},\n{}",
+ field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+ field.name.as_ref().unwrap(),
+ ty.print(cx.cache()),
+ tab
+ );
+ }
+ }
+
+ if it.has_stripped_fields().unwrap() {
+ write!(w, " // some fields omitted\n{}", tab);
+ }
+ w.write_str("}");
+}
+
+fn render_struct(
+ w: &mut Buffer,
+ it: &clean::Item,
+ g: Option<&clean::Generics>,
+ ty: CtorKind,
+ fields: &[clean::Item],
+ tab: &str,
+ structhead: bool,
+ cx: &Context<'_>,
+) {
+ write!(
+ w,
+ "{}{}{}",
+ it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()),
+ if structhead { "struct " } else { "" },
+ it.name.as_ref().unwrap()
+ );
+ if let Some(g) = g {
+ write!(w, "{}", g.print(cx.cache()))
+ }
+ match ty {
+ CtorKind::Fictive => {
+ if let Some(g) = g {
+ write!(
+ w,
+ "{}",
+ WhereClause { gens: g, indent: 0, end_newline: true }.print(cx.cache())
+ )
+ }
+ let mut has_visible_fields = false;
+ w.write_str(" {");
+ for field in fields {
+ if let clean::StructFieldItem(ref ty) = *field.kind {
+ write!(
+ w,
+ "\n{} {}{}: {},",
+ tab,
+ field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+ field.name.as_ref().unwrap(),
+ ty.print(cx.cache())
+ );
+ has_visible_fields = true;
+ }
+ }
+
+ if has_visible_fields {
+ if it.has_stripped_fields().unwrap() {
+ write!(w, "\n{} // some fields omitted", tab);
+ }
+ write!(w, "\n{}", tab);
+ } else if it.has_stripped_fields().unwrap() {
+ // If there are no visible fields we can just display
+ // `{ /* fields omitted */ }` to save space.
+ write!(w, " /* fields omitted */ ");
+ }
+ w.write_str("}");
+ }
+ CtorKind::Fn => {
+ w.write_str("(");
+ for (i, field) in fields.iter().enumerate() {
+ if i > 0 {
+ w.write_str(", ");
+ }
+ match *field.kind {
+ clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
+ clean::StructFieldItem(ref ty) => {
+ write!(
+ w,
+ "{}{}",
+ field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()),
+ ty.print(cx.cache())
+ )
+ }
+ _ => unreachable!(),
+ }
+ }
+ w.write_str(")");
+ if let Some(g) = g {
+ write!(
+ w,
+ "{}",
+ WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
+ )
+ }
+ w.write_str(";");
+ }
+ CtorKind::Const => {
+ // Needed for PhantomData.
+ if let Some(g) = g {
+ write!(
+ w,
+ "{}",
+ WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache())
+ )
+ }
+ w.write_str(";");
+ }
+ }
+}
+
+fn document_non_exhaustive_header(item: &clean::Item) -> &str {
+ if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
+}
+
+fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
+ if item.is_non_exhaustive() {
+ write!(w, "", {
+ if item.is_struct() {
+ "struct"
+ } else if item.is_enum() {
+ "enum"
+ } else if item.is_variant() {
+ "variant"
+ } else {
+ "type"
+ }
+ });
+
+ if item.is_struct() {
+ w.write_str(
+ "Non-exhaustive structs could have additional fields added in future. \
+ Therefore, non-exhaustive structs cannot be constructed in external crates \
+ using the traditional Struct {{ .. }}
syntax; cannot be \
+ matched against without a wildcard ..
; and \
+ struct update syntax will not work.",
+ );
+ } else if item.is_enum() {
+ w.write_str(
+ "Non-exhaustive enums could have additional variants added in future. \
+ Therefore, when matching against variants of non-exhaustive enums, an \
+ extra wildcard arm must be added to account for any future variants.",
+ );
+ } else if item.is_variant() {
+ w.write_str(
+ "Non-exhaustive enum variants could have additional fields added in future. \
+ Therefore, non-exhaustive enum variants cannot be constructed in external \
+ crates and cannot be matched against.",
+ );
+ } else {
+ w.write_str(
+ "This type will require a wildcard arm in any match statements or constructors.",
+ );
+ }
+
+ w.write_str("
");
+ }
+}
diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs
index 224c794fb3b4a..3175fbe5666d8 100644
--- a/src/librustdoc/html/render/tests.rs
+++ b/src/librustdoc/html/render/tests.rs
@@ -1,4 +1,7 @@
-use super::*;
+use std::cmp::Ordering;
+
+use super::print_item::compare_names;
+use super::{AllTypes, Buffer};
#[test]
fn test_compare_names() {
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
new file mode 100644
index 0000000000000..cbf0f9a4927c6
--- /dev/null
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -0,0 +1,542 @@
+use std::ffi::OsStr;
+use std::fmt::Write;
+use std::fs::{self, File};
+use std::io::prelude::*;
+use std::io::{self, BufReader};
+use std::path::{Component, Path, PathBuf};
+
+use itertools::Itertools;
+use rustc_data_structures::flock;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use serde::Serialize;
+
+use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS};
+use crate::clean::Crate;
+use crate::config::RenderOptions;
+use crate::docfs::{DocFS, PathError};
+use crate::error::Error;
+use crate::formats::FormatRenderer;
+use crate::html::{layout, static_files};
+
+pub(super) fn write_shared(
+ cx: &Context<'_>,
+ krate: &Crate,
+ search_index: String,
+ options: &RenderOptions,
+) -> Result<(), Error> {
+ // Write out the shared files. Note that these are shared among all rustdoc
+ // docs placed in the output directory, so this needs to be a synchronized
+ // operation with respect to all other rustdocs running around.
+ let lock_file = cx.dst.join(".lock");
+ let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
+
+ // Add all the static files. These may already exist, but we just
+ // overwrite them anyway to make sure that they're fresh and up-to-date.
+
+ write_minify(
+ &cx.shared.fs,
+ cx.path("rustdoc.css"),
+ static_files::RUSTDOC_CSS,
+ options.enable_minification,
+ )?;
+ write_minify(
+ &cx.shared.fs,
+ cx.path("settings.css"),
+ static_files::SETTINGS_CSS,
+ options.enable_minification,
+ )?;
+ write_minify(
+ &cx.shared.fs,
+ cx.path("noscript.css"),
+ static_files::NOSCRIPT_CSS,
+ options.enable_minification,
+ )?;
+
+ // To avoid "light.css" to be overwritten, we'll first run over the received themes and only
+ // then we'll run over the "official" styles.
+ let mut themes: FxHashSet = FxHashSet::default();
+
+ for entry in &cx.shared.style_files {
+ let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
+ let extension =
+ try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
+
+ // Handle the official themes
+ match theme {
+ "light" => write_minify(
+ &cx.shared.fs,
+ cx.path("light.css"),
+ static_files::themes::LIGHT,
+ options.enable_minification,
+ )?,
+ "dark" => write_minify(
+ &cx.shared.fs,
+ cx.path("dark.css"),
+ static_files::themes::DARK,
+ options.enable_minification,
+ )?,
+ "ayu" => write_minify(
+ &cx.shared.fs,
+ cx.path("ayu.css"),
+ static_files::themes::AYU,
+ options.enable_minification,
+ )?,
+ _ => {
+ // Handle added third-party themes
+ let content = try_err!(fs::read(&entry.path), &entry.path);
+ cx.shared
+ .fs
+ .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
+ }
+ };
+
+ themes.insert(theme.to_owned());
+ }
+
+ let write = |p, c| cx.shared.fs.write(p, c);
+ if (*cx.shared).layout.logo.is_empty() {
+ write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?;
+ }
+ if (*cx.shared).layout.favicon.is_empty() {
+ write(cx.path("favicon.svg"), static_files::RUST_FAVICON_SVG)?;
+ write(cx.path("favicon-16x16.png"), static_files::RUST_FAVICON_PNG_16)?;
+ write(cx.path("favicon-32x32.png"), static_files::RUST_FAVICON_PNG_32)?;
+ }
+ write(cx.path("brush.svg"), static_files::BRUSH_SVG)?;
+ write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?;
+ write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?;
+
+ let mut themes: Vec<&String> = themes.iter().collect();
+ themes.sort();
+ // To avoid theme switch latencies as much as possible, we put everything theme related
+ // at the beginning of the html files into another js file.
+ let theme_js = format!(
+ r#"var themes = document.getElementById("theme-choices");
+var themePicker = document.getElementById("theme-picker");
+
+function showThemeButtonState() {{
+ themes.style.display = "block";
+ themePicker.style.borderBottomRightRadius = "0";
+ themePicker.style.borderBottomLeftRadius = "0";
+}}
+
+function hideThemeButtonState() {{
+ themes.style.display = "none";
+ themePicker.style.borderBottomRightRadius = "3px";
+ themePicker.style.borderBottomLeftRadius = "3px";
+}}
+
+function switchThemeButtonState() {{
+ if (themes.style.display === "block") {{
+ hideThemeButtonState();
+ }} else {{
+ showThemeButtonState();
+ }}
+}};
+
+function handleThemeButtonsBlur(e) {{
+ var active = document.activeElement;
+ var related = e.relatedTarget;
+
+ if (active.id !== "theme-picker" &&
+ (!active.parentNode || active.parentNode.id !== "theme-choices") &&
+ (!related ||
+ (related.id !== "theme-picker" &&
+ (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{
+ hideThemeButtonState();
+ }}
+}}
+
+themePicker.onclick = switchThemeButtonState;
+themePicker.onblur = handleThemeButtonsBlur;
+{}.forEach(function(item) {{
+ var but = document.createElement("button");
+ but.textContent = item;
+ but.onclick = function(el) {{
+ switchTheme(currentTheme, mainTheme, item, true);
+ useSystemTheme(false);
+ }};
+ but.onblur = handleThemeButtonsBlur;
+ themes.appendChild(but);
+}});"#,
+ serde_json::to_string(&themes).unwrap()
+ );
+
+ write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?;
+ write_minify(
+ &cx.shared.fs,
+ cx.path("main.js"),
+ static_files::MAIN_JS,
+ options.enable_minification,
+ )?;
+ write_minify(
+ &cx.shared.fs,
+ cx.path("settings.js"),
+ static_files::SETTINGS_JS,
+ options.enable_minification,
+ )?;
+ if cx.shared.include_sources {
+ write_minify(
+ &cx.shared.fs,
+ cx.path("source-script.js"),
+ static_files::sidebar::SOURCE_SCRIPT,
+ options.enable_minification,
+ )?;
+ }
+
+ {
+ write_minify(
+ &cx.shared.fs,
+ cx.path("storage.js"),
+ &format!(
+ "var resourcesSuffix = \"{}\";{}",
+ cx.shared.resource_suffix,
+ static_files::STORAGE_JS
+ ),
+ options.enable_minification,
+ )?;
+ }
+
+ if let Some(ref css) = cx.shared.layout.css_file_extension {
+ let out = cx.path("theme.css");
+ let buffer = try_err!(fs::read_to_string(css), css);
+ if !options.enable_minification {
+ cx.shared.fs.write(&out, &buffer)?;
+ } else {
+ write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?;
+ }
+ }
+ write_minify(
+ &cx.shared.fs,
+ cx.path("normalize.css"),
+ static_files::NORMALIZE_CSS,
+ options.enable_minification,
+ )?;
+ write(cx.dst.join("FiraSans-Regular.woff2"), static_files::fira_sans::REGULAR2)?;
+ write(cx.dst.join("FiraSans-Medium.woff2"), static_files::fira_sans::MEDIUM2)?;
+ write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?;
+ write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?;
+ write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?;
+ write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?;
+ write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?;
+ write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?;
+ write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?;
+ write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?;
+ write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?;
+ write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?;
+ write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?;
+ write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?;
+ write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?;
+
+ fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> {
+ let mut ret = Vec::new();
+ let mut krates = Vec::new();
+
+ if path.exists() {
+ let prefix = format!(r#"{}["{}"]"#, key, krate);
+ for line in BufReader::new(File::open(path)?).lines() {
+ let line = line?;
+ if !line.starts_with(key) {
+ continue;
+ }
+ if line.starts_with(&prefix) {
+ continue;
+ }
+ ret.push(line.to_string());
+ krates.push(
+ line[key.len() + 2..]
+ .split('"')
+ .next()
+ .map(|s| s.to_owned())
+ .unwrap_or_else(String::new),
+ );
+ }
+ }
+ Ok((ret, krates))
+ }
+
+ fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec, Vec)> {
+ let mut ret = Vec::new();
+ let mut krates = Vec::new();
+
+ if path.exists() {
+ let prefix = format!("\"{}\"", krate);
+ for line in BufReader::new(File::open(path)?).lines() {
+ let line = line?;
+ if !line.starts_with('"') {
+ continue;
+ }
+ if line.starts_with(&prefix) {
+ continue;
+ }
+ if line.ends_with(",\\") {
+ ret.push(line[..line.len() - 2].to_string());
+ } else {
+ // Ends with "\\" (it's the case for the last added crate line)
+ ret.push(line[..line.len() - 1].to_string());
+ }
+ krates.push(
+ line.split('"')
+ .find(|s| !s.is_empty())
+ .map(|s| s.to_owned())
+ .unwrap_or_else(String::new),
+ );
+ }
+ }
+ Ok((ret, krates))
+ }
+
+ use std::ffi::OsString;
+
+ #[derive(Debug)]
+ struct Hierarchy {
+ elem: OsString,
+ children: FxHashMap,
+ elems: FxHashSet,
+ }
+
+ impl Hierarchy {
+ fn new(elem: OsString) -> Hierarchy {
+ Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() }
+ }
+
+ fn to_json_string(&self) -> String {
+ let mut subs: Vec<&Hierarchy> = self.children.values().collect();
+ subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem));
+ let mut files = self
+ .elems
+ .iter()
+ .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion")))
+ .collect::>();
+ files.sort_unstable();
+ let subs = subs.iter().map(|s| s.to_json_string()).collect::>().join(",");
+ let dirs =
+ if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) };
+ let files = files.join(",");
+ let files =
+ if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) };
+ format!(
+ "{{\"name\":\"{name}\"{dirs}{files}}}",
+ name = self.elem.to_str().expect("invalid osstring conversion"),
+ dirs = dirs,
+ files = files
+ )
+ }
+ }
+
+ if cx.shared.include_sources {
+ let mut hierarchy = Hierarchy::new(OsString::new());
+ for source in cx
+ .shared
+ .local_sources
+ .iter()
+ .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok())
+ {
+ let mut h = &mut hierarchy;
+ let mut elems = source
+ .components()
+ .filter_map(|s| match s {
+ Component::Normal(s) => Some(s.to_owned()),
+ _ => None,
+ })
+ .peekable();
+ loop {
+ let cur_elem = elems.next().expect("empty file path");
+ if elems.peek().is_none() {
+ h.elems.insert(cur_elem);
+ break;
+ } else {
+ let e = cur_elem.clone();
+ h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e));
+ }
+ }
+ }
+
+ let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix));
+ let (mut all_sources, _krates) =
+ try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst);
+ all_sources.push(format!(
+ "sourcesIndex[\"{}\"] = {};",
+ &krate.name,
+ hierarchy.to_json_string()
+ ));
+ all_sources.sort();
+ let v = format!(
+ "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n",
+ all_sources.join("\n")
+ );
+ cx.shared.fs.write(&dst, v.as_bytes())?;
+ }
+
+ // Update the search index and crate list.
+ let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix));
+ let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst);
+ all_indexes.push(search_index);
+ krates.push(krate.name.to_string());
+ krates.sort();
+
+ // Sort the indexes by crate so the file will be generated identically even
+ // with rustdoc running in parallel.
+ all_indexes.sort();
+ {
+ let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
+ v.push_str(&all_indexes.join(",\\\n"));
+ v.push_str("\\\n}');\ninitSearch(searchIndex);");
+ cx.shared.fs.write(&dst, &v)?;
+ }
+
+ let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix));
+ let crate_list =
+ format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(","));
+ cx.shared.fs.write(&crate_list_dst, &crate_list)?;
+
+ if options.enable_index_page {
+ if let Some(index_page) = options.index_page.clone() {
+ let mut md_opts = options.clone();
+ md_opts.output = cx.dst.clone();
+ md_opts.external_html = (*cx.shared).layout.external_html.clone();
+
+ crate::markdown::render(&index_page, md_opts, cx.shared.edition)
+ .map_err(|e| Error::new(e, &index_page))?;
+ } else {
+ let dst = cx.dst.join("index.html");
+ let page = layout::Page {
+ title: "Index of crates",
+ css_class: "mod",
+ root_path: "./",
+ static_root_path: cx.shared.static_root_path.as_deref(),
+ description: "List of crates",
+ keywords: BASIC_KEYWORDS,
+ resource_suffix: &cx.shared.resource_suffix,
+ extra_scripts: &[],
+ static_extra_scripts: &[],
+ };
+
+ let content = format!(
+ "\
+ List of all crates\
+
",
+ krates
+ .iter()
+ .map(|s| {
+ format!(
+ "{}",
+ ensure_trailing_slash(s),
+ s
+ )
+ })
+ .collect::()
+ );
+ let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
+ cx.shared.fs.write(&dst, v.as_bytes())?;
+ }
+ }
+
+ // Update the list of all implementors for traits
+ let dst = cx.dst.join("implementors");
+ for (&did, imps) in &cx.cache.implementors {
+ // Private modules can leak through to this phase of rustdoc, which
+ // could contain implementations for otherwise private types. In some
+ // rare cases we could find an implementation for an item which wasn't
+ // indexed, so we just skip this step in that case.
+ //
+ // FIXME: this is a vague explanation for why this can't be a `get`, in
+ // theory it should be...
+ let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) {
+ Some(p) => p,
+ None => match cx.cache.external_paths.get(&did) {
+ Some(p) => p,
+ None => continue,
+ },
+ };
+
+ #[derive(Serialize)]
+ struct Implementor {
+ text: String,
+ synthetic: bool,
+ types: Vec,
+ }
+
+ let implementors = imps
+ .iter()
+ .filter_map(|imp| {
+ // If the trait and implementation are in the same crate, then
+ // there's no need to emit information about it (there's inlining
+ // going on). If they're in different crates then the crate defining
+ // the trait will be interested in our implementation.
+ //
+ // If the implementation is from another crate then that crate
+ // should add it.
+ if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() {
+ None
+ } else {
+ Some(Implementor {
+ text: imp.inner_impl().print(cx.cache(), false).to_string(),
+ synthetic: imp.inner_impl().synthetic,
+ types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()),
+ })
+ }
+ })
+ .collect::>();
+
+ // 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() && !cx.cache.paths.contains_key(&did) {
+ continue;
+ }
+
+ let implementors = format!(
+ r#"implementors["{}"] = {};"#,
+ krate.name,
+ serde_json::to_string(&implementors).unwrap()
+ );
+
+ let mut mydst = dst.clone();
+ for part in &remote_path[..remote_path.len() - 1] {
+ mydst.push(part);
+ }
+ cx.shared.ensure_dir(&mydst)?;
+ mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));
+
+ let (mut all_implementors, _) =
+ try_err!(collect(&mydst, &krate.name.as_str(), "implementors"), &mydst);
+ all_implementors.push(implementors);
+ // Sort the implementors by crate so the file will be generated
+ // identically even with rustdoc running in parallel.
+ all_implementors.sort();
+
+ let mut v = String::from("(function() {var implementors = {};\n");
+ for implementor in &all_implementors {
+ writeln!(v, "{}", *implementor).unwrap();
+ }
+ v.push_str(
+ "if (window.register_implementors) {\
+ window.register_implementors(implementors);\
+ } else {\
+ window.pending_implementors = implementors;\
+ }",
+ );
+ v.push_str("})()");
+ cx.shared.fs.write(&mydst, &v)?;
+ }
+ Ok(())
+}
+
+fn write_minify(
+ fs: &DocFS,
+ dst: PathBuf,
+ contents: &str,
+ enable_minification: bool,
+) -> Result<(), Error> {
+ if enable_minification {
+ if dst.extension() == Some(&OsStr::new("css")) {
+ let res = try_none!(minifier::css::minify(contents).ok(), &dst);
+ fs.write(dst, res.as_bytes())
+ } else {
+ fs.write(dst, minifier::js::minify(contents).as_bytes())
+ }
+ } else {
+ fs.write(dst, contents.as_bytes())
+ }
+}
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 81104236314d9..ede9e18a511a8 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -9,8 +9,10 @@ use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString};
+use crate::visit_ast::inherits_doc_hidden;
use rustc_middle::lint::LintLevelSource;
use rustc_session::lint;
+use rustc_span::symbol::sym;
crate const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass {
name: "check-private-items-doc-tests",
@@ -51,23 +53,30 @@ impl crate::doctest::Tester for Tests {
}
crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
- if matches!(
- *item.kind,
- clean::StructFieldItem(_)
- | clean::VariantItem(_)
- | clean::AssocConstItem(_, _)
- | clean::AssocTypeItem(_, _)
- | clean::TypedefItem(_, _)
- | clean::StaticItem(_)
- | clean::ConstantItem(_)
- | clean::ExternCrateItem(_, _)
- | clean::ImportItem(_)
- | clean::PrimitiveItem(_)
- | clean::KeywordItem(_)
- ) {
+ if !cx.cache.access_levels.is_public(item.def_id)
+ || matches!(
+ *item.kind,
+ clean::StructFieldItem(_)
+ | clean::VariantItem(_)
+ | clean::AssocConstItem(_, _)
+ | clean::AssocTypeItem(_, _)
+ | clean::TypedefItem(_, _)
+ | clean::StaticItem(_)
+ | clean::ConstantItem(_)
+ | clean::ExternCrateItem(_, _)
+ | clean::ImportItem(_)
+ | clean::PrimitiveItem(_)
+ | clean::KeywordItem(_)
+ )
+ {
return false;
}
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local());
+ if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
+ || inherits_doc_hidden(cx.tcx, hir_id)
+ {
+ return false;
+ }
let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
}
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 5da7d2f1e9b84..ba6bb359b9135 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -29,6 +29,16 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec {
std::iter::once(crate_name).chain(relative).collect()
}
+crate fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool {
+ while let Some(id) = tcx.hir().get_enclosing_scope(node) {
+ node = id;
+ if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
+ return true;
+ }
+ }
+ false
+}
+
// Also, is there some reason that this doesn't use the 'visit'
// framework from syntax?.
@@ -158,19 +168,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
om: &mut Module<'tcx>,
please_inline: bool,
) -> bool {
- fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool {
- while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) {
- node = id;
- if cx.tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
- return true;
- }
- if node == hir::CRATE_HIR_ID {
- break;
- }
- }
- false
- }
-
debug!("maybe_inline_local res: {:?}", res);
let tcx = self.cx.tcx;
@@ -212,7 +209,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
};
let is_private = !self.cx.cache.access_levels.is_public(res_did);
- let is_hidden = inherits_doc_hidden(self.cx, res_hir_id);
+ let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);
// Only inline if requested or if the item would otherwise be stripped.
if (!please_inline && !is_private && !is_hidden) || is_no_inline {
diff --git a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
new file mode 100644
index 0000000000000..afd65ff6741a6
--- /dev/null
+++ b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
@@ -0,0 +1,18 @@
+// compile-flags: -C panic=abort -C opt-level=0
+
+// Test that `nounwind` atributes are applied to `C-unwind` extern functions when the
+// code is compiled with `panic=abort`. We disable optimizations above to prevent LLVM from
+// inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "C-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make sure that the LLVM attributes for this functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
diff --git a/src/test/codegen/unwind-abis/c-unwind-abi.rs b/src/test/codegen/unwind-abis/c-unwind-abi.rs
new file mode 100644
index 0000000000000..f157653675322
--- /dev/null
+++ b/src/test/codegen/unwind-abis/c-unwind-abi.rs
@@ -0,0 +1,29 @@
+// compile-flags: -C opt-level=0
+
+// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern
+// functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above
+// to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "C" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "C-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs
new file mode 100644
index 0000000000000..ed804ca278d2d
--- /dev/null
+++ b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs
@@ -0,0 +1,32 @@
+// compile-flags: -C opt-level=0
+// ignore-arm stdcall isn't supported
+// ignore-aarch64 stdcall isn't supported
+// ignore-riscv64 stdcall isn't supported
+
+// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind`
+// extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable
+// optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "stdcall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/system-unwind-abi.rs b/src/test/codegen/unwind-abis/system-unwind-abi.rs
new file mode 100644
index 0000000000000..c4d51328352c0
--- /dev/null
+++ b/src/test/codegen/unwind-abis/system-unwind-abi.rs
@@ -0,0 +1,29 @@
+// compile-flags: -C opt-level=0
+
+// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind`
+// extern functions. `system-unwind` functions MUST NOT have this attribute. We disable
+// optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "system" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "system-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs
new file mode 100644
index 0000000000000..aaa63ae55c3ac
--- /dev/null
+++ b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs
@@ -0,0 +1,33 @@
+// compile-flags: -C opt-level=0
+// ignore-arm thiscall isn't supported
+// ignore-aarch64 thiscall isn't supported
+// ignore-riscv64 thiscall isn't supported
+
+// Test that `nounwind` atributes are correctly applied to exported `thiscall` and
+// `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(abi_thiscall)]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "thiscall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile
new file mode 100644
index 0000000000000..a8515c533af57
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/Makefile
@@ -0,0 +1,30 @@
+-include ../tools.mk
+
+all: archive
+ # Compile `main.rs`, which will link into our library, and run it.
+ $(RUSTC) main.rs
+ $(call RUN,main)
+
+ifdef IS_MSVC
+archive: add.o panic.o
+ # Now, create an archive using these two objects.
+ $(AR) crus $(TMPDIR)/add.lib $(TMPDIR)/add.o $(TMPDIR)/panic.o
+else
+archive: add.o panic.o
+ # Now, create an archive using these two objects.
+ $(AR) crus $(TMPDIR)/libadd.a $(TMPDIR)/add.o $(TMPDIR)/panic.o
+endif
+
+# Compile `panic.rs` into an object file.
+#
+# Note that we invoke `rustc` directly, so we may emit an object rather
+# than an archive. We'll do that later.
+panic.o:
+ $(BARE_RUSTC) $(RUSTFLAGS) \
+ --out-dir $(TMPDIR) \
+ --emit=obj panic.rs
+
+# Compile `add.c` into an object file.
+add.o:
+ $(call COMPILE_OBJ,$(TMPDIR)/add.o,add.c)
+
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c
new file mode 100644
index 0000000000000..444359451f6ec
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/add.c
@@ -0,0 +1,12 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+
+// An external function, defined in Rust.
+extern void panic_if_greater_than_10(unsigned x);
+
+unsigned add_small_numbers(unsigned a, unsigned b) {
+ unsigned c = a + b;
+ panic_if_greater_than_10(c);
+ return c;
+}
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs
new file mode 100644
index 0000000000000..78a71219c7811
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/main.rs
@@ -0,0 +1,35 @@
+//! A test for calling `C-unwind` functions across foreign function boundaries.
+//!
+//! This test triggers a panic in a Rust library that our foreign function invokes. This shows
+//! that we can unwind through the C code in that library, and catch the underlying panic.
+#![feature(c_unwind)]
+
+use std::panic::{catch_unwind, AssertUnwindSafe};
+
+fn main() {
+ // Call `add_small_numbers`, passing arguments that will NOT trigger a panic.
+ let (a, b) = (9, 1);
+ let c = unsafe { add_small_numbers(a, b) };
+ assert_eq!(c, 10);
+
+ // Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it.
+ let caught_unwind = catch_unwind(AssertUnwindSafe(|| {
+ let (a, b) = (10, 1);
+ let _c = unsafe { add_small_numbers(a, b) };
+ unreachable!("should have unwound instead of returned");
+ }));
+
+ // Assert that we did indeed panic, then unwrap and downcast the panic into the sum.
+ assert!(caught_unwind.is_err());
+ let panic_obj = caught_unwind.unwrap_err();
+ let msg = panic_obj.downcast_ref::().unwrap();
+ assert_eq!(msg, "11");
+}
+
+#[link(name = "add", kind = "static")]
+extern "C-unwind" {
+ /// An external function, defined in C.
+ ///
+ /// Returns the sum of two numbers, or panics if the sum is greater than 10.
+ fn add_small_numbers(a: u32, b: u32) -> u32;
+}
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs
new file mode 100644
index 0000000000000..a99a04d5c6f4b
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic/panic.rs
@@ -0,0 +1,12 @@
+#![crate_type = "staticlib"]
+#![feature(c_unwind)]
+
+/// This function will panic if `x` is greater than 10.
+///
+/// This function is called by `add_small_numbers`.
+#[no_mangle]
+pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) {
+ if x > 10 {
+ panic!("{}", x); // That is too big!
+ }
+}
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile
new file mode 100644
index 0000000000000..9553b7aeeb983
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all: $(call NATIVE_STATICLIB,add)
+ $(RUSTC) main.rs
+ $(call RUN,main) || exit 1
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c
new file mode 100644
index 0000000000000..444359451f6ec
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/add.c
@@ -0,0 +1,12 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+
+// An external function, defined in Rust.
+extern void panic_if_greater_than_10(unsigned x);
+
+unsigned add_small_numbers(unsigned a, unsigned b) {
+ unsigned c = a + b;
+ panic_if_greater_than_10(c);
+ return c;
+}
diff --git a/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs
new file mode 100644
index 0000000000000..15d38d7216058
--- /dev/null
+++ b/src/test/run-make-fulldeps/c-unwind-abi-catch-panic/main.rs
@@ -0,0 +1,44 @@
+//! A test for calling `C-unwind` functions across foreign function boundaries.
+//!
+//! This test triggers a panic when calling a foreign function that calls *back* into Rust.
+#![feature(c_unwind)]
+
+use std::panic::{catch_unwind, AssertUnwindSafe};
+
+fn main() {
+ // Call `add_small_numbers`, passing arguments that will NOT trigger a panic.
+ let (a, b) = (9, 1);
+ let c = unsafe { add_small_numbers(a, b) };
+ assert_eq!(c, 10);
+
+ // Call `add_small_numbers`, passing arguments that will trigger a panic, and catch it.
+ let caught_unwind = catch_unwind(AssertUnwindSafe(|| {
+ let (a, b) = (10, 1);
+ let _c = unsafe { add_small_numbers(a, b) };
+ unreachable!("should have unwound instead of returned");
+ }));
+
+ // Assert that we did indeed panic, then unwrap and downcast the panic into the sum.
+ assert!(caught_unwind.is_err());
+ let panic_obj = caught_unwind.unwrap_err();
+ let msg = panic_obj.downcast_ref::().unwrap();
+ assert_eq!(msg, "11");
+}
+
+#[link(name = "add", kind = "static")]
+extern "C-unwind" {
+ /// An external function, defined in C.
+ ///
+ /// Returns the sum of two numbers, or panics if the sum is greater than 10.
+ fn add_small_numbers(a: u32, b: u32) -> u32;
+}
+
+/// This function will panic if `x` is greater than 10.
+///
+/// This function is called by `add_small_numbers`.
+#[no_mangle]
+pub extern "C-unwind" fn panic_if_greater_than_10(x: u32) {
+ if x > 10 {
+ panic!("{}", x); // That is too big!
+ }
+}
diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
index 8d727b0d0b550..41e8847792694 100644
--- a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
+++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
@@ -12,16 +12,16 @@
/// ```
/// println!("hello");
/// ```
-fn test() {
+pub fn test() {
}
#[allow(missing_docs)]
-mod module1 { //~ ERROR
+pub mod module1 { //~ ERROR
}
#[allow(rustdoc::missing_doc_code_examples)]
/// doc
-mod module2 {
+pub mod module2 {
/// doc
pub fn test() {}
@@ -63,9 +63,22 @@ pub enum Enum {
/// Doc
//~^ ERROR
#[repr(C)]
-union Union {
+pub union Union {
/// Doc, but no code example and it's fine!
a: i32,
/// Doc, but no code example and it's fine!
b: f32,
}
+
+
+#[doc(hidden)]
+pub mod foo {
+ pub fn bar() {}
+}
+
+fn babar() {}
+
+
+mod fofoo {
+ pub fn tadam() {}
+}
diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
index 370c577f85d8f..3715797854281 100644
--- a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
+++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
@@ -1,7 +1,7 @@
error: missing code example in this documentation
--> $DIR/lint-missing-doc-code-example.rs:19:1
|
-LL | / mod module1 {
+LL | / pub mod module1 {
LL | | }
| |_^
|
diff --git a/src/test/rustdoc/extern-links.rs b/src/test/rustdoc/extern-links.rs
index 991f869138d93..0383ccf7db666 100644
--- a/src/test/rustdoc/extern-links.rs
+++ b/src/test/rustdoc/extern-links.rs
@@ -3,7 +3,7 @@
#![crate_name = "foo"]
-extern crate extern_links;
+pub extern crate extern_links;
// @!has foo/index.html '//a' 'extern_links'
#[doc(no_inline)]
diff --git a/src/test/rustdoc/issue-28927.rs b/src/test/rustdoc/issue-28927.rs
index 7b535f33bf7e3..38a520850b6dd 100644
--- a/src/test/rustdoc/issue-28927.rs
+++ b/src/test/rustdoc/issue-28927.rs
@@ -2,5 +2,5 @@
// aux-build:issue-28927-1.rs
// ignore-cross-compile
-extern crate issue_28927_1 as inner1;
+pub extern crate issue_28927_1 as inner1;
pub use inner1 as foo;
diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs
index 4d40c7d0d2237..c9b4abbfd3fd3 100644
--- a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs
+++ b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs
@@ -7,7 +7,7 @@
// compile-flags:-C panic=abort
// aux-build:helper.rs
-#![feature(start, rustc_private, new_uninit, panic_info_message)]
+#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
#![feature(alloc_error_handler)]
#![no_std]
@@ -84,6 +84,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
}
}
+// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
+// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
+// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// unwind. So, for this test case we will define the symbol.
+#[lang = "eh_personality"]
+extern fn rust_eh_personality() {}
+
#[derive(Debug)]
struct Page([[u64; 32]; 16]);
diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs
index 4f8c44f1763c8..d6cd4a6af855f 100644
--- a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs
+++ b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs
@@ -8,7 +8,7 @@
// aux-build:helper.rs
// gate-test-default_alloc_error_handler
-#![feature(start, rustc_private, new_uninit, panic_info_message)]
+#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
#![feature(default_alloc_error_handler)]
#![no_std]
@@ -71,6 +71,13 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
}
}
+// Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed.
+// However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions
+// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
+// unwind. So, for this test case we will define the symbol.
+#[lang = "eh_personality"]
+extern fn rust_eh_personality() {}
+
#[derive(Debug)]
struct Page([[u64; 32]; 16]);
diff --git a/src/test/ui/attributes/const-stability-on-macro.rs b/src/test/ui/attributes/const-stability-on-macro.rs
new file mode 100644
index 0000000000000..3fc60f7ce48c2
--- /dev/null
+++ b/src/test/ui/attributes/const-stability-on-macro.rs
@@ -0,0 +1,13 @@
+#[rustc_const_stable(feature = "foo", since = "0")]
+//~^ ERROR macros cannot have const stability attributes
+macro_rules! foo {
+ () => {};
+}
+
+#[rustc_const_unstable(feature = "bar", issue="none")]
+//~^ ERROR macros cannot have const stability attributes
+macro_rules! bar {
+ () => {};
+}
+
+fn main() {}
diff --git a/src/test/ui/attributes/const-stability-on-macro.stderr b/src/test/ui/attributes/const-stability-on-macro.stderr
new file mode 100644
index 0000000000000..ef24e44d1908b
--- /dev/null
+++ b/src/test/ui/attributes/const-stability-on-macro.stderr
@@ -0,0 +1,20 @@
+error: macros cannot have const stability attributes
+ --> $DIR/const-stability-on-macro.rs:1:1
+ |
+LL | #[rustc_const_stable(feature = "foo", since = "0")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute
+LL |
+LL | macro_rules! foo {
+ | ---------------- const stability attribute affects this macro
+
+error: macros cannot have const stability attributes
+ --> $DIR/const-stability-on-macro.rs:7:1
+ |
+LL | #[rustc_const_unstable(feature = "bar", issue="none")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute
+LL |
+LL | macro_rules! bar {
+ | ---------------- const stability attribute affects this macro
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/codemap_tests/unicode.stderr b/src/test/ui/codemap_tests/unicode.stderr
index 61c3f4f1c9847..b7ba4fa46d92b 100644
--- a/src/test/ui/codemap_tests/unicode.stderr
+++ b/src/test/ui/codemap_tests/unicode.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `路濫狼á́́`
LL | extern "路濫狼á́́" fn foo() {}
| ^^^^^^^^^ invalid ABI
|
- = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+ = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
error: aborting due to previous error
diff --git a/src/test/ui/deprecation/deprecation-in-staged-api.rs b/src/test/ui/deprecation/deprecation-in-staged-api.rs
index f667de83b56d4..910bfd1b5e48a 100644
--- a/src/test/ui/deprecation/deprecation-in-staged-api.rs
+++ b/src/test/ui/deprecation/deprecation-in-staged-api.rs
@@ -1,8 +1,4 @@
-// #[deprecated] cannot be used in staged API
-
#![feature(staged_api)]
-
#![stable(feature = "stable_test_feature", since = "1.0.0")]
-
-#[deprecated]
-fn main() { } //~ ERROR `#[deprecated]` cannot be used in staged API
+#[deprecated] //~ ERROR `#[deprecated]` cannot be used in staged API
+fn main() {}
diff --git a/src/test/ui/deprecation/deprecation-in-staged-api.stderr b/src/test/ui/deprecation/deprecation-in-staged-api.stderr
index c6881d5a5735f..cf977fa4b7b44 100644
--- a/src/test/ui/deprecation/deprecation-in-staged-api.stderr
+++ b/src/test/ui/deprecation/deprecation-in-staged-api.stderr
@@ -1,8 +1,10 @@
-error: `#[deprecated]` cannot be used in staged API; use `#[rustc_deprecated]` instead
- --> $DIR/deprecation-in-staged-api.rs:8:1
+error: `#[deprecated]` cannot be used in staged API
+ --> $DIR/deprecation-in-staged-api.rs:3:1
|
-LL | fn main() { }
- | ^^^^^^^^^^^^^
+LL | #[deprecated]
+ | ^^^^^^^^^^^^^ use `#[rustc_deprecated]` instead
+LL | fn main() {}
+ | ------------
error: aborting due to previous error
diff --git a/src/test/ui/issues/issue-81584.fixed b/src/test/ui/issues/issue-81584.fixed
new file mode 100644
index 0000000000000..1cad59f1062c6
--- /dev/null
+++ b/src/test/ui/issues/issue-81584.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+fn main() {
+ let _ = vec![vec![0, 1], vec![2]]
+ .into_iter()
+ .map(|y| y.iter().map(|x| x + 1).collect::>())
+ //~^ ERROR cannot return value referencing function parameter `y`
+ .collect::>();
+}
diff --git a/src/test/ui/issues/issue-81584.rs b/src/test/ui/issues/issue-81584.rs
new file mode 100644
index 0000000000000..452288db08bd8
--- /dev/null
+++ b/src/test/ui/issues/issue-81584.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+fn main() {
+ let _ = vec![vec![0, 1], vec![2]]
+ .into_iter()
+ .map(|y| y.iter().map(|x| x + 1))
+ //~^ ERROR cannot return value referencing function parameter `y`
+ .collect::>();
+}
diff --git a/src/test/ui/issues/issue-81584.stderr b/src/test/ui/issues/issue-81584.stderr
new file mode 100644
index 0000000000000..d57f1b778df17
--- /dev/null
+++ b/src/test/ui/issues/issue-81584.stderr
@@ -0,0 +1,14 @@
+error[E0515]: cannot return value referencing function parameter `y`
+ --> $DIR/issue-81584.rs:5:22
+ |
+LL | .map(|y| y.iter().map(|x| x + 1))
+ | -^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | returns a value referencing data owned by the current function
+ | `y` is borrowed here
+ |
+ = help: use `.collect()` to allocate the iterator
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0515`.
diff --git a/src/test/ui/lint/auxiliary/lint_stability_fields.rs b/src/test/ui/lint/auxiliary/lint_stability_fields.rs
index 0efe7686ef76a..3cbb48c4a6be0 100644
--- a/src/test/ui/lint/auxiliary/lint_stability_fields.rs
+++ b/src/test/ui/lint/auxiliary/lint_stability_fields.rs
@@ -3,20 +3,35 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stable {
- #[stable(feature = "rust1", since = "1.0.0")]
- pub inherit: u8, // it's a lie (stable doesn't inherit)
+ pub inherit: u8,
#[unstable(feature = "unstable_test_feature", issue = "none")]
pub override1: u8,
#[rustc_deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "unstable_test_feature", issue = "none")]
pub override2: u8,
+ #[stable(feature = "rust2", since = "2.0.0")]
+ pub override3: u8,
}
#[stable(feature = "rust1", since = "1.0.0")]
-pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8,
+pub struct Stable2(#[stable(feature = "rust2", since = "2.0.0")] pub u8,
#[unstable(feature = "unstable_test_feature", issue = "none")] pub u8,
#[unstable(feature = "unstable_test_feature", issue = "none")]
- #[rustc_deprecated(since = "1.0.0", reason = "text")] pub u8);
+ #[rustc_deprecated(since = "1.0.0", reason = "text")] pub u8,
+ pub u8);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub enum Stable3 {
+ Inherit(u8),
+ InheritOverride(#[stable(feature = "rust2", since = "2.0.0")] u8),
+ #[stable(feature = "rust2", since = "2.0.0")]
+ Override1,
+ #[unstable(feature = "unstable_test_feature", issue = "none")]
+ Override2,
+ #[rustc_deprecated(since = "1.0.0", reason = "text")]
+ #[unstable(feature = "unstable_test_feature", issue = "none")]
+ Override3,
+}
#[unstable(feature = "unstable_test_feature", issue = "none")]
pub struct Unstable {
diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.rs b/src/test/ui/lint/lint-stability-fields-deprecated.rs
index 14c6383806fb8..2024cf15ab0a1 100644
--- a/src/test/ui/lint/lint-stability-fields-deprecated.rs
+++ b/src/test/ui/lint/lint-stability-fields-deprecated.rs
@@ -17,33 +17,38 @@ mod cross_crate {
override1: 2,
override2: 3,
//~^ ERROR use of deprecated field
+ override3: 4,
};
let _ = x.inherit;
let _ = x.override1;
let _ = x.override2;
//~^ ERROR use of deprecated field
+ let _ = x.override3;
let Stable {
inherit: _,
override1: _,
- override2: _
+ override2: _,
//~^ ERROR use of deprecated field
+ override3: _,
} = x;
// all fine
let Stable { .. } = x;
- let x = Stable2(1, 2, 3);
+ let x = Stable2(1, 2, 3, 4);
let _ = x.0;
let _ = x.1;
let _ = x.2;
//~^ ERROR use of deprecated field
+ let _ = x.3;
let Stable2(_,
_,
+ _,
+ //~^ ERROR use of deprecated field
_)
- //~^ ERROR use of deprecated field
= x;
// all fine
let Stable2(..) = x;
diff --git a/src/test/ui/lint/lint-stability-fields-deprecated.stderr b/src/test/ui/lint/lint-stability-fields-deprecated.stderr
index ec786786023b9..151b3e59b9130 100644
--- a/src/test/ui/lint/lint-stability-fields-deprecated.stderr
+++ b/src/test/ui/lint/lint-stability-fields-deprecated.stderr
@@ -1,5 +1,5 @@
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:94:17
+ --> $DIR/lint-stability-fields-deprecated.rs:99:17
|
LL | let x = Deprecated {
| ^^^^^^^^^^
@@ -11,67 +11,67 @@ LL | #![deny(deprecated)]
| ^^^^^^^^^^
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:111:13
+ --> $DIR/lint-stability-fields-deprecated.rs:116:13
|
LL | let Deprecated {
| ^^^^^^^^^^
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:121:13
+ --> $DIR/lint-stability-fields-deprecated.rs:126:13
|
LL | let Deprecated
| ^^^^^^^^^^
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:126:17
+ --> $DIR/lint-stability-fields-deprecated.rs:131:17
|
LL | let x = Deprecated2(1, 2, 3);
| ^^^^^^^^^^^
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:136:13
+ --> $DIR/lint-stability-fields-deprecated.rs:141:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
error: use of deprecated struct `cross_crate::lint_stability_fields::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:145:13
+ --> $DIR/lint-stability-fields-deprecated.rs:150:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
error: use of deprecated struct `this_crate::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:281:17
+ --> $DIR/lint-stability-fields-deprecated.rs:286:17
|
LL | let x = Deprecated {
| ^^^^^^^^^^
error: use of deprecated struct `this_crate::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:298:13
+ --> $DIR/lint-stability-fields-deprecated.rs:303:13
|
LL | let Deprecated {
| ^^^^^^^^^^
error: use of deprecated struct `this_crate::Deprecated`: text
- --> $DIR/lint-stability-fields-deprecated.rs:308:13
+ --> $DIR/lint-stability-fields-deprecated.rs:313:13
|
LL | let Deprecated
| ^^^^^^^^^^
error: use of deprecated tuple struct `this_crate::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:313:17
+ --> $DIR/lint-stability-fields-deprecated.rs:318:17
|
LL | let x = Deprecated2(1, 2, 3);
| ^^^^^^^^^^^
error: use of deprecated tuple struct `this_crate::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:323:13
+ --> $DIR/lint-stability-fields-deprecated.rs:328:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
error: use of deprecated tuple struct `this_crate::Deprecated2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:332:13
+ --> $DIR/lint-stability-fields-deprecated.rs:337:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
@@ -83,295 +83,295 @@ LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:24:17
+ --> $DIR/lint-stability-fields-deprecated.rs:25:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Stable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:30:13
+ --> $DIR/lint-stability-fields-deprecated.rs:32:13
|
-LL | override2: _
+LL | override2: _,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:40:17
+ --> $DIR/lint-stability-fields-deprecated.rs:43:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Stable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:45:20
+ --> $DIR/lint-stability-fields-deprecated.rs:49:20
|
-LL | _)
+LL | _,
| ^
error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:55:13
+ --> $DIR/lint-stability-fields-deprecated.rs:60:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:61:17
+ --> $DIR/lint-stability-fields-deprecated.rs:66:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:67:13
+ --> $DIR/lint-stability-fields-deprecated.rs:72:13
|
LL | override2: _
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:80:17
+ --> $DIR/lint-stability-fields-deprecated.rs:85:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Unstable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:86:14
+ --> $DIR/lint-stability-fields-deprecated.rs:91:14
|
LL | _)
| ^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:96:13
+ --> $DIR/lint-stability-fields-deprecated.rs:101:13
|
LL | inherit: 1,
| ^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:98:13
+ --> $DIR/lint-stability-fields-deprecated.rs:103:13
|
LL | override1: 2,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:100:13
+ --> $DIR/lint-stability-fields-deprecated.rs:105:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:104:17
+ --> $DIR/lint-stability-fields-deprecated.rs:109:17
|
LL | let _ = x.inherit;
| ^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:106:17
+ --> $DIR/lint-stability-fields-deprecated.rs:111:17
|
LL | let _ = x.override1;
| ^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:108:17
+ --> $DIR/lint-stability-fields-deprecated.rs:113:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:113:13
+ --> $DIR/lint-stability-fields-deprecated.rs:118:13
|
LL | inherit: _,
| ^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:115:13
+ --> $DIR/lint-stability-fields-deprecated.rs:120:13
|
LL | override1: _,
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:117:13
+ --> $DIR/lint-stability-fields-deprecated.rs:122:13
|
LL | override2: _
| ^^^^^^^^^^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text
- --> $DIR/lint-stability-fields-deprecated.rs:129:17
+ --> $DIR/lint-stability-fields-deprecated.rs:134:17
|
LL | let _ = x.0;
| ^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:131:17
+ --> $DIR/lint-stability-fields-deprecated.rs:136:17
|
LL | let _ = x.1;
| ^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:133:17
+ --> $DIR/lint-stability-fields-deprecated.rs:138:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::0`: text
- --> $DIR/lint-stability-fields-deprecated.rs:138:14
+ --> $DIR/lint-stability-fields-deprecated.rs:143:14
|
LL | (_,
| ^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:140:14
+ --> $DIR/lint-stability-fields-deprecated.rs:145:14
|
LL | _,
| ^
error: use of deprecated field `cross_crate::lint_stability_fields::Deprecated2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:142:14
+ --> $DIR/lint-stability-fields-deprecated.rs:147:14
|
LL | _)
| ^
error: use of deprecated field `this_crate::Stable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:205:13
+ --> $DIR/lint-stability-fields-deprecated.rs:210:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Stable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:211:17
+ --> $DIR/lint-stability-fields-deprecated.rs:216:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `this_crate::Stable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:217:13
+ --> $DIR/lint-stability-fields-deprecated.rs:222:13
|
LL | override2: _
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Stable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:227:17
+ --> $DIR/lint-stability-fields-deprecated.rs:232:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `this_crate::Stable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:232:20
+ --> $DIR/lint-stability-fields-deprecated.rs:237:20
|
LL | _)
| ^
error: use of deprecated field `this_crate::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:242:13
+ --> $DIR/lint-stability-fields-deprecated.rs:247:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:248:17
+ --> $DIR/lint-stability-fields-deprecated.rs:253:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `this_crate::Unstable::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:254:13
+ --> $DIR/lint-stability-fields-deprecated.rs:259:13
|
LL | override2: _
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Unstable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:267:17
+ --> $DIR/lint-stability-fields-deprecated.rs:272:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `this_crate::Unstable2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:273:14
+ --> $DIR/lint-stability-fields-deprecated.rs:278:14
|
LL | _)
| ^
error: use of deprecated field `this_crate::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:283:13
+ --> $DIR/lint-stability-fields-deprecated.rs:288:13
|
LL | inherit: 1,
| ^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:285:13
+ --> $DIR/lint-stability-fields-deprecated.rs:290:13
|
LL | override1: 2,
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:287:13
+ --> $DIR/lint-stability-fields-deprecated.rs:292:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:291:17
+ --> $DIR/lint-stability-fields-deprecated.rs:296:17
|
LL | let _ = x.inherit;
| ^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:293:17
+ --> $DIR/lint-stability-fields-deprecated.rs:298:17
|
LL | let _ = x.override1;
| ^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:295:17
+ --> $DIR/lint-stability-fields-deprecated.rs:300:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::inherit`: text
- --> $DIR/lint-stability-fields-deprecated.rs:300:13
+ --> $DIR/lint-stability-fields-deprecated.rs:305:13
|
LL | inherit: _,
| ^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:302:13
+ --> $DIR/lint-stability-fields-deprecated.rs:307:13
|
LL | override1: _,
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated::override2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:304:13
+ --> $DIR/lint-stability-fields-deprecated.rs:309:13
|
LL | override2: _
| ^^^^^^^^^^^^
error: use of deprecated field `this_crate::Deprecated2::0`: text
- --> $DIR/lint-stability-fields-deprecated.rs:316:17
+ --> $DIR/lint-stability-fields-deprecated.rs:321:17
|
LL | let _ = x.0;
| ^^^
error: use of deprecated field `this_crate::Deprecated2::1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:318:17
+ --> $DIR/lint-stability-fields-deprecated.rs:323:17
|
LL | let _ = x.1;
| ^^^
error: use of deprecated field `this_crate::Deprecated2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:320:17
+ --> $DIR/lint-stability-fields-deprecated.rs:325:17
|
LL | let _ = x.2;
| ^^^
error: use of deprecated field `this_crate::Deprecated2::0`: text
- --> $DIR/lint-stability-fields-deprecated.rs:325:14
+ --> $DIR/lint-stability-fields-deprecated.rs:330:14
|
LL | (_,
| ^
error: use of deprecated field `this_crate::Deprecated2::1`: text
- --> $DIR/lint-stability-fields-deprecated.rs:327:14
+ --> $DIR/lint-stability-fields-deprecated.rs:332:14
|
LL | _,
| ^
error: use of deprecated field `this_crate::Deprecated2::2`: text
- --> $DIR/lint-stability-fields-deprecated.rs:329:14
+ --> $DIR/lint-stability-fields-deprecated.rs:334:14
|
LL | _)
| ^
diff --git a/src/test/ui/lint/lint-stability-fields.rs b/src/test/ui/lint/lint-stability-fields.rs
index c5de5748aa300..4083648984867 100644
--- a/src/test/ui/lint/lint-stability-fields.rs
+++ b/src/test/ui/lint/lint-stability-fields.rs
@@ -20,29 +20,34 @@ mod cross_crate {
inherit: 1,
override1: 2, //~ ERROR use of unstable
override2: 3, //~ ERROR use of unstable
+ override3: 4,
};
let _ = x.inherit;
let _ = x.override1; //~ ERROR use of unstable
let _ = x.override2; //~ ERROR use of unstable
+ let _ = x.override3;
let Stable {
inherit: _,
override1: _, //~ ERROR use of unstable
- override2: _ //~ ERROR use of unstable
+ override2: _, //~ ERROR use of unstable
+ override3: _
} = x;
// all fine
let Stable { .. } = x;
- let x = Stable2(1, 2, 3);
+ let x = Stable2(1, 2, 3, 4);
let _ = x.0;
let _ = x.1; //~ ERROR use of unstable
let _ = x.2; //~ ERROR use of unstable
+ let _ = x.3;
let Stable2(_,
_, //~ ERROR use of unstable
- _) //~ ERROR use of unstable
+ _, //~ ERROR use of unstable
+ _)
= x;
// all fine
let Stable2(..) = x;
@@ -133,11 +138,13 @@ mod this_crate {
#[rustc_deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "unstable_test_feature", issue = "none")]
override2: u8,
+ #[stable(feature = "rust2", since = "2.0.0")]
+ override3: u8,
}
#[stable(feature = "rust1", since = "1.0.0")]
struct Stable2(u8,
- #[stable(feature = "rust1", since = "1.0.0")] u8,
+ #[stable(feature = "rust2", since = "2.0.0")] u8,
#[unstable(feature = "unstable_test_feature", issue = "none")]
#[rustc_deprecated(since = "1.0.0", reason = "text")] u8);
@@ -178,16 +185,19 @@ mod this_crate {
inherit: 1,
override1: 2,
override2: 3,
+ override3: 4,
};
let _ = x.inherit;
let _ = x.override1;
let _ = x.override2;
+ let _ = x.override3;
let Stable {
inherit: _,
override1: _,
- override2: _
+ override2: _,
+ override3: _
} = x;
// all fine
let Stable { .. } = x;
diff --git a/src/test/ui/lint/lint-stability-fields.stderr b/src/test/ui/lint/lint-stability-fields.stderr
index b6a08186b5fb8..3d2e73c1e8e92 100644
--- a/src/test/ui/lint/lint-stability-fields.stderr
+++ b/src/test/ui/lint/lint-stability-fields.stderr
@@ -1,5 +1,5 @@
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:51:17
+ --> $DIR/lint-stability-fields.rs:56:17
|
LL | let x = Unstable {
| ^^^^^^^^
@@ -7,7 +7,7 @@ LL | let x = Unstable {
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:61:13
+ --> $DIR/lint-stability-fields.rs:66:13
|
LL | let Unstable {
| ^^^^^^^^
@@ -15,7 +15,7 @@ LL | let Unstable {
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:67:13
+ --> $DIR/lint-stability-fields.rs:72:13
|
LL | let Unstable
| ^^^^^^^^
@@ -23,7 +23,7 @@ LL | let Unstable
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:72:17
+ --> $DIR/lint-stability-fields.rs:77:17
|
LL | let x = reexport::Unstable2(1, 2, 3);
| ^^^^^^^^^^^^^^^^^^^
@@ -31,7 +31,7 @@ LL | let x = reexport::Unstable2(1, 2, 3);
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:74:17
+ --> $DIR/lint-stability-fields.rs:79:17
|
LL | let x = Unstable2(1, 2, 3);
| ^^^^^^^^^
@@ -39,7 +39,7 @@ LL | let x = Unstable2(1, 2, 3);
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:80:13
+ --> $DIR/lint-stability-fields.rs:85:13
|
LL | let Unstable2
| ^^^^^^^^^
@@ -47,7 +47,7 @@ LL | let Unstable2
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:85:13
+ --> $DIR/lint-stability-fields.rs:90:13
|
LL | let Unstable2
| ^^^^^^^^^
@@ -55,7 +55,7 @@ LL | let Unstable2
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:90:17
+ --> $DIR/lint-stability-fields.rs:95:17
|
LL | let x = Deprecated {
| ^^^^^^^^^^
@@ -63,7 +63,7 @@ LL | let x = Deprecated {
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:100:13
+ --> $DIR/lint-stability-fields.rs:105:13
|
LL | let Deprecated {
| ^^^^^^^^^^
@@ -71,7 +71,7 @@ LL | let Deprecated {
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:106:13
+ --> $DIR/lint-stability-fields.rs:111:13
|
LL | let Deprecated
| ^^^^^^^^^^
@@ -79,7 +79,7 @@ LL | let Deprecated
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:110:17
+ --> $DIR/lint-stability-fields.rs:115:17
|
LL | let x = Deprecated2(1, 2, 3);
| ^^^^^^^^^^^
@@ -87,7 +87,7 @@ LL | let x = Deprecated2(1, 2, 3);
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:116:13
+ --> $DIR/lint-stability-fields.rs:121:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
@@ -95,7 +95,7 @@ LL | let Deprecated2
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:121:13
+ --> $DIR/lint-stability-fields.rs:126:13
|
LL | let Deprecated2
| ^^^^^^^^^^^
@@ -119,7 +119,7 @@ LL | override2: 3,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:26:17
+ --> $DIR/lint-stability-fields.rs:27:17
|
LL | let _ = x.override1;
| ^^^^^^^^^^^
@@ -127,7 +127,7 @@ LL | let _ = x.override1;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:27:17
+ --> $DIR/lint-stability-fields.rs:28:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
@@ -135,7 +135,7 @@ LL | let _ = x.override2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:31:13
+ --> $DIR/lint-stability-fields.rs:33:13
|
LL | override1: _,
| ^^^^^^^^^^^^
@@ -143,15 +143,15 @@ LL | override1: _,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:32:13
+ --> $DIR/lint-stability-fields.rs:34:13
|
-LL | override2: _
+LL | override2: _,
| ^^^^^^^^^^^^
|
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:40:17
+ --> $DIR/lint-stability-fields.rs:43:17
|
LL | let _ = x.1;
| ^^^
@@ -159,7 +159,7 @@ LL | let _ = x.1;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:41:17
+ --> $DIR/lint-stability-fields.rs:44:17
|
LL | let _ = x.2;
| ^^^
@@ -167,7 +167,7 @@ LL | let _ = x.2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:44:20
+ --> $DIR/lint-stability-fields.rs:48:20
|
LL | _,
| ^
@@ -175,15 +175,15 @@ LL | _,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:45:20
+ --> $DIR/lint-stability-fields.rs:49:20
|
-LL | _)
+LL | _,
| ^
|
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:52:13
+ --> $DIR/lint-stability-fields.rs:57:13
|
LL | inherit: 1,
| ^^^^^^^^^^
@@ -191,7 +191,7 @@ LL | inherit: 1,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:54:13
+ --> $DIR/lint-stability-fields.rs:59:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
@@ -199,7 +199,7 @@ LL | override2: 3,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:57:17
+ --> $DIR/lint-stability-fields.rs:62:17
|
LL | let _ = x.inherit;
| ^^^^^^^^^
@@ -207,7 +207,7 @@ LL | let _ = x.inherit;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:59:17
+ --> $DIR/lint-stability-fields.rs:64:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
@@ -215,7 +215,7 @@ LL | let _ = x.override2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:62:13
+ --> $DIR/lint-stability-fields.rs:67:13
|
LL | inherit: _,
| ^^^^^^^^^^
@@ -223,7 +223,7 @@ LL | inherit: _,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:64:13
+ --> $DIR/lint-stability-fields.rs:69:13
|
LL | override2: _
| ^^^^^^^^^^^^
@@ -231,7 +231,7 @@ LL | override2: _
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:76:17
+ --> $DIR/lint-stability-fields.rs:81:17
|
LL | let _ = x.0;
| ^^^
@@ -239,7 +239,7 @@ LL | let _ = x.0;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:78:17
+ --> $DIR/lint-stability-fields.rs:83:17
|
LL | let _ = x.2;
| ^^^
@@ -247,7 +247,7 @@ LL | let _ = x.2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:81:14
+ --> $DIR/lint-stability-fields.rs:86:14
|
LL | (_,
| ^
@@ -255,7 +255,7 @@ LL | (_,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:83:14
+ --> $DIR/lint-stability-fields.rs:88:14
|
LL | _)
| ^
@@ -263,7 +263,7 @@ LL | _)
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:91:13
+ --> $DIR/lint-stability-fields.rs:96:13
|
LL | inherit: 1,
| ^^^^^^^^^^
@@ -271,7 +271,7 @@ LL | inherit: 1,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:93:13
+ --> $DIR/lint-stability-fields.rs:98:13
|
LL | override2: 3,
| ^^^^^^^^^^^^
@@ -279,7 +279,7 @@ LL | override2: 3,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:96:17
+ --> $DIR/lint-stability-fields.rs:101:17
|
LL | let _ = x.inherit;
| ^^^^^^^^^
@@ -287,7 +287,7 @@ LL | let _ = x.inherit;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:98:17
+ --> $DIR/lint-stability-fields.rs:103:17
|
LL | let _ = x.override2;
| ^^^^^^^^^^^
@@ -295,7 +295,7 @@ LL | let _ = x.override2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:101:13
+ --> $DIR/lint-stability-fields.rs:106:13
|
LL | inherit: _,
| ^^^^^^^^^^
@@ -303,7 +303,7 @@ LL | inherit: _,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:103:13
+ --> $DIR/lint-stability-fields.rs:108:13
|
LL | override2: _
| ^^^^^^^^^^^^
@@ -311,7 +311,7 @@ LL | override2: _
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:112:17
+ --> $DIR/lint-stability-fields.rs:117:17
|
LL | let _ = x.0;
| ^^^
@@ -319,7 +319,7 @@ LL | let _ = x.0;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:114:17
+ --> $DIR/lint-stability-fields.rs:119:17
|
LL | let _ = x.2;
| ^^^
@@ -327,7 +327,7 @@ LL | let _ = x.2;
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:117:14
+ --> $DIR/lint-stability-fields.rs:122:14
|
LL | (_,
| ^
@@ -335,7 +335,7 @@ LL | (_,
= help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
error[E0658]: use of unstable library feature 'unstable_test_feature'
- --> $DIR/lint-stability-fields.rs:119:14
+ --> $DIR/lint-stability-fields.rs:124:14
|
LL | _)
| ^
diff --git a/src/test/ui/lint/noop-method-call.rs b/src/test/ui/lint/noop-method-call.rs
new file mode 100644
index 0000000000000..9870c813572e3
--- /dev/null
+++ b/src/test/ui/lint/noop-method-call.rs
@@ -0,0 +1,54 @@
+// check-pass
+
+#![allow(unused)]
+#![warn(noop_method_call)]
+
+use std::borrow::Borrow;
+use std::ops::Deref;
+
+struct PlainType(T);
+
+#[derive(Clone)]
+struct CloneType(T);
+
+fn main() {
+ let non_clone_type_ref = &PlainType(1u32);
+ let non_clone_type_ref_clone: &PlainType = non_clone_type_ref.clone();
+ //~^ WARNING call to `.clone()` on a reference in this situation does nothing
+
+ let clone_type_ref = &CloneType(1u32);
+ let clone_type_ref_clone: CloneType = clone_type_ref.clone();
+
+ // Calling clone on a double reference doesn't warn since the method call itself
+ // peels the outer reference off
+ let clone_type_ref = &&CloneType(1u32);
+ let clone_type_ref_clone: &CloneType = clone_type_ref.clone();
+
+ let non_deref_type = &PlainType(1u32);
+ let non_deref_type_deref: &PlainType = non_deref_type.deref();
+ //~^ WARNING call to `.deref()` on a reference in this situation does nothing
+
+ // Dereferencing a &&T does not warn since it has collapsed the double reference
+ let non_deref_type = &&PlainType(1u32);
+ let non_deref_type_deref: &PlainType = non_deref_type.deref();
+
+ let non_borrow_type = &PlainType(1u32);
+ let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow();
+ //~^ WARNING call to `.borrow()` on a reference in this situation does nothing
+
+ // Borrowing a &&T does not warn since it has collapsed the double reference
+ let non_borrow_type = &&PlainType(1u32);
+ let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow();
+
+ let xs = ["a", "b", "c"];
+ let _v: Vec<&str> = xs.iter().map(|x| x.clone()).collect(); // ok, but could use `*x` instead
+}
+
+fn generic(non_clone_type: &PlainType) {
+ non_clone_type.clone();
+}
+
+fn non_generic(non_clone_type: &PlainType) {
+ non_clone_type.clone();
+ //~^ WARNING call to `.clone()` on a reference in this situation does nothing
+}
diff --git a/src/test/ui/lint/noop-method-call.stderr b/src/test/ui/lint/noop-method-call.stderr
new file mode 100644
index 0000000000000..7f6f96bf1d142
--- /dev/null
+++ b/src/test/ui/lint/noop-method-call.stderr
@@ -0,0 +1,39 @@
+warning: call to `.clone()` on a reference in this situation does nothing
+ --> $DIR/noop-method-call.rs:16:71
+ |
+LL | let non_clone_type_ref_clone: &PlainType = non_clone_type_ref.clone();
+ | ^^^^^^^^ unnecessary method call
+ |
+note: the lint level is defined here
+ --> $DIR/noop-method-call.rs:4:9
+ |
+LL | #![warn(noop_method_call)]
+ | ^^^^^^^^^^^^^^^^
+ = note: the type `&PlainType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed
+
+warning: call to `.deref()` on a reference in this situation does nothing
+ --> $DIR/noop-method-call.rs:28:63
+ |
+LL | let non_deref_type_deref: &PlainType = non_deref_type.deref();
+ | ^^^^^^^^ unnecessary method call
+ |
+ = note: the type `&PlainType` which `deref` is being called on is the same as the type returned from `deref`, so the method call does not do anything and can be removed
+
+warning: call to `.borrow()` on a reference in this situation does nothing
+ --> $DIR/noop-method-call.rs:36:66
+ |
+LL | let non_borrow_type_borrow: &PlainType = non_borrow_type.borrow();
+ | ^^^^^^^^^ unnecessary method call
+ |
+ = note: the type `&PlainType` which `borrow` is being called on is the same as the type returned from `borrow`, so the method call does not do anything and can be removed
+
+warning: call to `.clone()` on a reference in this situation does nothing
+ --> $DIR/noop-method-call.rs:52:19
+ |
+LL | non_clone_type.clone();
+ | ^^^^^^^^ unnecessary method call
+ |
+ = note: the type `&PlainType` which `clone` is being called on is the same as the type returned from `clone`, so the method call does not do anything and can be removed
+
+warning: 4 warnings emitted
+
diff --git a/src/test/ui/parser/issue-8537.stderr b/src/test/ui/parser/issue-8537.stderr
index 3f63c08021098..85e2b77d867b9 100644
--- a/src/test/ui/parser/issue-8537.stderr
+++ b/src/test/ui/parser/issue-8537.stderr
@@ -4,7 +4,7 @@ error[E0703]: invalid ABI: found `invalid-ab_isize`
LL | "invalid-ab_isize"
| ^^^^^^^^^^^^^^^^^^ invalid ABI
|
- = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+ = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
error: aborting due to previous error
diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs
index e95d6924026ca..dd5cd420fa546 100644
--- a/src/test/ui/pub/pub-reexport-priv-extern-crate.rs
+++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.rs
@@ -1,5 +1,3 @@
-#![allow(unused)]
-
extern crate core;
pub use core as reexported_core; //~ ERROR `core` is private, and cannot be re-exported
//~^ WARN this was previously accepted
@@ -9,16 +7,14 @@ mod foo1 {
}
mod foo2 {
- use foo1::core; //~ ERROR `core` is private, and cannot be re-exported
- //~^ WARN this was previously accepted
+ use foo1::core; //~ ERROR crate import `core` is private
pub mod bar {
extern crate core;
}
}
mod baz {
- pub use foo2::bar::core; //~ ERROR `core` is private, and cannot be re-exported
- //~^ WARN this was previously accepted
+ pub use foo2::bar::core; //~ ERROR crate import `core` is private
}
fn main() {}
diff --git a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr
index 0b44c5a6525f6..e4d73c6475dc4 100644
--- a/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr
+++ b/src/test/ui/pub/pub-reexport-priv-extern-crate.stderr
@@ -1,30 +1,37 @@
-error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub`
- --> $DIR/pub-reexport-priv-extern-crate.rs:4:9
+error[E0603]: crate import `core` is private
+ --> $DIR/pub-reexport-priv-extern-crate.rs:10:15
|
-LL | pub use core as reexported_core;
- | ^^^^^^^^^^^^^^^^^^^^^^^
+LL | use foo1::core;
+ | ^^^^ private crate import
|
- = note: `#[deny(pub_use_of_private_extern_crate)]` on by default
- = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
- = note: for more information, see issue #34537
+note: the crate import `core` is defined here
+ --> $DIR/pub-reexport-priv-extern-crate.rs:6:5
+ |
+LL | extern crate core;
+ | ^^^^^^^^^^^^^^^^^^
-error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub`
- --> $DIR/pub-reexport-priv-extern-crate.rs:12:9
+error[E0603]: crate import `core` is private
+ --> $DIR/pub-reexport-priv-extern-crate.rs:17:24
|
-LL | use foo1::core;
- | ^^^^^^^^^^
+LL | pub use foo2::bar::core;
+ | ^^^^ private crate import
|
- = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
- = note: for more information, see issue #34537
+note: the crate import `core` is defined here
+ --> $DIR/pub-reexport-priv-extern-crate.rs:12:9
+ |
+LL | extern crate core;
+ | ^^^^^^^^^^^^^^^^^^
error: extern crate `core` is private, and cannot be re-exported (error E0365), consider declaring with `pub`
- --> $DIR/pub-reexport-priv-extern-crate.rs:20:13
+ --> $DIR/pub-reexport-priv-extern-crate.rs:2:9
|
-LL | pub use foo2::bar::core;
- | ^^^^^^^^^^^^^^^
+LL | pub use core as reexported_core;
+ | ^^^^^^^^^^^^^^^^^^^^^^^
|
+ = note: `#[deny(pub_use_of_private_extern_crate)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #34537
error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0603`.
diff --git a/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs b/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs
index 0b243bb52119b..3f4fdfd0180ed 100644
--- a/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs
+++ b/src/test/ui/stability-attribute/stability-attribute-issue-43027.rs
@@ -1,10 +1,15 @@
+// check-pass
#![feature(staged_api)]
#![stable(feature = "test", since = "0")]
#[stable(feature = "test", since = "0")]
-pub struct Reverse(pub T); //~ ERROR field has missing stability attribute
+pub struct A(pub T);
+
+#[stable(feature = "test", since = "0")]
+pub struct B(#[stable(feature = "test", since = "0")] pub T);
fn main() {
// Make sure the field is used to fill the stability cache
- Reverse(0).0;
+ A(0).0;
+ B(0).0;
}
diff --git a/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr b/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr
deleted file mode 100644
index 280c72acccb18..0000000000000
--- a/src/test/ui/stability-attribute/stability-attribute-issue-43027.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: field has missing stability attribute
- --> $DIR/stability-attribute-issue-43027.rs:5:23
- |
-LL | pub struct Reverse(pub T);
- | ^^^^^
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.rs b/src/test/ui/stability-attribute/stability-attribute-sanity.rs
index 0c40f8ae1c67e..9f8ecc2628126 100644
--- a/src/test/ui/stability-attribute/stability-attribute-sanity.rs
+++ b/src/test/ui/stability-attribute/stability-attribute-sanity.rs
@@ -57,17 +57,16 @@ fn multiple2() { }
#[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544]
fn multiple3() { }
-#[stable(feature = "a", since = "b")]
+#[stable(feature = "a", since = "b")] //~ ERROR invalid stability version found
#[rustc_deprecated(since = "b", reason = "text")]
#[rustc_deprecated(since = "b", reason = "text")] //~ ERROR multiple deprecated attributes
#[rustc_const_unstable(feature = "c", issue = "none")]
#[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels
pub const fn multiple4() { }
-//~^ ERROR Invalid stability version found
-#[stable(feature = "a", since = "1.0.0")]
+#[stable(feature = "a", since = "1.0.0")] //~ ERROR invalid deprecation version found
#[rustc_deprecated(since = "invalid", reason = "text")]
-fn invalid_deprecation_version() {} //~ ERROR Invalid deprecation version found
+fn invalid_deprecation_version() {}
#[rustc_deprecated(since = "a", reason = "text")]
fn deprecated_without_unstable_or_stable() { }
diff --git a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr
index 03fb80bb90abc..ddf65f3a7eefd 100644
--- a/src/test/ui/stability-attribute/stability-attribute-sanity.stderr
+++ b/src/test/ui/stability-attribute/stability-attribute-sanity.stderr
@@ -96,20 +96,26 @@ error[E0544]: multiple stability levels
LL | #[rustc_const_unstable(feature = "d", issue = "none")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: Invalid stability version found
- --> $DIR/stability-attribute-sanity.rs:65:1
+error: invalid stability version found
+ --> $DIR/stability-attribute-sanity.rs:60:1
|
+LL | #[stable(feature = "a", since = "b")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid stability version
+...
LL | pub const fn multiple4() { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ---------------------------- the stability attribute annotates this item
-error: Invalid deprecation version found
- --> $DIR/stability-attribute-sanity.rs:70:1
+error: invalid deprecation version found
+ --> $DIR/stability-attribute-sanity.rs:67:1
|
+LL | #[stable(feature = "a", since = "1.0.0")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid deprecation version
+LL | #[rustc_deprecated(since = "invalid", reason = "text")]
LL | fn invalid_deprecation_version() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ----------------------------------- the stability attribute annotates this item
error[E0549]: rustc_deprecated attribute must be paired with either stable or unstable attribute
- --> $DIR/stability-attribute-sanity.rs:72:1
+ --> $DIR/stability-attribute-sanity.rs:71:1
|
LL | #[rustc_deprecated(since = "a", reason = "text")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/static/static-reference-to-fn-2.stderr b/src/test/ui/static/static-reference-to-fn-2.stderr
index 028e11a60cef4..ff15884bd445d 100644
--- a/src/test/ui/static/static-reference-to-fn-2.stderr
+++ b/src/test/ui/static/static-reference-to-fn-2.stderr
@@ -40,6 +40,8 @@ LL | | statefn: &id(state1 as StateMachineFunc)
| | ------------------------------ temporary value created here
LL | | }
| |_____^ returns a value referencing data owned by the current function
+ |
+ = help: use `.collect()` to allocate the iterator
error: aborting due to 4 previous errors
diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs
index 24bdf6d669e88..a352d255c782d 100644
--- a/src/test/ui/symbol-names/impl1.rs
+++ b/src/test/ui/symbol-names/impl1.rs
@@ -3,8 +3,8 @@
// revisions: legacy v0
//[legacy]compile-flags: -Z symbol-mangling-version=legacy
//[v0]compile-flags: -Z symbol-mangling-version=v0
-//[legacy]normalize-stderr-32bit: "hee444285569b39c2" -> "SYMBOL_HASH"
-//[legacy]normalize-stderr-64bit: "h310ea0259fc3d32d" -> "SYMBOL_HASH"
+//[legacy]normalize-stderr-32bit: "h13cfe136e83a9196" -> "SYMBOL_HASH"
+//[legacy]normalize-stderr-64bit: "hd949d7797008991f" -> "SYMBOL_HASH"
#![feature(auto_traits, rustc_attrs)]
#![allow(dead_code)]
diff --git a/src/test/ui/underscore-imports/hygiene-2.rs b/src/test/ui/underscore-imports/hygiene-2.rs
index bea61eae6b51a..510d91d0d4624 100644
--- a/src/test/ui/underscore-imports/hygiene-2.rs
+++ b/src/test/ui/underscore-imports/hygiene-2.rs
@@ -29,5 +29,6 @@ m!(y);
fn main() {
use crate::y::*;
+ #[allow(noop_method_call)]
(&()).deref();
}
diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs
new file mode 100644
index 0000000000000..6ff5dbda2d560
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-c-unwind-enabled.rs
@@ -0,0 +1,12 @@
+// Test that the "C-unwind" ABI is feature-gated, and *can* be used when the
+// `c_unwind` feature gate is enabled.
+
+// check-pass
+
+#![feature(c_unwind)]
+
+extern "C-unwind" fn f() {}
+
+fn main() {
+ f();
+}
diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.rs b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs
new file mode 100644
index 0000000000000..f02a368d4e097
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.rs
@@ -0,0 +1,9 @@
+// Test that the "C-unwind" ABI is feature-gated, and cannot be used when the
+// `c_unwind` feature gate is not used.
+
+extern "C-unwind" fn f() {}
+//~^ ERROR C-unwind ABI is experimental and subject to change [E0658]
+
+fn main() {
+ f();
+}
diff --git a/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr
new file mode 100644
index 0000000000000..f4c785a235f67
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-c-unwind.stderr
@@ -0,0 +1,12 @@
+error[E0658]: C-unwind ABI is experimental and subject to change
+ --> $DIR/feature-gate-c-unwind.rs:4:8
+ |
+LL | extern "C-unwind" fn f() {}
+ | ^^^^^^^^^^
+ |
+ = note: see issue #74990 for more information
+ = help: add `#![feature(c_unwind)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs
new file mode 100644
index 0000000000000..7d4dc8c9343f1
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.rs
@@ -0,0 +1,13 @@
+// ignore-arm stdcall isn't supported
+// ignore-aarch64 stdcall isn't supported
+// ignore-riscv64 stdcall isn't supported
+
+// Test that the "stdcall-unwind" ABI is feature-gated, and cannot be used when
+// the `c_unwind` feature gate is not used.
+
+extern "stdcall-unwind" fn f() {}
+//~^ ERROR stdcall-unwind ABI is experimental and subject to change [E0658]
+
+fn main() {
+ f();
+}
diff --git a/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr
new file mode 100644
index 0000000000000..e3d569f464f87
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-stdcall-unwind.stderr
@@ -0,0 +1,12 @@
+error[E0658]: stdcall-unwind ABI is experimental and subject to change
+ --> $DIR/feature-gate-stdcall-unwind.rs:8:8
+ |
+LL | extern "stdcall-unwind" fn f() {}
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #74990 for more information
+ = help: add `#![feature(c_unwind)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.rs b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs
new file mode 100644
index 0000000000000..26c2de4e81767
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.rs
@@ -0,0 +1,9 @@
+// Test that the "system-unwind" ABI is feature-gated, and cannot be used when
+// the `c_unwind` feature gate is not used.
+
+extern "system-unwind" fn f() {}
+//~^ ERROR system-unwind ABI is experimental and subject to change [E0658]
+
+fn main() {
+ f();
+}
diff --git a/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr
new file mode 100644
index 0000000000000..87877336475b4
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-system-unwind.stderr
@@ -0,0 +1,12 @@
+error[E0658]: system-unwind ABI is experimental and subject to change
+ --> $DIR/feature-gate-system-unwind.rs:4:8
+ |
+LL | extern "system-unwind" fn f() {}
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #74990 for more information
+ = help: add `#![feature(c_unwind)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs
new file mode 100644
index 0000000000000..2f4cefccc1967
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.rs
@@ -0,0 +1,13 @@
+// ignore-arm thiscall isn't supported
+// ignore-aarch64 thiscall isn't supported
+// ignore-riscv64 thiscall isn't supported
+
+// Test that the "thiscall-unwind" ABI is feature-gated, and cannot be used when
+// the `c_unwind` feature gate is not used.
+
+extern "thiscall-unwind" fn f() {}
+//~^ ERROR thiscall-unwind ABI is experimental and subject to change [E0658]
+
+fn main() {
+ f();
+}
diff --git a/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr
new file mode 100644
index 0000000000000..b103bb8d56585
--- /dev/null
+++ b/src/test/ui/unwind-abis/feature-gate-thiscall-unwind.stderr
@@ -0,0 +1,12 @@
+error[E0658]: thiscall-unwind ABI is experimental and subject to change
+ --> $DIR/feature-gate-thiscall-unwind.rs:8:8
+ |
+LL | extern "thiscall-unwind" fn f() {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #74990 for more information
+ = help: add `#![feature(c_unwind)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.