From 12ce6e9c60c662dc7181b70021145f191c0f9f3e Mon Sep 17 00:00:00 2001 From: Esteban Kuber Date: Thu, 2 Sep 2021 18:34:03 +0000 Subject: [PATCH] Detect bare blocks with type ascription that were meant to be a `struct` literal Address part of #34255. Potential improvement: silence the other knock down errors in `issue-34255-1.rs`. --- compiler/rustc_ast/src/ast.rs | 8 ++++++++ compiler/rustc_ast/src/mut_visit.rs | 2 +- .../rustc_builtin_macros/src/deriving/mod.rs | 1 + compiler/rustc_builtin_macros/src/format.rs | 1 + compiler/rustc_expand/src/build.rs | 1 + compiler/rustc_interface/src/util.rs | 1 + compiler/rustc_parse/src/parser/diagnostics.rs | 11 ++++++++--- compiler/rustc_parse/src/parser/stmt.rs | 9 ++++++++- compiler/rustc_resolve/src/late.rs | 16 ++++++++++++++++ compiler/rustc_resolve/src/late/diagnostics.rs | 10 ++++++++++ src/test/ui-fulldeps/pprust-expr-roundtrip.rs | 1 + src/test/ui/type/ascription/issue-34255-1.stderr | 10 ++++++++++ src/tools/rustfmt/src/closures.rs | 1 + 13 files changed, 67 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 0632d937c4c17..5e1548056440c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -565,6 +565,14 @@ pub struct Block { pub rules: BlockCheckMode, pub span: Span, pub tokens: Option, + /// The following *isn't* a parse error, but will cause multiple errors in following stages. + /// ``` + /// let x = { + /// foo: var + /// }; + /// ``` + /// #34255 + pub could_be_bare_literal: bool, } /// A match pattern. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 368a23e34290d..4d9fc6554e750 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -949,7 +949,7 @@ pub fn noop_visit_mt(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu } pub fn noop_visit_block(block: &mut P, vis: &mut T) { - let Block { id, stmts, rules: _, span, tokens } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 572ec6e242e4b..bcf95719db56b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -102,6 +102,7 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), span, tokens: None, + could_be_bare_literal: false, })) } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 1dbf772842163..8508b3b7ae6ed 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -867,6 +867,7 @@ impl<'a, 'b> Context<'a, 'b> { rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), span: self.macsp, tokens: None, + could_be_bare_literal: false, })); let ident = Ident::from_str_and_span("args", self.macsp); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1d83ecbfd404b..2cc15b3e53f46 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -197,6 +197,7 @@ impl<'a> ExtCtxt<'a> { rules: BlockCheckMode::Default, span, tokens: None, + could_be_bare_literal: false, }) } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 6b64614363f18..a5f0c01477898 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -810,6 +810,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: resolver.next_node_id(), span: rustc_span::DUMMY_SP, tokens: None, + could_be_bare_literal: false, } } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 273fbea358021..59e0feb67c5f0 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -446,11 +446,13 @@ impl<'a> Parser<'a> { ) .emit(); *self = snapshot; - Ok(self.mk_block( + let mut tail = self.mk_block( vec![self.mk_stmt_err(expr.span)], s, lo.to(self.prev_token.span), - )) + ); + tail.could_be_bare_literal = true; + Ok(tail) } (Err(mut err), Ok(tail)) => { // We have a block tail that contains a somehow valid type ascription expr. @@ -463,7 +465,10 @@ impl<'a> Parser<'a> { self.consume_block(token::Brace, ConsumeClosingDelim::Yes); Err(err) } - (Ok(_), Ok(tail)) => Ok(tail), + (Ok(_), Ok(mut tail)) => { + tail.could_be_bare_literal = true; + Ok(tail) + } }); } None diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 068bd36af5524..25dcb4a112de1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -574,7 +574,14 @@ impl<'a> Parser<'a> { } pub(super) fn mk_block(&self, stmts: Vec, rules: BlockCheckMode, span: Span) -> P { - P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None }) + P(Block { + stmts, + id: DUMMY_NODE_ID, + rules, + span, + tokens: None, + could_be_bare_literal: false, + }) } pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5c7b4b028227e..fed739d186c44 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -383,6 +383,11 @@ struct DiagnosticMetadata<'ast> { /// Only used for better errors on `fn(): fn()`. current_type_ascription: Vec, + /// Only used for better errors on `let x = { foo: bar };`. + /// In the case of a parse error with `let x = { foo: bar, };`, this isn't needed, it's only + /// needed for cases where this parses as a correct type ascription. + current_block_could_be_bare_struct_literal: Option, + /// Only used for better errors on `let : ;`. current_let_binding: Option<(Span, Option, Option)>, @@ -1859,6 +1864,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let instead = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; + // get_from_node_id this.r.use_injections.push(UseError { err, @@ -2242,6 +2248,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[ValueNS].push(Rib::new(NormalRibKind)); } + let prev = self.diagnostic_metadata.current_block_could_be_bare_struct_literal.take(); + if let (true, [Stmt { kind: StmtKind::Expr(expr), .. }]) = + (block.could_be_bare_literal, &block.stmts[..]) + { + if let ExprKind::Type(..) = expr.kind { + self.diagnostic_metadata.current_block_could_be_bare_struct_literal = + Some(block.span); + } + } // Descend into the block. for stmt in &block.stmts { if let StmtKind::Item(ref item) = stmt.kind { @@ -2255,6 +2270,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.visit_stmt(stmt); } + self.diagnostic_metadata.current_block_could_be_bare_struct_literal = prev; // Move back up. self.parent_scope.module = orig_module; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d7cd34c5922c7..b2c0c7874655e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -207,6 +207,16 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let code = source.error_code(res.is_some()); let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code); + if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { + err.multipart_suggestion( + "you might have meant to write a `struct` literal", + vec![ + (span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (span.shrink_to_hi(), "}".to_string()), + ], + Applicability::HasPlaceholders, + ); + } match (source, self.diagnostic_metadata.in_if_condition) { (PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => { err.span_suggestion_verbose( diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index ed8e498b6b746..8b0cebfa60e2c 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -106,6 +106,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { rules: BlockCheckMode::Default, span: DUMMY_SP, tokens: None, + could_be_bare_literal: false, }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); } diff --git a/src/test/ui/type/ascription/issue-34255-1.stderr b/src/test/ui/type/ascription/issue-34255-1.stderr index 96ee422b7b9dd..43f0fbbc4e387 100644 --- a/src/test/ui/type/ascription/issue-34255-1.stderr +++ b/src/test/ui/type/ascription/issue-34255-1.stderr @@ -3,6 +3,16 @@ error[E0425]: cannot find value `input_cells` in this scope | LL | input_cells: Vec::new() | ^^^^^^^^^^^ a field by this name exists in `Self` + | +help: you might have meant to write a `struct` literal + | +LL ~ pub fn new() -> Self { SomeStruct { +LL | input_cells: Vec::new() +LL | +LL | +LL | +LL ~ }} + | error[E0214]: parenthesized type parameters may only be used with a `Fn` trait --> $DIR/issue-34255-1.rs:7:27 diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index c9d46aef294a0..34d73a77fd3d4 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -160,6 +160,7 @@ fn rewrite_closure_with_block( .first() .map(|attr| attr.span.to(body.span)) .unwrap_or(body.span), + could_be_bare_literal: false, }; let block = crate::expr::rewrite_block_with_visitor( context,