diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 830eb3d6d8170..849e60153cead 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -662,7 +662,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), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 63780676bead9..fd5516ff25c4e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -24,6 +24,7 @@ pub(crate) mod do_not_recommend; 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( @@ -123,6 +126,13 @@ fn parse_directive_items<'p, S: Stage>( span, ); } + Mode::DiagnosticOnUnknownItem => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnknownItemdAttr { span }, + span, + ); + } } continue; }} @@ -142,7 +152,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 { @@ -173,7 +183,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 }, diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs new file mode 100644 index 0000000000000..f2a243a8e0bba --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown_item.rs @@ -0,0 +1,73 @@ +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, + directive: Option<(Span, Directive)>, +} + +impl OnUnknownItemParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + if !cx.features().diagnostic_on_unknown_item() { + return; + } + 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 AttributeParser for OnUnknownItemParser { + const ATTRIBUTES: AcceptMapping = &[( + &[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-use statements but diagnostics are issued in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnUnknownItem { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 259a73de59853..c8dbc8a8ca39f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -31,6 +31,7 @@ use crate::attributes::diagnostic::do_not_recommend::*; 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::*; @@ -152,6 +153,7 @@ attribute_parsers!( OnConstParser, OnMoveParser, OnUnimplementedParser, + OnUnknownItemParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f75f63a0e811a..3449cc486676a 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -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, + 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>, @@ -71,7 +71,7 @@ impl<'sess> AttributeParser<'sess, Early> { pub fn parse_limited_should_emit( sess: &'sess Session, attrs: &[ast::Attribute], - sym: Symbol, + sym: &'static [Symbol], target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, @@ -101,7 +101,7 @@ impl<'sess> AttributeParser<'sess, Early> { pub fn parse_limited_all( sess: &'sess Session, attrs: &[ast::Attribute], - parse_only: Option, + parse_only: Option<&'static [Symbol]>, target: Target, target_span: Span, target_node_id: NodeId, @@ -275,7 +275,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { 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; } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7bbce730c1658..f4817f93b1102 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -164,9 +164,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { generic_args, }; ( - directive.message.as_ref().map(|e| e.1.format(&args)), - directive.label.as_ref().map(|e| e.1.format(&args)), - directive.notes.iter().map(|e| e.format(&args)).collect(), + directive.message.as_ref().map(|e| e.1.format(Some(&args))), + directive.label.as_ref().map(|e| e.1.format(Some(&args))), + directive.notes.iter().map(|e| e.format(Some(&args))).collect(), ) } else { (None, None, ThinVec::new()) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index b392a9623d050..18b1fafbe8754 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -493,7 +493,7 @@ impl<'a> TraitDef<'a> { 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(..))) ); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 24a5d79958c60..84f2a8e35b02c 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -108,7 +108,7 @@ impl<'a> CollectProcMacros<'a> { })) = AttributeParser::parse_limited( self.session, slice::from_ref(attr), - sym::proc_macro_derive, + &[sym::proc_macro_derive], item.span, item.node_id(), None, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 5764dfc83927a..071b807109b71 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -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, diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 1bb6d8a6bfd05..1c947ea07d1a9 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -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), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8e4039b32d942..df058aa5dac70 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -53,7 +53,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), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d74450e67cf1d..f9f70bb7e455a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e8ca20d7f5f44..054cf474a3701 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -474,6 +474,8 @@ declare_features! ( (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(150935)), + /// 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)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a18ddff947099..6fa3df2dfa7d1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1192,6 +1192,14 @@ pub enum AttributeKind { /// None if the directive was malformed in some way. directive: Option>, }, + + /// Represents `#[diagnostic::on_unknown_item]` + OnUnknownItem { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 7c66b3f844691..cd43f99434496 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -96,10 +96,10 @@ impl Directive { } OnUnimplementedNote { - label: label.map(|l| l.1.format(args)), - message: message.map(|m| m.1.format(args)), - notes: notes.into_iter().map(|n| n.format(args)).collect(), - parent_label: parent_label.map(|e_s| e_s.format(args)), + label: label.map(|l| l.1.format(Some(args))), + message: message.map(|m| m.1.format(Some(args))), + notes: notes.into_iter().map(|n| n.format(Some(args))).collect(), + parent_label: parent_label.map(|e_s| e_s.format(Some(args))), append_const_msg, } } @@ -133,7 +133,7 @@ pub struct FormatString { pub pieces: ThinVec, } impl FormatString { - pub fn format(&self, args: &FormatArgs) -> String { + pub fn format(&self, args: Option<&FormatArgs>) -> String { let mut ret = String::new(); for piece in &self.pieces { match piece { @@ -141,7 +141,9 @@ impl FormatString { // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => { - match args.generic_args.iter().find(|(p, _)| p == generic_param) { + match args + .and_then(|args| args.generic_args.iter().find(|(p, _)| p == generic_param)) + { Some((_, val)) => ret.push_str(val.as_str()), None => { @@ -153,7 +155,9 @@ impl FormatString { } // `{Self}` Piece::Arg(FormatArg::SelfUpper) => { - let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { + let slf = match args.and_then(|args| { + args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) + }) { Some((_, val)) => val.to_string(), None => "Self".to_string(), }; @@ -161,11 +165,21 @@ impl FormatString { } // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), + Piece::Arg(FormatArg::This) => { + ret.push_str(&args.map(|args| args.this.as_str()).unwrap_or_default()) + } Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); + let _ = fmt::write( + &mut ret, + format_args!( + "{}", + &args.map(|args| args.trait_sugared.as_str()).unwrap_or_default() + ), + ); + } + Piece::Arg(FormatArg::ItemContext) => { + ret.push_str(args.map(|args| args.item_context).unwrap_or_default()) } - Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), } } ret diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index c19fc6976c6e6..699caa203ae4b 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -79,6 +79,7 @@ impl AttributeKind { OnConst { .. } => Yes, OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, + OnUnknownItem { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6b7f31521d441..8b31b7370871a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1318,7 +1318,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, None, @@ -1367,7 +1367,7 @@ pub fn collect_crate_types( AttributeParser::::parse_limited_should_emit( session, attrs, - sym::crate_type, + &[sym::crate_type], crate_span, CRATE_NODE_ID, None, @@ -1423,7 +1423,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, None, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index af590d98c301c..66082d782e0bb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -312,7 +312,7 @@ impl EarlyLintPass for UnsafeCode { 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()), diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index c282eb34cf4d6..8501ae4cdba38 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -474,6 +474,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &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) } @@ -484,6 +487,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { 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) @@ -510,6 +516,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { &AttributeLintKind::MissingOptionsForOnMove => { lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level) } + &AttributeLintKind::MissingOptionsForOnUnknownItem => { + lints::MissingOptionsForOnUnknownItemAttr.into_diag(dcx, level) + } } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 02448035216ea..7c07c1f8af48e 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3919,6 +3919,11 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard { )] 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")] @@ -3948,6 +3953,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")] @@ -3966,6 +3976,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")] diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 1fd6699e2506e..297dfac4a5f78 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -145,7 +145,7 @@ impl NonCamelCaseTypes { 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) ); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ef7067b25baa3..c04d023fd9163 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -837,6 +837,9 @@ pub enum AttributeLintKind { MalformedOnUnimplementedAttr { span: Span, }, + MalformedOnUnknownItemdAttr { + span: Span, + }, MalformedOnConstAttr { span: Span, }, @@ -858,6 +861,7 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + MissingOptionsForOnUnknownItem, MissingOptionsForOnMove, OnMoveMalformedFormatLiterals { name: Symbol, @@ -869,6 +873,7 @@ pub enum AttributeLintKind { pub enum FormatWarning { PositionalArgument { span: Span, help: String }, InvalidSpecifier { name: String, span: Span }, + DisallowedPlaceholder { span: Span }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e495088c55649..584ba64eae040 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -78,6 +78,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, @@ -236,10 +243,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, 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::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()) }, + 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( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -741,6 +749,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// 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 { diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7211f3cf85b31..828ba698e0f2f 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -25,7 +25,7 @@ impl DebuggerVisualizerCollector<'_> { AttributeParser::parse_limited( &self.sess, attrs, - sym::debugger_visualizer, + &[sym::debugger_visualizer], span, node_id, None, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d9d42cfefcd81..d706e227aa77e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -32,7 +32,7 @@ use tracing::debug; 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::{ @@ -544,6 +544,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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); @@ -1023,6 +1024,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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); @@ -1118,7 +1120,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { AttributeParser::parse_limited( self.r.tcx.sess, &item.attrs, - sym::macro_use, + &[sym::macro_use], item.span, item.id, None, @@ -1155,6 +1157,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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), }) }; @@ -1326,6 +1329,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { 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); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 7696b4b220d6c..6b4df7572cb34 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -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, MultiSpan, pluralize, struct_span_code_err}; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::diagnostic::Directive; 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::BuiltinLintDiag; use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, @@ -140,6 +144,30 @@ impl<'ra> std::fmt::Debug for ImportKind<'ra> { } } +#[derive(Debug, Clone, Default)] +pub(crate) struct OnUnknownItemData { + directive: Directive, +} + +impl OnUnknownItemData { + pub(crate) fn from_attrs<'tcx>(tcx: TyCtxt<'tcx>, item: &Item) -> Option { + 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, } /// All imports are unique and allocated on a same arena, @@ -284,6 +317,7 @@ struct UnresolvedImportError { segment: Option, /// comes from `PathRes::Failed { module }` module: Option, + on_unknown_item_attr: Option, } // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` @@ -696,6 +730,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }; errors.push((*import, err)) } @@ -818,19 +853,42 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { format!("`{path}`") }) .collect::>(); - let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - - let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{msg}"); + let default_message = + format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); + let mut diag = if let Some((_, message)) = + errors[0].1.on_unknown_item_attr.as_mut().and_then(|a| a.directive.message.take()) + { + let message = message.format(None); + let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{message}"); + diag.note(default_message); + diag + } else { + struct_span_code_err!(self.dcx(), span, E0432, "{default_message}") + }; - if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() { + if let Some(notes) = errors[0].1.on_unknown_item_attr.as_ref().map(|a| &a.directive.notes) { + for note in notes { + diag.note(note.format(None)); + } + } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = + errors.iter().last() + { diag.note(note.clone()); } /// Upper limit on the number of `span_label` messages. const MAX_LABEL_COUNT: usize = 10; - for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) { - if let Some(label) = err.label { + for (import, mut err) in errors.into_iter().take(MAX_LABEL_COUNT) { + if let Some(label) = err + .on_unknown_item_attr + .as_mut() + .and_then(|a| a.directive.label.take()) + .map(|label| label.1.format(None)) + .or(err.label.clone()) + { + diag.span_label(err.span, label); + } else if let Some(label) = err.label { diag.span_label(err.span, label); } @@ -1097,6 +1155,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }, None => UnresolvedImportError { span, @@ -1106,6 +1165,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: Some(segment_name), module, + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }, }; return Some(err); @@ -1148,6 +1208,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { candidates: None, segment: None, module: None, + on_unknown_item_attr: None, }); } if let Some(max_vis) = max_vis.get() @@ -1351,7 +1412,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let parent_suggestion = self.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true); - Some(UnresolvedImportError { span: import.span, label: Some(label), @@ -1370,6 +1430,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }), segment: Some(ident.name), + on_unknown_item_attr: import.on_unknown_item_attr.clone(), }) } else { // `resolve_ident_in_module` reported a privacy error. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 6ae9d3aaeb236..8491c940928ca 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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), @@ -706,17 +706,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { 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::>(); + 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( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 738c9b975fd00..be0a360e6cda0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -798,6 +798,7 @@ symbols! { diagnostic_namespace, diagnostic_on_const, diagnostic_on_move, + diagnostic_on_unknown_item, dialect, direct, discriminant_kind, @@ -1410,6 +1411,7 @@ symbols! { on_const, on_move, on_unimplemented, + on_unknown_item, opaque, opaque_generic_const_args, opaque_module_name_placeholder: "", diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs new file mode 100644 index 0000000000000..fabcd86deddc6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.rs @@ -0,0 +1,51 @@ +#![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 foo; +//~^ERROR can't find crate for `foo` + +#[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() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr new file mode 100644 index 0000000000000..efa4805c318f8 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/incorrect-locations.stderr @@ -0,0 +1,110 @@ +error[E0463]: can't find crate for `foo` + --> $DIR/incorrect-locations.rs:5:1 + | +LL | extern crate foo; + | ^^^^^^^^^^^^^^^^^ can't find crate + +warning: `#[diagnostic::on_unknown_item]` can only be applied to `use` statements + --> $DIR/incorrect-locations.rs:3:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern crate foo; + | ---------------- 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:8: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:12: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:16: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:20: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:24: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:28: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:32: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:36: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:40: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:44:1 + | +LL | #[diagnostic::on_unknown_item(message = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | impl Trait for i32 {} + | ------------------ not an import + +error: aborting due to 1 previous error; 11 warnings emitted + +For more information about this error, try `rustc --explain E0463`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs new file mode 100644 index 0000000000000..4ffa9ffe37b5b --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.rs @@ -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() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr new file mode 100644 index 0000000000000..42caaa9354afa --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/malformed_attribute.stderr @@ -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`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs new file mode 100644 index 0000000000000..431ab6cdd831a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.rs @@ -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() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr new file mode 100644 index 0000000000000..fcce77f6aebbe --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/multiple_errors.stderr @@ -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`. diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs new file mode 100644 index 0000000000000..5af79af23c2cc --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.rs @@ -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() {} diff --git a/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr new file mode 100644 index 0000000000000..a9867fd74bfb0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unknown_item/unknown_import.stderr @@ -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`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs new file mode 100644 index 0000000000000..fffb54636cfb7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.rs @@ -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() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr new file mode 100644 index 0000000000000..10662eb83b4a5 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown-item.stderr @@ -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`.