Skip to content

Revert overzealous parse recovery for single colons in paths #140228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand Down
20 changes: 7 additions & 13 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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

22 changes: 22 additions & 0 deletions tests/ui/parser/ty-path-followed-by-single-colon.rs
Original file line number Diff line number Diff line change
@@ -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; }

}

This file was deleted.

15 changes: 0 additions & 15 deletions tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
Loading