mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Introduce a #[diagnostic::on_unknown_item] attribute
This PR introduces a `#[diagnostic::on_unknown_item]` attribute that allows crate authors to customize the error messages emitted by unresolved imports. The main usecase for this is using this attribute as part of a proc macro that expects a certain external module structure to exist or certain dependencies to be there. For me personally the motivating use-case are several derives in diesel, that expect to refer to a `tabe` module. That is done either implicitly (via the name of the type with the derive) or explicitly by the user. This attribute would allow us to improve the error message in both cases: * For the implicit case we could explicity call out our assumptions (turning the name into lower case, adding an `s` in the end) + point to the explicit variant as alternative * For the explicit variant we would add additional notes to tell the user why this is happening and what they should look for to fix the problem (be more explicit about certain diesel specific assumptions of the module structure) I assume that similar use-cases exist for other proc-macros as well, therefore I decided to put in the work implementing this new attribute. I would also assume that this is likely not useful for std-lib internal usage.
This commit is contained in:
@@ -649,7 +649,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate)
|
||||
AttributeParser::parse_limited(
|
||||
sess,
|
||||
&krate.attrs,
|
||||
sym::feature,
|
||||
&[sym::feature],
|
||||
DUMMY_SP,
|
||||
krate.id,
|
||||
Some(&features),
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
pub(crate) mod on_const;
|
||||
pub(crate) mod on_move;
|
||||
pub(crate) mod on_unimplemented;
|
||||
pub(crate) mod on_unknown_item;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum Mode {
|
||||
@@ -35,6 +36,8 @@ pub(crate) enum Mode {
|
||||
DiagnosticOnConst,
|
||||
/// `#[diagnostic::on_move]`
|
||||
DiagnosticOnMove,
|
||||
/// `#[diagnostic::on_unknown_item]`
|
||||
DiagnosticOnUnknownItem,
|
||||
}
|
||||
|
||||
fn merge_directives<S: Stage>(
|
||||
@@ -122,6 +125,13 @@ fn parse_directive_items<'p, S: Stage>(
|
||||
span,
|
||||
);
|
||||
}
|
||||
Mode::DiagnosticOnUnknownItem => {
|
||||
cx.emit_lint(
|
||||
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
AttributeLintKind::MalformedOnUnknownItemdAttr { span },
|
||||
span,
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}}
|
||||
@@ -140,7 +150,7 @@ fn parse_directive_items<'p, S: Stage>(
|
||||
Mode::RustcOnUnimplemented => {
|
||||
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
|
||||
}
|
||||
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => {
|
||||
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnknownItem => {
|
||||
cx.emit_lint(
|
||||
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
AttributeLintKind::IgnoredDiagnosticOption {
|
||||
@@ -176,7 +186,8 @@ fn parse_directive_items<'p, S: Stage>(
|
||||
Ok((f, warnings)) => {
|
||||
for warning in warnings {
|
||||
let (FormatWarning::InvalidSpecifier { span, .. }
|
||||
| FormatWarning::PositionalArgument { span, .. }) = warning;
|
||||
| FormatWarning::PositionalArgument { span, .. }
|
||||
| FormatWarning::DisallowedPlaceholder { span }) = warning;
|
||||
cx.emit_lint(
|
||||
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
|
||||
AttributeLintKind::MalformedDiagnosticFormat { warning },
|
||||
@@ -326,6 +337,10 @@ fn parse_arg(
|
||||
is_source_literal: bool,
|
||||
) -> FormatArg {
|
||||
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
|
||||
if matches!(mode, Mode::DiagnosticOnUnknownItem) {
|
||||
warnings.push(FormatWarning::DisallowedPlaceholder { span });
|
||||
return FormatArg::AsIs(sym::empty_braces);
|
||||
}
|
||||
|
||||
match arg.position {
|
||||
// Something like "hello {name}"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use rustc_hir::attrs::diagnostic::Directive;
|
||||
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||
|
||||
use crate::attributes::diagnostic::*;
|
||||
use crate::attributes::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct OnUnknownItemParser {
|
||||
span: Option<Span>,
|
||||
directive: Option<(Span, Directive)>,
|
||||
}
|
||||
|
||||
impl OnUnknownItemParser {
|
||||
fn parse<'sess, S: Stage>(
|
||||
&mut self,
|
||||
cx: &mut AcceptContext<'_, 'sess, S>,
|
||||
args: &ArgParser,
|
||||
mode: Mode,
|
||||
) {
|
||||
let span = cx.attr_span;
|
||||
self.span = Some(span);
|
||||
|
||||
let items = match args {
|
||||
ArgParser::List(items) if !items.is_empty() => items,
|
||||
ArgParser::NoArgs | ArgParser::List(_) => {
|
||||
cx.emit_lint(
|
||||
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
AttributeLintKind::MissingOptionsForOnUnknownItem,
|
||||
span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
cx.emit_lint(
|
||||
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||
AttributeLintKind::MalformedOnUnknownItemdAttr { span },
|
||||
span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) {
|
||||
merge_directives(cx, &mut self.directive, (span, directive));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for OnUnknownItemParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::diagnostic, sym::on_unknown_item],
|
||||
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|
||||
|this, cx, args| {
|
||||
this.parse(cx, args, Mode::DiagnosticOnUnknownItem);
|
||||
},
|
||||
)];
|
||||
//FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs`
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(span) = self.span {
|
||||
Some(AttributeKind::OnUnknownItem {
|
||||
span,
|
||||
directive: self.directive.map(|d| Box::new(d.1)),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
use crate::attributes::diagnostic::on_const::*;
|
||||
use crate::attributes::diagnostic::on_move::*;
|
||||
use crate::attributes::diagnostic::on_unimplemented::*;
|
||||
use crate::attributes::diagnostic::on_unknown_item::*;
|
||||
use crate::attributes::doc::*;
|
||||
use crate::attributes::dummy::*;
|
||||
use crate::attributes::inline::*;
|
||||
@@ -154,6 +155,7 @@ mod late {
|
||||
OnConstParser,
|
||||
OnMoveParser,
|
||||
OnUnimplementedParser,
|
||||
OnUnknownItemParser,
|
||||
RustcAlignParser,
|
||||
RustcAlignStaticParser,
|
||||
RustcCguTestAttributeParser,
|
||||
|
||||
@@ -29,7 +29,7 @@ pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
/// *Only* parse attributes with this symbol.
|
||||
///
|
||||
/// Used in cases where we want the lowering infrastructure for parse just a single attribute.
|
||||
parse_only: Option<Symbol>,
|
||||
parse_only: Option<&'static [Symbol]>,
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Early> {
|
||||
@@ -50,7 +50,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
sym: &'static [Symbol],
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
@@ -72,7 +72,7 @@ pub fn parse_limited(
|
||||
pub fn parse_limited_should_emit(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
sym: Symbol,
|
||||
sym: &'static [Symbol],
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
target: Target,
|
||||
@@ -103,7 +103,7 @@ pub fn parse_limited_should_emit(
|
||||
pub fn parse_limited_all(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
parse_only: Option<Symbol>,
|
||||
parse_only: Option<&'static [Symbol]>,
|
||||
target: Target,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
@@ -272,7 +272,7 @@ pub fn parse_attribute_list(
|
||||
for attr in attrs {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
if !attr.has_name(expected) {
|
||||
if !attr.path_matches(expected) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ pub(crate) fn expand_ext(
|
||||
match item {
|
||||
Annotatable::Item(item) => {
|
||||
let is_packed = matches!(
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id, None),
|
||||
AttributeParser::parse_limited(cx.sess, &item.attrs, &[sym::repr], item.span, item.id, None),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
|
||||
);
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ fn collect_custom_derive(
|
||||
})) = AttributeParser::parse_limited(
|
||||
self.session,
|
||||
slice::from_ref(attr),
|
||||
sym::proc_macro_derive,
|
||||
&[sym::proc_macro_derive],
|
||||
item.span,
|
||||
item.node_id(),
|
||||
None,
|
||||
|
||||
@@ -483,7 +483,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
|
||||
AttributeParser::parse_limited(
|
||||
cx.sess,
|
||||
&i.attrs,
|
||||
sym::should_panic,
|
||||
&[sym::should_panic],
|
||||
i.span,
|
||||
i.node_id(),
|
||||
None,
|
||||
|
||||
@@ -391,7 +391,7 @@ fn get_test_runner(sess: &Session, features: &Features, krate: &ast::Crate) -> O
|
||||
match AttributeParser::parse_limited(
|
||||
sess,
|
||||
&krate.attrs,
|
||||
sym::test_runner,
|
||||
&[sym::test_runner],
|
||||
krate.spans.inner_span,
|
||||
krate.id,
|
||||
Some(features),
|
||||
|
||||
@@ -54,7 +54,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
||||
AttributeParser::parse_limited(
|
||||
sess,
|
||||
krate_attrs,
|
||||
sym::feature,
|
||||
&[sym::feature],
|
||||
DUMMY_SP,
|
||||
DUMMY_NODE_ID,
|
||||
Some(&features),
|
||||
|
||||
@@ -1588,6 +1588,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool
|
||||
sym::on_unimplemented | sym::do_not_recommend => true,
|
||||
sym::on_const => features.diagnostic_on_const(),
|
||||
sym::on_move => features.diagnostic_on_move(),
|
||||
sym::on_unknown_item => features.diagnostic_on_unknown_item(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,6 +474,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(unstable, diagnostic_on_const, "1.93.0", Some(143874)),
|
||||
/// Allows giving on-move borrowck custom diagnostic messages for a type
|
||||
(unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(154181)),
|
||||
/// Allows giving unresolved imports a custom diagnostic message
|
||||
(unstable, diagnostic_on_unknown_item, "CURRENT_RUSTC_VERSION", Some(152900)),
|
||||
/// Allows `#[doc(cfg(...))]`.
|
||||
(unstable, doc_cfg, "1.21.0", Some(43781)),
|
||||
/// Allows `#[doc(masked)]`.
|
||||
|
||||
@@ -1192,6 +1192,14 @@ pub enum AttributeKind {
|
||||
/// None if the directive was malformed in some way.
|
||||
directive: Option<Box<Directive>>,
|
||||
},
|
||||
|
||||
/// Represents `#[diagnostic::on_unknown_item]`
|
||||
OnUnknownItem {
|
||||
span: Span,
|
||||
/// None if the directive was malformed in some way.
|
||||
directive: Option<Box<Directive>>,
|
||||
},
|
||||
|
||||
/// Represents `#[optimize(size|speed)]`
|
||||
Optimize(OptimizeAttr, Span),
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
|
||||
OnConst { .. } => Yes,
|
||||
OnMove { .. } => Yes,
|
||||
OnUnimplemented { .. } => Yes,
|
||||
OnUnknownItem { .. } => Yes,
|
||||
Optimize(..) => No,
|
||||
PanicRuntime => No,
|
||||
PatchableFunctionEntry { .. } => Yes,
|
||||
|
||||
@@ -1369,7 +1369,7 @@ pub(crate) fn parse_crate_name(
|
||||
AttributeParser::parse_limited_should_emit(
|
||||
sess,
|
||||
attrs,
|
||||
sym::crate_name,
|
||||
&[sym::crate_name],
|
||||
DUMMY_SP,
|
||||
rustc_ast::node_id::CRATE_NODE_ID,
|
||||
Target::Crate,
|
||||
@@ -1419,7 +1419,7 @@ pub fn collect_crate_types(
|
||||
AttributeParser::<Early>::parse_limited_should_emit(
|
||||
session,
|
||||
attrs,
|
||||
sym::crate_type,
|
||||
&[sym::crate_type],
|
||||
crate_span,
|
||||
CRATE_NODE_ID,
|
||||
Target::Crate,
|
||||
@@ -1476,7 +1476,7 @@ fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit
|
||||
let attr = AttributeParser::parse_limited_should_emit(
|
||||
sess,
|
||||
&krate_attrs,
|
||||
sym::recursion_limit,
|
||||
&[sym::recursion_limit],
|
||||
DUMMY_SP,
|
||||
rustc_ast::node_id::CRATE_NODE_ID,
|
||||
Target::Crate,
|
||||
|
||||
@@ -312,7 +312,7 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
||||
AttributeParser::parse_limited(
|
||||
cx.builder.sess(),
|
||||
&it.attrs,
|
||||
sym::allow_internal_unsafe,
|
||||
&[sym::allow_internal_unsafe],
|
||||
it.span,
|
||||
DUMMY_NODE_ID,
|
||||
Some(cx.builder.features()),
|
||||
|
||||
@@ -179,6 +179,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
|
||||
&AttributeLintKind::MalformedOnUnimplementedAttr { span } => {
|
||||
lints::MalformedOnUnimplementedAttrLint { span }.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::MalformedOnUnknownItemdAttr { span } => {
|
||||
lints::MalformedOnUnknownItemAttrLint { span }.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::MalformedOnConstAttr { span } => {
|
||||
lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level)
|
||||
}
|
||||
@@ -189,6 +192,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
|
||||
FormatWarning::InvalidSpecifier { .. } => {
|
||||
lints::InvalidFormatSpecifier.into_diag(dcx, level)
|
||||
}
|
||||
FormatWarning::DisallowedPlaceholder { .. } => {
|
||||
lints::DisallowedPlaceholder.into_diag(dcx, level)
|
||||
}
|
||||
},
|
||||
AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => {
|
||||
lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level)
|
||||
@@ -215,6 +221,9 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
|
||||
&AttributeLintKind::MissingOptionsForOnMove => {
|
||||
lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::MissingOptionsForOnUnknownItem => {
|
||||
lints::MissingOptionsForOnUnknownItemAttr.into_diag(dcx, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3542,6 +3542,11 @@ pub(crate) struct UnknownCrateTypesSuggestion {
|
||||
)]
|
||||
pub(crate) struct DisallowedPositionalArgument;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("format arguments are not allowed here")]
|
||||
#[help("consider removing this format argument")]
|
||||
pub(crate) struct DisallowedPlaceholder;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("invalid format specifier")]
|
||||
#[help("no format specifier are supported in this position")]
|
||||
@@ -3571,6 +3576,11 @@ pub(crate) struct IgnoredDiagnosticOption {
|
||||
#[help("at least one of the `message`, `note` and `label` options are expected")]
|
||||
pub(crate) struct MissingOptionsForOnUnimplementedAttr;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("missing options for `on_unknown_item` attribute")]
|
||||
#[help("at least one of the `message`, `note` and `label` options are expected")]
|
||||
pub(crate) struct MissingOptionsForOnUnknownItemAttr;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("missing options for `on_const` attribute")]
|
||||
#[help("at least one of the `message`, `note` and `label` options are expected")]
|
||||
@@ -3589,6 +3599,14 @@ pub(crate) struct MalformedOnUnimplementedAttrLint {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("malformed `on_unknown_item` attribute")]
|
||||
#[help("only `message`, `note` and `label` are allowed as options")]
|
||||
pub(crate) struct MalformedOnUnknownItemAttrLint {
|
||||
#[label("invalid option found here")]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("malformed `on_const` attribute")]
|
||||
#[help("only `message`, `note` and `label` are allowed as options")]
|
||||
|
||||
@@ -145,7 +145,7 @@ fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
|
||||
impl EarlyLintPass for NonCamelCaseTypes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
|
||||
let has_repr_c = matches!(
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id, None),
|
||||
AttributeParser::parse_limited(cx.sess(), &it.attrs, &[sym::repr], it.span, it.id, None),
|
||||
Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC)
|
||||
);
|
||||
|
||||
|
||||
@@ -736,6 +736,9 @@ pub enum AttributeLintKind {
|
||||
MalformedOnUnimplementedAttr {
|
||||
span: Span,
|
||||
},
|
||||
MalformedOnUnknownItemdAttr {
|
||||
span: Span,
|
||||
},
|
||||
MalformedOnConstAttr {
|
||||
span: Span,
|
||||
},
|
||||
@@ -757,6 +760,7 @@ pub enum AttributeLintKind {
|
||||
},
|
||||
MissingOptionsForOnUnimplemented,
|
||||
MissingOptionsForOnConst,
|
||||
MissingOptionsForOnUnknownItem,
|
||||
MissingOptionsForOnMove,
|
||||
OnMoveMalformedFormatLiterals {
|
||||
name: Symbol,
|
||||
@@ -768,6 +772,7 @@ pub enum AttributeLintKind {
|
||||
pub enum FormatWarning {
|
||||
PositionalArgument { span: Span, help: String },
|
||||
InvalidSpecifier { name: String, span: Span },
|
||||
DisallowedPlaceholder { span: Span },
|
||||
}
|
||||
|
||||
pub type RegisteredTools = FxIndexSet<Ident>;
|
||||
|
||||
@@ -74,6 +74,13 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls {
|
||||
#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")]
|
||||
struct DiagnosticOnMoveOnlyForAdt;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#[diagnostic::on_unknown_item]` can only be applied to `use` statements")]
|
||||
struct DiagnosticOnUnknownItemOnlyForImports {
|
||||
#[label("not an import")]
|
||||
item_span: Span,
|
||||
}
|
||||
|
||||
fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
|
||||
match impl_item.kind {
|
||||
hir::ImplItemKind::Const(..) => Target::AssocConst,
|
||||
@@ -219,6 +226,7 @@ fn check_attributes(
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)},
|
||||
Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())},
|
||||
Attribute::Parsed(AttributeKind::OnUnknownItem { span, .. }) => { self.check_diagnostic_on_unknown_item(*span, hir_id, target) },
|
||||
Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)}
|
||||
Attribute::Parsed(AttributeKind::OnMove { span, directive }) => {
|
||||
self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref())
|
||||
@@ -727,6 +735,19 @@ fn check_diagnostic_on_move(
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `#[diagnostic::on_unknown_item]` is applied to a trait impl
|
||||
fn check_diagnostic_on_unknown_item(&self, attr_span: Span, hir_id: HirId, target: Target) {
|
||||
if !matches!(target, Target::Use) {
|
||||
let item_span = self.tcx.hir_span(hir_id);
|
||||
self.tcx.emit_node_span_lint(
|
||||
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr_span,
|
||||
DiagnosticOnUnknownItemOnlyForImports { item_span },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an `#[inline]` is applied to a function or a closure.
|
||||
fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) {
|
||||
match target {
|
||||
|
||||
@@ -25,7 +25,7 @@ fn check_for_debugger_visualizer(
|
||||
AttributeParser::parse_limited(
|
||||
&self.sess,
|
||||
attrs,
|
||||
sym::debugger_visualizer,
|
||||
&[sym::debugger_visualizer],
|
||||
span,
|
||||
node_id,
|
||||
None,
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
use crate::Namespace::{MacroNS, TypeNS, ValueNS};
|
||||
use crate::def_collector::collect_definitions;
|
||||
use crate::imports::{ImportData, ImportKind};
|
||||
use crate::imports::{ImportData, ImportKind, OnUnknownItemData};
|
||||
use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef};
|
||||
use crate::ref_mut::CmCell;
|
||||
use crate::{
|
||||
@@ -545,6 +545,7 @@ fn add_import(
|
||||
root_id,
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item),
|
||||
});
|
||||
|
||||
self.r.indeterminate_imports.push(import);
|
||||
@@ -1026,6 +1027,7 @@ fn build_reduced_graph_for_extern_crate(
|
||||
module_path: Vec::new(),
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item),
|
||||
});
|
||||
if used {
|
||||
self.r.import_use_map.insert(import, Used::Other);
|
||||
@@ -1121,7 +1123,7 @@ fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> boo
|
||||
AttributeParser::parse_limited(
|
||||
self.r.tcx.sess,
|
||||
&item.attrs,
|
||||
sym::macro_use,
|
||||
&[sym::macro_use],
|
||||
item.span,
|
||||
item.id,
|
||||
None,
|
||||
@@ -1158,6 +1160,7 @@ fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> boo
|
||||
module_path: Vec::new(),
|
||||
vis: Visibility::Restricted(CRATE_DEF_ID),
|
||||
vis_span: item.vis.span,
|
||||
on_unknown_item_attr: OnUnknownItemData::from_attrs(this.r.tcx, item),
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1329,6 +1332,7 @@ fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'ra> {
|
||||
module_path: Vec::new(),
|
||||
vis,
|
||||
vis_span: item.vis.span,
|
||||
on_unknown_item_attr: OnUnknownItemData::from_attrs(self.r.tcx, item),
|
||||
});
|
||||
self.r.import_use_map.insert(import, Used::Other);
|
||||
let import_decl = self.r.new_import_decl(decl, import);
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
|
||||
use std::mem;
|
||||
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_ast::{Item, NodeId};
|
||||
use rustc_attr_parsing::AttributeParser;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan, pluralize, struct_span_code_err};
|
||||
use rustc_hir::Attribute;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
|
||||
use rustc_hir::def::{self, DefKind, PartialRes};
|
||||
use rustc_hir::def_id::{DefId, LocalDefIdMap};
|
||||
use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::Visibility;
|
||||
use rustc_middle::ty::{TyCtxt, Visibility};
|
||||
use rustc_session::lint::builtin::{
|
||||
AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS,
|
||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS,
|
||||
@@ -140,6 +144,30 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct OnUnknownItemData {
|
||||
directive: Directive,
|
||||
}
|
||||
|
||||
impl OnUnknownItemData {
|
||||
pub(crate) fn from_attrs<'tcx>(tcx: TyCtxt<'tcx>, item: &Item) -> Option<OnUnknownItemData> {
|
||||
if let Some(Attribute::Parsed(AttributeKind::OnUnknownItem { directive, .. })) =
|
||||
AttributeParser::parse_limited(
|
||||
tcx.sess,
|
||||
&item.attrs,
|
||||
&[sym::diagnostic, sym::on_unknown_item],
|
||||
item.span,
|
||||
item.id,
|
||||
Some(tcx.features()),
|
||||
)
|
||||
{
|
||||
Some(Self { directive: *directive? })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One import.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ImportData<'ra> {
|
||||
@@ -186,6 +214,11 @@ pub(crate) struct ImportData<'ra> {
|
||||
|
||||
/// Span of the visibility.
|
||||
pub vis_span: Span,
|
||||
|
||||
/// A `#[diagnostic::on_unknown_item]` attribute applied
|
||||
/// to the given import. This allows crates to specify
|
||||
/// custom error messages for a specific import
|
||||
pub on_unknown_item_attr: Option<OnUnknownItemData>,
|
||||
}
|
||||
|
||||
/// All imports are unique and allocated on a same arena,
|
||||
@@ -284,6 +317,7 @@ struct UnresolvedImportError {
|
||||
segment: Option<Symbol>,
|
||||
/// comes from `PathRes::Failed { module }`
|
||||
module: Option<DefId>,
|
||||
on_unknown_item_attr: Option<OnUnknownItemData>,
|
||||
}
|
||||
|
||||
// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;`
|
||||
@@ -700,6 +734,7 @@ pub(crate) fn finalize_imports(&mut self) {
|
||||
candidates: None,
|
||||
segment: None,
|
||||
module: None,
|
||||
on_unknown_item_attr: import.on_unknown_item_attr.clone(),
|
||||
};
|
||||
errors.push((*import, err))
|
||||
}
|
||||
@@ -822,11 +857,41 @@ fn throw_unresolved_import_error(
|
||||
format!("`{path}`")
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
|
||||
let default_message =
|
||||
format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
|
||||
let (message, label, notes) = if self.tcx.features().diagnostic_on_unknown_item()
|
||||
&& let Some(directive) = errors[0].1.on_unknown_item_attr.as_ref().map(|a| &a.directive)
|
||||
{
|
||||
let args = FormatArgs {
|
||||
this: paths.join(", "),
|
||||
// Unused
|
||||
this_sugared: String::new(),
|
||||
// Unused
|
||||
item_context: "",
|
||||
// Unused
|
||||
generic_args: Vec::new(),
|
||||
};
|
||||
let CustomDiagnostic { message, label, notes, .. } = directive.eval(None, &args);
|
||||
|
||||
let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{msg}");
|
||||
(message, label, notes)
|
||||
} else {
|
||||
(None, None, Vec::new())
|
||||
};
|
||||
let has_custom_message = message.is_some();
|
||||
let message = message.as_deref().unwrap_or(default_message.as_str());
|
||||
|
||||
if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() {
|
||||
let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{message}");
|
||||
if has_custom_message {
|
||||
diag.note(default_message);
|
||||
}
|
||||
|
||||
if !notes.is_empty() {
|
||||
for note in notes {
|
||||
diag.note(note);
|
||||
}
|
||||
} else if let Some((_, UnresolvedImportError { note: Some(note), .. })) =
|
||||
errors.iter().last()
|
||||
{
|
||||
diag.note(note.clone());
|
||||
}
|
||||
|
||||
@@ -834,8 +899,10 @@ fn throw_unresolved_import_error(
|
||||
const MAX_LABEL_COUNT: usize = 10;
|
||||
|
||||
for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
|
||||
if let Some(label) = err.label {
|
||||
diag.span_label(err.span, label);
|
||||
if let Some(label) = &label {
|
||||
diag.span_label(err.span, label.clone());
|
||||
} else if let Some(label) = &err.label {
|
||||
diag.span_label(err.span, label.clone());
|
||||
}
|
||||
|
||||
if let Some((suggestions, msg, applicability)) = err.suggestion {
|
||||
@@ -1101,6 +1168,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
|
||||
candidates: None,
|
||||
segment: Some(segment_name),
|
||||
module,
|
||||
on_unknown_item_attr: import.on_unknown_item_attr.clone(),
|
||||
},
|
||||
None => UnresolvedImportError {
|
||||
span,
|
||||
@@ -1110,6 +1178,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
|
||||
candidates: None,
|
||||
segment: Some(segment_name),
|
||||
module,
|
||||
on_unknown_item_attr: import.on_unknown_item_attr.clone(),
|
||||
},
|
||||
};
|
||||
return Some(err);
|
||||
@@ -1152,6 +1221,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
|
||||
candidates: None,
|
||||
segment: None,
|
||||
module: None,
|
||||
on_unknown_item_attr: None,
|
||||
});
|
||||
}
|
||||
if let Some(max_vis) = max_vis.get()
|
||||
@@ -1374,6 +1444,7 @@ fn finalize_import(&mut self, import: Import<'ra>) -> Option<UnresolvedImportErr
|
||||
}
|
||||
}),
|
||||
segment: Some(ident.name),
|
||||
on_unknown_item_attr: import.on_unknown_item_attr.clone(),
|
||||
})
|
||||
} else {
|
||||
// `resolve_ident_in_module` reported a privacy error.
|
||||
|
||||
@@ -140,7 +140,7 @@ pub fn registered_tools_ast(
|
||||
AttributeParser::parse_limited(
|
||||
sess,
|
||||
pre_configured_attrs,
|
||||
sym::register_tool,
|
||||
&[sym::register_tool],
|
||||
DUMMY_SP,
|
||||
DUMMY_NODE_ID,
|
||||
Some(features),
|
||||
@@ -712,17 +712,27 @@ fn smart_resolve_macro_path(
|
||||
feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit();
|
||||
}
|
||||
|
||||
const DIAG_ATTRS: &[Symbol] =
|
||||
&[sym::on_unimplemented, sym::do_not_recommend, sym::on_const, sym::on_move];
|
||||
let diagnostic_attributes: &[(Symbol, bool)] = &[
|
||||
(sym::on_unimplemented, true),
|
||||
(sym::do_not_recommend, true),
|
||||
(sym::on_move, true),
|
||||
(sym::on_const, self.tcx.features().diagnostic_on_const()),
|
||||
(sym::on_unknown_item, self.tcx.features().diagnostic_on_unknown_item()),
|
||||
];
|
||||
|
||||
if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
|
||||
&& let [namespace, attribute, ..] = &*path.segments
|
||||
&& namespace.ident.name == sym::diagnostic
|
||||
&& !DIAG_ATTRS.contains(&attribute.ident.name)
|
||||
&& !diagnostic_attributes
|
||||
.iter()
|
||||
.any(|(attr, stable)| *stable && attribute.ident.name == *attr)
|
||||
{
|
||||
let span = attribute.span();
|
||||
|
||||
let typo = find_best_match_for_name(DIAG_ATTRS, attribute.ident.name, Some(5))
|
||||
let candidates = diagnostic_attributes
|
||||
.iter()
|
||||
.filter_map(|(sym, stable)| stable.then_some(*sym))
|
||||
.collect::<Vec<_>>();
|
||||
let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5))
|
||||
.map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name });
|
||||
|
||||
self.tcx.sess.psess.buffer_lint(
|
||||
|
||||
@@ -799,6 +799,7 @@
|
||||
diagnostic_namespace,
|
||||
diagnostic_on_const,
|
||||
diagnostic_on_move,
|
||||
diagnostic_on_unknown_item,
|
||||
dialect,
|
||||
direct,
|
||||
discriminant_kind,
|
||||
@@ -1418,6 +1419,7 @@
|
||||
on_const,
|
||||
on_move,
|
||||
on_unimplemented,
|
||||
on_unknown_item,
|
||||
opaque,
|
||||
opaque_module_name_placeholder: "<opaque>",
|
||||
ops,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
//@ run-pass
|
||||
#![allow(dead_code, unused_imports)]
|
||||
#![feature(diagnostic_on_unknown_item)]
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
extern crate std as other_std;
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
const CONST: () = ();
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
static STATIC: () = ();
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
type Type = ();
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
enum Enum {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
impl Enum {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
extern "C" {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
fn fun() {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
struct Struct {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
trait Trait {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
//~^WARN `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
impl Trait for i32 {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo")]
|
||||
use std::str::FromStr;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,103 @@
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:5:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | extern crate std as other_std;
|
||||
| ----------------------------- not an import
|
||||
|
|
||||
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:9:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | const CONST: () = ();
|
||||
| --------------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:13:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | static STATIC: () = ();
|
||||
| ----------------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:17:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | type Type = ();
|
||||
| --------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:21:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | enum Enum {}
|
||||
| --------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:25:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | impl Enum {}
|
||||
| --------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:29:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | extern "C" {}
|
||||
| ------------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:33:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | fn fun() {}
|
||||
| -------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:37:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | struct Struct {}
|
||||
| ------------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:41:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | trait Trait {}
|
||||
| ----------- not an import
|
||||
|
||||
warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements
|
||||
--> $DIR/incorrect-locations.rs:45:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL |
|
||||
LL | impl Trait for i32 {}
|
||||
| ------------------ not an import
|
||||
|
||||
warning: 11 warnings emitted
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#![feature(diagnostic_on_unknown_item)]
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo {}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist;
|
||||
//~^ ERROR: foo {}
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "foo {A}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist2;
|
||||
//~^ ERROR: foo {}
|
||||
|
||||
#[diagnostic::on_unknown_item(label = "foo {}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist3;
|
||||
//~^ ERROR: unresolved import `std::does_not_exist3`
|
||||
|
||||
#[diagnostic::on_unknown_item(label = "foo {A}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist4;
|
||||
//~^ ERROR: unresolved import `std::does_not_exist4`
|
||||
|
||||
#[diagnostic::on_unknown_item(note = "foo {}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist5;
|
||||
//~^ ERROR: unresolved import `std::does_not_exist5`
|
||||
|
||||
#[diagnostic::on_unknown_item(note = "foo {A}")]
|
||||
//~^ WARN: format arguments are not allowed here
|
||||
use std::does_not_exist6;
|
||||
//~^ ERROR: unresolved import `std::does_not_exist6`
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,96 @@
|
||||
error[E0432]: foo {}
|
||||
--> $DIR/incorrect_format_string.rs:5:5
|
||||
|
|
||||
LL | use std::does_not_exist;
|
||||
| ^^^^^^^^^^^^^^^^^^^ no `does_not_exist` in the root
|
||||
|
|
||||
= note: unresolved import `std::does_not_exist`
|
||||
|
||||
error[E0432]: foo {}
|
||||
--> $DIR/incorrect_format_string.rs:10:5
|
||||
|
|
||||
LL | use std::does_not_exist2;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist2` in the root
|
||||
|
|
||||
= note: unresolved import `std::does_not_exist2`
|
||||
|
||||
error[E0432]: unresolved import `std::does_not_exist3`
|
||||
--> $DIR/incorrect_format_string.rs:15:5
|
||||
|
|
||||
LL | use std::does_not_exist3;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ foo {}
|
||||
|
||||
error[E0432]: unresolved import `std::does_not_exist4`
|
||||
--> $DIR/incorrect_format_string.rs:20:5
|
||||
|
|
||||
LL | use std::does_not_exist4;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ foo {}
|
||||
|
||||
error[E0432]: unresolved import `std::does_not_exist5`
|
||||
--> $DIR/incorrect_format_string.rs:25:5
|
||||
|
|
||||
LL | use std::does_not_exist5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist5` in the root
|
||||
|
|
||||
= note: foo {}
|
||||
|
||||
error[E0432]: unresolved import `std::does_not_exist6`
|
||||
--> $DIR/incorrect_format_string.rs:30:5
|
||||
|
|
||||
LL | use std::does_not_exist6;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ no `does_not_exist6` in the root
|
||||
|
|
||||
= note: foo {}
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:3:47
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo {}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:8:47
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "foo {A}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:13:45
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(label = "foo {}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:18:45
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(label = "foo {A}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:23:44
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(note = "foo {}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
|
||||
warning: format arguments are not allowed here
|
||||
--> $DIR/incorrect_format_string.rs:28:44
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(note = "foo {A}")]
|
||||
| ^
|
||||
|
|
||||
= help: consider removing this format argument
|
||||
|
||||
error: aborting due to 6 previous errors; 6 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0432`.
|
||||
@@ -0,0 +1,19 @@
|
||||
#![feature(diagnostic_on_unknown_item)]
|
||||
#[diagnostic::on_unknown_item]
|
||||
//~^WARN missing options for `on_unknown_item` attribute
|
||||
use std::str::FromStr;
|
||||
|
||||
#[diagnostic::on_unknown_item(foo = "bar", message = "foo")]
|
||||
//~^WARN malformed `on_unknown_item` attribute
|
||||
use std::str::Bytes;
|
||||
|
||||
#[diagnostic::on_unknown_item(label = "foo", label = "bar")]
|
||||
//~^WARN `label` is ignored due to previous definition of `label`
|
||||
use std::str::Chars;
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "Foo", message = "Bar")]
|
||||
//~^WARN `message` is ignored due to previous definition of `message`
|
||||
use std::str::NotExisting;
|
||||
//~^ERROR Foo
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,44 @@
|
||||
error[E0432]: Foo
|
||||
--> $DIR/malformed_attribute.rs:16:5
|
||||
|
|
||||
LL | use std::str::NotExisting;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `str`
|
||||
|
|
||||
= note: unresolved import `std::str::NotExisting`
|
||||
|
||||
warning: missing options for `on_unknown_item` attribute
|
||||
--> $DIR/malformed_attribute.rs:2:1
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: at least one of the `message`, `note` and `label` options are expected
|
||||
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
|
||||
|
||||
warning: malformed `on_unknown_item` attribute
|
||||
--> $DIR/malformed_attribute.rs:6:31
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(foo = "bar", message = "foo")]
|
||||
| ^^^^^^^^^^^ invalid option found here
|
||||
|
|
||||
= help: only `message`, `note` and `label` are allowed as options
|
||||
|
||||
warning: `label` is ignored due to previous definition of `label`
|
||||
--> $DIR/malformed_attribute.rs:10:46
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(label = "foo", label = "bar")]
|
||||
| ------------- ^^^^^^^^^^^^^ `label` is later redundantly declared here
|
||||
| |
|
||||
| `label` is first declared here
|
||||
|
||||
warning: `message` is ignored due to previous definition of `message`
|
||||
--> $DIR/malformed_attribute.rs:14:48
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "Foo", message = "Bar")]
|
||||
| --------------- ^^^^^^^^^^^^^^^ `message` is later redundantly declared here
|
||||
| |
|
||||
| `message` is first declared here
|
||||
|
||||
error: aborting due to 1 previous error; 4 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0432`.
|
||||
@@ -0,0 +1,48 @@
|
||||
#![feature(diagnostic_on_unknown_item)]
|
||||
|
||||
mod test1 {
|
||||
#[diagnostic::on_unknown_item(
|
||||
message = "custom message",
|
||||
label = "custom label",
|
||||
note = "custom note"
|
||||
)]
|
||||
use std::vec::{NonExisting, Vec, Whatever};
|
||||
//~^ ERROR: custom message
|
||||
}
|
||||
|
||||
mod test2 {
|
||||
#[diagnostic::on_unknown_item(
|
||||
message = "custom message",
|
||||
label = "custom label",
|
||||
note = "custom note"
|
||||
)]
|
||||
use std::{Whatever, vec::NonExisting, vec::Vec, *};
|
||||
//~^ ERROR: custom message
|
||||
}
|
||||
|
||||
mod test3 {
|
||||
#[diagnostic::on_unknown_item(
|
||||
message = "custom message",
|
||||
label = "custom label",
|
||||
note = "custom note"
|
||||
)]
|
||||
use std::{
|
||||
string::String,
|
||||
vec::{NonExisting, Vec},
|
||||
//~^ ERROR: custom message
|
||||
};
|
||||
}
|
||||
|
||||
mod test4 {
|
||||
#[diagnostic::on_unknown_item(
|
||||
message = "custom message",
|
||||
label = "custom label",
|
||||
note = "custom note"
|
||||
)]
|
||||
use std::{
|
||||
string::String,
|
||||
vec::{Vec, non_existing::*},
|
||||
//~^ ERROR: custom message
|
||||
};
|
||||
}
|
||||
fn main() {}
|
||||
@@ -0,0 +1,43 @@
|
||||
error[E0432]: custom message
|
||||
--> $DIR/multiple_errors.rs:9:20
|
||||
|
|
||||
LL | use std::vec::{NonExisting, Vec, Whatever};
|
||||
| ^^^^^^^^^^^ ^^^^^^^^ custom label
|
||||
| |
|
||||
| custom label
|
||||
|
|
||||
= note: unresolved imports `std::vec::NonExisting`, `std::vec::Whatever`
|
||||
= note: custom note
|
||||
|
||||
error[E0432]: custom message
|
||||
--> $DIR/multiple_errors.rs:19:15
|
||||
|
|
||||
LL | use std::{Whatever, vec::NonExisting, vec::Vec, *};
|
||||
| ^^^^^^^^ ^^^^^^^^^^^^^^^^ custom label
|
||||
| |
|
||||
| custom label
|
||||
|
|
||||
= note: unresolved imports `std::Whatever`, `std::vec::NonExisting`
|
||||
= note: custom note
|
||||
|
||||
error[E0432]: custom message
|
||||
--> $DIR/multiple_errors.rs:31:15
|
||||
|
|
||||
LL | vec::{NonExisting, Vec},
|
||||
| ^^^^^^^^^^^ custom label
|
||||
|
|
||||
= note: unresolved import `std::vec::NonExisting`
|
||||
= note: custom note
|
||||
|
||||
error[E0432]: custom message
|
||||
--> $DIR/multiple_errors.rs:44:20
|
||||
|
|
||||
LL | vec::{Vec, non_existing::*},
|
||||
| ^^^^^^^^^^^^ custom label
|
||||
|
|
||||
= note: unresolved import `std::vec::non_existing`
|
||||
= note: custom note
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0432`.
|
||||
@@ -0,0 +1,17 @@
|
||||
#![feature(diagnostic_on_unknown_item)]
|
||||
pub mod foo {
|
||||
pub struct Bar;
|
||||
}
|
||||
|
||||
#[diagnostic::on_unknown_item(
|
||||
message = "first message",
|
||||
label = "first label",
|
||||
note = "custom note",
|
||||
note = "custom note 2"
|
||||
)]
|
||||
use foo::Foo;
|
||||
//~^ERROR first message
|
||||
|
||||
use foo::Bar;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,13 @@
|
||||
error[E0432]: first message
|
||||
--> $DIR/unknown_import.rs:12:5
|
||||
|
|
||||
LL | use foo::Foo;
|
||||
| ^^^^^^^^ first label
|
||||
|
|
||||
= note: unresolved import `foo::Foo`
|
||||
= note: custom note
|
||||
= note: custom note 2
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0432`.
|
||||
@@ -0,0 +1,8 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
#[diagnostic::on_unknown_item(message = "Tada")]
|
||||
//~^ ERROR: unknown diagnostic attribute
|
||||
use std::vec::NotExisting;
|
||||
//~^ ERROR: unresolved import `std::vec::NotExisting`
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,22 @@
|
||||
error[E0432]: unresolved import `std::vec::NotExisting`
|
||||
--> $DIR/feature-gate-diagnostic-on-unknown-item.rs:5:5
|
||||
|
|
||||
LL | use std::vec::NotExisting;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ no `NotExisting` in `vec`
|
||||
|
||||
error: unknown diagnostic attribute
|
||||
--> $DIR/feature-gate-diagnostic-on-unknown-item.rs:3:15
|
||||
|
|
||||
LL | #[diagnostic::on_unknown_item(message = "Tada")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/feature-gate-diagnostic-on-unknown-item.rs:1:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(unknown_diagnostic_attributes)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0432`.
|
||||
Reference in New Issue
Block a user