Skip to content

Commit

Permalink
Rollup merge of rust-lang#130673 - GrigorenkoPV:path-triple-colon, r=…
Browse files Browse the repository at this point in the history
…compiler-errors

Parser: recover from `:::` to `::`

Closes rust-lang#130613
  • Loading branch information
compiler-errors authored Sep 21, 2024
2 parents a66563f + 82482dc commit 52f146d
Show file tree
Hide file tree
Showing 11 changed files with 366 additions and 16 deletions.
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

0 comments on commit 52f146d

Please sign in to comment.