From 09d9924713a119692b8b195b81bd57ee7821cb71 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 20 Jan 2015 10:58:06 +0100 Subject: [PATCH] librustc: hint close matches on accessing nonexisting fields --- src/librustc_typeck/check/mod.rs | 42 ++++++++++++++++++- .../struct-fields-hints-no-dupe.rs | 24 +++++++++++ src/test/compile-fail/struct-fields-hints.rs | 23 ++++++++++ src/test/compile-fail/struct-fields-typo.rs | 24 +++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/struct-fields-hints-no-dupe.rs create mode 100644 src/test/compile-fail/struct-fields-hints.rs create mode 100644 src/test/compile-fail/struct-fields-typo.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f610456f73c..df9d54ccd8a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -108,6 +108,7 @@ use lint; use util::common::{block_query, indenter, loop_query}; use util::ppaux::{self, Repr}; use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; +use util::lev_distance::lev_distance; use std::cell::{Cell, Ref, RefCell}; use std::mem::replace; @@ -3074,11 +3075,43 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, actual) }, expr_t, None); + if let Some(t) = ty::ty_to_def_id(expr_t) { + suggest_field_names(t, field, tcx, vec![]); + } } fcx.write_error(expr.id); } + // displays hints about the closest matches in field names + fn suggest_field_names<'tcx>(id : DefId, + field : &ast::SpannedIdent, + tcx : &ty::ctxt<'tcx>, + skip : Vec<&str>) { + let ident = token::get_ident(field.node); + let name = ident.get(); + // only find fits with at least one matching letter + let mut best_dist = name.len(); + let mut best = None; + let fields = ty::lookup_struct_fields(tcx, id); + for elem in fields.iter() { + let n = elem.name.as_str(); + // ignore already set fields + if skip.iter().any(|&x| x == n) { + continue; + } + let dist = lev_distance(n, name); + if dist < best_dist { + best = Some(n); + best_dist = dist; + } + } + if let Some(n) = best { + tcx.sess.span_help(field.span, + format!("did you mean `{}`?", n).as_slice()); + } + } + // Check tuple index expressions fn check_tup_field(fcx: &FnCtxt, expr: &ast::Expr, @@ -3186,6 +3219,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, }, struct_ty, None); + // prevent all specified fields from being suggested + let skip_fields = ast_fields.iter().map(|ref x| x.ident.node.name.as_str()); + let actual_id = match enum_id_opt { + Some(_) => class_id, + None => ty::ty_to_def_id(struct_ty).unwrap() + }; + suggest_field_names(actual_id, &field.ident, tcx, skip_fields.collect()); error_happened = true; } Some((_, true)) => { diff --git a/src/test/compile-fail/struct-fields-hints-no-dupe.rs b/src/test/compile-fail/struct-fields-hints-no-dupe.rs new file mode 100644 index 00000000000..8df9ffd6cc7 --- /dev/null +++ b/src/test/compile-fail/struct-fields-hints-no-dupe.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { + foo : i32, + car : i32, + barr : i32 +} + +fn main() { + let a = A { + foo : 5, + bar : 42,//~ ERROR structure `A` has no field named `bar` + //~^ HELP did you mean `barr`? + car : 9, + }; +} diff --git a/src/test/compile-fail/struct-fields-hints.rs b/src/test/compile-fail/struct-fields-hints.rs new file mode 100644 index 00000000000..37001f1e60a --- /dev/null +++ b/src/test/compile-fail/struct-fields-hints.rs @@ -0,0 +1,23 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { + foo : i32, + car : i32, + barr : i32 +} + +fn main() { + let a = A { + foo : 5, + bar : 42,//~ ERROR structure `A` has no field named `bar` + //~^ HELP did you mean `car`? + }; +} diff --git a/src/test/compile-fail/struct-fields-typo.rs b/src/test/compile-fail/struct-fields-typo.rs new file mode 100644 index 00000000000..c897dc55204 --- /dev/null +++ b/src/test/compile-fail/struct-fields-typo.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct BuildData { + foo: isize, + bar: f32 +} + +fn main() { + let foo = BuildData { + foo: 0, + bar: 0.5, + }; + let x = foo.baa;//~ ERROR attempted access of field `baa` on type `BuildData` + //~^ HELP did you mean `bar`? + println!("{}", x); +}