about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_analysis/src')
-rw-r--r--compiler/rustc_hir_analysis/src/check/method/suggest.rs75
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs25
2 files changed, 95 insertions, 5 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
index 0e82e4956c7..d7189e70d06 100644
--- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs
+++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
@@ -2,6 +2,7 @@
 //! found or is otherwise invalid.
 
 use crate::check::FnCtxt;
+use crate::errors;
 use rustc_ast::ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{
@@ -12,7 +13,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, Node, QPath};
+use rustc_hir::{is_range_literal, ExprKind, Node, QPath};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::traits::util::supertraits;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
@@ -271,9 +272,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 };
 
-                if self.suggest_constraining_numerical_ty(
-                    tcx, actual, source, span, item_kind, item_name, &ty_str,
-                ) {
+                if self.suggest_range_for_iter(tcx, actual, source, span, item_name, &ty_str)
+                    || self.suggest_constraining_numerical_ty(
+                        tcx, actual, source, span, item_kind, item_name, &ty_str,
+                    )
+                {
                     return None;
                 }
 
@@ -1201,6 +1204,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
+    fn suggest_range_for_iter(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        actual: Ty<'tcx>,
+        source: SelfSource<'tcx>,
+        span: Span,
+        item_name: Ident,
+        ty_str: &str,
+    ) -> bool {
+        if let SelfSource::MethodCall(expr) = source {
+            let mut search_limit = 5;
+            for (_, parent) in tcx.hir().parent_iter(expr.hir_id) {
+                search_limit -= 1;
+                if search_limit == 0 {
+                    break;
+                }
+
+                if let Node::Expr(parent_expr) = parent && is_range_literal(parent_expr) {
+                    let span_included = match parent_expr.kind {
+                            hir::ExprKind::Struct(_, eps, _) =>
+                                eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)),
+                            // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+                            hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
+                            _ => false,
+                    };
+
+                    if !span_included {
+                        continue;
+                    }
+
+                    let range_def_id = self.tcx.lang_items().range_struct().unwrap();
+                    let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
+
+                    // avoid suggesting when the method name is not implemented for a `range`
+                    let pick =  self.lookup_probe(
+                        span,
+                        item_name,
+                        range_ty,
+                        expr,
+                        ProbeScope::AllTraits
+                    );
+
+                    if pick.is_ok() {
+                        let range_span = parent_expr.span.with_hi(expr.span.hi());
+                        tcx.sess.emit_err(errors::MissingParentheseInRange {
+                            span: span,
+                            ty_str: ty_str.to_string(),
+                            add_missing_parentheses: Some(
+                                errors::AddMissingParenthesesInRange {
+                                    func_name: item_name.name.as_str().to_string(),
+                                    left: range_span.shrink_to_lo(),
+                                    right: range_span.shrink_to_hi(),
+                                }
+                            )
+                        });
+                        return true;
+                    }
+                }
+            }
+        }
+        false
+    }
+
     fn suggest_constraining_numerical_ty(
         &self,
         tcx: TyCtxt<'tcx>,
@@ -1263,7 +1329,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // If this is a floating point literal that ends with '.',
                     // get rid of it to stop this from becoming a member access.
                     let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
-
                     err.span_suggestion(
                         lit.span,
                         &format!(
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index d891171b824..6634444c636 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -346,3 +346,28 @@ pub struct ExpectedUsedSymbol {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")]
+pub struct MissingParentheseInRange {
+    #[primary_span]
+    #[label(hir_analysis::missing_parentheses_in_range)]
+    pub span: Span,
+    pub ty_str: String,
+
+    #[subdiagnostic]
+    pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion_verbose(
+    hir_analysis::add_missing_parentheses_in_range,
+    applicability = "maybe-incorrect"
+)]
+pub struct AddMissingParenthesesInRange {
+    pub func_name: String,
+    #[suggestion_part(code = "(")]
+    pub left: Span,
+    #[suggestion_part(code = ")")]
+    pub right: Span,
+}