diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 28caccbc87fde..2c7bdc427ea7c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1624,6 +1624,7 @@ impl [T] { /// let x = s.to_vec(); /// // Here, `s` and `x` can be modified independently. /// ``` + #[rustc_conversion_suggestion] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn to_vec(&self) -> Vec diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index ca493ab27e3ad..8d99d0bc8f4dc 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2034,6 +2034,7 @@ pub trait ToString { /// /// assert_eq!(five, i.to_string()); /// ``` + #[rustc_conversion_suggestion] #[stable(feature = "rust1", since = "1.0.0")] fn to_string(&self) -> String; } diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 7832db7dcb542..169959d12b5d5 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -14,6 +14,7 @@ use super::{FnCtxt, LvalueOp}; use super::method::MethodCallee; use rustc::infer::InferOk; +use rustc::session::DiagnosticMessageId; use rustc::traits; use rustc::ty::{self, Ty, TraitRef}; use rustc::ty::{ToPredicate, TypeFoldable}; @@ -56,19 +57,25 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> { return Some((self.cur_ty, 0)); } - if self.steps.len() == tcx.sess.recursion_limit.get() { + if self.steps.len() >= tcx.sess.recursion_limit.get() { // We've reached the recursion limit, error gracefully. let suggested_limit = tcx.sess.recursion_limit.get() * 2; - struct_span_err!(tcx.sess, - self.span, - E0055, - "reached the recursion limit while auto-dereferencing {:?}", - self.cur_ty) - .span_label(self.span, "deref recursion limit reached") - .help(&format!( - "consider adding a `#[recursion_limit=\"{}\"]` attribute to your crate", + let msg = format!("reached the recursion limit while auto-dereferencing {:?}", + self.cur_ty); + let error_id = (DiagnosticMessageId::ErrorId(55), Some(self.span), msg.clone()); + let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + struct_span_err!(tcx.sess, + self.span, + E0055, + "reached the recursion limit while auto-dereferencing {:?}", + self.cur_ty) + .span_label(self.span, "deref recursion limit reached") + .help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", suggested_limit)) - .emit(); + .emit(); + } return None; } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 45ddae5c644f4..6703bbba86b1c 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::iter; use check::FnCtxt; use rustc::infer::InferOk; @@ -137,49 +138,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Some((msg, suggestion)) = self.check_ref(expr, checked_ty, expected) { err.span_suggestion(expr.span, msg, suggestion); } else { - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); + let methods = self.get_conversion_methods(expected, checked_ty); + if let Ok(expr_text) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + let suggestions = iter::repeat(expr_text).zip(methods.iter()) + .map(|(receiver, method)| format!("{}.{}()", receiver, method.name)) + .collect::>(); + if !suggestions.is_empty() { + err.span_suggestions(expr.span, + "try using a conversion method", + suggestions); + } } } (expected, Some(err)) } - fn format_method_suggestion(&self, method: &AssociatedItem) -> String { - format!("- .{}({})", - method.name, - if self.has_no_input_arg(method) { - "" - } else { - "..." - }) - } - - fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec { - methods.iter() - .take(5) - .map(|method| self.format_method_suggestion(&*method)) - .collect::>() - } + fn get_conversion_methods(&self, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) + -> Vec { + let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP, + probe::Mode::MethodCall, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + methods.retain(|m| { + self.has_no_input_arg(m) && + self.tcx.get_attrs(m.def_id).iter() + // This special internal attribute is used to whitelist + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + .find(|a| a.check_name("rustc_conversion_suggestion")).is_some() + }); - fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec { - let no_argument_methods: Vec<_> = - methods.iter() - .filter(|ref x| self.has_no_input_arg(&*x)) - .map(|x| x.clone()) - .collect(); - if no_argument_methods.len() > 0 { - self.display_suggested_methods(&no_argument_methods) - } else { - self.display_suggested_methods(&methods) - } + methods } // This function checks if the method isn't static and takes other arguments than `self`. diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 6f83ecab01a1c..c88bbd03af82b 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { scope_expr_id); let method_names = self.probe_op(span, mode, None, Some(return_type), IsSuggestion(true), - self_ty, scope_expr_id, ProbeScope::TraitsInScope, + self_ty, scope_expr_id, ProbeScope::AllTraits, |probe_cx| Ok(probe_cx.candidate_method_names())) .unwrap_or(vec![]); method_names @@ -199,7 +199,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.probe_op( span, mode, Some(method_name), Some(return_type), IsSuggestion(true), self_ty, scope_expr_id, - ProbeScope::TraitsInScope, |probe_cx| probe_cx.pick() + ProbeScope::AllTraits, |probe_cx| probe_cx.pick() ).ok().map(|pick| pick.item) }) .collect() diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3b79e0c4f8267..28040bc20e2e0 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -229,7 +229,7 @@ // Turn warnings into errors, but only after stage0, where it can be useful for // code to emit warnings during language transitions -#![deny(warnings)] +#![cfg_attr(not(stage0), deny(warnings))] // std may use features in a platform-specific way #![allow(unused_features)] diff --git a/src/libstd/path.rs b/src/libstd/path.rs index e8297c20af34e..7631a9a44bbe7 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1709,6 +1709,7 @@ impl Path { /// let path_buf = Path::new("foo.txt").to_path_buf(); /// assert_eq!(path_buf, std::path::PathBuf::from("foo.txt")); /// ``` + #[rustc_conversion_suggestion] #[stable(feature = "rust1", since = "1.0.0")] pub fn to_path_buf(&self) -> PathBuf { PathBuf::from(self.inner.to_os_string()) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 08eec0f9117f8..8397f666eeea5 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -969,6 +969,13 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG never be stable", cfg_fn!(rustc_attrs))), + // whitelists "identity-like" conversion methods to suggest on type mismatch + ("rustc_conversion_suggestion", Whitelisted, Gated(Stability::Unstable, + "rustc_attrs", + "this is an internal attribute that will \ + never be stable", + cfg_fn!(rustc_attrs))), + ("wasm_import_memory", Whitelisted, Gated(Stability::Unstable, "wasm_import_memory", "wasm_import_memory attribute is currently unstable", diff --git a/src/test/ui/deref-suggestion.stderr b/src/test/ui/deref-suggestion.stderr index 98ec3d9693fb8..4c2896e220735 100644 --- a/src/test/ui/deref-suggestion.stderr +++ b/src/test/ui/deref-suggestion.stderr @@ -2,16 +2,13 @@ error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:18:9 | 18 | foo(s); //~ ERROR mismatched types - | ^ expected struct `std::string::String`, found reference + | ^ + | | + | expected struct `std::string::String`, found reference + | help: try using a conversion method: `s.to_string()` | = note: expected type `std::string::String` found type `&std::string::String` - = help: here are some functions which might fulfill your needs: - - .escape_debug() - - .escape_default() - - .escape_unicode() - - .to_ascii_lowercase() - - .to_ascii_uppercase() error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:23:10 diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr index 951b0b1058060..860c6bb5b909f 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -4,11 +4,11 @@ error[E0055]: reached the recursion limit while auto-dereferencing I 62 | let x: &Bottom = &t; //~ ERROR mismatched types | ^^ deref recursion limit reached | - = help: consider adding a `#[recursion_limit="20"]` attribute to your crate + = help: consider adding a `#![recursion_limit="20"]` attribute to your crate error[E0055]: reached the recursion limit while auto-dereferencing I | - = help: consider adding a `#[recursion_limit="20"]` attribute to your crate + = help: consider adding a `#![recursion_limit="20"]` attribute to your crate error[E0308]: mismatched types --> $DIR/recursion_limit_deref.rs:62:22 diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 078526197656c..06f0e6ec228f2 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -6,9 +6,6 @@ error[E0308]: mismatched types | = note: expected type `usize` found type `std::string::String` - = help: here are some functions which might fulfill your needs: - - .capacity() - - .len() error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:19:19 @@ -44,7 +41,10 @@ error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:27:9 | 27 | f = box f; - | ^^^^^ cyclic type of infinite size + | ^^^^^ + | | + | cyclic type of infinite size + | help: try using a conversion method: `box f.to_string()` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:31:9 diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr index 3794d6ba2ded9..18860a7456eac 100644 --- a/src/test/ui/span/issue-34264.stderr +++ b/src/test/ui/span/issue-34264.stderr @@ -33,8 +33,6 @@ error[E0308]: mismatched types | = note: expected type `usize` found type `&'static str` - = help: here are some functions which might fulfill your needs: - - .len() error[E0061]: this function takes 2 parameters but 3 parameters were supplied --> $DIR/issue-34264.rs:20:5 diff --git a/src/test/ui/suggestions/conversion-methods.rs b/src/test/ui/suggestions/conversion-methods.rs new file mode 100644 index 0000000000000..8a53bc3ca9385 --- /dev/null +++ b/src/test/ui/suggestions/conversion-methods.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::path::{Path, PathBuf}; + + +fn main() { + let _tis_an_instants_play: String = "'Tis a fond Ambush—"; //~ ERROR mismatched types + let _just_to_make_bliss: PathBuf = Path::new("/ern/her/own/surprise"); + //~^ ERROR mismatched types + + let _but_should_the_play: String = 2; // Perhaps surprisingly, we suggest .to_string() here + //~^ ERROR mismatched types + + let _prove_piercing_earnest: Vec = &[1, 2, 3]; //~ ERROR mismatched types +} diff --git a/src/test/ui/suggestions/conversion-methods.stderr b/src/test/ui/suggestions/conversion-methods.stderr new file mode 100644 index 0000000000000..96fdc29d952cb --- /dev/null +++ b/src/test/ui/suggestions/conversion-methods.stderr @@ -0,0 +1,50 @@ +error[E0308]: mismatched types + --> $DIR/conversion-methods.rs:15:41 + | +15 | let _tis_an_instants_play: String = "'Tis a fond Ambush—"; //~ ERROR mismatched types + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | expected struct `std::string::String`, found reference + | help: try using a conversion method: `"'Tis a fond Ambush—".to_string()` + | + = note: expected type `std::string::String` + found type `&'static str` + +error[E0308]: mismatched types + --> $DIR/conversion-methods.rs:16:40 + | +16 | let _just_to_make_bliss: PathBuf = Path::new("/ern/her/own/surprise"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected struct `std::path::PathBuf`, found reference + | help: try using a conversion method: `Path::new("/ern/her/own/surprise").to_path_buf()` + | + = note: expected type `std::path::PathBuf` + found type `&std::path::Path` + +error[E0308]: mismatched types + --> $DIR/conversion-methods.rs:19:40 + | +19 | let _but_should_the_play: String = 2; // Perhaps surprisingly, we suggest .to_string() here + | ^ + | | + | expected struct `std::string::String`, found integral variable + | help: try using a conversion method: `2.to_string()` + | + = note: expected type `std::string::String` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/conversion-methods.rs:22:47 + | +22 | let _prove_piercing_earnest: Vec = &[1, 2, 3]; //~ ERROR mismatched types + | ^^^^^^^^^^ + | | + | expected struct `std::vec::Vec`, found reference + | help: try using a conversion method: `&[1, 2, 3].to_vec()` + | + = note: expected type `std::vec::Vec` + found type `&[{integer}; 3]` + +error: aborting due to 4 previous errors +