]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/parse/diagnostics.rs
Review comments
[rust.git] / src / libsyntax / parse / diagnostics.rs
1 use crate::ast;
2 use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
3 use crate::parse::parser::PathStyle;
4 use crate::parse::token;
5 use crate::parse::PResult;
6 use crate::parse::Parser;
7 use crate::print::pprust;
8 use crate::ptr::P;
9 use crate::ThinVec;
10 use errors::Applicability;
11 use syntax_pos::Span;
12
13 pub trait RecoverQPath: Sized + 'static {
14     const PATH_STYLE: PathStyle = PathStyle::Expr;
15     fn to_ty(&self) -> Option<P<Ty>>;
16     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
17 }
18
19 impl RecoverQPath for Ty {
20     const PATH_STYLE: PathStyle = PathStyle::Type;
21     fn to_ty(&self) -> Option<P<Ty>> {
22         Some(P(self.clone()))
23     }
24     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
25         Self {
26             span: path.span,
27             node: TyKind::Path(qself, path),
28             id: ast::DUMMY_NODE_ID,
29         }
30     }
31 }
32
33 impl RecoverQPath for Pat {
34     fn to_ty(&self) -> Option<P<Ty>> {
35         self.to_ty()
36     }
37     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
38         Self {
39             span: path.span,
40             node: PatKind::Path(qself, path),
41             id: ast::DUMMY_NODE_ID,
42         }
43     }
44 }
45
46 impl RecoverQPath for Expr {
47     fn to_ty(&self) -> Option<P<Ty>> {
48         self.to_ty()
49     }
50     fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
51         Self {
52             span: path.span,
53             node: ExprKind::Path(qself, path),
54             attrs: ThinVec::new(),
55             id: ast::DUMMY_NODE_ID,
56         }
57     }
58 }
59
60 impl<'a> Parser<'a> {
61     crate fn maybe_report_ambiguous_plus(
62         &mut self,
63         allow_plus: bool,
64         impl_dyn_multi: bool,
65         ty: &Ty,
66     ) {
67         if !allow_plus && impl_dyn_multi {
68             let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
69             self.struct_span_err(ty.span, "ambiguous `+` in a type")
70                 .span_suggestion(
71                     ty.span,
72                     "use parentheses to disambiguate",
73                     sum_with_parens,
74                     Applicability::MachineApplicable,
75                 )
76                 .emit();
77         }
78     }
79
80     crate fn maybe_recover_from_bad_type_plus(
81         &mut self,
82         allow_plus: bool,
83         ty: &Ty,
84     ) -> PResult<'a, ()> {
85         // Do not add `+` to expected tokens.
86         if !allow_plus || !self.token.is_like_plus() {
87             return Ok(());
88         }
89
90         self.bump(); // `+`
91         let bounds = self.parse_generic_bounds(None)?;
92         let sum_span = ty.span.to(self.prev_span);
93
94         let mut err = struct_span_err!(
95             self.sess.span_diagnostic,
96             sum_span,
97             E0178,
98             "expected a path on the left-hand side of `+`, not `{}`",
99             pprust::ty_to_string(ty)
100         );
101
102         match ty.node {
103             TyKind::Rptr(ref lifetime, ref mut_ty) => {
104                 let sum_with_parens = pprust::to_string(|s| {
105                     use crate::print::pprust::PrintState;
106
107                     s.s.word("&")?;
108                     s.print_opt_lifetime(lifetime)?;
109                     s.print_mutability(mut_ty.mutbl)?;
110                     s.popen()?;
111                     s.print_type(&mut_ty.ty)?;
112                     s.print_type_bounds(" +", &bounds)?;
113                     s.pclose()
114                 });
115                 err.span_suggestion(
116                     sum_span,
117                     "try adding parentheses",
118                     sum_with_parens,
119                     Applicability::MachineApplicable,
120                 );
121             }
122             TyKind::Ptr(..) | TyKind::BareFn(..) => {
123                 err.span_label(sum_span, "perhaps you forgot parentheses?");
124             }
125             _ => {
126                 err.span_label(sum_span, "expected a path");
127             }
128         }
129         err.emit();
130         Ok(())
131     }
132
133     /// Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`.
134     /// Attempt to convert the base expression/pattern/type into a type, parse the `::AssocItem`
135     /// tail, and combine them into a `<Ty>::AssocItem` expression/pattern/type.
136     crate fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
137         &mut self,
138         base: P<T>,
139         allow_recovery: bool,
140     ) -> PResult<'a, P<T>> {
141         // Do not add `::` to expected tokens.
142         if allow_recovery && self.token == token::ModSep {
143             if let Some(ty) = base.to_ty() {
144                 return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
145             }
146         }
147         Ok(base)
148     }
149
150     /// Given an already parsed `Ty` parse the `::AssocItem` tail and
151     /// combine them into a `<Ty>::AssocItem` expression/pattern/type.
152     crate fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
153         &mut self,
154         ty_span: Span,
155         ty: P<Ty>,
156     ) -> PResult<'a, P<T>> {
157         self.expect(&token::ModSep)?;
158
159         let mut path = ast::Path {
160             segments: Vec::new(),
161             span: syntax_pos::DUMMY_SP,
162         };
163         self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
164         path.span = ty_span.to(self.prev_span);
165
166         let ty_str = self
167             .sess
168             .source_map()
169             .span_to_snippet(ty_span)
170             .unwrap_or_else(|_| pprust::ty_to_string(&ty));
171         self.diagnostic()
172             .struct_span_err(path.span, "missing angle brackets in associated item path")
173             .span_suggestion(
174                 // this is a best-effort recovery
175                 path.span,
176                 "try",
177                 format!("<{}>::{}", ty_str, path),
178                 Applicability::MaybeIncorrect,
179             )
180             .emit();
181
182         let path_span = ty_span.shrink_to_hi(); // use an empty path since `position` == 0
183         Ok(P(T::recovered(
184             Some(QSelf {
185                 ty,
186                 path_span,
187                 position: 0,
188             }),
189             path,
190         )))
191     }
192
193     crate fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
194         if self.eat(&token::Semi) {
195             let mut err = self.struct_span_err(self.prev_span, "expected item, found `;`");
196             err.span_suggestion_short(
197                 self.prev_span,
198                 "remove this semicolon",
199                 String::new(),
200                 Applicability::MachineApplicable,
201             );
202             if !items.is_empty() {
203                 let previous_item = &items[items.len() - 1];
204                 let previous_item_kind_name = match previous_item.node {
205                     // say "braced struct" because tuple-structs and
206                     // braceless-empty-struct declarations do take a semicolon
207                     ItemKind::Struct(..) => Some("braced struct"),
208                     ItemKind::Enum(..) => Some("enum"),
209                     ItemKind::Trait(..) => Some("trait"),
210                     ItemKind::Union(..) => Some("union"),
211                     _ => None,
212                 };
213                 if let Some(name) = previous_item_kind_name {
214                     err.help(&format!(
215                         "{} declarations are not followed by a semicolon",
216                         name
217                     ));
218                 }
219             }
220             err.emit();
221             true
222         } else {
223             false
224         }
225     }
226
227     /// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
228     /// and `await { <expr> }`.
229     crate fn parse_incorrect_await_syntax(
230         &mut self,
231         lo: Span,
232         await_sp: Span,
233     ) -> PResult<'a, (Span, ExprKind)> {
234         let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
235         let expr = if self.token == token::OpenDelim(token::Brace) {
236             // Handle `await { <expr> }`.
237             // This needs to be handled separatedly from the next arm to avoid
238             // interpreting `await { <expr> }?` as `<expr>?.await`.
239             self.parse_block_expr(
240                 None,
241                 self.span,
242                 BlockCheckMode::Default,
243                 ThinVec::new(),
244             )
245         } else {
246             self.parse_expr()
247         }.map_err(|mut err| {
248             err.span_label(await_sp, "while parsing this incorrect await expression");
249             err
250         })?;
251         let expr_str = self.sess.source_map().span_to_snippet(expr.span)
252             .unwrap_or_else(|_| pprust::expr_to_string(&expr));
253         let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
254         let sp = lo.to(expr.span);
255         let app = match expr.node {
256             ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await <expr>?`
257             _ => Applicability::MachineApplicable,
258         };
259         self.struct_span_err(sp, "incorrect use of `await`")
260             .span_suggestion(sp, "`await` is a postfix operation", suggestion, app)
261             .emit();
262         Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)))
263     }
264 }