From b188b85559d3bc41b31d51d5ba45e1adba731705 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Fri, 27 Feb 2026 15:32:44 +0000 Subject: [PATCH 1/3] check earlier for misused crate-level attributes --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- compiler/rustc_attr_parsing/src/errors.rs | 26 +++++ compiler/rustc_attr_parsing/src/interface.rs | 19 ++- compiler/rustc_attr_parsing/src/lib.rs | 2 + .../rustc_attr_parsing/src/target_checking.rs | 109 +++++++++++++++++- .../src/deriving/generic/mod.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 63 +--------- .../rustc_passes/src/debugger_visualizer.rs | 2 +- compiler/rustc_passes/src/errors.rs | 41 +------ .../rustc_resolve/src/build_reduced_graph.rs | 2 +- tests/ui/attributes/malformed-reprs.stderr | 14 +-- tests/ui/derives/issue-36617.stderr | 10 +- .../issue-43106-gating-of-bench.stderr | 2 +- ...sue-43106-gating-of-builtin-attrs-error.rs | 2 +- ...43106-gating-of-builtin-attrs-error.stderr | 30 ++--- .../issue-43106-gating-of-test.stderr | 2 +- tests/ui/lowering/issue-121108.stderr | 2 +- ...st-fix-invalid-top-level-macro-attr.stderr | 2 +- .../ui/span/issue-43927-non-ADT-derive.stderr | 2 +- 21 files changed, 193 insertions(+), 147 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/errors.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 830eb3d6d8170..d113dae6abf93 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -3,8 +3,8 @@ use rustc_ast::{self as ast, AttrVec, GenericBound, NodeId, PatKind, attr, token use rustc_attr_parsing::AttributeParser; use rustc_errors::msg; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; -use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; +use rustc_hir::Attribute; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, sym}; diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs new file mode 100644 index 0000000000000..f45b613c06e82 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -0,0 +1,26 @@ +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag("`{$name}` attribute cannot be used at crate level")] +pub(crate) struct InvalidAttrAtCrateLevel { + #[primary_span] + pub span: Span, + #[suggestion( + "perhaps you meant to use an outer attribute", + code = "#[", + applicability = "machine-applicable", + style = "verbose" + )] + pub pound_to_opening_bracket: Span, + pub name: Symbol, + #[subdiagnostic] + pub item: Option, +} + +#[derive(Clone, Copy, Subdiagnostic)] +#[label("the inner attribute doesn't annotate this item")] +pub(crate) struct ItemFollowingInnerAttr { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f75f63a0e811a..35bb86d2c1e9b 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -302,7 +302,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { kind: DocFragmentKind::Sugared(*comment_kind), span: attr_span, comment: *symbol, - })) + })); } ast::AttrKind::Normal(n) => { attr_paths.push(PathParser(&n.item.path)); @@ -405,15 +405,23 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { // "attribute {path} wasn't parsed and isn't a know tool attribute", // ); - attributes.push(Attribute::Unparsed(Box::new(AttrItem { + let attr = AttrItem { path: attr_path.clone(), args: self .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: attr_span, - }))); - } + }; + + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) + && target == Target::Crate + { + self.check_invalid_crate_level_attr_item(&attr, n.item.span()); + } + + attributes.push(Attribute::Unparsed(Box::new(attr))); + }; } } } @@ -477,4 +485,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } } + + + } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index db09572cc56b3..93eb5a0c3ab73 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -79,6 +79,7 @@ // tidy-alphabetical-start #![feature(decl_macro)] #![feature(iter_intersperse)] +#![feature(try_blocks)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -99,6 +100,7 @@ mod interface; pub mod parser; mod early_parsed; +mod errors; mod safety; mod session_diagnostics; mod target_checking; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index fb005477f0fa1..f2d1fef7d1373 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,14 +1,15 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; -use rustc_errors::DiagArgValue; +use rustc_errors::{DiagArgValue, StashKey}; use rustc_feature::Features; use rustc_hir::lints::AttributeLintKind; -use rustc_hir::{MethodKind, Target}; -use rustc_span::sym; +use rustc_hir::{AttrItem, MethodKind, Target}; +use rustc_span::{BytePos, Span, Symbol, sym}; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; +use crate::errors::{InvalidAttrAtCrateLevel, ItemFollowingInnerAttr}; use crate::session_diagnostics::InvalidTarget; use crate::target_checking::Policy::Allow; @@ -96,6 +97,27 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } + if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate { + // The allowed targets of `repr` depend on its arguments. They can't be checked using + // the `AttributeParser` code. + let span = cx.attr_span; + let item = cx + .cx + .first_line_of_next_item(span) + .map(|span| ItemFollowingInnerAttr { span }); + + let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span); + + cx.dcx() + .create_err(InvalidAttrAtCrateLevel { + span, + pound_to_opening_bracket, + name: sym::repr, + item, + }) + .emit(); + } + match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { @@ -163,6 +185,87 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); } + + // FIXME: Fix "Cannot determine resolution" error and remove built-in macros + // from this check. + pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) { + // Check for builtin attributes at the crate level + // which were unsuccessfully resolved due to cannot determine + // resolution for the attribute macro error. + const ATTRS_TO_CHECK: &[Symbol] = + &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench]; + + // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. + if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) { + let span = attr.span; + let name = *name; + + let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span }); + + let err = self.dcx().create_err(InvalidAttrAtCrateLevel { + span, + pound_to_opening_bracket: span.until(inner_span), + name, + item, + }); + + self.dcx().try_steal_replace_and_emit_err( + attr.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } + } + + fn first_line_of_next_item(&self, span: Span) -> Option { + // We can't exactly call `tcx.hir_free_items()` here because it's too early and querying + // this would create a circular dependency. Instead, we resort to getting the original + // source code that follows `span` and find the next item from here. + + self.sess() + .source_map() + .span_to_source(span, |content, _, span_end| { + let mut source = &content[span_end..]; + let initial_source_len = source.len(); + let span = try { + loop { + let first = source.chars().next()?; + + if first.is_whitespace() { + let split_idx = source.find(|c: char| !c.is_whitespace())?; + source = &source[split_idx..]; + } else if source.starts_with("//") { + let line_idx = source.find('\n')?; + source = &source[line_idx + '\n'.len_utf8()..]; + } else if source.starts_with("/*") { + // FIXME: support nested comments. + let close_idx = source.find("*/")?; + source = &source[close_idx + "*/".len()..]; + } else if first == '#' { + // FIXME: properly find the end of the attributes in order to accurately + // skip them. This version just consumes the source code until the next + // `]`. + let close_idx = source.find(']')?; + source = &source[close_idx + ']'.len_utf8()..]; + } else { + let lo = span_end + initial_source_len - source.len(); + let last_line = source.split('\n').next().map(|s| s.trim_end())?; + + let hi = lo + last_line.len(); + let lo = BytePos(lo as u32); + let hi = BytePos(hi as u32); + let next_item_span = Span::new(lo, hi, span.ctxt(), None); + + break next_item_span; + } + } + }; + + Ok(span) + }) + .ok() + .flatten() + } } /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index b392a9623d050..7fb87820cc75e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -188,8 +188,8 @@ use rustc_ast::{ }; use rustc_attr_parsing::AttributeParser; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, ReprPacked}; +use rustc_hir::Attribute; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use ty::{Bounds, Path, Ref, Self_, Ty}; diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 5764dfc83927a..2affad5a81ff4 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -8,8 +8,8 @@ use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, Diag, Level}; use rustc_expand::base::*; -use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; +use rustc_hir::Attribute; use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index af590d98c301c..8099f3f90f7b7 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -28,7 +28,9 @@ use rustc_hir::attrs::{AttributeKind, DocAttribute}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{ + self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr, +}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9945802d70c14..abd6cf1a3f28e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -15,7 +15,7 @@ use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey, msg}; +use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg}; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, @@ -49,7 +49,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; @@ -2055,64 +2055,6 @@ fn is_c_like_enum(item: &Item<'_>) -> bool { } } -// FIXME: Fix "Cannot determine resolution" error and remove built-in macros -// from this check. -fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { - // Check for builtin attributes at the crate level - // which were unsuccessfully resolved due to cannot determine - // resolution for the attribute macro error. - const ATTRS_TO_CHECK: &[Symbol] = - &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench]; - - for attr in attrs { - // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. - let (span, name) = if let Some(a) = - ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check)) - { - (attr.span(), *a) - } else if let Attribute::Parsed(AttributeKind::Repr { - reprs: _, - first_span: first_attr_span, - }) = attr - { - (*first_attr_span, sym::repr) - } else { - continue; - }; - - let item = tcx - .hir_free_items() - .map(|id| tcx.hir_item(id)) - .find(|item| !item.span.is_dummy()) // Skip prelude `use`s - .map(|item| errors::ItemFollowingInnerAttr { - span: if let Some(ident) = item.kind.ident() { ident.span } else { item.span }, - kind: tcx.def_descr(item.owner_id.to_def_id()), - }); - let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { - span, - sugg_span: tcx - .sess - .source_map() - .span_to_snippet(span) - .ok() - .filter(|src| src.starts_with("#![")) - .map(|_| span.with_lo(span.lo() + BytePos(1)).with_hi(span.lo() + BytePos(2))), - name, - item, - }); - - if let Attribute::Unparsed(p) = attr { - tcx.dcx().try_steal_replace_and_emit_err( - p.path.span, - StashKey::UndeterminedMacroResolution, - err, - ); - } else { - err.emit(); - } - } -} - fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) { let attrs = tcx.hir_attrs(item.hir_id()); @@ -2128,7 +2070,6 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { tcx.hir_visit_item_likes_in_module(module_def_id, check_attr_visitor); if module_def_id.to_local_def_id().is_top_level_module() { check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); - check_invalid_crate_level_attr(tcx, tcx.hir_krate_attrs()); } if check_attr_visitor.abort.get() { tcx.dcx().abort_if_errors() diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7211f3cf85b31..56b48197936fc 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -4,8 +4,8 @@ use rustc_ast::ast::NodeId; use rustc_ast::{HasNodeId, ItemKind, ast}; use rustc_attr_parsing::AttributeParser; use rustc_expand::base::resolve_path; -use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, DebugVisualizer}; +use rustc_hir::Attribute; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 6efbcd7bf85b6..3147349c628f2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -3,8 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, - MultiSpan, msg, + Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, msg, }; use rustc_hir::Target; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -447,44 +446,6 @@ pub(crate) struct LangItemOnIncorrectTarget { pub actual_target: Target, } -pub(crate) struct InvalidAttrAtCrateLevel { - pub span: Span, - pub sugg_span: Option, - pub name: Symbol, - pub item: Option, -} - -#[derive(Clone, Copy)] -pub(crate) struct ItemFollowingInnerAttr { - pub span: Span, - pub kind: &'static str, -} - -impl Diagnostic<'_, G> for InvalidAttrAtCrateLevel { - #[track_caller] - fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = - Diag::new(dcx, level, msg!("`{$name}` attribute cannot be used at crate level")); - diag.span(self.span); - diag.arg("name", self.name); - // Only emit an error with a suggestion if we can create a string out - // of the attribute span - if let Some(span) = self.sugg_span { - diag.span_suggestion_verbose( - span, - msg!("perhaps you meant to use an outer attribute"), - String::new(), - Applicability::MachineApplicable, - ); - } - if let Some(item) = self.item { - diag.arg("kind", item.kind); - diag.span_label(item.span, msg!("the inner attribute doesn't annotate this {$kind}")); - } - diag - } -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d9d42cfefcd81..ad5cf77022ac1 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -16,10 +16,10 @@ use rustc_attr_parsing as attr; use rustc_attr_parsing::AttributeParser; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; -use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, MacroUseArgs}; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; +use rustc_hir::Attribute; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::LoadedMacro; use rustc_middle::metadata::{ModChild, Reexport}; diff --git a/tests/ui/attributes/malformed-reprs.stderr b/tests/ui/attributes/malformed-reprs.stderr index 504ba91aac5f5..6e77c6e4b9a97 100644 --- a/tests/ui/attributes/malformed-reprs.stderr +++ b/tests/ui/attributes/malformed-reprs.stderr @@ -6,12 +6,6 @@ LL | #![repr] | = note: for more information, visit -error[E0589]: invalid `repr(align)` attribute: not a power of two - --> $DIR/malformed-reprs.rs:9:14 - | -LL | #[repr(align(0))] - | ^ - error: `repr` attribute cannot be used at crate level --> $DIR/malformed-reprs.rs:4:1 | @@ -19,7 +13,7 @@ LL | #![repr] | ^^^^^^^^ ... LL | enum Foo {} - | --- the inner attribute doesn't annotate this enum + | ----------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -27,6 +21,12 @@ LL - #![repr] LL + #[repr] | +error[E0589]: invalid `repr(align)` attribute: not a power of two + --> $DIR/malformed-reprs.rs:9:14 + | +LL | #[repr(align(0))] + | ^ + error[E0084]: unsupported representation for zero-variant enum --> $DIR/malformed-reprs.rs:9:1 | diff --git a/tests/ui/derives/issue-36617.stderr b/tests/ui/derives/issue-36617.stderr index 3de1d87c5046c..f76c5dfc7e067 100644 --- a/tests/ui/derives/issue-36617.stderr +++ b/tests/ui/derives/issue-36617.stderr @@ -5,7 +5,7 @@ LL | #![derive(Copy)] | ^^^^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -20,7 +20,7 @@ LL | #![test] | ^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -35,7 +35,7 @@ LL | #![test_case] | ^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -50,7 +50,7 @@ LL | #![bench] | ^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | @@ -65,7 +65,7 @@ LL | #![global_allocator] | ^^^^^^^^^^^^^^^^^^^^ ... LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr b/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr index 912c2746f3835..c51d615846f5a 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-bench.stderr @@ -5,7 +5,7 @@ LL | #![bench = "4100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 1560f2b5f4a77..66d36d9a5d6de 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -37,7 +37,7 @@ #[inline] //~^ ERROR attribute cannot be used on mod inline { - //~^ NOTE the inner attribute doesn't annotate this module + //~^ NOTE the inner attribute doesn't annotate this item mod inner { #![inline] } //~^ ERROR attribute cannot be used on diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 662776e580267..db56f22acfba6 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -24,6 +24,21 @@ LL | #![rustc_main] | = help: `#[rustc_main]` can only be applied to functions +error: `repr` attribute cannot be used at crate level + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 + | +LL | #![repr()] + | ^^^^^^^^^^ +... +LL | mod inline { + | ------------ the inner attribute doesn't annotate this item + | +help: perhaps you meant to use an outer attribute + | +LL - #![repr()] +LL + #[repr()] + | + error: `#[path]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:20:1 | @@ -243,21 +258,6 @@ help: remove the `#[no_mangle]` attribute LL - #![no_mangle] | -error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 - | -LL | #![repr()] - | ^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![repr()] -LL + #[repr()] - | - error[E0517]: attribute should be applied to a struct, enum, or union --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-test.stderr b/tests/ui/feature-gates/issue-43106-gating-of-test.stderr index 6e706b151b72f..8af140bc93d9b 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-test.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-test.stderr @@ -5,7 +5,7 @@ LL | #![test = "4200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | fn main() {} - | ---- the inner attribute doesn't annotate this function + | ------------ the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/lowering/issue-121108.stderr b/tests/ui/lowering/issue-121108.stderr index f68655a70021f..e1aa153eede64 100644 --- a/tests/ui/lowering/issue-121108.stderr +++ b/tests/ui/lowering/issue-121108.stderr @@ -5,7 +5,7 @@ LL | #![derive(Clone, Copy)] | ^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | use std::ptr::addr_of; - | ------- the inner attribute doesn't annotate this import + | ---------------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr b/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr index 09908160542d4..1d7b5ab8c6af1 100644 --- a/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr +++ b/tests/ui/proc-macro/issue-89566-suggest-fix-invalid-top-level-macro-attr.stderr @@ -5,7 +5,7 @@ LL | #![derive(Debug)] | ^^^^^^^^^^^^^^^^^ LL | #[allow(dead_code)] LL | struct Test {} - | ---- the inner attribute doesn't annotate this struct + | -------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | diff --git a/tests/ui/span/issue-43927-non-ADT-derive.stderr b/tests/ui/span/issue-43927-non-ADT-derive.stderr index 27ed561f5be8a..8742f34c45c62 100644 --- a/tests/ui/span/issue-43927-non-ADT-derive.stderr +++ b/tests/ui/span/issue-43927-non-ADT-derive.stderr @@ -5,7 +5,7 @@ LL | #![derive(Debug, PartialEq, Eq)] // should be an outer attribute! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct DerivedOn; - | --------- the inner attribute doesn't annotate this struct + | ----------------- the inner attribute doesn't annotate this item | help: perhaps you meant to use an outer attribute | From 6c65045a9f25f562622272da44ea258f996e9eee Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Wed, 4 Mar 2026 00:26:38 +0000 Subject: [PATCH 2/3] check earlier for attributes that are placed in `where` predicate --- compiler/rustc_attr_parsing/src/errors.rs | 9 ++ compiler/rustc_attr_parsing/src/interface.rs | 9 +- .../rustc_attr_parsing/src/target_checking.rs | 34 +++++- compiler/rustc_passes/src/check_attr.rs | 21 ---- compiler/rustc_passes/src/errors.rs | 8 -- tests/ui/where-clauses/cfg_attribute.a.stderr | 112 +++++++++--------- tests/ui/where-clauses/cfg_attribute.b.stderr | 112 +++++++++--------- .../unsupported_attribute.stderr | 64 +++++----- 8 files changed, 190 insertions(+), 179 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index f45b613c06e82..4eb9425b34bb0 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -1,3 +1,4 @@ +use rustc_errors::MultiSpan; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -24,3 +25,11 @@ pub(crate) struct ItemFollowingInnerAttr { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("most attributes are not supported in `where` clauses")] +#[help("only `#[cfg]` and `#[cfg_attr]` are supported")] +pub(crate) struct UnsupportedAttributesInWhere { + #[primary_span] + pub span: MultiSpan, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 35bb86d2c1e9b..d0da2125775d4 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -436,6 +436,12 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) + && target == Target::WherePredicate + { + self.check_invalid_where_predicate_attrs(attributes.iter()); + } + attributes } @@ -485,7 +491,4 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } } - - - } diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index f2d1fef7d1373..85a82ea5f1baa 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,15 +1,16 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; -use rustc_errors::{DiagArgValue, StashKey}; +use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; use rustc_feature::Features; +use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; -use rustc_hir::{AttrItem, MethodKind, Target}; +use rustc_hir::{AttrItem, Attribute, MethodKind, Target}; use rustc_span::{BytePos, Span, Symbol, sym}; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; -use crate::errors::{InvalidAttrAtCrateLevel, ItemFollowingInnerAttr}; +use crate::errors::{InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere}; use crate::session_diagnostics::InvalidTarget; use crate::target_checking::Policy::Allow; @@ -266,6 +267,33 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { .ok() .flatten() } + + pub(crate) fn check_invalid_where_predicate_attrs<'attr>( + &self, + attrs: impl IntoIterator, + ) { + // FIXME(where_clause_attrs): Currently, as the following check shows, + // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed + // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) + // in where clauses. After that, this function would become useless. + let spans = attrs + .into_iter() + // FIXME: We shouldn't need to special-case `doc`! + .filter(|attr| { + matches!( + attr, + Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_)) + | Attribute::Unparsed(_) + ) + }) + .map(|attr| attr.span()) + .collect::>(); + if !spans.is_empty() { + self.dcx().emit_err(UnsupportedAttributesInWhere { + span: MultiSpan::from_spans(spans), + }); + } + } } /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index abd6cf1a3f28e..2c3a5947154f2 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1937,27 +1937,6 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn visit_where_predicate(&mut self, where_predicate: &'tcx hir::WherePredicate<'tcx>) { - // FIXME(where_clause_attrs): Currently, as the following check shows, - // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed - // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) - // in where clauses. After that, only `self.check_attributes` should be enough. - let spans = self - .tcx - .hir_attrs(where_predicate.hir_id) - .iter() - // FIXME: We shouldn't need to special-case `doc`! - .filter(|attr| { - matches!( - attr, - Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_)) - | Attribute::Unparsed(_) - ) - }) - .map(|attr| attr.span()) - .collect::>(); - if !spans.is_empty() { - self.tcx.dcx().emit_err(errors::UnsupportedAttributesInWhere { span: spans.into() }); - } self.check_attributes( where_predicate.hir_id, where_predicate.span, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 3147349c628f2..8e80ff6e7be36 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1161,14 +1161,6 @@ pub(crate) struct RustcConstStableIndirectPairing { pub span: Span, } -#[derive(Diagnostic)] -#[diag("most attributes are not supported in `where` clauses")] -#[help("only `#[cfg]` and `#[cfg_attr]` are supported")] -pub(crate) struct UnsupportedAttributesInWhere { - #[primary_span] - pub span: MultiSpan, -} - #[derive(Diagnostic)] pub(crate) enum UnexportableItem<'a> { #[diag("{$descr}'s are not exportable")] diff --git a/tests/ui/where-clauses/cfg_attribute.a.stderr b/tests/ui/where-clauses/cfg_attribute.a.stderr index ee81072e70062..5bfa65df6032b 100644 --- a/tests/ui/where-clauses/cfg_attribute.a.stderr +++ b/tests/ui/where-clauses/cfg_attribute.a.stderr @@ -93,39 +93,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:65:5 + --> $DIR/cfg_attribute.rs:42:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:68:5 + --> $DIR/cfg_attribute.rs:45:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:99:5 + --> $DIR/cfg_attribute.rs:53:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:102:5 + --> $DIR/cfg_attribute.rs:56:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:113:5 + --> $DIR/cfg_attribute.rs:65:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:116:5 + --> $DIR/cfg_attribute.rs:68:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -141,39 +141,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:128:5 + --> $DIR/cfg_attribute.rs:75:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:131:5 + --> $DIR/cfg_attribute.rs:78:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:143:5 + --> $DIR/cfg_attribute.rs:86:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:146:5 + --> $DIR/cfg_attribute.rs:89:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:154:5 + --> $DIR/cfg_attribute.rs:99:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:157:5 + --> $DIR/cfg_attribute.rs:102:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:177:5 + --> $DIR/cfg_attribute.rs:113:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:180:5 + --> $DIR/cfg_attribute.rs:116:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -205,55 +205,55 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:42:9 + --> $DIR/cfg_attribute.rs:128:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:45:9 + --> $DIR/cfg_attribute.rs:131:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:53:9 + --> $DIR/cfg_attribute.rs:143:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:56:9 + --> $DIR/cfg_attribute.rs:146:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:75:9 + --> $DIR/cfg_attribute.rs:154:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:78:9 + --> $DIR/cfg_attribute.rs:157:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:86:9 + --> $DIR/cfg_attribute.rs:164:9 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -261,7 +261,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:89:9 + --> $DIR/cfg_attribute.rs:167:9 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -269,18 +269,18 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:164:9 + --> $DIR/cfg_attribute.rs:177:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:167:9 + --> $DIR/cfg_attribute.rs:180:5 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported diff --git a/tests/ui/where-clauses/cfg_attribute.b.stderr b/tests/ui/where-clauses/cfg_attribute.b.stderr index ee81072e70062..5bfa65df6032b 100644 --- a/tests/ui/where-clauses/cfg_attribute.b.stderr +++ b/tests/ui/where-clauses/cfg_attribute.b.stderr @@ -93,39 +93,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:65:5 + --> $DIR/cfg_attribute.rs:42:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:68:5 + --> $DIR/cfg_attribute.rs:45:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:99:5 + --> $DIR/cfg_attribute.rs:53:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:102:5 + --> $DIR/cfg_attribute.rs:56:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:113:5 + --> $DIR/cfg_attribute.rs:65:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:116:5 + --> $DIR/cfg_attribute.rs:68:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -141,39 +141,39 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:128:5 + --> $DIR/cfg_attribute.rs:75:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:131:5 + --> $DIR/cfg_attribute.rs:78:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():; + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:143:5 + --> $DIR/cfg_attribute.rs:86:9 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:146:5 + --> $DIR/cfg_attribute.rs:89:9 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:154:5 + --> $DIR/cfg_attribute.rs:99:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:157:5 + --> $DIR/cfg_attribute.rs:102:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:177:5 + --> $DIR/cfg_attribute.rs:113:5 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:180:5 + --> $DIR/cfg_attribute.rs:116:5 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -205,55 +205,55 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:42:9 + --> $DIR/cfg_attribute.rs:128:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:45:9 + --> $DIR/cfg_attribute.rs:131:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:53:9 + --> $DIR/cfg_attribute.rs:143:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:56:9 + --> $DIR/cfg_attribute.rs:146:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:75:9 + --> $DIR/cfg_attribute.rs:154:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:78:9 + --> $DIR/cfg_attribute.rs:157:5 | -LL | #[rustfmt::skip] ():; - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:86:9 + --> $DIR/cfg_attribute.rs:164:9 | LL | #[derive(Clone)] ():, | ^^^^^^^^^^^^^^^^ @@ -261,7 +261,7 @@ LL | #[derive(Clone)] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:89:9 + --> $DIR/cfg_attribute.rs:167:9 | LL | #[rustfmt::skip] ():, | ^^^^^^^^^^^^^^^^ @@ -269,18 +269,18 @@ LL | #[rustfmt::skip] ():, = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:164:9 + --> $DIR/cfg_attribute.rs:177:5 | -LL | #[derive(Clone)] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[derive(Clone)] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported error: most attributes are not supported in `where` clauses - --> $DIR/cfg_attribute.rs:167:9 + --> $DIR/cfg_attribute.rs:180:5 | -LL | #[rustfmt::skip] ():, - | ^^^^^^^^^^^^^^^^ +LL | #[rustfmt::skip] ():, + | ^^^^^^^^^^^^^^^^ | = help: only `#[cfg]` and `#[cfg_attr]` are supported diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr index e55a7c380c2be..11f13ffa4d007 100644 --- a/tests/ui/where-clauses/unsupported_attribute.stderr +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -10,6 +10,22 @@ error: expected non-macro attribute, found attribute macro `derive` LL | #[derive(Clone)] 'a: 'static, | ^^^^^^ not a non-macro attribute +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:13:5 + | +LL | #[doc = "doc"] T: Trait, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:14:5 + | +LL | #[doc = "doc"] 'a: 'static, + | ^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + error: `#[ignore]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:15:5 | @@ -58,6 +74,22 @@ LL | #[macro_use] 'a: 'static, | = help: `#[macro_use]` can be applied to crates, extern crates, and modules +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:21:5 + | +LL | #[allow(unused)] T: Trait, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + +error: most attributes are not supported in `where` clauses + --> $DIR/unsupported_attribute.rs:22:5 + | +LL | #[allow(unused)] 'a: 'static, + | ^^^^^^^^^^^^^^^^ + | + = help: only `#[cfg]` and `#[cfg_attr]` are supported + error: `#[deprecated]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:23:5 | @@ -90,38 +122,6 @@ LL | #[automatically_derived] 'a: 'static, | = help: `#[automatically_derived]` can only be applied to trait impl blocks -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:13:5 - | -LL | #[doc = "doc"] T: Trait, - | ^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:14:5 - | -LL | #[doc = "doc"] 'a: 'static, - | ^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:21:5 - | -LL | #[allow(unused)] T: Trait, - | ^^^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - -error: most attributes are not supported in `where` clauses - --> $DIR/unsupported_attribute.rs:22:5 - | -LL | #[allow(unused)] 'a: 'static, - | ^^^^^^^^^^^^^^^^ - | - = help: only `#[cfg]` and `#[cfg_attr]` are supported - error: most attributes are not supported in `where` clauses --> $DIR/unsupported_attribute.rs:27:5 | From a01ee9d9fffbb3a17568e83b0ddb6ee3d2dcdbc8 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Fri, 27 Feb 2026 15:32:44 +0000 Subject: [PATCH 3/3] drop derive helpers during ast lowering --- compiler/rustc_attr_parsing/src/interface.rs | 37 ++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index d0da2125775d4..b2bc85a6a84fb 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -267,6 +267,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { mut emit_lint: impl FnMut(LintId, Span, AttributeLintKind), ) -> Vec { let mut attributes = Vec::new(); + // We store the attributes we intend to discard at the end of this function in order to + // check they are applied to the right target and error out if necessary. In practice, we + // end up dropping only derive attributes and derive helpers, both being fully processed + // at macro expansion. + let mut dropped_attributes = Vec::new(); let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); @@ -390,21 +395,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { Self::check_target(&accept.allowed_targets, target, &mut cx); } } else { - // If we're here, we must be compiling a tool attribute... Or someone - // forgot to parse their fancy new attribute. Let's warn them in any case. - // If you are that person, and you really think your attribute should - // remain unparsed, carefully read the documentation in this module and if - // you still think so you can add an exception to this assertion. - - // FIXME(jdonszelmann): convert other attributes, and check with this that - // we caught em all - // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; - // assert!( - // self.tools.contains(&parts[0]) || true, - // // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), - // "attribute {path} wasn't parsed and isn't a know tool attribute", - // ); - let attr = AttrItem { path: attr_path.clone(), args: self @@ -420,8 +410,19 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { self.check_invalid_crate_level_attr_item(&attr, n.item.span()); } - attributes.push(Attribute::Unparsed(Box::new(attr))); - }; + let attr = Attribute::Unparsed(Box::new(attr)); + + if self.tools.contains(&parts[0]) + // FIXME: this can be removed once #152369 has been merged. + // https://github.com/rust-lang/rust/pull/152369 + || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn] + .contains(&parts[0]) + { + attributes.push(attr); + } else { + dropped_attributes.push(attr); + } + } } } } @@ -439,7 +440,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) && target == Target::WherePredicate { - self.check_invalid_where_predicate_attrs(attributes.iter()); + self.check_invalid_where_predicate_attrs(attributes.iter().chain(&dropped_attributes)); } attributes