|
| 1 | +use crate::ast; |
| 2 | +use crate::ast::{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 | +} |
0 commit comments