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