#![feature(if_let_guard)]
#![feature(let_else)]
#![feature(nll)]
-#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
+#![allow(rustc::potential_query_instability)]
#[macro_use]
extern crate rustc_macros;
pub use emitter::ColorConfig;
+use rustc_lint_defs::LintExpectationId;
use Level::*;
use emitter::{is_case_difference, Emitter, EmitterWriter};
mod styled_buffer;
pub use snippet::Style;
-pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
+pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a, ErrorReported>>;
// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
// (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.)
deduplicated_warn_count: usize,
future_breakage_diagnostics: Vec<Diagnostic>,
+
+ /// Lint [`Diagnostic`]s can be expected as described in [RFC-2383]. An
+ /// expected diagnostic will have the level `Expect` which additionally
+ /// carries the [`LintExpectationId`] of the expectation that can be
+ /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s
+ /// that have been marked as fulfilled this way.
+ ///
+ /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
+ fulfilled_expectations: FxHashSet<LintExpectationId>,
}
/// A key denoting where from a diagnostic was stashed.
self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
}
+ // FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
+ // They're `delayed_span_bugs` but for "require some diagnostic happened"
+ // instead of "require some error happened". Sadly that isn't ideal, as
+ // lints can be `#[allow]`'d, potentially leading to this triggering.
+ // Also, "good path" should be replaced with a better naming.
if !self.has_any_message() {
let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
self.flush_delayed(
- bugs.into_iter().map(DelayedDiagnostic::decorate).collect(),
+ bugs.into_iter().map(DelayedDiagnostic::decorate),
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
);
}
emitted_diagnostics: Default::default(),
stashed_diagnostics: Default::default(),
future_breakage_diagnostics: Vec::new(),
+ fulfilled_expectations: Default::default(),
}),
}
}
}
/// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
- pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> {
+ pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
self.inner
.borrow_mut()
.stashed_diagnostics
self.inner.borrow_mut().emit_stashed_diagnostics();
}
- /// Construct a dummy builder with `Level::Cancelled`.
- ///
- /// Using this will neither report anything to the user (e.g. a warning),
- /// nor will compilation cancel as a result.
- pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
- DiagnosticBuilder::new(self, Level::Cancelled, "")
- }
-
- /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
- ///
- /// The builder will be canceled if warnings cannot be emitted.
- pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
- let mut result = self.struct_warn(msg);
- result.set_span(span);
- result
- }
-
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
///
- /// This will "force" the warning meaning it will not be canceled even
- /// if warnings cannot be emitted.
- pub fn struct_span_force_warn(
+ /// Attempting to `.emit()` the builder will only emit if either:
+ /// * `can_emit_warnings` is `true`
+ /// * `is_force_warn` was set in `DiagnosticId::Lint`
+ pub fn struct_span_warn(
&self,
span: impl Into<MultiSpan>,
msg: &str,
- ) -> DiagnosticBuilder<'_> {
- let mut result = self.struct_force_warn(msg);
+ ) -> DiagnosticBuilder<'_, ()> {
+ let mut result = self.struct_warn(msg);
result.set_span(span);
result
}
&self,
span: impl Into<MultiSpan>,
msg: &str,
- ) -> DiagnosticBuilder<'_> {
+ ) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_allow(msg);
result.set_span(span);
result
span: impl Into<MultiSpan>,
msg: &str,
code: DiagnosticId,
- ) -> DiagnosticBuilder<'_> {
+ ) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_span_warn(span, msg);
result.code(code);
result
/// Construct a builder at the `Warning` level with the `msg`.
///
- /// The builder will be canceled if warnings cannot be emitted.
- pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
- let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
- if !self.flags.can_emit_warnings {
- result.cancel();
- }
- result
- }
-
- /// Construct a builder at the `Warning` level with the `msg`.
- ///
- /// This will "force" a warning meaning it will not be canceled even
- /// if warnings cannot be emitted.
- pub fn struct_force_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
+ /// Attempting to `.emit()` the builder will only emit if either:
+ /// * `can_emit_warnings` is `true`
+ /// * `is_force_warn` was set in `DiagnosticId::Lint`
+ pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Warning, msg)
}
/// Construct a builder at the `Allow` level with the `msg`.
- pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Allow, msg)
}
+ /// Construct a builder at the `Expect` level with the `msg`.
+ pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> {
+ DiagnosticBuilder::new(self, Level::Expect(id), msg)
+ }
+
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
- pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn struct_span_err(
+ &self,
+ span: impl Into<MultiSpan>,
+ msg: &str,
+ ) -> DiagnosticBuilder<'_, ErrorReported> {
let mut result = self.struct_err(msg);
result.set_span(span);
result
span: impl Into<MultiSpan>,
msg: &str,
code: DiagnosticId,
- ) -> DiagnosticBuilder<'_> {
+ ) -> DiagnosticBuilder<'_, ErrorReported> {
let mut result = self.struct_span_err(span, msg);
result.code(code);
result
/// Construct a builder at the `Error` level with the `msg`.
// FIXME: This method should be removed (every error should have an associated error code).
- pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> {
- DiagnosticBuilder::new(self, Level::Error { lint: false }, msg)
+ pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
+ DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg)
}
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
#[doc(hidden)]
- pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
}
/// Construct a builder at the `Error` level with the `msg` and the `code`.
- pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> {
+ pub fn struct_err_with_code(
+ &self,
+ msg: &str,
+ code: DiagnosticId,
+ ) -> DiagnosticBuilder<'_, ErrorReported> {
let mut result = self.struct_err(msg);
result.code(code);
result
&self,
span: impl Into<MultiSpan>,
msg: &str,
- ) -> DiagnosticBuilder<'_> {
+ ) -> DiagnosticBuilder<'_, ErrorReported> {
let mut result = self.struct_fatal(msg);
result.set_span(span);
result
span: impl Into<MultiSpan>,
msg: &str,
code: DiagnosticId,
- ) -> DiagnosticBuilder<'_> {
+ ) -> DiagnosticBuilder<'_, ErrorReported> {
let mut result = self.struct_span_fatal(span, msg);
result.code(code);
result
}
/// Construct a builder at the `Error` level with the `msg`.
- pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
- DiagnosticBuilder::new(self, Level::Fatal, msg)
+ pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
+ DiagnosticBuilder::new_guaranteeing_error::<{ Level::Fatal }>(self, msg)
}
/// Construct a builder at the `Help` level with the `msg`.
- pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Help, msg)
}
/// Construct a builder at the `Note` level with the `msg`.
- pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Note, msg)
}
self.inner.borrow_mut().delay_span_bug(span, msg)
}
+ // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
+ // where the explanation of what "good path" is (also, it should be renamed).
pub fn delay_good_path_bug(&self, msg: &str) {
self.inner.borrow_mut().delay_good_path_bug(msg)
}
self.emit_diag_at_span(Diagnostic::new(Note, msg), span);
}
- pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> {
+ pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> {
let mut db = DiagnosticBuilder::new(self, Note, msg);
db.set_span(span);
db
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
}
- pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
- self.inner.borrow_mut().delay_as_bug(diagnostic)
+ /// This methods steals all [`LintExpectationId`]s that are stored inside
+ /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled.
+ pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet<LintExpectationId> {
+ std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
}
}
diags.iter().for_each(|diag| self.emit_diagnostic(diag));
}
+ // FIXME(eddyb) this should ideally take `diagnostic` by value.
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
- if diagnostic.cancelled() {
- return;
+ if diagnostic.level == Level::DelayedBug {
+ // FIXME(eddyb) this should check for `has_errors` and stop pushing
+ // once *any* errors were emitted (and truncate `delayed_span_bugs`
+ // when an error is first emitted, also), but maybe there's a case
+ // in which that's not sound? otherwise this is really inefficient.
+ self.delayed_span_bugs.push(diagnostic.clone());
+
+ if !self.flags.report_delayed_bugs {
+ return;
+ }
}
if diagnostic.has_future_breakage() {
(*TRACK_DIAGNOSTICS)(diagnostic);
- if diagnostic.level == Allow {
+ if let Level::Expect(expectation_id) = diagnostic.level {
+ self.fulfilled_expectations.insert(expectation_id);
+ return;
+ } else if diagnostic.level == Allow {
return;
}
// FIXME: don't abort here if report_delayed_bugs is off
self.span_bug(sp, msg);
}
- let mut diagnostic = Diagnostic::new(Level::Bug, msg);
+ let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
diagnostic.set_span(sp.into());
diagnostic.note(&format!("delayed at {}", std::panic::Location::caller()));
- self.delay_as_bug(diagnostic)
+ self.emit_diagnostic(&diagnostic)
}
+ // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
+ // where the explanation of what "good path" is (also, it should be renamed).
fn delay_good_path_bug(&mut self, msg: &str) {
- let diagnostic = Diagnostic::new(Level::Bug, msg);
+ let diagnostic = Diagnostic::new(Level::DelayedBug, msg);
if self.flags.report_delayed_bugs {
self.emit_diagnostic(&diagnostic);
}
panic::panic_any(ExplicitBug);
}
- fn delay_as_bug(&mut self, diagnostic: Diagnostic) {
- if self.flags.report_delayed_bugs {
- self.emit_diagnostic(&diagnostic);
- }
- self.delayed_span_bugs.push(diagnostic);
- }
+ fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) {
+ let mut no_bugs = true;
+ for mut bug in bugs {
+ if no_bugs {
+ // Put the overall explanation before the `DelayedBug`s, to
+ // frame them better (e.g. separate warnings from them).
+ self.emit_diagnostic(&Diagnostic::new(Bug, explanation));
+ no_bugs = false;
+ }
+
+ // "Undelay" the `DelayedBug`s (into plain `Bug`s).
+ if bug.level != Level::DelayedBug {
+ // NOTE(eddyb) not panicking here because we're already producing
+ // an ICE, and the more information the merrier.
+ bug.note(&format!(
+ "`flushed_delayed` got diagnostic with level {:?}, \
+ instead of the expected `DelayedBug`",
+ bug.level,
+ ));
+ }
+ bug.level = Level::Bug;
- fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) {
- let has_bugs = !bugs.is_empty();
- for bug in bugs {
self.emit_diagnostic(&bug);
}
- if has_bugs {
- panic!("{}", explanation);
+
+ // Panic with `ExplicitBug` to avoid "unexpected panic" messages.
+ if !no_bugs {
+ panic::panic_any(ExplicitBug);
}
}
}
}
-#[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)]
+#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
pub enum Level {
Bug,
+ DelayedBug,
Fatal,
Error {
/// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
Warning,
Note,
Help,
- Cancelled,
FailureNote,
Allow,
+ Expect(LintExpectationId),
}
impl fmt::Display for Level {
fn color(self) -> ColorSpec {
let mut spec = ColorSpec::new();
match self {
- Bug | Fatal | Error { .. } => {
+ Bug | DelayedBug | Fatal | Error { .. } => {
spec.set_fg(Some(Color::Red)).set_intense(true);
}
Warning => {
spec.set_fg(Some(Color::Cyan)).set_intense(true);
}
FailureNote => {}
- Allow | Cancelled => unreachable!(),
+ Allow | Expect(_) => unreachable!(),
}
spec
}
pub fn to_str(self) -> &'static str {
match self {
- Bug => "error: internal compiler error",
+ Bug | DelayedBug => "error: internal compiler error",
Fatal | Error { .. } => "error",
Warning => "warning",
Note => "note",
Help => "help",
FailureNote => "failure-note",
- Cancelled => panic!("Shouldn't call on cancelled error"),
Allow => panic!("Shouldn't call on allowed error"),
+ Expect(_) => panic!("Shouldn't call on expected error"),
}
}
}
}
+// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
pub fn add_elided_lifetime_in_path_suggestion(
source_map: &SourceMap,
- db: &mut DiagnosticBuilder<'_>,
+ diag: &mut Diagnostic,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
(insertion_span, anon_lts)
}
};
- db.span_suggestion(
+ diag.span_suggestion(
replace_span,
&format!("indicate the anonymous lifetime{}", pluralize!(n)),
suggestion,