diff --git a/Cargo.lock b/Cargo.lock index e11fd2189569c..5b3cd45c5205e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4354,6 +4354,7 @@ dependencies = [ "rustc_target", "rustc_trait_selection", "smallvec", + "thin-vec", "tracing", "unicode-security", ] diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 04fee06e2434c..c3f0056a4bd93 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -24,6 +24,7 @@ use thin_vec::ThinVec; pub use crate::canonical_symbols::{CanonicalSymbol, CanonicalSymbols}; use crate::diagnostic::*; use crate::lang_items::LangItem; +use crate::lint::LintCheck; use crate::pretty_printing::PrintAttribute; use crate::stability::{DefaultBodyStability, PartialConstStability, Stability}; @@ -932,6 +933,9 @@ pub enum AttributeKind { /// Represents `#[linkage]`. Linkage(Linkage, Span), + /// Represents `#[allow]`, `#[warn]`, `#[deny]`, `#[forbid]`, and `#[expect]`. + LintCheck(ThinVec), + /// Represents `#[loop_match]`. LoopMatch(Span), diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index b6733e8c1db54..b7dee26e1306d 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -58,6 +58,7 @@ impl AttributeKind { LinkOrdinal { .. } => No, LinkSection { .. } => Yes, // Needed for rustdoc Linkage(..) => No, + LintCheck(..) => No, LoopMatch(..) => No, MacroEscape => No, MacroExport { .. } => Yes, @@ -272,6 +273,7 @@ impl AttributeKind { | crate::Linkage::WeakAny | crate::Linkage::WeakODR => true, }, + LintCheck(_) => false, LoopMatch(..) => false, MacroEscape => false, MacroExport { .. } => false, diff --git a/compiler/rustc_attr_ir/src/lib.rs b/compiler/rustc_attr_ir/src/lib.rs index f835e257762a9..79bd690cbbc4c 100644 --- a/compiler/rustc_attr_ir/src/lib.rs +++ b/compiler/rustc_attr_ir/src/lib.rs @@ -29,6 +29,7 @@ pub mod diagnostic; pub mod diagnostic_items; mod encode_cross_crate; pub mod lang_items; +pub mod lint; mod pretty_printing; mod stability; pub mod target; diff --git a/compiler/rustc_attr_ir/src/lint.rs b/compiler/rustc_attr_ir/src/lint.rs new file mode 100644 index 0000000000000..f8f169962ea75 --- /dev/null +++ b/compiler/rustc_attr_ir/src/lint.rs @@ -0,0 +1,73 @@ +use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; +use rustc_span::{Ident, Span, Symbol, sym}; + +use crate::{HashIgnoredAttrId, PrintAttribute}; +#[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub enum LintCheckKind { + Allow, + Warn, + Deny, + Forbid, + Expect, +} + +impl LintCheckKind { + pub fn sym(self) -> Symbol { + match self { + LintCheckKind::Allow => sym::allow, + LintCheckKind::Warn => sym::warn, + LintCheckKind::Deny => sym::deny, + LintCheckKind::Forbid => sym::forbid, + LintCheckKind::Expect => sym::expect, + } + } +} + +/// A lint check attribute. +/// +/// For example `#[deny(clippy::blah, reason = "reason")]` is lowered into this. +/// +/// These are smooshed and flattened together; +/// ```rust +/// #[allow(dead_code)] +/// #[deny(unused, unsafe_code)] +/// # const _: () = (); +/// ``` +/// is lowered into +/// ```text +/// #[attr = LintCheck([ +/// LintCheck { lint_name: "dead_code", kind: Allow }, +/// LintCheck { lint_name: "unused", kind: Deny }, +/// LintCheck { lint_name: "unsafe_code", kind: Deny }, +/// ])] +/// ``` +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub struct LintCheck { + /// The lint's tool name, if present. + pub tool: Option, + /// The lint's name. + /// + /// With e.g. `clippy::blah` this will be `blah`. + /// Any extra segments are stored in `rest`. + pub lint_name: Symbol, + /// The span of the lint name. + /// + /// For example `#[deny(foo, bar, reason = "reason")]` + /// produces multiple `LintCheck`s, one with a span pointing to `foo` + /// and another pointing to `bar`. + pub lint_span: Span, + pub kind: LintCheckKind, + pub reason: Option, + /// Needed by `LintExpectationId` to track fulfilled expectations + pub attr_id: HashIgnoredAttrId, + /// The span of the attribute this lintcheck came from. + /// + /// Like mentioned above, multiple lint attributes and multiple lints + /// inside one attribute are all smooshed and flattened together, so + /// multiple `LintCheck`s can have the same `attr_span`. + pub attr_span: Span, + /// Any extra segments of the lint name (not including `tool` and `lint_name`), + /// this should be rare and indicates misuse of the attribute as nothing supports + /// 3+ segment lints like `#[allow(tool::two::three)]`. + pub rest: Option>, +} diff --git a/compiler/rustc_attr_ir/src/pretty_printing.rs b/compiler/rustc_attr_ir/src/pretty_printing.rs index cd8a0c0e1e96f..313602e48993f 100644 --- a/compiler/rustc_attr_ir/src/pretty_printing.rs +++ b/compiler/rustc_attr_ir/src/pretty_printing.rs @@ -17,6 +17,8 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_structures::{CollapseMacroDebuginfo, CrateType, Limit, NativeLibKind, SanitizerSet}; use thin_vec::ThinVec; +use crate::HashIgnoredAttrId; + /// This trait is used to print attributes in `rustc_hir_pretty`. /// /// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`]. @@ -32,7 +34,7 @@ pub trait PrintAttribute { fn print_attribute(&self, p: &mut Printer); } -impl PrintAttribute for &T { +impl PrintAttribute for &T { fn should_render(&self) -> bool { T::should_render(self) } @@ -41,7 +43,7 @@ impl PrintAttribute for &T { T::print_attribute(self, p) } } -impl PrintAttribute for Box { +impl PrintAttribute for Box { fn should_render(&self) -> bool { self.deref().should_render() } @@ -61,7 +63,8 @@ impl PrintAttribute for Option { } } } -impl PrintAttribute for ThinVec { + +impl PrintAttribute for [T] { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() } @@ -79,6 +82,16 @@ impl PrintAttribute for ThinVec { p.word("]"); } } + +impl PrintAttribute for ThinVec { + fn should_render(&self) -> bool { + self.as_slice().should_render() + } + + fn print_attribute(&self, p: &mut Printer) { + self.as_slice().print_attribute(p) + } +} impl PrintAttribute for FxIndexMap { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() @@ -189,7 +202,7 @@ macro_rules! print_tup { } print_tup!(A B C D E F G H); -print_skip!(Span, (), ErrorGuaranteed, AttrId); +print_skip!(Span, (), ErrorGuaranteed, AttrId, HashIgnoredAttrId); print_disp!(u8, u16, u32, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, diff --git a/compiler/rustc_attr_parsing/src/attributes/lint.rs b/compiler/rustc_attr_parsing/src/attributes/lint.rs new file mode 100644 index 0000000000000..e1221ecbb96d8 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/lint.rs @@ -0,0 +1,198 @@ +use rustc_attr_ir::AttributeKind; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; +use rustc_lint_defs::builtin::UNUSED_ATTRIBUTES; +use rustc_span::sym; +use thin_vec::ThinVec; + +use crate::attributes::{AcceptMapping, AttributeParser, AttributeStability}; +use crate::context::{AcceptContext, ExpectStringLiteral, FinalizeContext}; +use crate::parser::ArgParser; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::{Allow, Warn}; +use crate::{AttributeTemplate, diagnostics, template}; + +const LINT_TEMPLATE: AttributeTemplate = template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" +); + +#[derive(Default, Debug)] +pub(crate) struct LintParser { + lints: ThinVec, +} + +impl LintParser { + fn parse(&mut self, kind: LintCheckKind, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) { + let attr_span = cx.attr_span; + let attr_id = cx.attr_id.expect("no `AttrId` for lint attribute"); + + if let Some(list) = cx.expect_list(args, cx.attr_span) { + let mut parsers = list.sub_parsers(); + // Optionally, the last (and only the last) + // element can be `reason = "reason"` + let reason = try { + let p = parsers.last()?.meta_item()?; + let nv = p.args().as_name_value()?; + if !p.path().word_is(sym::reason) { + cx.emit_err(diagnostics::MalformedAttribute { + span: p.span(), + sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(p.span()), + }); + } else { + parsers = &parsers[..(parsers.len() - 1)]; + } + + let reason = nv.expect_string_literal(cx)?; + reason + }; + + for item in parsers { + if let Some(p) = item.meta_item() { + match p.args() { + ArgParser::NoArgs => { + let (tool, lint_name, rest) = match &*p.path().0.segments { + [] => unreachable!(), + [lint_name] => (None, lint_name.ident.name, None), + [tool, lint_name] => (Some(tool.ident), lint_name.ident.name, None), + [tool, lint_name, rest @ ..] => { + let rest = rest + .iter() + .map(|s| s.ident.name) + .collect::>() + .into(); + (Some(tool.ident), lint_name.ident.name, Some(rest)) + } + }; + + self.lints.push(LintCheck { + tool, + lint_name, + lint_span: p.span(), + kind, + attr_id, + reason, + attr_span, + rest, + }) + } + // We're found a `reason = "reason"` but we're not the last element. + ArgParser::NameValue(nv) if p.path().word_is(sym::reason) => { + cx.emit_err(diagnostics::MalformedAttribute { + span: p.span(), + sub: diagnostics::MalformedAttributeSub::ReasonMustComeLast( + item.span(), + ), + }); + nv.expect_string_literal(cx); + } + ArgParser::NameValue(_) | ArgParser::List(_) => { + cx.emit_err(diagnostics::MalformedAttribute { + span: p.span(), + sub: diagnostics::MalformedAttributeSub::BadAttributeArgument( + item.span(), + ), + }); + } + } + } else { + cx.emit_err(diagnostics::MalformedAttribute { + span: item.span(), + sub: diagnostics::MalformedAttributeSub::BadAttributeArgument(item.span()), + }); + } + } + if parsers.is_empty() { + cx.emit_lint( + UNUSED_ATTRIBUTES, + diagnostics::Unused { + attr_span, + note: if list.is_empty() { + diagnostics::UnusedNote::EmptyList { name: kind.sym() } + } else { + diagnostics::UnusedNote::NoLints { name: kind.sym() } + }, + }, + attr_span, + ); + } + } + } +} + +impl AttributeParser for LintParser { + const ATTRIBUTES: AcceptMapping = &[ + (&[sym::allow], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| { + this.parse(LintCheckKind::Allow, cx, args) + }), + (&[sym::warn], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| { + this.parse(LintCheckKind::Warn, cx, args) + }), + (&[sym::deny], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| { + this.parse(LintCheckKind::Deny, cx, args) + }), + (&[sym::forbid], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| { + this.parse(LintCheckKind::Forbid, cx, args) + }), + (&[sym::expect], LINT_TEMPLATE, AttributeStability::Stable, |this, cx, args| { + this.parse(LintCheckKind::Expect, cx, args) + }), + ]; + const ALLOWED_TARGETS: AllowedTargets<'_> = { + AllowedTargets::AllowList(&[ + Allow(Target::ExternCrate), + Allow(Target::Use), + Allow(Target::Static), + Allow(Target::Const), + Allow(Target::Fn), + Allow(Target::Closure), + Allow(Target::Mod), + Allow(Target::ForeignMod), + Allow(Target::GlobalAsm), + Allow(Target::TyAlias), + Allow(Target::Enum), + Allow(Target::Variant), + Allow(Target::Struct), + Allow(Target::Field), + Allow(Target::Union), + Allow(Target::Trait), + Allow(Target::TraitAlias), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Expression), + Allow(Target::Statement), + Allow(Target::Arm), + Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: false })), + Allow(Target::AssocConst(AssocCtxt::Trait)), + Allow(Target::AssocConst(AssocCtxt::Impl { of_trait: true })), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: false })), + Allow(Target::AssocTy(AssocCtxt::Trait)), + Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: true })), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), + Allow(Target::MacroDef), + Allow(Target::Param), + Allow(Target::PatField), + Allow(Target::ExprField), + Allow(Target::Crate), + Allow(Target::Delegation { mac: false }), + Allow(Target::Delegation { mac: true }), + Allow(Target::ConstParam), + Allow(Target::LifetimeParam), + Allow(Target::TypeParam), + Allow(Target::Loop), + Allow(Target::ForLoop), + Allow(Target::While), + Allow(Target::Break), + Warn(Target::MacroCall), + ]) + }; + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + if self.lints.is_empty() { None } else { Some(AttributeKind::LintCheck(self.lints)) } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 8e47414f6c72a..1a9c23150a894 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -52,6 +52,7 @@ pub(crate) mod dummy; pub(crate) mod inline; pub(crate) mod instruction_set; pub(crate) mod link_attrs; +pub(crate) mod lint; pub(crate) mod lint_helpers; pub(crate) mod loop_match; pub(crate) mod macro_attrs; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 060fd74c1db21..cf306f0109b31 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_attr_ir::target::Target; -use rustc_attr_ir::{AttrPath, Attribute, AttributeKind}; +use rustc_attr_ir::{AttrPath, Attribute, AttributeKind, HashIgnoredAttrId}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, DiagLocation, Diagnostic, Level, MultiSpan}; use rustc_feature::AttributeStability; @@ -43,6 +43,7 @@ use crate::attributes::dummy::*; use crate::attributes::inline::*; use crate::attributes::instruction_set::*; use crate::attributes::link_attrs::*; +use crate::attributes::lint::*; use crate::attributes::lint_helpers::*; use crate::attributes::loop_match::*; use crate::attributes::macro_attrs::*; @@ -166,6 +167,7 @@ attribute_parsers!( ConfusablesParser, ConstStabilityParser, DocParser, + LintParser, MacroUseParser, NakedParser, OnConstParser, @@ -387,6 +389,11 @@ pub struct AcceptContext<'f, 'sess> { /// Whether it is an inner or outer attribute. pub(crate) attr_style: AttrStyle, + /// Used for lint attributes. + /// + /// These should not end up in query results, as they cannot be deserialized from disk. + pub(crate) attr_id: Option, + /// A description of the thing we are parsing using this attribute parser. /// We are not only using these parsers for attributes, but also for macros such as the `cfg!()` macro. pub(crate) parsed_description: ParsedDescription, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 7d0a7832f52cf..b55d7e476896b 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2132,3 +2132,37 @@ pub(crate) enum MacroExport { #[note("declarative macros follow the same exporting rules as regular items")] OnDeclMacro, } + +#[derive(Diagnostic)] +#[diag("malformed lint attribute input", code = E0452)] +pub(crate) struct MalformedAttribute { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sub: MalformedAttributeSub, +} + +#[derive(Subdiagnostic)] +pub(crate) enum MalformedAttributeSub { + #[label("bad attribute argument")] + BadAttributeArgument(#[primary_span] Span), + #[label("reason in lint attribute must come last")] + ReasonMustComeLast(#[primary_span] Span), +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnusedNote { + #[note("attribute `{$name}` with an empty list has no effect")] + EmptyList { name: Symbol }, + #[note("attribute `{$name}` without any lints has no effect")] + NoLints { name: Symbol }, +} + +#[derive(Diagnostic)] +#[diag("unused attribute")] +pub(crate) struct Unused { + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + pub attr_span: Span, + #[subdiagnostic] + pub note: UnusedNote, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index bfdfaf57c2c64..567fa622725bc 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -266,6 +266,7 @@ impl<'sess> AttributeParser<'sess> { attr_path, #[cfg(debug_assertions)] has_target_been_checked: false, + attr_id: None, }; parse_fn(&mut cx, args) } @@ -427,6 +428,7 @@ impl<'sess> AttributeParser<'sess> { attr_path: attr_path.clone(), #[cfg(debug_assertions)] has_target_been_checked: false, + attr_id: Some(HashIgnoredAttrId { attr_id: attr.id }), }; (accept.accept_fn)(&mut cx, &args); diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index c85b1f8fee967..a5461d9044b96 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -792,4 +792,8 @@ impl MetaItemListParser { let mut iter = self.mixed(); iter.next().filter(|_| iter.next().is_none()) } + + pub fn sub_parsers(&self) -> &[MetaItemOrLitParser] { + &self.sub_parsers + } } diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 2931bc0e3d524..43417348f1d4b 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -30,6 +30,7 @@ rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.19" tracing = "0.1" unicode-security = "0.1.0" # tidy-alphabetical-end diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 87802a2afb53f..0bcb42af7469d 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -357,14 +357,14 @@ impl LintStore { } let complete_name = if let Some(tool_name) = tool_name { - format!("{tool_name}::{lint_name}") + &format!("{tool_name}::{lint_name}") } else { - lint_name.to_string() + lint_name }; // If the lint was scoped with `tool::` check if the tool lint exists if let Some(tool_name) = tool_name { - match self.by_name.get(&complete_name) { - None => match self.lint_groups.get(&*complete_name) { + match self.by_name.get(complete_name) { + None => match self.lint_groups.get(complete_name) { // If the lint isn't registered, there are two possibilities: None => { // 1. The tool is currently running, so this lint really doesn't exist. @@ -373,7 +373,7 @@ impl LintStore { let tool_prefix = format!("{tool_name}::"); return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { - self.no_lint_suggestion(&complete_name, tool_name.as_str()) + self.no_lint_suggestion(complete_name, tool_name.as_str()) } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. @@ -394,13 +394,13 @@ impl LintStore { _ => {} } } - match self.by_name.get(&complete_name) { + match self.by_name.get(complete_name) { Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()), Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()), - None => match self.lint_groups.get(&*complete_name) { + None => match self.lint_groups.get(complete_name) { // If neither the lint, nor the lint group exists check if there is a `clippy::` // variant of this lint - None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"), + None => self.check_tool_name_for_backwards_compat(complete_name, "clippy"), Some(LintGroup { lint_ids, depr, .. }) => { // Check if the lint group name is deprecated if let &Some(LintAlias { name, silent: false }) = depr { diff --git a/compiler/rustc_lint/src/diagnostics.rs b/compiler/rustc_lint/src/diagnostics.rs index 601f1213650e1..8b575033d0df6 100644 --- a/compiler/rustc_lint/src/diagnostics.rs +++ b/compiler/rustc_lint/src/diagnostics.rs @@ -62,25 +62,6 @@ impl Subdiagnostic for OverruledAttributeSub { } } -#[derive(Diagnostic)] -#[diag("malformed lint attribute input", code = E0452)] -pub(crate) struct MalformedAttribute { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub sub: MalformedAttributeSub, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MalformedAttributeSub { - #[label("bad attribute argument")] - BadAttributeArgument(#[primary_span] Span), - #[label("reason must be a string literal")] - ReasonMustBeStringLiteral(#[primary_span] Span), - #[label("reason in lint attribute must come last")] - ReasonMustComeLast(#[primary_span] Span), -} - #[derive(Diagnostic)] #[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)] pub(crate) struct UnknownToolInScopedLint { diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 02184ac623908..d71e881590263 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,3 +1,4 @@ +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_lint_defs::builtin::UNFULFILLED_LINT_EXPECTATIONS; use rustc_lint_defs::{LintExpectationId, StableLintExpectationId}; @@ -35,7 +36,11 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { LintExpectationId::Unstable(id) => (id.attr_id, id.lint_index), LintExpectationId::Stable(id) => { // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - (tcx.hir_attrs(id.hir_id)[id.attr_index as usize].id(), id.lint_index) + + let lint_checks = find_attr!(tcx, id.hir_id, LintCheck(lints) => lints).unwrap(); + let attr_id = lint_checks[id.lint_index as usize].attr_id.attr_id; + + (attr_id, id.lint_index) } }; (attr_id, lint_index) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f847d4c6ae904..a9995f6ec5451 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,11 @@ use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::attr::AttributeExt; -use rustc_ast_pretty::pprust; +use rustc_ast::{CRATE_NODE_ID, join_path_syms}; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::target::Target; +use rustc_attr_ir::{Attribute, AttributeKind, find_attr}; +use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg}; @@ -27,17 +31,18 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; use rustc_span::def_id::CRATE_MOD_ID; -use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, sym}; +use thin_vec::ThinVec; use tracing::{debug, instrument}; use crate::builtin::MISSING_DOCS; use crate::context::{CheckLintNameResult, LintStore}; use crate::diagnostics::{ CheckNameUnknownTool, DeprecatedLintName, DeprecatedLintNameFromCommandLine, - IgnoredUnlessCrateSpecified, MalformedAttribute, MalformedAttributeSub, OverruledAttribute, - OverruledAttributeLint, OverruledAttributeSub, RemovedLint, RemovedLintFromCommandLine, - RenamedLint, RenamedLintFromCommandLine, RenamedLintSuggestion, RequestedLevel, UnknownLint, - UnknownLintFromCommandLine, UnknownLintSuggestion, UnknownToolInScopedLint, UnsupportedGroup, + IgnoredUnlessCrateSpecified, OverruledAttribute, OverruledAttributeLint, OverruledAttributeSub, + RemovedLint, RemovedLintFromCommandLine, RenamedLint, RenamedLintFromCommandLine, + RenamedLintSuggestion, RequestedLevel, UnknownLint, UnknownLintFromCommandLine, + UnknownLintSuggestion, UnknownToolInScopedLint, UnsupportedGroup, }; use crate::late::unerased_lint_store; @@ -227,12 +232,7 @@ pub trait LintLevelsProvider { fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation); - fn mk_lint_expectation_id( - &self, - attr_id: AttrId, - attr_index: usize, - lint_index: u16, - ) -> Self::LintExpectationId; + fn mk_lint_expectation_id(&self, attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId; } impl LintLevelsProvider for TopDown { @@ -252,12 +252,7 @@ impl LintLevelsProvider for TopDown { fn push_expectation(&mut self, _: Self::LintExpectationId, _: LintExpectation) {} - fn mk_lint_expectation_id( - &self, - attr_id: AttrId, - _attr_index: usize, - lint_index: u16, - ) -> Self::LintExpectationId { + fn mk_lint_expectation_id(&self, attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId { UnstableLintExpectationId { attr_id, lint_index } } } @@ -290,14 +285,8 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { self.specs.expectations.push((id, expectation)) } - fn mk_lint_expectation_id( - &self, - _attr_id: AttrId, - attr_index: usize, - lint_index: u16, - ) -> Self::LintExpectationId { - let attr_index = attr_index.try_into().unwrap(); - StableLintExpectationId { hir_id: self.cur, attr_index, lint_index } + fn mk_lint_expectation_id(&self, _attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId { + StableLintExpectationId { hir_id: self.cur, lint_index } } } @@ -666,9 +655,81 @@ where }; } - fn add(&mut self, attrs: &[impl AttributeExt], is_crate_node: bool) { - let sess = self.sess; - for (attr_index, attr) in attrs.iter().enumerate() { + fn parse_lint_attributes( + &mut self, + attrs: &[impl AttributeExt + 'static], + ) -> ThinVec { + fn try_cast_slice(slice: &[T]) -> Option<&[U]> { + use std::any::TypeId; + if TypeId::of::() == TypeId::of::() { + // SAFETY: T == U, so it is sound to cast `&[T]` to `&[U]` + unsafe { + Some(core::slice::from_raw_parts(slice.as_ptr().cast::(), slice.len())) + } + } else { + None + } + } + + if let Some(attrs) = try_cast_slice::<_, rustc_attr_ir::Attribute>(attrs) { + return find_attr!(attrs, LintCheck(lints) => lints.clone()).unwrap_or_default(); + } + + let parsed = AttributeParser::parse_limited_all( + self.sess, + try_cast_slice(attrs).unwrap_or_else(|| { + unreachable!("unknown `&[implAttributeExt]` {}", std::any::type_name_of_val(attrs)) + }), + Some(&|attr| { + // This is ...complicated. Sometimes we need to parse lint check + // attributes pre-expansion, but of course things are allowed to + // be invalid if they're removed by a macro/cfg attribute before + // we do attribute parsing, which happens (mostly) post-expansion. + // + // Unfortunately we're somewhat inconsistent - sometimes we just + // give up and sometimes we error. Take for example: + // + // #[expect] // OK + // #[expect[wut]] // OK + // #[expect(expect)] // OK + // #[expect(expect(expect))] //~ ERROR malformed lint attribute input + // #[deny(({!}))] // OK + // #[expect[helix::]] // OK + // #[cfg(false)] + // const _: () = (); + // + // FIXME: just allow everything? + (attr.path_matches(&[sym::allow]) + || attr.path_matches(&[sym::warn]) + || attr.path_matches(&[sym::deny]) + || attr.path_matches(&[sym::forbid]) + || attr.path_matches(&[sym::expect])) + && attr.meta_item_list().is_some() + }), + Target::Crate, + DUMMY_SP, + CRATE_NODE_ID, + Some(self.features), + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + Some(self.registered_lint_tools), + ); + parsed + .into_iter() + .find_map(|p| { + if let Attribute::Parsed(AttributeKind::LintCheck(lints)) = p { + Some(lints) + } else { + None + } + }) + .unwrap_or_default() + } + + fn add(&mut self, attrs: &[impl AttributeExt + 'static], is_crate_node: bool) { + if attrs.is_empty() { + return; + } + for attr in attrs { if attr.is_automatically_derived_attr() { self.provider.insert( LintId::of(SINGLE_USE_LIFETIMES), @@ -685,230 +746,180 @@ where ); continue; } + } + let lint_checks = self.parse_lint_attributes(attrs); - let level = match Level::from_opt_symbol(attr.name()) { - None => continue, - Some(level) => level, - }; + let sess = self.sess; - let Some(mut metas) = attr.meta_item_list() else { continue }; + // FIXME: consider performing these checks in attribute parsing instead + for (lint_index, lint_check) in lint_checks.into_iter().enumerate() { + let LintCheck { tool, lint_name, lint_span, kind, reason, attr_id, attr_span: _, rest } = + lint_check; - // Check whether `metas` is empty, and get its last element. - let Some(tail_li) = metas.last() else { - // This emits the unused_attributes lint for `#[level()]` - continue; + let full_lint_name = if let Some(rest) = rest { + &join_path_syms([lint_name].into_iter().chain(rest)) + } else { + lint_name.as_str() }; - // Before processing the lint names, look for a reason (RFC 2383) - // at the end. - let mut reason = None; - if let Some(item) = tail_li.meta_item() { - match item.kind { - ast::MetaItemKind::Word => {} // actual lint names handled later - ast::MetaItemKind::NameValue(ref name_value) => { - if item.path == sym::reason { - if let ast::LitKind::Str(rationale, _) = name_value.kind { - reason = Some(rationale); - } else { - sess.dcx().emit_err(MalformedAttribute { - span: name_value.span, - sub: MalformedAttributeSub::ReasonMustBeStringLiteral( - name_value.span, - ), - }); - } - // found reason, reslice meta list to exclude it - metas.pop().unwrap(); - } else { - sess.dcx().emit_err(MalformedAttribute { - span: item.span, - sub: MalformedAttributeSub::BadAttributeArgument(item.span), - }); - } - } - ast::MetaItemKind::List(_) => { - sess.dcx().emit_err(MalformedAttribute { - span: item.span, - sub: MalformedAttributeSub::BadAttributeArgument(item.span), - }); - } - } - } - - for (lint_index, li) in metas.iter_mut().enumerate() { - // `Expect` is the only lint level with a `LintExpectationId` that can be created - // from an attribute. - let lint_id = (level == Level::Expect).then(|| { - self.provider.mk_lint_expectation_id(attr.id(), attr_index, lint_index as u16) - }); - - let sp = li.span(); - let meta_item = match li { - ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item, - _ => { - let sub = if let Some(item) = li.meta_item() - && let ast::MetaItemKind::NameValue(_) = item.kind - && item.path == sym::reason - { - MalformedAttributeSub::ReasonMustComeLast(sp) - } else { - MalformedAttributeSub::BadAttributeArgument(sp) - }; - - sess.dcx().emit_err(MalformedAttribute { span: sp, sub }); - continue; - } - }; - let tool_ident = if meta_item.path.segments.len() > 1 { - Some(meta_item.path.segments.remove(0).ident) - } else { - None - }; - let tool_name = tool_ident.map(|ident| ident.name); - let name = pprust::path_to_string(&meta_item.path); - let lint_result = - self.store.check_lint_name(&name, tool_name, self.registered_lint_tools); - - let (ids, name) = match lint_result { - CheckLintNameResult::Ok(ids) => { - let name = - meta_item.path.segments.last().expect("empty lint name").ident.name; - (ids, name) - } + let level = match kind { + LintCheckKind::Allow => Level::Allow, + LintCheckKind::Warn => Level::Warn, + LintCheckKind::Deny => Level::Deny, + LintCheckKind::Forbid => Level::Forbid, + LintCheckKind::Expect => Level::Expect, + }; - CheckLintNameResult::Tool(ids, new_lint_name) => { - let name = match new_lint_name { - None => { - let complete_name = - &format!("{}::{}", tool_ident.unwrap().name, name); - Symbol::intern(complete_name) - } - Some(new_lint_name) => { - self.emit_span_lint( - builtin::RENAMED_AND_REMOVED_LINTS, - sp.into(), - DeprecatedLintName { - name, - suggestion: sp, - replace: &new_lint_name, - }, - ); - Symbol::intern(&new_lint_name) - } - }; - (ids, name) - } + // `Expect` is the only lint level with a `LintExpectationId` that can be created + // from an attribute. + let lint_id = (level == Level::Expect) + .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16)); - CheckLintNameResult::MissingTool => { - // If `MissingTool` is returned, then either the lint does not - // exist in the tool or the code was not compiled with the tool and - // therefore the lint was never added to the `LintStore`. To detect - // this is the responsibility of the lint tool. - continue; - } + let lint_result = self.store.check_lint_name( + full_lint_name, + tool.map(|i| i.name), + self.registered_lint_tools, + ); - CheckLintNameResult::NoTool => { - sess.dcx().emit_err(UnknownToolInScopedLint { - span: tool_ident.map(|ident| ident.span), - tool_name: tool_name.unwrap(), - lint_name: pprust::path_to_string(&meta_item.path), - is_nightly_build: sess.is_nightly_build(), - }); - continue; - } + let (ids, name) = match lint_result { + CheckLintNameResult::Ok(ids) => (ids, lint_name), - CheckLintNameResult::Renamed(ref replace) => { - if self.lint_added_lints { - let suggestion = - RenamedLintSuggestion::WithSpan { suggestion: sp, replace }; - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); + CheckLintNameResult::Tool(ids, new_lint_name) => { + let name = match new_lint_name { + None => { + let complete_name = &format!("{}::{lint_name}", tool.unwrap().name); + Symbol::intern(complete_name) + } + Some(new_lint_name) => { self.emit_span_lint( - RENAMED_AND_REMOVED_LINTS, - sp.into(), - RenamedLint { name: name.as_str(), replace, suggestion }, + builtin::RENAMED_AND_REMOVED_LINTS, + lint_span.into(), + DeprecatedLintName { + name: lint_name.to_string(), + suggestion: lint_span, + replace: &new_lint_name, + }, ); + Symbol::intern(&new_lint_name) } + }; + (ids, name) + } - // If this lint was renamed, apply the new lint instead of ignoring the - // attribute. Ignore any errors or warnings that happen because the new - // name is inaccurate. - // NOTE: `new_name` already includes the tool name, so we don't - // have to add it again. - let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(replace, None, self.registered_lint_tools) - else { - panic!("renamed lint does not exist: {replace}"); - }; + CheckLintNameResult::MissingTool => { + // If `MissingTool` is returned, then either the lint does not + // exist in the tool or the code was not compiled with the tool and + // therefore the lint was never added to the `LintStore`. To detect + // this is the responsibility of the lint tool. + continue; + } - (ids, Symbol::intern(&replace)) - } + CheckLintNameResult::NoTool => { + sess.dcx().emit_err(UnknownToolInScopedLint { + span: Some(tool.unwrap().span), + tool_name: tool.unwrap().name, + lint_name: full_lint_name.to_string(), + is_nightly_build: sess.is_nightly_build(), + }); + continue; + } - CheckLintNameResult::Removed(ref reason) => { - if self.lint_added_lints { - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - self.emit_span_lint( - RENAMED_AND_REMOVED_LINTS, - sp.into(), - RemovedLint { name: name.as_str(), reason }, - ); - } - continue; + CheckLintNameResult::Renamed(ref replace) => { + if self.lint_added_lints { + let suggestion = + RenamedLintSuggestion::WithSpan { suggestion: lint_span, replace }; + let name = tool + .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) + .unwrap_or_else(|| full_lint_name.to_string()); + self.emit_span_lint( + RENAMED_AND_REMOVED_LINTS, + lint_span.into(), + RenamedLint { name: name.as_str(), replace, suggestion }, + ); } - CheckLintNameResult::NoLint(suggestion) => { - if self.lint_added_lints { - let name = - tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); - let suggestion = suggestion.map(|(replace, from_rustc)| { - UnknownLintSuggestion::WithSpan { - suggestion: sp, - replace, - from_rustc, - } - }); - self.emit_span_lint( - UNKNOWN_LINTS, - sp.into(), - UnknownLint { name, suggestion }, - ); - } - continue; + // If this lint was renamed, apply the new lint instead of ignoring the + // attribute. Ignore any errors or warnings that happen because the new + // name is inaccurate. + // NOTE: `new_name` already includes the tool name, so we don't + // have to add it again. + let CheckLintNameResult::Ok(ids) = + self.store.check_lint_name(replace, None, self.registered_lint_tools) + else { + panic!("renamed lint does not exist: {replace}"); + }; + + (ids, Symbol::intern(&replace)) + } + + CheckLintNameResult::Removed(ref reason) => { + if self.lint_added_lints { + let name = if let Some(Ident { name, .. }) = tool { + &format!("{name}::{full_lint_name}") + } else { + full_lint_name + }; + self.emit_span_lint( + RENAMED_AND_REMOVED_LINTS, + lint_span.into(), + RemovedLint { name, reason }, + ); } - }; + continue; + } - let src = LintLevelSource::Node { name, span: sp, reason }; - for &id in ids { - if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, LevelSpec::new(level, lint_id, src)); + CheckLintNameResult::NoLint(suggestion) => { + if self.lint_added_lints { + let name = tool + .map(|Ident { name, .. }| format!("{name}::{full_lint_name}")) + .unwrap_or_else(|| full_lint_name.to_string()); + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithSpan { + suggestion: lint_span, + replace, + from_rustc, + } + }); + self.emit_span_lint( + UNKNOWN_LINTS, + lint_span.into(), + UnknownLint { name, suggestion }, + ); } + continue; } + }; - // This checks for instances where the user writes - // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid - // overriding the lint level but instead add an expectation that can't be - // fulfilled. The lint message will include an explanation, that the - // `unfulfilled_lint_expectations` lint can't be expected. - if let (Level::Expect, Some(expect_id)) = (level, lint_id) { - // The `unfulfilled_lint_expectations` lint is not part of any lint - // groups. Therefore. we only need to check the slice if it contains a - // single lint. - let is_unfulfilled_lint_expectations = match ids { - [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), - _ => false, - }; - self.provider.push_expectation( - expect_id, - LintExpectation::new( - reason, - sp, - is_unfulfilled_lint_expectations, - tool_name, - ), - ); + let src = LintLevelSource::Node { name, span: lint_span, reason }; + for &id in ids { + if self.check_gated_lint(id, lint_span, false) { + self.insert_spec(id, LevelSpec::new(level, lint_id, src)); } } + + // This checks for instances where the user writes + // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid + // overriding the lint level but instead add an expectation that can't be + // fulfilled. The lint message will include an explanation, that the + // `unfulfilled_lint_expectations` lint can't be expected. + if let (Level::Expect, Some(expect_id)) = (level, lint_id) { + // The `unfulfilled_lint_expectations` lint is not part of any lint + // groups. Therefore. we only need to check the slice if it contains a + // single lint. + let is_unfulfilled_lint_expectations = match ids { + [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), + _ => false, + }; + self.provider.push_expectation( + expect_id, + LintExpectation::new( + reason, + lint_span, + is_unfulfilled_lint_expectations, + tool.map(|i| i.name), + ), + ); + } } if self.lint_added_lints && !is_crate_node { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4657621c5500d..5fd2777fd46f6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -125,17 +125,15 @@ impl From for LintExpectationId { #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)] pub struct StableLintExpectationId { pub hir_id: HirId, - pub attr_index: u16, pub lint_index: u16, } impl StableHash for StableLintExpectationId { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - let StableLintExpectationId { hir_id, attr_index, lint_index } = self; + let StableLintExpectationId { hir_id, lint_index } = self; hir_id.stable_hash(hcx, hasher); - attr_index.stable_hash(hcx, hasher); lint_index.stable_hash(hcx, hasher); } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 79505dd6f03fc..b094a0696c6b8 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -84,10 +84,10 @@ that contains only loops and breakable blocks. It tracks where a `break`, use std::mem; use interpret::ErrorHandled; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_hir::HirId; use rustc_index::{IndexSlice, IndexVec}; -use rustc_lint_defs::Level; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind}; @@ -1292,13 +1292,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // This is a moderately common case, mostly hit for previously unseen nodes. break; } - - if self - .tcx - .hir_attrs(id) - .iter() - .any(|attr| Level::from_opt_symbol(attr.name()).is_some()) - { + if find_attr!(self.tcx, id, LintCheck(_)) { // This is a rare case. It's for a node path that doesn't reach the root due to an // intervening lint level attribute. This result doesn't get cached. return id; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3eb6892177b05..125dc9161abce 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -8,9 +8,9 @@ use std::cell::Cell; use std::slice; -use rustc_ast::MetaItemKind; use rustc_attr_ir::diagnostic::Directive; use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::lint::LintCheck; use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; use rustc_attr_ir::{ Attribute, AttributeKind, DocAttribute, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, @@ -136,13 +136,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match attr { Attribute::Parsed(attr_kind) => { self.check_one_parsed_attribute(hir_id, span, target, item, attr_kind); - self.check_unused_attribute(hir_id, attr); } Attribute::Unparsed(_) => { match attr.path().as_slice() { - // ok - [sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid, ..] => {} - [name, rest @ ..] => { if BUILTIN_ATTRIBUTE_SET.contains(name) { if rest.len() > 0 @@ -164,8 +160,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [] => unreachable!(), } - - self.check_unused_attribute(hir_id, attr); } } } @@ -223,6 +217,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Linkage(_linkage, span) => { self.check_linkage(*span, hir_id, target, item) } + AttributeKind::LintCheck(lints) => self.check_lint_check(hir_id, lints), // All of the following attributes have no specific checks. // tidy-alphabetical-start @@ -1148,75 +1143,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { - // Warn on useless empty attributes. - // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` - let note = - if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid]) - && attr.meta_item_list().is_some_and(|list| list.is_empty()) - { - diagnostics::UnusedNote::EmptyList { name: attr.name().unwrap() } - } else if attr.has_any_name(&[ - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - sym::expect, - ]) && let Some(meta) = attr.meta_item_list() - && let [meta] = meta.as_slice() - && let Some(item) = meta.meta_item() - && let MetaItemKind::NameValue(_) = &item.kind - && item.path == sym::reason - { - diagnostics::UnusedNote::NoLints { name: attr.name().unwrap() } - } else if attr.has_any_name(&[ - sym::allow, - sym::warn, - sym::deny, - sym::forbid, - sym::expect, - ]) && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().is_some_and(|item| { - item.path == sym::linker_messages || item.path == sym::linker_info - }) - }) - { - if hir_id != CRATE_HIR_ID { - return; - } else { - let never_needs_link = self - .tcx - .crate_types() - .iter() - .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)); - if never_needs_link { - diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly - } else { - return; - } - } - } else if hir_id == CRATE_HIR_ID - && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) - && let Some(meta) = attr.meta_item_list() - && meta.iter().any(|meta| { - meta.meta_item().is_some_and(|item| item.path == sym::dead_code_pub_in_binary) - }) - && !self.tcx.crate_types().contains(&CrateType::Executable) - { - diagnostics::UnusedNote::NoEffectDeadCodePubInBinary - } else { - return; - }; - - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - diagnostics::Unused { attr_span: attr.span(), note }, - ); - } - /// A best effort attempt to create an error for a mismatching proc macro signature. /// /// If this best effort goes wrong, it will just emit a worse error later (see #102923) @@ -1417,6 +1343,60 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => {} } } + + fn check_lint_check(&self, hir_id: HirId, lints: &[LintCheck]) { + for lint in lints { + let LintCheck { + tool: None, + lint_name, + lint_span, + rest: None, + kind: _, + reason: _, + attr_id: _, + attr_span: _, + } = *lint + else { + continue; + }; + + match lint_name { + sym::dead_code_pub_in_binary => { + if !self.tcx.crate_types().contains(&CrateType::Executable) { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + lint_span, + diagnostics::Unused { + attr_span: lint_span, + note: diagnostics::UnusedNote::NoEffectDeadCodePubInBinary, + }, + ); + } + } + sym::linker_messages | sym::linker_info => { + if hir_id == CRATE_HIR_ID + && self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib)) + { + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + lint_span, + diagnostics::Unused { + attr_span: lint_span, + note: diagnostics::UnusedNote::LinkerMessagesBinaryCrateOnly, + }, + ); + } + } + _ => {} + } + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index b3094d8aaab73..bad976bc57836 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -146,10 +146,6 @@ pub(crate) struct RustcForceInlineCoro { #[derive(Subdiagnostic)] pub(crate) enum UnusedNote { - #[note("attribute `{$name}` with an empty list has no effect")] - EmptyList { name: Symbol }, - #[note("attribute `{$name}` without any lints has no effect")] - NoLints { name: Symbol }, #[note( "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 10814cdb4c585..5898b2eb267a4 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -3,12 +3,14 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::source::{IntoSpan as _, SpanExt as _, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; -use rustc_ast::{BinOpKind, MetaItemInner}; +use rustc_ast::BinOpKind; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, StmtKind}; use rustc_lexer::TokenKind; -use rustc_lint::{LateContext, LateLintPass, Level, impl_lint_pass}; -use rustc_span::{BytePos, Span, Symbol}; +use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; +use rustc_span::{BytePos, Ident, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -238,17 +240,26 @@ impl CollapsibleIf { !span_contains_non_whitespace(cx, span, self.lint_commented_code) }, - [attr] - if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) - && let Some(metas) = attr.meta_item_list() - && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) => + [Attribute::Parsed(AttributeKind::LintCheck(lints))] + if lints + .iter() + .filter(|lint| { + matches!( + lint, + LintCheck { + tool: Some(Ident { name: sym::clippy, .. }), + rest: None, + kind: LintCheckKind::Expect, + .. + } + ) + }) + .any(|lint| [expected_lint_name, sym::style, sym::all].contains(&lint.lint_name)) => { + let attr_span = lints.first().unwrap().attr_span.to(lints.last().unwrap().attr_span); // There is an `expect` attribute -- check that there is no _other_ significant text - let span_before_attr = inner_if.span.split_at(1).1.until(attr.span()); - let span_after_attr = attr.span().between(inner_if_expr.span); + let span_before_attr = inner_if.span.split_at(1).1.until(attr_span); + let span_after_attr = attr_span.between(inner_if_expr.span); !span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code) && !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code) }, diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index 39be16b947a4c..a5bac01d2bc53 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; +use rustc_attr_ir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{self as hir, ExprKind}; @@ -183,8 +184,10 @@ fn suggestion<'tcx>( } fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { - if let Some(attr) = tcx.hir_attrs(field.hir_id).first() { - field.span.with_lo(attr.span().lo()) + if let Some(lints) = find_attr!(tcx, field.hir_id, LintCheck(lints) => lints) { + field.span.with_lo(lints.first().unwrap().attr_span.lo()) + } else if let Some(cfg_span) = find_attr!(tcx, field.hir_id, CfgTrace(cfgs) => cfgs[0].1) { + field.span.with_lo(cfg_span.lo()) } else { field.span } diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index 33d3e15412060..f4e17571173e1 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -5,9 +5,10 @@ use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_non_whitespace}; use core::ops::ControlFlow; +use rustc_attr_ir::find_attr; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, Stmt, StmtKind}; -use rustc_lint::{LateContext, Level, LintContext as _}; +use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::GenericArgKind; use rustc_span::edition::Edition; @@ -91,11 +92,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) /// or if there is only whitespace between `let` and return expression. /// Non-lint attrs like `#[cfg]` should still block. fn has_lint_attrs_or_only_whitespace_between(cx: &LateContext<'_>, retexpr: &Expr<'_>, stmt: &Stmt<'_>) -> bool { - // TODO: Turn into find_attr! when lint level attr parsing is done. - let retexpr_attrs = cx.tcx.hir_attrs(retexpr.hir_id); + let retexpr_lintcheck_attr = find_attr!(cx.tcx, retexpr.hir_id, LintCheck(_)); - retexpr_attrs - .iter() - .any(|a| a.name().is_some_and(|name| Level::from_symbol(name).is_some())) - || !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), false) + retexpr_lintcheck_attr || !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), false) } diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index e3df1934e5ad1..cc7c72b6151ff 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -4,14 +4,16 @@ use clippy_utils::{ binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, span_contains_cfg, span_find_starting_semi, sym, }; -use rustc_ast::MetaItemInner; + use rustc_attr_ir::lang_items::LangItem; +use rustc_attr_ir::lint::{LintCheck, LintCheckKind}; +use rustc_attr_ir::{Attribute, AttributeKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, Level, LintContext as _}; +use rustc_lint::{LateContext, LintContext as _}; use rustc_middle::ty::{self, Ty}; -use rustc_span::{BytePos, Pos as _, Span}; +use rustc_span::{BytePos, Ident, Pos as _, Span}; use std::borrow::Cow; use std::fmt::Display; @@ -181,18 +183,19 @@ fn check_final_expr<'tcx>( // actually fulfill the expectation (clippy::#12998) match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, - [attr] => { - if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings + [Attribute::Parsed(AttributeKind::LintCheck(lints))] => { + if lints.iter().any(|lint| { + matches!( + lint, + LintCheck { + tool: Some(Ident { name: sym::clippy, .. }), + lint_name: sym::needless_return | sym::style | sym::all | sym::warnings, + rest: None, + kind: LintCheckKind::Expect, + .. + } ) - { + }) { // This is an expectation of the `needless_return` lint } else { return; diff --git a/tests/pretty/delegation/generics.pp b/tests/pretty/delegation/generics.pp index f5429c3c26a75..5bfecd355396d 100644 --- a/tests/pretty/delegation/generics.pp +++ b/tests/pretty/delegation/generics.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:generics.pp - -#![allow(incomplete_features)] +#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:generics.pp + mod free_to_trait { trait Trait<'a, XX, Y, T = (), const N: usize = 2> { diff --git a/tests/pretty/delegation/inherit-attributes.pp b/tests/pretty/delegation/inherit-attributes.pp index a45eb5a1c5f38..01aa7bd2fa5eb 100644 --- a/tests/pretty/delegation/inherit-attributes.pp +++ b/tests/pretty/delegation/inherit-attributes.pp @@ -1,14 +1,15 @@ +#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +kind: Allow}])] +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use std::prelude::rust_2021::*; //@ edition:2021 //@ aux-crate:to_reuse_functions=to-reuse-functions.rs //@ pretty-mode:hir //@ pretty-compare-only //@ pp-exact:inherit-attributes.pp -#![allow(incomplete_features)] -#![attr = Feature([fn_delegation#0])] -extern crate std; -#[attr = PreludeImport] -use std::prelude::rust_2021::*; extern crate to_reuse_functions; diff --git a/tests/pretty/delegation/inline-attribute.pp b/tests/pretty/delegation/inline-attribute.pp index 361eb56f558f4..bf55d76ad3fb8 100644 --- a/tests/pretty/delegation/inline-attribute.pp +++ b/tests/pretty/delegation/inline-attribute.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:inline-attribute.pp - -#![allow(incomplete_features)] +#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:inline-attribute.pp + mod to_reuse { fn foo(x: usize) -> usize { x } diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index 375460e86542b..15841d103e0f6 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:hir-delegation.pp - -#![allow(incomplete_features)] +#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +kind: Allow}])] #![attr = Feature([fn_delegation#0])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-delegation.pp + fn b(e: C) { } diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index c35a40eed0c50..92cc37b584b32 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -1,13 +1,13 @@ +#![attr = LintCheck([LintCheck {lint_name: "unused", kind: Allow}])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-lifetimes.pp // This tests the pretty-printing of lifetimes in lots of ways. -#![allow(unused)] -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; struct Foo<'a> { x: &'a u32, diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 6c9dec2bfb1fb..5dd405c89258d 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -1,12 +1,13 @@ -//@ pretty-compare-only -//@ pretty-mode:hir -//@ pp-exact:pin-ergonomics-hir.pp - -#![allow(dead_code, incomplete_features)] #![attr = Feature([pin_ergonomics#0])] +#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, +LintCheck {lint_name: "incomplete_features", kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:pin-ergonomics-hir.pp + use std::pin::Pin; diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index adbf66fa48507..e2c56722b1311 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -28,86 +28,6 @@ error[E0463]: can't find crate for `wloop` LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate -error: malformed `allow` attribute input - --> $DIR/malformed-attrs.rs:178:1 - | -LL | #[allow] - | ^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL | #[allow(lint1)] - | +++++++ -LL | #[allow(lint1, lint2, ...)] - | +++++++++++++++++++ -LL | #[allow(lint1, lint2, lint3, reason = "...")] - | +++++++++++++++++++++++++++++++++++++ - -error: malformed `expect` attribute input - --> $DIR/malformed-attrs.rs:180:1 - | -LL | #[expect] - | ^^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL | #[expect(lint1)] - | +++++++ -LL | #[expect(lint1, lint2, ...)] - | +++++++++++++++++++ -LL | #[expect(lint1, lint2, lint3, reason = "...")] - | +++++++++++++++++++++++++++++++++++++ - -error: malformed `warn` attribute input - --> $DIR/malformed-attrs.rs:182:1 - | -LL | #[warn] - | ^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL | #[warn(lint1)] - | +++++++ -LL | #[warn(lint1, lint2, ...)] - | +++++++++++++++++++ -LL | #[warn(lint1, lint2, lint3, reason = "...")] - | +++++++++++++++++++++++++++++++++++++ - -error: malformed `deny` attribute input - --> $DIR/malformed-attrs.rs:184:1 - | -LL | #[deny] - | ^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL | #[deny(lint1)] - | +++++++ -LL | #[deny(lint1, lint2, ...)] - | +++++++++++++++++++ -LL | #[deny(lint1, lint2, lint3, reason = "...")] - | +++++++++++++++++++++++++++++++++++++ - -error: malformed `forbid` attribute input - --> $DIR/malformed-attrs.rs:186:1 - | -LL | #[forbid] - | ^^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL | #[forbid(lint1)] - | +++++++ -LL | #[forbid(lint1, lint2, ...)] - | +++++++++++++++++++ -LL | #[forbid(lint1, lint2, lint3, reason = "...")] - | +++++++++++++++++++++++++++++++++++++ - error: the `proc_macro` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/malformed-attrs.rs:105:3 | @@ -714,6 +634,86 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage] | ^^^^^^^ expected this to be of the form `linkage = "..."` +error[E0539]: malformed `allow` attribute input + --> $DIR/malformed-attrs.rs:178:3 + | +LL | #[allow] + | ^^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL | #[allow(lint1)] + | +++++++ +LL | #[allow(lint1, lint2, ...)] + | +++++++++++++++++++ +LL | #[allow(lint1, lint2, lint3, reason = "...")] + | +++++++++++++++++++++++++++++++++++++ + +error[E0539]: malformed `expect` attribute input + --> $DIR/malformed-attrs.rs:180:3 + | +LL | #[expect] + | ^^^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL | #[expect(lint1)] + | +++++++ +LL | #[expect(lint1, lint2, ...)] + | +++++++++++++++++++ +LL | #[expect(lint1, lint2, lint3, reason = "...")] + | +++++++++++++++++++++++++++++++++++++ + +error[E0539]: malformed `warn` attribute input + --> $DIR/malformed-attrs.rs:182:3 + | +LL | #[warn] + | ^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL | #[warn(lint1)] + | +++++++ +LL | #[warn(lint1, lint2, ...)] + | +++++++++++++++++++ +LL | #[warn(lint1, lint2, lint3, reason = "...")] + | +++++++++++++++++++++++++++++++++++++ + +error[E0539]: malformed `deny` attribute input + --> $DIR/malformed-attrs.rs:184:3 + | +LL | #[deny] + | ^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL | #[deny(lint1)] + | +++++++ +LL | #[deny(lint1, lint2, ...)] + | +++++++++++++++++++ +LL | #[deny(lint1, lint2, lint3, reason = "...")] + | +++++++++++++++++++++++++++++++++++++ + +error[E0539]: malformed `forbid` attribute input + --> $DIR/malformed-attrs.rs:186:3 + | +LL | #[forbid] + | ^^^^^^ expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL | #[forbid(lint1)] + | +++++++ +LL | #[forbid(lint1, lint2, ...)] + | +++++++++++++++++++ +LL | #[forbid(lint1, lint2, lint3, reason = "...")] + | +++++++++++++++++++++++++++++++++++++ + error[E0539]: malformed `debugger_visualizer` attribute input --> $DIR/malformed-attrs.rs:188:3 | diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index bd136e64d3f92..33324685a84f4 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -21,15 +21,18 @@ pub fn c() {} pub fn d() {} #[unsafe(allow(dead_code))] -//~^ ERROR: is not an unsafe attribute +//~^ ERROR: allow` is not an unsafe attribute +//~| ERROR: `allow` is not an unsafe attribute +//~| ERROR: `allow` is not an unsafe attribute pub fn e() {} #[unsafe(allow(unsafe(dead_code)))] -//~^ ERROR: is not an unsafe attribute -//~| ERROR: malformed lint attribute input -//~| ERROR: malformed lint attribute input +//~^ ERROR: allow` is not an unsafe attribute +//~| ERROR: `allow` is not an unsafe attribute +//~| ERROR: `allow` is not an unsafe attribute +//~| ERROR: expected identifier, found keyword `unsafe` +//~| ERROR: expected identifier, found keyword `unsafe` //~| ERROR: expected identifier, found keyword `unsafe` -//~| ERROR: malformed lint attribute input //~| ERROR: malformed lint attribute input //~| ERROR: malformed lint attribute input //~| ERROR: malformed lint attribute input diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index 3b5baaace1a50..04e8c4d2dd453 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -1,19 +1,21 @@ -error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 +error: `allow` is not an unsafe attribute + --> $DIR/proc-unsafe-attributes.rs:23:3 | -LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument +LL | #[unsafe(allow(dead_code))] + | ^^^^^^ this is not an unsafe attribute + | + = note: extraneous unsafe is not allowed in attributes -error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 +error: `allow` is not an unsafe attribute + --> $DIR/proc-unsafe-attributes.rs:29:3 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^ this is not an unsafe attribute | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: extraneous unsafe is not allowed in attributes error: expected identifier, found keyword `unsafe` - --> $DIR/proc-unsafe-attributes.rs:27:16 + --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^ expected identifier, found keyword @@ -23,6 +25,12 @@ help: escape `unsafe` to use it as an identifier LL | #[unsafe(allow(r#unsafe(dead_code)))] | ++ +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:29:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + error: the `proc_macro` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/proc-unsafe-attributes.rs:1:10 | @@ -47,16 +55,38 @@ error: the `proc_macro_attribute` attribute is only usable with crates of the `p LL | #[unsafe(proc_macro_attribute)] | ^^^^^^^^^^^^^^^^^^^^ -error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 +error: `allow` is not an unsafe attribute + --> $DIR/proc-unsafe-attributes.rs:23:3 + | +LL | #[unsafe(allow(dead_code))] + | ^^^^^^ this is not an unsafe attribute + | + = note: extraneous unsafe is not allowed in attributes + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `allow` is not an unsafe attribute + --> $DIR/proc-unsafe-attributes.rs:29:3 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^ this is not an unsafe attribute | + = note: extraneous unsafe is not allowed in attributes = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: expected identifier, found keyword `unsafe` + --> $DIR/proc-unsafe-attributes.rs:29:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^ expected identifier, found keyword + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: escape `unsafe` to use it as an identifier + | +LL | #[unsafe(allow(r#unsafe(dead_code)))] + | ++ + error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 + --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument @@ -123,32 +153,38 @@ LL | #[unsafe(allow(dead_code))] | ^^^^^^ this is not an unsafe attribute | = note: extraneous unsafe is not allowed in attributes + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `allow` is not an unsafe attribute - --> $DIR/proc-unsafe-attributes.rs:27:3 + --> $DIR/proc-unsafe-attributes.rs:29:3 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^ this is not an unsafe attribute | = note: extraneous unsafe is not allowed in attributes + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 +error: expected identifier, found keyword `unsafe` + --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument + | ^^^^^^ expected identifier, found keyword | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: escape `unsafe` to use it as an identifier + | +LL | #[unsafe(allow(r#unsafe(dead_code)))] + | ++ error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:27:16 + --> $DIR/proc-unsafe-attributes.rs:29:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0452, E0565. For more information about an error, try `rustc --explain E0452`. diff --git a/tests/ui/eii/cfg_on_eii.stdout b/tests/ui/eii/cfg_on_eii.stdout index 1217ee5c6b2de..f6ed2aa90ed97 100644 --- a/tests/ui/eii/cfg_on_eii.stdout +++ b/tests/ui/eii/cfg_on_eii.stdout @@ -1,10 +1,9 @@ -//@ compile-flags: -Z unpretty=hir,typed - -#![deny(deprecated)] #![attr = Feature([extern_item_impls#0])] +#![attr = LintCheck([LintCheck {lint_name: "deprecated", kind: Deny}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ compile-flags: -Z unpretty=hir,typed const _: () = ({ diff --git a/tests/ui/inference/issue-71309.stderr b/tests/ui/inference/issue-71309.stderr index 673649c767e3d..a96cbccf02708 100644 --- a/tests/ui/inference/issue-71309.stderr +++ b/tests/ui/inference/issue-71309.stderr @@ -7,8 +7,8 @@ LL | let y: u32 = x?; = note: `?` operator cannot convert from `i32` to `u32` help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit | -LL | let y: u32 = x?.try_into().unwrap(); - | ++++++++++++++++++++ +LL | let y: u32 = (x?).try_into().unwrap(); + | + +++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/lint/dead-code-pub-in-binary/library.stderr b/tests/ui/lint/dead-code-pub-in-binary/library.stderr index 8ac71b01c6627..af3a6a104ad55 100644 --- a/tests/ui/lint/dead-code-pub-in-binary/library.stderr +++ b/tests/ui/lint/dead-code-pub-in-binary/library.stderr @@ -1,8 +1,8 @@ warning: unused attribute - --> $DIR/library.rs:2:1 + --> $DIR/library.rs:2:9 | LL | #![deny(dead_code_pub_in_binary)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: the `dead_code_pub_in_binary` lint has no effect in library crates = note: requested on the command line with `-W unused-attributes` diff --git a/tests/ui/lint/empty-lint-attributes.rs b/tests/ui/lint/empty-lint-attributes.rs index 4f550eae06363..9406925444b74 100644 --- a/tests/ui/lint/empty-lint-attributes.rs +++ b/tests/ui/lint/empty-lint-attributes.rs @@ -3,13 +3,21 @@ // Empty (and reason-only) lint attributes are legal—although we may want to // lint them in the future (Issue #55112). -#![allow()] //~ WARN unused attribute -#![warn(reason = "observationalism")] //~ WARN unused attribute +#![allow()] +//~^ WARN unused attribute +//~| WARN unused attribute +#![warn(reason = "observationalism")] +//~^ WARN unused attribute +//~| WARN unused attribute -#[forbid()] //~ WARN unused attribute +#[forbid()] +//~^ WARN unused attribute +//~| WARN unused attribute fn devoir() {} -#[deny(reason = "ultion")] //~ WARN unused attribute +#[deny(reason = "ultion")] +//~^ WARN unused attribute +//~| WARN unused attribute fn waldgrave() {} fn main() {} diff --git a/tests/ui/lint/empty-lint-attributes.stderr b/tests/ui/lint/empty-lint-attributes.stderr index 5bf8ae1e9ee7f..0c9b9771d007a 100644 --- a/tests/ui/lint/empty-lint-attributes.stderr +++ b/tests/ui/lint/empty-lint-attributes.stderr @@ -1,14 +1,30 @@ +warning: unused attribute + --> $DIR/empty-lint-attributes.rs:6:1 + | +LL | #![allow()] + | ^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `allow` with an empty list has no effect + = note: requested on the command line with `-W unused-attributes` + warning: unused attribute --> $DIR/empty-lint-attributes.rs:9:1 | +LL | #![warn(reason = "observationalism")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `warn` without any lints has no effect + +warning: unused attribute + --> $DIR/empty-lint-attributes.rs:13:1 + | LL | #[forbid()] | ^^^^^^^^^^^ help: remove this attribute | = note: attribute `forbid` with an empty list has no effect - = note: requested on the command line with `-W unused-attributes` warning: unused attribute - --> $DIR/empty-lint-attributes.rs:12:1 + --> $DIR/empty-lint-attributes.rs:18:1 | LL | #[deny(reason = "ultion")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute @@ -22,14 +38,34 @@ LL | #![allow()] | ^^^^^^^^^^^ help: remove this attribute | = note: attribute `allow` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: unused attribute - --> $DIR/empty-lint-attributes.rs:7:1 + --> $DIR/empty-lint-attributes.rs:9:1 | LL | #![warn(reason = "observationalism")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: attribute `warn` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unused attribute + --> $DIR/empty-lint-attributes.rs:13:1 + | +LL | #[forbid()] + | ^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `forbid` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unused attribute + --> $DIR/empty-lint-attributes.rs:18:1 + | +LL | #[deny(reason = "ultion")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `deny` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -warning: 4 warnings emitted +warning: 8 warnings emitted diff --git a/tests/ui/lint/inert-attr-macro.rs b/tests/ui/lint/inert-attr-macro.rs index 3c7542ff4d225..deb8995f36008 100644 --- a/tests/ui/lint/inert-attr-macro.rs +++ b/tests/ui/lint/inert-attr-macro.rs @@ -12,8 +12,10 @@ fn main() { // This does nothing, since `#[allow(warnings)]` is itself // an inert attribute on a macro call - #[allow(warnings)] #[inline] foo!(); //~ WARN unused attribute `allow` - //~^ WARN the `inline` attribute cannot be used on macro calls + #[allow(warnings)] #[inline] foo!(); + //~^ WARN the `allow` attribute cannot be used on macro calls + //~| WARN previously accepted + //~| WARN the `inline` attribute cannot be used on macro calls //~| WARN previously accepted // This does work, since the attribute is on a parent diff --git a/tests/ui/lint/inert-attr-macro.stderr b/tests/ui/lint/inert-attr-macro.stderr index dcb9fb3d61ce7..a13d17bd4b0c2 100644 --- a/tests/ui/lint/inert-attr-macro.stderr +++ b/tests/ui/lint/inert-attr-macro.stderr @@ -14,17 +14,15 @@ LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_attributes)]` implied by `#[warn(unused)]` -warning: unused attribute `allow` - --> $DIR/inert-attr-macro.rs:15:5 +warning: the `allow` attribute cannot be used on macro calls + --> $DIR/inert-attr-macro.rs:15:7 | LL | #[allow(warnings)] #[inline] foo!(); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^ | -note: the built-in attribute `allow` will be ignored, since it's applied to the macro invocation `foo` - --> $DIR/inert-attr-macro.rs:15:34 - | -LL | #[allow(warnings)] #[inline] foo!(); - | ^^^ + = help: the `allow` attribute can be applied to associated consts, associated types, break expressions, const parameters, constants, crates, data types, enum variants, extern crates, for loops, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, loops, macro defs, match arms, modules, pattern fields, statics, struct fields, trait aliases, traits, type aliases, type parameters, use statements, and while loops + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: placing this attribute on a macro invocation does nothing even if the macro expands to what would be a valid target for the attribute warning: the `inline` attribute cannot be used on macro calls --> $DIR/inert-attr-macro.rs:15:26 diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr index 39e6522a45f66..22842a7141453 100644 --- a/tests/ui/lint/linker-warning.stderr +++ b/tests/ui/lint/linker-warning.stderr @@ -11,10 +11,10 @@ LL | #![warn(unused_attributes)] | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/linker-warning.rs:4:1 + --> $DIR/linker-warning.rs:4:10 | LL | #![allow(linker_messages)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | ^^^^^^^^^^^^^^^ help: remove this attribute | = note: the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked diff --git a/tests/ui/lint/lint-malformed.stderr b/tests/ui/lint/lint-malformed.stderr index 25a4298bd75bf..7be928de86985 100644 --- a/tests/ui/lint/lint-malformed.stderr +++ b/tests/ui/lint/lint-malformed.stderr @@ -12,25 +12,6 @@ LL | #![allow(bar = "baz")] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: malformed `deny` attribute input - --> $DIR/lint-malformed.rs:1:1 - | -LL | #![deny = "foo"] - | ^^^^^^^^^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL - #![deny = "foo"] -LL + #![deny(lint1)] - | -LL - #![deny = "foo"] -LL + #![deny(lint1, lint2, ...)] - | -LL - #![deny = "foo"] -LL + #![deny(lint1, lint2, lint3, reason = "...")] - | - error[E0452]: malformed lint attribute input --> $DIR/lint-malformed.rs:2:10 | @@ -47,6 +28,27 @@ LL | #![allow(bar = "baz")] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error[E0539]: malformed `deny` attribute input + --> $DIR/lint-malformed.rs:1:4 + | +LL | #![deny = "foo"] + | ^^^^^------- + | | + | expected this to be a list + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #![deny = "foo"] +LL + #![deny(lint1)] + | +LL - #![deny = "foo"] +LL + #![deny(lint1, lint2, ...)] + | +LL - #![deny = "foo"] +LL + #![deny(lint1, lint2, lint3, reason = "...")] + | + error[E0452]: malformed lint attribute input --> $DIR/lint-malformed.rs:2:10 | @@ -65,4 +67,5 @@ LL | #![allow(bar = "baz")] error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0452`. +Some errors have detailed explanations: E0452, E0539. +For more information about an error, try `rustc --explain E0452`. diff --git a/tests/ui/lint/pre-expansion-parsing.rs b/tests/ui/lint/pre-expansion-parsing.rs index 3df56775c6c5f..7cfc57ff4e903 100644 --- a/tests/ui/lint/pre-expansion-parsing.rs +++ b/tests/ui/lint/pre-expansion-parsing.rs @@ -4,7 +4,6 @@ #[expect[wut]] // OK #[expect(expect)] // OK #[expect(expect(expect))] //~ ERROR malformed lint attribute input - //~| ERROR malformed lint attribute input #[deny(({!}))] // OK #[expect[helix::]] // OK #[cfg(false)] diff --git a/tests/ui/lint/pre-expansion-parsing.stderr b/tests/ui/lint/pre-expansion-parsing.stderr index c0c4e792b676f..ecc8b7017d92d 100644 --- a/tests/ui/lint/pre-expansion-parsing.stderr +++ b/tests/ui/lint/pre-expansion-parsing.stderr @@ -4,14 +4,6 @@ error[E0452]: malformed lint attribute input LL | #[expect(expect(expect))] | ^^^^^^^^^^^^^^ bad attribute argument -error[E0452]: malformed lint attribute input - --> $DIR/pre-expansion-parsing.rs:6:10 - | -LL | #[expect(expect(expect))] - | ^^^^^^^^^^^^^^ bad attribute argument - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0452`. diff --git a/tests/ui/lint/reasons-erroneous.rs b/tests/ui/lint/reasons-erroneous.rs index 0aa46953bf1ac..38ad00da64cd7 100644 --- a/tests/ui/lint/reasons-erroneous.rs +++ b/tests/ui/lint/reasons-erroneous.rs @@ -1,11 +1,12 @@ //@ compile-flags: -Zdeduplicate-diagnostics=yes #![warn(absolute_paths_not_starting_with_crate, reason = 0)] -//~^ ERROR malformed lint attribute -//~| NOTE reason must be a string literal +//~^ ERROR malformed `warn` attribute input +//~| NOTE expected a string literal here +//~| NOTE for more information, visit #![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")] -//~^ ERROR malformed lint attribute -//~| NOTE reason must be a string literal +//~^ ERROR malformed `warn` attribute input +//~| NOTE expected a normal string literal, not a byte string literal #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] //~^ ERROR malformed lint attribute //~| NOTE bad attribute argument diff --git a/tests/ui/lint/reasons-erroneous.stderr b/tests/ui/lint/reasons-erroneous.stderr index fcff88d8e0fa7..d771366d6168e 100644 --- a/tests/ui/lint/reasons-erroneous.stderr +++ b/tests/ui/lint/reasons-erroneous.stderr @@ -1,47 +1,66 @@ -error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:3:58 +error[E0539]: malformed `warn` attribute input + --> $DIR/reasons-erroneous.rs:3:4 | LL | #![warn(absolute_paths_not_starting_with_crate, reason = 0)] - | ^ reason must be a string literal + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^ + | | + | expected a string literal here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #![warn(absolute_paths_not_starting_with_crate, reason = 0)] +LL + #![warn(lint1)] + | +LL - #![warn(absolute_paths_not_starting_with_crate, reason = 0)] +LL + #![warn(lint1, lint2, ...)] + | +LL - #![warn(absolute_paths_not_starting_with_crate, reason = 0)] +LL + #![warn(lint1, lint2, lint3, reason = "...")] + | -error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:6:40 +error[E0539]: malformed `warn` attribute input + --> $DIR/reasons-erroneous.rs:7:4 | LL | #![warn(anonymous_parameters, reason = b"consider these, for we have condemned them")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reason must be a string literal + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider removing the prefix + | + = note: expected a normal string literal, not a byte string literal error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:9:29 + --> $DIR/reasons-erroneous.rs:10:29 | LL | #![warn(bare_trait_objects, reasons = "leaders to no sure land, guides their bearings lost")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:12:22 + --> $DIR/reasons-erroneous.rs:13:22 | LL | #![warn(unsafe_code, blerp = "or in league with robbers have reversed the signposts")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:15:36 + --> $DIR/reasons-erroneous.rs:16:36 | LL | #![warn(elided_lifetimes_in_paths, reason("disrespectful to ancestors", "irresponsible to heirs"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bad attribute argument error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:18:44 + --> $DIR/reasons-erroneous.rs:19:44 | LL | #![warn(ellipsis_inclusive_range_patterns, reason = "born barren", reason = "a freak growth")] | ^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last error[E0452]: malformed lint attribute input - --> $DIR/reasons-erroneous.rs:21:25 + --> $DIR/reasons-erroneous.rs:22:25 | LL | #![warn(keyword_idents, reason = "root in rubble", macro_use_extern_crate)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ reason in lint attribute must come last warning: unknown lint: `reason` - --> $DIR/reasons-erroneous.rs:24:39 + --> $DIR/reasons-erroneous.rs:25:39 | LL | #![warn(missing_copy_implementations, reason)] | ^^^^^^ @@ -50,4 +69,5 @@ LL | #![warn(missing_copy_implementations, reason)] error: aborting due to 7 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0452`. +Some errors have detailed explanations: E0452, E0539. +For more information about an error, try `rustc --explain E0452`. diff --git a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs index c93c94ae84a9a..52aa8c0980f7b 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs +++ b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs @@ -23,7 +23,9 @@ pub fn check_expect_on_item() { pub fn check_expect_on_macro() { // This should be fulfilled by the macro - #[expect(unused_variables)] //~ WARN unused attribute + #[expect(unused_variables)] + //~^ WARN the `expect` attribute cannot be used on macro call + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! trigger_unused_variables_macro!(); // FIXME: Lint attributes currently don't work directly on macros, and diff --git a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr index 19a41481542b5..ec0f260833ccf 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr +++ b/tests/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr @@ -1,14 +1,12 @@ -warning: unused attribute `expect` - --> $DIR/expect_lint_from_macro.rs:26:5 +warning: the `expect` attribute cannot be used on macro calls + --> $DIR/expect_lint_from_macro.rs:26:7 | LL | #[expect(unused_variables)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ | -note: the built-in attribute `expect` will be ignored, since it's applied to the macro invocation `trigger_unused_variables_macro` - --> $DIR/expect_lint_from_macro.rs:27:5 - | -LL | trigger_unused_variables_macro!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: the `expect` attribute can be applied to associated consts, associated types, break expressions, const parameters, constants, crates, data types, enum variants, extern crates, for loops, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, loops, macro defs, match arms, modules, pattern fields, statics, struct fields, trait aliases, traits, type aliases, type parameters, use statements, and while loops + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: placing this attribute on a macro invocation does nothing even if the macro expands to what would be a valid target for the attribute = note: requested on the command line with `-W unused-attributes` warning: unused variable: `x` diff --git a/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.rs b/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.rs index a7767769344a2..2d14bcda47ee0 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.rs +++ b/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.rs @@ -1,10 +1,20 @@ #![deny(unused_attributes)] -#[allow(reason = "I want to allow something")]//~ ERROR unused attribute -#[expect(reason = "I don't know what I'm waiting for")]//~ ERROR unused attribute -#[warn(reason = "This should be warn by default")]//~ ERROR unused attribute -#[deny(reason = "All listed lints are denied")]//~ ERROR unused attribute -#[forbid(reason = "Just some reason")]//~ ERROR unused attribute +#[allow(reason = "I want to allow something")] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#[expect(reason = "I don't know what I'm waiting for")] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#[warn(reason = "This should be warn by default")] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#[deny(reason = "All listed lints are denied")] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#[forbid(reason = "Just some reason")] +//~^ ERROR unused attribute +//~| ERROR unused attribute #[allow(clippy::box_collection, reason = "This is still valid")] #[warn(dead_code, reason = "This is also reasonable")] diff --git a/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.stderr b/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.stderr index 7f01c2dc61beb..b19514804e44f 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.stderr +++ b/tests/ui/lint/rfc-2383-lint-reason/lint-attribute-only-with-reason.stderr @@ -12,7 +12,7 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/lint-attribute-only-with-reason.rs:4:1 + --> $DIR/lint-attribute-only-with-reason.rs:6:1 | LL | #[expect(reason = "I don't know what I'm waiting for")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute @@ -20,28 +20,73 @@ LL | #[expect(reason = "I don't know what I'm waiting for")] = note: attribute `expect` without any lints has no effect error: unused attribute - --> $DIR/lint-attribute-only-with-reason.rs:5:1 + --> $DIR/lint-attribute-only-with-reason.rs:9:1 | LL | #[warn(reason = "This should be warn by default")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: attribute `warn` without any lints has no effect +error: unused attribute + --> $DIR/lint-attribute-only-with-reason.rs:12:1 + | +LL | #[deny(reason = "All listed lints are denied")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `deny` without any lints has no effect + +error: unused attribute + --> $DIR/lint-attribute-only-with-reason.rs:15:1 + | +LL | #[forbid(reason = "Just some reason")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `forbid` without any lints has no effect + +error: unused attribute + --> $DIR/lint-attribute-only-with-reason.rs:3:1 + | +LL | #[allow(reason = "I want to allow something")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `allow` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: unused attribute --> $DIR/lint-attribute-only-with-reason.rs:6:1 | +LL | #[expect(reason = "I don't know what I'm waiting for")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `expect` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/lint-attribute-only-with-reason.rs:9:1 + | +LL | #[warn(reason = "This should be warn by default")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `warn` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/lint-attribute-only-with-reason.rs:12:1 + | LL | #[deny(reason = "All listed lints are denied")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: attribute `deny` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused attribute - --> $DIR/lint-attribute-only-with-reason.rs:7:1 + --> $DIR/lint-attribute-only-with-reason.rs:15:1 | LL | #[forbid(reason = "Just some reason")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: attribute `forbid` without any lints has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 5 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs b/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs index 28c2a2c33abeb..d3c4a205c4b1f 100644 --- a/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs +++ b/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs @@ -48,5 +48,6 @@ fn main() { // This `#[allow]` does not work, since the attribute gets dropped // when we expand the macro let _ = #[allow(semicolon_in_expressions_from_macros)] foo!(allow_does_not_work); - //~^ WARN unused attribute + //~^ WARN the `allow` attribute cannot be used on macro calls + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! } diff --git a/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr b/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr index d722ea7ee19e8..fd2af817ea775 100644 --- a/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr +++ b/tests/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr @@ -33,17 +33,15 @@ LL | let _ = foo!(warn_in_expr); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79813 -warning: unused attribute `allow` - --> $DIR/semicolon-in-expressions-from-macros.rs:50:13 +warning: the `allow` attribute cannot be used on macro calls + --> $DIR/semicolon-in-expressions-from-macros.rs:50:15 | LL | let _ = #[allow(semicolon_in_expressions_from_macros)] foo!(allow_does_not_work); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ | -note: the built-in attribute `allow` will be ignored, since it's applied to the macro invocation `foo` - --> $DIR/semicolon-in-expressions-from-macros.rs:50:60 - | -LL | let _ = #[allow(semicolon_in_expressions_from_macros)] foo!(allow_does_not_work); - | ^^^ + = help: the `allow` attribute can be applied to associated consts, associated types, break expressions, const parameters, constants, crates, data types, enum variants, expressions, extern crates, for loops, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, loops, macro defs, match arms, modules, pattern fields, statements, statics, struct fields, trait aliases, traits, type aliases, type parameters, use statements, and while loops + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: placing this attribute on a macro invocation does nothing even if the macro expands to what would be a valid target for the attribute = note: requested on the command line with `-W unused-attributes` warning: trailing semicolon in macro used in expression position diff --git a/tests/ui/lint/unused/empty-attributes.rs b/tests/ui/lint/unused/empty-attributes.rs index 027d30cce17f0..c60d19680bf1b 100644 --- a/tests/ui/lint/unused/empty-attributes.rs +++ b/tests/ui/lint/unused/empty-attributes.rs @@ -1,9 +1,19 @@ #![deny(unused_attributes)] -#![allow()] //~ ERROR unused attribute -#![expect()] //~ ERROR unused attribute -#![warn()] //~ ERROR unused attribute -#![deny()] //~ ERROR unused attribute -#![forbid()] //~ ERROR unused attribute +#![allow()] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#![expect()] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#![warn()] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#![deny()] +//~^ ERROR unused attribute +//~| ERROR unused attribute +#![forbid()] +//~^ ERROR unused attribute +//~| ERROR unused attribute #![feature()] //~ ERROR unused attribute #[repr()] //~ ERROR unused attribute diff --git a/tests/ui/lint/unused/empty-attributes.stderr b/tests/ui/lint/unused/empty-attributes.stderr index 5b5ca73e5137e..8414b21cd4906 100644 --- a/tests/ui/lint/unused/empty-attributes.stderr +++ b/tests/ui/lint/unused/empty-attributes.stderr @@ -12,7 +12,7 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/empty-attributes.rs:3:1 + --> $DIR/empty-attributes.rs:5:1 | LL | #![expect()] | ^^^^^^^^^^^^ help: remove this attribute @@ -20,31 +20,76 @@ LL | #![expect()] = note: attribute `expect` with an empty list has no effect error: unused attribute - --> $DIR/empty-attributes.rs:4:1 + --> $DIR/empty-attributes.rs:8:1 | LL | #![warn()] | ^^^^^^^^^^ help: remove this attribute | = note: attribute `warn` with an empty list has no effect +error: unused attribute + --> $DIR/empty-attributes.rs:11:1 + | +LL | #![deny()] + | ^^^^^^^^^^ help: remove this attribute + | + = note: attribute `deny` with an empty list has no effect + +error: unused attribute + --> $DIR/empty-attributes.rs:14:1 + | +LL | #![forbid()] + | ^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `forbid` with an empty list has no effect + +error: unused attribute + --> $DIR/empty-attributes.rs:2:1 + | +LL | #![allow()] + | ^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `allow` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: unused attribute --> $DIR/empty-attributes.rs:5:1 | +LL | #![expect()] + | ^^^^^^^^^^^^ help: remove this attribute + | + = note: attribute `expect` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/empty-attributes.rs:8:1 + | +LL | #![warn()] + | ^^^^^^^^^^ help: remove this attribute + | + = note: attribute `warn` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: unused attribute + --> $DIR/empty-attributes.rs:11:1 + | LL | #![deny()] | ^^^^^^^^^^ help: remove this attribute | = note: attribute `deny` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused attribute - --> $DIR/empty-attributes.rs:6:1 + --> $DIR/empty-attributes.rs:14:1 | LL | #![forbid()] | ^^^^^^^^^^^^ help: remove this attribute | = note: attribute `forbid` with an empty list has no effect + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: unused attribute - --> $DIR/empty-attributes.rs:7:1 + --> $DIR/empty-attributes.rs:17:1 | LL | #![feature()] | ^^^^^^^^^^^^^ help: remove this attribute @@ -52,7 +97,7 @@ LL | #![feature()] = note: using `feature` with an empty list has no effect error: unused attribute - --> $DIR/empty-attributes.rs:9:1 + --> $DIR/empty-attributes.rs:19:1 | LL | #[repr()] | ^^^^^^^^^ help: remove this attribute @@ -60,12 +105,12 @@ LL | #[repr()] = note: using `repr` with an empty list has no effect error: unused attribute - --> $DIR/empty-attributes.rs:12:1 + --> $DIR/empty-attributes.rs:22:1 | LL | #[target_feature()] | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute | = note: using `target_feature` with an empty list has no effect -error: aborting due to 8 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/macros/meta-item-global-path-fail-issue-53048.rs b/tests/ui/macros/meta-item-global-path-fail-issue-53048.rs index b6fcbbcf9084f..a3987a54d02a1 100644 --- a/tests/ui/macros/meta-item-global-path-fail-issue-53048.rs +++ b/tests/ui/macros/meta-item-global-path-fail-issue-53048.rs @@ -13,6 +13,6 @@ m!(::); // value must be a literal, not a path #[allow(a = ::b::c)] -//~^ ERROR expected unsuffixed literal, found `::` +//~^ ERROR expected a literal fn main() {} diff --git a/tests/ui/macros/meta-item-global-path-fail-issue-53048.stderr b/tests/ui/macros/meta-item-global-path-fail-issue-53048.stderr index 85f7c5fadfc65..a1d76bcbc0930 100644 --- a/tests/ui/macros/meta-item-global-path-fail-issue-53048.stderr +++ b/tests/ui/macros/meta-item-global-path-fail-issue-53048.stderr @@ -13,11 +13,11 @@ error: expected unsuffixed literal, found `` LL | #[derive(::)] pub struct D; | ^^ -error: expected unsuffixed literal, found `::` +error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression --> $DIR/meta-item-global-path-fail-issue-53048.rs:15:13 | LL | #[allow(a = ::b::c)] - | ^^ + | ^^^^^^ expressions are not allowed here error: aborting due to 3 previous errors diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 6dd7b4c5e1f3a..7eca271aeda13 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -28,7 +28,7 @@ #![feature(try_blocks)] #![feature(try_blocks_heterogeneous)] #![feature(yeet_expr)] -#![allow(incomplete_features)] +#![allow(incomplete_features, unused_attributes)] extern crate std; #[prelude_import] use std::prelude::rust_2024::*; diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 025314709cf6c..34c5c7e589bc5 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -1,3 +1,13 @@ +#![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, +coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, +gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, +pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, +trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] +#![attr = LintCheck([LintCheck {lint_name: "incomplete_features", +kind: Allow}, LintCheck {lint_name: "unused_attributes", kind: Allow}])] +extern crate std; +#[attr = PreludeImport] +use std::prelude::rust_2024::*; //@ revisions: expanded hir //@[expanded]compile-flags: -Zunpretty=expanded //@[expanded]check-pass @@ -8,15 +18,6 @@ // Note: the HIR revision includes a `.stderr` file because there are some // errors that only occur once we get past the AST. -#![allow(incomplete_features)] -#![attr = Feature([auto_traits#0, builtin_syntax#0, const_trait_impl#0, -coroutines#0, decl_macro#0, deref_patterns#0, explicit_tail_calls#0, -gen_blocks#0, more_qualified_paths#0, never_patterns#0, pattern_types#0, -pattern_type_macro#0, prelude_import#0, specialization#0, trace_macros#0, -trait_alias#0, try_blocks#0, try_blocks_heterogeneous#0, yeet_expr#0])] -extern crate std; -#[attr = PreludeImport] -use std::prelude::rust_2024::*; mod prelude { use std::prelude::rust_2024::*; @@ -38,7 +39,8 @@ use self::prelude::*; * inner multi-line doc comment */ #[doc = "inner doc attribute"] -#[allow(dead_code, unused_variables)] +#[attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}, +LintCheck {lint_name: "unused_variables", kind: Allow}])] #[attr = NoStd] mod attributes { @@ -48,7 +50,6 @@ mod attributes { */ #[doc = "outer doc attribute"] #[doc = "macro"] - #[allow()] #[attr = Repr {reprs: [ReprC]}] struct Struct; } @@ -207,15 +208,7 @@ mod expressions { } /// ExprKind::Block - fn expr_block() { - { } - unsafe { } - 'a: { } - #[allow()] - { } - #[allow()] - { } - } + fn expr_block() { { } unsafe { } 'a: { } { } { } } /// ExprKind::Gen fn expr_gen() { @@ -243,9 +236,13 @@ mod expressions { type_ascribe!({ from_output(()) }, Option<_>); type_ascribe!({ from_output(match branch(None) { - Break { 0: residual } => #[allow(unreachable_code)] + Break { 0: residual } => + #[attr = LintCheck([LintCheck {lint_name: "unreachable_code", + kind: Allow}])] break from_residual(residual), - Continue { 0: val } => #[allow(unreachable_code)] + Continue { 0: val } => + #[attr = LintCheck([LintCheck {lint_name: "unreachable_code", + kind: Allow}])] val, }) }, Option) @@ -367,9 +364,13 @@ mod expressions { fn expr_try() { let expr; match branch(expr) { - Break { 0: residual } => #[allow(unreachable_code)] + Break { 0: residual } => + #[attr = LintCheck([LintCheck {lint_name: "unreachable_code", + kind: Allow}])] return from_residual(residual), - Continue { 0: val } => #[allow(unreachable_code)] + Continue { 0: val } => + #[attr = LintCheck([LintCheck {lint_name: "unreachable_code", + kind: Allow}])] val, }; } diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs index 62a8b6b9ecc20..03ef63874ac0a 100644 --- a/tests/ui/unpretty/exhaustive.rs +++ b/tests/ui/unpretty/exhaustive.rs @@ -27,7 +27,7 @@ #![feature(try_blocks)] #![feature(try_blocks_heterogeneous)] #![feature(yeet_expr)] -#![allow(incomplete_features)] +#![allow(incomplete_features, unused_attributes)] mod prelude { pub use std::prelude::rust_2024::*; diff --git a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout index f117acc810606..11d31361acdd6 100644 --- a/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout +++ b/tests/ui/unpretty/struct-exprs-tuple-call-pretty-printing.stdout @@ -1,12 +1,12 @@ -//@ compile-flags: -Zunpretty=hir -//@ check-pass - -#![allow(dead_code)] #![attr = Feature([adt_const_params#0, gca_adts#0, gca_min_const_items#0, gca_macroless_args#0])] +#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] extern crate std; #[attr = PreludeImport] use ::std::prelude::rust_2015::*; +//@ compile-flags: -Zunpretty=hir +//@ check-pass + use std::marker::ConstParamTy; diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index 19bfe92e3b27f..9c70b1fec7469 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -1,3 +1,7 @@ +#![attr = LintCheck([LintCheck {lint_name: "dead_code", kind: Allow}])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; // Regression test for the ICE described in #82328. The pretty-printer for // `-Zunpretty=hir,typed` would previously retrieve type-checking results // when entering a body, which means that type information was not available @@ -7,10 +11,6 @@ //@ check-pass //@ compile-flags: -Zunpretty=hir,typed //@ edition: 2015 -#![allow(dead_code)] -extern crate std; -#[attr = PreludeImport] -use ::std::prelude::rust_2015::*; fn main() ({ } as ()) diff --git a/tests/ui/where-clauses/unsupported_attribute.rs b/tests/ui/where-clauses/unsupported_attribute.rs index e86274f3ed5e7..505c42f127390 100644 --- a/tests/ui/where-clauses/unsupported_attribute.rs +++ b/tests/ui/where-clauses/unsupported_attribute.rs @@ -18,8 +18,8 @@ where #[should_panic] 'a: 'static, //~ ERROR attribute cannot be used on #[macro_use] T: Trait, //~ ERROR attribute cannot be used on #[macro_use] 'a: 'static, //~ ERROR attribute cannot be used on - #[allow(unused)] T: Trait, //~ ERROR most attributes are not supported in `where` clauses - #[allow(unused)] 'a: 'static, //~ ERROR most attributes are not supported in `where` clauses + #[allow(unused)] T: Trait, //~ ERROR the `allow` attribute cannot be used on where predicates + #[allow(unused)] 'a: 'static, //~ ERROR the `allow` attribute cannot be used on where predicates #[deprecated] T: Trait, //~ ERROR attribute cannot be used on #[deprecated] 'a: 'static, //~ ERROR attribute cannot be used on #[automatically_derived] T: Trait, //~ ERROR attribute cannot be used on diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr index 94f9719365c6e..678bbd813aaaa 100644 --- a/tests/ui/where-clauses/unsupported_attribute.stderr +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -74,21 +74,21 @@ LL | #[macro_use] 'a: 'static, | = help: the `macro_use` attribute can be applied to extern crates and modules -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:21:5 +error: the `allow` attribute cannot be used on where predicates + --> $DIR/unsupported_attribute.rs:21:7 | LL | #[allow(unused)] T: Trait, - | ^^^^^^^^^^^^^^^^ + | ^^^^^ | - = help: only `#[cfg]` and `#[cfg_attr]` are supported + = help: the `allow` attribute can be applied to associated consts, associated types, break expressions, const parameters, constants, crates, data types, enum variants, extern crates, for loops, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, loops, macro defs, match arms, modules, pattern fields, statics, struct fields, trait aliases, traits, type aliases, type parameters, use statements, and while loops -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:22:5 +error: the `allow` attribute cannot be used on where predicates + --> $DIR/unsupported_attribute.rs:22:7 | LL | #[allow(unused)] 'a: 'static, - | ^^^^^^^^^^^^^^^^ + | ^^^^^ | - = help: only `#[cfg]` and `#[cfg_attr]` are supported + = help: the `allow` attribute can be applied to associated consts, associated types, break expressions, const parameters, constants, crates, data types, enum variants, extern crates, for loops, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, loops, macro defs, match arms, modules, pattern fields, statics, struct fields, trait aliases, traits, type aliases, type parameters, use statements, and while loops error: the `deprecated` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:23:7