about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/coercion.rs11
-rw-r--r--src/librustc_typeck/check/demand.rs89
-rw-r--r--src/librustdoc/html/markdown.rs10
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/test.rs35
-rw-r--r--src/libsyntax/ext/expand.rs14
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/auxiliary/issue-41211.rs23
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/issue-41211.rs22
-rw-r--r--src/test/compile-fail/coercion-slice.rs1
-rw-r--r--src/test/compile-fail/cross-borrow-trait.rs2
-rw-r--r--src/test/compile-fail/issue-11374.rs2
-rw-r--r--src/test/compile-fail/issue-13058.rs1
-rw-r--r--src/test/ui/span/coerce-suggestions.rs1
-rw-r--r--src/test/ui/span/coerce-suggestions.stderr14
-rw-r--r--src/test/ui/span/issue-33884.rs27
-rw-r--r--src/test/ui/span/issue-33884.stderr12
16 files changed, 227 insertions, 38 deletions
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index c6a1f6cfc0d..d21b5f739bd 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::<hir::Expr>(&[], 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 e922c7447ff..4cc3f2dacdf 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<String> {
+        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 b02b60531d1..4bf856240f6 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::<Vec<&str>>().join("\n"), filename);
         }
     }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index d5b997001bb..0ca267bb82d 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 fb681b20065..3206b502107 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<testing::TestDescAndFn>,
     // to be removed when hoedown will be definitely gone
-    pub old_tests: Vec<String>,
+    pub old_tests: HashMap<String, Vec<String>>,
     names: Vec<String>,
     cfgs: Vec<String>,
     libs: SearchPaths,
@@ -403,7 +404,7 @@ impl Collector {
                codemap: Option<Rc<CodeMap>>, filename: Option<String>) -> 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<String>,
                     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 680bd7599ac..842398ea02b 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 00000000000..99400bd147c
--- /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 <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.
+
+// 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 00000000000..17237912be4
--- /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 <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.
+
+// 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 6b468ff9662..7c5a4e0c3c6 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 e5afccb9cf3..847a82c0826 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<Trait> = Box::new(Foo);
-    let _y: &Trait = x; //~  ERROR mismatched types
+    let _y: &Trait = x; //~ ERROR E0308
                         //~| expected type `&Trait`
                         //~| found type `std::boxed::Box<Trait>`
 }
diff --git a/src/test/compile-fail/issue-11374.rs b/src/test/compile-fail/issue-11374.rs
index f78786a2889..1e444a6bebf 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 408c6d411de..ed163444149 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<Item=usize>, 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 3177e858ff4..bc3122bf71c 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 6a70b8ff851..220b2f471da 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 00000000000..93aa502ee15
--- /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 <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.
+
+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 00000000000..2a874181c7a
--- /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
+