1
1
use crate :: check:: method:: MethodCallee ;
2
2
use crate :: check:: { has_expected_num_generic_args, FnCtxt , PlaceOp } ;
3
+ use rustc_ast as ast;
4
+ use rustc_errors:: Applicability ;
3
5
use rustc_hir as hir;
4
6
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
5
7
use rustc_infer:: infer:: InferOk ;
@@ -47,6 +49,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
47
49
expr : & hir:: Expr < ' _ > ,
48
50
base_expr : & ' tcx hir:: Expr < ' tcx > ,
49
51
base_ty : Ty < ' tcx > ,
52
+ idx_expr : & ' tcx hir:: Expr < ' tcx > ,
50
53
idx_ty : Ty < ' tcx > ,
51
54
) -> Option < ( /*index type*/ Ty < ' tcx > , /*element type*/ Ty < ' tcx > ) > {
52
55
// FIXME(#18741) -- this is almost but not quite the same as the
@@ -56,7 +59,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
56
59
let mut autoderef = self . autoderef ( base_expr. span , base_ty) ;
57
60
let mut result = None ;
58
61
while result. is_none ( ) && autoderef. next ( ) . is_some ( ) {
59
- result = self . try_index_step ( expr, base_expr, & autoderef, idx_ty) ;
62
+ result = self . try_index_step ( expr, base_expr, & autoderef, idx_ty, idx_expr ) ;
60
63
}
61
64
self . register_predicates ( autoderef. into_obligations ( ) ) ;
62
65
result
@@ -73,6 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
73
76
base_expr : & hir:: Expr < ' _ > ,
74
77
autoderef : & Autoderef < ' a , ' tcx > ,
75
78
index_ty : Ty < ' tcx > ,
79
+ idx_expr : & hir:: Expr < ' _ > ,
76
80
) -> Option < ( /*index type*/ Ty < ' tcx > , /*element type*/ Ty < ' tcx > ) > {
77
81
let adjusted_ty =
78
82
self . structurally_resolved_type ( autoderef. span ( ) , autoderef. final_ty ( false ) ) ;
@@ -82,6 +86,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
82
86
expr, base_expr, adjusted_ty, index_ty
83
87
) ;
84
88
89
+ let negative_index = || {
90
+ let ty = self . resolve_vars_if_possible ( adjusted_ty) ;
91
+ let mut err = self . tcx . sess . struct_span_err (
92
+ idx_expr. span ,
93
+ & format ! ( "negative integers cannot be used to index on a `{}`" , ty) ,
94
+ ) ;
95
+ err. span_label (
96
+ idx_expr. span ,
97
+ & format ! ( "cannot use a negative integer for indexing on `{}`" , ty) ,
98
+ ) ;
99
+ if let ( hir:: ExprKind :: Path ( ..) , Ok ( snippet) ) =
100
+ ( & base_expr. kind , self . tcx . sess . source_map ( ) . span_to_snippet ( base_expr. span ) )
101
+ {
102
+ // `foo[-1]` to `foo[foo.len() - 1]`
103
+ err. span_suggestion_verbose (
104
+ idx_expr. span . shrink_to_lo ( ) ,
105
+ & format ! (
106
+ "if you wanted to access an element starting from the end of the `{}`, you \
107
+ must compute it",
108
+ ty,
109
+ ) ,
110
+ format ! ( "{}.len() " , snippet) ,
111
+ Applicability :: MachineApplicable ,
112
+ ) ;
113
+ }
114
+ err. emit ( ) ;
115
+ Some ( ( self . tcx . ty_error ( ) , self . tcx . ty_error ( ) ) )
116
+ } ;
117
+ if let hir:: ExprKind :: Unary (
118
+ hir:: UnOp :: Neg ,
119
+ hir:: Expr {
120
+ kind : hir:: ExprKind :: Lit ( hir:: Lit { node : ast:: LitKind :: Int ( ..) , .. } ) ,
121
+ ..
122
+ } ,
123
+ ) = idx_expr. kind
124
+ {
125
+ match adjusted_ty. kind ( ) {
126
+ ty:: Adt ( ty:: AdtDef { did, .. } , _)
127
+ if self . tcx . is_diagnostic_item ( sym:: vec_type, * did) =>
128
+ {
129
+ return negative_index ( ) ;
130
+ }
131
+ ty:: Slice ( _) | ty:: Array ( _, _) => return negative_index ( ) ,
132
+ _ => { }
133
+ }
134
+ }
135
+
85
136
for unsize in [ false , true ] {
86
137
let mut self_ty = adjusted_ty;
87
138
if unsize {
0 commit comments