Use attribute name in message for "outer attr used as inner attr" errors

This commit is contained in:
Sasha Pourcelot 2025-08-23 19:45:00 +02:00
parent 93edf9f9b0
commit a8e9ca195e
22 changed files with 693 additions and 215 deletions

View file

@ -86,6 +86,12 @@ attr_parsing_invalid_repr_hint_no_value =
attr_parsing_invalid_since =
'since' must be a Rust version number, such as "1.31.0"
attr_parsing_invalid_style = {$is_used_as_inner ->
[false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]`
*[other] the `#![{$name}]` attribute can only be used at the crate root
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
.note = the value may not exceed `u16::MAX`

View file

@ -1,3 +1,5 @@
use rustc_feature::AttributeType;
use super::prelude::*;
pub(crate) struct CrateNameParser;
@ -7,6 +9,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
const TYPE: AttributeType = AttributeType::CrateLevel;
// FIXME: crate name is allowed on all targets and ignored,
// even though it should only be valid on crates of course

View file

@ -12,11 +12,15 @@
//! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the
//! contents of attributes, if an attribute appear multiple times in a list
//!
//! By default, attributes are allowed anywhere. When adding an attribute that should only be used
//! at the crate root, consider setting the `TYPE` in the parser trait to
//! [`AttributeType::CrateLevel`](rustc_feature::AttributeType::CrateLevel).
//!
//! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed.
use std::marker::PhantomData;
use rustc_feature::{AttributeTemplate, template};
use rustc_feature::{AttributeTemplate, AttributeType, template};
use rustc_hir::attrs::AttributeKind;
use rustc_span::{Span, Symbol};
use thin_vec::ThinVec;
@ -88,6 +92,8 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
const ALLOWED_TARGETS: AllowedTargets;
const TYPE: AttributeType = AttributeType::Normal;
/// The parser has gotten a chance to accept the attributes on an item,
/// here it can produce an attribute.
///
@ -129,6 +135,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;
const TYPE: AttributeType = AttributeType::Normal;
/// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
}
@ -175,6 +183,8 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S>
)];
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
const TYPE: AttributeType = T::TYPE;
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
Some(self.1?.0)
}
@ -259,6 +269,7 @@ pub(crate) trait NoArgsAttributeParser<S: Stage>: 'static {
const PATH: &[Symbol];
const ON_DUPLICATE: OnDuplicate<S>;
const ALLOWED_TARGETS: AllowedTargets;
const TYPE: AttributeType = AttributeType::Normal;
/// Create the [`AttributeKind`] given attribute's [`Span`].
const CREATE: fn(Span) -> AttributeKind;
@ -278,6 +289,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without
const ON_DUPLICATE: OnDuplicate<S> = T::ON_DUPLICATE;
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
const TEMPLATE: AttributeTemplate = template!(Word);
const TYPE: AttributeType = T::TYPE;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
if let Err(span) = args.no_args() {
@ -311,6 +323,8 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
/// The template this attribute parser should implement. Used for diagnostics.
const TEMPLATE: AttributeTemplate;
const TYPE: AttributeType = AttributeType::Normal;
/// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
fn extend<'c>(
cx: &'c mut AcceptContext<'_, '_, S>,
@ -346,6 +360,7 @@ impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S
group.items.extend(T::extend(cx, args))
})];
const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS;
const TYPE: AttributeType = T::TYPE;
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(first_span) = self.first_span {

View file

@ -1,5 +1,7 @@
use std::mem;
use rustc_feature::AttributeType;
use super::prelude::*;
use crate::attributes::{
AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
@ -154,6 +156,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser {
const PATH: &[Symbol] = &[sym::rustc_coherence_is_core];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TYPE: AttributeType = AttributeType::CrateLevel;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore;
}

View file

@ -4,12 +4,12 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use private::Sealed;
use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId};
use rustc_errors::{Diag, Diagnostic, Level};
use rustc_feature::AttributeTemplate;
use rustc_feature::{AttributeTemplate, AttributeType};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{AttrPath, HirId};
use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId};
use rustc_session::Session;
use rustc_span::{ErrorGuaranteed, Span, Symbol};
@ -80,6 +80,7 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(super) template: AttributeTemplate,
pub(super) accept_fn: AcceptFn<S>,
pub(super) allowed_targets: AllowedTargets,
pub(super) attribute_type: AttributeType,
}
type AcceptFn<S> =
@ -129,6 +130,7 @@ macro_rules! attribute_parsers {
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE,
});
}
@ -250,6 +252,8 @@ pub trait Stage: Sized + 'static + Sealed {
) -> ErrorGuaranteed;
fn should_emit(&self) -> ShouldEmit;
fn id_is_crate_root(id: Self::Id) -> bool;
}
// allow because it's a sealed trait
@ -271,6 +275,10 @@ impl Stage for Early {
fn should_emit(&self) -> ShouldEmit {
self.emit_errors
}
fn id_is_crate_root(id: Self::Id) -> bool {
id == CRATE_NODE_ID
}
}
// allow because it's a sealed trait
@ -292,6 +300,10 @@ impl Stage for Late {
fn should_emit(&self) -> ShouldEmit {
ShouldEmit::ErrorsAndLints
}
fn id_is_crate_root(id: Self::Id) -> bool {
id == CRATE_HIR_ID
}
}
/// used when parsing attributes for miscellaneous things *before* ast lowering

View file

@ -271,8 +271,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
};
(accept.accept_fn)(&mut cx, args);
if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_type(accept.attribute_type, target, &mut cx);
self.check_target(
path.get_attribute_path(),
attr.span,

View file

@ -60,5 +60,19 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emi
attr_span: *span,
},
),
&AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::InvalidAttrStyle {
name: name.clone(),
is_used_as_inner,
target_span: (!is_used_as_inner).then_some(target_span),
target,
},
)
}
}
}

View file

@ -6,7 +6,7 @@ use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
};
use rustc_feature::AttributeTemplate;
use rustc_hir::AttrPath;
use rustc_hir::{AttrPath, Target};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
@ -826,3 +826,13 @@ pub(crate) struct SuffixedLiteralInAttribute {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_invalid_style)]
pub(crate) struct InvalidAttrStyle {
pub name: AttrPath,
pub is_used_as_inner: bool,
#[note]
pub target_span: Option<Span>,
pub target: Target,
}

View file

@ -1,13 +1,14 @@
use std::borrow::Cow;
use rustc_ast::AttrStyle;
use rustc_errors::DiagArgValue;
use rustc_feature::Features;
use rustc_feature::{AttributeType, Features};
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{AttrPath, MethodKind, Target};
use rustc_span::Span;
use crate::AttributeParser;
use crate::context::Stage;
use crate::context::{AcceptContext, Stage};
use crate::session_diagnostics::InvalidTarget;
#[derive(Debug)]
@ -68,7 +69,7 @@ pub(crate) enum Policy {
Error(Target),
}
impl<S: Stage> AttributeParser<'_, S> {
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub(crate) fn check_target(
&self,
attr_name: AttrPath,
@ -111,6 +112,32 @@ impl<S: Stage> AttributeParser<'_, S> {
}
}
}
pub(crate) fn check_type(
attribute_type: AttributeType,
target: Target,
cx: &mut AcceptContext<'_, 'sess, S>,
) {
let is_crate_root = S::id_is_crate_root(cx.target_id);
if is_crate_root {
return;
}
if attribute_type != AttributeType::CrateLevel {
return;
}
let lint = AttributeLintKind::InvalidStyle {
name: cx.attr_path.clone(),
is_used_as_inner: cx.attr_style == AttrStyle::Inner,
target,
target_span: cx.target_span,
};
let attr_span = cx.attr_span;
cx.emit_lint(lint, attr_span);
}
}
/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.

View file

@ -35,4 +35,5 @@ pub enum AttributeLintKind {
IllFormedAttributeInput { suggestions: Vec<String> },
EmptyAttribute { first_span: Span },
InvalidTarget { name: AttrPath, target: Target, applied: Vec<String>, only: &'static str },
InvalidStyle { name: AttrPath, is_used_as_inner: bool, target: Target, target_span: Span },
}

View file

@ -462,8 +462,10 @@ passes_object_lifetime_err =
{$repr}
passes_outer_crate_level_attr =
crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
crate-level attribute should be an inner attribute
passes_outer_crate_level_attr_suggestion =
add a `!`
passes_panic_unwind_without_std =
unwinding panics are not supported without std

View file

@ -369,24 +369,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
if hir_id != CRATE_HIR_ID {
match attr {
// FIXME(jdonszelmann) move to attribute parsing when validation gets better there
&Attribute::Parsed(AttributeKind::CrateName {
attr_span: span, style, ..
}) => match style {
ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
span,
errors::OuterCrateLevelAttr,
),
ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
span,
errors::InnerCrateLevelAttr,
),
},
Attribute::Parsed(_) => { /* not crate-level */ }
Attribute::Parsed(_) => { /* Already validated. */ }
Attribute::Unparsed(attr) => {
// FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by
// the above
@ -397,12 +380,26 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
.and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
{
match attr.style {
ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
errors::OuterCrateLevelAttr,
),
ast::AttrStyle::Outer => {
let attr_span = attr.span;
let bang_position = self
.tcx
.sess
.source_map()
.span_until_char(attr_span, '[')
.shrink_to_hi();
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
errors::OuterCrateLevelAttr {
suggestion: errors::OuterCrateLevelAttrSuggestion {
bang_position,
},
},
)
}
ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
@ -1851,12 +1848,24 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
{
if hir_id != CRATE_HIR_ID {
match style {
Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
errors::OuterCrateLevelAttr,
),
Some(ast::AttrStyle::Outer) => {
let attr_span = attr.span();
let bang_position = self
.tcx
.sess
.source_map()
.span_until_char(attr_span, '[')
.shrink_to_hi();
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr_span,
errors::OuterCrateLevelAttr {
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
},
)
}
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,

View file

@ -64,7 +64,17 @@ pub(crate) struct MixedExportNameAndNoMangle {
#[derive(LintDiagnostic)]
#[diag(passes_outer_crate_level_attr)]
pub(crate) struct OuterCrateLevelAttr;
pub(crate) struct OuterCrateLevelAttr {
#[subdiagnostic]
pub suggestion: OuterCrateLevelAttrSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(passes_outer_crate_level_attr_suggestion, style = "verbose")]
pub(crate) struct OuterCrateLevelAttrSuggestion {
#[suggestion_part(code = "!")]
pub bang_position: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_inner_crate_level_attr)]

View file

@ -0,0 +1,10 @@
#![deny(unused)]
#[crate_name = "owo"]
//~^ ERROR: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]`
fn main() {}
mod inner {
#![crate_name = "iwi"]
//~^ ERROR: the `#![crate_name]` attribute can only be used at the crate root
}

View file

@ -0,0 +1,26 @@
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]`
--> $DIR/crate-only-as-outer.rs:2:1
|
LL | #[crate_name = "owo"]
| ^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
--> $DIR/crate-only-as-outer.rs:5:1
|
LL | fn main() {}
| ^^^^^^^^^^^^
note: the lint level is defined here
--> $DIR/crate-only-as-outer.rs:1:9
|
LL | #![deny(unused)]
| ^^^^^^
= note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
error: the `#![crate_name]` attribute can only be used at the crate root
--> $DIR/crate-only-as-outer.rs:8:5
|
LL | #![crate_name = "iwi"]
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -482,21 +482,26 @@ mod no_implicit_prelude {
#[reexport_test_harness_main = "2900"]
//~^ WARN crate-level attribute should be
//~| HELP add a `!`
mod reexport_test_harness_main {
mod inner { #![reexport_test_harness_main="2900"] }
//~^ WARN crate-level attribute should be
#[reexport_test_harness_main = "2900"] fn f() { }
//~^ WARN crate-level attribute should be
//~| HELP add a `!`
#[reexport_test_harness_main = "2900"] struct S;
//~^ WARN crate-level attribute should be
//~| HELP add a `!`
#[reexport_test_harness_main = "2900"] type T = S;
//~^ WARN crate-level attribute should be
//~| HELP add a `!`
#[reexport_test_harness_main = "2900"] impl S { }
//~^ WARN crate-level attribute should be
//~| HELP add a `!`
}
// Cannot feed "2700" to `#[macro_escape]` without signaling an error.
@ -534,21 +539,26 @@ mod macro_escape {
#[no_std]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod no_std {
mod inner { #![no_std] }
//~^ WARN crate-level attribute should be in the root module
#[no_std] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_std] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_std] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_std] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
// At time of authorship, #[proc_macro_derive = "2500"] signals error
@ -760,21 +770,26 @@ mod must_use {
#[windows_subsystem = "windows"]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod windows_subsystem {
mod inner { #![windows_subsystem="windows"] }
//~^ WARN crate-level attribute should be in the root module
#[windows_subsystem = "windows"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[windows_subsystem = "windows"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[windows_subsystem = "windows"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[windows_subsystem = "windows"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
// BROKEN USES OF CRATE-LEVEL BUILT-IN ATTRIBUTES
@ -782,135 +797,170 @@ mod windows_subsystem {
#[crate_name = "0900"]
//~^ WARN crate-level attribute should be an inner attribute
mod crate_name {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
mod inner { #![crate_name="0900"] }
//~^ WARN crate-level attribute should be in the root module
//~^ WARN the `#![crate_name]` attribute can only be used at the crate root
#[crate_name = "0900"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
#[crate_name = "0900"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
#[crate_name = "0900"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
#[crate_name = "0900"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
}
#[crate_type = "0800"]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod crate_type {
mod inner { #![crate_type="0800"] }
//~^ WARN crate-level attribute should be in the root module
#[crate_type = "0800"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[crate_type = "0800"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[crate_type = "0800"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[crate_type = "0800"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
#[feature(x0600)]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod feature {
mod inner { #![feature(x0600)] }
//~^ WARN crate-level attribute should be in the root module
#[feature(x0600)] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[feature(x0600)] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[feature(x0600)] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[feature(x0600)] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
#[no_main]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod no_main_1 {
mod inner { #![no_main] }
//~^ WARN crate-level attribute should be in the root module
#[no_main] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_main] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_main] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_main] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
#[no_builtins]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod no_builtins {
mod inner { #![no_builtins] }
//~^ WARN crate-level attribute should be in the root module
#[no_builtins] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_builtins] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_builtins] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[no_builtins] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
#[recursion_limit="0200"]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod recursion_limit {
mod inner { #![recursion_limit="0200"] }
//~^ WARN crate-level attribute should be in the root module
#[recursion_limit="0200"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[recursion_limit="0200"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[recursion_limit="0200"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[recursion_limit="0200"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
#[type_length_limit="0100"]
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
mod type_length_limit {
mod inner { #![type_length_limit="0100"] }
//~^ WARN crate-level attribute should be in the root module
#[type_length_limit="0100"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[type_length_limit="0100"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[type_length_limit="0100"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
#[type_length_limit="0100"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| HELP add a `!`
}
fn main() {}

View file

@ -1,4 +1,4 @@
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
warning: crate-level attribute should be an inner attribute
--> $DIR/linker-warning.rs:7:1
|
LL | #[allow(linker_messages)]
@ -9,6 +9,10 @@ note: the lint level is defined here
|
LL | #![warn(unused_attributes)]
| ^^^^^^^^^^^^^^^^^
help: add a `!`
|
LL | #![allow(linker_messages)]
| +
warning: unused attribute
--> $DIR/linker-warning.rs:4:1

View file

@ -10,11 +10,16 @@ note: the lint level is defined here
LL | #![deny(unused_attributes)]
| ^^^^^^^^^^^^^^^^^
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
error: crate-level attribute should be an inner attribute
--> $DIR/lint-misplaced-attr.rs:10:1
|
LL | #[crate_type = "bin"] fn main() {}
| ^^^^^^^^^^^^^^^^^^^^^
|
help: add a `!`
|
LL | #![crate_type = "bin"] fn main() {}
| +
error: aborting due to 2 previous errors

View file

@ -1,7 +1,7 @@
#![deny(unused)]
#[crate_name = concat !()]
//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]
//~^ ERROR crate-level attribute should be an inner attribute
macro_rules! a {
//~^ ERROR unused macro definition
() => {};

View file

@ -11,12 +11,20 @@ LL | #![deny(unused)]
| ^^^^^^
= note: `#[deny(unused_macros)]` implied by `#[deny(unused)]`
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]`
--> $DIR/concat-in-crate-name-issue-137687.rs:3:1
|
LL | #[crate_name = concat !()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this macro def
--> $DIR/concat-in-crate-name-issue-137687.rs:5:1
|
LL | / macro_rules! a {
LL | |
LL | | () => {};
LL | | }
| |_^
= note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
error: aborting due to 2 previous errors

View file

@ -1,4 +1,4 @@
error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
error: crate-level attribute should be an inner attribute
--> $DIR/unused-attr-macro-rules.rs:11:1
|
LL | #[recursion_limit="1"]
@ -9,6 +9,10 @@ note: the lint level is defined here
|
LL | #![deny(unused_attributes)]
| ^^^^^^^^^^^^^^^^^
help: add a `!`
|
LL | #![recursion_limit="1"]
| +
error: `#[macro_use]` attribute cannot be used on macro defs
--> $DIR/unused-attr-macro-rules.rs:7:1