]> git.lizzy.rs Git - rust.git/commitdiff
Detect when negative literal indices are used and suggest appropriate code
authorEsteban Kuber <esteban@kuber.com.ar>
Sat, 25 Sep 2021 18:23:07 +0000 (18:23 +0000)
committerEsteban Kuber <esteban@kuber.com.ar>
Sun, 26 Sep 2021 16:52:10 +0000 (16:52 +0000)
compiler/rustc_typeck/src/check/expr.rs
compiler/rustc_typeck/src/check/place_op.rs
src/test/ui/suggestions/negative-literal-index.fixed [new file with mode: 0644]
src/test/ui/suggestions/negative-literal-index.rs [new file with mode: 0644]
src/test/ui/suggestions/negative-literal-index.stderr [new file with mode: 0644]

index 8a69e0a737d501c1b2aed4d780c1a65fd8b321da..09aeb62fc02c7e507c37fe0ab6dc931ec1bd0abd 100644 (file)
@@ -2136,7 +2136,7 @@ fn check_expr_index(
             idx_t
         } else {
             let base_t = self.structurally_resolved_type(base.span, base_t);
-            match self.lookup_indexing(expr, base, base_t, idx_t) {
+            match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
                 Some((index_ty, element_ty)) => {
                     // two-phase not needed because index_ty is never mutable
                     self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
index 055072d3a1d9d4a9cf020d6d7f999592b7029091..e5a5066544a8fd8790648e89b696b74c094b3747 100644 (file)
@@ -1,5 +1,7 @@
 use crate::check::method::MethodCallee;
 use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
+use rustc_ast as ast;
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::InferOk;
@@ -47,6 +49,7 @@ pub(super) fn lookup_indexing(
         expr: &hir::Expr<'_>,
         base_expr: &'tcx hir::Expr<'tcx>,
         base_ty: Ty<'tcx>,
+        idx_expr: &'tcx hir::Expr<'tcx>,
         idx_ty: Ty<'tcx>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
         // FIXME(#18741) -- this is almost but not quite the same as the
@@ -56,7 +59,7 @@ pub(super) fn lookup_indexing(
         let mut autoderef = self.autoderef(base_expr.span, base_ty);
         let mut result = None;
         while result.is_none() && autoderef.next().is_some() {
-            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
+            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty, idx_expr);
         }
         self.register_predicates(autoderef.into_obligations());
         result
@@ -73,6 +76,7 @@ fn try_index_step(
         base_expr: &hir::Expr<'_>,
         autoderef: &Autoderef<'a, 'tcx>,
         index_ty: Ty<'tcx>,
+        idx_expr: &hir::Expr<'_>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
         let adjusted_ty =
             self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
@@ -82,6 +86,53 @@ fn try_index_step(
             expr, base_expr, adjusted_ty, index_ty
         );
 
+        let negative_index = || {
+            let ty = self.resolve_vars_if_possible(adjusted_ty);
+            let mut err = self.tcx.sess.struct_span_err(
+                idx_expr.span,
+                &format!("negative integers cannot be used to index on a `{}`", ty),
+            );
+            err.span_label(
+                idx_expr.span,
+                &format!("cannot use a negative integer for indexing on `{}`", ty),
+            );
+            if let (hir::ExprKind::Path(..), Ok(snippet)) =
+                (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span))
+            {
+                // `foo[-1]` to `foo[foo.len() - 1]`
+                err.span_suggestion_verbose(
+                    idx_expr.span.shrink_to_lo(),
+                    &format!(
+                        "if you wanted to access an element starting from the end of the `{}`, you \
+                        must compute it",
+                        ty,
+                    ),
+                    format!("{}.len() ", snippet),
+                    Applicability::MachineApplicable,
+                );
+            }
+            err.emit();
+            Some((self.tcx.ty_error(), self.tcx.ty_error()))
+        };
+        if let hir::ExprKind::Unary(
+            hir::UnOp::Neg,
+            hir::Expr {
+                kind: hir::ExprKind::Lit(hir::Lit { node: ast::LitKind::Int(..), .. }),
+                ..
+            },
+        ) = idx_expr.kind
+        {
+            match adjusted_ty.kind() {
+                ty::Adt(ty::AdtDef { did, .. }, _)
+                    if self.tcx.is_diagnostic_item(sym::vec_type, *did) =>
+                {
+                    return negative_index();
+                }
+                ty::Slice(_) | ty::Array(_, _) => return negative_index(),
+                _ => {}
+            }
+        }
+
         for unsize in [false, true] {
             let mut self_ty = adjusted_ty;
             if unsize {
diff --git a/src/test/ui/suggestions/negative-literal-index.fixed b/src/test/ui/suggestions/negative-literal-index.fixed
new file mode 100644 (file)
index 0000000..e52714c
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+    type Output = ();
+
+    fn index(&self, _: i32) -> &() {
+        &()
+    }
+}
+
+fn main() {
+    let x = vec![1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let x = [1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let x = &[1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let _ = x;
+    X[-1];
+}
diff --git a/src/test/ui/suggestions/negative-literal-index.rs b/src/test/ui/suggestions/negative-literal-index.rs
new file mode 100644 (file)
index 0000000..d88b66e
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+    type Output = ();
+
+    fn index(&self, _: i32) -> &() {
+        &()
+    }
+}
+
+fn main() {
+    let x = vec![1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let x = [1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let x = &[1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let _ = x;
+    X[-1];
+}
diff --git a/src/test/ui/suggestions/negative-literal-index.stderr b/src/test/ui/suggestions/negative-literal-index.stderr
new file mode 100644 (file)
index 0000000..f5ea980
--- /dev/null
@@ -0,0 +1,35 @@
+error: negative integers cannot be used to index on a `Vec<{integer}>`
+  --> $DIR/negative-literal-index.rs:15:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `Vec<{integer}>`
+   |
+help: if you wanted to access an element starting from the end of the `Vec<{integer}>`, you must compute it
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+  --> $DIR/negative-literal-index.rs:17:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+   |
+help: if you wanted to access an element starting from the end of the `[{integer}; 3]`, you must compute it
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+  --> $DIR/negative-literal-index.rs:19:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+   |
+help: if you wanted to access an element starting from the end of the `[{integer}; 3]`, you must compute it
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: aborting due to 3 previous errors
+