diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index df2f4b8871249..03284d4d9bd9b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,6 +1,6 @@ //! Functions dealing with attributes and meta items. -use std::fmt::Debug; +use std::fmt::{self, Debug, Display}; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_index::bit_set::GrowableBitSet; @@ -720,6 +720,31 @@ impl MetaItemLit { } } +/// An attribute relevant to the expansion of the proc macro harness performed +/// by `rustc_builtin_macros`. +#[derive(PartialEq, Debug)] +pub enum ProcMacroAttr { + /// `#[proc_macro_derive]` + Derive, + /// `#[proc_macro_attribute]` + Attribute, + /// `#[proc_macro]` + Bang, + /// `#[proc_macro_lint]` + Lint, +} + +impl Display for ProcMacroAttr { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(match self { + ProcMacroAttr::Derive => "proc_macro_derive", + ProcMacroAttr::Attribute => "proc_macro_attribute", + ProcMacroAttr::Bang => "proc_macro", + ProcMacroAttr::Lint => "proc_macro_lint", + }) + } +} + pub trait AttributeExt: Debug { fn id(&self) -> AttrId; @@ -776,10 +801,18 @@ pub trait AttributeExt: Debug { /// * `#[doc(...)]` returns `None`. fn doc_str(&self) -> Option; - fn is_proc_macro_attr(&self) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| self.has_name(*kind)) + fn proc_macro_attr(&self) -> Option { + if self.has_name(sym::proc_macro_derive) { + Some(ProcMacroAttr::Derive) + } else if self.has_name(sym::proc_macro_attribute) { + Some(ProcMacroAttr::Attribute) + } else if self.has_name(sym::proc_macro) { + Some(ProcMacroAttr::Bang) + } else if self.has_name(sym::proc_macro_lint) { + Some(ProcMacroAttr::Lint) + } else { + None + } } /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment. @@ -852,8 +885,8 @@ impl Attribute { AttributeExt::doc_str(self) } - pub fn is_proc_macro_attr(&self) -> bool { - AttributeExt::is_proc_macro_attr(self) + pub fn proc_macro_attr(&self) -> Option { + AttributeExt::proc_macro_attr(self) } pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0049c5b4823cb..ef274a51e842f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -20,6 +20,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use itertools::{Either, Itertools}; +use rustc_ast::attr::ProcMacroAttr; use rustc_ast::ptr::P; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; @@ -813,7 +814,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_item(&mut self, item: &'a Item) { - if item.attrs.iter().any(|attr| attr.is_proc_macro_attr()) { + if item.attrs.iter().any(|attr| attr.proc_macro_attr().is_some()) { self.has_proc_macro_decls = true; } @@ -1080,7 +1081,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.dcx().emit_err(errors::UnsafeStatic { span: item.span }); } - if expr.is_none() { + if expr.is_none() + && !item + .attrs + .iter() + .any(|attr| attr.proc_macro_attr() == Some(ProcMacroAttr::Lint)) + { self.dcx().emit_err(errors::StaticWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 4cac7cb93f581..2482cc965cb2a 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -272,8 +272,13 @@ builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions = the `#[{$path}]` attribute may only be used on bare functions +builtin_macros_proc_macro_attribute_only_be_used_on_statics = the `#[{$path}]` attribute may only be used on a `static` + builtin_macros_proc_macro_attribute_only_usable_with_crate_type = the `#[{$path}]` attribute is only usable with crates of the `proc-macro` crate type +builtin_macros_proc_macro_lint_id_is_filled_in_by_attribute = a unique LintId value is automatically filled in by `#[proc_macro_lint]` + .suggestion = remove this value + builtin_macros_requires_cfg_pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 6213bd802c751..bba3a6f320922 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -993,6 +993,22 @@ pub(crate) struct AttributeOnlyBeUsedOnBareFunctions<'a> { pub path: &'a str, } +#[derive(Diagnostic)] +#[diag(builtin_macros_proc_macro_attribute_only_be_used_on_statics)] +pub(crate) struct AttributeOnlyBeUsedOnStatics<'a> { + #[primary_span] + pub span: Span, + pub path: &'a str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_proc_macro_lint_id_is_filled_in_by_attribute)] +pub(crate) struct LintIdIsFilledInByAttribute { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(builtin_macros_proc_macro_attribute_only_usable_with_crate_type)] pub(crate) struct AttributeOnlyUsableWithCrateType<'a> { diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index ee6475c8b8e91..205fbb681afe5 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -1,9 +1,9 @@ use std::mem; +use rustc_ast::attr::{self, ProcMacroAttr}; +use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, NodeId, attr}; -use rustc_ast_pretty::pprust; +use rustc_ast::{self as ast, NodeId}; use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; @@ -27,7 +27,7 @@ struct ProcMacroDerive { struct ProcMacroDef { id: NodeId, - function_name: Ident, + item_name: Ident, span: Span, } @@ -35,11 +35,13 @@ enum ProcMacro { Derive(ProcMacroDerive), Attr(ProcMacroDef), Bang(ProcMacroDef), + Lint(ProcMacroDef), } struct CollectProcMacros<'a> { macros: Vec, in_root: bool, + cx: ExtCtxt<'a>, dcx: DiagCtxtHandle<'a>, source_map: &'a SourceMap, is_proc_macro_crate: bool, @@ -57,11 +59,12 @@ pub fn inject( dcx: DiagCtxtHandle<'_>, ) { let ecfg = ExpansionConfig::default("proc_macro".to_string(), features); - let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); + let cx = ExtCtxt::new(sess, ecfg, resolver, None); let mut collect = CollectProcMacros { macros: Vec::new(), in_root: true, + cx, dcx, source_map: sess.source_map(), is_proc_macro_crate, @@ -69,9 +72,8 @@ pub fn inject( }; if has_proc_macro_decls || is_proc_macro_crate { - visit::walk_crate(&mut collect, krate); + mut_visit::walk_crate(&mut collect, krate); } - let macros = collect.macros; if !is_proc_macro_crate { return; @@ -81,18 +83,18 @@ pub fn inject( return; } - let decls = mk_decls(&mut cx, ¯os); + let decls = mk_decls(&mut collect.cx, &collect.macros); krate.items.push(decls); } -impl<'a> CollectProcMacros<'a> { +impl CollectProcMacros<'_> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { self.dcx.emit_err(errors::ProcMacro { span: sp }); } } - fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + fn collect_custom_derive(&mut self, item: &ast::Item, attr: &ast::Attribute) { let Some((trait_name, proc_attrs)) = parse_macro_name_and_helper_attrs(self.dcx, attr, "derive") else { @@ -118,12 +120,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { + fn collect_attr_proc_macro(&mut self, item: &ast::Item) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Attr(ProcMacroDef { id: item.id, span: item.span, - function_name: item.ident, + item_name: item.ident, })); } else { let msg = if !self.in_root { @@ -136,12 +138,12 @@ impl<'a> CollectProcMacros<'a> { } } - fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { + fn collect_bang_proc_macro(&mut self, item: &ast::Item) { if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Bang(ProcMacroDef { id: item.id, span: item.span, - function_name: item.ident, + item_name: item.ident, })); } else { let msg = if !self.in_root { @@ -153,44 +155,54 @@ impl<'a> CollectProcMacros<'a> { self.dcx.span_err(self.source_map.guess_head_span(item.span), msg); } } + + fn collect_proc_macro_lint(&mut self, item: &ast::Item) { + if self.in_root && item.vis.kind.is_pub() { + self.macros.push(ProcMacro::Lint(ProcMacroDef { + id: item.id, + span: item.span, + item_name: item.ident, + })); + } else { + let msg = if !self.in_root { + "statics tagged with `#[proc_macro_lint]` must currently \ + reside in the root of the crate" + } else { + "statics tagged with `#[proc_macro_lint]` must be `pub`" + }; + self.dcx.span_err(self.source_map.guess_head_span(item.span), msg); + } + } } -impl<'a> Visitor<'a> for CollectProcMacros<'a> { - fn visit_item(&mut self, item: &'a ast::Item) { - if let ast::ItemKind::MacroDef(..) = item.kind { - if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { +impl MutVisitor for CollectProcMacros<'_> { + fn visit_item(&mut self, item: &mut P) { + let item_inner = &mut **item; + + if let ast::ItemKind::MacroDef(..) = item_inner.kind { + if self.is_proc_macro_crate && attr::contains_name(&item_inner.attrs, sym::macro_export) + { self.dcx.emit_err(errors::ExportMacroRules { - span: self.source_map.guess_head_span(item.span), + span: self.source_map.guess_head_span(item_inner.span), }); } } - // First up, make sure we're checking a bare function. If we're not then - // we're just not interested in this item. - // - // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. - let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); - - let mut found_attr: Option<&'a ast::Attribute> = None; - - for attr in &item.attrs { - if attr.is_proc_macro_attr() { - if let Some(prev_attr) = found_attr { - let prev_item = prev_attr.get_normal_item(); - let item = attr.get_normal_item(); - let path_str = pprust::path_to_string(&item.path); - let msg = if item.path.segments[0].ident.name - == prev_item.path.segments[0].ident.name - { + let mut found_attr = None::<(&ast::Attribute, ProcMacroAttr)>; + + for attr in &item_inner.attrs { + if let Some(kind) = attr.proc_macro_attr() { + if let Some((prev_attr, prev_kind)) = found_attr { + let msg = if prev_kind == kind { format!( - "only one `#[{path_str}]` attribute is allowed on any given function", + "only one `#[{kind}]` attribute is allowed on any given {descr}", + descr = item_inner.kind.descr(), ) } else { format!( - "`#[{}]` and `#[{}]` attributes cannot both be applied - to the same function", - path_str, - pprust::path_to_string(&prev_item.path), + "`#[{kind}]` and `#[{prev_kind}]` attributes cannot both be applied \ + to the same {descr}", + descr = item_inner.kind.descr(), ) }; @@ -202,26 +214,94 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { return; } - found_attr = Some(attr); + found_attr = Some((attr, kind)); } } - let Some(attr) = found_attr else { - self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span)); + let Some((attr, kind)) = found_attr else { + self.check_not_pub_in_root( + &item_inner.vis, + self.source_map.guess_head_span(item_inner.span), + ); let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); + mut_visit::walk_item(self, item); self.in_root = prev_in_root; return; }; - if !is_fn { - self.dcx - .create_err(errors::AttributeOnlyBeUsedOnBareFunctions { - span: attr.span, - path: &pprust::path_to_string(&attr.get_normal_item().path), - }) - .emit(); - return; + let mut extra_attrs = Vec::new(); + + match kind { + ProcMacroAttr::Derive | ProcMacroAttr::Attribute | ProcMacroAttr::Bang => { + if !matches!(item_inner.kind, ast::ItemKind::Fn(..)) { + self.dcx + .create_err(errors::AttributeOnlyBeUsedOnBareFunctions { + span: attr.span, + path: &kind.to_string(), + }) + .emit(); + return; + } + } + ProcMacroAttr::Lint => { + let ast::ItemKind::Static(static_item) = &mut item_inner.kind else { + self.dcx + .create_err(errors::AttributeOnlyBeUsedOnStatics { + span: attr.span, + path: &kind.to_string(), + }) + .emit(); + return; + }; + if let Some(expr) = &static_item.expr { + self.dcx + .create_err(errors::LintIdIsFilledInByAttribute { + span: static_item.ty.span.between(expr.span).to(expr.span), + }) + .emit(); + } else { + // Expand `pub static lintname: LintId;` into: + // + // #[allow(non_upper_case_globals)] + // pub static lintname: LintId = ::proc_macro::LintId::new("lintname"); + let expn_id = self.cx.resolver.expansion_for_ast_pass( + attr.span, + AstPass::ProcMacroHarness, + // Edition >2015 to be able to use ::proc_macro without generating an `extern crate`. + Some(rustc_span::edition::Edition::Edition2021), + &[sym::proc_macro_internals], + None, + ); + let span = attr.span.with_def_site_ctxt(expn_id.to_expn_id()); + extra_attrs.push(attr::mk_attr_nested_word( + &self.cx.psess().attr_id_generator, + ast::AttrStyle::Outer, + ast::Safety::Default, + sym::allow, + sym::non_upper_case_globals, + span, + )); + let lintid_new = self.cx.expr_call( + span, + self.cx.expr_path(self.cx.path( + span, + vec![ + Ident::new(kw::PathRoot, span), + Ident::new(sym::proc_macro, span), + Ident::new(sym::LintId, span), + Ident::new(sym::new, span), + ], + )), + thin_vec![self.cx.expr_str(span, item_inner.ident.name)], + ); + static_item.expr = Some( + self.cx + .monotonic_expander() + .fully_expand_fragment(AstFragment::Expr(lintid_new)) + .make_expr(), + ); + } + } } if self.is_test_crate { @@ -232,22 +312,25 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { self.dcx .create_err(errors::AttributeOnlyUsableWithCrateType { span: attr.span, - path: &pprust::path_to_string(&attr.get_normal_item().path), + path: &kind.to_string(), }) .emit(); return; } - if attr.has_name(sym::proc_macro_derive) { - self.collect_custom_derive(item, attr); - } else if attr.has_name(sym::proc_macro_attribute) { - self.collect_attr_proc_macro(item); - } else if attr.has_name(sym::proc_macro) { - self.collect_bang_proc_macro(item); - }; + match kind { + ProcMacroAttr::Derive => self.collect_custom_derive(item_inner, attr), + ProcMacroAttr::Attribute => self.collect_attr_proc_macro(item_inner), + ProcMacroAttr::Bang => self.collect_bang_proc_macro(item_inner), + ProcMacroAttr::Lint => self.collect_proc_macro_lint(item_inner), + } + + // Insert #[allow(non_upper_case_globals)]. + // This needs to happen after `attr` is finished being borrowed above. + item_inner.attrs.extend(extra_attrs); let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); + mut_visit::walk_item(self, item); self.in_root = prev_in_root; } } @@ -272,6 +355,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { let expn_id = cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::ProcMacroHarness, + None, &[sym::rustc_attrs, sym::proc_macro_internals], None, ); @@ -298,6 +382,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { let span = match m { ProcMacro::Derive(m) => m.span, ProcMacro::Attr(m) | ProcMacro::Bang(m) => m.span, + ProcMacro::Lint(m) => m.span, }; let local_path = |cx: &ExtCtxt<'_>, name| cx.expr_path(cx.path(span, vec![name])); let proc_macro_ty_method_path = |cx: &ExtCtxt<'_>, method| { @@ -332,7 +417,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { let ident = match m { ProcMacro::Attr(_) => attr, ProcMacro::Bang(_) => bang, - ProcMacro::Derive(_) => unreachable!(), + ProcMacro::Derive(_) | ProcMacro::Lint(_) => unreachable!(), }; // The call needs to use `harness_span` so that the const stability checker @@ -341,11 +426,19 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![ - cx.expr_str(span, ca.function_name.name), - local_path(cx, ca.function_name), + cx.expr_str(span, ca.item_name.name), + local_path(cx, ca.item_name), ], ) } + ProcMacro::Lint(lint) => { + cx.resolver.declare_proc_macro(lint.id); + cx.expr_call( + harness_span, + proc_macro_ty_method_path(cx, Ident::new(sym::lint, span)), + thin_vec![local_path(cx, lint.item_name)], + ) + } } }) .collect(); diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index 1a3f4d2d44908..08afe756919b9 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -34,6 +34,7 @@ pub fn inject( let expn_id = resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::StdImports, + None, &[sym::prelude_import], None, ); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 472e16e62d5b0..518efd206cc88 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -104,6 +104,7 @@ impl TestHarnessGenerator<'_> { let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass( span, AstPass::TestHarness, + None, &[], Some(node_id), ); @@ -241,6 +242,7 @@ fn generate_test_harness( let expn_id = ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::TestHarness, + None, &[sym::test, sym::rustc_attrs, sym::coverage_attribute], None, ); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 819694d1cdc1f..960da1fb0f651 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1007,6 +1007,7 @@ pub trait ResolverExpand { &mut self, call_site: Span, pass: AstPass, + edition: Option, features: &[Symbol], parent_module_id: Option, ) -> LocalExpnId; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index eb5fac96af270..f0fab0d64536d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -360,6 +360,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ErrorFollowing, EncodeCrossCrate::No, ), ungated!(proc_macro_attribute, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No), + gated!( + proc_macro_lint, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + proc_macro_diagnostic, experimental!(proc_macro_lint), + ), // Lints: ungated!( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3a2e810dc6af7..10766ddc4c9bb 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -600,6 +600,8 @@ declare_features! ( (unstable, postfix_match, "1.79.0", Some(121618)), /// Allows `use<..>` precise capturign on impl Trait in traits. (unstable, precise_capturing_in_traits, "1.83.0", Some(130044)), + /// Allows `#[proc_macro_lint]` in procedural macro crates. + (unstable, proc_macro_diagnostic, "CURRENT_RUSTC_VERSION", Some(54140)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 7e3a8561da08b..c99ce93c343fc 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -101,7 +101,10 @@ pub enum DefKind { AssocConst, // Macro namespace + /// `#[proc_macro]` or `#[proc_macro_attribute]` or `#[proc_macro_derive]` Macro(MacroKind), + /// `#[proc_macro_lint]` + LintId, // Not namespaced (or they are, but we don't treat them so) ExternCrate, @@ -178,6 +181,7 @@ impl DefKind { DefKind::TyParam => "type parameter", DefKind::ConstParam => "const parameter", DefKind::Macro(macro_kind) => macro_kind.descr(), + DefKind::LintId => "lint id", DefKind::LifetimeParam => "lifetime parameter", DefKind::Use => "import", DefKind::ForeignMod => "foreign module", @@ -235,7 +239,7 @@ impl DefKind { | DefKind::AssocFn | DefKind::AssocConst => Some(Namespace::ValueNS), - DefKind::Macro(..) => Some(Namespace::MacroNS), + DefKind::Macro(..) | DefKind::LintId => Some(Namespace::MacroNS), // Not namespaced. DefKind::AnonConst @@ -277,7 +281,7 @@ impl DefKind { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Field => DefPathData::ValueNs(name), - DefKind::Macro(..) => DefPathData::MacroNs(name), + DefKind::Macro(..) | DefKind::LintId => DefPathData::MacroNs(name), DefKind::LifetimeParam => DefPathData::LifetimeNs(name), DefKind::Ctor(..) => DefPathData::Ctor, DefKind::Use => DefPathData::Use, @@ -322,6 +326,7 @@ impl DefKind { | DefKind::Const | DefKind::AssocConst | DefKind::Macro(..) + | DefKind::LintId | DefKind::Use | DefKind::ForeignMod | DefKind::OpaqueTy diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index eafc60f9d72f7..cf10ca6b332ea 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_abi::ExternAbi; // ignore-tidy-filelength -use rustc_ast::attr::AttributeExt; +use rustc_ast::attr::{AttributeExt, ProcMacroAttr}; use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ @@ -1206,8 +1206,8 @@ impl Attribute { AttributeExt::doc_str(self) } - pub fn is_proc_macro_attr(&self) -> bool { - AttributeExt::is_proc_macro_attr(self) + pub fn proc_macro_attr(&self) -> Option { + AttributeExt::proc_macro_attr(self) } pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 1689437ffb0ed..769e68b0a71e8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2328,7 +2328,8 @@ fn lint_redundant_lifetimes<'tcx>( | DefKind::TraitAlias | DefKind::Fn | DefKind::Const - | DefKind::Impl { of_trait: _ } => { + | DefKind::Impl { of_trait: _ } + | DefKind::LintId => { // Proceed } DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index ffddf6f73aaf0..fc3982efd4aa1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2179,6 +2179,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | DefKind::AssocConst | DefKind::TyParam | DefKind::Macro(_) + | DefKind::LintId | DefKind::LifetimeParam | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e8dcda875e67a..84a950995c1a8 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1066,6 +1066,9 @@ impl<'a> CrateMetadataRef<'a> { ProcMacro::Bang { name, client } => { (name, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })), Vec::new()) } + ProcMacro::Lint { .. } => { + bug!("load_proc_macro called without checking that DefKind is Macro"); + } }; let sess = tcx.sess; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 81fb918c6040a..5c2945bd425ce 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -876,6 +876,7 @@ fn should_encode_span(def_kind: DefKind) -> bool { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::AnonConst @@ -907,6 +908,7 @@ fn should_encode_attrs(def_kind: DefKind) -> bool { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Macro(_) + | DefKind::LintId | DefKind::Field | DefKind::Impl { .. } => true, // Tools may want to be able to detect their tool lints on @@ -952,6 +954,7 @@ fn should_encode_expn_that_defined(def_kind: DefKind) -> bool { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod @@ -985,6 +988,7 @@ fn should_encode_visibility(def_kind: DefKind) -> bool { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Macro(..) + | DefKind::LintId | DefKind::Field => true, DefKind::Use | DefKind::ForeignMod @@ -1027,6 +1031,7 @@ fn should_encode_stability(def_kind: DefKind) -> bool { | DefKind::Trait | DefKind::TraitAlias | DefKind::Macro(..) + | DefKind::LintId | DefKind::ForeignTy => true, DefKind::Use | DefKind::LifetimeParam @@ -1122,6 +1127,7 @@ fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: Def | DefKind::Trait | DefKind::TraitAlias | DefKind::Macro(..) + | DefKind::LintId | DefKind::ForeignTy | DefKind::Use | DefKind::LifetimeParam @@ -1152,6 +1158,7 @@ fn should_encode_generics(def_kind: DefKind) -> bool { | DefKind::Ctor(..) | DefKind::AssocFn | DefKind::AssocConst + | DefKind::LintId | DefKind::AnonConst | DefKind::InlineConst | DefKind::OpaqueTy @@ -1187,6 +1194,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> | DefKind::Impl { .. } | DefKind::AssocFn | DefKind::AssocConst + | DefKind::LintId | DefKind::Closure | DefKind::ConstParam | DefKind::AnonConst @@ -1260,6 +1268,7 @@ fn should_encode_fn_sig(def_kind: DefKind) -> bool { | DefKind::Mod | DefKind::ForeignMod | DefKind::Macro(..) + | DefKind::LintId | DefKind::Use | DefKind::LifetimeParam | DefKind::GlobalAsm @@ -1293,6 +1302,7 @@ fn should_encode_constness(def_kind: DefKind) -> bool { | DefKind::Mod | DefKind::ForeignMod | DefKind::Macro(..) + | DefKind::LintId | DefKind::Use | DefKind::LifetimeParam | DefKind::GlobalAsm @@ -1329,6 +1339,7 @@ fn should_encode_const(def_kind: DefKind) -> bool { | DefKind::Mod | DefKind::ForeignMod | DefKind::Macro(..) + | DefKind::LintId | DefKind::Use | DefKind::LifetimeParam | DefKind::GlobalAsm @@ -1923,10 +1934,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = hir.attrs(proc_macro); - let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { - MacroKind::Bang + let def_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { + DefKind::Macro(MacroKind::Bang) } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { - MacroKind::Attr + DefKind::Macro(MacroKind::Attr) } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) { // This unwrap chain should have been checked by the proc-macro harness. name = attr.meta_item_list().unwrap()[0] @@ -1935,7 +1946,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .ident() .unwrap() .name; - MacroKind::Derive + DefKind::Macro(MacroKind::Derive) + } else if ast::attr::contains_name(attrs, sym::proc_macro_lint) { + DefKind::LintId } else { bug!("Unknown proc-macro type for item {:?}", id); }; @@ -1944,8 +1957,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind)); - self.tables.proc_macro.set_some(def_id.index, macro_kind); + self.tables.def_kind.set_some(def_id.index, def_kind); + if let DefKind::Macro(macro_kind) = def_kind { + self.tables.proc_macro.set_some(def_id.index, macro_kind); + } self.encode_attrs(id); record!(self.tables.def_keys[def_id] <- def_key); record!(self.tables.def_ident_span[def_id] <- span); diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 28f406fbc9629..89383e7dac258 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -170,6 +170,7 @@ fixed_size_enum! { ( Macro(MacroKind::Bang) ) ( Macro(MacroKind::Attr) ) ( Macro(MacroKind::Derive) ) + ( LintId ) ( SyntheticCoroutineBody ) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6fe1502c66dd6..62c7774ebd02e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2062,6 +2062,7 @@ impl<'tcx> TyCtxt<'tcx> { | DefKind::Static { .. } | DefKind::AssocConst | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index a9a47c87a3882..055dce89404b3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -632,6 +632,7 @@ impl<'tcx> Ty<'tcx> { | DefKind::AssocFn | DefKind::AssocConst | DefKind::Macro(..) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 978cb7af2427f..f0932750524da 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -622,6 +622,8 @@ passes_pass_by_value = passes_proc_macro_bad_sig = {$kind} has incorrect signature +passes_proc_macro_lint_wrong_type = wrong type for proc macro lint id + passes_remove_fields = consider removing { $num -> [one] this diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f578708b40cd3..7d1bbcb0b4ce0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -24,7 +24,7 @@ use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint::builtin::{ @@ -70,7 +70,7 @@ enum ItemLike<'tcx> { ForeignItem, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub(crate) enum ProcMacroKind { FunctionLike, Derive, @@ -231,14 +231,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), [sym::proc_macro, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + self.check_proc_macro_fn(hir_id, target, ProcMacroKind::FunctionLike) } [sym::proc_macro_attribute, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + self.check_proc_macro_fn(hir_id, target, ProcMacroKind::Attribute); } [sym::proc_macro_derive, ..] => { self.check_generic_attr(hir_id, attr, target, Target::Fn); - self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + self.check_proc_macro_fn(hir_id, target, ProcMacroKind::Derive) + } + [sym::proc_macro_lint, ..] => { + self.check_proc_macro_lint(hir_id, target, attr.span); } [sym::autodiff, ..] => { self.check_autodiff(hir_id, attr, span, target) @@ -2134,7 +2137,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Fn => { for attr in attrs { - if attr.is_proc_macro_attr() { + if attr.proc_macro_attr().is_some() { debug!("Is proc macro attr"); return; } @@ -2412,7 +2415,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// A best effort attempt to create an error for a mismatching proc macro signature. /// /// If this best effort goes wrong, it will just emit a worse error later (see #102923) - fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { + fn check_proc_macro_fn(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) { if target != Target::Fn { return; } @@ -2517,6 +2520,60 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_proc_macro_lint(&self, hir_id: HirId, target: Target, attr_span: Span) { + if target != Target::Static { + return; + } + + let def_id = hir_id.expect_owner().def_id; + let hir_ty: &hir::Ty<'_> = self.tcx.hir().expect_item(def_id).expect_static().0; + let actual: Ty<'_> = self.tcx.type_of(def_id).instantiate_identity(); + + let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = ObligationCause::misc(attr_span, def_id); + let param_env = ty::ParamEnv::empty(); + let actual = ocx.normalize(&cause, param_env, actual); + + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + return; + } + + let Some(expected) = self + .tcx + .get_diagnostic_item(sym::LintId) + .and_then(|lint_id| self.tcx.type_of(lint_id).no_bound_vars()) + else { + return; + }; + + if let Err(terr) = ocx.eq(&cause, param_env, expected, actual) { + let mut diag = + self.tcx.dcx().create_err(errors::ProcMacroLintWrongType { span: hir_ty.span }); + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + None, + Some(param_env.and(ValuePairs::Terms(ExpectedFound { + expected: ty::Term::from(expected), + found: ty::Term::from(actual), + }))), + terr, + false, + None, + ); + diag.emit(); + self.abort.set(true); + } + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(errors); + self.abort.set(true); + } + } + fn check_coroutine(&self, attr: &Attribute, target: Target) { match target { Target::Closure => return, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9bcdd2385470c..c5e731589961a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1705,6 +1705,13 @@ pub(crate) struct ProcMacroBadSig { pub kind: ProcMacroKind, } +#[derive(Diagnostic)] +#[diag(passes_proc_macro_lint_wrong_type)] +pub(crate) struct ProcMacroLintWrongType { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(passes_unreachable_due_to_uninhabited)] pub(crate) struct UnreachableDueToUninhabited<'desc, 'tcx> { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3842b7035e537..0b0d0b767cab9 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -557,7 +557,11 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.update(def_id, macro_ev, Level::Reachable); match def_kind { // No type privacy, so can be directly marked as reachable. - DefKind::Const | DefKind::Static { .. } | DefKind::TraitAlias | DefKind::TyAlias => { + DefKind::Const + | DefKind::Static { .. } + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::LintId => { if vis.is_accessible_from(module, self.tcx) { self.update(def_id, macro_ev, Level::Reachable); } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c6781ecc56681..dd5ea7b5a6a01 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -247,7 +247,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { | DefKind::Ctor(..), _, ) => self.define(parent, ident, ValueNS, (res, vis, span, expansion)), - Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => { + Res::Def(DefKind::Macro(..) | DefKind::LintId, _) | Res::NonMacroAttr(..) => { self.define(parent, ident, MacroNS, (res, vis, span, expansion)) } Res::Def( @@ -792,10 +792,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } // These items live in the value namespace. - ItemKind::Const(..) | ItemKind::Delegation(..) | ItemKind::Static(..) => { + ItemKind::Const(..) | ItemKind::Delegation(..) => { self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } - ItemKind::Fn(..) => { + ItemKind::Fn(..) | ItemKind::Static(..) => { self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); // Functions introducing procedural macros reserve a slot @@ -1177,17 +1177,19 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } - fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { + fn proc_macro_stub(&self, item: &ast::Item) -> Option<(DefKind, Ident, Span)> { if ast::attr::contains_name(&item.attrs, sym::proc_macro) { - return Some((MacroKind::Bang, item.ident, item.span)); + return Some((DefKind::Macro(MacroKind::Bang), item.ident, item.span)); } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) { - return Some((MacroKind::Attr, item.ident, item.span)); + return Some((DefKind::Macro(MacroKind::Attr), item.ident, item.span)); } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) && let Some(meta_item_inner) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) && let Some(ident) = meta_item_inner.ident() { - return Some((MacroKind::Derive, ident, ident.span)); + return Some((DefKind::Macro(MacroKind::Derive), ident, ident.span)); + } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_lint) { + return Some((DefKind::LintId, item.ident, item.span)); } None } @@ -1215,12 +1217,14 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let def_id = feed.key(); let (res, ident, span, macro_rules) = match &item.kind { ItemKind::MacroDef(def) => (self.res(def_id), item.ident, item.span, def.macro_rules), - ItemKind::Fn(..) => match self.proc_macro_stub(item) { - Some((macro_kind, ident, span)) => { - let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id()); - let macro_data = MacroData::new(self.r.dummy_ext(macro_kind)); - self.r.macro_map.insert(def_id.to_def_id(), macro_data); - self.r.proc_macro_stubs.insert(def_id); + ItemKind::Fn(..) | ItemKind::Static(..) => match self.proc_macro_stub(item) { + Some((def_kind, ident, span)) => { + let res = Res::Def(def_kind, def_id.to_def_id()); + if let DefKind::Macro(macro_kind) = def_kind { + let macro_data = MacroData::new(self.r.dummy_ext(macro_kind)); + self.r.macro_map.insert(def_id.to_def_id(), macro_data); + self.r.proc_macro_stubs.insert(def_id); + } (res, ident, span, false) } None => return parent_scope.macro_rules, @@ -1278,12 +1282,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let vis = match item.kind { // Visibilities must not be resolved non-speculatively twice // and we already resolved this one as a `fn` item visibility. - ItemKind::Fn(..) => { + ItemKind::Fn(..) | ItemKind::Static(..) => { self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public) } _ => self.resolve_visibility(&item.vis), }; - if !vis.is_public() { + if !vis.is_public() && matches!(res, Res::Def(DefKind::Macro(_), ..)) { self.insert_unused_macro(ident, def_id, item.id); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index cca01a01e9877..91d39b9a46feb 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -206,6 +206,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { &mut self, call_site: Span, pass: AstPass, + edition: Option, features: &[Symbol], parent_module_id: Option, ) -> LocalExpnId { @@ -215,7 +216,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ExpnData::allow_unstable( ExpnKind::AstPass(pass), call_site, - self.tcx.sess.edition(), + edition.unwrap_or_else(|| self.tcx.sess.edition()), features.into(), None, parent_module, @@ -588,6 +589,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } + Res::Def(DefKind::LintId, _) => { + self.dcx().emit_err(MacroExpectedFound { + span: path.span, + expected: kind.descr_expected(), + article: "a", + found: res.descr(), + macro_path: &pprust::path_to_string(path), + remove_surrounding_derive: None, + add_as_non_derive: None, + }); + return Ok((self.dummy_ext(kind), Res::Err)); + } Res::NonMacroAttr(..) | Res::Err => {} _ => panic!("expected `DefKind::Macro` or `Res::NonMacroAttr`"), }; diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index c5d33f090a05b..c9382339775c0 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -134,6 +134,7 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind { | DefKind::TyParam | DefKind::ConstParam | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c819d43323583..f5907f5ec0a2b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -267,6 +267,7 @@ symbols! { Left, LinkedList, LintDiagnostic, + LintId, LintPass, LocalKey, Mutex, @@ -1201,6 +1202,7 @@ symbols! { linkage, linker, linker_messages, + lint, lint_reasons, literal, load, @@ -1409,6 +1411,7 @@ symbols! { non_exhaustive_omitted_patterns_lint, non_lifetime_binders, non_modrs_mods, + non_upper_case_globals, none, nontemporal_store, noop_method_borrow, @@ -1558,10 +1561,12 @@ symbols! { proc_macro, proc_macro_attribute, proc_macro_derive, + proc_macro_diagnostic, proc_macro_expr, proc_macro_gen, proc_macro_hygiene, proc_macro_internals, + proc_macro_lint, proc_macro_mod, proc_macro_non_items, proc_macro_path_invoc, diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index cc7baf8daac54..e3a62afe6241a 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -138,6 +138,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' | DefKind::Static { .. } | DefKind::Ctor(_, _) | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 34f461aac58a3..d29b57ace7fea 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -342,6 +342,7 @@ fn opaque_types_defined_by<'tcx>( | DefKind::ConstParam | DefKind::Ctor(_, _) | DefKind::Macro(_) + | DefKind::LintId | DefKind::ExternCrate | DefKind::Use | DefKind::ForeignMod diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 64e5a609b2f6d..e7db3dc877864 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -47,7 +47,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // Walk over the type behind the alias DefKind::TyAlias { .. } | DefKind::AssocTy | // Walk over the type of the item - DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => { + DefKind::Static { .. } | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::LintId => { if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { // If the type of the item uses `_`, we're gonna error out anyway, but // typeck (which type_of invokes below), will call back into opaque_types_defined_by diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index f6d4825c67b24..834a0aa4adb51 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use std::sync::atomic::AtomicU32; use super::*; +use crate::LintId; macro_rules! define_client_handles { ( @@ -380,6 +381,10 @@ pub enum ProcMacro { name: &'static str, client: Client, }, + + Lint { + id: LintId, + }, } impl ProcMacro { @@ -388,6 +393,7 @@ impl ProcMacro { ProcMacro::CustomDerive { trait_name, .. } => trait_name, ProcMacro::Attr { name, .. } => name, ProcMacro::Bang { name, .. } => name, + ProcMacro::Lint { id } => id.name, } } @@ -412,4 +418,8 @@ impl ProcMacro { ) -> Self { ProcMacro::Bang { name, client: Client::expand1(expand) } } + + pub const fn lint(id: LintId) -> Self { + ProcMacro::Lint { id } + } } diff --git a/library/proc_macro/src/diagnostic.rs b/library/proc_macro/src/diagnostic.rs index 5a209f7c7aa18..5e8f2ac5e662a 100644 --- a/library/proc_macro/src/diagnostic.rs +++ b/library/proc_macro/src/diagnostic.rs @@ -1,4 +1,4 @@ -use crate::Span; +use crate::{LintId, Span}; /// An enum representing a diagnostic level. #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] @@ -112,7 +112,27 @@ impl Diagnostic { } diagnostic_child_methods!(span_error, error, Level::Error); - diagnostic_child_methods!(span_warning, warning, Level::Warning); + + /// Adds a new child diagnostics message to `self` with the [`Level::Warning`] level, and the given `spans` and `message`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn span_warning(mut self, id: LintId, spans: S, message: T) -> Diagnostic + where + S: MultiSpan, + T: Into, + { + let _ = id; // to be used in the future for allow/warn/deny + self.children.push(Diagnostic::spanned(spans, Level::Warning, message)); + self + } + + /// Adds a new child diagnostic message to `self` with the [`Level::Warning`] level, and the given `message`. + #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] + pub fn warning>(mut self, id: LintId, message: T) -> Diagnostic { + let _ = id; // to be used in the future for allow/warn/deny + self.children.push(Diagnostic::new(Level::Warning, message)); + self + } + diagnostic_child_methods!(span_note, note, Level::Note); diagnostic_child_methods!(span_help, help, Level::Help); diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 6611ce30a1b01..0012c50cd28a7 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1502,6 +1502,40 @@ impl fmt::Debug for Literal { } } +/// The name that downstream code can use in `allow`/`warn`/`deny` attributes +/// for locally changing the lint level of a macro-generated warning diagnostic. +/// +/// Zero or more `LintId` may be created using the `#[proc_macro_lint]` +/// attribute in the crate root of a procedural macro crate. +/// +/// ```ignore (illustrative) +/// use proc_macro::LintId; +/// +/// #[proc_macro_lint] +/// pub static ambiguous_thing: LintId; +/// ``` +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +#[derive(Copy, Clone)] +#[rustc_diagnostic_item = "LintId"] +pub struct LintId { + name: &'static str, +} + +impl LintId { + #[doc(hidden)] + #[unstable(feature = "proc_macro_internals", issue = "27812")] + pub const fn new(name: &'static str) -> Self { + LintId { name } + } +} + +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +impl fmt::Debug for LintId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("LintId").field(&self.name).finish() + } +} + /// Tracked access to environment variables. #[unstable(feature = "proc_macro_tracked_env", issue = "99515")] pub mod tracked_env { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index de6537e992f19..dd5cd1599e52d 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -166,6 +166,7 @@ impl ItemType { | DefKind::Impl { .. } | DefKind::Closure | DefKind::SyntheticCoroutineBody => Self::ForeignType, + DefKind::LintId => unimplemented!("LintId"), } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9fe8b99e8af87..f6481f54167c2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -2066,7 +2066,7 @@ fn resolution_failure( | TraitAlias | TyParam | Static { .. } => "associated item", - Impl { .. } | GlobalAsm | SyntheticCoroutineBody => { + Impl { .. } | GlobalAsm | SyntheticCoroutineBody | LintId => { unreachable!("not a path") } } diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 052e6502da926..046763cdba8bc 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -126,7 +126,8 @@ fn is_not_const(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | DefKind::ConstParam | DefKind::Static { .. } | DefKind::Ctor(..) - | DefKind::AssocConst => false, + | DefKind::AssocConst + | DefKind::LintId => false, DefKind::Fn | DefKind::AssocFn | DefKind::Closure => tcx.constness(def_id) == Constness::NotConst, } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 09de5c055379f..d89c87d3835fa 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,5 +1,5 @@ use rustc_ast::attr; -use rustc_ast::attr::AttributeExt; +use rustc_ast::attr::{AttributeExt, ProcMacroAttr}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; use rustc_lint::LateContext; @@ -151,7 +151,10 @@ pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], n /// Returns true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool { - attrs.iter().any(AttributeExt::is_proc_macro_attr) + attrs.iter().any(|attr| match attr.proc_macro_attr() { + Some(ProcMacroAttr::Derive | ProcMacroAttr::Attribute | ProcMacroAttr::Bang) => true, + Some(ProcMacroAttr::Lint) | None => false, + }) } /// Returns true if the attributes contain `#[doc(hidden)]` diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs index 58f5e80dc4ea6..328b6bf05d12c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs @@ -104,16 +104,17 @@ impl ProcMacros { pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { self.0 .iter() - .map(|proc_macro| match proc_macro { + .filter_map(|proc_macro| match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) + Some((trait_name.to_string(), ProcMacroKind::CustomDerive)) } bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::Bang) + Some((name.to_string(), ProcMacroKind::Bang)) } bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) + Some((name.to_string(), ProcMacroKind::Attr)) } + bridge::client::ProcMacro::Lint { .. } => None, }) .collect() } diff --git a/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.rs b/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.rs new file mode 100644 index 0000000000000..a3e4d6300c457 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.rs @@ -0,0 +1,15 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::LintId; +//~^ use of unstable library feature `proc_macro_diagnostic` + +#[proc_macro_lint] +//~^ the `#[proc_macro_lint]` attribute is an experimental feature +//~^^ use of unstable library feature `proc_macro_diagnostic` +pub static ambiguous_thing: LintId; +//~^ use of unstable library feature `proc_macro_diagnostic` diff --git a/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.stderr b/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.stderr new file mode 100644 index 0000000000000..bab1d2cfd14b1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-proc-macro-diagnostic.stderr @@ -0,0 +1,43 @@ +error[E0658]: the `#[proc_macro_lint]` attribute is an experimental feature + --> $DIR/feature-gate-proc-macro-diagnostic.rs:11:1 + | +LL | #[proc_macro_lint] + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54140 for more information + = help: add `#![feature(proc_macro_diagnostic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `proc_macro_diagnostic` + --> $DIR/feature-gate-proc-macro-diagnostic.rs:8:5 + | +LL | use proc_macro::LintId; + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54140 for more information + = help: add `#![feature(proc_macro_diagnostic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `proc_macro_diagnostic` + --> $DIR/feature-gate-proc-macro-diagnostic.rs:14:29 + | +LL | pub static ambiguous_thing: LintId; + | ^^^^^^ + | + = note: see issue #54140 for more information + = help: add `#![feature(proc_macro_diagnostic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `proc_macro_diagnostic` + --> $DIR/feature-gate-proc-macro-diagnostic.rs:11:1 + | +LL | #[proc_macro_lint] + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54140 for more information + = help: add `#![feature(proc_macro_diagnostic)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/proc-macro/auxiliary/lint-id.rs b/tests/ui/proc-macro/auxiliary/lint-id.rs new file mode 100644 index 0000000000000..cb81e8ec279ec --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/lint-id.rs @@ -0,0 +1,14 @@ +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use proc_macro::{LintId, TokenStream}; + +#[proc_macro_lint] +pub static ambiguous_thing: LintId; + +#[proc_macro] +pub fn print_lintid_to_stderr(_input: TokenStream) -> TokenStream { + eprintln!("printed by macro: {:?}", crate::ambiguous_thing); + TokenStream::new() +} diff --git a/tests/ui/proc-macro/lintid-debug.rs b/tests/ui/proc-macro/lintid-debug.rs new file mode 100644 index 0000000000000..4a20875724be8 --- /dev/null +++ b/tests/ui/proc-macro/lintid-debug.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ proc-macro: lint-id.rs + +extern crate lint_id; + +use std::process::ExitCode; + +lint_id::print_lintid_to_stderr!(); + +fn main() -> ExitCode { + ExitCode::FAILURE +} diff --git a/tests/ui/proc-macro/lintid-debug.stderr b/tests/ui/proc-macro/lintid-debug.stderr new file mode 100644 index 0000000000000..1f91e2c0d804c --- /dev/null +++ b/tests/ui/proc-macro/lintid-debug.stderr @@ -0,0 +1 @@ +printed by macro: LintId("ambiguous_thing") diff --git a/tests/ui/proc-macro/lintid-duplicate.rs b/tests/ui/proc-macro/lintid-duplicate.rs new file mode 100644 index 0000000000000..54b8b0fcc396c --- /dev/null +++ b/tests/ui/proc-macro/lintid-duplicate.rs @@ -0,0 +1,16 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use proc_macro::LintId; + +#[proc_macro_lint] +pub static ambiguous_thing: LintId; + +#[proc_macro_lint] +pub static ambiguous_thing: LintId; +//~^ the name `ambiguous_thing` is defined multiple times diff --git a/tests/ui/proc-macro/lintid-duplicate.stderr b/tests/ui/proc-macro/lintid-duplicate.stderr new file mode 100644 index 0000000000000..cc0ce1a434af5 --- /dev/null +++ b/tests/ui/proc-macro/lintid-duplicate.stderr @@ -0,0 +1,14 @@ +error[E0428]: the name `ambiguous_thing` is defined multiple times + --> $DIR/lintid-duplicate.rs:15:1 + | +LL | pub static ambiguous_thing: LintId; + | ----------------------------------- previous definition of the value `ambiguous_thing` here +... +LL | pub static ambiguous_thing: LintId; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ambiguous_thing` redefined here + | + = note: `ambiguous_thing` must be defined only once in the value namespace of this module + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/proc-macro/lintid-in-macro-namespace.rs b/tests/ui/proc-macro/lintid-in-macro-namespace.rs new file mode 100644 index 0000000000000..87ad38356c2f3 --- /dev/null +++ b/tests/ui/proc-macro/lintid-in-macro-namespace.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ proc-macro: lint-id.rs + +extern crate lint_id; + +// macro namespace +pub use lint_id::ambiguous_thing; + +// type namespace +#[allow(non_camel_case_types)] +pub struct ambiguous_thing {} + +// value namespace +pub fn ambiguous_thing() {} + +fn main() {} diff --git a/tests/ui/proc-macro/lintid-inside-module.rs b/tests/ui/proc-macro/lintid-inside-module.rs new file mode 100644 index 0000000000000..73609e389e176 --- /dev/null +++ b/tests/ui/proc-macro/lintid-inside-module.rs @@ -0,0 +1,15 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +mod detail { + use proc_macro::LintId; + + #[proc_macro_lint] + pub static ambiguous_thing: LintId; + //~^ statics tagged with `#[proc_macro_lint]` must currently reside in the root of the crate +} diff --git a/tests/ui/proc-macro/lintid-inside-module.stderr b/tests/ui/proc-macro/lintid-inside-module.stderr new file mode 100644 index 0000000000000..921e308277ef5 --- /dev/null +++ b/tests/ui/proc-macro/lintid-inside-module.stderr @@ -0,0 +1,8 @@ +error: statics tagged with `#[proc_macro_lint]` must currently reside in the root of the crate + --> $DIR/lintid-inside-module.rs:13:5 + | +LL | pub static ambiguous_thing: LintId; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/lintid-invoke-as-macro.rs b/tests/ui/proc-macro/lintid-invoke-as-macro.rs new file mode 100644 index 0000000000000..2fed25d382066 --- /dev/null +++ b/tests/ui/proc-macro/lintid-invoke-as-macro.rs @@ -0,0 +1,8 @@ +//@ proc-macro: lint-id.rs + +extern crate lint_id; + +lint_id::ambiguous_thing!(); +//~^ expected macro, found lint id `lint_id::ambiguous_thing` + +fn main() {} diff --git a/tests/ui/proc-macro/lintid-invoke-as-macro.stderr b/tests/ui/proc-macro/lintid-invoke-as-macro.stderr new file mode 100644 index 0000000000000..909b599e5af8b --- /dev/null +++ b/tests/ui/proc-macro/lintid-invoke-as-macro.stderr @@ -0,0 +1,8 @@ +error: expected macro, found lint id `lint_id::ambiguous_thing` + --> $DIR/lintid-invoke-as-macro.rs:5:1 + | +LL | lint_id::ambiguous_thing!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a macro + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/lintid-nonpublic.rs b/tests/ui/proc-macro/lintid-nonpublic.rs new file mode 100644 index 0000000000000..488e2ef4082c7 --- /dev/null +++ b/tests/ui/proc-macro/lintid-nonpublic.rs @@ -0,0 +1,13 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use proc_macro::LintId; + +#[proc_macro_lint] +static ambiguous_thing: LintId; +//~^ statics tagged with `#[proc_macro_lint]` must be `pub` diff --git a/tests/ui/proc-macro/lintid-nonpublic.stderr b/tests/ui/proc-macro/lintid-nonpublic.stderr new file mode 100644 index 0000000000000..12a3e59c545d0 --- /dev/null +++ b/tests/ui/proc-macro/lintid-nonpublic.stderr @@ -0,0 +1,8 @@ +error: statics tagged with `#[proc_macro_lint]` must be `pub` + --> $DIR/lintid-nonpublic.rs:12:1 + | +LL | static ambiguous_thing: LintId; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/lintid-not-in-value-namespace.rs b/tests/ui/proc-macro/lintid-not-in-value-namespace.rs new file mode 100644 index 0000000000000..3bf666f04b086 --- /dev/null +++ b/tests/ui/proc-macro/lintid-not-in-value-namespace.rs @@ -0,0 +1,8 @@ +//@ proc-macro: lint-id.rs + +extern crate lint_id; + +fn main() { + eprintln!("{:?}", lint_id::ambiguous_thing); + //~^ expected value, found lint id `lint_id::ambiguous_thing` +} diff --git a/tests/ui/proc-macro/lintid-not-in-value-namespace.stderr b/tests/ui/proc-macro/lintid-not-in-value-namespace.stderr new file mode 100644 index 0000000000000..c2e4f16e7827b --- /dev/null +++ b/tests/ui/proc-macro/lintid-not-in-value-namespace.stderr @@ -0,0 +1,9 @@ +error[E0423]: expected value, found lint id `lint_id::ambiguous_thing` + --> $DIR/lintid-not-in-value-namespace.rs:6:23 + | +LL | eprintln!("{:?}", lint_id::ambiguous_thing); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/proc-macro/lintid-with-value.rs b/tests/ui/proc-macro/lintid-with-value.rs new file mode 100644 index 0000000000000..d3378e484486c --- /dev/null +++ b/tests/ui/proc-macro/lintid-with-value.rs @@ -0,0 +1,14 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +use proc_macro::LintId; + +#[proc_macro_lint] +pub static ambiguous_thing: LintId = "..."; +//~^ a unique LintId value is automatically filled in by `#[proc_macro_lint]` +//~^^ mismatched types diff --git a/tests/ui/proc-macro/lintid-with-value.stderr b/tests/ui/proc-macro/lintid-with-value.stderr new file mode 100644 index 0000000000000..d9c8de774cdf3 --- /dev/null +++ b/tests/ui/proc-macro/lintid-with-value.stderr @@ -0,0 +1,15 @@ +error: a unique LintId value is automatically filled in by `#[proc_macro_lint]` + --> $DIR/lintid-with-value.rs:12:35 + | +LL | pub static ambiguous_thing: LintId = "..."; + | ^^^^^^^^ help: remove this value + +error[E0308]: mismatched types + --> $DIR/lintid-with-value.rs:12:38 + | +LL | pub static ambiguous_thing: LintId = "..."; + | ^^^^^ expected `LintId`, found `&str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/lintid-wrong-type.rs b/tests/ui/proc-macro/lintid-wrong-type.rs new file mode 100644 index 0000000000000..70fb3d9468b18 --- /dev/null +++ b/tests/ui/proc-macro/lintid-wrong-type.rs @@ -0,0 +1,11 @@ +//@ force-host +//@ no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic)] + +extern crate proc_macro; + +#[proc_macro_lint] +pub static ambiguous_thing: String; +//~^ wrong type for proc macro lint id diff --git a/tests/ui/proc-macro/lintid-wrong-type.stderr b/tests/ui/proc-macro/lintid-wrong-type.stderr new file mode 100644 index 0000000000000..eaa8e2d088fe0 --- /dev/null +++ b/tests/ui/proc-macro/lintid-wrong-type.stderr @@ -0,0 +1,14 @@ +error: wrong type for proc macro lint id + --> $DIR/lintid-wrong-type.rs:10:29 + | +LL | pub static ambiguous_thing: String; + | ^^^^^^ + | +note: expected `LintId`, found `String` + --> $DIR/lintid-wrong-type.rs:9:1 + | +LL | #[proc_macro_lint] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error +