|
1 | 1 | use rustc_data_structures::sync::{Lock, Lrc};
|
2 | 2 | use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
|
| 3 | +use rustc_middle::lint::LintDiagnosticBuilder; |
3 | 4 | use rustc_parse::parse_stream_from_source_str;
|
4 | 5 | use rustc_session::parse::ParseSess;
|
5 | 6 | use rustc_span::source_map::{FilePathMapping, SourceMap};
|
@@ -47,63 +48,86 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
|
47 | 48 | .unwrap_or(false);
|
48 | 49 | let buffer = buffer.borrow();
|
49 | 50 |
|
50 |
| - if buffer.has_errors || is_empty { |
51 |
| - let mut diag = if let Some(sp) = super::source_span_for_markdown_range( |
52 |
| - self.cx.tcx, |
53 |
| - &dox, |
54 |
| - &code_block.range, |
55 |
| - &item.attrs, |
56 |
| - ) { |
57 |
| - let (warning_message, suggest_using_text) = if buffer.has_errors { |
58 |
| - ("could not parse code block as Rust code", true) |
59 |
| - } else { |
60 |
| - ("Rust code block is empty", false) |
61 |
| - }; |
62 |
| - |
63 |
| - let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); |
64 |
| - |
65 |
| - if code_block.syntax.is_none() && code_block.is_fenced { |
66 |
| - let sp = sp.from_inner(InnerSpan::new(0, 3)); |
67 |
| - diag.span_suggestion( |
68 |
| - sp, |
69 |
| - "mark blocks that do not contain Rust code as text", |
70 |
| - String::from("```text"), |
71 |
| - Applicability::MachineApplicable, |
| 51 | + if !buffer.has_errors && !is_empty { |
| 52 | + // No errors in a non-empty program. |
| 53 | + return; |
| 54 | + } |
| 55 | + |
| 56 | + let local_id = match item.def_id.as_real().and_then(|x| x.as_local()) { |
| 57 | + Some(id) => id, |
| 58 | + // We don't need to check the syntax for other crates so returning |
| 59 | + // without doing anything should not be a problem. |
| 60 | + None => return, |
| 61 | + }; |
| 62 | + |
| 63 | + let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id); |
| 64 | + let empty_block = code_block.syntax.is_none() && code_block.is_fenced; |
| 65 | + let is_ignore = code_block.is_ignore; |
| 66 | + |
| 67 | + // The span and whether it is precise or not. |
| 68 | + let (sp, precise_span) = match super::source_span_for_markdown_range( |
| 69 | + self.cx.tcx, |
| 70 | + &dox, |
| 71 | + &code_block.range, |
| 72 | + &item.attrs, |
| 73 | + ) { |
| 74 | + Some(sp) => (sp, true), |
| 75 | + None => (item.attr_span(self.cx.tcx), false), |
| 76 | + }; |
| 77 | + |
| 78 | + // lambda that will use the lint to start a new diagnostic and add |
| 79 | + // a suggestion to it when needed. |
| 80 | + let diag_builder = |lint: LintDiagnosticBuilder<'_>| { |
| 81 | + let explanation = if is_ignore { |
| 82 | + "`ignore` code blocks require valid Rust code for syntax highlighting; \ |
| 83 | + mark blocks that do not contain Rust code as text" |
| 84 | + } else { |
| 85 | + "mark blocks that do not contain Rust code as text" |
| 86 | + }; |
| 87 | + let msg = if buffer.has_errors { |
| 88 | + "could not parse code block as Rust code" |
| 89 | + } else { |
| 90 | + "Rust code block is empty" |
| 91 | + }; |
| 92 | + let mut diag = lint.build(msg); |
| 93 | + |
| 94 | + if precise_span { |
| 95 | + if is_ignore { |
| 96 | + // giving an accurate suggestion is hard because `ignore` might not have come first in the list. |
| 97 | + // just give a `help` instead. |
| 98 | + diag.span_help( |
| 99 | + sp.from_inner(InnerSpan::new(0, 3)), |
| 100 | + &format!("{}: ```text", explanation), |
72 | 101 | );
|
73 |
| - } else if suggest_using_text && code_block.is_ignore { |
74 |
| - let sp = sp.from_inner(InnerSpan::new(0, 3)); |
| 102 | + } else if empty_block { |
75 | 103 | diag.span_suggestion(
|
76 |
| - sp, |
77 |
| - "`ignore` code blocks require valid Rust code for syntax highlighting. \ |
78 |
| - Mark blocks that do not contain Rust code as text", |
79 |
| - String::from("```text,"), |
| 104 | + sp.from_inner(InnerSpan::new(0, 3)), |
| 105 | + explanation, |
| 106 | + String::from("```text"), |
80 | 107 | Applicability::MachineApplicable,
|
81 | 108 | );
|
82 | 109 | }
|
83 |
| - |
84 |
| - diag |
85 |
| - } else { |
86 |
| - // We couldn't calculate the span of the markdown block that had the error, so our |
87 |
| - // diagnostics are going to be a bit lacking. |
88 |
| - let mut diag = self.cx.sess().struct_span_warn( |
89 |
| - item.attr_span(self.cx.tcx), |
90 |
| - "doc comment contains an invalid Rust code block", |
91 |
| - ); |
92 |
| - |
93 |
| - if code_block.syntax.is_none() && code_block.is_fenced { |
94 |
| - diag.help("mark blocks that do not contain Rust code as text: ```text"); |
95 |
| - } |
96 |
| - |
97 |
| - diag |
98 |
| - }; |
| 110 | + } else if empty_block || is_ignore { |
| 111 | + diag.help(&format!("{}: ```text", explanation)); |
| 112 | + } |
99 | 113 |
|
100 | 114 | // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
|
101 | 115 | for message in buffer.messages.iter() {
|
102 | 116 | diag.note(&message);
|
103 | 117 | }
|
104 | 118 |
|
105 | 119 | diag.emit();
|
106 |
| - } |
| 120 | + }; |
| 121 | + |
| 122 | + // Finally build and emit the completed diagnostic. |
| 123 | + // All points of divergence have been handled earlier so this can be |
| 124 | + // done the same way whether the span is precise or not. |
| 125 | + self.cx.tcx.struct_span_lint_hir( |
| 126 | + crate::lint::INVALID_RUST_CODEBLOCKS, |
| 127 | + hir_id, |
| 128 | + sp, |
| 129 | + diag_builder, |
| 130 | + ); |
107 | 131 | }
|
108 | 132 | }
|
109 | 133 |
|
|
0 commit comments