about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/src/parser/path.rs86
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.rs14
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.stderr17
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.rs5
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.stderr10
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.rs14
-rw-r--r--tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.stderr16
7 files changed, 160 insertions, 2 deletions
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 545db5138a3..2edf2111de7 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -1,6 +1,7 @@
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{Parser, Restrictions, TokenType};
 use crate::errors::PathSingleColon;
+use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
 use crate::{errors, maybe_whole};
 use ast::token::IdentIsRaw;
 use rustc_ast::ptr::P;
@@ -10,7 +11,7 @@ use rustc_ast::{
     AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
     Path, PathSegment, QSelf,
 };
-use rustc_errors::{Applicability, PResult};
+use rustc_errors::{Applicability, Diag, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span};
 use std::mem;
@@ -373,7 +374,38 @@ impl<'a> Parser<'a> {
                     .into()
                 } else {
                     // `(T, U) -> R`
-                    let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?;
+
+                    let prev_token_before_parsing = self.prev_token.clone();
+                    let token_before_parsing = self.token.clone();
+                    let mut snapshot = None;
+                    if self.may_recover()
+                        && prev_token_before_parsing.kind == token::ModSep
+                        && (style == PathStyle::Expr && self.token.can_begin_expr()
+                            || style == PathStyle::Pat && self.token.can_begin_pattern())
+                    {
+                        snapshot = Some(self.create_snapshot_for_diagnostic());
+                    }
+
+                    let (inputs, _) = match self.parse_paren_comma_seq(|p| p.parse_ty()) {
+                        Ok(output) => output,
+                        Err(mut error) if prev_token_before_parsing.kind == token::ModSep => {
+                            error.span_label(
+                                prev_token_before_parsing.span.to(token_before_parsing.span),
+                                "while parsing this parenthesized list of type arguments starting here",
+                            );
+
+                            if let Some(mut snapshot) = snapshot {
+                                snapshot.recover_fn_call_leading_path_sep(
+                                    style,
+                                    prev_token_before_parsing,
+                                    &mut error,
+                                )
+                            }
+
+                            return Err(error);
+                        }
+                        Err(error) => return Err(error),
+                    };
                     let inputs_span = lo.to(self.prev_token.span);
                     let output =
                         self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
@@ -399,6 +431,56 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Recover `$path::(...)` as `$path(...)`.
+    ///
+    /// ```ignore (diagnostics)
+    /// foo::(420, "bar")
+    ///    ^^ remove extra separator to make the function call
+    /// // or
+    /// match x {
+    ///    Foo::(420, "bar") => { ... },
+    ///       ^^ remove extra separator to turn this into tuple struct pattern
+    ///    _ => { ... },
+    /// }
+    /// ```
+    fn recover_fn_call_leading_path_sep(
+        &mut self,
+        style: PathStyle,
+        prev_token_before_parsing: Token,
+        error: &mut Diag<'_>,
+    ) {
+        if ((style == PathStyle::Expr && self.parse_paren_comma_seq(|p| p.parse_expr()).is_ok())
+            || (style == PathStyle::Pat
+                && self
+                    .parse_paren_comma_seq(|p| {
+                        p.parse_pat_allow_top_alt(
+                            None,
+                            RecoverComma::No,
+                            RecoverColon::No,
+                            CommaRecoveryMode::LikelyTuple,
+                        )
+                    })
+                    .is_ok()))
+            && !matches!(self.token.kind, token::ModSep | token::RArrow)
+        {
+            error.span_suggestion_verbose(
+                prev_token_before_parsing.span,
+                format!(
+                    "consider removing the `::` here to {}",
+                    match style {
+                        PathStyle::Expr => "call the expression",
+                        PathStyle::Pat => "turn this into a tuple struct pattern",
+                        _ => {
+                            return;
+                        }
+                    }
+                ),
+                "",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
     /// Parses generic args (within a path segment) with recovery for extra leading angle brackets.
     /// For the purposes of understanding the parsing logic of generic arguments, this function
     /// can be thought of being the same as just calling `self.parse_angle_args()` if the source
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.rs b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.rs
new file mode 100644
index 00000000000..5de3b401ab4
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.rs
@@ -0,0 +1,14 @@
+fn main() {
+  foo::( //~ HELP: consider removing the `::` here to call the expression
+    //~^ NOTE: while parsing this parenthesized list of type arguments starting
+    bar(x, y, z),
+    bar(x, y, z),
+    bar(x, y, z),
+    bar(x, y, z),
+    bar(x, y, z),
+    bar(x, y, z),
+    bar(x, y, z),
+    baz("test"), //~ ERROR: expected type, found `"test"`
+    //~^ NOTE: expected type
+  )
+}
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.stderr b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.stderr
new file mode 100644
index 00000000000..60e57a3629c
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-1.stderr
@@ -0,0 +1,17 @@
+error: expected type, found `"test"`
+  --> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-1.rs:11:9
+   |
+LL |   foo::(
+   |      --- while parsing this parenthesized list of type arguments starting here
+...
+LL |     baz("test"),
+   |         ^^^^^^ expected type
+   |
+help: consider removing the `::` here to call the expression
+   |
+LL -   foo::(
+LL +   foo(
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.rs b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.rs
new file mode 100644
index 00000000000..2876a9afe00
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.rs
@@ -0,0 +1,5 @@
+fn main() {
+  foo::/* definitely not harmful comment */(123, "foo") -> (u32); //~ ERROR: expected type, found `123`
+  //~^ NOTE: while parsing this parenthesized list of type arguments starting
+  //~^^ NOTE: expected type
+}
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.stderr b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.stderr
new file mode 100644
index 00000000000..7fd2b6b18e3
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-2.stderr
@@ -0,0 +1,10 @@
+error: expected type, found `123`
+  --> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-2.rs:2:45
+   |
+LL |   foo::/* definitely not harmful comment */(123, "foo") -> (u32);
+   |      ---------------------------------------^^^ expected type
+   |      |
+   |      while parsing this parenthesized list of type arguments starting here
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.rs b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.rs
new file mode 100644
index 00000000000..402afac46de
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.rs
@@ -0,0 +1,14 @@
+struct Foo(u32, u32);
+impl Foo {
+    fn foo(&self) {
+        match *self {
+            Foo::(1, 2) => {}, //~ HELP: consider removing the `::` here to turn this into a tuple struct pattern
+            //~^ NOTE: while parsing this parenthesized list of type arguments starting
+            //~^^ ERROR: expected type, found `1`
+            //~^^^ NOTE: expected type
+             _ => {},
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.stderr b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.stderr
new file mode 100644
index 00000000000..72770ca265d
--- /dev/null
+++ b/tests/ui/parser/issues/diagnostics-parenthesized-type-arguments-issue-120892-3.stderr
@@ -0,0 +1,16 @@
+error: expected type, found `1`
+  --> $DIR/diagnostics-parenthesized-type-arguments-issue-120892-3.rs:5:19
+   |
+LL |             Foo::(1, 2) => {},
+   |                ---^ expected type
+   |                |
+   |                while parsing this parenthesized list of type arguments starting here
+   |
+help: consider removing the `::` here to turn this into a tuple struct pattern
+   |
+LL -             Foo::(1, 2) => {},
+LL +             Foo(1, 2) => {},
+   |
+
+error: aborting due to 1 previous error
+