diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 39251f1ce2737..4be8a90368d29 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 1a02d45f0e3ce..1093e4f4af0a5 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 9eb62de275614..163ea4bbda6cd 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, | ^ | help: use a double colon instead | LL | a: Vec, - | + + | + 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 0000000000000..a9082ea317a78 --- /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: +//@ check-pass +#![crate_type = "lib"] +#![expect(non_camel_case_types)] + +#[rustfmt::skip] +mod garden { + + fn f() where path:to::somewhere {} // OK! + + fn g(_: impl Take) {} // OK! + + #[cfg(any())] fn h() where a::path:to::nowhere {} // OK! + + fn i(_: impl Take: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 f5dbf0c8b6f4e..0000000000000 --- 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 c41880a26f6ec..0000000000000 --- 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 713b071a625a0..0000000000000 --- 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 a3111028895dd..482641fc7cac9 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 b9302b0453d5b..5ffc5b40849b6 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`.