about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2017-06-20 15:53:03 +0800
committerkennytm <kennytm@gmail.com>2017-06-23 15:31:53 +0800
commit2c8916581472f5f9892c306632bb2b3d8bc96bc4 (patch)
treec1aadcc1c69e59c835788afbdddb259bf8b2733f
parent4711982314ba33cab83704c26484b501c8652774 (diff)
downloadrust-2c8916581472f5f9892c306632bb2b3d8bc96bc4.tar.gz
rust-2c8916581472f5f9892c306632bb2b3d8bc96bc4.zip
Modify --explain to handle hidden code (`# ...`) and indented code blocks.
-rw-r--r--src/librustc_driver/lib.rs19
-rw-r--r--src/test/ui/explain.rs11
-rw-r--r--src/test/ui/explain.stdout63
3 files changed, 87 insertions, 6 deletions
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index b4a4aaaaf5c..54e7c398fe6 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -355,14 +355,21 @@ fn handle_explain(code: &str,
     };
     match descriptions.find_description(&normalised) {
         Some(ref description) => {
+            let mut is_in_code_block = false;
             // Slice off the leading newline and print.
-            print!("{}", &(&description[1..]).split("\n").map(|x| {
-                format!("{}\n", if x.starts_with("```") {
-                    "```"
+            for line in description[1..].lines() {
+                let indent_level = line.find(|c: char| !c.is_whitespace())
+                    .unwrap_or_else(|| line.len());
+                let dedented_line = &line[indent_level..];
+                if dedented_line.starts_with("```") {
+                    is_in_code_block = !is_in_code_block;
+                    println!("{}", &line[..(indent_level+3)]);
+                } else if is_in_code_block && dedented_line.starts_with("# ") {
+                    continue;
                 } else {
-                    x
-                })
-            }).collect::<String>());
+                    println!("{}", line);
+                }
+            }
         }
         None => {
             early_error(output, &format!("no extended information for {}", code));
diff --git a/src/test/ui/explain.rs b/src/test/ui/explain.rs
new file mode 100644
index 00000000000..17fb59935dd
--- /dev/null
+++ b/src/test/ui/explain.rs
@@ -0,0 +1,11 @@
+// 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.
+
+// compile-flags: --explain E0591
diff --git a/src/test/ui/explain.stdout b/src/test/ui/explain.stdout
new file mode 100644
index 00000000000..0bbbd95320a
--- /dev/null
+++ b/src/test/ui/explain.stdout
@@ -0,0 +1,63 @@
+Per [RFC 401][rfc401], if you have a function declaration `foo`:
+
+```
+// For the purposes of this explanation, all of these
+// different kinds of `fn` declarations are equivalent:
+struct S;
+fn foo(x: S) { /* ... */ }
+extern "C" { fn foo(x: S); }
+impl S { fn foo(self) { /* ... */ } }
+```
+
+the type of `foo` is **not** `fn(S)`, as one might expect.
+Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`.
+However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`,
+so you rarely notice this:
+
+```
+let x: fn(S) = foo; // OK, coerces
+```
+
+The reason that this matter is that the type `fn(S)` is not specific to
+any particular function: it's a function _pointer_. So calling `x()` results
+in a virtual call, whereas `foo()` is statically dispatched, because the type
+of `foo` tells us precisely what function is being called.
+
+As noted above, coercions mean that most code doesn't have to be
+concerned with this distinction. However, you can tell the difference
+when using **transmute** to convert a fn item into a fn pointer.
+
+This is sometimes done as part of an FFI:
+
+```
+extern "C" fn foo(userdata: Box<i32>) {
+    /* ... */
+}
+
+let f: extern "C" fn(*mut i32) = transmute(foo);
+callback(f);
+```
+
+Here, transmute is being used to convert the types of the fn arguments.
+This pattern is incorrect because, because the type of `foo` is a function
+**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
+is a function pointer, which is not zero-sized.
+This pattern should be rewritten. There are a few possible ways to do this:
+
+- change the original fn declaration to match the expected signature,
+  and do the cast in the fn body (the prefered option)
+- cast the fn item fo a fn pointer before calling transmute, as shown here:
+
+    ```
+    let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
+    let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too
+    ```
+
+The same applies to transmutes to `*mut fn()`, which were observedin practice.
+Note though that use of this type is generally incorrect.
+The intention is typically to describe a function pointer, but just `fn()`
+alone suffices for that. `*mut fn()` is a pointer to a fn pointer.
+(Since these values are typically just passed to C code, however, this rarely
+makes a difference in practice.)
+
+[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md