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