Skip to content

Commit 540785e

Browse files
committed
rustdoc: point at span in include_str!-ed md file
1 parent d748046 commit 540785e

File tree

10 files changed

+121
-42
lines changed

10 files changed

+121
-42
lines changed

Diff for: compiler/rustc_builtin_macros/src/source_util.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,10 @@ pub fn expand_include_str(
196196
Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
197197
};
198198
ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) {
199-
Ok(bytes) => match std::str::from_utf8(&bytes) {
199+
Ok((bytes, bsp)) => match std::str::from_utf8(&bytes) {
200200
Ok(src) => {
201201
let interned_src = Symbol::intern(src);
202-
MacEager::expr(cx.expr_str(sp, interned_src))
202+
MacEager::expr(cx.expr_str(bsp, interned_src))
203203
}
204204
Err(_) => {
205205
let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file"));
@@ -225,7 +225,9 @@ pub fn expand_include_bytes(
225225
Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
226226
};
227227
ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) {
228-
Ok(bytes) => {
228+
Ok((bytes, _bsp)) => {
229+
// Don't care about getting the span for the raw bytes,
230+
// because the console can't really show them anyway.
229231
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes));
230232
MacEager::expr(expr)
231233
}
@@ -238,7 +240,7 @@ fn load_binary_file(
238240
original_path: &Path,
239241
macro_span: Span,
240242
path_span: Span,
241-
) -> Result<Lrc<[u8]>, Box<dyn MacResult>> {
243+
) -> Result<(Lrc<[u8]>, Span), Box<dyn MacResult>> {
242244
let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) {
243245
Ok(path) => path,
244246
Err(err) => {

Diff for: compiler/rustc_resolve/src/rustdoc.rs

+38-7
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,12 @@ pub fn attrs_to_doc_fragments<'a>(
194194
for (attr, item_id) in attrs {
195195
if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
196196
let doc = beautify_doc_string(doc_str, comment_kind);
197-
let kind = if attr.is_doc_comment() {
198-
DocFragmentKind::SugaredDoc
197+
let (span, kind) = if attr.is_doc_comment() {
198+
(attr.span, DocFragmentKind::SugaredDoc)
199199
} else {
200-
DocFragmentKind::RawDoc
200+
(span_for_value(attr), DocFragmentKind::RawDoc)
201201
};
202-
let fragment = DocFragment { span: attr.span, doc, kind, item_id, indent: 0 };
202+
let fragment = DocFragment { span, doc, kind, item_id, indent: 0 };
203203
doc_fragments.push(fragment);
204204
} else if !doc_only {
205205
other_attrs.push(attr.clone());
@@ -211,6 +211,16 @@ pub fn attrs_to_doc_fragments<'a>(
211211
(doc_fragments, other_attrs)
212212
}
213213

214+
fn span_for_value(attr: &ast::Attribute) -> Span {
215+
if let ast::AttrKind::Normal(normal) = &attr.kind
216+
&& let ast::AttrArgs::Eq(_, ast::AttrArgsEq::Hir(meta)) = &normal.item.args
217+
{
218+
meta.span.with_ctxt(attr.span.ctxt())
219+
} else {
220+
attr.span
221+
}
222+
}
223+
214224
/// Return the doc-comments on this item, grouped by the module they came from.
215225
/// The module can be different if this is a re-export with added documentation.
216226
///
@@ -482,15 +492,36 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
482492

483493
/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
484494
///
485-
/// This method will return `None` if we cannot construct a span from the source map or if the
486-
/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in
487-
/// that case due to escaping and other source features.
495+
/// This method does not always work, because markdown bytes don't necessarily match source bytes,
496+
/// like if escapes are used in the string. In this case, it returns `None`.
497+
///
498+
/// This method will return `Some` only if:
499+
///
500+
/// - The doc is made entirely from sugared doc comments, which cannot contain escapes
501+
/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal
502+
/// - The doc comes from `include_str!`
488503
pub fn source_span_for_markdown_range(
489504
tcx: TyCtxt<'_>,
490505
markdown: &str,
491506
md_range: &Range<usize>,
492507
fragments: &[DocFragment],
493508
) -> Option<Span> {
509+
if let &[fragment] = &fragments
510+
&& fragment.kind == DocFragmentKind::RawDoc
511+
&& let Ok(snippet) = tcx.sess.source_map().span_to_snippet(fragment.span)
512+
&& snippet.trim_end() == markdown.trim_end()
513+
&& let Ok(md_range_lo) = u32::try_from(md_range.start)
514+
&& let Ok(md_range_hi) = u32::try_from(md_range.end)
515+
{
516+
// Single fragment with string that contains same bytes as doc.
517+
return Some(Span::new(
518+
fragment.span.lo() + rustc_span::BytePos(md_range_lo),
519+
fragment.span.lo() + rustc_span::BytePos(md_range_hi),
520+
fragment.span.ctxt(),
521+
fragment.span.parent(),
522+
));
523+
}
524+
494525
let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
495526

496527
if !is_all_sugared_doc {

Diff for: compiler/rustc_span/src/source_map.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl SourceMap {
218218
///
219219
/// Unlike `load_file`, guarantees that no normalization like BOM-removal
220220
/// takes place.
221-
pub fn load_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
221+
pub fn load_binary_file(&self, path: &Path) -> io::Result<(Lrc<[u8]>, Span)> {
222222
let bytes = self.file_loader.read_binary_file(path)?;
223223

224224
// We need to add file to the `SourceMap`, so that it is present
@@ -227,8 +227,16 @@ impl SourceMap {
227227
// via `mod`, so we try to use real file contents and not just an
228228
// empty string.
229229
let text = std::str::from_utf8(&bytes).unwrap_or("").to_string();
230-
self.new_source_file(path.to_owned().into(), text);
231-
Ok(bytes)
230+
let file = self.new_source_file(path.to_owned().into(), text);
231+
Ok((
232+
bytes,
233+
Span::new(
234+
file.start_pos,
235+
BytePos(file.start_pos.0 + file.source_len.0),
236+
SyntaxContext::root(),
237+
None,
238+
),
239+
))
232240
}
233241

234242
// By returning a `MonotonicVec`, we ensure that consumers cannot invalidate

Diff for: tests/rustdoc-ui/auxiliary/include-str-bare-urls.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
HEADS UP! https://example.com MUST SHOW UP IN THE STDERR FILE!
2+
3+
Normally, a line with errors on it will also have a comment
4+
marking it up as something that needs to generate an error.
5+
6+
The test harness doesn't gather hot comments from this file.
7+
Rustdoc will generate an error for the line, and the `.stderr`
8+
snapshot includes this error, but Compiletest doesn't see it.
9+
10+
If the stderr file changes, make sure the warning points at the URL!

Diff for: tests/rustdoc-ui/include-str-bare-urls.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// https://github.com/rust-lang/rust/issues/118549
2+
//
3+
// HEADS UP!
4+
//
5+
// Normally, a line with errors on it will also have a comment
6+
// marking it up as something that needs to generate an error.
7+
//
8+
// The test harness doesn't gather hot comments from the `.md` file.
9+
// Rustdoc will generate an error for the line, and the `.stderr`
10+
// snapshot includes this error, but Compiletest doesn't see it.
11+
//
12+
// If the stderr file changes, make sure the warning points at the URL!
13+
14+
#![deny(rustdoc::bare_urls)]
15+
#![doc=include_str!("auxiliary/include-str-bare-urls.md")]

Diff for: tests/rustdoc-ui/include-str-bare-urls.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: this URL is not a hyperlink
2+
--> $DIR/auxiliary/include-str-bare-urls.md:1:11
3+
|
4+
LL | HEADS UP! https://example.com MUST SHOW UP IN THE STDERR FILE!
5+
| ^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com>`
6+
|
7+
= note: bare URLs are not automatically turned into clickable links
8+
note: the lint level is defined here
9+
--> $DIR/include-str-bare-urls.rs:11:9
10+
|
11+
LL | #![deny(rustdoc::bare_urls)]
12+
| ^^^^^^^^^^^^^^^^^^
13+
14+
error: aborting due to 1 previous error
15+

Diff for: tests/rustdoc-ui/intra-doc/warning.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ pub fn d() {}
4747

4848
macro_rules! f {
4949
($f:expr) => {
50-
#[doc = $f] //~ WARNING `BarF`
50+
#[doc = $f]
5151
pub fn f() {}
5252
}
5353
}
54-
f!("Foo\nbar [BarF] bar\nbaz");
54+
f!("Foo\nbar [BarF] bar\nbaz"); //~ WARNING `BarF`
5555

5656
/** # for example,
5757
*

Diff for: tests/rustdoc-ui/intra-doc/warning.stderr

+8-11
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ LL | bar [BarC] bar
6969
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
7070

7171
warning: unresolved link to `BarD`
72-
--> $DIR/warning.rs:45:1
72+
--> $DIR/warning.rs:45:9
7373
|
7474
LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
75-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
7676
|
7777
= note: the link appears in this line:
7878

@@ -82,13 +82,10 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
8282
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
8383

8484
warning: unresolved link to `BarF`
85-
--> $DIR/warning.rs:50:9
85+
--> $DIR/warning.rs:54:4
8686
|
87-
LL | #[doc = $f]
88-
| ^^^^^^^^^^^
89-
...
9087
LL | f!("Foo\nbar [BarF] bar\nbaz");
91-
| ------------------------------ in this macro invocation
88+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
9289
|
9390
= note: the link appears in this line:
9491

@@ -115,10 +112,10 @@ LL | * time to introduce a link [error]
115112
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
116113

117114
warning: unresolved link to `error`
118-
--> $DIR/warning.rs:68:1
115+
--> $DIR/warning.rs:68:9
119116
|
120117
LL | #[doc = "single line [error]"]
121-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
118+
| ^^^^^^^^^^^^^^^^^^^^^
122119
|
123120
= note: the link appears in this line:
124121

@@ -128,10 +125,10 @@ LL | #[doc = "single line [error]"]
128125
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
129126

130127
warning: unresolved link to `error`
131-
--> $DIR/warning.rs:71:1
128+
--> $DIR/warning.rs:71:9
132129
|
133130
LL | #[doc = "single line with \"escaping\" [error]"]
134-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
135132
|
136133
= note: the link appears in this line:
137134

Diff for: tests/rustdoc-ui/invalid-syntax.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,13 @@ LL | | /// ```
9090
= note: error from rustc: unknown start of token: \
9191

9292
warning: could not parse code block as Rust code
93-
--> $DIR/invalid-syntax.rs:70:1
93+
--> $DIR/invalid-syntax.rs:70:9
9494
|
95-
LL | / #[doc = "```"]
95+
LL | #[doc = "```"]
96+
| _________^
9697
LL | | /// \_
9798
LL | | #[doc = "```"]
98-
| |______________^
99+
| |_____________^
99100
|
100101
= help: mark blocks that do not contain Rust code as text: ```text
101102
= note: error from rustc: unknown start of token: \

Diff for: tests/rustdoc-ui/unescaped_backticks.stderr

+12-12
Original file line numberDiff line numberDiff line change
@@ -640,21 +640,21 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)!
640640
| +
641641

642642
error: unescaped backtick
643-
--> $DIR/unescaped_backticks.rs:108:1
643+
--> $DIR/unescaped_backticks.rs:108:9
644644
|
645645
LL | #[doc = "`"]
646-
| ^^^^^^^^^^^^
646+
| ^^^
647647
|
648648
= help: the opening or closing backtick of an inline code may be missing
649649
= help: if you meant to use a literal backtick, escape it
650650
change: `
651651
to this: \`
652652

653653
error: unescaped backtick
654-
--> $DIR/unescaped_backticks.rs:115:1
654+
--> $DIR/unescaped_backticks.rs:115:9
655655
|
656656
LL | #[doc = concat!("\\", "`")]
657-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
657+
| ^^^^^^^^^^^^^^^^^^^^
658658
|
659659
= help: the opening backtick of an inline code may be missing
660660
change: \`
@@ -664,10 +664,10 @@ LL | #[doc = concat!("\\", "`")]
664664
to this: \\`
665665

666666
error: unescaped backtick
667-
--> $DIR/unescaped_backticks.rs:119:1
667+
--> $DIR/unescaped_backticks.rs:119:9
668668
|
669669
LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."]
670-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
670+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
671671
|
672672
= help: the opening backtick of a previous inline code may be missing
673673
change: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`.
@@ -677,10 +677,10 @@ LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same a
677677
to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`.
678678

679679
error: unescaped backtick
680-
--> $DIR/unescaped_backticks.rs:123:1
680+
--> $DIR/unescaped_backticks.rs:123:9
681681
|
682682
LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."]
683-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
683+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
684684
|
685685
= help: a previous inline code might be longer than expected
686686
change: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`.
@@ -690,10 +690,10 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same a
690690
to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`.
691691

692692
error: unescaped backtick
693-
--> $DIR/unescaped_backticks.rs:127:1
693+
--> $DIR/unescaped_backticks.rs:127:9
694694
|
695695
LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."]
696-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
696+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
697697
|
698698
= help: the opening backtick of an inline code may be missing
699699
change: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`.
@@ -703,10 +703,10 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same
703703
to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`.
704704

705705
error: unescaped backtick
706-
--> $DIR/unescaped_backticks.rs:131:1
706+
--> $DIR/unescaped_backticks.rs:131:9
707707
|
708708
LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."]
709-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
709+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
710710
|
711711
= help: the closing backtick of an inline code may be missing
712712
change: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a).

0 commit comments

Comments
 (0)