Skip to content

Commit 2c89165

Browse files
committed
Modify --explain to handle hidden code (# ...) and indented code blocks.
1 parent 4711982 commit 2c89165

File tree

3 files changed

+87
-6
lines changed

3 files changed

+87
-6
lines changed

src/librustc_driver/lib.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,21 @@ fn handle_explain(code: &str,
355355
};
356356
match descriptions.find_description(&normalised) {
357357
Some(ref description) => {
358+
let mut is_in_code_block = false;
358359
// Slice off the leading newline and print.
359-
print!("{}", &(&description[1..]).split("\n").map(|x| {
360-
format!("{}\n", if x.starts_with("```") {
361-
"```"
360+
for line in description[1..].lines() {
361+
let indent_level = line.find(|c: char| !c.is_whitespace())
362+
.unwrap_or_else(|| line.len());
363+
let dedented_line = &line[indent_level..];
364+
if dedented_line.starts_with("```") {
365+
is_in_code_block = !is_in_code_block;
366+
println!("{}", &line[..(indent_level+3)]);
367+
} else if is_in_code_block && dedented_line.starts_with("# ") {
368+
continue;
362369
} else {
363-
x
364-
})
365-
}).collect::<String>());
370+
println!("{}", line);
371+
}
372+
}
366373
}
367374
None => {
368375
early_error(output, &format!("no extended information for {}", code));

src/test/ui/explain.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: --explain E0591

src/test/ui/explain.stdout

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Per [RFC 401][rfc401], if you have a function declaration `foo`:
2+
3+
```
4+
// For the purposes of this explanation, all of these
5+
// different kinds of `fn` declarations are equivalent:
6+
struct S;
7+
fn foo(x: S) { /* ... */ }
8+
extern "C" { fn foo(x: S); }
9+
impl S { fn foo(self) { /* ... */ } }
10+
```
11+
12+
the type of `foo` is **not** `fn(S)`, as one might expect.
13+
Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`.
14+
However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`,
15+
so you rarely notice this:
16+
17+
```
18+
let x: fn(S) = foo; // OK, coerces
19+
```
20+
21+
The reason that this matter is that the type `fn(S)` is not specific to
22+
any particular function: it's a function _pointer_. So calling `x()` results
23+
in a virtual call, whereas `foo()` is statically dispatched, because the type
24+
of `foo` tells us precisely what function is being called.
25+
26+
As noted above, coercions mean that most code doesn't have to be
27+
concerned with this distinction. However, you can tell the difference
28+
when using **transmute** to convert a fn item into a fn pointer.
29+
30+
This is sometimes done as part of an FFI:
31+
32+
```
33+
extern "C" fn foo(userdata: Box<i32>) {
34+
/* ... */
35+
}
36+
37+
let f: extern "C" fn(*mut i32) = transmute(foo);
38+
callback(f);
39+
```
40+
41+
Here, transmute is being used to convert the types of the fn arguments.
42+
This pattern is incorrect because, because the type of `foo` is a function
43+
**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
44+
is a function pointer, which is not zero-sized.
45+
This pattern should be rewritten. There are a few possible ways to do this:
46+
47+
- change the original fn declaration to match the expected signature,
48+
and do the cast in the fn body (the prefered option)
49+
- cast the fn item fo a fn pointer before calling transmute, as shown here:
50+
51+
```
52+
let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
53+
let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too
54+
```
55+
56+
The same applies to transmutes to `*mut fn()`, which were observedin practice.
57+
Note though that use of this type is generally incorrect.
58+
The intention is typically to describe a function pointer, but just `fn()`
59+
alone suffices for that. `*mut fn()` is a pointer to a fn pointer.
60+
(Since these values are typically just passed to C code, however, this rarely
61+
makes a difference in practice.)
62+
63+
[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md

0 commit comments

Comments
 (0)