Skip to content

Commit 1f2ba0d

Browse files
Rollup merge of rust-lang#59058 - petrochenkov:assocrecov3, r=estebank
syntax: Better recovery for `$ty::AssocItem` and `ty!()::AssocItem` This PR improves on rust-lang#46788 covering a few missing cases. Fixes rust-lang#52307 Fixes rust-lang#53776 r? @estebank
2 parents 9c53011 + 1ab8ca3 commit 1f2ba0d

File tree

7 files changed

+217
-92
lines changed

7 files changed

+217
-92
lines changed

Diff for: src/libsyntax/parse/parser.rs

+82-88
Original file line numberDiff line numberDiff line change
@@ -118,23 +118,22 @@ enum BlockMode {
118118
/// `token::Interpolated` tokens.
119119
macro_rules! maybe_whole_expr {
120120
($p:expr) => {
121-
if let token::Interpolated(nt) = $p.token.clone() {
122-
match *nt {
123-
token::NtExpr(ref e) | token::NtLiteral(ref e) => {
121+
if let token::Interpolated(nt) = &$p.token {
122+
match &**nt {
123+
token::NtExpr(e) | token::NtLiteral(e) => {
124+
let e = e.clone();
124125
$p.bump();
125-
return Ok((*e).clone());
126+
return Ok(e);
126127
}
127-
token::NtPath(ref path) => {
128+
token::NtPath(path) => {
129+
let path = path.clone();
128130
$p.bump();
129-
let span = $p.span;
130-
let kind = ExprKind::Path(None, (*path).clone());
131-
return Ok($p.mk_expr(span, kind, ThinVec::new()));
131+
return Ok($p.mk_expr($p.span, ExprKind::Path(None, path), ThinVec::new()));
132132
}
133-
token::NtBlock(ref block) => {
133+
token::NtBlock(block) => {
134+
let block = block.clone();
134135
$p.bump();
135-
let span = $p.span;
136-
let kind = ExprKind::Block((*block).clone(), None);
137-
return Ok($p.mk_expr(span, kind, ThinVec::new()));
136+
return Ok($p.mk_expr($p.span, ExprKind::Block(block, None), ThinVec::new()));
138137
}
139138
_ => {},
140139
};
@@ -145,15 +144,31 @@ macro_rules! maybe_whole_expr {
145144
/// As maybe_whole_expr, but for things other than expressions
146145
macro_rules! maybe_whole {
147146
($p:expr, $constructor:ident, |$x:ident| $e:expr) => {
148-
if let token::Interpolated(nt) = $p.token.clone() {
149-
if let token::$constructor($x) = (*nt).clone() {
147+
if let token::Interpolated(nt) = &$p.token {
148+
if let token::$constructor(x) = &**nt {
149+
let $x = x.clone();
150150
$p.bump();
151151
return Ok($e);
152152
}
153153
}
154154
};
155155
}
156156

157+
/// If the next tokens are ill-formed `$ty::` recover them as `<$ty>::`.
158+
macro_rules! maybe_recover_from_interpolated_ty_qpath {
159+
($self: expr, $allow_qpath_recovery: expr) => {
160+
if $allow_qpath_recovery && $self.look_ahead(1, |t| t == &token::ModSep) {
161+
if let token::Interpolated(nt) = &$self.token {
162+
if let token::NtTy(ty) = &**nt {
163+
let ty = ty.clone();
164+
$self.bump();
165+
return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_span, ty);
166+
}
167+
}
168+
}
169+
}
170+
}
171+
157172
fn maybe_append(mut lhs: Vec<Attribute>, mut rhs: Option<Vec<Attribute>>) -> Vec<Attribute> {
158173
if let Some(ref mut rhs) = rhs {
159174
lhs.append(rhs);
@@ -172,48 +187,38 @@ enum PrevTokenKind {
172187
Other,
173188
}
174189

175-
trait RecoverQPath: Sized {
190+
trait RecoverQPath: Sized + 'static {
176191
const PATH_STYLE: PathStyle = PathStyle::Expr;
177192
fn to_ty(&self) -> Option<P<Ty>>;
178-
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self;
179-
fn to_string(&self) -> String;
193+
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
180194
}
181195

182196
impl RecoverQPath for Ty {
183197
const PATH_STYLE: PathStyle = PathStyle::Type;
184198
fn to_ty(&self) -> Option<P<Ty>> {
185199
Some(P(self.clone()))
186200
}
187-
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
188-
Self { span: path.span, node: TyKind::Path(qself, path), id: self.id }
189-
}
190-
fn to_string(&self) -> String {
191-
pprust::ty_to_string(self)
201+
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
202+
Self { span: path.span, node: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
192203
}
193204
}
194205

195206
impl RecoverQPath for Pat {
196207
fn to_ty(&self) -> Option<P<Ty>> {
197208
self.to_ty()
198209
}
199-
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
200-
Self { span: path.span, node: PatKind::Path(qself, path), id: self.id }
201-
}
202-
fn to_string(&self) -> String {
203-
pprust::pat_to_string(self)
210+
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
211+
Self { span: path.span, node: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
204212
}
205213
}
206214

207215
impl RecoverQPath for Expr {
208216
fn to_ty(&self) -> Option<P<Ty>> {
209217
self.to_ty()
210218
}
211-
fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
219+
fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
212220
Self { span: path.span, node: ExprKind::Path(qself, path),
213-
id: self.id, attrs: self.attrs.clone() }
214-
}
215-
fn to_string(&self) -> String {
216-
pprust::expr_to_string(self)
221+
attrs: ThinVec::new(), id: ast::DUMMY_NODE_ID }
217222
}
218223
}
219224

@@ -1649,6 +1654,7 @@ impl<'a> Parser<'a> {
16491654

16501655
fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
16511656
allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
1657+
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
16521658
maybe_whole!(self, NtTy, |x| x);
16531659

16541660
let lo = self.span;
@@ -1800,14 +1806,12 @@ impl<'a> Parser<'a> {
18001806
};
18011807

18021808
let span = lo.to(self.prev_span);
1803-
let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
1809+
let ty = P(Ty { node, span, id: ast::DUMMY_NODE_ID });
18041810

18051811
// Try to recover from use of `+` with incorrect priority.
18061812
self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
18071813
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
1808-
let ty = self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)?;
1809-
1810-
Ok(P(ty))
1814+
self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)
18111815
}
18121816

18131817
fn parse_remaining_bounds(&mut self, generic_params: Vec<GenericParam>, path: ast::Path,
@@ -1878,36 +1882,40 @@ impl<'a> Parser<'a> {
18781882
Ok(())
18791883
}
18801884

1881-
// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
1882-
fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: T, allow_recovery: bool)
1883-
-> PResult<'a, T> {
1885+
/// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
1886+
/// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem`
1887+
/// tail, and combine them into a `<Ty>::AssocItem` expression/pattern/type.
1888+
fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: P<T>, allow_recovery: bool)
1889+
-> PResult<'a, P<T>> {
18841890
// Do not add `::` to expected tokens.
1885-
if !allow_recovery || self.token != token::ModSep {
1886-
return Ok(base);
1891+
if allow_recovery && self.token == token::ModSep {
1892+
if let Some(ty) = base.to_ty() {
1893+
return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
1894+
}
18871895
}
1888-
let ty = match base.to_ty() {
1889-
Some(ty) => ty,
1890-
None => return Ok(base),
1891-
};
1896+
Ok(base)
1897+
}
18921898

1893-
self.bump(); // `::`
1894-
let mut segments = Vec::new();
1895-
self.parse_path_segments(&mut segments, T::PATH_STYLE, true)?;
1899+
/// Given an already parsed `Ty` parse the `::AssocItem` tail and
1900+
/// combine them into a `<Ty>::AssocItem` expression/pattern/type.
1901+
fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(&mut self, ty_span: Span, ty: P<Ty>)
1902+
-> PResult<'a, P<T>> {
1903+
self.expect(&token::ModSep)?;
18961904

1897-
let span = ty.span.to(self.prev_span);
1898-
let path_span = span.to(span); // use an empty path since `position` == 0
1899-
let recovered = base.to_recovered(
1900-
Some(QSelf { ty, path_span, position: 0 }),
1901-
ast::Path { segments, span },
1902-
);
1905+
let mut path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP };
1906+
self.parse_path_segments(&mut path.segments, T::PATH_STYLE, true)?;
1907+
path.span = ty_span.to(self.prev_span);
19031908

1909+
let ty_str = self.sess.source_map().span_to_snippet(ty_span)
1910+
.unwrap_or_else(|_| pprust::ty_to_string(&ty));
19041911
self.diagnostic()
1905-
.struct_span_err(span, "missing angle brackets in associated item path")
1912+
.struct_span_err(path.span, "missing angle brackets in associated item path")
19061913
.span_suggestion( // this is a best-effort recovery
1907-
span, "try", recovered.to_string(), Applicability::MaybeIncorrect
1914+
path.span, "try", format!("<{}>::{}", ty_str, path), Applicability::MaybeIncorrect
19081915
).emit();
19091916

1910-
Ok(recovered)
1917+
let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0
1918+
Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path)))
19111919
}
19121920

19131921
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
@@ -2572,15 +2580,6 @@ impl<'a> Parser<'a> {
25722580
ExprKind::AssignOp(binop, lhs, rhs)
25732581
}
25742582

2575-
pub fn mk_mac_expr(&mut self, span: Span, m: Mac_, attrs: ThinVec<Attribute>) -> P<Expr> {
2576-
P(Expr {
2577-
id: ast::DUMMY_NODE_ID,
2578-
node: ExprKind::Mac(source_map::Spanned {node: m, span: span}),
2579-
span,
2580-
attrs,
2581-
})
2582-
}
2583-
25842583
fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStream)> {
25852584
let delim = match self.token {
25862585
token::OpenDelim(delim) => delim,
@@ -2610,6 +2609,7 @@ impl<'a> Parser<'a> {
26102609
/// N.B., this does not parse outer attributes, and is private because it only works
26112610
/// correctly if called from `parse_dot_or_call_expr()`.
26122611
fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
2612+
maybe_recover_from_interpolated_ty_qpath!(self, true);
26132613
maybe_whole_expr!(self);
26142614

26152615
// Outer attributes are already parsed and will be
@@ -2824,29 +2824,23 @@ impl<'a> Parser<'a> {
28242824
db.note("variable declaration using `let` is a statement");
28252825
return Err(db);
28262826
} else if self.token.is_path_start() {
2827-
let pth = self.parse_path(PathStyle::Expr)?;
2827+
let path = self.parse_path(PathStyle::Expr)?;
28282828

28292829
// `!`, as an operator, is prefix, so we know this isn't that
28302830
if self.eat(&token::Not) {
28312831
// MACRO INVOCATION expression
28322832
let (delim, tts) = self.expect_delimited_token_tree()?;
2833-
let hi = self.prev_span;
2834-
let node = Mac_ { path: pth, tts, delim };
2835-
return Ok(self.mk_mac_expr(lo.to(hi), node, attrs))
2836-
}
2837-
if self.check(&token::OpenDelim(token::Brace)) {
2833+
hi = self.prev_span;
2834+
ex = ExprKind::Mac(respan(lo.to(hi), Mac_ { path, tts, delim }));
2835+
} else if self.check(&token::OpenDelim(token::Brace)) &&
2836+
!self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) {
28382837
// This is a struct literal, unless we're prohibited
28392838
// from parsing struct literals here.
2840-
let prohibited = self.restrictions.contains(
2841-
Restrictions::NO_STRUCT_LITERAL
2842-
);
2843-
if !prohibited {
2844-
return self.parse_struct_expr(lo, pth, attrs);
2845-
}
2839+
return self.parse_struct_expr(lo, path, attrs);
2840+
} else {
2841+
hi = path.span;
2842+
ex = ExprKind::Path(None, path);
28462843
}
2847-
2848-
hi = pth.span;
2849-
ex = ExprKind::Path(None, pth);
28502844
} else {
28512845
if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
28522846
// Don't complain about bare semicolons after unclosed braces
@@ -2881,10 +2875,8 @@ impl<'a> Parser<'a> {
28812875
}
28822876
}
28832877

2884-
let expr = Expr { node: ex, span: lo.to(hi), id: ast::DUMMY_NODE_ID, attrs };
2885-
let expr = self.maybe_recover_from_bad_qpath(expr, true)?;
2886-
2887-
return Ok(P(expr));
2878+
let expr = self.mk_expr(lo.to(hi), ex, attrs);
2879+
self.maybe_recover_from_bad_qpath(expr, true)
28882880
}
28892881

28902882
fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>)
@@ -4579,6 +4571,7 @@ impl<'a> Parser<'a> {
45794571
allow_range_pat: bool,
45804572
expected: Option<&'static str>,
45814573
) -> PResult<'a, P<Pat>> {
4574+
maybe_recover_from_interpolated_ty_qpath!(self, true);
45824575
maybe_whole!(self, NtPat, |x| x);
45834576

45844577
let lo = self.span;
@@ -4754,7 +4747,7 @@ impl<'a> Parser<'a> {
47544747
}
47554748
}
47564749

4757-
let pat = Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID };
4750+
let pat = P(Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID });
47584751
let pat = self.maybe_recover_from_bad_qpath(pat, true)?;
47594752

47604753
if !allow_range_pat {
@@ -4780,7 +4773,7 @@ impl<'a> Parser<'a> {
47804773
}
47814774
}
47824775

4783-
Ok(P(pat))
4776+
Ok(pat)
47844777
}
47854778

47864779
/// Parses `ident` or `ident @ pat`.
@@ -5244,7 +5237,8 @@ impl<'a> Parser<'a> {
52445237
self.warn_missing_semicolon();
52455238
StmtKind::Mac(P((mac, style, attrs.into())))
52465239
} else {
5247-
let e = self.mk_mac_expr(lo.to(hi), mac.node, ThinVec::new());
5240+
let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
5241+
let e = self.maybe_recover_from_bad_qpath(e, true)?;
52485242
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
52495243
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
52505244
StmtKind::Expr(e)

Diff for: src/test/ui/did_you_mean/bad-assoc-expr.rs

+16
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,19 @@ fn main() {
1818
10 + (u8)::clone(&0);
1919
//~^ ERROR missing angle brackets in associated item path
2020
}
21+
22+
macro_rules! expr {
23+
($ty: ty) => ($ty::clone(&0))
24+
//~^ ERROR missing angle brackets in associated item path
25+
}
26+
macro_rules! ty {
27+
() => (u8)
28+
}
29+
30+
fn check_macros() {
31+
expr!(u8);
32+
let _ = ty!()::clone(&0);
33+
//~^ ERROR missing angle brackets in associated item path
34+
ty!()::clone(&0);
35+
//~^ ERROR missing angle brackets in associated item path
36+
}

Diff for: src/test/ui/did_you_mean/bad-assoc-expr.stderr

+22-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,26 @@ error: missing angle brackets in associated item path
3434
LL | 10 + (u8)::clone(&0);
3535
| ^^^^^^^^^^^ help: try: `<(u8)>::clone`
3636

37-
error: aborting due to 6 previous errors
37+
error: missing angle brackets in associated item path
38+
--> $DIR/bad-assoc-expr.rs:32:13
39+
|
40+
LL | let _ = ty!()::clone(&0);
41+
| ^^^^^^^^^^^^ help: try: `<ty!()>::clone`
42+
43+
error: missing angle brackets in associated item path
44+
--> $DIR/bad-assoc-expr.rs:34:5
45+
|
46+
LL | ty!()::clone(&0);
47+
| ^^^^^^^^^^^^ help: try: `<ty!()>::clone`
48+
49+
error: missing angle brackets in associated item path
50+
--> $DIR/bad-assoc-expr.rs:23:19
51+
|
52+
LL | ($ty: ty) => ($ty::clone(&0))
53+
| ^^^^^^^^^^ help: try: `<$ty>::clone`
54+
...
55+
LL | expr!(u8);
56+
| ---------- in this macro invocation
57+
58+
error: aborting due to 9 previous errors
3859

Diff for: src/test/ui/did_you_mean/bad-assoc-pat.rs

+18
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,21 @@ fn main() {
1616
//~| ERROR no associated item named `AssocItem` found for type `(u8,)` in the current scope
1717
}
1818
}
19+
20+
macro_rules! pat {
21+
($ty: ty) => ($ty::AssocItem)
22+
//~^ ERROR missing angle brackets in associated item path
23+
//~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
24+
}
25+
macro_rules! ty {
26+
() => (u8)
27+
}
28+
29+
fn check_macros() {
30+
match 0u8 {
31+
pat!(u8) => {}
32+
ty!()::AssocItem => {}
33+
//~^ ERROR missing angle brackets in associated item path
34+
//~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
35+
}
36+
}

0 commit comments

Comments
 (0)