]> git.lizzy.rs Git - rust.git/commitdiff
Recover gracefully from argument with missing type or param name
authorEsteban Küber <esteban@kuber.com.ar>
Wed, 29 May 2019 22:25:46 +0000 (15:25 -0700)
committerEsteban Küber <esteban@kuber.com.ar>
Fri, 31 May 2019 00:59:05 +0000 (17:59 -0700)
src/librustc/hir/lowering.rs
src/libsyntax/ast.rs
src/libsyntax/mut_visit.rs
src/libsyntax/parse/diagnostics.rs
src/libsyntax/parse/parser.rs
src/test/ui/anon-params-denied-2018.rs
src/test/ui/anon-params-denied-2018.stderr
src/test/ui/parser/inverted-parameters.rs
src/test/ui/parser/inverted-parameters.stderr
src/test/ui/parser/omitted-arg-in-item-fn.stderr
src/test/ui/span/issue-34264.stderr

index 08fbd0d20d74dac3a23818ad7ee4cf5e4fc187aa..248d9d3950c2e07d75ccd8db00ed53c799499447 100644 (file)
@@ -2298,7 +2298,7 @@ fn lower_arg(&mut self, arg: &Arg) -> hir::Arg {
 
     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)),
         }
     }
index 75e83bd9f9c74db2f0b606452781f3e389da361e..3e25f22f0a43b6709d088005464d091c75b081b6 100644 (file)
@@ -1780,6 +1780,8 @@ pub enum ArgSource {
     Normal,
     /// Argument from `async fn` lowering, contains the original binding pattern.
     AsyncFn(P<Pat>),
+    /// Placeholder argument caused by incorrect syntax. Used to silence unecessary errors.
+    Recovery,
 }
 
 /// Alternative representation for `Arg`s describing `self` parameter of methods.
index 0016c0d4d7e2b26ade57deeecdd568b5fb016c53..e31bded0dec5b938593aaa641a6c9d4e1cc2ed08 100644 (file)
@@ -580,7 +580,7 @@ pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty, source }: &mut Arg, vis:
 
 pub fn noop_visit_arg_source<T: MutVisitor>(source: &mut ArgSource, vis: &mut T) {
     match source {
-        ArgSource::Normal => {},
+        ArgSource::Normal | ArgSource::Recovery => {},
         ArgSource::AsyncFn(pat) => vis.visit_pat(pat),
     }
 }
index b3d49524d7668599e9d37ed6d46907db65981776..b6f26c73a70a5724a0a5f9c5cb720396d178777e 100644 (file)
@@ -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 @@ pub fn unexpected_try_recover(
         pat: P<ast::Pat>,
         require_name: bool,
         is_trait_item: bool,
-    ) {
+    ) -> Option<Ident> {
         // 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 @@ pub fn unexpected_try_recover(
                 String::from("<identifier>: <type>"),
                 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<ast::Pat>, P<ast::Ty>)> {
index 746e9cad4962ca4057e9214ef433aedaedade7f2..2c35b9ea7fdada71056b3286342abd6d09558043 100644 (file)
@@ -51,6 +51,7 @@
 
 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 @@ fn from(expr: P<Expr>) -> Self {
 }
 
 /// 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 @@ fn parse_arg_general(
             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 @@ fn parse_fn_args(&mut self, named_args: bool, allow_c_variadic: bool)
                             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 @@ fn parse_fn_decl_with_self<F>(&mut self, parse_arg_fn: F) -> PResult<'a, P<FnDec
 
         // 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 @@ fn parse_fn_decl_with_self<F>(&mut self, parse_arg_fn: F) -> PResult<'a, P<FnDec
             // 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)?,
index 5e77aa8fbb923fdf17dcdbc6985e5a947fc2d1a6..abff8275064e24dfd8d922a003feab905deaa06a 100644 (file)
@@ -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() {}
index 1ec0cf323e9960798b21f96b7cc9b46c0ed982d8..438bcf4274daaf0ab6e0c1dca14f6633b93ee5ef 100644 (file)
@@ -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
 
index 42430278c7267f6b2274d2269dc60c855a61e65c..f06b9510417310ba616f0651b6986a080ad08900 100644 (file)
@@ -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 `@`
index bdb8faa6c6593975ad0dfd09db6fa7ed04362fcd..fb48bd1fe9383625660ab5150f43c7efef6d494d 100644 (file)
@@ -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) {}
    |                  -----^
index 4f2a76d2d25627db8ebabe3dbc70dd504e45779c..e501f235d6d3b6fce85d1e2858810e455d9771c6 100644 (file)
@@ -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
 
index 295b8c6f67f357758b2ee27fdb667d8ef3b6ed9e..5dd9895c6e4f45f141292a27afd0eeaaf78afd42 100644 (file)
@@ -9,12 +9,32 @@ error: expected one of `:` or `@`, found `)`
    |
 LL | fn foo(Option<i32>, 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<i32>, String: TypeName) {}
+   |                     ^^^^^^^^^^^^^^^^
+help: if this is a type, explicitly ignore the parameter name
+   |
+LL | fn foo(Option<i32>, _: 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