New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect bare blocks with type ascription that were meant to be a struct literal #88598

Merged
merged 1 commit into from Sep 4, 2021
Merged
Changes from all commits
Commits
File filter
Filter file types
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -565,6 +565,14 @@ pub struct Block {
pub rules: BlockCheckMode,
pub span: Span,
pub tokens: Option<LazyTokenStream>,
/// 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.
@@ -949,7 +949,7 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu
}

pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, 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);
@@ -102,6 +102,7 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
span,
tokens: None,
could_be_bare_literal: false,
}))
}

@@ -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);
@@ -197,6 +197,7 @@ impl<'a> ExtCtxt<'a> {
rules: BlockCheckMode::Default,
span,
tokens: None,
could_be_bare_literal: false,
})
}

@@ -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,
}
}

@@ -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
@@ -574,7 +574,14 @@ impl<'a> Parser<'a> {
}

pub(super) fn mk_block(&self, stmts: Vec<Stmt>, rules: BlockCheckMode, span: Span) -> P<Block> {
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 {
@@ -383,6 +383,11 @@ struct DiagnosticMetadata<'ast> {
/// Only used for better errors on `fn(): fn()`.
current_type_ascription: Vec<Span>,

/// 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<Span>,

/// Only used for better errors on `let <pat>: <expr, not type>;`.
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,

@@ -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;
@@ -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(
@@ -106,6 +106,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
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)));
}
@@ -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
@@ -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,
Detect bare blocks with type ascription that were meant to be a `struct` literal by estebank · Pull Request #88598 · rust-lang/rust · GitHub