From 5881fd5c953a1af80f870b59de9726ae9ae62b37 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 14 Nov 2021 17:11:40 +0100 Subject: [PATCH 01/13] Display empty impl blocks if they have documentations --- src/librustdoc/html/render/mod.rs | 7 +++++++ src/librustdoc/html/static/css/themes/ayu.css | 3 ++- src/librustdoc/html/static/css/themes/dark.css | 1 + src/librustdoc/html/static/css/themes/light.css | 1 + src/librustdoc/passes/stripper.rs | 5 +++-- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b8d6b340b14dd..178e0cd8f6488 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1600,6 +1600,13 @@ fn render_impl( } if let Some(ref dox) = i.impl_item.collapsed_doc_value() { + if trait_.is_none() && i.inner_impl().items.is_empty() { + w.write_str( + "
\ +
This impl block contains no items.
+
", + ); + } write!( w, "
{}
", diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 7303cecc0d61a..ad3831e073a26 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -283,7 +283,8 @@ details.undocumented > summary::before { .stab.unstable, .stab.deprecated, -.stab.portability { +.stab.portability, +.stab.empty-impl { color: #c5c5c5; background: #314559 !important; border-style: none !important; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 34a4f446b560b..071ad006ed350 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -266,6 +266,7 @@ details.undocumented > summary::before { color: #ddd; } +.stab.empty-impl { 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.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index aa6ad2f547333..5c3789bf4630a 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -255,6 +255,7 @@ details.undocumented > summary::before { color: #000; } +.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; } .stab.unstable { background: #FFF5D6; border-color: #FFC600; } .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; } .stab.portability { background: #F3DFFF; border-color: #b07bdb; } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index d5db919dc4b2a..0fd124e615415 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -124,8 +124,9 @@ pub(crate) struct ImplStripper<'a> { impl<'a> DocFolder for ImplStripper<'a> { fn fold_item(&mut self, i: Item) -> Option { if let clean::ImplItem(ref imp) = *i.kind { - // emptied none trait impls can be stripped - if imp.trait_.is_none() && imp.items.is_empty() { + // Impl blocks can be skipped if they are: empty; not a trait impl; and have no + // documentation. + if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() { return None; } if let Some(did) = imp.for_.def_id(self.cache) { From 198dea59d0b1d6305d8b18a9f4ba6064968c7803 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 14 Nov 2021 17:12:12 +0100 Subject: [PATCH 02/13] Update test script for src/test/rustdoc to allow to add a filter for the @count command --- src/etc/htmldocck.py | 47 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 269b6868e29f9..3ff7e89a759a4 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -94,6 +94,10 @@ in the specified file. The number of occurrences must match the given 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. A snapshot test captures a subtree of the DOM, at the location 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 -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) - ret = False + match_count = 0 try: for e in tree.findall(path): try: @@ -392,13 +397,14 @@ def check_tree_text(tree, path, pat, regexp): except KeyError: continue else: - ret = check_string(value, pat, regexp) - if ret: - break + if check_string(value, pat, regexp): + match_count += 1 + if stop_at_first: + break except Exception: print('Failed to get path "{}"'.format(path)) raise - return ret + return match_count def get_tree_count(tree, path): @@ -516,6 +522,19 @@ def print_err(lineno, context, err, message=None): 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 @@ -536,16 +555,7 @@ def check_command(c, cache): ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp) elif len(c.args) == 3: # @has/matches = XML tree test cerr = "`XPATH PATTERN` did not match" - tree = cache.get_tree(c.args[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) + ret = get_nb_matching_elements(cache, c, regexp, True) != 0 else: raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) @@ -555,6 +565,11 @@ def check_command(c, cache): found = get_tree_count(cache.get_tree(c.args[0]), c.args[1]) cerr = "Expected {} occurrences but found {}".format(expected, found) ret = expected == found + elif len(c.args) == 4: # @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: raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) From 51dd090d5ee0d42d3a0df17d64b86654b515fd13 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 14 Nov 2021 17:12:44 +0100 Subject: [PATCH 03/13] Add test for empty impl blocks --- src/test/rustdoc/empty-impl-block.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/rustdoc/empty-impl-block.rs diff --git a/src/test/rustdoc/empty-impl-block.rs b/src/test/rustdoc/empty-impl-block.rs new file mode 100644 index 0000000000000..6a2a254f63a7f --- /dev/null +++ b/src/test/rustdoc/empty-impl-block.rs @@ -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 {} From eca12e33e9aca609b6409625f6a617d03bc479d3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 15 Feb 2022 12:05:45 +0100 Subject: [PATCH 04/13] Fix theme checks --- src/librustdoc/html/static/css/themes/ayu.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index ad3831e073a26..8e0521d9ad6a1 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -281,6 +281,9 @@ details.undocumented > summary::before { color: #000; } +/* Created this empty rule to satisfy the theme checks. */ +.stab.empty-impl {} + .stab.unstable, .stab.deprecated, .stab.portability, From bacd8adeda36fd3ddbea0328f375595872f1e08c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 27 May 2022 11:02:11 +0200 Subject: [PATCH 05/13] Fail gracefully when encountering an HRTB in APIT. --- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_resolve/src/late/lifetimes.rs | 71 +++++++++++++++++-- .../ui/impl-trait/universal_wrong_hrtb.rs | 8 +++ .../ui/impl-trait/universal_wrong_hrtb.stderr | 14 ++++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/impl-trait/universal_wrong_hrtb.rs create mode 100644 src/test/ui/impl-trait/universal_wrong_hrtb.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b98d434111862..dbbdb8b192b14 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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 { WhereClause, GenericParam, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 11f80b314d770..bfc42902be4ca 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -207,6 +207,13 @@ enum Scope<'a> { /// In some cases not allowing late bounds allows us to avoid ICEs. /// This is almost ways set to true. 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, }, /// 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, scope_type, hir_id, - s: _, allow_late_bound, + where_bound_origin, + s: _, } => f .debug_struct("Binder") .field("lifetimes", lifetimes) @@ -286,8 +294,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) - .field("s", &"..") .field("allow_late_bound", allow_late_bound) + .field("where_bound_origin", where_bound_origin) + .field("s", &"..") .finish(), Scope::Body { id, s: _ } => { 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, scope_type: BinderScopeType::Normal, allow_late_bound: true, + where_bound_origin: None, }; 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, s: ROOT_SCOPE, allow_late_bound: false, + where_bound_origin: None, }; self.with(scope, |this| { let scope = Scope::TraitRefBoundary { s: this.scope }; @@ -818,6 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, + where_bound_origin: None, }; self.with(scope, |this| { // 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, scope_type: BinderScopeType::Normal, allow_late_bound: false, + where_bound_origin: None, }; this.with(scope, |this| { this.visit_generics(generics); @@ -1026,6 +1039,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: false, + where_bound_origin: None, }; self.with(scope, |this| { let scope = Scope::TraitRefBoundary { s: this.scope }; @@ -1084,6 +1098,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: false, + where_bound_origin: None, }; self.with(scope, |this| { let scope = Scope::TraitRefBoundary { s: this.scope }; @@ -1151,6 +1166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: true, + where_bound_origin: None, }; self.with(scope, |this| { let scope = Scope::TraitRefBoundary { s: this.scope }; @@ -1266,6 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ref bounded_ty, bounds, ref bound_generic_params, + origin, .. }) => { let (lifetimes, binders): (FxIndexMap, Vec<_>) = @@ -1296,6 +1313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, + where_bound_origin: Some(origin), }; this.with(scope, |this| { this.visit_ty(&bounded_ty); @@ -1368,6 +1386,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: false, scope_type, allow_late_bound: true, + where_bound_origin: None, }; self.with(scope, |this| { intravisit::walk_param_bound(this, bound); @@ -1420,6 +1439,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: false, scope_type, allow_late_bound: true, + where_bound_origin: None, }; self.with(scope, |this| { 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, scope_type: BinderScopeType::Normal, allow_late_bound: true, + where_bound_origin: None, }; self.with(scope, walk); } @@ -1783,12 +1804,48 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } self.insert_lifetime(lifetime_ref, def); - } else { - self.tcx.sess.delay_span_bug( - lifetime_ref.span, - &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,), - ); + return; } + + // 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( diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.rs b/src/test/ui/impl-trait/universal_wrong_hrtb.rs new file mode 100644 index 0000000000000..b9551c2ceb0e5 --- /dev/null +++ b/src/test/ui/impl-trait/universal_wrong_hrtb.rs @@ -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() {} diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.stderr b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr new file mode 100644 index 0000000000000..37eb8dfa1a141 --- /dev/null +++ b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr @@ -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 + From 2d2577cdec6121e57facd55a6355ed8bf0c2c9b1 Mon Sep 17 00:00:00 2001 From: klensy Date: Sun, 5 Jun 2022 07:02:32 +0300 Subject: [PATCH 06/13] typo: `-Zcodegen-backend=llvm -Cpasses=list` should work now --- compiler/rustc_driver/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 179a184536efa..1a7972716d3df 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1055,13 +1055,7 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.iter().any(|x| *x == "passes=list") { - let backend_name = debug_flags.iter().find_map(|x| { - if x.starts_with("codegen-backend=") { - Some(&x["codegen-backends=".len()..]) - } else { - None - } - }); + let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); get_codegen_backend(&None, backend_name).print_passes(); return None; } From f21c0a274ebfb35612359e32bee965afb09640d6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 5 Jun 2022 16:45:29 +0400 Subject: [PATCH 07/13] Suggest adding `{}` for `'label: non_block_expr` --- compiler/rustc_parse/src/parser/expr.rs | 30 ++++++++++- .../ui/parser/labeled-no-colon-expr.stderr | 5 ++ .../recover-labeled-non-block-expr.fixed | 25 +++++++++ .../parser/recover-labeled-non-block-expr.rs | 22 +++++++- .../recover-labeled-non-block-expr.stderr | 52 +++++++++++++++---- 5 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/parser/recover-labeled-non-block-expr.fixed diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 63c7decbb2fe6..761d521a07ec6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -13,10 +13,12 @@ use rustc_ast::tokenstream::Spacing; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; +use rustc_ast::StmtKind; 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::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast_pretty::pprust; +use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; @@ -1548,9 +1550,33 @@ impl<'a> Parser<'a> { Ok(self.mk_expr_err(lo)) } else { 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`. - self.parse_expr() + let expr = self.parse_expr().map(|expr| { + let span = expr.span; + 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 { diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr index 26884dc5d7452..4d0d9c4a00bf6 100644 --- a/src/test/ui/parser/labeled-no-colon-expr.stderr +++ b/src/test/ui/parser/labeled-no-colon-expr.stderr @@ -47,6 +47,11 @@ error: expected `while`, `for`, `loop` or `{` after a label | LL | 'l4 0; | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: consider enclosing expression in a block + | +LL | 'l4 {0}; + | + + error: labeled expression must be followed by `:` --> $DIR/labeled-no-colon-expr.rs:8:9 diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed new file mode 100644 index 0000000000000..dbe2f2e0bddec --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![feature(label_break_value)] +fn main() { + #[allow(unused_labels)] + 'label: {1 + 1}; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + + 'label: {match () { () => break 'label, }}; //~ 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 +} diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs index be92170acf027..9c22c07371296 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.rs +++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs @@ -1,5 +1,25 @@ +// run-rustfix +#![feature(label_break_value)] fn main() { + #[allow(unused_labels)] 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label - let _recovery_witness: () = 0; //~ ERROR mismatched types + 'label: match () { () => break 'label, }; //~ 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 } diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr index 771a915288c56..2e4e45d617138 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -1,17 +1,51 @@ 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:5:13 | LL | 'label: 1 + 1; | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: consider enclosing expression in a block + | +LL | 'label: {1 + 1}; + | + + + +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[E0308]: mismatched types - --> $DIR/recover-labeled-non-block-expr.rs:4:33 +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/recover-labeled-non-block-expr.rs:10: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:24: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 _recovery_witness: () = 0; - | -- ^ expected `()`, found integer - | | - | expected due to this +LL | let _val = 'label: {(1, if other == 3 { break 'label (2, 3) } else { other })}; + | + + -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. From 4a41c3574251ced43076acd28a9e63a5937802d5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Jun 2022 11:44:12 -0400 Subject: [PATCH 08/13] use strict provenance APIs --- library/core/src/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5b04ae7b07e69..a3b4e5886ef68 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -532,7 +532,7 @@ pub const fn null() -> *const T { #[rustc_diagnostic_item = "ptr_null"] #[cfg(not(bootstrap))] pub const fn null() -> *const T { - from_raw_parts(0 as *const (), ()) + from_raw_parts(invalid(0), ()) } /// Creates a null mutable raw pointer. @@ -709,7 +709,7 @@ where #[rustc_diagnostic_item = "ptr_null_mut"] #[cfg(not(bootstrap))] pub const fn null_mut() -> *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. From e5dd503d6194be85f8c016425b723d39903ebf95 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Jun 2022 12:00:43 -0400 Subject: [PATCH 09/13] restore a test --- src/test/ui/cast/casts-issue-46365.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/ui/cast/casts-issue-46365.rs b/src/test/ui/cast/casts-issue-46365.rs index a2205b718c1ca..50aa1a856a716 100644 --- a/src/test/ui/cast/casts-issue-46365.rs +++ b/src/test/ui/cast/casts-issue-46365.rs @@ -3,5 +3,6 @@ struct Lorem { } 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 } From c6e5bb32fbb3d0a85189e56086726c183f68ad0c Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 5 Jun 2022 23:12:51 +0400 Subject: [PATCH 10/13] Do not suggest adding labeled block if there are no labeled breaks --- compiler/rustc_parse/src/parser/expr.rs | 23 +++++++++++++++++++ .../ui/parser/labeled-no-colon-expr.stderr | 5 ---- .../recover-labeled-non-block-expr.fixed | 5 ++-- .../parser/recover-labeled-non-block-expr.rs | 5 ++-- .../recover-labeled-non-block-expr.stderr | 19 ++++----------- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 761d521a07ec6..0d252c3d465d2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -13,6 +13,7 @@ use rustc_ast::tokenstream::Spacing; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; 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::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; @@ -1556,6 +1557,28 @@ impl<'a> Parser<'a> { // Continue as an expression in an effort to recover on `'label: non_block_expr`. let expr = self.parse_expr().map(|expr| { + 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 + // so if the label is not used, just return the unmodified expression + if !found_labeled_breaks { + return expr; + } + let span = expr.span; let sugg_msg = "consider enclosing expression in a block"; let suggestions = vec![ diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr index 4d0d9c4a00bf6..26884dc5d7452 100644 --- a/src/test/ui/parser/labeled-no-colon-expr.stderr +++ b/src/test/ui/parser/labeled-no-colon-expr.stderr @@ -47,11 +47,6 @@ error: expected `while`, `for`, `loop` or `{` after a label | LL | 'l4 0; | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: consider enclosing expression in a block - | -LL | 'l4 {0}; - | + + error: labeled expression must be followed by `:` --> $DIR/labeled-no-colon-expr.rs:8:9 diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed index dbe2f2e0bddec..aabda4a67a182 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.fixed +++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed @@ -1,8 +1,9 @@ // run-rustfix #![feature(label_break_value)] fn main() { - #[allow(unused_labels)] - 'label: {1 + 1}; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + // FIXME(waffle): add this back + // #[allow(unused_labels)] + // 'label: 1 + 1; // ERROR expected `while`, `for`, `loop` or `{` after a label 'label: {match () { () => break 'label, }}; //~ ERROR expected `while`, `for`, `loop` or `{` after a label diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs index 9c22c07371296..9244acabc2646 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.rs +++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs @@ -1,8 +1,9 @@ // run-rustfix #![feature(label_break_value)] fn main() { - #[allow(unused_labels)] - 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + // FIXME(waffle): add this back + // #[allow(unused_labels)] + // 'label: 1 + 1; // ERROR expected `while`, `for`, `loop` or `{` after a label 'label: match () { () => break 'label, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr index 2e4e45d617138..37843141ef3b1 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -1,16 +1,5 @@ error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:5:13 - | -LL | 'label: 1 + 1; - | ^ expected `while`, `for`, `loop` or `{` after a label - | -help: consider enclosing expression in a block - | -LL | 'label: {1 + 1}; - | + + - -error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:7:13 + --> $DIR/recover-labeled-non-block-expr.rs:8:13 | LL | 'label: match () { () => break 'label, }; | ^^^^^ expected `while`, `for`, `loop` or `{` after a label @@ -21,7 +10,7 @@ LL | 'label: {match () { () => break 'label, }}; | + + error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:10:22 + --> $DIR/recover-labeled-non-block-expr.rs:11:22 | LL | let _i = 'label: match x { | ^^^^^ expected `while`, `for`, `loop` or `{` after a label @@ -37,7 +26,7 @@ LL | break 'label 13 ... error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:24:24 + --> $DIR/recover-labeled-non-block-expr.rs:25:24 | LL | let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other }); | ^ expected `while`, `for`, `loop` or `{` after a label @@ -47,5 +36,5 @@ 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 4 previous errors +error: aborting due to 3 previous errors From f06f05174581702e822f1d866a06e0a6a22d0b3d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 5 Jun 2022 23:34:11 +0400 Subject: [PATCH 11/13] Suggest removing label in `'label: non_block_expr` --- compiler/rustc_parse/src/parser/expr.rs | 17 ++++++-- .../ui/parser/labeled-no-colon-expr.stderr | 6 +++ .../recover-labeled-non-block-expr.fixed | 7 +-- .../parser/recover-labeled-non-block-expr.rs | 7 +-- .../recover-labeled-non-block-expr.stderr | 43 +++++++++++++++++-- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0d252c3d465d2..0bc71de90ff1f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1557,6 +1557,8 @@ impl<'a> Parser<'a> { // Continue as an expression in an effort to recover on `'label: non_block_expr`. let expr = self.parse_expr().map(|expr| { + let span = expr.span; + let found_labeled_breaks = { struct FindLabeledBreaksVisitor(bool); @@ -1573,13 +1575,22 @@ impl<'a> Parser<'a> { vis.0 }; - // Suggestion involves adding a (as of time of writing this, unstable) labeled block - // so if the label is not used, just return the unmodified expression + // 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 span = expr.span; let sugg_msg = "consider enclosing expression in a block"; let suggestions = vec![ (span.shrink_to_lo(), "{".to_owned()), diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr index 26884dc5d7452..5c9597c440cbd 100644 --- a/src/test/ui/parser/labeled-no-colon-expr.stderr +++ b/src/test/ui/parser/labeled-no-colon-expr.stderr @@ -47,6 +47,12 @@ error: expected `while`, `for`, `loop` or `{` after a label | LL | 'l4 0; | ^ 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 `:` --> $DIR/labeled-no-colon-expr.rs:8:9 diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed index aabda4a67a182..3193d90d2d30d 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.fixed +++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed @@ -1,11 +1,12 @@ // run-rustfix #![feature(label_break_value)] fn main() { - // FIXME(waffle): add this back - // #[allow(unused_labels)] - // 'label: 1 + 1; // ERROR expected `while`, `for`, `loop` or `{` after a label + 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 diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs index 9244acabc2646..35862e2eef973 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.rs +++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs @@ -1,11 +1,12 @@ // run-rustfix #![feature(label_break_value)] fn main() { - // FIXME(waffle): add this back - // #[allow(unused_labels)] - // '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 + '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 diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr index 37843141ef3b1..e289c5db5cb9d 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -1,5 +1,29 @@ error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:8:13 + --> $DIR/recover-labeled-non-block-expr.rs:4:21 + | +LL | let _ = 'label: 1 + 1; + | ^ expected `while`, `for`, `loop` or `{` after a label + | +help: consider removing the label + | +LL - let _ = 'label: 1 + 1; +LL + let _ = 1 + 1; + | + +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 @@ -10,7 +34,18 @@ LL | 'label: {match () { () => break 'label, }}; | + + error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:11:22 + --> $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 @@ -26,7 +61,7 @@ LL | break 'label 13 ... error: expected `while`, `for`, `loop` or `{` after a label - --> $DIR/recover-labeled-non-block-expr.rs:25:24 + --> $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 @@ -36,5 +71,5 @@ 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 3 previous errors +error: aborting due to 6 previous errors From 4f85a73e516530844aecc44fb91d0db7604f7ac4 Mon Sep 17 00:00:00 2001 From: Waffle Maybe Date: Mon, 6 Jun 2022 01:35:37 +0400 Subject: [PATCH 12/13] Add spaces before and after expr in add {} suggestion Co-authored-by: Michael Goulet --- compiler/rustc_parse/src/parser/expr.rs | 4 ++-- .../ui/parser/recover-labeled-non-block-expr.fixed | 10 +++++----- .../parser/recover-labeled-non-block-expr.stderr | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0bc71de90ff1f..b786c52e6880f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1593,8 +1593,8 @@ impl<'a> Parser<'a> { let sugg_msg = "consider enclosing expression in a block"; let suggestions = vec![ - (span.shrink_to_lo(), "{".to_owned()), - (span.shrink_to_hi(), "}".to_owned()), + (span.shrink_to_lo(), "{ ".to_owned()), + (span.shrink_to_hi(), " }".to_owned()), ]; err.multipart_suggestion_verbose( diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed index 3193d90d2d30d..fe546a7197117 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.fixed +++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed @@ -4,12 +4,12 @@ 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 + '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 + '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 + let _i = 'label: { match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label 0 => 42, 1 if false => break 'label 17, 1 => { @@ -20,8 +20,8 @@ fn main() { } } _ => 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 + let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label } diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr index e289c5db5cb9d..767389c48088a 100644 --- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -30,8 +30,8 @@ LL | 'label: match () { () => break 'label, }; | help: consider enclosing expression in a block | -LL | 'label: {match () { () => break 'label, }}; - | + + +LL | 'label: { match () { () => break 'label, } }; + | + + error: expected `while`, `for`, `loop` or `{` after a label --> $DIR/recover-labeled-non-block-expr.rs:9:13 @@ -41,8 +41,8 @@ LL | 'label: match () { () => 'lp: loop { break 'lp 0 }, }; | help: consider enclosing expression in a block | -LL | 'label: {match () { () => 'lp: loop { break 'lp 0 }, }}; - | + + +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 @@ -52,7 +52,7 @@ LL | let _i = 'label: match x { | help: consider enclosing expression in a block | -LL ~ let _i = 'label: {match x { +LL ~ let _i = 'label: { match x { LL | 0 => 42, LL | 1 if false => break 'label 17, LL | 1 => { @@ -68,8 +68,8 @@ LL | let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { oth | help: consider enclosing expression in a block | -LL | let _val = 'label: {(1, if other == 3 { break 'label (2, 3) } else { other })}; - | + + +LL | let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; + | + + error: aborting due to 6 previous errors From d268b34da48373a54a3f4666b3b83277ad8f9db3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 3 Jun 2022 18:40:39 -0700 Subject: [PATCH 13/13] Do suggest_await_before_try with infer in self, clean up binders --- .../src/traits/error_reporting/suggestions.rs | 48 +++++++------------ src/test/ui/suggestions/issue-97704.fixed | 19 ++++++++ src/test/ui/suggestions/issue-97704.rs | 19 ++++++++ src/test/ui/suggestions/issue-97704.stderr | 15 ++++++ 4 files changed, 71 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/suggestions/issue-97704.fixed create mode 100644 src/test/ui/suggestions/issue-97704.rs create mode 100644 src/test/ui/suggestions/issue-97704.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index d0f20022bfbad..4dbb479ede5e1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5,7 +5,7 @@ use super::{ use crate::autoderef::Autoderef; 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::stack::ensure_sufficient_stack; @@ -2704,55 +2704,43 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); 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( future_trait, - self_ty.skip_binder(), + self.tcx.erase_late_bound_regions(self_ty), ty::List::empty(), obligation.param_env, ); + if !impls_future.must_apply_modulo_regions() { + return; + } let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; // `::Output` - let projection_ty = ty::ProjectionTy { - // `T` - substs: self.tcx.mk_substs_trait( - trait_pred.self_ty().skip_binder(), - &self.fresh_substs_for_item(span, item_def_id)[1..], - ), - // `Future::Output` - item_def_id, - }; - - let mut selcx = SelectionContext::new(self); - - let mut obligations = vec![]; - let normalized_ty = normalize_projection_type( - &mut selcx, + let projection_ty = trait_pred.map_bound(|trait_pred| { + self.tcx.mk_projection( + item_def_id, + // Future::Output has no substs + self.tcx.mk_substs_trait(trait_pred.self_ty(), &[]), + ) + }); + let projection_ty = normalize_to( + &mut SelectionContext::new(self), obligation.param_env, - projection_ty, obligation.cause.clone(), - 0, - &mut obligations, + projection_ty, + &mut vec![], ); debug!( "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( 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); 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) && snippet.ends_with('?') { diff --git a/src/test/ui/suggestions/issue-97704.fixed b/src/test/ui/suggestions/issue-97704.fixed new file mode 100644 index 0000000000000..c42bdfff5f9e6 --- /dev/null +++ b/src/test/ui/suggestions/issue-97704.fixed @@ -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(fut: impl Future) -> T { + fut.await +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-97704.rs b/src/test/ui/suggestions/issue-97704.rs new file mode 100644 index 0000000000000..5dfee6cac6097 --- /dev/null +++ b/src/test/ui/suggestions/issue-97704.rs @@ -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(fut: impl Future) -> T { + fut.await +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-97704.stderr b/src/test/ui/suggestions/issue-97704.stderr new file mode 100644 index 0000000000000..ca017be45ac47 --- /dev/null +++ b/src/test/ui/suggestions/issue-97704.stderr @@ -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>` + | + = help: the trait `Try` is not implemented for `impl Future>` +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`.