Skip to content
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

Parser: recover from ::: to :: #130673

Merged
merged 2 commits into from
Sep 22, 2024
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
2 changes: 1 addition & 1 deletion compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
parse_parenthesized_lifetime_suggestion = remove the parentheses

parse_path_single_colon = path separator must be a double colon
parse_path_double_colon = path separator must be a double colon
.suggestion = use a double colon instead

parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1571,7 +1571,7 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword {
}

#[derive(Diagnostic)]
#[diag(parse_path_single_colon)]
#[diag(parse_path_double_colon)]
pub(crate) struct PathSingleColon {
#[primary_span]
pub span: Span,
Expand All @@ -1583,6 +1583,14 @@ pub(crate) struct PathSingleColon {
pub type_ascription: bool,
}

#[derive(Diagnostic)]
#[diag(parse_path_double_colon)]
pub(crate) struct PathTripleColon {
#[primary_span]
#[suggestion(applicability = "maybe-incorrect", code = "", style = "verbose")]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_colon_as_semi)]
pub(crate) struct ColonAsSemi {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ impl<'a> Parser<'a> {
})
};

let (ident, item_kind) = if self.eat(&token::PathSep) {
let (ident, item_kind) = if self.eat_path_sep() {
let suffixes = if self.eat(&token::BinOp(token::Star)) {
None
} else {
Expand Down Expand Up @@ -1054,7 +1054,7 @@ impl<'a> Parser<'a> {
{
// `use *;` or `use ::*;` or `use {...};` or `use ::{...};`
let mod_sep_ctxt = self.token.span.ctxt();
if self.eat(&token::PathSep) {
if self.eat_path_sep() {
prefix
.segments
.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
Expand All @@ -1065,7 +1065,7 @@ impl<'a> Parser<'a> {
// `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;`
prefix = self.parse_path(PathStyle::Mod)?;

if self.eat(&token::PathSep) {
if self.eat_path_sep() {
self.parse_use_tree_glob_or_nested()?
} else {
// Recover from using a colon as path separator.
Expand Down
21 changes: 17 additions & 4 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1562,12 +1562,25 @@ impl<'a> Parser<'a> {
})
}

/// Checks for `::` or, potentially, `:::` and then look ahead after it.
fn check_path_sep_and_look_ahead(&mut self, looker: impl Fn(&Token) -> bool) -> bool {
if self.check(&token::PathSep) {
if self.may_recover() && self.look_ahead(1, |t| t.kind == token::Colon) {
debug_assert!(!self.look_ahead(1, &looker), "Looker must not match on colon");
self.look_ahead(2, looker)
} else {
self.look_ahead(1, looker)
}
} else {
false
}
}

/// `::{` or `::*`
fn is_import_coupler(&mut self) -> bool {
self.check(&token::PathSep)
&& self.look_ahead(1, |t| {
*t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star)
})
self.check_path_sep_and_look_ahead(|t| {
matches!(t.kind, token::OpenDelim(Delimiter::Brace) | token::BinOp(token::Star))
})
}

// Debug view of the parser's token stream, up to `{lookahead}` tokens.
Expand Down
25 changes: 18 additions & 7 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use tracing::debug;

use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType};
use crate::errors::PathSingleColon;
use crate::errors::{PathSingleColon, PathTripleColon};
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::{errors, maybe_whole};

Expand Down Expand Up @@ -210,7 +210,7 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let mut segments = ThinVec::new();
let mod_sep_ctxt = self.token.span.ctxt();
if self.eat(&token::PathSep) {
if self.eat_path_sep() {
segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
}
self.parse_path_segments(&mut segments, style, ty_generics)?;
Expand Down Expand Up @@ -246,7 +246,7 @@ impl<'a> Parser<'a> {
}
segments.push(segment);

if self.is_import_coupler() || !self.eat(&token::PathSep) {
if self.is_import_coupler() || !self.eat_path_sep() {
if style == PathStyle::Expr
&& self.may_recover()
&& self.token == token::Colon
Expand All @@ -272,6 +272,18 @@ impl<'a> Parser<'a> {
}
}

/// Eat `::` or, potentially, `:::`.
#[must_use]
pub(super) fn eat_path_sep(&mut self) -> bool {
let result = self.eat(&token::PathSep);
if result && self.may_recover() {
if self.eat_noexpect(&token::Colon) {
self.dcx().emit_err(PathTripleColon { span: self.prev_token.span });
}
}
result
}

pub(super) fn parse_path_segment(
&mut self,
style: PathStyle,
Expand All @@ -297,9 +309,7 @@ impl<'a> Parser<'a> {

Ok(
if style == PathStyle::Type && check_args_start(self)
|| style != PathStyle::Mod
&& self.check(&token::PathSep)
&& self.look_ahead(1, |t| is_args_start(t))
|| style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start)
{
// We use `style == PathStyle::Expr` to check if this is in a recursion or not. If
// it isn't, then we reset the unmatched angle bracket count as we're about to start
Expand All @@ -310,7 +320,8 @@ impl<'a> Parser<'a> {

// Generic arguments are found - `<`, `(`, `::<` or `::(`.
// First, eat `::` if it exists.
let _ = self.eat(&token::PathSep);
let _ = self.eat_path_sep();

let lo = self.token.span;
let args = if self.eat_lt() {
// `<'a, T, A = U>`
Expand Down
44 changes: 44 additions & 0 deletions tests/ui/parser/triple-colon-delegation.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ run-rustfix

#![feature(fn_delegation)]
#![allow(incomplete_features, unused)]

trait Trait {
fn foo(&self) {}
}

struct F;
impl Trait for F {}

pub mod to_reuse {
pub fn bar() {}
}

mod fn_to_other {
use super::*;

reuse Trait::foo; //~ ERROR path separator must be a double colon
reuse to_reuse::bar; //~ ERROR path separator must be a double colon
}

impl Trait for u8 {}

struct S(u8);

mod to_import {
pub fn check(arg: &u8) -> &u8 { arg }
}

impl Trait for S {
reuse Trait::* { //~ ERROR path separator must be a double colon
use to_import::check;

let _arr = Some(self.0).map(|x| [x * 2; 3]);
check(&self.0)
}
}

fn main() {
let s = S(0);
s.foo();
}
44 changes: 44 additions & 0 deletions tests/ui/parser/triple-colon-delegation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ run-rustfix

#![feature(fn_delegation)]
#![allow(incomplete_features, unused)]

trait Trait {
fn foo(&self) {}
}

struct F;
impl Trait for F {}

pub mod to_reuse {
pub fn bar() {}
}

mod fn_to_other {
use super::*;

reuse Trait:::foo; //~ ERROR path separator must be a double colon
reuse to_reuse:::bar; //~ ERROR path separator must be a double colon
}

impl Trait for u8 {}

struct S(u8);

mod to_import {
pub fn check(arg: &u8) -> &u8 { arg }
}

impl Trait for S {
reuse Trait:::* { //~ ERROR path separator must be a double colon
use to_import::check;

let _arr = Some(self.0).map(|x| [x * 2; 3]);
check(&self.0)
}
}

fn main() {
let s = S(0);
s.foo();
}
38 changes: 38 additions & 0 deletions tests/ui/parser/triple-colon-delegation.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error: path separator must be a double colon
--> $DIR/triple-colon-delegation.rs:20:18
|
LL | reuse Trait:::foo;
| ^
|
help: use a double colon instead
|
LL - reuse Trait:::foo;
LL + reuse Trait::foo;
|

error: path separator must be a double colon
--> $DIR/triple-colon-delegation.rs:21:21
|
LL | reuse to_reuse:::bar;
| ^
|
help: use a double colon instead
|
LL - reuse to_reuse:::bar;
LL + reuse to_reuse::bar;
|

error: path separator must be a double colon
--> $DIR/triple-colon-delegation.rs:33:18
|
LL | reuse Trait:::* {
| ^
|
help: use a double colon instead
|
LL - reuse Trait:::* {
LL + reuse Trait::* {
|

error: aborting due to 3 previous errors

23 changes: 23 additions & 0 deletions tests/ui/parser/triple-colon.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ run-rustfix

#![allow(unused)]

use ::std::{cell as _}; //~ ERROR path separator must be a double colon
use std::cell::*; //~ ERROR path separator must be a double colon
use std::cell::Cell; //~ ERROR path separator must be a double colon
use std::{cell as _}; //~ ERROR path separator must be a double colon

mod foo{
use ::{}; //~ ERROR path separator must be a double colon
use ::*; //~ ERROR path separator must be a double colon
}

fn main() {
let c: ::std::cell::Cell::<u8> = Cell::<u8>::new(0);
//~^ ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
}
23 changes: 23 additions & 0 deletions tests/ui/parser/triple-colon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ run-rustfix

#![allow(unused)]

use :::std::{cell as _}; //~ ERROR path separator must be a double colon
use std::cell:::*; //~ ERROR path separator must be a double colon
use std::cell:::Cell; //~ ERROR path separator must be a double colon
use std:::{cell as _}; //~ ERROR path separator must be a double colon

mod foo{
use :::{}; //~ ERROR path separator must be a double colon
use :::*; //~ ERROR path separator must be a double colon
}

fn main() {
let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
//~^ ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
//~| ERROR path separator must be a double colon
}
Loading
Loading