about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2025-04-24 02:19:02 +0200
committerLeón Orell Valerian Liehr <me@fmease.dev>2025-04-24 02:57:10 +0200
commit16da97be2f1e94a550985f22289975db4b32c18d (patch)
tree4fba215a6c8eccfc642faee430a147b5b6140a56
parent6bc57c6bf7d0024ad9ea5a2c112f3fc9c383c8a4 (diff)
downloadrust-16da97be2f1e94a550985f22289975db4b32c18d.tar.gz
rust-16da97be2f1e94a550985f22289975db4b32c18d.zip
Revert overzealous parse recovery for single colons
-rw-r--r--compiler/rustc_parse/src/parser/item.rs11
-rw-r--r--compiler/rustc_parse/src/parser/path.rs20
-rw-r--r--tests/ui/generics/single-colon-path-not-const-generics.stderr4
-rw-r--r--tests/ui/parser/ty-path-followed-by-single-colon.rs22
-rw-r--r--tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed15
-rw-r--r--tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs15
-rw-r--r--tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr13
-rw-r--r--tests/ui/suggestions/struct-field-type-including-single-colon.rs8
-rw-r--r--tests/ui/suggestions/struct-field-type-including-single-colon.stderr45
9 files changed, 64 insertions, 89 deletions
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 39251f1ce27..4be8a90368d 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2058,6 +2058,17 @@ impl<'a> Parser<'a> {
         }
         self.expect_field_ty_separator()?;
         let ty = self.parse_ty()?;
+        if self.token == token::Colon && self.look_ahead(1, |&t| t != token::Colon) {
+            self.dcx()
+                .struct_span_err(self.token.span, "found single colon in a struct field type path")
+                .with_span_suggestion_verbose(
+                    self.token.span,
+                    "write a path separator here",
+                    "::",
+                    Applicability::MaybeIncorrect,
+                )
+                .emit();
+        }
         let default = if self.token == token::Eq {
             self.bump();
             let const_expr = self.parse_expr_anon_const()?;
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 1a02d45f0e3..1093e4f4af0 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -248,19 +248,13 @@ impl<'a> Parser<'a> {
             segments.push(segment);
 
             if self.is_import_coupler() || !self.eat_path_sep() {
-                let ok_for_recovery = self.may_recover()
-                    && match style {
-                        PathStyle::Expr => true,
-                        PathStyle::Type if let Some((ident, _)) = self.prev_token.ident() => {
-                            self.token == token::Colon
-                                && ident.as_str().chars().all(|c| c.is_lowercase())
-                                && self.token.span.lo() == self.prev_token.span.hi()
-                                && self
-                                    .look_ahead(1, |token| self.token.span.hi() == token.span.lo())
-                        }
-                        _ => false,
-                    };
-                if ok_for_recovery
+                // IMPORTANT: We can *only ever* treat single colons as typo'ed double colons in
+                // expression contexts (!) since only there paths cannot possibly be followed by
+                // a colon and still form a syntactically valid construct. In pattern contexts,
+                // a path may be followed by a type annotation. E.g., `let pat:ty`. In type
+                // contexts, a path may be followed by a list of bounds. E.g., `where ty:bound`.
+                if self.may_recover()
+                    && style == PathStyle::Expr // (!)
                     && self.token == token::Colon
                     && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
                 {
diff --git a/tests/ui/generics/single-colon-path-not-const-generics.stderr b/tests/ui/generics/single-colon-path-not-const-generics.stderr
index 9eb62de2756..163ea4bbda6 100644
--- a/tests/ui/generics/single-colon-path-not-const-generics.stderr
+++ b/tests/ui/generics/single-colon-path-not-const-generics.stderr
@@ -1,13 +1,15 @@
 error: path separator must be a double colon
   --> $DIR/single-colon-path-not-const-generics.rs:8:18
    |
+LL | pub struct Foo {
+   |            --- while parsing this struct
 LL |   a: Vec<foo::bar:A>,
    |                  ^
    |
 help: use a double colon instead
    |
 LL |   a: Vec<foo::bar::A>,
-   |                   +
+   |                  +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/ty-path-followed-by-single-colon.rs b/tests/ui/parser/ty-path-followed-by-single-colon.rs
new file mode 100644
index 00000000000..a9082ea317a
--- /dev/null
+++ b/tests/ui/parser/ty-path-followed-by-single-colon.rs
@@ -0,0 +1,22 @@
+// Paths in type contexts may be followed by single colons.
+// This means we can't generally assume that the user typo'ed a double colon.
+// issue: <https://github.com/rust-lang/rust/issues/140227>
+//@ check-pass
+#![crate_type = "lib"]
+#![expect(non_camel_case_types)]
+
+#[rustfmt::skip]
+mod garden {
+
+    fn f<path>() where path:to::somewhere {} // OK!
+
+    fn g(_: impl Take<path:to::somewhere>) {} // OK!
+
+    #[cfg(any())] fn h() where a::path:to::nowhere {} // OK!
+
+    fn i(_: impl Take<path::<>:to::somewhere>) {} // OK!
+
+    mod to { pub(super) trait somewhere {} }
+    trait Take { type path; }
+
+}
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed
deleted file mode 100644
index f5dbf0c8b6f..00000000000
--- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-
-use std::fmt;
-
-struct Hello;
-
-impl fmt::Display for Hello {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon
-        write!(f, "hello")
-    }
-}
-
-fn main() {
-    let _ = Hello;
-}
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs
deleted file mode 100644
index c41880a26f6..00000000000
--- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-
-use std::fmt;
-
-struct Hello;
-
-impl fmt::Display for Hello {
-    fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon
-        write!(f, "hello")
-    }
-}
-
-fn main() {
-    let _ = Hello;
-}
diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr
deleted file mode 100644
index 713b071a625..00000000000
--- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error: path separator must be a double colon
-  --> $DIR/argument-list-from-path-sep-error-129273.rs:8:30
-   |
-LL |     fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result {
-   |                              ^
-   |
-help: use a double colon instead
-   |
-LL |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-   |                               +
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.rs b/tests/ui/suggestions/struct-field-type-including-single-colon.rs
index a3111028895..482641fc7ca 100644
--- a/tests/ui/suggestions/struct-field-type-including-single-colon.rs
+++ b/tests/ui/suggestions/struct-field-type-including-single-colon.rs
@@ -7,14 +7,14 @@ mod foo {
 
 struct Foo {
     a: foo:A,
-    //~^ ERROR path separator must be a double colon
-    //~| ERROR struct `A` is private
+    //~^ ERROR found single colon in a struct field type path
+    //~| ERROR expected `,`, or `}`, found `:`
 }
 
 struct Bar {
     b: foo::bar:B,
-    //~^ ERROR path separator must be a double colon
-    //~| ERROR module `bar` is private
+    //~^ ERROR found single colon in a struct field type path
+    //~| ERROR expected `,`, or `}`, found `:`
 }
 
 fn main() {}
diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
index b9302b0453d..5ffc5b40849 100644
--- a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
+++ b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr
@@ -1,51 +1,40 @@
-error: path separator must be a double colon
+error: found single colon in a struct field type path
   --> $DIR/struct-field-type-including-single-colon.rs:9:11
    |
 LL |     a: foo:A,
    |           ^
    |
-help: use a double colon instead
+help: write a path separator here
    |
 LL |     a: foo::A,
    |            +
 
-error: path separator must be a double colon
+error: expected `,`, or `}`, found `:`
+  --> $DIR/struct-field-type-including-single-colon.rs:9:11
+   |
+LL | struct Foo {
+   |        --- while parsing this struct
+LL |     a: foo:A,
+   |           ^
+
+error: found single colon in a struct field type path
   --> $DIR/struct-field-type-including-single-colon.rs:15:16
    |
 LL |     b: foo::bar:B,
    |                ^
    |
-help: use a double colon instead
+help: write a path separator here
    |
 LL |     b: foo::bar::B,
    |                 +
 
-error[E0603]: struct `A` is private
-  --> $DIR/struct-field-type-including-single-colon.rs:9:12
-   |
-LL |     a: foo:A,
-   |            ^ private struct
-   |
-note: the struct `A` is defined here
-  --> $DIR/struct-field-type-including-single-colon.rs:2:5
-   |
-LL |     struct A;
-   |     ^^^^^^^^^
-
-error[E0603]: module `bar` is private
-  --> $DIR/struct-field-type-including-single-colon.rs:15:13
+error: expected `,`, or `}`, found `:`
+  --> $DIR/struct-field-type-including-single-colon.rs:15:16
    |
+LL | struct Bar {
+   |        --- while parsing this struct
 LL |     b: foo::bar:B,
-   |             ^^^ - struct `B` is not publicly re-exported
-   |             |
-   |             private module
-   |
-note: the module `bar` is defined here
-  --> $DIR/struct-field-type-including-single-colon.rs:3:5
-   |
-LL |     mod bar {
-   |     ^^^^^^^
+   |                ^
 
 error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0603`.