diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index e8bef70be550a..662e5197d88d1 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -292,6 +292,11 @@ impl<'sess> AttributeParser<'sess> { mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute), ) -> 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(); @@ -452,7 +457,19 @@ impl<'sess> AttributeParser<'sess> { self.check_invalid_crate_level_attr_item(&attr, inner_span); } - attributes.push(Attribute::Unparsed(Box::new(attr))); + let attr = Attribute::Unparsed(Box::new(attr)); + + if self.sess.opts.actually_rustdoc + || self.tools.is_some_and(|t| t.iter().any(|i| i.name == parts[0])) + // FIXME: this can be removed once #155691 has been merged. + // https://github.com/rust-lang/rust/pull/155691 + || [sym::allow, sym::deny, sym::expect, sym::forbid, sym::warn] + .contains(&parts[0]) + { + attributes.push(attr); + } else { + dropped_attributes.push(attr); + } }; } } @@ -476,7 +493,7 @@ impl<'sess> AttributeParser<'sess> { } if !matches!(self.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