Modify --explain to handle hidden code (# ...
) and indented code blocks.
This commit is contained in:
parent
4711982314
commit
2c89165814
3 changed files with 87 additions and 6 deletions
|
@ -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));
|
||||
|
|
11
src/test/ui/explain.rs
Normal file
11
src/test/ui/explain.rs
Normal file
|
@ -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
|
63
src/test/ui/explain.stdout
Normal file
63
src/test/ui/explain.stdout
Normal file
|
@ -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
|
Loading…
Add table
Reference in a new issue