diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c6a1f6cfc0d7f..d21b5f739bd7b 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -78,6 +78,7 @@ use errors::DiagnosticBuilder; use syntax::abi; use syntax::feature_gate; use syntax::ptr::P; +use syntax_pos; use std::collections::VecDeque; use std::ops::Deref; @@ -722,6 +723,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(target) } + /// Same as `try_coerce()`, but without side-effects. + pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { + let source = self.resolve_type_vars_with_obligations(expr_ty); + debug!("coercion::can({:?} -> {:?})", source, target); + + let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let coerce = Coerce::new(self, cause); + self.probe(|_| coerce.coerce::(&[], source, target)).is_ok() + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index e922c7447ff85..4cc3f2dacdfe9 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -10,15 +10,14 @@ use check::FnCtxt; -use rustc::ty::Ty; -use rustc::infer::{InferOk}; +use rustc::infer::InferOk; use rustc::traits::ObligationCause; use syntax::ast; use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; -use rustc::ty::{self, AssociatedItem}; +use rustc::ty::{self, Ty, AssociatedItem}; use errors::DiagnosticBuilder; use super::method::probe; @@ -80,18 +79,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) { let cause = self.misc(expr.span); let expr_ty = self.resolve_type_vars_with_obligations(checked_ty); - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - }; + if let Some(suggestion) = self.check_ref(expr, + checked_ty, + expected) { + err.help(&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"))); + } + } err.emit(); } } @@ -140,4 +145,60 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => false, } } + + /// This function is used to determine potential "simple" improvements or users' errors and + /// provide them useful help. For example: + /// + /// ``` + /// fn some_fn(s: &str) {} + /// + /// let x = "hey!".to_owned(); + /// some_fn(x); // error + /// ``` + /// + /// No need to find every potential function which could make a coercion to transform a + /// `String` into a `&str` since a `&` would do the trick! + /// + /// In addition of this check, it also checks between references mutability state. If the + /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with + /// `&mut`!". + fn check_ref(&self, + expr: &hir::Expr, + checked_ty: Ty<'tcx>, + expected: Ty<'tcx>) + -> Option { + match (&expected.sty, &checked_ty.sty) { + (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None, + (&ty::TyRef(_, mutability), _) => { + // Check if it can work when put into a ref. For example: + // + // ``` + // fn bar(x: &mut i32) {} + // + // let x = 0u32; + // bar(&x); // error, expected &mut + // ``` + let ref_ty = match mutability.mutbl { + hir::Mutability::MutMutable => self.tcx.mk_mut_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + hir::Mutability::MutImmutable => self.tcx.mk_imm_ref( + self.tcx.mk_region(ty::ReStatic), + checked_ty), + }; + if self.can_coerce(ref_ty, expected) { + if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { + return Some(format!("try with `{}{}`", + match mutability.mutbl { + hir::Mutability::MutMutable => "&mut ", + hir::Mutability::MutImmutable => "&", + }, + &src)); + } + } + None + } + _ => None, + } + } } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b02b60531d108..4bf856240f66a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -492,7 +492,7 @@ pub fn old_find_testable_code(doc: &str, tests: &mut ::test::Collector, position text: *const hoedown_buffer, lang: *const hoedown_buffer, data: *const hoedown_renderer_data, - line: libc::size_t) { + _line: libc::size_t) { unsafe { if text.is_null() { return } let block_info = if lang.is_null() { @@ -503,11 +503,15 @@ pub fn old_find_testable_code(doc: &str, tests: &mut ::test::Collector, position LangString::parse(s) }; if !block_info.rust { return } + let text = (*text).as_bytes(); let opaque = (*data).opaque as *mut hoedown_html_renderer_state; let tests = &mut *((*opaque).opaque as *mut ::test::Collector); - let line = tests.get_line() + line; + let text = str::from_utf8(text).unwrap(); + let lines = text.lines().map(|l| { + stripped_filtered_line(l).unwrap_or(l) + }); let filename = tests.get_filename(); - tests.add_old_test(line, filename); + tests.add_old_test(lines.collect::>().join("\n"), filename); } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d5b997001bb9d..0ca267bb82d2e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -27,6 +27,7 @@ #![feature(staged_api)] #![feature(test)] #![feature(unicode)] +#![feature(vec_remove_item)] extern crate arena; extern crate getopts; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index fb681b20065fa..3206b5021075d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::io::prelude::*; @@ -381,7 +382,7 @@ fn partition_source(s: &str) -> (String, String) { pub struct Collector { pub tests: Vec, // to be removed when hoedown will be definitely gone - pub old_tests: Vec, + pub old_tests: HashMap>, names: Vec, cfgs: Vec, libs: SearchPaths, @@ -403,7 +404,7 @@ impl Collector { codemap: Option>, filename: Option) -> Collector { Collector { tests: Vec::new(), - old_tests: Vec::new(), + old_tests: HashMap::new(), names: Vec::new(), cfgs: cfgs, libs: libs, @@ -432,9 +433,24 @@ impl Collector { } } - pub fn add_old_test(&mut self, line: usize, filename: String) { - let name = self.generate_name(line, &filename); - self.old_tests.push(name); + // to be removed once hoedown is gone + fn generate_name_beginning(&self, filename: &str) -> String { + if self.use_headers { + if let Some(ref header) = self.current_header { + format!("{} - {} (line", filename, header) + } else { + format!("{} - (line", filename) + } + } else { + format!("{} - {} (line", filename, self.names.join("::")) + } + } + + pub fn add_old_test(&mut self, test: String, filename: String) { + let name_beg = self.generate_name_beginning(&filename); + let entry = self.old_tests.entry(name_beg) + .or_insert(Vec::new()); + entry.push(test.trim().to_owned()); } pub fn add_test(&mut self, test: String, @@ -442,7 +458,14 @@ impl Collector { as_test_harness: bool, compile_fail: bool, error_codes: Vec, line: usize, filename: String) { let name = self.generate_name(line, &filename); - if self.old_tests.iter().find(|&x| x == &name).is_none() { + let name_beg = self.generate_name_beginning(&filename); + let mut found = false; + // to be removed when hoedown is removed + let test = test.trim().to_owned(); + if let Some(entry) = self.old_tests.get_mut(&name_beg) { + found = entry.remove_item(&test).is_some(); + } + if !found { let _ = writeln!(&mut io::stderr(), "WARNING: {} Code block is not currently run as a test, but will in \ future versions of rustdoc. Please ensure this code block is a \ diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 680bd7599ace0..842398ea02b9e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -205,6 +205,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { module.directory.pop(); self.cx.current_expansion.module = Rc::new(module); + let orig_mod_span = krate.module.inner; + let krate_item = Expansion::Items(SmallVector::one(P(ast::Item { attrs: krate.attrs, span: krate.span, @@ -214,11 +216,19 @@ impl<'a, 'b> MacroExpander<'a, 'b> { vis: ast::Visibility::Public, }))); - match self.expand(krate_item).make_items().pop().unwrap().unwrap() { - ast::Item { attrs, node: ast::ItemKind::Mod(module), .. } => { + match self.expand(krate_item).make_items().pop().map(P::unwrap) { + Some(ast::Item { attrs, node: ast::ItemKind::Mod(module), .. }) => { krate.attrs = attrs; krate.module = module; }, + None => { + // Resolution failed so we return an empty expansion + krate.attrs = vec![]; + krate.module = ast::Mod { + inner: orig_mod_span, + items: vec![], + }; + }, _ => unreachable!(), }; diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs new file mode 100644 index 0000000000000..99400bd147c20 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.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. + +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro)] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn emit_unchanged(_args: TokenStream, input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs b/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs new file mode 100644 index 0000000000000..17237912be49b --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs @@ -0,0 +1,22 @@ +// 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. + +// aux-build:issue-41211.rs + +// FIXME: https://github.com/rust-lang/rust/issues/41430 +// This is a temporary regression test for the ICE reported in #41211 + +#![feature(proc_macro)] +#![emit_unchanged] +//~^ ERROR: cannot find attribute macro `emit_unchanged` in this scope +extern crate issue_41211; +use issue_41211::emit_unchanged; + +fn main() {} diff --git a/src/test/compile-fail/coercion-slice.rs b/src/test/compile-fail/coercion-slice.rs index 6b468ff96620d..7c5a4e0c3c6f6 100644 --- a/src/test/compile-fail/coercion-slice.rs +++ b/src/test/compile-fail/coercion-slice.rs @@ -14,6 +14,5 @@ fn main() { let _: &[i32] = [0]; //~^ ERROR mismatched types //~| expected type `&[i32]` - //~| found type `[{integer}; 1]` //~| expected &[i32], found array of 1 elements } diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index e5afccb9cf394..847a82c082651 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -17,7 +17,7 @@ impl Trait for Foo {} pub fn main() { let x: Box = Box::new(Foo); - let _y: &Trait = x; //~ ERROR mismatched types + let _y: &Trait = x; //~ ERROR E0308 //~| expected type `&Trait` //~| found type `std::boxed::Box` } diff --git a/src/test/compile-fail/issue-11374.rs b/src/test/compile-fail/issue-11374.rs index f78786a2889da..1e444a6bebf9b 100644 --- a/src/test/compile-fail/issue-11374.rs +++ b/src/test/compile-fail/issue-11374.rs @@ -33,5 +33,5 @@ pub fn for_stdin<'a>() -> Container<'a> { fn main() { let mut c = for_stdin(); let mut v = Vec::new(); - c.read_to(v); //~ ERROR mismatched types + c.read_to(v); //~ ERROR E0308 } diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index 408c6d411de90..ed1634441498b 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -35,4 +35,5 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types +//~| HELP try with `&(3, 5)` } diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs index 3177e858ff4fd..bc3122bf71c0e 100644 --- a/src/test/ui/span/coerce-suggestions.rs +++ b/src/test/ui/span/coerce-suggestions.rs @@ -32,7 +32,6 @@ fn main() { //~| NOTE types differ in mutability //~| NOTE expected type `&mut std::string::String` //~| NOTE found type `&std::string::String` - //~| HELP try with `&mut y` test2(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 6a70b8ff851d7..220b2f471da9a 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -18,11 +18,7 @@ error[E0308]: mismatched types | = note: expected type `&str` found type `std::string::String` - = help: here are some functions which might fulfill your needs: - - .as_str() - - .trim() - - .trim_left() - - .trim_right() + = help: try with `&String::new()` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:30:10 @@ -34,18 +30,18 @@ error[E0308]: mismatched types found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:36:11 + --> $DIR/coerce-suggestions.rs:35:11 | -36 | test2(&y); +35 | test2(&y); | ^^ types differ in mutability | = note: expected type `&mut i32` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:42:9 + --> $DIR/coerce-suggestions.rs:41:9 | -42 | f = box f; +41 | f = box f; | ^^^^^ cyclic type of infinite size | = note: expected type `_` diff --git a/src/test/ui/span/issue-33884.rs b/src/test/ui/span/issue-33884.rs new file mode 100644 index 0000000000000..93aa502ee1531 --- /dev/null +++ b/src/test/ui/span/issue-33884.rs @@ -0,0 +1,27 @@ +// 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::net::TcpListener; +use std::net::TcpStream; +use std::io::{self, Read, Write}; + +fn handle_client(stream: TcpStream) -> io::Result<()> { + stream.write_fmt(format!("message received")) +} + +fn main() { + if let Ok(listener) = TcpListener::bind("127.0.0.1:8080") { + for incoming in listener.incoming() { + if let Ok(stream) = incoming { + handle_client(stream); + } + } + } +} diff --git a/src/test/ui/span/issue-33884.stderr b/src/test/ui/span/issue-33884.stderr new file mode 100644 index 0000000000000..2a874181c7ad9 --- /dev/null +++ b/src/test/ui/span/issue-33884.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/issue-33884.rs:16:22 + | +16 | stream.write_fmt(format!("message received")) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::fmt::Arguments`, found struct `std::string::String` + | + = note: expected type `std::fmt::Arguments<'_>` + found type `std::string::String` + = note: this error originates in a macro outside of the current crate + +error: aborting due to previous error +