Skip to content

Commit e90e259

Browse files
committed
Parser: recover from ::: to ::
1 parent 74fd001 commit e90e259

File tree

8 files changed

+239
-15
lines changed

8 files changed

+239
-15
lines changed

Diff for: compiler/rustc_parse/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call
670670
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
671671
parse_parenthesized_lifetime_suggestion = remove the parentheses
672672
673-
parse_path_single_colon = path separator must be a double colon
673+
parse_path_double_colon = path separator must be a double colon
674674
.suggestion = use a double colon instead
675675
676676
parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies

Diff for: compiler/rustc_parse/src/errors.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,7 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword {
15711571
}
15721572

15731573
#[derive(Diagnostic)]
1574-
#[diag(parse_path_single_colon)]
1574+
#[diag(parse_path_double_colon)]
15751575
pub(crate) struct PathSingleColon {
15761576
#[primary_span]
15771577
pub span: Span,
@@ -1583,6 +1583,14 @@ pub(crate) struct PathSingleColon {
15831583
pub type_ascription: bool,
15841584
}
15851585

1586+
#[derive(Diagnostic)]
1587+
#[diag(parse_path_double_colon)]
1588+
pub(crate) struct PathTripleColon {
1589+
#[primary_span]
1590+
#[suggestion(applicability = "maybe-incorrect", code = "", style = "verbose")]
1591+
pub span: Span,
1592+
}
1593+
15861594
#[derive(Diagnostic)]
15871595
#[diag(parse_colon_as_semi)]
15881596
pub(crate) struct ColonAsSemi {

Diff for: compiler/rustc_parse/src/parser/item.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ impl<'a> Parser<'a> {
10541054
{
10551055
// `use *;` or `use ::*;` or `use {...};` or `use ::{...};`
10561056
let mod_sep_ctxt = self.token.span.ctxt();
1057-
if self.eat(&token::PathSep) {
1057+
if self.eat_path_sep() {
10581058
prefix
10591059
.segments
10601060
.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
@@ -1065,7 +1065,7 @@ impl<'a> Parser<'a> {
10651065
// `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;`
10661066
prefix = self.parse_path(PathStyle::Mod)?;
10671067

1068-
if self.eat(&token::PathSep) {
1068+
if self.eat_path_sep() {
10691069
self.parse_use_tree_glob_or_nested()?
10701070
} else {
10711071
// Recover from using a colon as path separator.

Diff for: compiler/rustc_parse/src/parser/mod.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -1562,12 +1562,25 @@ impl<'a> Parser<'a> {
15621562
})
15631563
}
15641564

1565+
/// Checks for `::` or, potentially, `:::` and then look ahead after it.
1566+
fn check_path_sep_and_look_ahead(&mut self, looker: impl Fn(&Token) -> bool) -> bool {
1567+
if self.check(&token::PathSep) {
1568+
if self.may_recover() && self.look_ahead(1, |t| t.kind == token::Colon) {
1569+
debug_assert!(!self.look_ahead(1, &looker), "Looker must not match on colon");
1570+
self.look_ahead(2, looker)
1571+
} else {
1572+
self.look_ahead(1, looker)
1573+
}
1574+
} else {
1575+
false
1576+
}
1577+
}
1578+
15651579
/// `::{` or `::*`
15661580
fn is_import_coupler(&mut self) -> bool {
1567-
self.check(&token::PathSep)
1568-
&& self.look_ahead(1, |t| {
1569-
*t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star)
1570-
})
1581+
self.check_path_sep_and_look_ahead(|t| {
1582+
matches!(t.kind, token::OpenDelim(Delimiter::Brace) | token::BinOp(token::Star))
1583+
})
15711584
}
15721585

15731586
// Debug view of the parser's token stream, up to `{lookahead}` tokens.

Diff for: compiler/rustc_parse/src/parser/path.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use tracing::debug;
1616

1717
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
1818
use super::{Parser, Restrictions, TokenType};
19-
use crate::errors::PathSingleColon;
19+
use crate::errors::{PathSingleColon, PathTripleColon};
2020
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
2121
use crate::{errors, maybe_whole};
2222

@@ -210,7 +210,7 @@ impl<'a> Parser<'a> {
210210
let lo = self.token.span;
211211
let mut segments = ThinVec::new();
212212
let mod_sep_ctxt = self.token.span.ctxt();
213-
if self.eat(&token::PathSep) {
213+
if self.eat_path_sep() {
214214
segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
215215
}
216216
self.parse_path_segments(&mut segments, style, ty_generics)?;
@@ -246,7 +246,7 @@ impl<'a> Parser<'a> {
246246
}
247247
segments.push(segment);
248248

249-
if self.is_import_coupler() || !self.eat(&token::PathSep) {
249+
if self.is_import_coupler() || !self.eat_path_sep() {
250250
if style == PathStyle::Expr
251251
&& self.may_recover()
252252
&& self.token == token::Colon
@@ -272,6 +272,18 @@ impl<'a> Parser<'a> {
272272
}
273273
}
274274

275+
/// Eat `::` or, potentially, `:::`.
276+
#[must_use]
277+
pub(super) fn eat_path_sep(&mut self) -> bool {
278+
let result = self.eat(&token::PathSep);
279+
if result && self.may_recover() {
280+
if self.eat_noexpect(&token::Colon) {
281+
self.dcx().emit_err(PathTripleColon { span: self.prev_token.span });
282+
}
283+
}
284+
result
285+
}
286+
275287
pub(super) fn parse_path_segment(
276288
&mut self,
277289
style: PathStyle,
@@ -297,9 +309,7 @@ impl<'a> Parser<'a> {
297309

298310
Ok(
299311
if style == PathStyle::Type && check_args_start(self)
300-
|| style != PathStyle::Mod
301-
&& self.check(&token::PathSep)
302-
&& self.look_ahead(1, |t| is_args_start(t))
312+
|| style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start)
303313
{
304314
// We use `style == PathStyle::Expr` to check if this is in a recursion or not. If
305315
// 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> {
310320

311321
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
312322
// First, eat `::` if it exists.
313-
let _ = self.eat(&token::PathSep);
323+
let _ = self.eat_path_sep();
324+
314325
let lo = self.token.span;
315326
let args = if self.eat_lt() {
316327
// `<'a, T, A = U>`

Diff for: tests/ui/parser/triple-colon.fixed

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
use ::std::{cell as _}; //~ ERROR path separator must be a double colon
6+
use std::cell::*; //~ ERROR path separator must be a double colon
7+
use std::cell::Cell; //~ ERROR path separator must be a double colon
8+
use std::{cell as _}; //~ ERROR path separator must be a double colon
9+
10+
mod foo{
11+
use ::{}; //~ ERROR path separator must be a double colon
12+
use ::*; //~ ERROR path separator must be a double colon
13+
}
14+
15+
fn main() {
16+
let c: ::std::cell::Cell::<u8> = Cell::<u8>::new(0);
17+
//~^ ERROR path separator must be a double colon
18+
//~| ERROR path separator must be a double colon
19+
//~| ERROR path separator must be a double colon
20+
//~| ERROR path separator must be a double colon
21+
//~| ERROR path separator must be a double colon
22+
//~| ERROR path separator must be a double colon
23+
}

Diff for: tests/ui/parser/triple-colon.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ run-rustfix
2+
3+
#![allow(unused)]
4+
5+
use :::std::{cell as _}; //~ ERROR path separator must be a double colon
6+
use std::cell:::*; //~ ERROR path separator must be a double colon
7+
use std::cell:::Cell; //~ ERROR path separator must be a double colon
8+
use std:::{cell as _}; //~ ERROR path separator must be a double colon
9+
10+
mod foo{
11+
use :::{}; //~ ERROR path separator must be a double colon
12+
use :::*; //~ ERROR path separator must be a double colon
13+
}
14+
15+
fn main() {
16+
let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
17+
//~^ ERROR path separator must be a double colon
18+
//~| ERROR path separator must be a double colon
19+
//~| ERROR path separator must be a double colon
20+
//~| ERROR path separator must be a double colon
21+
//~| ERROR path separator must be a double colon
22+
//~| ERROR path separator must be a double colon
23+
}

Diff for: tests/ui/parser/triple-colon.stderr

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
error: path separator must be a double colon
2+
--> $DIR/triple-colon.rs:5:7
3+
|
4+
LL | use :::std::{cell as _};
5+
| ^
6+
|
7+
help: use a double colon instead
8+
|
9+
LL - use :::std::{cell as _};
10+
LL + use ::std::{cell as _};
11+
|
12+
13+
error: path separator must be a double colon
14+
--> $DIR/triple-colon.rs:6:16
15+
|
16+
LL | use std::cell:::*;
17+
| ^
18+
|
19+
help: use a double colon instead
20+
|
21+
LL - use std::cell:::*;
22+
LL + use std::cell::*;
23+
|
24+
25+
error: path separator must be a double colon
26+
--> $DIR/triple-colon.rs:7:16
27+
|
28+
LL | use std::cell:::Cell;
29+
| ^
30+
|
31+
help: use a double colon instead
32+
|
33+
LL - use std::cell:::Cell;
34+
LL + use std::cell::Cell;
35+
|
36+
37+
error: path separator must be a double colon
38+
--> $DIR/triple-colon.rs:8:10
39+
|
40+
LL | use std:::{cell as _};
41+
| ^
42+
|
43+
help: use a double colon instead
44+
|
45+
LL - use std:::{cell as _};
46+
LL + use std::{cell as _};
47+
|
48+
49+
error: path separator must be a double colon
50+
--> $DIR/triple-colon.rs:11:11
51+
|
52+
LL | use :::{};
53+
| ^
54+
|
55+
help: use a double colon instead
56+
|
57+
LL - use :::{};
58+
LL + use ::{};
59+
|
60+
61+
error: path separator must be a double colon
62+
--> $DIR/triple-colon.rs:12:11
63+
|
64+
LL | use :::*;
65+
| ^
66+
|
67+
help: use a double colon instead
68+
|
69+
LL - use :::*;
70+
LL + use ::*;
71+
|
72+
73+
error: path separator must be a double colon
74+
--> $DIR/triple-colon.rs:16:14
75+
|
76+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
77+
| ^
78+
|
79+
help: use a double colon instead
80+
|
81+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
82+
LL + let c: ::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
83+
|
84+
85+
error: path separator must be a double colon
86+
--> $DIR/triple-colon.rs:16:20
87+
|
88+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
89+
| ^
90+
|
91+
help: use a double colon instead
92+
|
93+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
94+
LL + let c: :::std::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
95+
|
96+
97+
error: path separator must be a double colon
98+
--> $DIR/triple-colon.rs:16:27
99+
|
100+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
101+
| ^
102+
|
103+
help: use a double colon instead
104+
|
105+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
106+
LL + let c: :::std:::cell::Cell:::<u8> = Cell:::<u8>:::new(0);
107+
|
108+
109+
error: path separator must be a double colon
110+
--> $DIR/triple-colon.rs:16:34
111+
|
112+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
113+
| ^
114+
|
115+
help: use a double colon instead
116+
|
117+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
118+
LL + let c: :::std:::cell:::Cell::<u8> = Cell:::<u8>:::new(0);
119+
|
120+
121+
error: path separator must be a double colon
122+
--> $DIR/triple-colon.rs:16:48
123+
|
124+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
125+
| ^
126+
|
127+
help: use a double colon instead
128+
|
129+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
130+
LL + let c: :::std:::cell:::Cell:::<u8> = Cell::<u8>:::new(0);
131+
|
132+
133+
error: path separator must be a double colon
134+
--> $DIR/triple-colon.rs:16:55
135+
|
136+
LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
137+
| ^
138+
|
139+
help: use a double colon instead
140+
|
141+
LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0);
142+
LL + let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>::new(0);
143+
|
144+
145+
error: aborting due to 12 previous errors
146+

0 commit comments

Comments
 (0)