Be more obvious when suggesting dereference

Include enclosing span when suggesting dereference on a span that is
already a reference:

```
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
```
This commit is contained in:
Esteban Küber 2017-11-12 14:18:47 -08:00
parent 71da1c21eb
commit 7c2526a9d7
4 changed files with 67 additions and 10 deletions

View File

@ -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",
);
if let Ok(snippet) = tcx.sess.codemap().span_to_snippet(pat.span) {
err.span_suggestion(pat.span, "consider using", format!("&{}", &snippet));
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) {
// 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();
}
}
}

View File

@ -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 }

View File

@ -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();
}

View File

@ -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