diff options
| author | kennytm <kennytm@gmail.com> | 2017-06-20 15:53:03 +0800 |
|---|---|---|
| committer | kennytm <kennytm@gmail.com> | 2017-06-23 15:31:53 +0800 |
| commit | 2c8916581472f5f9892c306632bb2b3d8bc96bc4 (patch) | |
| tree | c1aadcc1c69e59c835788afbdddb259bf8b2733f | |
| parent | 4711982314ba33cab83704c26484b501c8652774 (diff) | |
| download | rust-2c8916581472f5f9892c306632bb2b3d8bc96bc4.tar.gz rust-2c8916581472f5f9892c306632bb2b3d8bc96bc4.zip | |
Modify --explain to handle hidden code (`# ...`) and indented code blocks.
| -rw-r--r-- | src/librustc_driver/lib.rs | 19 | ||||
| -rw-r--r-- | src/test/ui/explain.rs | 11 | ||||
| -rw-r--r-- | src/test/ui/explain.stdout | 63 |
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 |
