From 7c72d544dc952037ef1b9aff46b7b317e7eae41e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 14 May 2026 12:16:42 +0200 Subject: [PATCH 1/2] Introduce `CHECKED_LATER` for all targets except `MacroCall` --- .../src/attributes/codegen_attrs.rs | 2 +- .../src/attributes/crate_level.rs | 2 +- .../rustc_attr_parsing/src/attributes/doc.rs | 4 +- .../src/attributes/link_attrs.rs | 4 +- .../src/attributes/prelude.rs | 2 +- .../rustc_attr_parsing/src/attributes/repr.rs | 2 +- .../src/attributes/rustc_internal.rs | 4 +- .../src/attributes/semantics.rs | 2 +- .../rustc_attr_parsing/src/target_checking.rs | 79 ++++++++++++++++++- 9 files changed, 86 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 41abb4806567f..e6cd6bfd673c0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -556,7 +556,7 @@ impl SingleAttributeParser for SanitizeParser { const PATH: &[Symbol] = &[sym::sanitize]; // FIXME: still checked in check_attrs.rs - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); const TEMPLATE: AttributeTemplate = template!(List: &[ r#"address = "on|off""#, diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 9d6887a338739..c9e8ab2748741 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -293,7 +293,7 @@ impl CombineAttributeParser for RegisterToolParser { const PATH: &[Symbol] = &[sym::register_tool]; type Item = Ident; const CONVERT: ConvertFn = |tools, _span| AttributeKind::RegisterTool(tools); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); fn extend( diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 535f67f67a263..ec785bb298e5e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -9,7 +9,7 @@ use rustc_session::errors::feature_err; use rustc_span::{Span, Symbol, edition, sym}; use thin_vec::ThinVec; -use super::prelude::{ALL_TARGETS, AllowedTargets}; +use super::prelude::{AllowedTargets, CHECKED_LATER}; use super::{AcceptMapping, AttributeParser}; use crate::context::{AcceptContext, FinalizeContext}; use crate::errors::{ @@ -707,7 +707,7 @@ impl AttributeParser for DocParser { }, )]; // FIXME: Currently emitted from 2 different places, generating duplicated warnings. - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); // const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ // Allow(Target::ExternCrate), // Allow(Target::Use), diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index ec084a9fba09c..f2323ea8edb40 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -69,7 +69,7 @@ impl CombineAttributeParser for LinkParser { r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#, r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#, ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); //FIXME Still checked fully in `check_attr.rs` fn extend( cx: &mut AcceptContext<'_, '_>, @@ -528,7 +528,7 @@ impl SingleAttributeParser for LinkSectionParser { pub(crate) struct ExportStableParser; impl NoArgsAttributeParser for ExportStableParser { const PATH: &[Symbol] = &[sym::export_stable]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); //FIXME Still checked fully in `check_attr.rs` const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable; } diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 9b27dd3d53586..1e5fd688ed63d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -24,4 +24,4 @@ pub(super) use crate::parser::*; #[doc(hidden)] pub(super) use crate::target_checking::Policy::{Allow, Error, Warn}; #[doc(hidden)] -pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets}; +pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets, CHECKED_LATER}; diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 0ba32c6ca8fa9..6831e5795de4e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -58,7 +58,7 @@ impl CombineAttributeParser for ReprParser { //FIXME Still checked fully in `check_attr.rs` //This one is slightly more complicated because the allowed targets depend on the arguments - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); } macro_rules! int_pat { diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 8a648550ae8c6..0f5a95d24aaba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -532,7 +532,7 @@ pub(crate) struct LangParser; impl SingleAttributeParser for LangParser { const PATH: &[Symbol] = &[sym::lang]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); // Targets are checked per lang item in `rustc_passes` const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -564,7 +564,7 @@ pub(crate) struct PanicHandlerParser; impl NoArgsAttributeParser for PanicHandlerParser { const PATH: &[Symbol] = &[sym::panic_handler]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Targets are checked per lang item in `rustc_passes` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); // Targets are checked per lang item in `rustc_passes` const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Lang(LangItem::PanicImpl); } diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index b5c836e6119dc..4d06be521599f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -3,6 +3,6 @@ use super::prelude::*; pub(crate) struct MayDangleParser; impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs` + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(CHECKED_LATER); //FIXME Still checked fully in `check_attr.rs` const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle; } diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index d05f1baf63dad..7bda357d735b9 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -14,7 +14,7 @@ use crate::errors::{ UnsupportedAttributesInWhere, }; use crate::session_diagnostics::InvalidTarget; -use crate::target_checking::Policy::Allow; +use crate::target_checking::Policy::{Allow, Warn}; #[derive(Debug)] pub(crate) enum AllowedTargets { @@ -394,10 +394,81 @@ fn filter_targets( added_fake_targets.push(target_group_name); } +/// This is a list of default targets to which a attribute can be applied +/// This is used for attributes that are not parted to the new target checking system yet can use this list as a placeholder. +/// This excludes `Target::MacroCall`, as attributes on macro calls are otherwise not checked for parsed attributes. +pub(crate) const CHECKED_LATER: &'static [Policy] = { + use Policy::Allow; + &[ + 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), + 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), + Allow(Target::ForeignFn), + Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), + Allow(Target::MacroDef), + Allow(Target::Param), + Allow(Target::PatField), + Allow(Target::ExprField), + Allow(Target::WherePredicate), + Allow(Target::Crate), + Allow(Target::Delegation { mac: false }), + Allow(Target::Delegation { mac: true }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Const, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Const, + has_default: true, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Lifetime, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Lifetime, + has_default: true, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Type, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Type, + has_default: true, + }), + Warn(Target::MacroCall), + ] +}; + /// This is the list of all targets to which a attribute can be applied -/// This is used for: -/// - `rustc_dummy`, which can be applied to all targets -/// - Attributes that are not parted to the new target system yet can use this list as a placeholder +/// This is used for attributes that are actually allowed on all targets, such as `rustc_dummy` pub(crate) const ALL_TARGETS: &'static [Policy] = { use Policy::Allow; &[ From d69f6ba4a86a243d6e99a1793119ac131dfcc7dc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 14 May 2026 12:36:40 +0200 Subject: [PATCH 2/2] Add regression test --- tests/ui/attributes/macro-call-attr.rs | 55 +++++++++ tests/ui/attributes/macro-call-attr.stderr | 136 +++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 tests/ui/attributes/macro-call-attr.rs create mode 100644 tests/ui/attributes/macro-call-attr.stderr diff --git a/tests/ui/attributes/macro-call-attr.rs b/tests/ui/attributes/macro-call-attr.rs new file mode 100644 index 0000000000000..83af7062a3f7f --- /dev/null +++ b/tests/ui/attributes/macro-call-attr.rs @@ -0,0 +1,55 @@ +//@ check-pass +#![feature(sanitize)] +#![feature(register_tool)] +#![feature(export_stable)] +#![feature(lang_items)] +#![feature(dropck_eyepatch)] +#![feature(diagnostic_on_const)] +#![feature(diagnostic_on_move)] +#![feature(diagnostic_on_unknown)] +#![feature(diagnostic_on_unmatch_args)] +#![warn(unused)] + +macro_rules! test { () => {} } + +#[doc = ""] +//~^ WARN unused doc comment +#[diagnostic::do_not_recommend] +//~^ WARN can only be placed on trait implementations +#[diagnostic::on_const] +//~^ WARN can only be applied to non-const trait implementations +#[diagnostic::on_move] +//~^ WARN can only be applied to enums, structs or unions +#[diagnostic::on_unimplemented] +//~^ WARN can only be applied to trait definitions +#[diagnostic::on_unknown] +//~^ WARN can only be applied to `use` statements +#[diagnostic::on_unmatch_args] +//~^ WARN can only be applied to macro definitions +#[sanitize()] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[register_tool(test)] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[link(name = "x")] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[export_stable] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[repr(align(64))] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[lang = "sized"] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[panic_handler] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +#[may_dangle] +//~^ WARN attribute cannot be used on macro calls +//~| WARN previously accepted +test!(); + +fn main() {} diff --git a/tests/ui/attributes/macro-call-attr.stderr b/tests/ui/attributes/macro-call-attr.stderr new file mode 100644 index 0000000000000..997b7b99fab6e --- /dev/null +++ b/tests/ui/attributes/macro-call-attr.stderr @@ -0,0 +1,136 @@ +warning: unused doc comment + --> $DIR/macro-call-attr.rs:15:1 + | +LL | #[doc = ""] + | ^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations + | + = help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion +note: the lint level is defined here + --> $DIR/macro-call-attr.rs:11:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_doc_comments)]` implied by `#[warn(unused)]` + +warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations + --> $DIR/macro-call-attr.rs:17:1 + | +LL | #[diagnostic::do_not_recommend] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | test!(); + | ------- not a trait implementation + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `#[diagnostic::on_const]` can only be applied to non-const trait implementations + --> $DIR/macro-call-attr.rs:19:1 + | +LL | #[diagnostic::on_const] + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | test!(); + | ------- not a trait implementation + +warning: `#[diagnostic::on_move]` can only be applied to enums, structs or unions + --> $DIR/macro-call-attr.rs:21:1 + | +LL | #[diagnostic::on_move] + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + --> $DIR/macro-call-attr.rs:23:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements + --> $DIR/macro-call-attr.rs:25:1 + | +LL | #[diagnostic::on_unknown] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | test!(); + | ------- not an import + +warning: `#[diagnostic::on_unmatch_args]` can only be applied to macro definitions + --> $DIR/macro-call-attr.rs:27:1 + | +LL | #[diagnostic::on_unmatch_args] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: `#[sanitize]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:29:1 + | +LL | #[sanitize()] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[sanitize]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + = note: `#[warn(unused_attributes)]` implied by `#[warn(unused)]` + +warning: `#[register_tool]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:32:1 + | +LL | #[register_tool(test)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[register_tool]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[link]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:35:1 + | +LL | #[link(name = "x")] + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[link]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[export_stable]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:38:1 + | +LL | #[export_stable] + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[export_stable]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[repr]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:41:1 + | +LL | #[repr(align(64))] + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[repr]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[lang]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:44:1 + | +LL | #[lang = "sized"] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[lang]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[panic_handler]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:47:1 + | +LL | #[panic_handler] + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[panic_handler]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: `#[may_dangle]` attribute cannot be used on macro calls + --> $DIR/macro-call-attr.rs:50:1 + | +LL | #[may_dangle] + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[may_dangle]` can be applied to associated consts, associated types, const parameters, const parameters, constants, crates, data types, enum variants, extern crates, foreign modules, foreign statics, function params, functions, global asms, impl blocks, lifetime parameters, lifetime parameters, macro defs, match arms, modules, pattern fields, statics, struct fields, struct fields, trait aliases, traits, type aliases, type parameters, type parameters, use statements, and where predicates + +warning: 15 warnings emitted +