]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use crate::snippet::Style; |
04454e1e | 2 | use crate::{ |
923072b8 FG |
3 | CodeSuggestion, DiagnosticMessage, Level, MultiSpan, SubdiagnosticMessage, Substitution, |
4 | SubstitutionPart, SuggestionStyle, | |
04454e1e | 5 | }; |
5e7ed085 | 6 | use rustc_data_structures::stable_map::FxHashMap; |
04454e1e | 7 | use rustc_error_messages::FluentValue; |
5e7ed085 | 8 | use rustc_lint_defs::{Applicability, LintExpectationId}; |
5e7ed085 | 9 | use rustc_span::edition::LATEST_STABLE_EDITION; |
04454e1e FG |
10 | use rustc_span::symbol::{Ident, Symbol}; |
11 | use rustc_span::{Span, DUMMY_SP}; | |
12 | use std::borrow::Cow; | |
c30ab7b3 | 13 | use std::fmt; |
c295e0f8 | 14 | use std::hash::{Hash, Hasher}; |
c30ab7b3 | 15 | |
5099ac24 FG |
16 | /// Error type for `Diagnostic`'s `suggestions` field, indicating that |
17 | /// `.disable_suggestions()` was called on the `Diagnostic`. | |
18 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] | |
19 | pub struct SuggestionsDisabled; | |
20 | ||
04454e1e FG |
21 | /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of |
22 | /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of | |
23 | /// diagnostic emission. | |
24 | pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>); | |
25 | ||
26 | /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted | |
27 | /// to a `FluentValue` by the emitter to be used in diagnostic translation. | |
28 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] | |
29 | pub enum DiagnosticArgValue<'source> { | |
30 | Str(Cow<'source, str>), | |
31 | Number(usize), | |
32 | } | |
33 | ||
34 | /// Converts a value of a type into a `DiagnosticArg` (typically a field of a `SessionDiagnostic` | |
35 | /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type | |
36 | /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*` | |
37 | /// crates to implement this. | |
38 | pub trait IntoDiagnosticArg { | |
39 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; | |
40 | } | |
41 | ||
42 | impl IntoDiagnosticArg for String { | |
43 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { | |
44 | DiagnosticArgValue::Str(Cow::Owned(self)) | |
45 | } | |
46 | } | |
47 | ||
48 | impl IntoDiagnosticArg for Symbol { | |
49 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { | |
50 | self.to_ident_string().into_diagnostic_arg() | |
51 | } | |
52 | } | |
53 | ||
54 | impl IntoDiagnosticArg for Ident { | |
55 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { | |
56 | self.to_string().into_diagnostic_arg() | |
57 | } | |
58 | } | |
59 | ||
60 | impl<'a> IntoDiagnosticArg for &'a str { | |
61 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { | |
62 | self.to_string().into_diagnostic_arg() | |
63 | } | |
64 | } | |
65 | ||
66 | impl IntoDiagnosticArg for usize { | |
67 | fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { | |
68 | DiagnosticArgValue::Number(self) | |
69 | } | |
70 | } | |
71 | ||
72 | impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> { | |
73 | fn into(self) -> FluentValue<'source> { | |
74 | match self { | |
75 | DiagnosticArgValue::Str(s) => From::from(s), | |
76 | DiagnosticArgValue::Number(n) => From::from(n), | |
77 | } | |
78 | } | |
79 | } | |
80 | ||
81 | /// Trait implemented by error types. This should not be implemented manually. Instead, use | |
82 | /// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic]. | |
923072b8 | 83 | #[rustc_diagnostic_item = "AddSubdiagnostic"] |
04454e1e FG |
84 | pub trait AddSubdiagnostic { |
85 | /// Add a subdiagnostic to an existing diagnostic. | |
86 | fn add_to_diagnostic(self, diag: &mut Diagnostic); | |
87 | } | |
88 | ||
c30ab7b3 | 89 | #[must_use] |
c295e0f8 | 90 | #[derive(Clone, Debug, Encodable, Decodable)] |
c30ab7b3 | 91 | pub struct Diagnostic { |
5e7ed085 FG |
92 | // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, |
93 | // outside of what methods in this crate themselves allow. | |
923072b8 | 94 | pub(crate) level: Level, |
5e7ed085 | 95 | |
04454e1e | 96 | pub message: Vec<(DiagnosticMessage, Style)>, |
abe05a73 | 97 | pub code: Option<DiagnosticId>, |
c30ab7b3 SL |
98 | pub span: MultiSpan, |
99 | pub children: Vec<SubDiagnostic>, | |
5099ac24 | 100 | pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, |
04454e1e | 101 | args: Vec<DiagnosticArg<'static>>, |
60c5eb7d XL |
102 | |
103 | /// This is not used for highlighting or rendering any error message. Rather, it can be used | |
104 | /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of | |
105 | /// `span` if there is one. Otherwise, it is `DUMMY_SP`. | |
106 | pub sort_span: Span, | |
c295e0f8 XL |
107 | |
108 | /// If diagnostic is from Lint, custom hash function ignores notes | |
109 | /// otherwise hash is based on the all the fields | |
110 | pub is_lint: bool, | |
c30ab7b3 SL |
111 | } |
112 | ||
3dfed10e | 113 | #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] |
abe05a73 XL |
114 | pub enum DiagnosticId { |
115 | Error(String), | |
136023e0 | 116 | Lint { name: String, has_future_breakage: bool, is_force_warn: bool }, |
abe05a73 XL |
117 | } |
118 | ||
fc512014 XL |
119 | /// A "sub"-diagnostic attached to a parent diagnostic. |
120 | /// For example, a note attached to an error. | |
3dfed10e | 121 | #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] |
c30ab7b3 SL |
122 | pub struct SubDiagnostic { |
123 | pub level: Level, | |
04454e1e | 124 | pub message: Vec<(DiagnosticMessage, Style)>, |
c30ab7b3 | 125 | pub span: MultiSpan, |
abe05a73 | 126 | pub render_span: Option<MultiSpan>, |
c30ab7b3 SL |
127 | } |
128 | ||
e74abb32 | 129 | #[derive(Debug, PartialEq, Eq)] |
cc61c64b XL |
130 | pub struct DiagnosticStyledString(pub Vec<StringPart>); |
131 | ||
132 | impl DiagnosticStyledString { | |
133 | pub fn new() -> DiagnosticStyledString { | |
134 | DiagnosticStyledString(vec![]) | |
135 | } | |
136 | pub fn push_normal<S: Into<String>>(&mut self, t: S) { | |
137 | self.0.push(StringPart::Normal(t.into())); | |
138 | } | |
139 | pub fn push_highlighted<S: Into<String>>(&mut self, t: S) { | |
140 | self.0.push(StringPart::Highlighted(t.into())); | |
141 | } | |
60c5eb7d XL |
142 | pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) { |
143 | if highlight { | |
144 | self.push_highlighted(t); | |
145 | } else { | |
146 | self.push_normal(t); | |
147 | } | |
148 | } | |
cc61c64b XL |
149 | pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString { |
150 | DiagnosticStyledString(vec![StringPart::Normal(t.into())]) | |
151 | } | |
152 | ||
153 | pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString { | |
154 | DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) | |
155 | } | |
c295e0f8 XL |
156 | |
157 | pub fn content(&self) -> String { | |
158 | self.0.iter().map(|x| x.content()).collect::<String>() | |
159 | } | |
cc61c64b XL |
160 | } |
161 | ||
e74abb32 | 162 | #[derive(Debug, PartialEq, Eq)] |
cc61c64b XL |
163 | pub enum StringPart { |
164 | Normal(String), | |
165 | Highlighted(String), | |
166 | } | |
167 | ||
c295e0f8 XL |
168 | impl StringPart { |
169 | pub fn content(&self) -> &str { | |
170 | match self { | |
171 | &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s, | |
172 | } | |
173 | } | |
174 | } | |
175 | ||
c30ab7b3 | 176 | impl Diagnostic { |
04454e1e | 177 | pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { |
c30ab7b3 SL |
178 | Diagnostic::new_with_code(level, None, message) |
179 | } | |
180 | ||
04454e1e FG |
181 | pub fn new_with_code<M: Into<DiagnosticMessage>>( |
182 | level: Level, | |
183 | code: Option<DiagnosticId>, | |
184 | message: M, | |
185 | ) -> Self { | |
c30ab7b3 | 186 | Diagnostic { |
3b2f2976 | 187 | level, |
04454e1e | 188 | message: vec![(message.into(), Style::NoStyle)], |
3b2f2976 | 189 | code, |
c30ab7b3 SL |
190 | span: MultiSpan::new(), |
191 | children: vec![], | |
5099ac24 | 192 | suggestions: Ok(vec![]), |
04454e1e | 193 | args: vec![], |
60c5eb7d | 194 | sort_span: DUMMY_SP, |
c295e0f8 | 195 | is_lint: false, |
c30ab7b3 SL |
196 | } |
197 | } | |
198 | ||
5e7ed085 FG |
199 | #[inline(always)] |
200 | pub fn level(&self) -> Level { | |
201 | self.level | |
202 | } | |
203 | ||
8faf50e0 XL |
204 | pub fn is_error(&self) -> bool { |
205 | match self.level { | |
5e7ed085 FG |
206 | Level::Bug |
207 | | Level::DelayedBug | |
208 | | Level::Fatal | |
209 | | Level::Error { .. } | |
210 | | Level::FailureNote => true, | |
211 | ||
923072b8 | 212 | Level::Warning(_) |
5e7ed085 FG |
213 | | Level::Note |
214 | | Level::OnceNote | |
215 | | Level::Help | |
216 | | Level::Allow | |
217 | | Level::Expect(_) => false, | |
218 | } | |
219 | } | |
220 | ||
221 | pub fn update_unstable_expectation_id( | |
222 | &mut self, | |
223 | unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, | |
224 | ) { | |
923072b8 FG |
225 | if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) = |
226 | &mut self.level | |
227 | { | |
5e7ed085 FG |
228 | if expectation_id.is_stable() { |
229 | return; | |
230 | } | |
8faf50e0 | 231 | |
5e7ed085 FG |
232 | // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index. |
233 | // The lint index inside the attribute is manually transferred here. | |
234 | let lint_index = expectation_id.get_lint_index(); | |
235 | expectation_id.set_lint_index(None); | |
236 | let mut stable_id = *unstable_to_stable | |
237 | .get(&expectation_id) | |
238 | .expect("each unstable `LintExpectationId` must have a matching stable id"); | |
239 | ||
240 | stable_id.set_lint_index(lint_index); | |
241 | *expectation_id = stable_id; | |
29967ef6 XL |
242 | } |
243 | } | |
244 | ||
245 | pub fn has_future_breakage(&self) -> bool { | |
246 | match self.code { | |
247 | Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage, | |
248 | _ => false, | |
8faf50e0 XL |
249 | } |
250 | } | |
251 | ||
136023e0 XL |
252 | pub fn is_force_warn(&self) -> bool { |
253 | match self.code { | |
254 | Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn, | |
255 | _ => false, | |
256 | } | |
257 | } | |
258 | ||
5e7ed085 FG |
259 | /// Delay emission of this diagnostic as a bug. |
260 | /// | |
261 | /// This can be useful in contexts where an error indicates a bug but | |
262 | /// typically this only happens when other compilation errors have already | |
263 | /// happened. In those cases this can be used to defer emission of this | |
264 | /// diagnostic as a bug in the compiler only if no other errors have been | |
265 | /// emitted. | |
266 | /// | |
267 | /// In the meantime, though, callsites are required to deal with the "bug" | |
268 | /// locally in whichever way makes the most sense. | |
269 | #[track_caller] | |
270 | pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self { | |
271 | assert!( | |
272 | self.is_error(), | |
273 | "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", | |
274 | self.level | |
275 | ); | |
276 | self.level = Level::DelayedBug; | |
c30ab7b3 | 277 | |
5e7ed085 | 278 | self |
c30ab7b3 SL |
279 | } |
280 | ||
9fa01778 | 281 | /// Adds a span/label to be included in the resulting snippet. |
e1599b0c | 282 | /// |
3dfed10e XL |
283 | /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic |
284 | /// was first built. That means it will be shown together with the original | |
285 | /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods. | |
286 | /// | |
287 | /// This span is *not* considered a ["primary span"][`MultiSpan`]; only | |
288 | /// the `Span` supplied when creating the diagnostic is primary. | |
923072b8 FG |
289 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
290 | pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self { | |
291 | self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label)); | |
c30ab7b3 SL |
292 | self |
293 | } | |
294 | ||
5e7ed085 FG |
295 | /// Labels all the given spans with the provided label. |
296 | /// See [`Self::span_label()`] for more information. | |
297 | pub fn span_labels( | |
298 | &mut self, | |
299 | spans: impl IntoIterator<Item = Span>, | |
300 | label: impl AsRef<str>, | |
301 | ) -> &mut Self { | |
302 | let label = label.as_ref(); | |
303 | for span in spans { | |
304 | self.span_label(span, label); | |
305 | } | |
306 | self | |
307 | } | |
308 | ||
a1dfa0c6 XL |
309 | pub fn replace_span_with(&mut self, after: Span) -> &mut Self { |
310 | let before = self.span.clone(); | |
311 | self.set_span(after); | |
312 | for span_label in before.span_labels() { | |
313 | if let Some(label) = span_label.label { | |
04454e1e | 314 | self.span.push_span_label(after, label); |
a1dfa0c6 XL |
315 | } |
316 | } | |
317 | self | |
318 | } | |
319 | ||
5e7ed085 | 320 | pub fn note_expected_found( |
60c5eb7d XL |
321 | &mut self, |
322 | expected_label: &dyn fmt::Display, | |
323 | expected: DiagnosticStyledString, | |
324 | found_label: &dyn fmt::Display, | |
325 | found: DiagnosticStyledString, | |
326 | ) -> &mut Self { | |
327 | self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") | |
c30ab7b3 SL |
328 | } |
329 | ||
5e7ed085 | 330 | pub fn note_unsuccessful_coercion( |
60c5eb7d XL |
331 | &mut self, |
332 | expected: DiagnosticStyledString, | |
333 | found: DiagnosticStyledString, | |
334 | ) -> &mut Self { | |
e74abb32 | 335 | let mut msg: Vec<_> = |
74b04a01 | 336 | vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)]; |
dfeec247 XL |
337 | msg.extend(expected.0.iter().map(|x| match *x { |
338 | StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), | |
339 | StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), | |
340 | })); | |
74b04a01 | 341 | msg.push(("` to type '".to_string(), Style::NoStyle)); |
dfeec247 XL |
342 | msg.extend(found.0.iter().map(|x| match *x { |
343 | StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), | |
344 | StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), | |
345 | })); | |
74b04a01 | 346 | msg.push(("`".to_string(), Style::NoStyle)); |
e74abb32 XL |
347 | |
348 | // For now, just attach these as notes | |
349 | self.highlighted_note(msg); | |
350 | self | |
351 | } | |
352 | ||
60c5eb7d XL |
353 | pub fn note_expected_found_extra( |
354 | &mut self, | |
355 | expected_label: &dyn fmt::Display, | |
356 | expected: DiagnosticStyledString, | |
357 | found_label: &dyn fmt::Display, | |
358 | found: DiagnosticStyledString, | |
359 | expected_extra: &dyn fmt::Display, | |
360 | found_extra: &dyn fmt::Display, | |
361 | ) -> &mut Self { | |
f9f354fc XL |
362 | let expected_label = expected_label.to_string(); |
363 | let expected_label = if expected_label.is_empty() { | |
364 | "expected".to_string() | |
365 | } else { | |
366 | format!("expected {}", expected_label) | |
367 | }; | |
368 | let found_label = found_label.to_string(); | |
369 | let found_label = if found_label.is_empty() { | |
370 | "found".to_string() | |
371 | } else { | |
372 | format!("found {}", found_label) | |
373 | }; | |
60c5eb7d XL |
374 | let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { |
375 | (expected_label.len() - found_label.len(), 0) | |
376 | } else { | |
377 | (0, found_label.len() - expected_label.len()) | |
378 | }; | |
dfeec247 XL |
379 | let mut msg: Vec<_> = |
380 | vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)]; | |
381 | msg.extend(expected.0.iter().map(|x| match *x { | |
382 | StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), | |
383 | StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), | |
384 | })); | |
cc61c64b | 385 | msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); |
60c5eb7d | 386 | msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); |
dfeec247 XL |
387 | msg.extend(found.0.iter().map(|x| match *x { |
388 | StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), | |
389 | StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), | |
390 | })); | |
cc61c64b XL |
391 | msg.push((format!("`{}", found_extra), Style::NoStyle)); |
392 | ||
60c5eb7d | 393 | // For now, just attach these as notes. |
cc61c64b | 394 | self.highlighted_note(msg); |
c30ab7b3 SL |
395 | self |
396 | } | |
397 | ||
7cac9316 XL |
398 | pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self { |
399 | self.highlighted_note(vec![ | |
400 | (format!("`{}` from trait: `", name), Style::NoStyle), | |
401 | (signature, Style::Highlight), | |
dfeec247 XL |
402 | ("`".to_string(), Style::NoStyle), |
403 | ]); | |
7cac9316 XL |
404 | self |
405 | } | |
406 | ||
fc512014 | 407 | /// Add a note attached to this diagnostic. |
923072b8 FG |
408 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
409 | pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { | |
c30ab7b3 SL |
410 | self.sub(Level::Note, msg, MultiSpan::new(), None); |
411 | self | |
412 | } | |
413 | ||
923072b8 | 414 | pub fn highlighted_note<M: Into<SubdiagnosticMessage>>( |
04454e1e FG |
415 | &mut self, |
416 | msg: Vec<(M, Style)>, | |
417 | ) -> &mut Self { | |
32a655c1 SL |
418 | self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); |
419 | self | |
420 | } | |
421 | ||
e1599b0c | 422 | /// Prints the span with a note above it. |
fc512014 | 423 | /// This is like [`Diagnostic::note()`], but it gets its own span. |
923072b8 | 424 | pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { |
5e7ed085 FG |
425 | self.sub(Level::OnceNote, msg, MultiSpan::new(), None); |
426 | self | |
427 | } | |
428 | ||
429 | /// Prints the span with a note above it. | |
430 | /// This is like [`Diagnostic::note()`], but it gets its own span. | |
923072b8 | 431 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
04454e1e FG |
432 | pub fn span_note<S: Into<MultiSpan>>( |
433 | &mut self, | |
434 | sp: S, | |
923072b8 | 435 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 436 | ) -> &mut Self { |
c30ab7b3 SL |
437 | self.sub(Level::Note, msg, sp.into(), None); |
438 | self | |
439 | } | |
440 | ||
5e7ed085 FG |
441 | /// Prints the span with a note above it. |
442 | /// This is like [`Diagnostic::note()`], but it gets its own span. | |
04454e1e FG |
443 | pub fn span_note_once<S: Into<MultiSpan>>( |
444 | &mut self, | |
445 | sp: S, | |
923072b8 | 446 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 447 | ) -> &mut Self { |
5e7ed085 FG |
448 | self.sub(Level::OnceNote, msg, sp.into(), None); |
449 | self | |
450 | } | |
451 | ||
fc512014 | 452 | /// Add a warning attached to this diagnostic. |
923072b8 FG |
453 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
454 | pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { | |
455 | self.sub(Level::Warning(None), msg, MultiSpan::new(), None); | |
c30ab7b3 SL |
456 | self |
457 | } | |
458 | ||
fc512014 XL |
459 | /// Prints the span with a warning above it. |
460 | /// This is like [`Diagnostic::warn()`], but it gets its own span. | |
923072b8 | 461 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
04454e1e FG |
462 | pub fn span_warn<S: Into<MultiSpan>>( |
463 | &mut self, | |
464 | sp: S, | |
923072b8 | 465 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 466 | ) -> &mut Self { |
923072b8 | 467 | self.sub(Level::Warning(None), msg, sp.into(), None); |
c30ab7b3 SL |
468 | self |
469 | } | |
470 | ||
fc512014 | 471 | /// Add a help message attached to this diagnostic. |
923072b8 FG |
472 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
473 | pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { | |
c30ab7b3 SL |
474 | self.sub(Level::Help, msg, MultiSpan::new(), None); |
475 | self | |
476 | } | |
477 | ||
04454e1e FG |
478 | /// Add a help message attached to this diagnostic with a customizable highlighted message. |
479 | pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self { | |
480 | self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None); | |
481 | self | |
482 | } | |
483 | ||
e1599b0c | 484 | /// Prints the span with some help above it. |
fc512014 | 485 | /// This is like [`Diagnostic::help()`], but it gets its own span. |
923072b8 | 486 | #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)] |
04454e1e FG |
487 | pub fn span_help<S: Into<MultiSpan>>( |
488 | &mut self, | |
489 | sp: S, | |
923072b8 | 490 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 491 | ) -> &mut Self { |
c30ab7b3 SL |
492 | self.sub(Level::Help, msg, sp.into(), None); |
493 | self | |
494 | } | |
495 | ||
5e7ed085 FG |
496 | /// Help the user upgrade to the latest edition. |
497 | /// This is factored out to make sure it does the right thing with `Cargo.toml`. | |
498 | pub fn help_use_latest_edition(&mut self) -> &mut Self { | |
499 | if std::env::var_os("CARGO").is_some() { | |
500 | self.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); | |
501 | } else { | |
502 | self.help(&format!("pass `--edition {}` to `rustc`", LATEST_STABLE_EDITION)); | |
503 | } | |
504 | self.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); | |
505 | self | |
506 | } | |
507 | ||
5099ac24 FG |
508 | /// Disallow attaching suggestions this diagnostic. |
509 | /// Any suggestions attached e.g. with the `span_suggestion_*` methods | |
510 | /// (before and after the call to `disable_suggestions`) will be ignored. | |
511 | pub fn disable_suggestions(&mut self) -> &mut Self { | |
512 | self.suggestions = Err(SuggestionsDisabled); | |
513 | self | |
514 | } | |
515 | ||
516 | /// Helper for pushing to `self.suggestions`, if available (not disable). | |
517 | fn push_suggestion(&mut self, suggestion: CodeSuggestion) { | |
518 | if let Ok(suggestions) = &mut self.suggestions { | |
519 | suggestions.push(suggestion); | |
520 | } | |
521 | } | |
522 | ||
fc512014 XL |
523 | /// Show a suggestion that has multiple parts to it. |
524 | /// In other words, multiple changes need to be applied as part of this suggestion. | |
9fa01778 XL |
525 | pub fn multipart_suggestion( |
526 | &mut self, | |
923072b8 | 527 | msg: impl Into<SubdiagnosticMessage>, |
9fa01778 XL |
528 | suggestion: Vec<(Span, String)>, |
529 | applicability: Applicability, | |
17df50a5 XL |
530 | ) -> &mut Self { |
531 | self.multipart_suggestion_with_style( | |
532 | msg, | |
533 | suggestion, | |
534 | applicability, | |
535 | SuggestionStyle::ShowCode, | |
536 | ) | |
537 | } | |
538 | ||
c295e0f8 XL |
539 | /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. |
540 | /// In other words, multiple changes need to be applied as part of this suggestion. | |
541 | pub fn multipart_suggestion_verbose( | |
542 | &mut self, | |
923072b8 | 543 | msg: impl Into<SubdiagnosticMessage>, |
c295e0f8 XL |
544 | suggestion: Vec<(Span, String)>, |
545 | applicability: Applicability, | |
546 | ) -> &mut Self { | |
547 | self.multipart_suggestion_with_style( | |
548 | msg, | |
549 | suggestion, | |
550 | applicability, | |
551 | SuggestionStyle::ShowAlways, | |
552 | ) | |
553 | } | |
17df50a5 XL |
554 | /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. |
555 | pub fn multipart_suggestion_with_style( | |
556 | &mut self, | |
923072b8 | 557 | msg: impl Into<SubdiagnosticMessage>, |
17df50a5 XL |
558 | suggestion: Vec<(Span, String)>, |
559 | applicability: Applicability, | |
560 | style: SuggestionStyle, | |
9fa01778 | 561 | ) -> &mut Self { |
6a06907d | 562 | assert!(!suggestion.is_empty()); |
5099ac24 | 563 | self.push_suggestion(CodeSuggestion { |
9fa01778 XL |
564 | substitutions: vec![Substitution { |
565 | parts: suggestion | |
566 | .into_iter() | |
567 | .map(|(span, snippet)| SubstitutionPart { snippet, span }) | |
568 | .collect(), | |
569 | }], | |
923072b8 | 570 | msg: self.subdiagnostic_message_to_diagnostic_message(msg), |
17df50a5 | 571 | style, |
9fa01778 XL |
572 | applicability, |
573 | }); | |
574 | self | |
575 | } | |
576 | ||
577 | /// Prints out a message with for a multipart suggestion without showing the suggested code. | |
3b2f2976 | 578 | /// |
9fa01778 XL |
579 | /// This is intended to be used for suggestions that are obvious in what the changes need to |
580 | /// be from the message, showing the span label inline would be visually unpleasant | |
581 | /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't | |
582 | /// improve understandability. | |
583 | pub fn tool_only_multipart_suggestion( | |
584 | &mut self, | |
923072b8 | 585 | msg: impl Into<SubdiagnosticMessage>, |
9fa01778 XL |
586 | suggestion: Vec<(Span, String)>, |
587 | applicability: Applicability, | |
588 | ) -> &mut Self { | |
6a06907d | 589 | assert!(!suggestion.is_empty()); |
5099ac24 | 590 | self.push_suggestion(CodeSuggestion { |
abe05a73 | 591 | substitutions: vec![Substitution { |
9fa01778 XL |
592 | parts: suggestion |
593 | .into_iter() | |
594 | .map(|(span, snippet)| SubstitutionPart { snippet, span }) | |
595 | .collect(), | |
3b2f2976 | 596 | }], |
923072b8 | 597 | msg: self.subdiagnostic_message_to_diagnostic_message(msg), |
9fa01778 XL |
598 | style: SuggestionStyle::CompletelyHidden, |
599 | applicability, | |
3b2f2976 XL |
600 | }); |
601 | self | |
602 | } | |
603 | ||
c30ab7b3 SL |
604 | /// Prints out a message with a suggested edit of the code. |
605 | /// | |
9fa01778 | 606 | /// In case of short messages and a simple suggestion, rustc displays it as a label: |
041b39d2 | 607 | /// |
9fa01778 XL |
608 | /// ```text |
609 | /// try adding parentheses: `(tup.0).1` | |
610 | /// ``` | |
041b39d2 XL |
611 | /// |
612 | /// The message | |
abe05a73 | 613 | /// |
041b39d2 | 614 | /// * should not end in any punctuation (a `:` is added automatically) |
9fa01778 XL |
615 | /// * should not be a question (avoid language like "did you mean") |
616 | /// * should not contain any phrases like "the following", "as shown", etc. | |
041b39d2 | 617 | /// * may look like "to do xyz, use" or "to do xyz, use abc" |
9fa01778 | 618 | /// * may contain a name of a function, variable, or type, but not whole expressions |
041b39d2 | 619 | /// |
ea8adc8c | 620 | /// See `CodeSuggestion` for more information. |
e74abb32 XL |
621 | pub fn span_suggestion( |
622 | &mut self, | |
623 | sp: Span, | |
923072b8 | 624 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 625 | suggestion: impl ToString, |
e74abb32 XL |
626 | applicability: Applicability, |
627 | ) -> &mut Self { | |
628 | self.span_suggestion_with_style( | |
629 | sp, | |
630 | msg, | |
631 | suggestion, | |
632 | applicability, | |
633 | SuggestionStyle::ShowCode, | |
634 | ); | |
635 | self | |
636 | } | |
637 | ||
fc512014 | 638 | /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`]. |
e74abb32 XL |
639 | pub fn span_suggestion_with_style( |
640 | &mut self, | |
641 | sp: Span, | |
923072b8 | 642 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 643 | suggestion: impl ToString, |
e74abb32 XL |
644 | applicability: Applicability, |
645 | style: SuggestionStyle, | |
646 | ) -> &mut Self { | |
5099ac24 | 647 | self.push_suggestion(CodeSuggestion { |
abe05a73 | 648 | substitutions: vec![Substitution { |
04454e1e | 649 | parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }], |
7cac9316 | 650 | }], |
923072b8 | 651 | msg: self.subdiagnostic_message_to_diagnostic_message(msg), |
e74abb32 | 652 | style, |
0bf4aa26 | 653 | applicability, |
94b46f34 XL |
654 | }); |
655 | self | |
656 | } | |
657 | ||
fc512014 | 658 | /// Always show the suggested change. |
e74abb32 XL |
659 | pub fn span_suggestion_verbose( |
660 | &mut self, | |
661 | sp: Span, | |
923072b8 | 662 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 663 | suggestion: impl ToString, |
e74abb32 XL |
664 | applicability: Applicability, |
665 | ) -> &mut Self { | |
666 | self.span_suggestion_with_style( | |
667 | sp, | |
668 | msg, | |
669 | suggestion, | |
670 | applicability, | |
671 | SuggestionStyle::ShowAlways, | |
672 | ); | |
673 | self | |
674 | } | |
675 | ||
abe05a73 | 676 | /// Prints out a message with multiple suggested edits of the code. |
fc512014 | 677 | /// See also [`Diagnostic::span_suggestion()`]. |
e74abb32 XL |
678 | pub fn span_suggestions( |
679 | &mut self, | |
680 | sp: Span, | |
923072b8 | 681 | msg: impl Into<SubdiagnosticMessage>, |
e74abb32 XL |
682 | suggestions: impl Iterator<Item = String>, |
683 | applicability: Applicability, | |
684 | ) -> &mut Self { | |
3c0e092e XL |
685 | let mut suggestions: Vec<_> = suggestions.collect(); |
686 | suggestions.sort(); | |
687 | let substitutions = suggestions | |
688 | .into_iter() | |
689 | .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }) | |
690 | .collect(); | |
5099ac24 | 691 | self.push_suggestion(CodeSuggestion { |
3c0e092e | 692 | substitutions, |
923072b8 | 693 | msg: self.subdiagnostic_message_to_diagnostic_message(msg), |
9fa01778 XL |
694 | style: SuggestionStyle::ShowCode, |
695 | applicability, | |
2c00a5a8 XL |
696 | }); |
697 | self | |
698 | } | |
699 | ||
94222f64 XL |
700 | /// Prints out a message with multiple suggested edits of the code. |
701 | /// See also [`Diagnostic::span_suggestion()`]. | |
702 | pub fn multipart_suggestions( | |
703 | &mut self, | |
923072b8 | 704 | msg: impl Into<SubdiagnosticMessage>, |
94222f64 XL |
705 | suggestions: impl Iterator<Item = Vec<(Span, String)>>, |
706 | applicability: Applicability, | |
707 | ) -> &mut Self { | |
5099ac24 | 708 | self.push_suggestion(CodeSuggestion { |
94222f64 XL |
709 | substitutions: suggestions |
710 | .map(|sugg| Substitution { | |
711 | parts: sugg | |
712 | .into_iter() | |
713 | .map(|(span, snippet)| SubstitutionPart { snippet, span }) | |
714 | .collect(), | |
715 | }) | |
716 | .collect(), | |
923072b8 | 717 | msg: self.subdiagnostic_message_to_diagnostic_message(msg), |
94222f64 XL |
718 | style: SuggestionStyle::ShowCode, |
719 | applicability, | |
94222f64 XL |
720 | }); |
721 | self | |
722 | } | |
9fa01778 XL |
723 | /// Prints out a message with a suggested edit of the code. If the suggestion is presented |
724 | /// inline, it will only show the message and not the suggestion. | |
725 | /// | |
726 | /// See `CodeSuggestion` for more information. | |
727 | pub fn span_suggestion_short( | |
dfeec247 XL |
728 | &mut self, |
729 | sp: Span, | |
923072b8 | 730 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 731 | suggestion: impl ToString, |
dfeec247 | 732 | applicability: Applicability, |
9fa01778 | 733 | ) -> &mut Self { |
e74abb32 XL |
734 | self.span_suggestion_with_style( |
735 | sp, | |
736 | msg, | |
737 | suggestion, | |
83c7162d | 738 | applicability, |
e74abb32 XL |
739 | SuggestionStyle::HideCodeInline, |
740 | ); | |
2c00a5a8 XL |
741 | self |
742 | } | |
743 | ||
fc512014 | 744 | /// Prints out a message for a suggestion without showing the suggested code. |
9fa01778 XL |
745 | /// |
746 | /// This is intended to be used for suggestions that are obvious in what the changes need to | |
747 | /// be from the message, showing the span label inline would be visually unpleasant | |
748 | /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't | |
749 | /// improve understandability. | |
750 | pub fn span_suggestion_hidden( | |
dfeec247 XL |
751 | &mut self, |
752 | sp: Span, | |
923072b8 | 753 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 754 | suggestion: impl ToString, |
dfeec247 | 755 | applicability: Applicability, |
9fa01778 | 756 | ) -> &mut Self { |
e74abb32 XL |
757 | self.span_suggestion_with_style( |
758 | sp, | |
759 | msg, | |
760 | suggestion, | |
83c7162d | 761 | applicability, |
e74abb32 XL |
762 | SuggestionStyle::HideCodeAlways, |
763 | ); | |
c30ab7b3 SL |
764 | self |
765 | } | |
94b46f34 | 766 | |
fc512014 | 767 | /// Adds a suggestion to the JSON output that will not be shown in the CLI. |
9fa01778 XL |
768 | /// |
769 | /// This is intended to be used for suggestions that are *very* obvious in what the changes | |
770 | /// need to be from the message, but we still want other tools to be able to apply them. | |
771 | pub fn tool_only_span_suggestion( | |
dfeec247 XL |
772 | &mut self, |
773 | sp: Span, | |
923072b8 | 774 | msg: impl Into<SubdiagnosticMessage>, |
04454e1e | 775 | suggestion: impl ToString, |
dfeec247 | 776 | applicability: Applicability, |
94b46f34 | 777 | ) -> &mut Self { |
e74abb32 XL |
778 | self.span_suggestion_with_style( |
779 | sp, | |
780 | msg, | |
781 | suggestion, | |
dc9dc135 | 782 | applicability, |
e74abb32 XL |
783 | SuggestionStyle::CompletelyHidden, |
784 | ); | |
94b46f34 XL |
785 | self |
786 | } | |
c30ab7b3 | 787 | |
04454e1e FG |
788 | /// Add a subdiagnostic from a type that implements `SessionSubdiagnostic` - see |
789 | /// [rustc_macros::SessionSubdiagnostic]. | |
790 | pub fn subdiagnostic(&mut self, subdiagnostic: impl AddSubdiagnostic) -> &mut Self { | |
791 | subdiagnostic.add_to_diagnostic(self); | |
792 | self | |
6a06907d XL |
793 | } |
794 | ||
c30ab7b3 SL |
795 | pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { |
796 | self.span = sp.into(); | |
60c5eb7d XL |
797 | if let Some(span) = self.span.primary_span() { |
798 | self.sort_span = span; | |
799 | } | |
c30ab7b3 SL |
800 | self |
801 | } | |
802 | ||
c295e0f8 XL |
803 | pub fn set_is_lint(&mut self) -> &mut Self { |
804 | self.is_lint = true; | |
805 | self | |
806 | } | |
807 | ||
abe05a73 | 808 | pub fn code(&mut self, s: DiagnosticId) -> &mut Self { |
c30ab7b3 SL |
809 | self.code = Some(s); |
810 | self | |
811 | } | |
812 | ||
60c5eb7d XL |
813 | pub fn clear_code(&mut self) -> &mut Self { |
814 | self.code = None; | |
815 | self | |
816 | } | |
817 | ||
2c00a5a8 XL |
818 | pub fn get_code(&self) -> Option<DiagnosticId> { |
819 | self.code.clone() | |
820 | } | |
821 | ||
04454e1e | 822 | pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { |
60c5eb7d XL |
823 | self.message[0] = (msg.into(), Style::NoStyle); |
824 | self | |
825 | } | |
826 | ||
04454e1e FG |
827 | pub fn args(&self) -> &[DiagnosticArg<'static>] { |
828 | &self.args | |
32a655c1 SL |
829 | } |
830 | ||
04454e1e FG |
831 | pub fn set_arg( |
832 | &mut self, | |
833 | name: impl Into<Cow<'static, str>>, | |
834 | arg: impl IntoDiagnosticArg, | |
835 | ) -> &mut Self { | |
836 | self.args.push((name.into(), arg.into_diagnostic_arg())); | |
837 | self | |
838 | } | |
839 | ||
923072b8 | 840 | pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] { |
c30ab7b3 SL |
841 | &self.message |
842 | } | |
843 | ||
923072b8 FG |
844 | /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by |
845 | /// combining it with the primary message of the diagnostic (if translatable, otherwise it just | |
846 | /// passes the user's string along). | |
847 | fn subdiagnostic_message_to_diagnostic_message( | |
848 | &self, | |
849 | attr: impl Into<SubdiagnosticMessage>, | |
850 | ) -> DiagnosticMessage { | |
851 | let msg = | |
852 | self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); | |
853 | msg.with_subdiagnostic_message(attr.into()) | |
854 | } | |
855 | ||
c30ab7b3 SL |
856 | /// Convenience function for internal use, clients should use one of the |
857 | /// public methods above. | |
cdc7bbd5 XL |
858 | /// |
859 | /// Used by `proc_macro_server` for implementing `server::Diagnostic`. | |
dfeec247 XL |
860 | pub fn sub( |
861 | &mut self, | |
862 | level: Level, | |
923072b8 | 863 | message: impl Into<SubdiagnosticMessage>, |
dfeec247 XL |
864 | span: MultiSpan, |
865 | render_span: Option<MultiSpan>, | |
866 | ) { | |
c30ab7b3 | 867 | let sub = SubDiagnostic { |
3b2f2976 | 868 | level, |
923072b8 FG |
869 | message: vec![( |
870 | self.subdiagnostic_message_to_diagnostic_message(message), | |
871 | Style::NoStyle, | |
872 | )], | |
3b2f2976 XL |
873 | span, |
874 | render_span, | |
c30ab7b3 SL |
875 | }; |
876 | self.children.push(sub); | |
877 | } | |
32a655c1 SL |
878 | |
879 | /// Convenience function for internal use, clients should use one of the | |
880 | /// public methods above. | |
923072b8 | 881 | fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( |
dfeec247 XL |
882 | &mut self, |
883 | level: Level, | |
04454e1e | 884 | mut message: Vec<(M, Style)>, |
dfeec247 XL |
885 | span: MultiSpan, |
886 | render_span: Option<MultiSpan>, | |
887 | ) { | |
923072b8 FG |
888 | let message = message |
889 | .drain(..) | |
890 | .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) | |
891 | .collect(); | |
dfeec247 | 892 | let sub = SubDiagnostic { level, message, span, render_span }; |
32a655c1 SL |
893 | self.children.push(sub); |
894 | } | |
c295e0f8 XL |
895 | |
896 | /// Fields used for Hash, and PartialEq trait | |
897 | fn keys( | |
898 | &self, | |
899 | ) -> ( | |
900 | &Level, | |
923072b8 | 901 | &[(DiagnosticMessage, Style)], |
c295e0f8 XL |
902 | &Option<DiagnosticId>, |
903 | &MultiSpan, | |
5099ac24 | 904 | &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, |
923072b8 | 905 | Option<&[SubDiagnostic]>, |
c295e0f8 XL |
906 | ) { |
907 | ( | |
908 | &self.level, | |
909 | &self.message, | |
910 | &self.code, | |
911 | &self.span, | |
912 | &self.suggestions, | |
913 | (if self.is_lint { None } else { Some(&self.children) }), | |
914 | ) | |
915 | } | |
916 | } | |
917 | ||
918 | impl Hash for Diagnostic { | |
919 | fn hash<H>(&self, state: &mut H) | |
920 | where | |
921 | H: Hasher, | |
922 | { | |
923 | self.keys().hash(state); | |
924 | } | |
925 | } | |
926 | ||
927 | impl PartialEq for Diagnostic { | |
928 | fn eq(&self, other: &Self) -> bool { | |
929 | self.keys() == other.keys() | |
930 | } | |
32a655c1 | 931 | } |