about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/_match.rs35
-rw-r--r--src/libsyntax_pos/lib.rs12
-rw-r--r--src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs18
-rw-r--r--src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr10
4 files changed, 66 insertions, 9 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index ea0fa945c37..fbfc97d33c5 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -23,7 +23,9 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::cmp;
 use syntax::ast;
 use syntax::codemap::Spanned;
+use syntax::errors::DiagnosticBuilder;
 use syntax::feature_gate;
+use syntax::parse::ParseSess;
 use syntax::ptr::P;
 use syntax_pos::Span;
 
@@ -120,17 +122,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         .pat_adjustments_mut()
                         .insert(pat.hir_id, pat_adjustments);
                 } else {
-                    let mut err = feature_gate::feature_err(
-                        &tcx.sess.parse_sess,
-                        "match_default_bindings",
-                        pat.span,
-                        feature_gate::GateIssue::Language,
-                        "non-reference pattern used to match a reference",
-                    );
+                    fn feature_err<'a>(sp: Span, sess: &'a ParseSess) -> DiagnosticBuilder<'a> {
+                        feature_gate::feature_err(
+                            sess,
+                            "match_default_bindings",
+                            sp,
+                            feature_gate::GateIssue::Language,
+                            "non-reference pattern used to match a reference",
+                        )
+                    }
                     if let Ok(snippet) = tcx.sess.codemap().span_to_snippet(pat.span) {
-                        err.span_suggestion(pat.span, "consider using", format!("&{}", &snippet));
+                        // The following is a bit of a hack. We probably should check the AST for
+                        // this instead, but this should be good enough for the expected cases.
+                        let prev_span = pat.span.prev_point();
+                        let (sp, sugg) = match tcx.sess.codemap().span_to_snippet(prev_span) {
+                            // Make the suggestion more obvious when having `&(_, _)`
+                            Ok(ref prev) if &*prev == "&" => {
+                                (prev_span.to(pat.span), format!("&&{}", &snippet)),
+                            }
+                            _ => (pat.span, format!("&{}", &snippet)),
+                        };
+                        let mut err = feature_err(sp, &tcx.sess.parse_sess);
+                        err.span_suggestion(sp, "consider using a reference", sugg);
+                        err.emit();
+                    } else {
+                        feature_err(pat.span, &tcx.sess.parse_sess).emit();
                     }
-                    err.emit();
                 }
             }
         }
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 47755dc1d54..40a7917613f 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -159,6 +159,18 @@ impl Span {
         Span::new(BytePos(lo), BytePos(lo), span.ctxt)
     }
 
+    /// Returns a new span representing the previous character after the start-point of this span
+    pub fn prev_point(self) -> Span {
+        let span = self.data();
+        let span_lo = span.lo.0;
+        let lo = if span_lo == 0 {
+            0
+        } else {
+            span_lo - 1
+        };
+        Span::new(BytePos(lo), BytePos(span_lo), span.ctxt)
+    }
+
     /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
     pub fn substitute_dummy(self, other: Span) -> Span {
         if self.source_equal(&DUMMY_SP) { other } else { self }
diff --git a/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs
new file mode 100644
index 00000000000..2a9ea464416
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo(s: &str) -> bool { true }
+
+fn main() {
+    let x = vec![(String::new(), String::new())];
+    x.iter()
+        .filter(|&(ref a, _)| foo(a))
+        .collect();
+}
diff --git a/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr
new file mode 100644
index 00000000000..fc1eb335d05
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-dereference-on-arg.stderr
@@ -0,0 +1,10 @@
+error: non-reference pattern used to match a reference (see issue #42640)
+  --> dont-suggest-dereference-on-arg.rs:16:19
+   |
+16 |         .filter(|&(ref a, _)| foo(a))
+   |                  ^^^^^^^^^^^ help: consider using: `&&(ref k, _)`
+   |
+   = help: add #![feature(match_default_bindings)] to the crate attributes to enable
+
+error: aborting due to previous error
+