Auto merge of #97783 - matthiaskrgr:rollup-14t9htt, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #90905 (Add empty impl blocks if they have documentation) - #97683 (Fail gracefully when encountering an HRTB in APIT. ) - #97721 (Do `suggest_await_before_try` with infer variables in self, and clean up binders) - #97752 (typo: `-Zcodegen-backend=llvm -Cpasses=list` should work now) - #97759 (Suggest adding `{}` for `'label: non_block_expr`) - #97764 (use strict provenance APIs) - #97765 (Restore a test that was intended to test `as` cast to ptr) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
e70c60d34b
23 changed files with 420 additions and 83 deletions
|
@ -1055,13 +1055,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cg_flags.iter().any(|x| *x == "passes=list") {
|
if cg_flags.iter().any(|x| *x == "passes=list") {
|
||||||
let backend_name = debug_flags.iter().find_map(|x| {
|
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
|
||||||
if x.starts_with("codegen-backend=") {
|
|
||||||
Some(&x["codegen-backends=".len()..])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
get_codegen_backend(&None, backend_name).print_passes();
|
get_codegen_backend(&None, backend_name).print_passes();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -734,7 +734,7 @@ impl<'hir> WherePredicate<'hir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, HashStable_Generic, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
|
||||||
pub enum PredicateOrigin {
|
pub enum PredicateOrigin {
|
||||||
WhereClause,
|
WhereClause,
|
||||||
GenericParam,
|
GenericParam,
|
||||||
|
|
|
@ -13,10 +13,13 @@ use rustc_ast::tokenstream::Spacing;
|
||||||
use rustc_ast::util::classify;
|
use rustc_ast::util::classify;
|
||||||
use rustc_ast::util::literal::LitError;
|
use rustc_ast::util::literal::LitError;
|
||||||
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
|
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
|
||||||
|
use rustc_ast::visit::Visitor;
|
||||||
|
use rustc_ast::StmtKind;
|
||||||
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
|
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
|
||||||
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
|
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
|
||||||
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
|
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
|
use rustc_data_structures::thin_vec::ThinVec;
|
||||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
|
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
|
||||||
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
|
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
|
||||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||||
|
@ -1548,9 +1551,66 @@ impl<'a> Parser<'a> {
|
||||||
Ok(self.mk_expr_err(lo))
|
Ok(self.mk_expr_err(lo))
|
||||||
} else {
|
} else {
|
||||||
let msg = "expected `while`, `for`, `loop` or `{` after a label";
|
let msg = "expected `while`, `for`, `loop` or `{` after a label";
|
||||||
self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
|
|
||||||
|
let mut err = self.struct_span_err(self.token.span, msg);
|
||||||
|
err.span_label(self.token.span, msg);
|
||||||
|
|
||||||
// Continue as an expression in an effort to recover on `'label: non_block_expr`.
|
// Continue as an expression in an effort to recover on `'label: non_block_expr`.
|
||||||
self.parse_expr()
|
let expr = self.parse_expr().map(|expr| {
|
||||||
|
let span = expr.span;
|
||||||
|
|
||||||
|
let found_labeled_breaks = {
|
||||||
|
struct FindLabeledBreaksVisitor(bool);
|
||||||
|
|
||||||
|
impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {
|
||||||
|
fn visit_expr_post(&mut self, ex: &'ast Expr) {
|
||||||
|
if let ExprKind::Break(Some(_label), _) = ex.kind {
|
||||||
|
self.0 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vis = FindLabeledBreaksVisitor(false);
|
||||||
|
vis.visit_expr(&expr);
|
||||||
|
vis.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Suggestion involves adding a (as of time of writing this, unstable) labeled block.
|
||||||
|
//
|
||||||
|
// If there are no breaks that may use this label, suggest removing the label and
|
||||||
|
// recover to the unmodified expression.
|
||||||
|
if !found_labeled_breaks {
|
||||||
|
let msg = "consider removing the label";
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
lo.until(span),
|
||||||
|
msg,
|
||||||
|
"",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sugg_msg = "consider enclosing expression in a block";
|
||||||
|
let suggestions = vec![
|
||||||
|
(span.shrink_to_lo(), "{ ".to_owned()),
|
||||||
|
(span.shrink_to_hi(), " }".to_owned()),
|
||||||
|
];
|
||||||
|
|
||||||
|
err.multipart_suggestion_verbose(
|
||||||
|
sugg_msg,
|
||||||
|
suggestions,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
|
||||||
|
let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
|
||||||
|
let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
|
||||||
|
self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new())
|
||||||
|
});
|
||||||
|
|
||||||
|
err.emit();
|
||||||
|
expr
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
if !ate_colon && consume_colon {
|
if !ate_colon && consume_colon {
|
||||||
|
|
|
@ -207,6 +207,13 @@ enum Scope<'a> {
|
||||||
/// In some cases not allowing late bounds allows us to avoid ICEs.
|
/// In some cases not allowing late bounds allows us to avoid ICEs.
|
||||||
/// This is almost ways set to true.
|
/// This is almost ways set to true.
|
||||||
allow_late_bound: bool,
|
allow_late_bound: bool,
|
||||||
|
|
||||||
|
/// If this binder comes from a where clause, specify how it was created.
|
||||||
|
/// This is used to diagnose inaccessible lifetimes in APIT:
|
||||||
|
/// ```ignore (illustrative)
|
||||||
|
/// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
|
||||||
|
/// ```
|
||||||
|
where_bound_origin: Option<hir::PredicateOrigin>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Lifetimes introduced by a fn are scoped to the call-site for that fn,
|
/// Lifetimes introduced by a fn are scoped to the call-site for that fn,
|
||||||
|
@ -277,8 +284,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
|
||||||
opaque_type_parent,
|
opaque_type_parent,
|
||||||
scope_type,
|
scope_type,
|
||||||
hir_id,
|
hir_id,
|
||||||
s: _,
|
|
||||||
allow_late_bound,
|
allow_late_bound,
|
||||||
|
where_bound_origin,
|
||||||
|
s: _,
|
||||||
} => f
|
} => f
|
||||||
.debug_struct("Binder")
|
.debug_struct("Binder")
|
||||||
.field("lifetimes", lifetimes)
|
.field("lifetimes", lifetimes)
|
||||||
|
@ -286,8 +294,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
|
||||||
.field("opaque_type_parent", opaque_type_parent)
|
.field("opaque_type_parent", opaque_type_parent)
|
||||||
.field("scope_type", scope_type)
|
.field("scope_type", scope_type)
|
||||||
.field("hir_id", hir_id)
|
.field("hir_id", hir_id)
|
||||||
.field("s", &"..")
|
|
||||||
.field("allow_late_bound", allow_late_bound)
|
.field("allow_late_bound", allow_late_bound)
|
||||||
|
.field("where_bound_origin", where_bound_origin)
|
||||||
|
.field("s", &"..")
|
||||||
.finish(),
|
.finish(),
|
||||||
Scope::Body { id, s: _ } => {
|
Scope::Body { id, s: _ } => {
|
||||||
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
|
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
|
||||||
|
@ -638,6 +647,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, move |this| intravisit::walk_fn(this, fk, fd, b, s, hir_id));
|
self.with(scope, move |this| intravisit::walk_fn(this, fk, fd, b, s, hir_id));
|
||||||
}
|
}
|
||||||
|
@ -753,6 +763,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
s: ROOT_SCOPE,
|
s: ROOT_SCOPE,
|
||||||
allow_late_bound: false,
|
allow_late_bound: false,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||||
|
@ -818,6 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
// a bare fn has no bounds, so everything
|
// a bare fn has no bounds, so everything
|
||||||
|
@ -1006,6 +1018,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: false,
|
allow_late_bound: false,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
this.with(scope, |this| {
|
this.with(scope, |this| {
|
||||||
this.visit_generics(generics);
|
this.visit_generics(generics);
|
||||||
|
@ -1026,6 +1039,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: false,
|
allow_late_bound: false,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||||
|
@ -1084,6 +1098,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: true,
|
opaque_type_parent: true,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: false,
|
allow_late_bound: false,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||||
|
@ -1151,6 +1166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: true,
|
opaque_type_parent: true,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||||
|
@ -1266,6 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
ref bounded_ty,
|
ref bounded_ty,
|
||||||
bounds,
|
bounds,
|
||||||
ref bound_generic_params,
|
ref bound_generic_params,
|
||||||
|
origin,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
|
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
|
||||||
|
@ -1296,6 +1313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: Some(origin),
|
||||||
};
|
};
|
||||||
this.with(scope, |this| {
|
this.with(scope, |this| {
|
||||||
this.visit_ty(&bounded_ty);
|
this.visit_ty(&bounded_ty);
|
||||||
|
@ -1368,6 +1386,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type,
|
scope_type,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
intravisit::walk_param_bound(this, bound);
|
intravisit::walk_param_bound(this, bound);
|
||||||
|
@ -1420,6 +1439,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: false,
|
opaque_type_parent: false,
|
||||||
scope_type,
|
scope_type,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, |this| {
|
self.with(scope, |this| {
|
||||||
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
|
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
|
||||||
|
@ -1680,6 +1700,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
opaque_type_parent: true,
|
opaque_type_parent: true,
|
||||||
scope_type: BinderScopeType::Normal,
|
scope_type: BinderScopeType::Normal,
|
||||||
allow_late_bound: true,
|
allow_late_bound: true,
|
||||||
|
where_bound_origin: None,
|
||||||
};
|
};
|
||||||
self.with(scope, walk);
|
self.with(scope, walk);
|
||||||
}
|
}
|
||||||
|
@ -1783,12 +1804,48 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.insert_lifetime(lifetime_ref, def);
|
self.insert_lifetime(lifetime_ref, def);
|
||||||
} else {
|
return;
|
||||||
self.tcx.sess.delay_span_bug(
|
|
||||||
lifetime_ref.span,
|
|
||||||
&format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
|
||||||
|
// AST-based resolution does not care for impl-trait desugaring, which are the
|
||||||
|
// responibility of lowering. This may create a mismatch between the resolution
|
||||||
|
// AST found (`region_def_id`) which points to HRTB, and what HIR allows.
|
||||||
|
// ```
|
||||||
|
// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// In such case, walk back the binders to diagnose it properly.
|
||||||
|
let mut scope = self.scope;
|
||||||
|
loop {
|
||||||
|
match *scope {
|
||||||
|
Scope::Binder {
|
||||||
|
where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
|
||||||
|
} => {
|
||||||
|
let mut err = self.tcx.sess.struct_span_err(
|
||||||
|
lifetime_ref.span,
|
||||||
|
"`impl Trait` can only mention lifetimes bound at the fn or impl level",
|
||||||
|
);
|
||||||
|
err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
|
||||||
|
err.emit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Scope::Root => break,
|
||||||
|
Scope::Binder { s, .. }
|
||||||
|
| Scope::Body { s, .. }
|
||||||
|
| Scope::Elision { s, .. }
|
||||||
|
| Scope::ObjectLifetimeDefault { s, .. }
|
||||||
|
| Scope::Supertrait { s, .. }
|
||||||
|
| Scope::TraitRefBoundary { s, .. } => {
|
||||||
|
scope = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tcx.sess.delay_span_bug(
|
||||||
|
lifetime_ref.span,
|
||||||
|
&format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_segment_args(
|
fn visit_segment_args(
|
||||||
|
|
|
@ -5,7 +5,7 @@ use super::{
|
||||||
|
|
||||||
use crate::autoderef::Autoderef;
|
use crate::autoderef::Autoderef;
|
||||||
use crate::infer::InferCtxt;
|
use crate::infer::InferCtxt;
|
||||||
use crate::traits::normalize_projection_type;
|
use crate::traits::normalize_to;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
|
@ -2706,55 +2706,43 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||||
|
|
||||||
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
|
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
|
||||||
|
|
||||||
// Do not check on infer_types to avoid panic in evaluate_obligation.
|
|
||||||
if self_ty.has_infer_types() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let self_ty = self.tcx.erase_regions(self_ty);
|
|
||||||
|
|
||||||
let impls_future = self.type_implements_trait(
|
let impls_future = self.type_implements_trait(
|
||||||
future_trait,
|
future_trait,
|
||||||
self_ty.skip_binder(),
|
self.tcx.erase_late_bound_regions(self_ty),
|
||||||
ty::List::empty(),
|
ty::List::empty(),
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
);
|
);
|
||||||
|
if !impls_future.must_apply_modulo_regions() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
||||||
// `<T as Future>::Output`
|
// `<T as Future>::Output`
|
||||||
let projection_ty = ty::ProjectionTy {
|
let projection_ty = trait_pred.map_bound(|trait_pred| {
|
||||||
// `T`
|
self.tcx.mk_projection(
|
||||||
substs: self.tcx.mk_substs_trait(
|
item_def_id,
|
||||||
trait_pred.self_ty().skip_binder(),
|
// Future::Output has no substs
|
||||||
&self.fresh_substs_for_item(span, item_def_id)[1..],
|
self.tcx.mk_substs_trait(trait_pred.self_ty(), &[]),
|
||||||
),
|
)
|
||||||
// `Future::Output`
|
});
|
||||||
item_def_id,
|
let projection_ty = normalize_to(
|
||||||
};
|
&mut SelectionContext::new(self),
|
||||||
|
|
||||||
let mut selcx = SelectionContext::new(self);
|
|
||||||
|
|
||||||
let mut obligations = vec![];
|
|
||||||
let normalized_ty = normalize_projection_type(
|
|
||||||
&mut selcx,
|
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
projection_ty,
|
|
||||||
obligation.cause.clone(),
|
obligation.cause.clone(),
|
||||||
0,
|
projection_ty,
|
||||||
&mut obligations,
|
&mut vec![],
|
||||||
);
|
);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"suggest_await_before_try: normalized_projection_type {:?}",
|
"suggest_await_before_try: normalized_projection_type {:?}",
|
||||||
self.resolve_vars_if_possible(normalized_ty)
|
self.resolve_vars_if_possible(projection_ty)
|
||||||
);
|
);
|
||||||
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
|
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
trait_pred.map_bound(|trait_pred| (trait_pred, normalized_ty.ty().unwrap())),
|
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
|
||||||
);
|
);
|
||||||
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
|
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
|
||||||
if self.predicate_may_hold(&try_obligation)
|
if self.predicate_may_hold(&try_obligation)
|
||||||
&& impls_future.must_apply_modulo_regions()
|
|
||||||
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||||
&& snippet.ends_with('?')
|
&& snippet.ends_with('?')
|
||||||
{
|
{
|
||||||
|
|
|
@ -532,7 +532,7 @@ pub const fn null<T>() -> *const T {
|
||||||
#[rustc_diagnostic_item = "ptr_null"]
|
#[rustc_diagnostic_item = "ptr_null"]
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg(not(bootstrap))]
|
||||||
pub const fn null<T: ?Sized + Thin>() -> *const T {
|
pub const fn null<T: ?Sized + Thin>() -> *const T {
|
||||||
from_raw_parts(0 as *const (), ())
|
from_raw_parts(invalid(0), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a null mutable raw pointer.
|
/// Creates a null mutable raw pointer.
|
||||||
|
@ -709,7 +709,7 @@ where
|
||||||
#[rustc_diagnostic_item = "ptr_null_mut"]
|
#[rustc_diagnostic_item = "ptr_null_mut"]
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg(not(bootstrap))]
|
||||||
pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
|
pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
|
||||||
from_raw_parts_mut(0 as *mut (), ())
|
from_raw_parts_mut(invalid_mut(0), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forms a raw slice from a pointer and a length.
|
/// Forms a raw slice from a pointer and a length.
|
||||||
|
|
|
@ -94,6 +94,10 @@ There are a number of supported commands:
|
||||||
in the specified file. The number of occurrences must match the given
|
in the specified file. The number of occurrences must match the given
|
||||||
count.
|
count.
|
||||||
|
|
||||||
|
* `@count PATH XPATH TEXT COUNT` checks for the occurrence of the given XPath
|
||||||
|
with the given text in the specified file. The number of occurrences must
|
||||||
|
match the given count.
|
||||||
|
|
||||||
* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
|
* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
|
||||||
A snapshot test captures a subtree of the DOM, at the location
|
A snapshot test captures a subtree of the DOM, at the location
|
||||||
determined by the XPath, and compares it to a pre-recorded value
|
determined by the XPath, and compares it to a pre-recorded value
|
||||||
|
@ -382,9 +386,10 @@ def check_tree_attr(tree, path, attr, pat, regexp):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def check_tree_text(tree, path, pat, regexp):
|
# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
|
||||||
|
def check_tree_text(tree, path, pat, regexp, stop_at_first):
|
||||||
path = normalize_xpath(path)
|
path = normalize_xpath(path)
|
||||||
ret = False
|
match_count = 0
|
||||||
try:
|
try:
|
||||||
for e in tree.findall(path):
|
for e in tree.findall(path):
|
||||||
try:
|
try:
|
||||||
|
@ -392,13 +397,14 @@ def check_tree_text(tree, path, pat, regexp):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
ret = check_string(value, pat, regexp)
|
if check_string(value, pat, regexp):
|
||||||
if ret:
|
match_count += 1
|
||||||
break
|
if stop_at_first:
|
||||||
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
print('Failed to get path "{}"'.format(path))
|
print('Failed to get path "{}"'.format(path))
|
||||||
raise
|
raise
|
||||||
return ret
|
return match_count
|
||||||
|
|
||||||
|
|
||||||
def get_tree_count(tree, path):
|
def get_tree_count(tree, path):
|
||||||
|
@ -518,6 +524,19 @@ def print_err(lineno, context, err, message=None):
|
||||||
stderr("\t{}".format(context))
|
stderr("\t{}".format(context))
|
||||||
|
|
||||||
|
|
||||||
|
def get_nb_matching_elements(cache, c, regexp, stop_at_first):
|
||||||
|
tree = cache.get_tree(c.args[0])
|
||||||
|
pat, sep, attr = c.args[1].partition('/@')
|
||||||
|
if sep: # attribute
|
||||||
|
tree = cache.get_tree(c.args[0])
|
||||||
|
return check_tree_attr(tree, pat, attr, c.args[2], False)
|
||||||
|
else: # normalized text
|
||||||
|
pat = c.args[1]
|
||||||
|
if pat.endswith('/text()'):
|
||||||
|
pat = pat[:-7]
|
||||||
|
return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)
|
||||||
|
|
||||||
|
|
||||||
ERR_COUNT = 0
|
ERR_COUNT = 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -538,16 +557,7 @@ def check_command(c, cache):
|
||||||
ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
|
ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
|
||||||
elif len(c.args) == 3: # @has/matches <path> <pat> <match> = XML tree test
|
elif len(c.args) == 3: # @has/matches <path> <pat> <match> = XML tree test
|
||||||
cerr = "`XPATH PATTERN` did not match"
|
cerr = "`XPATH PATTERN` did not match"
|
||||||
tree = cache.get_tree(c.args[0])
|
ret = get_nb_matching_elements(cache, c, regexp, True) != 0
|
||||||
pat, sep, attr = c.args[1].partition('/@')
|
|
||||||
if sep: # attribute
|
|
||||||
tree = cache.get_tree(c.args[0])
|
|
||||||
ret = check_tree_attr(tree, pat, attr, c.args[2], regexp)
|
|
||||||
else: # normalized text
|
|
||||||
pat = c.args[1]
|
|
||||||
if pat.endswith('/text()'):
|
|
||||||
pat = pat[:-7]
|
|
||||||
ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp)
|
|
||||||
else:
|
else:
|
||||||
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
||||||
|
|
||||||
|
@ -557,6 +567,11 @@ def check_command(c, cache):
|
||||||
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
|
found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
|
||||||
cerr = "Expected {} occurrences but found {}".format(expected, found)
|
cerr = "Expected {} occurrences but found {}".format(expected, found)
|
||||||
ret = expected == found
|
ret = expected == found
|
||||||
|
elif len(c.args) == 4: # @count <path> <pat> <text> <count> = count test
|
||||||
|
expected = int(c.args[3])
|
||||||
|
found = get_nb_matching_elements(cache, c, False, False)
|
||||||
|
cerr = "Expected {} occurrences but found {}".format(expected, found)
|
||||||
|
ret = found == expected
|
||||||
else:
|
else:
|
||||||
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
|
||||||
|
|
||||||
|
|
|
@ -1600,6 +1600,13 @@ fn render_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
|
if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
|
||||||
|
if trait_.is_none() && i.inner_impl().items.is_empty() {
|
||||||
|
w.write_str(
|
||||||
|
"<div class=\"item-info\">\
|
||||||
|
<div class=\"stab empty-impl\">This impl block contains no items.</div>
|
||||||
|
</div>",
|
||||||
|
);
|
||||||
|
}
|
||||||
write!(
|
write!(
|
||||||
w,
|
w,
|
||||||
"<div class=\"docblock\">{}</div>",
|
"<div class=\"docblock\">{}</div>",
|
||||||
|
|
|
@ -281,9 +281,13 @@ details.undocumented > summary::before {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Created this empty rule to satisfy the theme checks. */
|
||||||
|
.stab.empty-impl {}
|
||||||
|
|
||||||
.stab.unstable,
|
.stab.unstable,
|
||||||
.stab.deprecated,
|
.stab.deprecated,
|
||||||
.stab.portability {
|
.stab.portability,
|
||||||
|
.stab.empty-impl {
|
||||||
color: #c5c5c5;
|
color: #c5c5c5;
|
||||||
background: #314559 !important;
|
background: #314559 !important;
|
||||||
border-style: none !important;
|
border-style: none !important;
|
||||||
|
|
|
@ -266,6 +266,7 @@ details.undocumented > summary::before {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
|
||||||
.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
|
.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
|
||||||
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
|
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
|
||||||
.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
|
.stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
|
||||||
|
|
|
@ -255,6 +255,7 @@ details.undocumented > summary::before {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; }
|
||||||
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
||||||
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
|
.stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
|
||||||
.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
|
.stab.portability { background: #F3DFFF; border-color: #b07bdb; }
|
||||||
|
|
|
@ -124,8 +124,9 @@ pub(crate) struct ImplStripper<'a> {
|
||||||
impl<'a> DocFolder for ImplStripper<'a> {
|
impl<'a> DocFolder for ImplStripper<'a> {
|
||||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||||
if let clean::ImplItem(ref imp) = *i.kind {
|
if let clean::ImplItem(ref imp) = *i.kind {
|
||||||
// emptied none trait impls can be stripped
|
// Impl blocks can be skipped if they are: empty; not a trait impl; and have no
|
||||||
if imp.trait_.is_none() && imp.items.is_empty() {
|
// documentation.
|
||||||
|
if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Some(did) = imp.for_.def_id(self.cache) {
|
if let Some(did) = imp.for_.def_id(self.cache) {
|
||||||
|
|
20
src/test/rustdoc/empty-impl-block.rs
Normal file
20
src/test/rustdoc/empty-impl-block.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
|
// @has 'foo/struct.Foo.html'
|
||||||
|
pub struct Foo;
|
||||||
|
|
||||||
|
// @has - '//*[@class="docblock"]' 'Hello empty impl block!'
|
||||||
|
// @has - '//*[@class="item-info"]' 'This impl block contains no items.'
|
||||||
|
/// Hello empty impl block!
|
||||||
|
impl Foo {}
|
||||||
|
// We ensure that this empty impl block without doc isn't rendered.
|
||||||
|
// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 1
|
||||||
|
impl Foo {}
|
||||||
|
|
||||||
|
// Just to ensure that empty trait impl blocks are rendered.
|
||||||
|
pub struct Another;
|
||||||
|
pub trait Bar {}
|
||||||
|
|
||||||
|
// @has 'foo/struct.Another.html'
|
||||||
|
// @has - '//h3[@class="code-header in-band"]' 'impl Bar for Another'
|
||||||
|
impl Bar for Another {}
|
|
@ -3,5 +3,6 @@ struct Lorem {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _foo: *mut Lorem = core::ptr::NonNull::dangling().as_ptr(); // no error here
|
// Testing `as` casts, so deliberately not using `ptr::null`.
|
||||||
|
let _foo: *mut Lorem = 0 as *mut _; // no error here
|
||||||
}
|
}
|
||||||
|
|
8
src/test/ui/impl-trait/universal_wrong_hrtb.rs
Normal file
8
src/test/ui/impl-trait/universal_wrong_hrtb.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
trait Trait<'a> {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
|
||||||
|
//~^ ERROR `impl Trait` can only mention lifetimes bound at the fn or impl level
|
||||||
|
|
||||||
|
fn main() {}
|
14
src/test/ui/impl-trait/universal_wrong_hrtb.stderr
Normal file
14
src/test/ui/impl-trait/universal_wrong_hrtb.stderr
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
error: `impl Trait` can only mention lifetimes bound at the fn or impl level
|
||||||
|
--> $DIR/universal_wrong_hrtb.rs:5:73
|
||||||
|
|
|
||||||
|
LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
note: lifetime declared here
|
||||||
|
--> $DIR/universal_wrong_hrtb.rs:5:39
|
||||||
|
|
|
||||||
|
LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -47,6 +47,12 @@ error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
|
||||||
LL | 'l4 0;
|
LL | 'l4 0;
|
||||||
| ^ expected `while`, `for`, `loop` or `{` after a label
|
| ^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider removing the label
|
||||||
|
|
|
||||||
|
LL - 'l4 0;
|
||||||
|
LL + 0;
|
||||||
|
|
|
||||||
|
|
||||||
error: labeled expression must be followed by `:`
|
error: labeled expression must be followed by `:`
|
||||||
--> $DIR/labeled-no-colon-expr.rs:8:9
|
--> $DIR/labeled-no-colon-expr.rs:8:9
|
||||||
|
|
27
src/test/ui/parser/recover-labeled-non-block-expr.fixed
Normal file
27
src/test/ui/parser/recover-labeled-non-block-expr.fixed
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// run-rustfix
|
||||||
|
#![feature(label_break_value)]
|
||||||
|
fn main() {
|
||||||
|
let _ = 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
||||||
|
match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
'label: { match () { () => break 'label, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
#[allow(unused_labels)]
|
||||||
|
'label: { match () { () => 'lp: loop { break 'lp 0 }, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
||||||
|
let x = 1;
|
||||||
|
let _i = 'label: { match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
0 => 42,
|
||||||
|
1 if false => break 'label 17,
|
||||||
|
1 => {
|
||||||
|
if true {
|
||||||
|
break 'label 13
|
||||||
|
} else {
|
||||||
|
break 'label 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => 1,
|
||||||
|
} };
|
||||||
|
|
||||||
|
let other = 3;
|
||||||
|
let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
}
|
|
@ -1,5 +1,27 @@
|
||||||
|
// run-rustfix
|
||||||
|
#![feature(label_break_value)]
|
||||||
fn main() {
|
fn main() {
|
||||||
'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
let _ = 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
||||||
let _recovery_witness: () = 0; //~ ERROR mismatched types
|
'label: match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
'label: match () { () => break 'label, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
#[allow(unused_labels)]
|
||||||
|
'label: match () { () => 'lp: loop { break 'lp 0 }, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
||||||
|
let x = 1;
|
||||||
|
let _i = 'label: match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
0 => 42,
|
||||||
|
1 if false => break 'label 17,
|
||||||
|
1 => {
|
||||||
|
if true {
|
||||||
|
break 'label 13
|
||||||
|
} else {
|
||||||
|
break 'label 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let other = 3;
|
||||||
|
let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other }); //~ ERROR expected `while`, `for`, `loop` or `{` after a label
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,75 @@
|
||||||
error: expected `while`, `for`, `loop` or `{` after a label
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
--> $DIR/recover-labeled-non-block-expr.rs:2:13
|
--> $DIR/recover-labeled-non-block-expr.rs:4:21
|
||||||
|
|
|
|
||||||
LL | 'label: 1 + 1;
|
LL | let _ = 'label: 1 + 1;
|
||||||
| ^ expected `while`, `for`, `loop` or `{` after a label
|
| ^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/recover-labeled-non-block-expr.rs:4:33
|
|
||||||
|
|
|
|
||||||
LL | let _recovery_witness: () = 0;
|
help: consider removing the label
|
||||||
| -- ^ expected `()`, found integer
|
|
|
||||||
| |
|
LL - let _ = 'label: 1 + 1;
|
||||||
| expected due to this
|
LL + let _ = 1 + 1;
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
--> $DIR/recover-labeled-non-block-expr.rs:6:13
|
||||||
|
|
|
||||||
|
LL | 'label: match () { () => {}, };
|
||||||
|
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider removing the label
|
||||||
|
|
|
||||||
|
LL - 'label: match () { () => {}, };
|
||||||
|
LL + match () { () => {}, };
|
||||||
|
|
|
||||||
|
|
||||||
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
--> $DIR/recover-labeled-non-block-expr.rs:7:13
|
||||||
|
|
|
||||||
|
LL | 'label: match () { () => break 'label, };
|
||||||
|
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider enclosing expression in a block
|
||||||
|
|
|
||||||
|
LL | 'label: { match () { () => break 'label, } };
|
||||||
|
| + +
|
||||||
|
|
||||||
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
--> $DIR/recover-labeled-non-block-expr.rs:9:13
|
||||||
|
|
|
||||||
|
LL | 'label: match () { () => 'lp: loop { break 'lp 0 }, };
|
||||||
|
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider enclosing expression in a block
|
||||||
|
|
|
||||||
|
LL | 'label: { match () { () => 'lp: loop { break 'lp 0 }, } };
|
||||||
|
| + +
|
||||||
|
|
||||||
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
--> $DIR/recover-labeled-non-block-expr.rs:12:22
|
||||||
|
|
|
||||||
|
LL | let _i = 'label: match x {
|
||||||
|
| ^^^^^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider enclosing expression in a block
|
||||||
|
|
|
||||||
|
LL ~ let _i = 'label: { match x {
|
||||||
|
LL | 0 => 42,
|
||||||
|
LL | 1 if false => break 'label 17,
|
||||||
|
LL | 1 => {
|
||||||
|
LL | if true {
|
||||||
|
LL | break 'label 13
|
||||||
|
...
|
||||||
|
|
||||||
|
error: expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
--> $DIR/recover-labeled-non-block-expr.rs:26:24
|
||||||
|
|
|
||||||
|
LL | let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other });
|
||||||
|
| ^ expected `while`, `for`, `loop` or `{` after a label
|
||||||
|
|
|
||||||
|
help: consider enclosing expression in a block
|
||||||
|
|
|
||||||
|
LL | let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) };
|
||||||
|
| + +
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
|
||||||
|
|
19
src/test/ui/suggestions/issue-97704.fixed
Normal file
19
src/test/ui/suggestions/issue-97704.fixed
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// edition:2021
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
async fn foo() -> Result<(), i32> {
|
||||||
|
func(async { Ok::<_, i32>(()) }).await?;
|
||||||
|
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func<T>(fut: impl Future<Output = T>) -> T {
|
||||||
|
fut.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
19
src/test/ui/suggestions/issue-97704.rs
Normal file
19
src/test/ui/suggestions/issue-97704.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// edition:2021
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
|
async fn foo() -> Result<(), i32> {
|
||||||
|
func(async { Ok::<_, i32>(()) })?;
|
||||||
|
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func<T>(fut: impl Future<Output = T>) -> T {
|
||||||
|
fut.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
15
src/test/ui/suggestions/issue-97704.stderr
Normal file
15
src/test/ui/suggestions/issue-97704.stderr
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
error[E0277]: the `?` operator can only be applied to values that implement `Try`
|
||||||
|
--> $DIR/issue-97704.rs:9:5
|
||||||
|
|
|
||||||
|
LL | func(async { Ok::<_, i32>(()) })?;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), i32>>`
|
||||||
|
|
|
||||||
|
= help: the trait `Try` is not implemented for `impl Future<Output = Result<(), i32>>`
|
||||||
|
help: consider `await`ing on the `Future`
|
||||||
|
|
|
||||||
|
LL | func(async { Ok::<_, i32>(()) }).await?;
|
||||||
|
| ++++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Reference in a new issue