|
2 | 2 | //! found or is otherwise invalid.
|
3 | 3 |
|
4 | 4 | use crate::check::FnCtxt;
|
| 5 | +use crate::errors; |
5 | 6 | use rustc_ast::ast::Mutability;
|
6 | 7 | use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
7 | 8 | use rustc_errors::{
|
@@ -271,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
271 | 272 | }
|
272 | 273 | };
|
273 | 274 |
|
274 |
| - if self.suggest_constraining_numerical_ty( |
| 275 | + if self.suggest_wrapping_range_with_parens( |
| 276 | + tcx, actual, source, span, item_name, &ty_str, |
| 277 | + ) || self.suggest_constraining_numerical_ty( |
275 | 278 | tcx, actual, source, span, item_kind, item_name, &ty_str,
|
276 | 279 | ) {
|
277 | 280 | return None;
|
@@ -1202,6 +1205,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
1202 | 1205 | false
|
1203 | 1206 | }
|
1204 | 1207 |
|
| 1208 | + /// Suggest possible range with adding parentheses, for example: |
| 1209 | + /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. |
| 1210 | + fn suggest_wrapping_range_with_parens( |
| 1211 | + &self, |
| 1212 | + tcx: TyCtxt<'tcx>, |
| 1213 | + actual: Ty<'tcx>, |
| 1214 | + source: SelfSource<'tcx>, |
| 1215 | + span: Span, |
| 1216 | + item_name: Ident, |
| 1217 | + ty_str: &str, |
| 1218 | + ) -> bool { |
| 1219 | + if let SelfSource::MethodCall(expr) = source { |
| 1220 | + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { |
| 1221 | + if let Node::Expr(parent_expr) = parent { |
| 1222 | + let lang_item = match parent_expr.kind { |
| 1223 | + ExprKind::Struct(ref qpath, _, _) => match **qpath { |
| 1224 | + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), |
| 1225 | + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), |
| 1226 | + QPath::LangItem(LangItem::RangeToInclusive, ..) => { |
| 1227 | + Some(LangItem::RangeToInclusive) |
| 1228 | + } |
| 1229 | + _ => None, |
| 1230 | + }, |
| 1231 | + ExprKind::Call(ref func, _) => match func.kind { |
| 1232 | + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. |
| 1233 | + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { |
| 1234 | + Some(LangItem::RangeInclusiveStruct) |
| 1235 | + } |
| 1236 | + _ => None, |
| 1237 | + }, |
| 1238 | + _ => None, |
| 1239 | + }; |
| 1240 | + |
| 1241 | + if lang_item.is_none() { |
| 1242 | + continue; |
| 1243 | + } |
| 1244 | + |
| 1245 | + let span_included = match parent_expr.kind { |
| 1246 | + hir::ExprKind::Struct(_, eps, _) => { |
| 1247 | + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) |
| 1248 | + } |
| 1249 | + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. |
| 1250 | + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), |
| 1251 | + _ => false, |
| 1252 | + }; |
| 1253 | + |
| 1254 | + if !span_included { |
| 1255 | + continue; |
| 1256 | + } |
| 1257 | + |
| 1258 | + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); |
| 1259 | + let range_ty = |
| 1260 | + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); |
| 1261 | + |
| 1262 | + let pick = self.probe_for_name( |
| 1263 | + span, |
| 1264 | + Mode::MethodCall, |
| 1265 | + item_name, |
| 1266 | + IsSuggestion(true), |
| 1267 | + range_ty, |
| 1268 | + expr.hir_id, |
| 1269 | + ProbeScope::AllTraits, |
| 1270 | + ); |
| 1271 | + if pick.is_ok() { |
| 1272 | + let range_span = parent_expr.span.with_hi(expr.span.hi()); |
| 1273 | + tcx.sess.emit_err(errors::MissingParentheseInRange { |
| 1274 | + span, |
| 1275 | + ty_str: ty_str.to_string(), |
| 1276 | + method_name: item_name.as_str().to_string(), |
| 1277 | + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { |
| 1278 | + func_name: item_name.name.as_str().to_string(), |
| 1279 | + left: range_span.shrink_to_lo(), |
| 1280 | + right: range_span.shrink_to_hi(), |
| 1281 | + }), |
| 1282 | + }); |
| 1283 | + return true; |
| 1284 | + } |
| 1285 | + } |
| 1286 | + } |
| 1287 | + } |
| 1288 | + false |
| 1289 | + } |
| 1290 | + |
1205 | 1291 | fn suggest_constraining_numerical_ty(
|
1206 | 1292 | &self,
|
1207 | 1293 | tcx: TyCtxt<'tcx>,
|
@@ -1264,7 +1350,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
1264 | 1350 | // If this is a floating point literal that ends with '.',
|
1265 | 1351 | // get rid of it to stop this from becoming a member access.
|
1266 | 1352 | let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
|
1267 |
| - |
1268 | 1353 | err.span_suggestion(
|
1269 | 1354 | lit.span,
|
1270 | 1355 | &format!(
|
|
0 commit comments