From b3ac88ad922a618e6bf0eb269084d60233a311e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 May 2019 15:25:46 -0700 Subject: [PATCH 1/4] Recover gracefully from argument with missing type or param name --- src/librustc/hir/lowering.rs | 2 +- src/libsyntax/ast.rs | 2 + src/libsyntax/mut_visit.rs | 2 +- src/libsyntax/parse/diagnostics.rs | 28 ++++++--- src/libsyntax/parse/parser.rs | 45 +++++++++++--- src/test/ui/anon-params-denied-2018.rs | 8 ++- src/test/ui/anon-params-denied-2018.stderr | 58 ++++++++++++++++--- src/test/ui/parser/inverted-parameters.rs | 2 + src/test/ui/parser/inverted-parameters.stderr | 12 +++- .../ui/parser/omitted-arg-in-item-fn.stderr | 10 ++++ src/test/ui/span/issue-34264.stderr | 20 +++++++ 11 files changed, 161 insertions(+), 28 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 08fbd0d20d74d..248d9d3950c2e 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2298,7 +2298,7 @@ impl<'a> LoweringContext<'a> { fn lower_arg_source(&mut self, source: &ArgSource) -> hir::ArgSource { match source { - ArgSource::Normal => hir::ArgSource::Normal, + ArgSource::Normal | ArgSource::Recovery => hir::ArgSource::Normal, ArgSource::AsyncFn(pat) => hir::ArgSource::AsyncFn(self.lower_pat(pat)), } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 75e83bd9f9c74..3e25f22f0a43b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1780,6 +1780,8 @@ pub enum ArgSource { Normal, /// Argument from `async fn` lowering, contains the original binding pattern. AsyncFn(P), + /// Placeholder argument caused by incorrect syntax. Used to silence unecessary errors. + Recovery, } /// Alternative representation for `Arg`s describing `self` parameter of methods. diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 0016c0d4d7e2b..e31bded0dec5b 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -580,7 +580,7 @@ pub fn noop_visit_arg(Arg { id, pat, ty, source }: &mut Arg, vis: pub fn noop_visit_arg_source(source: &mut ArgSource, vis: &mut T) { match source { - ArgSource::Normal => {}, + ArgSource::Normal | ArgSource::Recovery => {}, ArgSource::AsyncFn(pat) => vis.visit_pat(pat), } } diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index b3d49524d7668..b6f26c73a70a5 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -1,7 +1,7 @@ use crate::ast; use crate::ast::{ BlockCheckMode, BinOpKind, Expr, ExprKind, Item, ItemKind, Pat, PatKind, PathSegment, QSelf, - Ty, TyKind, VariantData, + Ty, TyKind, VariantData, Ident, }; use crate::parse::{SeqSep, token, PResult, Parser}; use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType}; @@ -1092,12 +1092,12 @@ impl<'a> Parser<'a> { pat: P, require_name: bool, is_trait_item: bool, - ) { + ) -> Option { // If we find a pattern followed by an identifier, it could be an (incorrect) // C-style parameter declaration. if self.check_ident() && self.look_ahead(1, |t| { *t == token::Comma || *t == token::CloseDelim(token::Paren) - }) { + }) { // `fn foo(String s) {}` let ident = self.parse_ident().unwrap(); let span = pat.span.with_hi(ident.span.hi()); @@ -1107,18 +1107,30 @@ impl<'a> Parser<'a> { String::from(": "), Applicability::HasPlaceholders, ); - } else if require_name && is_trait_item { - if let PatKind::Ident(_, ident, _) = pat.node { + return Some(ident); + } else if let PatKind::Ident(_, ident, _) = pat.node { + if require_name && ( + is_trait_item || + self.token == token::Comma || + self.token == token::CloseDelim(token::Paren) + ) { // `fn foo(a, b) {}` or `fn foo(usize, usize) {}` + err.span_suggestion( + pat.span, + "if this was a parameter name, give it a type", + format!("{}: TypeName", ident), + Applicability::HasPlaceholders, + ); err.span_suggestion( pat.span, - "explicitly ignore parameter", + "if this is a type, explicitly ignore the parameter name", format!("_: {}", ident), Applicability::MachineApplicable, ); + err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); + return Some(ident); } - - err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); } + None } crate fn recover_arg_parse(&mut self) -> PResult<'a, (P, P)> { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 746e9cad4962c..2c35b9ea7fdad 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -51,6 +51,7 @@ use crate::parse::diagnostics::Error; use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError}; use rustc_target::spec::abi::{self, Abi}; +use rustc_data_structures::fx::FxHashSet; use syntax_pos::{Span, BytePos, DUMMY_SP, FileName, hygiene::CompilerDesugaringKind}; use log::debug; @@ -452,19 +453,18 @@ impl From> for LhsExpr { } /// Creates a placeholder argument. -fn dummy_arg(span: Span) -> Arg { - let ident = Ident::new(kw::Invalid, span); +fn dummy_arg(ident: Ident) -> Arg { let pat = P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None), - span, + span: ident.span, }); let ty = Ty { node: TyKind::Err, - span, + span: ident.span, id: ast::DUMMY_NODE_ID }; - Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal } + Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Recovery } } #[derive(Copy, Clone, Debug)] @@ -1528,8 +1528,17 @@ impl<'a> Parser<'a> { let pat = self.parse_pat(Some("argument name"))?; if let Err(mut err) = self.expect(&token::Colon) { - self.argument_without_type(&mut err, pat, require_name, is_trait_item); - return Err(err); + if let Some(ident) = self.argument_without_type( + &mut err, + pat, + require_name, + is_trait_item, + ) { + err.emit(); + return Ok(dummy_arg(ident)); + } else { + return Err(err); + } } self.eat_incorrect_doc_comment("a method argument's type"); @@ -5431,7 +5440,7 @@ impl<'a> Parser<'a> { p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); // Create a placeholder argument for proper arg count (issue #34264). let span = lo.to(p.prev_span); - Ok(Some(dummy_arg(span))) + Ok(Some(dummy_arg(Ident::new(kw::Invalid, span)))) } } } @@ -5584,7 +5593,7 @@ impl<'a> Parser<'a> { // Parse the rest of the function parameter list. let sep = SeqSep::trailing_allowed(token::Comma); - let (fn_inputs, recovered) = if let Some(self_arg) = self_arg { + let (mut fn_inputs, recovered) = if let Some(self_arg) = self_arg { if self.check(&token::CloseDelim(token::Paren)) { (vec![self_arg], false) } else if self.eat(&token::Comma) { @@ -5607,6 +5616,24 @@ impl<'a> Parser<'a> { // Parse closing paren and return type. self.expect(&token::CloseDelim(token::Paren))?; } + // Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors. + let mut seen_inputs = FxHashSet::default(); + for input in fn_inputs.iter_mut() { + let opt_ident = if let (PatKind::Ident(_, ident, _), ast::ArgSource::Recovery) = ( + &input.pat.node, &input.source, + ) { + Some(*ident) + } else { + None + }; + if let Some(ident) = opt_ident { + if seen_inputs.contains(&ident) { + input.pat.node = PatKind::Wild; + } + seen_inputs.insert(ident); + } + } + Ok(P(FnDecl { inputs: fn_inputs, output: self.parse_ret_ty(true)?, diff --git a/src/test/ui/anon-params-denied-2018.rs b/src/test/ui/anon-params-denied-2018.rs index 5e77aa8fbb923..abff8275064e2 100644 --- a/src/test/ui/anon-params-denied-2018.rs +++ b/src/test/ui/anon-params-denied-2018.rs @@ -6,7 +6,13 @@ trait T { fn foo(i32); //~ expected one of `:` or `@`, found `)` fn bar_with_default_impl(String, String) {} - //~^ ERROR expected one of `:` or `@`, found `,` + //~^ ERROR expected one of `:` + //~| ERROR expected one of `:` + + // do not complain about missing `b` + fn baz(a:usize, b, c: usize) -> usize { //~ ERROR expected one of `:` + a + b + c + } } fn main() {} diff --git a/src/test/ui/anon-params-denied-2018.stderr b/src/test/ui/anon-params-denied-2018.stderr index 1ec0cf323e996..438bcf4274daa 100644 --- a/src/test/ui/anon-params-denied-2018.stderr +++ b/src/test/ui/anon-params-denied-2018.stderr @@ -2,21 +2,65 @@ error: expected one of `:` or `@`, found `)` --> $DIR/anon-params-denied-2018.rs:6:15 | LL | fn foo(i32); - | ---^ expected one of `:` or `@` here - | | - | help: explicitly ignore parameter: `_: i32` + | ^ expected one of `:` or `@` here | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn foo(i32: TypeName); + | ^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn foo(_: i32); + | ^^^^^^ error: expected one of `:` or `@`, found `,` --> $DIR/anon-params-denied-2018.rs:8:36 | LL | fn bar_with_default_impl(String, String) {} - | ------^ expected one of `:` or `@` here - | | - | help: explicitly ignore parameter: `_: String` + | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn bar_with_default_impl(String: TypeName, String) {} + | ^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn bar_with_default_impl(_: String, String) {} + | ^^^^^^^^^ + +error: expected one of `:` or `@`, found `)` + --> $DIR/anon-params-denied-2018.rs:8:44 + | +LL | fn bar_with_default_impl(String, String) {} + | ^ expected one of `:` or `@` here | = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn bar_with_default_impl(String, String: TypeName) {} + | ^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn bar_with_default_impl(String, _: String) {} + | ^^^^^^^^^ + +error: expected one of `:` or `@`, found `,` + --> $DIR/anon-params-denied-2018.rs:13:22 + | +LL | fn baz(a:usize, b, c: usize) -> usize { + | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn baz(a:usize, b: TypeName, c: usize) -> usize { + | ^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn baz(a:usize, _: b, c: usize) -> usize { + | ^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/parser/inverted-parameters.rs b/src/test/ui/parser/inverted-parameters.rs index 42430278c7267..f06b951041731 100644 --- a/src/test/ui/parser/inverted-parameters.rs +++ b/src/test/ui/parser/inverted-parameters.rs @@ -20,6 +20,8 @@ fn pattern((i32, i32) (a, b)) {} fn fizz(i32) {} //~^ ERROR expected one of `:` or `@` +//~| HELP if this was a parameter name, give it a type +//~| HELP if this is a type, explicitly ignore the parameter name fn missing_colon(quux S) {} //~^ ERROR expected one of `:` or `@` diff --git a/src/test/ui/parser/inverted-parameters.stderr b/src/test/ui/parser/inverted-parameters.stderr index bdb8faa6c6593..fb48bd1fe9383 100644 --- a/src/test/ui/parser/inverted-parameters.stderr +++ b/src/test/ui/parser/inverted-parameters.stderr @@ -33,9 +33,19 @@ error: expected one of `:` or `@`, found `)` | LL | fn fizz(i32) {} | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn fizz(i32: TypeName) {} + | ^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn fizz(_: i32) {} + | ^^^^^^ error: expected one of `:` or `@`, found `S` - --> $DIR/inverted-parameters.rs:24:23 + --> $DIR/inverted-parameters.rs:26:23 | LL | fn missing_colon(quux S) {} | -----^ diff --git a/src/test/ui/parser/omitted-arg-in-item-fn.stderr b/src/test/ui/parser/omitted-arg-in-item-fn.stderr index 4f2a76d2d2562..e501f235d6d3b 100644 --- a/src/test/ui/parser/omitted-arg-in-item-fn.stderr +++ b/src/test/ui/parser/omitted-arg-in-item-fn.stderr @@ -3,6 +3,16 @@ error: expected one of `:` or `@`, found `)` | LL | fn foo(x) { | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn foo(x: TypeName) { + | ^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn foo(_: x) { + | ^^^^ error: aborting due to previous error diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr index 295b8c6f67f35..5dd9895c6e4f4 100644 --- a/src/test/ui/span/issue-34264.stderr +++ b/src/test/ui/span/issue-34264.stderr @@ -9,12 +9,32 @@ error: expected one of `:` or `@`, found `)` | LL | fn foo(Option, String) {} | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn foo(Option, String: TypeName) {} + | ^^^^^^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn foo(Option, _: String) {} + | ^^^^^^^^^ error: expected one of `:` or `@`, found `,` --> $DIR/issue-34264.rs:3:9 | LL | fn bar(x, y: usize) {} | ^ expected one of `:` or `@` here + | + = note: anonymous parameters are removed in the 2018 edition (see RFC 1685) +help: if this was a parameter name, give it a type + | +LL | fn bar(x: TypeName, y: usize) {} + | ^^^^^^^^^^^ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn bar(_: x, y: usize) {} + | ^^^^ error[E0061]: this function takes 2 parameters but 3 parameters were supplied --> $DIR/issue-34264.rs:7:5 From 1ee45da2b9789881b8c79d6a1af8a2c9fd5364f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 30 May 2019 18:02:40 -0700 Subject: [PATCH 2/4] Remove `ArgSource::Recovery` --- src/librustc/hir/lowering.rs | 2 +- src/libsyntax/ast.rs | 2 -- src/libsyntax/mut_visit.rs | 2 +- src/libsyntax/parse/parser.rs | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 248d9d3950c2e..08fbd0d20d74d 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2298,7 +2298,7 @@ impl<'a> LoweringContext<'a> { fn lower_arg_source(&mut self, source: &ArgSource) -> hir::ArgSource { match source { - ArgSource::Normal | ArgSource::Recovery => hir::ArgSource::Normal, + ArgSource::Normal => hir::ArgSource::Normal, ArgSource::AsyncFn(pat) => hir::ArgSource::AsyncFn(self.lower_pat(pat)), } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 3e25f22f0a43b..75e83bd9f9c74 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1780,8 +1780,6 @@ pub enum ArgSource { Normal, /// Argument from `async fn` lowering, contains the original binding pattern. AsyncFn(P), - /// Placeholder argument caused by incorrect syntax. Used to silence unecessary errors. - Recovery, } /// Alternative representation for `Arg`s describing `self` parameter of methods. diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index e31bded0dec5b..0016c0d4d7e2b 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -580,7 +580,7 @@ pub fn noop_visit_arg(Arg { id, pat, ty, source }: &mut Arg, vis: pub fn noop_visit_arg_source(source: &mut ArgSource, vis: &mut T) { match source { - ArgSource::Normal | ArgSource::Recovery => {}, + ArgSource::Normal => {}, ArgSource::AsyncFn(pat) => vis.visit_pat(pat), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2c35b9ea7fdad..3bb34d0402db7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -464,7 +464,7 @@ fn dummy_arg(ident: Ident) -> Arg { span: ident.span, id: ast::DUMMY_NODE_ID }; - Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Recovery } + Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal } } #[derive(Copy, Clone, Debug)] @@ -5619,8 +5619,8 @@ impl<'a> Parser<'a> { // Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors. let mut seen_inputs = FxHashSet::default(); for input in fn_inputs.iter_mut() { - let opt_ident = if let (PatKind::Ident(_, ident, _), ast::ArgSource::Recovery) = ( - &input.pat.node, &input.source, + let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = ( + &input.pat.node, &input.ty, ) { Some(*ident) } else { From ad0d3b5d4064cfd9013c498299a53b3f1586553e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 30 May 2019 18:19:48 -0700 Subject: [PATCH 3/4] Move code from `parser` to `diagnostics` --- src/libsyntax/parse/diagnostics.rs | 41 +++++++++++++++++++++++++++--- src/libsyntax/parse/parser.rs | 35 ++----------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index b6f26c73a70a5..138b2ccfe35e7 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -1,7 +1,6 @@ -use crate::ast; use crate::ast::{ - BlockCheckMode, BinOpKind, Expr, ExprKind, Item, ItemKind, Pat, PatKind, PathSegment, QSelf, - Ty, TyKind, VariantData, Ident, + self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind, + Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData, }; use crate::parse::{SeqSep, token, PResult, Parser}; use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType}; @@ -12,9 +11,25 @@ use crate::symbol::{kw, sym}; use crate::ThinVec; use crate::util::parser::AssocOp; use errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_data_structures::fx::FxHashSet; use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use log::{debug, trace}; +/// Creates a placeholder argument. +crate fn dummy_arg(ident: Ident) -> Arg { + let pat = P(Pat { + id: ast::DUMMY_NODE_ID, + node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None), + span: ident.span, + }); + let ty = Ty { + node: TyKind::Err, + span: ident.span, + id: ast::DUMMY_NODE_ID + }; + Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal } +} + pub enum Error { FileNotFoundForModule { mod_name: String, @@ -1217,4 +1232,24 @@ impl<'a> Parser<'a> { err.span_label(span, "expected expression"); err } + + /// Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors. + crate fn deduplicate_recovered_arg_names(&self, fn_inputs: &mut Vec) { + let mut seen_inputs = FxHashSet::default(); + for input in fn_inputs.iter_mut() { + let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = ( + &input.pat.node, &input.ty.node, + ) { + Some(*ident) + } else { + None + }; + if let Some(ident) = opt_ident { + if seen_inputs.contains(&ident) { + input.pat.node = PatKind::Wild; + } + seen_inputs.insert(ident); + } + } + } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3bb34d0402db7..659058ffcf3c7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -47,11 +47,10 @@ use crate::parse::PResult; use crate::ThinVec; use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint}; use crate::symbol::{kw, sym, Symbol}; -use crate::parse::diagnostics::Error; +use crate::parse::diagnostics::{Error, dummy_arg}; use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError}; use rustc_target::spec::abi::{self, Abi}; -use rustc_data_structures::fx::FxHashSet; use syntax_pos::{Span, BytePos, DUMMY_SP, FileName, hygiene::CompilerDesugaringKind}; use log::debug; @@ -452,21 +451,6 @@ impl From> for LhsExpr { } } -/// Creates a placeholder argument. -fn dummy_arg(ident: Ident) -> Arg { - let pat = P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None), - span: ident.span, - }); - let ty = Ty { - node: TyKind::Err, - span: ident.span, - id: ast::DUMMY_NODE_ID - }; - Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal } -} - #[derive(Copy, Clone, Debug)] crate enum TokenExpectType { Expect, @@ -5617,22 +5601,7 @@ impl<'a> Parser<'a> { self.expect(&token::CloseDelim(token::Paren))?; } // Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors. - let mut seen_inputs = FxHashSet::default(); - for input in fn_inputs.iter_mut() { - let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = ( - &input.pat.node, &input.ty, - ) { - Some(*ident) - } else { - None - }; - if let Some(ident) = opt_ident { - if seen_inputs.contains(&ident) { - input.pat.node = PatKind::Wild; - } - seen_inputs.insert(ident); - } - } + self.deduplicate_recovered_arg_names(&mut fn_inputs); Ok(P(FnDecl { inputs: fn_inputs, From e275f2caf62b452913b301a31a2877d3f2f0711b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 1 Jun 2019 14:13:57 -0700 Subject: [PATCH 4/4] Extend docstring --- src/libsyntax/parse/diagnostics.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 138b2ccfe35e7..71d074b99ecce 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -1234,6 +1234,13 @@ impl<'a> Parser<'a> { } /// Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors. + /// + /// This is necessary because at this point we don't know whether we parsed a function with + /// anonymous arguments or a function with names but no types. In order to minimize + /// unecessary errors, we assume the arguments are in the shape of `fn foo(a, b, c)` where + /// the arguments are *names* (so we don't emit errors about not being able to find `b` in + /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`, + /// we deduplicate them to not complain about duplicated argument names. crate fn deduplicate_recovered_arg_names(&self, fn_inputs: &mut Vec) { let mut seen_inputs = FxHashSet::default(); for input in fn_inputs.iter_mut() {