Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ use rustc_parse_format::{
Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
};
use rustc_session::lint::builtin::{
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FILTERS,
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
};
use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};

use crate::context::AcceptContext;
use crate::diagnostics::{
DupesNotAllowed, FormatWarning, IgnoredDiagnosticOption, InvalidOnClause,
MalFormedDiagnosticAttributeLint, MissingOptionsForDiagnosticAttribute,
NonMetaItemDiagnosticAttribute, WrappedParserError,
FormatWarning, IgnoredDiagnosticOption, InvalidOnClause, MalFormedDiagnosticAttributeLint,
MissingOptionsForDiagnosticAttribute, NonMetaItemDiagnosticAttribute, WrappedParserError,
};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};

Expand Down Expand Up @@ -129,13 +129,31 @@ fn merge_directives(
later: (Span, Directive),
) {
if let Some((_, first)) = first {
if first.is_rustc_attr || later.1.is_rustc_attr {
cx.emit_err(DupesNotAllowed);
let Directive { is_rustc_attr, filters, message, label, notes, parent_label } = later.1;

// Evaluation visits every filter before the root directive. Appending filters and notes
// preserves their source order across separate attribute occurrences.
first.is_rustc_attr |= is_rustc_attr;
first.filters.extend(filters);
merge(cx, &mut first.message, message, sym::message);
merge(cx, &mut first.label, label, sym::label);
first.notes.extend(notes);

if let Some(parent_label) = parent_label {
if let Some(first_parent_label) = &first.parent_label {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
IgnoredDiagnosticOption {
first_span: first_parent_label.span,
later_span: parent_label.span,
option_name: sym::parent_label,
},
parent_label.span,
);
} else {
first.parent_label = Some(parent_label);
}
}

merge(cx, &mut first.message, later.1.message, sym::message);
merge(cx, &mut first.label, later.1.label, sym::label);
first.notes.extend(later.1.notes);
} else {
*first = Some(later);
}
Expand Down Expand Up @@ -215,7 +233,7 @@ fn parse_directive_items<'p>(
let mut message: Option<(Span, _)> = None;
let mut label: Option<(Span, _)> = None;
let mut notes = ThinVec::new();
let mut parent_label = None;
let mut parent_label: Option<FormatString> = None;
let mut filters = ThinVec::new();

for item in items {
Expand Down Expand Up @@ -346,10 +364,10 @@ fn parse_directive_items<'p>(
}
(Mode::RustcOnUnimplemented, sym::parent_label) => {
let value = or_malformed!(value?);
if parent_label.is_none() {
parent_label = Some(parse_format(value));
if let Some(parent_label) = &parent_label {
duplicate!(name, parent_label.span)
} else {
duplicate!(name, span)
parent_label = Some(parse_format(value));
}
}
(Mode::RustcOnUnimplemented, sym::on) => {
Expand All @@ -359,7 +377,11 @@ fn parse_directive_items<'p>(
let filter = if let Some(c) = iter.next() {
c
} else {
cx.emit_err(InvalidOnClause::Empty { span });
cx.emit_lint(
MALFORMED_DIAGNOSTIC_FILTERS,
InvalidOnClause::Empty { span },
span,
);
continue;
};

Expand All @@ -378,7 +400,7 @@ fn parse_directive_items<'p>(
filters.push((filter, directive));
}
Err(e) => {
cx.emit_err(e);
cx.emit_lint(MALFORMED_DIAGNOSTIC_FILTERS, e, span);
}
}
} else {
Expand Down
20 changes: 7 additions & 13 deletions compiler/rustc_attr_parsing/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_errors::{Applicability, DiagArgValue, E0232, E0264, MultiSpan};
use rustc_errors::{Applicability, DiagArgValue, E0264, MultiSpan};
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};
Expand Down Expand Up @@ -764,39 +764,39 @@ pub(crate) mod unexpected_cfg_value {

#[derive(Diagnostic)]
pub(crate) enum InvalidOnClause {
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`")]
Empty {
#[primary_span]
#[label("empty `on`-clause here")]
span: Span,
},
#[diag("expected a single predicate in `not(..)`", code = E0232)]
#[diag("expected a single predicate in `not(..)`")]
ExpectedOnePredInNot {
#[primary_span]
#[label("unexpected quantity of predicates here")]
span: Span,
},
#[diag("literals inside `on`-clauses are not supported", code = E0232)]
#[diag("literals inside `on`-clauses are not supported")]
UnsupportedLiteral {
#[primary_span]
#[label("unexpected literal here")]
span: Span,
},
#[diag("expected an identifier inside this `on`-clause", code = E0232)]
#[diag("expected an identifier inside this `on`-clause")]
ExpectedIdentifier {
#[primary_span]
#[label("expected an identifier here, not `{$path}`")]
span: Span,
path: AttrPath,
},
#[diag("this predicate is invalid", code = E0232)]
#[diag("this predicate is invalid")]
InvalidPredicate {
#[primary_span]
#[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")]
span: Span,
invalid_pred: Symbol,
},
#[diag("invalid flag in `on`-clause", code = E0232)]
#[diag("invalid flag in `on`-clause")]
InvalidFlag {
#[primary_span]
#[label(
Expand All @@ -807,12 +807,6 @@ pub(crate) enum InvalidOnClause {
},
}

#[derive(Diagnostic)]
#[diag(
"using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported"
)]
pub(crate) struct DupesNotAllowed;

#[derive(Diagnostic)]
#[diag("usage of the unsafe `{$attr_path}` attribute")]
#[note("{$note}")]
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_error_codes/src/error_codes/E0232.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#### Note: this error code is no longer emitted by the compiler.

The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. The attribute will let you filter on
various types, with `on`:

```compile_fail,E0232
```ignore (error is no longer emitted)
#![feature(rustc_attrs)]
#![allow(internal_features)]
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub mod hardwired {
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
MACRO_USE_EXTERN_CRATE,
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_FILTERS,
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
META_VARIABLE_MISUSE,
METHOD_CALL_ON_DIVERGING_INFER_VAR,
Expand Down Expand Up @@ -4542,6 +4543,35 @@ declare_lint! {
"detects diagnostic attribute with malformed diagnostic format literals",
}

declare_lint! {
/// The `malformed_diagnostic_filters` lint detects malformed filters in diagnostic
/// attributes.
///
/// ### Example
///
// FIXME(bootstrap): Use a regular Rust doc code block after stage 0 emits
// `malformed_diagnostic_filters` instead of E0232 for this example.
#[cfg_attr(bootstrap, doc = "```rust,ignore (stage 0 emits E0232)")]
#[cfg_attr(not(bootstrap), doc = "```rust")]
/// #![feature(rustc_attrs)]
/// #![allow(internal_features)]
///
/// #[rustc_on_unimplemented(on(invalid, message = "unused"))]
/// trait Trait {}
#[doc = "```"]
///
/// {{produces}}
///
/// ### Explanation
///
/// A `rustc_on_unimplemented` filter must use a supported flag, a name-value predicate,
/// or the `all`, `any`, and `not` predicate operators. Invalid filters are ignored.
pub MALFORMED_DIAGNOSTIC_FILTERS,
Warn,
"detects malformed filters in diagnostic attributes",
@feature_gate = rustc_attrs;
}

declare_lint! {
/// The `ambiguous_glob_imports` lint detects glob imports that should report ambiguity
/// errors, but previously didn't do that due to rustc bugs.
Expand Down
25 changes: 15 additions & 10 deletions tests/ui/on-unimplemented/bad-annotation.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Check that every malformed `on` filter warns without stopping compilation.

//@ check-pass

#![crate_type = "lib"]
#![feature(rustc_attrs)]
#![allow(unused)]
Expand Down Expand Up @@ -44,12 +48,13 @@ trait Invalid {}
trait DuplicateMessage {}

#[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))]
//~^ ERROR invalid flag in `on`-clause
//~^ WARN invalid flag in `on`-clause
//~| NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `desugared`
//~| NOTE `#[warn(malformed_diagnostic_filters)]` on by default
trait OnInWrongPosition {}

#[rustc_on_unimplemented(on(), message = "y")]
//~^ ERROR empty `on`-clause
//~^ WARN empty `on`-clause
//~^^ NOTE empty `on`-clause here
trait EmptyOn {}

Expand All @@ -69,42 +74,42 @@ trait OnWithoutDirectives {}
trait NestedOn {}

#[rustc_on_unimplemented(on("y", message = "y"))]
//~^ ERROR literals inside `on`-clauses are not supported
//~^ WARN literals inside `on`-clauses are not supported
//~^^ NOTE unexpected literal here
trait UnsupportedLiteral {}

#[rustc_on_unimplemented(on(42, message = "y"))]
//~^ ERROR literals inside `on`-clauses are not supported
//~^ WARN literals inside `on`-clauses are not supported
//~^^ NOTE unexpected literal here
trait UnsupportedLiteral2 {}

#[rustc_on_unimplemented(on(not(a, b), message = "y"))]
//~^ ERROR expected a single predicate in `not(..)` [E0232]
//~^ WARN expected a single predicate in `not(..)`
//~^^ NOTE unexpected quantity of predicates here
trait ExpectedOnePattern {}

#[rustc_on_unimplemented(on(not(), message = "y"))]
//~^ ERROR expected a single predicate in `not(..)` [E0232]
//~^ WARN expected a single predicate in `not(..)`
//~^^ NOTE unexpected quantity of predicates here
trait ExpectedOnePattern2 {}

#[rustc_on_unimplemented(on(thing::What, message = "y"))]
//~^ ERROR expected an identifier inside this `on`-clause
//~^ WARN expected an identifier inside this `on`-clause
//~^^ NOTE expected an identifier here, not `thing::What`
trait KeyMustBeIdentifier {}

#[rustc_on_unimplemented(on(thing::What = "value", message = "y"))]
//~^ ERROR expected an identifier inside this `on`-clause
//~^ WARN expected an identifier inside this `on`-clause
//~^^ NOTE expected an identifier here, not `thing::What`
trait KeyMustBeIdentifier2 {}

#[rustc_on_unimplemented(on(aaaaaaaaaaaaaa(a, b), message = "y"))]
//~^ ERROR this predicate is invalid
//~^ WARN this predicate is invalid
//~^^ NOTE expected one of `any`, `all` or `not` here, not `aaaaaaaaaaaaaa`
trait InvalidPredicate {}

#[rustc_on_unimplemented(on(something, message = "y"))]
//~^ ERROR invalid flag in `on`-clause
//~^ WARN invalid flag in `on`-clause
//~^^ NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something`
trait InvalidFlag {}

Expand Down
Loading
Loading