1 use rustc_data_structures
::sync
::{Lock, Lrc}
;
2 use rustc_errors
::{emitter::Emitter, Applicability, Diagnostic, Handler}
;
3 use rustc_parse
::parse_stream_from_source_str
;
4 use rustc_session
::parse
::ParseSess
;
5 use rustc_span
::source_map
::{FilePathMapping, SourceMap}
;
6 use rustc_span
::{FileName, InnerSpan}
;
9 use crate::core
::DocContext
;
10 use crate::fold
::DocFolder
;
11 use crate::html
::markdown
::{self, RustCodeBlock}
;
12 use crate::passes
::{span_of_attrs, Pass}
;
14 crate const CHECK_CODE_BLOCK_SYNTAX
: Pass
= Pass
{
15 name
: "check-code-block-syntax",
16 run
: check_code_block_syntax
,
17 description
: "validates syntax inside Rust code blocks",
20 crate fn check_code_block_syntax(krate
: clean
::Crate
, cx
: &DocContext
<'_
>) -> clean
::Crate
{
21 SyntaxChecker { cx }
.fold_crate(krate
)
24 struct SyntaxChecker
<'a
, 'tcx
> {
25 cx
: &'a DocContext
<'tcx
>,
28 impl<'a
, 'tcx
> SyntaxChecker
<'a
, 'tcx
> {
29 fn check_rust_syntax(&self, item
: &clean
::Item
, dox
: &str, code_block
: RustCodeBlock
) {
30 let buffer
= Lrc
::new(Lock
::new(Buffer
::default()));
31 let emitter
= BufferEmitter { buffer: Lrc::clone(&buffer) }
;
33 let sm
= Lrc
::new(SourceMap
::new(FilePathMapping
::empty()));
34 let handler
= Handler
::with_emitter(false, None
, Box
::new(emitter
));
35 let source
= dox
[code_block
.code
].to_owned();
36 let sess
= ParseSess
::with_span_handler(handler
, sm
);
38 let is_empty
= rustc_driver
::catch_fatal_errors(|| {
39 parse_stream_from_source_str(
40 FileName
::Custom(String
::from("doctest")),
48 let buffer
= buffer
.borrow();
50 if buffer
.has_errors
|| is_empty
{
51 let mut diag
= if let Some(sp
) =
52 super::source_span_for_markdown_range(self.cx
, &dox
, &code_block
.range
, &item
.attrs
)
54 let warning_message
= if buffer
.has_errors
{
55 "could not parse code block as Rust code"
57 "Rust code block is empty"
60 let mut diag
= self.cx
.sess().struct_span_warn(sp
, warning_message
);
62 if code_block
.syntax
.is_none() && code_block
.is_fenced
{
63 let sp
= sp
.from_inner(InnerSpan
::new(0, 3));
66 "mark blocks that do not contain Rust code as text",
67 String
::from("```text"),
68 Applicability
::MachineApplicable
,
74 // We couldn't calculate the span of the markdown block that had the error, so our
75 // diagnostics are going to be a bit lacking.
76 let mut diag
= self.cx
.sess().struct_span_warn(
77 super::span_of_attrs(&item
.attrs
).unwrap_or(item
.source
.span()),
78 "doc comment contains an invalid Rust code block",
81 if code_block
.syntax
.is_none() && code_block
.is_fenced
{
82 diag
.help("mark blocks that do not contain Rust code as text: ```text");
88 // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
89 for message
in buffer
.messages
.iter() {
98 impl<'a
, 'tcx
> DocFolder
for SyntaxChecker
<'a
, 'tcx
> {
99 fn fold_item(&mut self, item
: clean
::Item
) -> Option
<clean
::Item
> {
100 if let Some(dox
) = &item
.attrs
.collapsed_doc_value() {
101 let sp
= span_of_attrs(&item
.attrs
).unwrap_or(item
.source
.span());
102 let extra
= crate::html
::markdown
::ExtraInfo
::new_did(&self.cx
.tcx
, item
.def_id
, sp
);
103 for code_block
in markdown
::rust_code_blocks(&dox
, &extra
) {
104 self.check_rust_syntax(&item
, &dox
, code_block
);
108 Some(self.fold_item_recur(item
))
114 messages
: Vec
<String
>,
118 struct BufferEmitter
{
119 buffer
: Lrc
<Lock
<Buffer
>>,
122 impl Emitter
for BufferEmitter
{
123 fn emit_diagnostic(&mut self, diag
: &Diagnostic
) {
124 let mut buffer
= self.buffer
.borrow_mut();
125 buffer
.messages
.push(format
!("error from rustc: {}", diag
.message
[0].0));
127 buffer
.has_errors
= true;
131 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>> {