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
25 changed files with 986 additions and 367 deletions
7
.github/workflows/dependencies.yml
vendored
7
.github/workflows/dependencies.yml
vendored
|
@ -64,6 +64,10 @@ jobs:
|
||||||
- name: cargo update
|
- name: cargo update
|
||||||
# Remove first line that always just says "Updating crates.io index"
|
# 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
|
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
|
- name: cargo update rustbook
|
||||||
run: |
|
run: |
|
||||||
echo -e "\nrustbook dependencies:" >> cargo_update.log
|
echo -e "\nrustbook dependencies:" >> cargo_update.log
|
||||||
|
@ -74,6 +78,7 @@ jobs:
|
||||||
name: Cargo-lock
|
name: Cargo-lock
|
||||||
path: |
|
path: |
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
library/Cargo.lock
|
||||||
src/tools/rustbook/Cargo.lock
|
src/tools/rustbook/Cargo.lock
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
- name: upload cargo-update log artifact for use in PR
|
- name: upload cargo-update log artifact for use in PR
|
||||||
|
@ -119,7 +124,7 @@ jobs:
|
||||||
git config user.name github-actions
|
git config user.name github-actions
|
||||||
git config user.email github-actions@github.com
|
git config user.email github-actions@github.com
|
||||||
git switch --force-create cargo_update
|
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
|
git commit --no-verify --file=commit.txt
|
||||||
|
|
||||||
- name: push
|
- name: push
|
||||||
|
|
|
@ -747,7 +747,7 @@ fn region_known_to_outlive<'tcx>(
|
||||||
region_b: ty::Region<'tcx>,
|
region_b: ty::Region<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
|
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>) {
|
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>) {
|
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
|
/// The given region parameter was instantiated with a region
|
||||||
/// that must outlive some other region.
|
/// that must outlive some other region.
|
||||||
RelateRegionParamBound(Span),
|
RelateRegionParamBound(Span, Option<Ty<'tcx>>),
|
||||||
|
|
||||||
/// Creating a pointer `b` to contents of another reference.
|
/// Creating a pointer `b` to contents of another reference.
|
||||||
Reborrow(Span),
|
Reborrow(Span),
|
||||||
|
@ -859,7 +859,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
) {
|
) {
|
||||||
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
|
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
|
||||||
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
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`
|
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(),
|
Subtype(ref a) => a.span(),
|
||||||
RelateObjectBound(a) => a,
|
RelateObjectBound(a) => a,
|
||||||
RelateParamBound(a, ..) => a,
|
RelateParamBound(a, ..) => a,
|
||||||
RelateRegionParamBound(a) => a,
|
RelateRegionParamBound(a, _) => a,
|
||||||
Reborrow(a) => a,
|
Reborrow(a) => a,
|
||||||
ReferenceOutlivesReferent(_, a) => a,
|
ReferenceOutlivesReferent(_, a) => a,
|
||||||
CompareImplItemObligation { span, .. } => span,
|
CompareImplItemObligation { span, .. } => span,
|
||||||
|
@ -1726,6 +1726,10 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||||
SubregionOrigin::AscribeUserTypeProvePredicate(span)
|
SubregionOrigin::AscribeUserTypeProvePredicate(span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
traits::ObligationCauseCode::ObjectTypeBound(ty, _reg) => {
|
||||||
|
SubregionOrigin::RelateRegionParamBound(cause.span, Some(ty))
|
||||||
|
}
|
||||||
|
|
||||||
_ => default(),
|
_ => default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::{
|
||||||
BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
|
BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
|
||||||
Path, PathSegment, QPath, Ty, TyKind,
|
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_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||||
use rustc_span::symbol::{kw, sym, Symbol};
|
use rustc_span::symbol::{kw, sym, Symbol};
|
||||||
|
@ -415,14 +415,17 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE
|
||||||
|
|
||||||
impl LateLintPass<'_> for Diagnostics {
|
impl LateLintPass<'_> for Diagnostics {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
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.
|
// 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) => {
|
ExprKind::Call(callee, args) => {
|
||||||
match cx.typeck_results().node_type(callee.hir_id).kind() {
|
match cx.typeck_results().node_type(callee.hir_id).kind() {
|
||||||
&ty::FnDef(def_id, fn_gen_args) => {
|
&ty::FnDef(def_id, fn_gen_args) => {
|
||||||
let call_tys: Vec<_> =
|
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
|
||||||
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
|
|
||||||
(callee.span, def_id, fn_gen_args, call_tys)
|
|
||||||
}
|
}
|
||||||
_ => return, // occurs for fns passed as args
|
_ => return, // occurs for fns passed as args
|
||||||
}
|
}
|
||||||
|
@ -432,38 +435,40 @@ impl LateLintPass<'_> for Diagnostics {
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut call_tys: Vec<_> =
|
let mut args = collect_args_tys_and_spans(args, true);
|
||||||
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
|
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
|
||||||
call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self`
|
(span, def_id, fn_gen_args, args)
|
||||||
(span, def_id, fn_gen_args, call_tys)
|
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Is the callee marked with `#[rustc_lint_diagnostics]`?
|
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
|
||||||
let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
|
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
|
||||||
.ok()
|
}
|
||||||
.flatten()
|
}
|
||||||
.is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
|
|
||||||
|
|
||||||
// Closure: is the type `{D,Subd}iagMessage`?
|
impl Diagnostics {
|
||||||
let is_diag_message = |ty: MiddleTy<'_>| {
|
// Is the type `{D,Subd}iagMessage`?
|
||||||
if let Some(adt_def) = ty.ty_adt_def()
|
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
|
||||||
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
|
if let Some(adt_def) = ty.ty_adt_def()
|
||||||
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
|
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
|
||||||
{
|
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
|
||||||
true
|
{
|
||||||
} else {
|
true
|
||||||
false
|
} else {
|
||||||
}
|
false
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
|
fn untranslatable_diagnostic<'cx>(
|
||||||
let mut impl_into_diagnostic_message_params = vec![];
|
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 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;
|
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
|
||||||
for (i, ¶m_ty) in fn_sig.inputs().iter().enumerate() {
|
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>`.
|
// It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
|
||||||
for pred in predicates.iter() {
|
for pred in predicates.iter() {
|
||||||
if let Some(trait_pred) = pred.as_trait_clause()
|
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?
|
&& trait_ref.self_ty() == param_ty // correct predicate for the param?
|
||||||
&& cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
|
&& cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
|
||||||
&& let ty1 = trait_ref.args.type_at(1)
|
&& 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?
|
fn diagnostic_outside_of_impl<'cx>(
|
||||||
if !has_attr && impl_into_diagnostic_message_params.is_empty() {
|
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;
|
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]`?
|
for (hir_id, _parent) in cx.tcx.hir().parent_iter(current_id) {
|
||||||
let mut parent_has_attr = false;
|
|
||||||
for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
|
||||||
if let Some(owner_did) = hir_id.as_owner()
|
if let Some(owner_did) = hir_id.as_owner()
|
||||||
&& cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
|
&& cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
|
||||||
{
|
{
|
||||||
parent_has_attr = true;
|
// The parent method is marked with `#[rustc_lint_diagnostics]`
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
|
||||||
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
|
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
|
||||||
//
|
//
|
||||||
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
|
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
|
||||||
if has_attr && !parent_has_attr {
|
let mut is_inside_appropriate_impl = false;
|
||||||
let mut is_inside_appropriate_impl = false;
|
for (_hir_id, parent) in cx.tcx.hir().parent_iter(current_id) {
|
||||||
for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
debug!(?parent);
|
||||||
debug!(?parent);
|
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
|
||||||
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
|
&& let Impl { of_trait: Some(of_trait), .. } = impl_
|
||||||
&& let Impl { of_trait: Some(of_trait), .. } = impl_
|
&& let Some(def_id) = of_trait.trait_def_id()
|
||||||
&& let Some(def_id) = of_trait.trait_def_id()
|
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
||||||
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
|
||||||
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
|
{
|
||||||
{
|
is_inside_appropriate_impl = true;
|
||||||
is_inside_appropriate_impl = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug!(?is_inside_appropriate_impl);
|
|
||||||
if !is_inside_appropriate_impl {
|
|
||||||
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debug!(?is_inside_appropriate_impl);
|
||||||
// Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
|
if !is_inside_appropriate_impl {
|
||||||
// with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
|
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
|
||||||
// `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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
// Overwrite the dummy data with our decoded SyntaxContextData
|
||||||
HygieneData::with(|hygiene_data| {
|
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(
|
let dummy = std::mem::replace(
|
||||||
&mut hygiene_data.syntax_context_data[ctxt.as_u32() as usize],
|
&mut hygiene_data.syntax_context_data[ctxt.as_u32() as usize],
|
||||||
ctxt_data,
|
ctxt_data,
|
||||||
|
|
|
@ -11,7 +11,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||||
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
|
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
|
||||||
match &self.error {
|
match &self.error {
|
||||||
Some(RegionResolutionError::ConcreteFailure(
|
Some(RegionResolutionError::ConcreteFailure(
|
||||||
SubregionOrigin::RelateRegionParamBound(span),
|
SubregionOrigin::RelateRegionParamBound(span, _),
|
||||||
Region(Interned(
|
Region(Interned(
|
||||||
RePlaceholder(ty::Placeholder {
|
RePlaceholder(ty::Placeholder {
|
||||||
bound: ty::BoundRegion { kind: sub_name, .. },
|
bound: ty::BoundRegion { kind: sub_name, .. },
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
.add_to_diag(err);
|
.add_to_diag(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infer::RelateRegionParamBound(span) => {
|
infer::RelateRegionParamBound(span, _) => {
|
||||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||||
.add_to_diag(err);
|
.add_to_diag(err);
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
note,
|
note,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
infer::RelateRegionParamBound(span) => {
|
infer::RelateRegionParamBound(span, _) => {
|
||||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
generic_param_scope,
|
generic_param_scope,
|
||||||
|
|
|
@ -257,7 +257,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
.add_to_diag(err);
|
.add_to_diag(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
infer::RelateRegionParamBound(span) => {
|
infer::RelateRegionParamBound(span, _) => {
|
||||||
RegionOriginNote::Plain {
|
RegionOriginNote::Plain {
|
||||||
span,
|
span,
|
||||||
msg: fluent::trait_selection_relate_region_param_bound,
|
msg: fluent::trait_selection_relate_region_param_bound,
|
||||||
|
@ -410,7 +410,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
note,
|
note,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
infer::RelateRegionParamBound(span) => {
|
infer::RelateRegionParamBound(span, ty) => {
|
||||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
generic_param_scope,
|
generic_param_scope,
|
||||||
|
@ -419,11 +419,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||||
note_and_explain::PrefixKind::LfParamInstantiatedWith,
|
note_and_explain::PrefixKind::LfParamInstantiatedWith,
|
||||||
note_and_explain::SuffixKind::Empty,
|
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(
|
let param_must_outlive = note_and_explain::RegionExplanation::new(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
generic_param_scope,
|
generic_param_scope,
|
||||||
sub,
|
sub,
|
||||||
None,
|
alt_span,
|
||||||
note_and_explain::PrefixKind::LfParamMustOutlive,
|
note_and_explain::PrefixKind::LfParamMustOutlive,
|
||||||
note_and_explain::SuffixKind::Empty,
|
note_and_explain::SuffixKind::Empty,
|
||||||
);
|
);
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl fmt::Write for PadAdapter<'_, '_> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }),
|
/// 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"]
|
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||||
|
@ -125,7 +125,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }),
|
/// 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")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -237,7 +237,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Bar { bar: 10, baz: "Hello World".to_string() }),
|
/// 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")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -280,7 +280,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
/// 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"]
|
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||||
|
@ -322,7 +322,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
||||||
/// "Foo(10, \"Hello World\")",
|
/// r#"Foo(10, "Hello World")"#,
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -360,6 +360,51 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||||
self
|
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.
|
/// Finishes output and returns any error encountered.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -381,7 +426,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
/// format!("{:?}", Foo(10, "Hello World".to_string())),
|
||||||
/// "Foo(10, \"Hello World\")",
|
/// r#"Foo(10, "Hello World")"#,
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -555,6 +600,56 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
|
||||||
self
|
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.
|
/// Finishes output and returns any error encountered.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -699,6 +794,55 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
|
||||||
self
|
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.
|
/// Finishes output and returns any error encountered.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -750,7 +894,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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"]
|
#[must_use = "must eventually call `finish()` on Debug builders"]
|
||||||
|
@ -790,7 +934,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -826,7 +970,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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")]
|
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
|
||||||
|
@ -902,7 +1046,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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")]
|
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
|
||||||
|
@ -960,7 +1104,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
@ -976,6 +1120,62 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
self
|
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.
|
/// Finishes output and returns any error encountered.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
@ -1000,7 +1200,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
|
/// 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")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
|
|
|
@ -79,23 +79,23 @@ mod debug_struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
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:?}")
|
format!("{Bar:?}")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Bar {
|
r#"Bar {
|
||||||
foo: Foo {
|
foo: Foo {
|
||||||
bar: true,
|
bar: true,
|
||||||
baz: 10/20,
|
baz: 10/20,
|
||||||
},
|
},
|
||||||
hello: \"world\",
|
hello: "world",
|
||||||
}",
|
}"#,
|
||||||
format!("{Bar:#?}")
|
format!("{Bar:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_only_non_exhaustive() {
|
fn test_empty_non_exhaustive() {
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
impl fmt::Debug for Foo {
|
impl fmt::Debug for Foo {
|
||||||
|
@ -157,19 +157,19 @@ mod debug_struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
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:?}")
|
format!("{Bar:?}")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Bar {
|
r#"Bar {
|
||||||
foo: Foo {
|
foo: Foo {
|
||||||
bar: true,
|
bar: true,
|
||||||
baz: 10/20,
|
baz: 10/20,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
hello: \"world\",
|
hello: "world",
|
||||||
..
|
..
|
||||||
}",
|
}"#,
|
||||||
format!("{Bar:#?}")
|
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!(
|
assert_eq!(
|
||||||
"Bar(
|
r#"Bar(
|
||||||
Foo(
|
Foo(
|
||||||
true,
|
true,
|
||||||
10/20,
|
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:#?}")
|
format!("{Bar:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -301,11 +375,11 @@ mod debug_map {
|
||||||
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
||||||
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!(
|
assert_eq!(
|
||||||
"{
|
r#"{
|
||||||
\"bar\": true,
|
"bar": true,
|
||||||
}",
|
}"#,
|
||||||
format!("{Entry:#?}")
|
format!("{Entry:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -339,12 +413,12 @@ mod debug_map {
|
||||||
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
|
||||||
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!(
|
assert_eq!(
|
||||||
"{
|
r#"{
|
||||||
\"bar\": true,
|
"bar": true,
|
||||||
10: 10/20,
|
10: 10/20,
|
||||||
}",
|
}"#,
|
||||||
format!("{Entry:#?}")
|
format!("{Entry:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -371,21 +445,20 @@ mod debug_map {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"{\"foo\": {\"bar\": true, 10: 10/20}, \
|
r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#,
|
||||||
{\"bar\": true, 10: 10/20}: \"world\"}",
|
|
||||||
format!("{Bar:?}")
|
format!("{Bar:?}")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"{
|
r#"{
|
||||||
\"foo\": {
|
"foo": {
|
||||||
\"bar\": true,
|
"bar": true,
|
||||||
10: 10/20,
|
10: 10/20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
\"bar\": true,
|
"bar": true,
|
||||||
10: 10/20,
|
10: 10/20,
|
||||||
}: \"world\",
|
}: "world",
|
||||||
}",
|
}"#,
|
||||||
format!("{Bar:#?}")
|
format!("{Bar:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -471,6 +544,103 @@ mod debug_map {
|
||||||
|
|
||||||
let _ = format!("{Foo:?}");
|
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 {
|
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!(
|
assert_eq!(
|
||||||
"{
|
r#"{
|
||||||
{
|
{
|
||||||
true,
|
true,
|
||||||
10/20,
|
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:#?}")
|
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!(
|
assert_eq!(
|
||||||
"[
|
r#"[
|
||||||
[
|
[
|
||||||
true,
|
true,
|
||||||
10/20,
|
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:#?}")
|
format!("{Bar:#?}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#![feature(core_io_borrowed_buf)]
|
#![feature(core_io_borrowed_buf)]
|
||||||
#![feature(core_private_bignum)]
|
#![feature(core_private_bignum)]
|
||||||
#![feature(core_private_diy_float)]
|
#![feature(core_private_diy_float)]
|
||||||
|
#![feature(debug_more_non_exhaustive)]
|
||||||
#![feature(dec2flt)]
|
#![feature(dec2flt)]
|
||||||
#![feature(duration_constants)]
|
#![feature(duration_constants)]
|
||||||
#![feature(duration_constructors)]
|
#![feature(duration_constructors)]
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
//! directory unless the `--all` flag is present.
|
//! directory unless the `--all` flag is present.
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, ErrorKind};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
|
use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
|
||||||
|
@ -101,11 +100,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rm_rf("tmp".as_ref());
|
remove_dir_recursive("tmp");
|
||||||
|
|
||||||
// Clean the entire build directory
|
// Clean the entire build directory
|
||||||
if all {
|
if all {
|
||||||
rm_rf(&build.out);
|
remove_dir_recursive(&build.out);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,17 +135,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = t!(entry.path().canonicalize());
|
let path = t!(entry.path().canonicalize());
|
||||||
rm_rf(&path);
|
remove_dir_recursive(&path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_default(build: &Build) {
|
fn clean_default(build: &Build) {
|
||||||
rm_rf(&build.out.join("tmp"));
|
remove_dir_recursive(build.out.join("tmp"));
|
||||||
rm_rf(&build.out.join("dist"));
|
remove_dir_recursive(build.out.join("dist"));
|
||||||
rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
|
remove_dir_recursive(build.out.join("bootstrap").join(".last-warned-change-id"));
|
||||||
rm_rf(&build.out.join("bootstrap-shims-dump"));
|
remove_dir_recursive(build.out.join("bootstrap-shims-dump"));
|
||||||
rm_rf(&build.out.join("rustfmt.stamp"));
|
remove_dir_recursive(build.out.join("rustfmt.stamp"));
|
||||||
|
|
||||||
let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect();
|
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)
|
// After cross-compilation, artifacts of the host architecture (which may differ from build.host)
|
||||||
|
@ -166,78 +165,16 @@ fn clean_default(build: &Build) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let path = t!(entry.path().canonicalize());
|
let path = t!(entry.path().canonicalize());
|
||||||
rm_rf(&path);
|
remove_dir_recursive(&path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rm_rf(path: &Path) {
|
/// Wrapper for [`std::fs::remove_dir_all`] that panics on failure and prints the `path` we failed
|
||||||
match path.symlink_metadata() {
|
/// on.
|
||||||
Err(e) => {
|
fn remove_dir_recursive<P: AsRef<Path>>(path: P) {
|
||||||
if e.kind() == ErrorKind::NotFound {
|
let path = path.as_ref();
|
||||||
return;
|
if let Err(e) = fs::remove_dir_all(path) {
|
||||||
}
|
panic!("failed to `remove_dir_all` at `{}`: {e}", path.display());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
|
||||||
|
|
||||||
// Initialize the llvm submodule if not initialized already.
|
// Initialize the llvm submodule if not initialized already.
|
||||||
// If submodules are disabled, this does nothing.
|
// 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 root = "src/llvm-project/llvm";
|
||||||
let out_dir = builder.llvm_out(target);
|
let out_dir = builder.llvm_out(target);
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::sync::OnceLock;
|
||||||
use std::{cmp, env, fs};
|
use std::{cmp, env, fs};
|
||||||
|
|
||||||
use build_helper::exit;
|
use build_helper::exit;
|
||||||
use build_helper::git::GitConfig;
|
use build_helper::git::{output_result, GitConfig};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde_derive::Deserialize;
|
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")]
|
#[cfg(feature = "bootstrap-self-test")]
|
||||||
pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
|
pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
|
||||||
|
|
||||||
|
@ -2613,19 +2730,23 @@ impl Config {
|
||||||
asserts: bool,
|
asserts: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let if_unchanged = || {
|
let if_unchanged = || {
|
||||||
// Git is needed to track modifications here, but tarball source is not available.
|
if self.rust_info.is_from_tarball() {
|
||||||
// If not modified here or built through tarball source, we maintain consistency
|
// Git is needed for running "if-unchanged" logic.
|
||||||
// with '"if available"'.
|
println!(
|
||||||
if !self.rust_info.is_from_tarball()
|
"WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`."
|
||||||
&& self
|
);
|
||||||
.last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
|
return false;
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
// there are some untracked changes in the given paths.
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
llvm::is_ci_llvm_available(self, asserts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
match download_ci_llvm {
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl Config {
|
||||||
/// Returns false if do not execute at all, otherwise returns its
|
/// Returns false if do not execute at all, otherwise returns its
|
||||||
/// `status.success()`.
|
/// `status.success()`.
|
||||||
pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
|
pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
|
||||||
if self.dry_run() {
|
if self.dry_run() && !cmd.run_always {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
self.verbose(|| println!("running: {cmd:?}"));
|
self.verbose(|| println!("running: {cmd:?}"));
|
||||||
|
|
|
@ -473,117 +473,6 @@ impl Build {
|
||||||
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
|
/// Updates a submodule, and exits with a failure if submodule management
|
||||||
/// is disabled and the submodule does not exist.
|
/// is disabled and the submodule does not exist.
|
||||||
///
|
///
|
||||||
|
@ -598,7 +487,7 @@ impl Build {
|
||||||
if cfg!(test) && !self.config.submodules() {
|
if cfg!(test) && !self.config.submodules() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.update_submodule(submodule);
|
self.config.update_submodule(submodule);
|
||||||
let absolute_path = self.config.src.join(submodule);
|
let absolute_path = self.config.src.join(submodule);
|
||||||
if dir_is_empty(&absolute_path) {
|
if dir_is_empty(&absolute_path) {
|
||||||
let maybe_enable = if !self.config.submodules()
|
let maybe_enable = if !self.config.submodules()
|
||||||
|
@ -646,7 +535,7 @@ impl Build {
|
||||||
let path = Path::new(submodule);
|
let path = Path::new(submodule);
|
||||||
// Don't update the submodule unless it's already been cloned.
|
// Don't update the submodule unless it's already been cloned.
|
||||||
if GitInfo::new(false, path).is_managed_git_subrepository() {
|
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() {
|
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;
|
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 {
|
:target {
|
||||||
padding-right: 3px;
|
padding-right: 3px;
|
||||||
background-color: var(--target-background-color);
|
background-color: var(--target-background-color);
|
||||||
border-right: 3px solid var(--target-border-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 {
|
.code-header a.tooltip {
|
||||||
|
|
|
@ -3265,7 +3265,7 @@ impl<'test> TestCx<'test> {
|
||||||
|
|
||||||
let tmpdir = cwd.join(self.output_base_name());
|
let tmpdir = cwd.join(self.output_base_name());
|
||||||
if tmpdir.exists() {
|
if tmpdir.exists() {
|
||||||
self.aggressive_rm_rf(&tmpdir).unwrap();
|
fs::remove_dir_all(&tmpdir).unwrap();
|
||||||
}
|
}
|
||||||
create_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) {
|
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
|
// 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
|
// (`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.
|
// This setup intentionally diverges from legacy Makefile run-make tests.
|
||||||
let base_dir = self.output_base_name();
|
let base_dir = self.output_base_name();
|
||||||
if base_dir.exists() {
|
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");
|
let rmake_out_dir = base_dir.join("rmake_out");
|
||||||
create_dir_all(&rmake_out_dir).unwrap();
|
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],
|
[theme, background, border],
|
||||||
block {
|
block {
|
||||||
call-function: ("switch-theme", {"theme": |theme|})
|
call-function: ("switch-theme", {"theme": |theme|})
|
||||||
assert-css: ("#method\.a_method:target", {
|
wait-for-css: ("#method\.a_method:target", {
|
||||||
"background-color": |background|,
|
"background-color": |background|,
|
||||||
"border-right": "3px solid " + |border|,
|
"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 f(_x: impl Into<DiagMessage>, _y: impl Into<SubdiagMessage>) {}
|
||||||
fn g() {
|
fn g() {
|
||||||
f(crate::fluent_generated::no_crate_example, crate::fluent_generated::no_crate_example);
|
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
|
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")
|
LL | Diag::new(dcx, level, "untranslatable diagnostic")
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/diagnostics.rs:7:9
|
--> $DIR/diagnostics.rs:7:9
|
||||||
|
@ -11,16 +11,16 @@ LL | #![deny(rustc::untranslatable_diagnostic)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should be created using translatable messages
|
error: diagnostics should be created using translatable messages
|
||||||
--> $DIR/diagnostics.rs:64:14
|
--> $DIR/diagnostics.rs:64:19
|
||||||
|
|
|
|
||||||
LL | diag.note("untranslatable diagnostic");
|
LL | diag.note("untranslatable diagnostic");
|
||||||
| ^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should be created using translatable messages
|
error: diagnostics should be created using translatable messages
|
||||||
--> $DIR/diagnostics.rs:85:14
|
--> $DIR/diagnostics.rs:85:19
|
||||||
|
|
|
|
||||||
LL | diag.note("untranslatable diagnostic");
|
LL | diag.note("untranslatable diagnostic");
|
||||||
| ^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
|
error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
|
||||||
--> $DIR/diagnostics.rs:99:21
|
--> $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
|
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");
|
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…
Add table
Add a link
Reference in a new issue