From e90e2593ea9c29ff6be20ec38b314fb2d6d927a4 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sat, 21 Sep 2024 20:07:52 +0300 Subject: [PATCH] Parser: recover from `:::` to `::` --- compiler/rustc_parse/messages.ftl | 2 +- compiler/rustc_parse/src/errors.rs | 10 +- compiler/rustc_parse/src/parser/item.rs | 4 +- compiler/rustc_parse/src/parser/mod.rs | 21 +++- compiler/rustc_parse/src/parser/path.rs | 25 ++-- tests/ui/parser/triple-colon.fixed | 23 ++++ tests/ui/parser/triple-colon.rs | 23 ++++ tests/ui/parser/triple-colon.stderr | 146 ++++++++++++++++++++++++ 8 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 tests/ui/parser/triple-colon.fixed create mode 100644 tests/ui/parser/triple-colon.rs create mode 100644 tests/ui/parser/triple-colon.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f5aa8984f51fc..6cb851eb8df6b 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index e3e7fcebaaabb..e9fe2e6c1dd12 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -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, @@ -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 { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 104678e081c30..25627ad53a396 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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))); @@ -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. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 9d9265d5318db..3b58b2337f36e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -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. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 42039c621d63c..961679b1f562f 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -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}; @@ -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)?; @@ -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 @@ -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, @@ -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 @@ -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>` diff --git a/tests/ui/parser/triple-colon.fixed b/tests/ui/parser/triple-colon.fixed new file mode 100644 index 0000000000000..168e4c1f618ae --- /dev/null +++ b/tests/ui/parser/triple-colon.fixed @@ -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:: = Cell::::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 +} diff --git a/tests/ui/parser/triple-colon.rs b/tests/ui/parser/triple-colon.rs new file mode 100644 index 0000000000000..1a70012685f8d --- /dev/null +++ b/tests/ui/parser/triple-colon.rs @@ -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::: = Cell::::::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 +} diff --git a/tests/ui/parser/triple-colon.stderr b/tests/ui/parser/triple-colon.stderr new file mode 100644 index 0000000000000..8d57fd7ebc9fe --- /dev/null +++ b/tests/ui/parser/triple-colon.stderr @@ -0,0 +1,146 @@ +error: path separator must be a double colon + --> $DIR/triple-colon.rs:5:7 + | +LL | use :::std::{cell as _}; + | ^ + | +help: use a double colon instead + | +LL - use :::std::{cell as _}; +LL + use ::std::{cell as _}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:6:16 + | +LL | use std::cell:::*; + | ^ + | +help: use a double colon instead + | +LL - use std::cell:::*; +LL + use std::cell::*; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:7:16 + | +LL | use std::cell:::Cell; + | ^ + | +help: use a double colon instead + | +LL - use std::cell:::Cell; +LL + use std::cell::Cell; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:8:10 + | +LL | use std:::{cell as _}; + | ^ + | +help: use a double colon instead + | +LL - use std:::{cell as _}; +LL + use std::{cell as _}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:11:11 + | +LL | use :::{}; + | ^ + | +help: use a double colon instead + | +LL - use :::{}; +LL + use ::{}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:12:11 + | +LL | use :::*; + | ^ + | +help: use a double colon instead + | +LL - use :::*; +LL + use ::*; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:14 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: ::std:::cell:::Cell::: = Cell::::::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:20 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: :::std::cell:::Cell::: = Cell::::::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:27 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: :::std:::cell::Cell::: = Cell::::::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:34 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: :::std:::cell:::Cell:: = Cell::::::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:48 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: :::std:::cell:::Cell::: = Cell:::::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:55 + | +LL | let c: :::std:::cell:::Cell::: = Cell::::::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell::: = Cell::::::new(0); +LL + let c: :::std:::cell:::Cell::: = Cell:::::new(0); + | + +error: aborting due to 12 previous errors +