mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
Auto merge of #129365 - matthiaskrgr:rollup-ebwx6ya, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #127279 (use old ctx if has same expand environment during decode span) - #127945 (Implement `debug_more_non_exhaustive`) - #128941 ( Improve diagnostic-related lints: `untranslatable_diagnostic` & `diagnostic_outside_of_impl`) - #129070 (Point at explicit `'static` obligations on a trait) - #129187 (bootstrap: fix clean's remove_dir_all implementation) - #129231 (improve submodule updates) - #129264 (Update `library/Cargo.toml` in weekly job) - #129284 (rustdoc: animate the `:target` highlight) - #129302 (compiletest: use `std::fs::remove_dir_all` now that it is available) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
739b1fdb15
7
.github/workflows/dependencies.yml
vendored
7
.github/workflows/dependencies.yml
vendored
@ -64,6 +64,10 @@ jobs:
|
||||
- name: cargo update
|
||||
# Remove first line that always just says "Updating crates.io index"
|
||||
run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
|
||||
- name: cargo update library
|
||||
run: |
|
||||
echo -e "\nlibrary dependencies:" >> cargo_update.log
|
||||
cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
|
||||
- name: cargo update rustbook
|
||||
run: |
|
||||
echo -e "\nrustbook dependencies:" >> cargo_update.log
|
||||
@ -74,6 +78,7 @@ jobs:
|
||||
name: Cargo-lock
|
||||
path: |
|
||||
Cargo.lock
|
||||
library/Cargo.lock
|
||||
src/tools/rustbook/Cargo.lock
|
||||
retention-days: 1
|
||||
- name: upload cargo-update log artifact for use in PR
|
||||
@ -119,7 +124,7 @@ jobs:
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git switch --force-create cargo_update
|
||||
git add ./Cargo.lock ./src/tools/rustbook/Cargo.lock
|
||||
git add ./Cargo.lock ./library/Cargo.lock ./src/tools/rustbook/Cargo.lock
|
||||
git commit --no-verify --file=commit.txt
|
||||
|
||||
- name: push
|
||||
|
@ -747,7 +747,7 @@ fn region_known_to_outlive<'tcx>(
|
||||
region_b: ty::Region<'tcx>,
|
||||
) -> bool {
|
||||
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
|
||||
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP), region_b, region_a);
|
||||
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP, None), region_b, region_a);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,7 +167,7 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
|
||||
}
|
||||
|
||||
fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
|
||||
self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup)
|
||||
self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), sub, sup)
|
||||
}
|
||||
|
||||
fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {
|
||||
|
@ -390,7 +390,7 @@ pub enum SubregionOrigin<'tcx> {
|
||||
|
||||
/// The given region parameter was instantiated with a region
|
||||
/// that must outlive some other region.
|
||||
RelateRegionParamBound(Span),
|
||||
RelateRegionParamBound(Span, Option<Ty<'tcx>>),
|
||||
|
||||
/// Creating a pointer `b` to contents of another reference.
|
||||
Reborrow(Span),
|
||||
@ -859,7 +859,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
) {
|
||||
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
|
||||
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
||||
RelateRegionParamBound(cause.span)
|
||||
RelateRegionParamBound(cause.span, None)
|
||||
});
|
||||
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
|
||||
})
|
||||
@ -1685,7 +1685,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||
Subtype(ref a) => a.span(),
|
||||
RelateObjectBound(a) => a,
|
||||
RelateParamBound(a, ..) => a,
|
||||
RelateRegionParamBound(a) => a,
|
||||
RelateRegionParamBound(a, _) => a,
|
||||
Reborrow(a) => a,
|
||||
ReferenceOutlivesReferent(_, a) => a,
|
||||
CompareImplItemObligation { span, .. } => span,
|
||||
@ -1726,6 +1726,10 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||
SubregionOrigin::AscribeUserTypeProvePredicate(span)
|
||||
}
|
||||
|
||||
traits::ObligationCauseCode::ObjectTypeBound(ty, _reg) => {
|
||||
SubregionOrigin::RelateRegionParamBound(cause.span, Some(ty))
|
||||
}
|
||||
|
||||
_ => default(),
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use rustc_hir::{
|
||||
BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
|
||||
Path, PathSegment, QPath, Ty, TyKind,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty as MiddleTy};
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
@ -415,14 +415,17 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE
|
||||
|
||||
impl LateLintPass<'_> for Diagnostics {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let collect_args_tys_and_spans = |args: &[Expr<'_>], reserve_one_extra: bool| {
|
||||
let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
|
||||
result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
|
||||
result
|
||||
};
|
||||
// Only check function calls and method calls.
|
||||
let (span, def_id, fn_gen_args, call_tys) = match expr.kind {
|
||||
let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
|
||||
ExprKind::Call(callee, args) => {
|
||||
match cx.typeck_results().node_type(callee.hir_id).kind() {
|
||||
&ty::FnDef(def_id, fn_gen_args) => {
|
||||
let call_tys: Vec<_> =
|
||||
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
|
||||
(callee.span, def_id, fn_gen_args, call_tys)
|
||||
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
|
||||
}
|
||||
_ => return, // occurs for fns passed as args
|
||||
}
|
||||
@ -432,38 +435,40 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mut call_tys: Vec<_> =
|
||||
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
|
||||
call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self`
|
||||
(span, def_id, fn_gen_args, call_tys)
|
||||
let mut args = collect_args_tys_and_spans(args, true);
|
||||
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
|
||||
(span, def_id, fn_gen_args, args)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Is the callee marked with `#[rustc_lint_diagnostics]`?
|
||||
let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
|
||||
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
|
||||
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
|
||||
}
|
||||
}
|
||||
|
||||
// Closure: is the type `{D,Subd}iagMessage`?
|
||||
let is_diag_message = |ty: MiddleTy<'_>| {
|
||||
if let Some(adt_def) = ty.ty_adt_def()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
|
||||
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
impl Diagnostics {
|
||||
// Is the type `{D,Subd}iagMessage`?
|
||||
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
|
||||
if let Some(adt_def) = ty.ty_adt_def()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
|
||||
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
|
||||
let mut impl_into_diagnostic_message_params = vec![];
|
||||
fn untranslatable_diagnostic<'cx>(
|
||||
cx: &LateContext<'cx>,
|
||||
def_id: DefId,
|
||||
arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
|
||||
) {
|
||||
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
||||
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
|
||||
for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() {
|
||||
if let ty::Param(p) = param_ty.kind() {
|
||||
if let ty::Param(sig_param) = param_ty.kind() {
|
||||
// It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
|
||||
for pred in predicates.iter() {
|
||||
if let Some(trait_pred) = pred.as_trait_clause()
|
||||
@ -471,27 +476,53 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
&& trait_ref.self_ty() == param_ty // correct predicate for the param?
|
||||
&& cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
|
||||
&& let ty1 = trait_ref.args.type_at(1)
|
||||
&& is_diag_message(ty1)
|
||||
&& Self::is_diag_message(cx, ty1)
|
||||
{
|
||||
impl_into_diagnostic_message_params.push((i, p.name));
|
||||
// Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
|
||||
// with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
|
||||
// `UNTRANSLATABLE_DIAGNOSTIC` lint.
|
||||
let (arg_ty, arg_span) = arg_tys_and_spans[i];
|
||||
|
||||
// Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
|
||||
let is_translatable = Self::is_diag_message(cx, arg_ty)
|
||||
|| matches!(arg_ty.kind(), ty::Param(arg_param) if arg_param.name == sig_param.name);
|
||||
if !is_translatable {
|
||||
cx.emit_span_lint(
|
||||
UNTRANSLATABLE_DIAGNOSTIC,
|
||||
arg_span,
|
||||
UntranslatableDiag,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the callee interesting?
|
||||
if !has_attr && impl_into_diagnostic_message_params.is_empty() {
|
||||
fn diagnostic_outside_of_impl<'cx>(
|
||||
cx: &LateContext<'cx>,
|
||||
span: Span,
|
||||
current_id: HirId,
|
||||
def_id: DefId,
|
||||
fn_gen_args: GenericArgsRef<'cx>,
|
||||
) {
|
||||
// Is the callee marked with `#[rustc_lint_diagnostics]`?
|
||||
let Some(inst) =
|
||||
ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args).ok().flatten()
|
||||
else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics);
|
||||
if !has_attr {
|
||||
return;
|
||||
};
|
||||
|
||||
// Is the parent method marked with `#[rustc_lint_diagnostics]`?
|
||||
let mut parent_has_attr = false;
|
||||
for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
for (hir_id, _parent) in cx.tcx.hir().parent_iter(current_id) {
|
||||
if let Some(owner_did) = hir_id.as_owner()
|
||||
&& cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
|
||||
{
|
||||
parent_has_attr = true;
|
||||
break;
|
||||
// The parent method is marked with `#[rustc_lint_diagnostics]`
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
|
||||
//
|
||||
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
|
||||
if has_attr && !parent_has_attr {
|
||||
let mut is_inside_appropriate_impl = false;
|
||||
for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
debug!(?parent);
|
||||
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
|
||||
&& let Impl { of_trait: Some(of_trait), .. } = impl_
|
||||
&& let Some(def_id) = of_trait.trait_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
|
||||
{
|
||||
is_inside_appropriate_impl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug!(?is_inside_appropriate_impl);
|
||||
if !is_inside_appropriate_impl {
|
||||
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
|
||||
let mut is_inside_appropriate_impl = false;
|
||||
for (_hir_id, parent) in cx.tcx.hir().parent_iter(current_id) {
|
||||
debug!(?parent);
|
||||
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
|
||||
&& let Impl { of_trait: Some(of_trait), .. } = impl_
|
||||
&& let Some(def_id) = of_trait.trait_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
|
||||
{
|
||||
is_inside_appropriate_impl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
|
||||
// with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
|
||||
// `UNTRANSLATABLE_DIAGNOSTIC` lint.
|
||||
for (param_i, param_i_p_name) in impl_into_diagnostic_message_params {
|
||||
// Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
|
||||
let arg_ty = call_tys[param_i];
|
||||
let is_translatable = is_diag_message(arg_ty)
|
||||
|| matches!(arg_ty.kind(), ty::Param(p) if p.name == param_i_p_name);
|
||||
if !is_translatable {
|
||||
cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
|
||||
}
|
||||
debug!(?is_inside_appropriate_impl);
|
||||
if !is_inside_appropriate_impl {
|
||||
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1415,6 +1415,14 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
|
||||
|
||||
// Overwrite the dummy data with our decoded SyntaxContextData
|
||||
HygieneData::with(|hygiene_data| {
|
||||
if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize)
|
||||
&& old.outer_expn == ctxt_data.outer_expn
|
||||
&& old.outer_transparency == ctxt_data.outer_transparency
|
||||
&& old.parent == ctxt_data.parent
|
||||
{
|
||||
ctxt_data = old.clone();
|
||||
}
|
||||
|
||||
let dummy = std::mem::replace(
|
||||
&mut hygiene_data.syntax_context_data[ctxt.as_u32() as usize],
|
||||
ctxt_data,
|
||||
|
@ -11,7 +11,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
|
||||
match &self.error {
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::RelateRegionParamBound(span),
|
||||
SubregionOrigin::RelateRegionParamBound(span, _),
|
||||
Region(Interned(
|
||||
RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: sub_name, .. },
|
||||
|
@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
infer::RelateRegionParamBound(span, _) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
@ -199,7 +199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
note,
|
||||
})
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
infer::RelateRegionParamBound(span, _) => {
|
||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
|
@ -257,7 +257,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
infer::RelateRegionParamBound(span, _) => {
|
||||
RegionOriginNote::Plain {
|
||||
span,
|
||||
msg: fluent::trait_selection_relate_region_param_bound,
|
||||
@ -410,7 +410,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
note,
|
||||
})
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
infer::RelateRegionParamBound(span, ty) => {
|
||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
@ -419,11 +419,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
note_and_explain::PrefixKind::LfParamInstantiatedWith,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let mut alt_span = None;
|
||||
if let Some(ty) = ty
|
||||
&& sub.is_static()
|
||||
&& let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind()
|
||||
&& let Some(def_id) = preds.principal_def_id()
|
||||
{
|
||||
for (clause, span) in
|
||||
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
|
||||
{
|
||||
if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) =
|
||||
clause.kind().skip_binder()
|
||||
&& let ty::Param(param) = a.kind()
|
||||
&& param.name == kw::SelfUpper
|
||||
&& b.is_static()
|
||||
{
|
||||
// Point at explicit `'static` bound on the trait (`trait T: 'static`).
|
||||
alt_span = Some(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
let param_must_outlive = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
alt_span,
|
||||
note_and_explain::PrefixKind::LfParamMustOutlive,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
|
@ -78,7 +78,7 @@ impl fmt::Write for PadAdapter<'_, '_> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }),
|
||||
/// "Foo { bar: 10, baz: \"Hello World\" }",
|
||||
/// r#"Foo { bar: 10, baz: "Hello World" }"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||
@ -125,7 +125,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }),
|
||||
/// "Bar { bar: 10, another: \"Hello World\", nonexistent_field: 1 }",
|
||||
/// r#"Bar { bar: 10, another: "Hello World", nonexistent_field: 1 }"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -237,7 +237,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Bar { bar: 10, baz: "Hello World".to_string() }),
|
||||
/// "Bar { bar: 10, baz: \"Hello World\" }",
|
||||
/// r#"Bar { bar: 10, baz: "Hello World" }"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -280,7 +280,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
||||
/// "Foo(10, \"Hello World\")",
|
||||
/// r#"Foo(10, "Hello World")"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||
@ -322,7 +322,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
||||
/// "Foo(10, \"Hello World\")",
|
||||
/// r#"Foo(10, "Hello World")"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -360,6 +360,51 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some
|
||||
/// other fields that are not shown in the debug representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(debug_more_non_exhaustive)]
|
||||
///
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Foo(i32, String);
|
||||
///
|
||||
/// impl fmt::Debug for Foo {
|
||||
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// fmt.debug_tuple("Foo")
|
||||
/// .field(&self.0)
|
||||
/// .finish_non_exhaustive() // Show that some other field(s) exist.
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(10, "secret!".to_owned())),
|
||||
/// "Foo(10, ..)",
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
|
||||
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
|
||||
self.result = self.result.and_then(|_| {
|
||||
if self.fields > 0 {
|
||||
if self.is_pretty() {
|
||||
let mut slot = None;
|
||||
let mut state = Default::default();
|
||||
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
|
||||
writer.write_str("..\n")?;
|
||||
self.fmt.write_str(")")
|
||||
} else {
|
||||
self.fmt.write_str(", ..)")
|
||||
}
|
||||
} else {
|
||||
self.fmt.write_str("(..)")
|
||||
}
|
||||
});
|
||||
self.result
|
||||
}
|
||||
|
||||
/// Finishes output and returns any error encountered.
|
||||
///
|
||||
/// # Examples
|
||||
@ -381,7 +426,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
||||
/// "Foo(10, \"Hello World\")",
|
||||
/// r#"Foo(10, "Hello World")"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -555,6 +600,56 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Marks the set as non-exhaustive, indicating to the reader that there are some other
|
||||
/// elements that are not shown in the debug representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(debug_more_non_exhaustive)]
|
||||
///
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Foo(Vec<i32>);
|
||||
///
|
||||
/// impl fmt::Debug for Foo {
|
||||
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// // Print at most two elements, abbreviate the rest
|
||||
/// let mut f = fmt.debug_set();
|
||||
/// let mut f = f.entries(self.0.iter().take(2));
|
||||
/// if self.0.len() > 2 {
|
||||
/// f.finish_non_exhaustive()
|
||||
/// } else {
|
||||
/// f.finish()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
|
||||
/// "{1, 2, ..}",
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
|
||||
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
|
||||
self.inner.result = self.inner.result.and_then(|_| {
|
||||
if self.inner.has_fields {
|
||||
if self.inner.is_pretty() {
|
||||
let mut slot = None;
|
||||
let mut state = Default::default();
|
||||
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
|
||||
writer.write_str("..\n")?;
|
||||
self.inner.fmt.write_str("}")
|
||||
} else {
|
||||
self.inner.fmt.write_str(", ..}")
|
||||
}
|
||||
} else {
|
||||
self.inner.fmt.write_str("..}")
|
||||
}
|
||||
});
|
||||
self.inner.result
|
||||
}
|
||||
|
||||
/// Finishes output and returns any error encountered.
|
||||
///
|
||||
/// # Examples
|
||||
@ -699,6 +794,55 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Marks the list as non-exhaustive, indicating to the reader that there are some other
|
||||
/// elements that are not shown in the debug representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(debug_more_non_exhaustive)]
|
||||
///
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Foo(Vec<i32>);
|
||||
///
|
||||
/// impl fmt::Debug for Foo {
|
||||
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// // Print at most two elements, abbreviate the rest
|
||||
/// let mut f = fmt.debug_list();
|
||||
/// let mut f = f.entries(self.0.iter().take(2));
|
||||
/// if self.0.len() > 2 {
|
||||
/// f.finish_non_exhaustive()
|
||||
/// } else {
|
||||
/// f.finish()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
|
||||
/// "[1, 2, ..]",
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
|
||||
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
|
||||
self.inner.result.and_then(|_| {
|
||||
if self.inner.has_fields {
|
||||
if self.inner.is_pretty() {
|
||||
let mut slot = None;
|
||||
let mut state = Default::default();
|
||||
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
|
||||
writer.write_str("..\n")?;
|
||||
self.inner.fmt.write_str("]")
|
||||
} else {
|
||||
self.inner.fmt.write_str(", ..]")
|
||||
}
|
||||
} else {
|
||||
self.inner.fmt.write_str("..]")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Finishes output and returns any error encountered.
|
||||
///
|
||||
/// # Examples
|
||||
@ -750,7 +894,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"A\": 10, \"B\": 11}",
|
||||
/// r#"{"A": 10, "B": 11}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||
@ -790,7 +934,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
|
||||
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -826,7 +970,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
|
||||
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
|
||||
@ -902,7 +1046,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
|
||||
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
|
||||
@ -960,7 +1104,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"A\": 10, \"B\": 11}",
|
||||
/// r#"{"A": 10, "B": 11}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
@ -976,6 +1120,62 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Marks the map as non-exhaustive, indicating to the reader that there are some other
|
||||
/// entries that are not shown in the debug representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(debug_more_non_exhaustive)]
|
||||
///
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Foo(Vec<(String, i32)>);
|
||||
///
|
||||
/// impl fmt::Debug for Foo {
|
||||
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// // Print at most two elements, abbreviate the rest
|
||||
/// let mut f = fmt.debug_map();
|
||||
/// let mut f = f.entries(self.0.iter().take(2).map(|&(ref k, ref v)| (k, v)));
|
||||
/// if self.0.len() > 2 {
|
||||
/// f.finish_non_exhaustive()
|
||||
/// } else {
|
||||
/// f.finish()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![
|
||||
/// ("A".to_string(), 10),
|
||||
/// ("B".to_string(), 11),
|
||||
/// ("C".to_string(), 12),
|
||||
/// ])),
|
||||
/// r#"{"A": 10, "B": 11, ..}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
|
||||
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
|
||||
self.result = self.result.and_then(|_| {
|
||||
assert!(!self.has_key, "attempted to finish a map with a partial entry");
|
||||
|
||||
if self.has_fields {
|
||||
if self.is_pretty() {
|
||||
let mut slot = None;
|
||||
let mut state = Default::default();
|
||||
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
|
||||
writer.write_str("..\n")?;
|
||||
self.fmt.write_str("}")
|
||||
} else {
|
||||
self.fmt.write_str(", ..}")
|
||||
}
|
||||
} else {
|
||||
self.fmt.write_str("..}")
|
||||
}
|
||||
});
|
||||
self.result
|
||||
}
|
||||
|
||||
/// Finishes output and returns any error encountered.
|
||||
///
|
||||
/// # Panics
|
||||
@ -1000,7 +1200,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
||||
/// "{\"A\": 10, \"B\": 11}",
|
||||
/// r#"{"A": 10, "B": 11}"#,
|
||||
/// );
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
|
@ -79,23 +79,23 @@ mod debug_struct {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
"Bar { foo: Foo { bar: true, baz: 10/20 }, hello: \"world\" }",
|
||||
r#"Bar { foo: Foo { bar: true, baz: 10/20 }, hello: "world" }"#,
|
||||
format!("{Bar:?}")
|
||||
);
|
||||
assert_eq!(
|
||||
"Bar {
|
||||
r#"Bar {
|
||||
foo: Foo {
|
||||
bar: true,
|
||||
baz: 10/20,
|
||||
},
|
||||
hello: \"world\",
|
||||
}",
|
||||
hello: "world",
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_only_non_exhaustive() {
|
||||
fn test_empty_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
@ -157,19 +157,19 @@ mod debug_struct {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
"Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: \"world\", .. }",
|
||||
r#"Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: "world", .. }"#,
|
||||
format!("{Bar:?}")
|
||||
);
|
||||
assert_eq!(
|
||||
"Bar {
|
||||
r#"Bar {
|
||||
foo: Foo {
|
||||
bar: true,
|
||||
baz: 10/20,
|
||||
..
|
||||
},
|
||||
hello: \"world\",
|
||||
hello: "world",
|
||||
..
|
||||
}",
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
@ -249,15 +249,89 @@ mod debug_tuple {
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("Bar(Foo(true, 10/20), \"world\")", format!("{Bar:?}"));
|
||||
assert_eq!(r#"Bar(Foo(true, 10/20), "world")"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
"Bar(
|
||||
r#"Bar(
|
||||
Foo(
|
||||
true,
|
||||
10/20,
|
||||
),
|
||||
\"world\",
|
||||
"world",
|
||||
)"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Foo").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("Foo(..)", format!("{Foo:?}"));
|
||||
assert_eq!("Foo(..)", format!("{Foo:#?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_and_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Foo")
|
||||
.field(&true)
|
||||
.field(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("Foo(true, 10/20, ..)", format!("{Foo:?}"));
|
||||
assert_eq!(
|
||||
"Foo(
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
)",
|
||||
format!("{Foo:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Foo")
|
||||
.field(&true)
|
||||
.field(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl fmt::Debug for Bar {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Bar").field(&Foo).field(&"world").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(r#"Bar(Foo(true, 10/20, ..), "world", ..)"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
r#"Bar(
|
||||
Foo(
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
),
|
||||
"world",
|
||||
..
|
||||
)"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
@ -301,11 +375,11 @@ mod debug_map {
|
||||
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
||||
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
|
||||
|
||||
assert_eq!("{\"bar\": true}", format!("{Entry:?}"));
|
||||
assert_eq!(r#"{"bar": true}"#, format!("{Entry:?}"));
|
||||
assert_eq!(
|
||||
"{
|
||||
\"bar\": true,
|
||||
}",
|
||||
r#"{
|
||||
"bar": true,
|
||||
}"#,
|
||||
format!("{Entry:#?}")
|
||||
);
|
||||
}
|
||||
@ -339,12 +413,12 @@ mod debug_map {
|
||||
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
||||
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
|
||||
|
||||
assert_eq!("{\"bar\": true, 10: 10/20}", format!("{Entry:?}"));
|
||||
assert_eq!(r#"{"bar": true, 10: 10/20}"#, format!("{Entry:?}"));
|
||||
assert_eq!(
|
||||
"{
|
||||
\"bar\": true,
|
||||
r#"{
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
}",
|
||||
}"#,
|
||||
format!("{Entry:#?}")
|
||||
);
|
||||
}
|
||||
@ -371,21 +445,20 @@ mod debug_map {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
"{\"foo\": {\"bar\": true, 10: 10/20}, \
|
||||
{\"bar\": true, 10: 10/20}: \"world\"}",
|
||||
r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#,
|
||||
format!("{Bar:?}")
|
||||
);
|
||||
assert_eq!(
|
||||
"{
|
||||
\"foo\": {
|
||||
\"bar\": true,
|
||||
r#"{
|
||||
"foo": {
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
},
|
||||
{
|
||||
\"bar\": true,
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
}: \"world\",
|
||||
}",
|
||||
}: "world",
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
@ -471,6 +544,103 @@ mod debug_map {
|
||||
|
||||
let _ = format!("{Foo:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_map().finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("{..}", format!("{Foo:?}"));
|
||||
assert_eq!("{..}", format!("{Foo:#?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_and_non_exhaustive() {
|
||||
struct Entry;
|
||||
|
||||
impl fmt::Debug for Entry {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_map()
|
||||
.entry(&"bar", &true)
|
||||
.entry(&10, &format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyValue;
|
||||
|
||||
impl fmt::Debug for KeyValue {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_map()
|
||||
.key(&"bar")
|
||||
.value(&true)
|
||||
.key(&10)
|
||||
.value(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
||||
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
|
||||
|
||||
assert_eq!(r#"{"bar": true, 10: 10/20, ..}"#, format!("{Entry:?}"));
|
||||
assert_eq!(
|
||||
r#"{
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
..
|
||||
}"#,
|
||||
format!("{Entry:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_map()
|
||||
.entry(&"bar", &true)
|
||||
.entry(&10, &format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl fmt::Debug for Bar {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_map().entry(&"foo", &Foo).entry(&Foo, &"world").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
r#"{"foo": {"bar": true, 10: 10/20, ..}, {"bar": true, 10: 10/20, ..}: "world", ..}"#,
|
||||
format!("{Bar:?}")
|
||||
);
|
||||
assert_eq!(
|
||||
r#"{
|
||||
"foo": {
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
..
|
||||
},
|
||||
{
|
||||
"bar": true,
|
||||
10: 10/20,
|
||||
..
|
||||
}: "world",
|
||||
..
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod debug_set {
|
||||
@ -547,15 +717,89 @@ mod debug_set {
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("{{true, 10/20}, \"world\"}", format!("{Bar:?}"));
|
||||
assert_eq!(r#"{{true, 10/20}, "world"}"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
"{
|
||||
r#"{
|
||||
{
|
||||
true,
|
||||
10/20,
|
||||
},
|
||||
\"world\",
|
||||
"world",
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_set().finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("{..}", format!("{Foo:?}"));
|
||||
assert_eq!("{..}", format!("{Foo:#?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_and_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_set()
|
||||
.entry(&true)
|
||||
.entry(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("{true, 10/20, ..}", format!("{Foo:?}"));
|
||||
assert_eq!(
|
||||
"{
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
}",
|
||||
format!("{Foo:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_set()
|
||||
.entry(&true)
|
||||
.entry(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl fmt::Debug for Bar {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_set().entry(&Foo).entry(&"world").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(r#"{{true, 10/20, ..}, "world", ..}"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
r#"{
|
||||
{
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
},
|
||||
"world",
|
||||
..
|
||||
}"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
@ -635,15 +879,89 @@ mod debug_list {
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("[[true, 10/20], \"world\"]", format!("{Bar:?}"));
|
||||
assert_eq!(r#"[[true, 10/20], "world"]"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
"[
|
||||
r#"[
|
||||
[
|
||||
true,
|
||||
10/20,
|
||||
],
|
||||
\"world\",
|
||||
"world",
|
||||
]"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_list().finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("[..]", format!("{Foo:?}"));
|
||||
assert_eq!("[..]", format!("{Foo:#?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_list()
|
||||
.entry(&true)
|
||||
.entry(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("[true, 10/20, ..]", format!("{Foo:?}"));
|
||||
assert_eq!(
|
||||
"[
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
]",
|
||||
format!("{Foo:#?}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_non_exhaustive() {
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Debug for Foo {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_list()
|
||||
.entry(&true)
|
||||
.entry(&format_args!("{}/{}", 10, 20))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl fmt::Debug for Bar {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_list().entry(&Foo).entry(&"world").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(r#"[[true, 10/20, ..], "world", ..]"#, format!("{Bar:?}"));
|
||||
assert_eq!(
|
||||
r#"[
|
||||
[
|
||||
true,
|
||||
10/20,
|
||||
..
|
||||
],
|
||||
"world",
|
||||
..
|
||||
]"#,
|
||||
format!("{Bar:#?}")
|
||||
);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
#![feature(core_io_borrowed_buf)]
|
||||
#![feature(core_private_bignum)]
|
||||
#![feature(core_private_diy_float)]
|
||||
#![feature(debug_more_non_exhaustive)]
|
||||
#![feature(dec2flt)]
|
||||
#![feature(duration_constants)]
|
||||
#![feature(duration_constructors)]
|
||||
|
@ -6,7 +6,6 @@
|
||||
//! directory unless the `--all` flag is present.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
|
||||
@ -101,11 +100,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
|
||||
return;
|
||||
}
|
||||
|
||||
rm_rf("tmp".as_ref());
|
||||
remove_dir_recursive("tmp");
|
||||
|
||||
// Clean the entire build directory
|
||||
if all {
|
||||
rm_rf(&build.out);
|
||||
remove_dir_recursive(&build.out);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -136,17 +135,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
|
||||
}
|
||||
|
||||
let path = t!(entry.path().canonicalize());
|
||||
rm_rf(&path);
|
||||
remove_dir_recursive(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_default(build: &Build) {
|
||||
rm_rf(&build.out.join("tmp"));
|
||||
rm_rf(&build.out.join("dist"));
|
||||
rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
|
||||
rm_rf(&build.out.join("bootstrap-shims-dump"));
|
||||
rm_rf(&build.out.join("rustfmt.stamp"));
|
||||
remove_dir_recursive(build.out.join("tmp"));
|
||||
remove_dir_recursive(build.out.join("dist"));
|
||||
remove_dir_recursive(build.out.join("bootstrap").join(".last-warned-change-id"));
|
||||
remove_dir_recursive(build.out.join("bootstrap-shims-dump"));
|
||||
remove_dir_recursive(build.out.join("rustfmt.stamp"));
|
||||
|
||||
let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect();
|
||||
// After cross-compilation, artifacts of the host architecture (which may differ from build.host)
|
||||
@ -166,78 +165,16 @@ fn clean_default(build: &Build) {
|
||||
continue;
|
||||
}
|
||||
let path = t!(entry.path().canonicalize());
|
||||
rm_rf(&path);
|
||||
remove_dir_recursive(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rm_rf(path: &Path) {
|
||||
match path.symlink_metadata() {
|
||||
Err(e) => {
|
||||
if e.kind() == ErrorKind::NotFound {
|
||||
return;
|
||||
}
|
||||
panic!("failed to get metadata for file {}: {}", path.display(), e);
|
||||
}
|
||||
Ok(metadata) => {
|
||||
if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
|
||||
do_op(path, "remove file", |p| match fs::remove_file(p) {
|
||||
#[cfg(windows)]
|
||||
Err(e)
|
||||
if e.kind() == std::io::ErrorKind::PermissionDenied
|
||||
&& p.file_name().and_then(std::ffi::OsStr::to_str)
|
||||
== Some("bootstrap.exe") =>
|
||||
{
|
||||
eprintln!("WARNING: failed to delete '{}'.", p.display());
|
||||
Ok(())
|
||||
}
|
||||
r => r,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for file in t!(fs::read_dir(path)) {
|
||||
rm_rf(&t!(file).path());
|
||||
}
|
||||
|
||||
do_op(path, "remove dir", |p| match fs::remove_dir(p) {
|
||||
// Check for dir not empty on Windows
|
||||
// FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
|
||||
// match on `e.kind()` instead.
|
||||
#[cfg(windows)]
|
||||
Err(e) if e.raw_os_error() == Some(145) => Ok(()),
|
||||
r => r,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn do_op<F>(path: &Path, desc: &str, mut f: F)
|
||||
where
|
||||
F: FnMut(&Path) -> io::Result<()>,
|
||||
{
|
||||
match f(path) {
|
||||
Ok(()) => {}
|
||||
// On windows we can't remove a readonly file, and git will often clone files as readonly.
|
||||
// As a result, we have some special logic to remove readonly files on windows.
|
||||
// This is also the reason that we can't use things like fs::remove_dir_all().
|
||||
#[cfg(windows)]
|
||||
Err(ref e) if e.kind() == ErrorKind::PermissionDenied => {
|
||||
let m = t!(path.symlink_metadata());
|
||||
let mut p = m.permissions();
|
||||
p.set_readonly(false);
|
||||
t!(fs::set_permissions(path, p));
|
||||
f(path).unwrap_or_else(|e| {
|
||||
// Delete symlinked directories on Windows
|
||||
if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
|
||||
return;
|
||||
}
|
||||
panic!("failed to {} {}: {}", desc, path.display(), e);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("failed to {} {}: {}", desc, path.display(), e);
|
||||
}
|
||||
/// Wrapper for [`std::fs::remove_dir_all`] that panics on failure and prints the `path` we failed
|
||||
/// on.
|
||||
fn remove_dir_recursive<P: AsRef<Path>>(path: P) {
|
||||
let path = path.as_ref();
|
||||
if let Err(e) = fs::remove_dir_all(path) {
|
||||
panic!("failed to `remove_dir_all` at `{}`: {e}", path.display());
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
|
||||
|
||||
// Initialize the llvm submodule if not initialized already.
|
||||
// If submodules are disabled, this does nothing.
|
||||
builder.update_submodule("src/llvm-project");
|
||||
builder.config.update_submodule("src/llvm-project");
|
||||
|
||||
let root = "src/llvm-project/llvm";
|
||||
let out_dir = builder.llvm_out(target);
|
||||
|
@ -14,7 +14,7 @@ use std::sync::OnceLock;
|
||||
use std::{cmp, env, fs};
|
||||
|
||||
use build_helper::exit;
|
||||
use build_helper::git::GitConfig;
|
||||
use build_helper::git::{output_result, GitConfig};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
@ -2509,6 +2509,123 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a path to the directory of a submodule, update it.
|
||||
///
|
||||
/// `relative_path` should be relative to the root of the git repository, not an absolute path.
|
||||
///
|
||||
/// This *does not* update the submodule if `config.toml` explicitly says
|
||||
/// not to, or if we're not in a git repository (like a plain source
|
||||
/// tarball). Typically [`crate::Build::require_submodule`] should be
|
||||
/// used instead to provide a nice error to the user if the submodule is
|
||||
/// missing.
|
||||
pub(crate) fn update_submodule(&self, relative_path: &str) {
|
||||
if !self.submodules() {
|
||||
return;
|
||||
}
|
||||
|
||||
let absolute_path = self.src.join(relative_path);
|
||||
|
||||
// NOTE: The check for the empty directory is here because when running x.py the first time,
|
||||
// the submodule won't be checked out. Check it out now so we can build it.
|
||||
if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
|
||||
&& !helpers::dir_is_empty(&absolute_path)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Submodule updating actually happens during in the dry run mode. We need to make sure that
|
||||
// all the git commands below are actually executed, because some follow-up code
|
||||
// in bootstrap might depend on the submodules being checked out. Furthermore, not all
|
||||
// the command executions below work with an empty output (produced during dry run).
|
||||
// Therefore, all commands below are marked with `run_always()`, so that they also run in
|
||||
// dry run mode.
|
||||
let submodule_git = || {
|
||||
let mut cmd = helpers::git(Some(&absolute_path));
|
||||
cmd.run_always();
|
||||
cmd
|
||||
};
|
||||
|
||||
// Determine commit checked out in submodule.
|
||||
let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"]).as_command_mut());
|
||||
let checked_out_hash = checked_out_hash.trim_end();
|
||||
// Determine commit that the submodule *should* have.
|
||||
let recorded = output(
|
||||
helpers::git(Some(&self.src))
|
||||
.run_always()
|
||||
.args(["ls-tree", "HEAD"])
|
||||
.arg(relative_path)
|
||||
.as_command_mut(),
|
||||
);
|
||||
|
||||
let actual_hash = recorded
|
||||
.split_whitespace()
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
|
||||
|
||||
if actual_hash == checked_out_hash {
|
||||
// already checked out
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Updating submodule {relative_path}");
|
||||
self.check_run(
|
||||
helpers::git(Some(&self.src))
|
||||
.run_always()
|
||||
.args(["submodule", "-q", "sync"])
|
||||
.arg(relative_path),
|
||||
);
|
||||
|
||||
// Try passing `--progress` to start, then run git again without if that fails.
|
||||
let update = |progress: bool| {
|
||||
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
|
||||
// even though that has no relation to the upstream for the submodule.
|
||||
let current_branch = output_result(
|
||||
helpers::git(Some(&self.src))
|
||||
.allow_failure()
|
||||
.run_always()
|
||||
.args(["symbolic-ref", "--short", "HEAD"])
|
||||
.as_command_mut(),
|
||||
)
|
||||
.map(|b| b.trim().to_owned());
|
||||
|
||||
let mut git = helpers::git(Some(&self.src)).allow_failure();
|
||||
git.run_always();
|
||||
if let Ok(branch) = current_branch {
|
||||
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
|
||||
// This syntax isn't accepted by `branch.{branch}`. Strip it.
|
||||
let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
|
||||
git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
|
||||
}
|
||||
git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
|
||||
if progress {
|
||||
git.arg("--progress");
|
||||
}
|
||||
git.arg(relative_path);
|
||||
git
|
||||
};
|
||||
if !self.check_run(&mut update(true)) {
|
||||
self.check_run(&mut update(false));
|
||||
}
|
||||
|
||||
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
|
||||
// diff-index reports the modifications through the exit status
|
||||
let has_local_modifications = !self.check_run(submodule_git().allow_failure().args([
|
||||
"diff-index",
|
||||
"--quiet",
|
||||
"HEAD",
|
||||
]));
|
||||
if has_local_modifications {
|
||||
self.check_run(submodule_git().args(["stash", "push"]));
|
||||
}
|
||||
|
||||
self.check_run(submodule_git().args(["reset", "-q", "--hard"]));
|
||||
self.check_run(submodule_git().args(["clean", "-qdfx"]));
|
||||
|
||||
if has_local_modifications {
|
||||
self.check_run(submodule_git().args(["stash", "pop"]));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bootstrap-self-test")]
|
||||
pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
|
||||
|
||||
@ -2613,19 +2730,23 @@ impl Config {
|
||||
asserts: bool,
|
||||
) -> bool {
|
||||
let if_unchanged = || {
|
||||
// Git is needed to track modifications here, but tarball source is not available.
|
||||
// If not modified here or built through tarball source, we maintain consistency
|
||||
// with '"if available"'.
|
||||
if !self.rust_info.is_from_tarball()
|
||||
&& self
|
||||
.last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
|
||||
.is_none()
|
||||
{
|
||||
// there are some untracked changes in the given paths.
|
||||
false
|
||||
} else {
|
||||
llvm::is_ci_llvm_available(self, asserts)
|
||||
if self.rust_info.is_from_tarball() {
|
||||
// Git is needed for running "if-unchanged" logic.
|
||||
println!(
|
||||
"WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.update_submodule("src/llvm-project");
|
||||
|
||||
// Check for untracked changes in `src/llvm-project`.
|
||||
let has_changes = self
|
||||
.last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
|
||||
.is_none();
|
||||
|
||||
// Return false if there are untracked changes, otherwise check if CI LLVM is available.
|
||||
if has_changes { false } else { llvm::is_ci_llvm_available(self, asserts) }
|
||||
};
|
||||
|
||||
match download_ci_llvm {
|
||||
|
@ -56,7 +56,7 @@ impl Config {
|
||||
/// Returns false if do not execute at all, otherwise returns its
|
||||
/// `status.success()`.
|
||||
pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
|
||||
if self.dry_run() {
|
||||
if self.dry_run() && !cmd.run_always {
|
||||
return true;
|
||||
}
|
||||
self.verbose(|| println!("running: {cmd:?}"));
|
||||
|
@ -473,117 +473,6 @@ impl Build {
|
||||
build
|
||||
}
|
||||
|
||||
/// Given a path to the directory of a submodule, update it.
|
||||
///
|
||||
/// `relative_path` should be relative to the root of the git repository, not an absolute path.
|
||||
///
|
||||
/// This *does not* update the submodule if `config.toml` explicitly says
|
||||
/// not to, or if we're not in a git repository (like a plain source
|
||||
/// tarball). Typically [`Build::require_submodule`] should be
|
||||
/// used instead to provide a nice error to the user if the submodule is
|
||||
/// missing.
|
||||
fn update_submodule(&self, relative_path: &str) {
|
||||
if !self.config.submodules() {
|
||||
return;
|
||||
}
|
||||
|
||||
let absolute_path = self.config.src.join(relative_path);
|
||||
|
||||
// NOTE: The check for the empty directory is here because when running x.py the first time,
|
||||
// the submodule won't be checked out. Check it out now so we can build it.
|
||||
if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
|
||||
&& !dir_is_empty(&absolute_path)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Submodule updating actually happens during in the dry run mode. We need to make sure that
|
||||
// all the git commands below are actually executed, because some follow-up code
|
||||
// in bootstrap might depend on the submodules being checked out. Furthermore, not all
|
||||
// the command executions below work with an empty output (produced during dry run).
|
||||
// Therefore, all commands below are marked with `run_always()`, so that they also run in
|
||||
// dry run mode.
|
||||
let submodule_git = || {
|
||||
let mut cmd = helpers::git(Some(&absolute_path));
|
||||
cmd.run_always();
|
||||
cmd
|
||||
};
|
||||
|
||||
// Determine commit checked out in submodule.
|
||||
let checked_out_hash =
|
||||
submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout();
|
||||
let checked_out_hash = checked_out_hash.trim_end();
|
||||
// Determine commit that the submodule *should* have.
|
||||
let recorded = helpers::git(Some(&self.src))
|
||||
.run_always()
|
||||
.args(["ls-tree", "HEAD"])
|
||||
.arg(relative_path)
|
||||
.run_capture_stdout(self)
|
||||
.stdout();
|
||||
let actual_hash = recorded
|
||||
.split_whitespace()
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
|
||||
|
||||
if actual_hash == checked_out_hash {
|
||||
// already checked out
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Updating submodule {relative_path}");
|
||||
helpers::git(Some(&self.src))
|
||||
.run_always()
|
||||
.args(["submodule", "-q", "sync"])
|
||||
.arg(relative_path)
|
||||
.run(self);
|
||||
|
||||
// Try passing `--progress` to start, then run git again without if that fails.
|
||||
let update = |progress: bool| {
|
||||
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
|
||||
// even though that has no relation to the upstream for the submodule.
|
||||
let current_branch = helpers::git(Some(&self.src))
|
||||
.allow_failure()
|
||||
.run_always()
|
||||
.args(["symbolic-ref", "--short", "HEAD"])
|
||||
.run_capture_stdout(self)
|
||||
.stdout_if_ok()
|
||||
.map(|s| s.trim().to_owned());
|
||||
|
||||
let mut git = helpers::git(Some(&self.src)).allow_failure();
|
||||
git.run_always();
|
||||
if let Some(branch) = current_branch {
|
||||
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
|
||||
// This syntax isn't accepted by `branch.{branch}`. Strip it.
|
||||
let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
|
||||
git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
|
||||
}
|
||||
git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
|
||||
if progress {
|
||||
git.arg("--progress");
|
||||
}
|
||||
git.arg(relative_path);
|
||||
git
|
||||
};
|
||||
if !update(true).run(self) {
|
||||
update(false).run(self);
|
||||
}
|
||||
|
||||
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
|
||||
// diff-index reports the modifications through the exit status
|
||||
let has_local_modifications =
|
||||
!submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self);
|
||||
if has_local_modifications {
|
||||
submodule_git().args(["stash", "push"]).run(self);
|
||||
}
|
||||
|
||||
submodule_git().args(["reset", "-q", "--hard"]).run(self);
|
||||
submodule_git().args(["clean", "-qdfx"]).run(self);
|
||||
|
||||
if has_local_modifications {
|
||||
submodule_git().args(["stash", "pop"]).run(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates a submodule, and exits with a failure if submodule management
|
||||
/// is disabled and the submodule does not exist.
|
||||
///
|
||||
@ -598,7 +487,7 @@ impl Build {
|
||||
if cfg!(test) && !self.config.submodules() {
|
||||
return;
|
||||
}
|
||||
self.update_submodule(submodule);
|
||||
self.config.update_submodule(submodule);
|
||||
let absolute_path = self.config.src.join(submodule);
|
||||
if dir_is_empty(&absolute_path) {
|
||||
let maybe_enable = if !self.config.submodules()
|
||||
@ -646,7 +535,7 @@ impl Build {
|
||||
let path = Path::new(submodule);
|
||||
// Don't update the submodule unless it's already been cloned.
|
||||
if GitInfo::new(false, path).is_managed_git_subrepository() {
|
||||
self.update_submodule(submodule);
|
||||
self.config.update_submodule(submodule);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -659,7 +548,7 @@ impl Build {
|
||||
}
|
||||
|
||||
if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
|
||||
self.update_submodule(submodule);
|
||||
self.config.update_submodule(submodule);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1547,10 +1547,23 @@ instead, we check that it's not a "finger" cursor.
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
@keyframes targetfadein {
|
||||
from {
|
||||
background-color: var(--main-background-color);
|
||||
}
|
||||
10% {
|
||||
background-color: var(--target-border-color);
|
||||
}
|
||||
to {
|
||||
background-color: var(--target-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
:target {
|
||||
padding-right: 3px;
|
||||
background-color: var(--target-background-color);
|
||||
border-right: 3px solid var(--target-border-color);
|
||||
animation: 0.65s cubic-bezier(0, 0, 0.1, 1.0) 0.1s targetfadein;
|
||||
}
|
||||
|
||||
.code-header a.tooltip {
|
||||
|
@ -3265,7 +3265,7 @@ impl<'test> TestCx<'test> {
|
||||
|
||||
let tmpdir = cwd.join(self.output_base_name());
|
||||
if tmpdir.exists() {
|
||||
self.aggressive_rm_rf(&tmpdir).unwrap();
|
||||
fs::remove_dir_all(&tmpdir).unwrap();
|
||||
}
|
||||
create_dir_all(&tmpdir).unwrap();
|
||||
|
||||
@ -3404,29 +3404,6 @@ impl<'test> TestCx<'test> {
|
||||
}
|
||||
}
|
||||
|
||||
fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
|
||||
for e in path.read_dir()? {
|
||||
let entry = e?;
|
||||
let path = entry.path();
|
||||
if entry.file_type()?.is_dir() {
|
||||
self.aggressive_rm_rf(&path)?;
|
||||
} else {
|
||||
// Remove readonly files as well on windows (by default we can't)
|
||||
fs::remove_file(&path).or_else(|e| {
|
||||
if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
|
||||
let mut meta = entry.metadata()?.permissions();
|
||||
meta.set_readonly(false);
|
||||
fs::set_permissions(&path, meta)?;
|
||||
fs::remove_file(&path)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})?;
|
||||
}
|
||||
}
|
||||
fs::remove_dir(path)
|
||||
}
|
||||
|
||||
fn run_rmake_v2_test(&self) {
|
||||
// For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
|
||||
// (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
|
||||
@ -3475,7 +3452,7 @@ impl<'test> TestCx<'test> {
|
||||
// This setup intentionally diverges from legacy Makefile run-make tests.
|
||||
let base_dir = self.output_base_name();
|
||||
if base_dir.exists() {
|
||||
self.aggressive_rm_rf(&base_dir).unwrap();
|
||||
fs::remove_dir_all(&base_dir).unwrap();
|
||||
}
|
||||
let rmake_out_dir = base_dir.join("rmake_out");
|
||||
create_dir_all(&rmake_out_dir).unwrap();
|
||||
|
34
tests/incremental/decl_macro.rs
Normal file
34
tests/incremental/decl_macro.rs
Normal file
@ -0,0 +1,34 @@
|
||||
//@ revisions: rpass1 rpass2
|
||||
|
||||
// issue#112680
|
||||
|
||||
#![feature(decl_macro)]
|
||||
|
||||
pub trait T {
|
||||
type Key;
|
||||
fn index_from_key(key: Self::Key) -> usize;
|
||||
}
|
||||
|
||||
pub macro m($key_ty:ident, $val_ty:ident) {
|
||||
struct $key_ty {
|
||||
inner: usize,
|
||||
}
|
||||
|
||||
impl T for $val_ty {
|
||||
type Key = $key_ty;
|
||||
|
||||
fn index_from_key(key: Self::Key) -> usize {
|
||||
key.inner
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m!(TestId, Test);
|
||||
|
||||
#[cfg(rpass1)]
|
||||
struct Test(u32);
|
||||
|
||||
#[cfg(rpass2)]
|
||||
struct Test;
|
||||
|
||||
fn main() {}
|
@ -11,7 +11,7 @@ define-function: (
|
||||
[theme, background, border],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
assert-css: ("#method\.a_method:target", {
|
||||
wait-for-css: ("#method\.a_method:target", {
|
||||
"background-color": |background|,
|
||||
"border-right": "3px solid " + |border|,
|
||||
})
|
||||
|
@ -117,4 +117,11 @@ pub fn skipped_because_of_annotation<'a>(dcx: DiagCtxtHandle<'a>) {
|
||||
fn f(_x: impl Into<DiagMessage>, _y: impl Into<SubdiagMessage>) {}
|
||||
fn g() {
|
||||
f(crate::fluent_generated::no_crate_example, crate::fluent_generated::no_crate_example);
|
||||
f("untranslatable diagnostic", crate::fluent_generated::no_crate_example);
|
||||
//~^ ERROR diagnostics should be created using translatable messages
|
||||
f(crate::fluent_generated::no_crate_example, "untranslatable diagnostic");
|
||||
//~^ ERROR diagnostics should be created using translatable messages
|
||||
f("untranslatable diagnostic", "untranslatable diagnostic");
|
||||
//~^ ERROR diagnostics should be created using translatable messages
|
||||
//~^^ ERROR diagnostics should be created using translatable messages
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:43:9
|
||||
--> $DIR/diagnostics.rs:43:31
|
||||
|
|
||||
LL | Diag::new(dcx, level, "untranslatable diagnostic")
|
||||
| ^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/diagnostics.rs:7:9
|
||||
@ -11,16 +11,16 @@ LL | #![deny(rustc::untranslatable_diagnostic)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:64:14
|
||||
--> $DIR/diagnostics.rs:64:19
|
||||
|
|
||||
LL | diag.note("untranslatable diagnostic");
|
||||
| ^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:85:14
|
||||
--> $DIR/diagnostics.rs:85:19
|
||||
|
|
||||
LL | diag.note("untranslatable diagnostic");
|
||||
| ^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
|
||||
--> $DIR/diagnostics.rs:99:21
|
||||
@ -41,10 +41,34 @@ LL | let _diag = dcx.struct_err("untranslatable diagnostic");
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:102:21
|
||||
--> $DIR/diagnostics.rs:102:32
|
||||
|
|
||||
LL | let _diag = dcx.struct_err("untranslatable diagnostic");
|
||||
| ^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:120:7
|
||||
|
|
||||
LL | f("untranslatable diagnostic", crate::fluent_generated::no_crate_example);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:122:50
|
||||
|
|
||||
LL | f(crate::fluent_generated::no_crate_example, "untranslatable diagnostic");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:124:7
|
||||
|
|
||||
LL | f("untranslatable diagnostic", "untranslatable diagnostic");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:124:36
|
||||
|
|
||||
LL | f("untranslatable diagnostic", "untranslatable diagnostic");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
13
tests/ui/regions/explicit-static-bound-on-trait.rs
Normal file
13
tests/ui/regions/explicit-static-bound-on-trait.rs
Normal file
@ -0,0 +1,13 @@
|
||||
struct Hello<'a> {
|
||||
value: Box<dyn std::any::Any + 'a>,
|
||||
//~^ ERROR lifetime bound not satisfied
|
||||
}
|
||||
|
||||
impl<'a> Hello<'a> {
|
||||
fn new<T: 'a>(value: T) -> Self {
|
||||
Self { value: Box::new(value) }
|
||||
//~^ ERROR the parameter type `T` may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
32
tests/ui/regions/explicit-static-bound-on-trait.stderr
Normal file
32
tests/ui/regions/explicit-static-bound-on-trait.stderr
Normal file
@ -0,0 +1,32 @@
|
||||
error[E0478]: lifetime bound not satisfied
|
||||
--> $DIR/explicit-static-bound-on-trait.rs:2:12
|
||||
|
|
||||
LL | value: Box<dyn std::any::Any + 'a>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: lifetime parameter instantiated with the lifetime `'a` as defined here
|
||||
--> $DIR/explicit-static-bound-on-trait.rs:1:14
|
||||
|
|
||||
LL | struct Hello<'a> {
|
||||
| ^^
|
||||
note: but lifetime parameter must outlive the static lifetime
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
|
||||
error[E0310]: the parameter type `T` may not live long enough
|
||||
--> $DIR/explicit-static-bound-on-trait.rs:8:23
|
||||
|
|
||||
LL | Self { value: Box::new(value) }
|
||||
| ^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| the parameter type `T` must be valid for the static lifetime...
|
||||
| ...so that the type `T` will meet its required lifetime bounds
|
||||
|
|
||||
help: consider adding an explicit lifetime bound
|
||||
|
|
||||
LL | fn new<T: 'a + 'static>(value: T) -> Self {
|
||||
| +++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0310, E0478.
|
||||
For more information about an error, try `rustc --explain E0310`.
|
Loading…
Reference in New Issue
Block a user