about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-06-21 14:48:43 +0000
committerbors <bors@rust-lang.org>2025-06-21 14:48:43 +0000
commitea34650916887b5075812d0f11c1d3209e7f94ab (patch)
treea2878d9eb8695942e89b47c8e9fdb76ce59e5edc
parent6d0c9e2a1c80e350c50f5fb9338ea9e585ec603b (diff)
parentc693bc268acd4826faa2d1fa7908aaafc36a7e02 (diff)
downloadrust-ea34650916887b5075812d0f11c1d3209e7f94ab.tar.gz
rust-ea34650916887b5075812d0f11c1d3209e7f94ab.zip
Auto merge of #142826 - jdonszelmann:rollup-1wxomvb, r=jdonszelmann
Rollup of 3 pull requests

Successful merges:

 - rust-lang/rust#142539 (Port `#[may_dangle]` to the new attribute system)
 - rust-lang/rust#142690 (expand: Remove some unnecessary generic parameters)
 - rust-lang/rust#142698 (Improve diagnostics for `concat_bytes!` with C string  literals)

Failed merges:

 - rust-lang/rust#142600 (Port `#[rustc_pub_transparent]` to the new attribute system)
 - rust-lang/rust#142776 (All HIR attributes are outer)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/semantics.rs19
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs2
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs40
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs9
-rw-r--r--compiler/rustc_expand/src/expand.rs92
-rw-r--r--compiler/rustc_hir/src/hir.rs1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs8
-rw-r--r--tests/ui/macros/concat-bytes-error.rs25
-rw-r--r--tests/ui/macros/concat-bytes-error.stderr99
12 files changed, 215 insertions, 88 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 9ebf4c1793d..066e3e9eceb 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -233,8 +233,13 @@ pub enum AttributeKind {
 
     /// Represents `#[rustc_macro_transparency]`.
     MacroTransparency(Transparency),
+
+    /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
+    MayDangle(Span),
+
     /// Represents `#[optimize(size|speed)]`
     Optimize(OptimizeAttr, Span),
+
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr(ThinVec<(ReprAttr, Span)>),
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 78a696afa27..1bb5edba326 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -34,6 +34,7 @@ pub(crate) mod deprecation;
 pub(crate) mod inline;
 pub(crate) mod lint_helpers;
 pub(crate) mod repr;
+pub(crate) mod semantics;
 pub(crate) mod stability;
 pub(crate) mod transparency;
 pub(crate) mod util;
diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
new file mode 100644
index 00000000000..071574a5612
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
@@ -0,0 +1,19 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct MayDangleParser;
+impl<S: Stage> SingleAttributeParser<S> for MayDangleParser {
+    const PATH: &[Symbol] = &[sym::may_dangle];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::MayDangle(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 8311fccacd8..1bcf500459d 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -21,6 +21,7 @@ use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
 use crate::attributes::lint_helpers::AsPtrParser;
 use crate::attributes::repr::{AlignParser, ReprParser};
+use crate::attributes::semantics::MayDangleParser;
 use crate::attributes::stability::{
     BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
 };
@@ -110,6 +111,7 @@ attribute_parsers!(
         Single<ConstStabilityIndirectParser>,
         Single<DeprecationParser>,
         Single<InlineParser>,
+        Single<MayDangleParser>,
         Single<OptimizeParser>,
         Single<RustcForceInlineParser>,
         Single<TransparencyParser>,
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index d32e6f1558e..c5d1f2ad2de 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -104,6 +104,8 @@ builtin_macros_concat_bytes_bad_repeat = repeat count is not a positive number
 builtin_macros_concat_bytes_invalid = cannot concatenate {$lit_kind} literals
     .byte_char = try using a byte character
     .byte_str = try using a byte string
+    .c_str = try using a null-terminated byte string
+    .c_str_note = concatenating C strings is ambiguous about including the '\0'
     .number_array = try wrapping the number in an array
 
 builtin_macros_concat_bytes_missing_literal = expected a byte literal
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 456f2b9ab31..92d011fb9d1 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -1,6 +1,6 @@
 use rustc_ast::ptr::P;
 use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
+use rustc_ast::{ExprKind, LitIntType, LitKind, StrStyle, UintTy, token};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 use rustc_session::errors::report_lit_error;
 use rustc_span::{ErrorGuaranteed, Span};
@@ -21,15 +21,32 @@ fn invalid_type_err(
     let snippet = cx.sess.source_map().span_to_snippet(span).ok();
     let dcx = cx.dcx();
     match LitKind::from_token_lit(token_lit) {
-        Ok(LitKind::CStr(_, _)) => {
+        Ok(LitKind::CStr(_, style)) => {
             // Avoid ambiguity in handling of terminal `NUL` by refusing to
             // concatenate C string literals as bytes.
-            dcx.emit_err(errors::ConcatCStrLit { span })
+            let sugg = if let Some(mut as_bstr) = snippet
+                && style == StrStyle::Cooked
+                && as_bstr.starts_with('c')
+                && as_bstr.ends_with('"')
+            {
+                // Suggest`c"foo"` -> `b"foo\0"` if we can
+                as_bstr.replace_range(0..1, "b");
+                as_bstr.pop();
+                as_bstr.push_str(r#"\0""#);
+                Some(ConcatBytesInvalidSuggestion::CStrLit { span, as_bstr })
+            } else {
+                // No suggestion for a missing snippet, raw strings, or if for some reason we have
+                // a span that doesn't match `c"foo"` (possible if a proc macro assigns a span
+                // that doesn't actually point to a C string).
+                None
+            };
+            // We can only provide a suggestion if we have a snip and it is not a raw string
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "C string", sugg, cs_note: Some(()) })
         }
         Ok(LitKind::Char(_)) => {
             let sugg =
                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg, cs_note: None })
         }
         Ok(LitKind::Str(_, _)) => {
             // suggestion would be invalid if we are nested
@@ -38,18 +55,21 @@ fn invalid_type_err(
             } else {
                 None
             };
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg, cs_note: None })
         }
         Ok(LitKind::Float(_, _)) => {
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
-        }
-        Ok(LitKind::Bool(_)) => {
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None, cs_note: None })
         }
+        Ok(LitKind::Bool(_)) => dcx.emit_err(ConcatBytesInvalid {
+            span,
+            lit_kind: "boolean",
+            sugg: None,
+            cs_note: None,
+        }),
         Ok(LitKind::Int(_, _)) if !is_nested => {
             let sugg =
                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg, cs_note: None })
         }
         Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
             assert!(val.get() > u8::MAX.into()); // must be an error
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 3a2e96a5e5a..b7ecfd2285c 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -215,6 +215,8 @@ pub(crate) struct ConcatBytesInvalid {
     pub(crate) lit_kind: &'static str,
     #[subdiagnostic]
     pub(crate) sugg: Option<ConcatBytesInvalidSuggestion>,
+    #[note(builtin_macros_c_str_note)]
+    pub(crate) cs_note: Option<()>,
 }
 
 #[derive(Subdiagnostic)]
@@ -239,6 +241,13 @@ pub(crate) enum ConcatBytesInvalidSuggestion {
         span: Span,
         snippet: String,
     },
+    #[note(builtin_macros_c_str_note)]
+    #[suggestion(builtin_macros_c_str, code = "{as_bstr}", applicability = "machine-applicable")]
+    CStrLit {
+        #[primary_span]
+        span: Span,
+        as_bstr: String,
+    },
     #[suggestion(
         builtin_macros_number_array,
         code = "[{snippet}]",
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 37010700fab..3ba40859d45 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -86,7 +86,7 @@ macro_rules! ast_fragments {
                 }
             }
 
-            fn make_from<'a>(self, result: Box<dyn MacResult + 'a>) -> Option<AstFragment> {
+            fn make_from(self, result: Box<dyn MacResult + '_>) -> Option<AstFragment> {
                 match self {
                     AstFragmentKind::OptExpr =>
                         result.make_expr().map(Some).map(AstFragment::OptExpr),
@@ -136,7 +136,7 @@ macro_rules! ast_fragments {
                 T::fragment_to_output(self)
             }
 
-            pub(crate) fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) {
+            pub(crate) fn mut_visit_with(&mut self, vis: &mut impl MutVisitor) {
                 match self {
                     AstFragment::OptExpr(opt_expr) => {
                         if let Some(expr) = opt_expr.take() {
@@ -316,9 +316,9 @@ impl AstFragmentKind {
         }
     }
 
-    pub(crate) fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(
+    pub(crate) fn expect_from_annotatables(
         self,
-        items: I,
+        items: impl IntoIterator<Item = Annotatable>,
     ) -> AstFragment {
         let mut items = items.into_iter();
         match self {
@@ -1218,10 +1218,10 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
     fn descr() -> &'static str {
         unreachable!()
     }
-    fn walk_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
+    fn walk_flat_map(self, _collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
         unreachable!()
     }
-    fn walk<V: MutVisitor>(&mut self, _visitor: &mut V) {
+    fn walk(&mut self, _collector: &mut InvocationCollector<'_, '_>) {
         unreachable!()
     }
     fn is_mac_call(&self) -> bool {
@@ -1276,8 +1276,8 @@ impl InvocationCollectorNode for P<ast::Item> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_item(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_item(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ItemKind::MacCall(..))
@@ -1431,8 +1431,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_trait_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Trait)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Trait)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1472,8 +1472,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_impl_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: false })
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: false })
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1513,8 +1513,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitImplItem
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_trait_impl_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: true })
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: true })
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1551,8 +1551,8 @@ impl InvocationCollectorNode for P<ast::ForeignItem> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_foreign_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_foreign_item(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_foreign_item(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ForeignItemKind::MacCall(..))
@@ -1573,8 +1573,8 @@ impl InvocationCollectorNode for ast::Variant {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_variants()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_variant(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_variant(collector, self)
     }
 }
 
@@ -1586,8 +1586,8 @@ impl InvocationCollectorNode for ast::WherePredicate {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_where_predicates()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_where_predicate(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_where_predicate(collector, self)
     }
 }
 
@@ -1599,8 +1599,8 @@ impl InvocationCollectorNode for ast::FieldDef {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_field_defs()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_field_def(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_field_def(collector, self)
     }
 }
 
@@ -1612,8 +1612,8 @@ impl InvocationCollectorNode for ast::PatField {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_pat_fields()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_pat_field(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_pat_field(collector, self)
     }
 }
 
@@ -1625,8 +1625,8 @@ impl InvocationCollectorNode for ast::ExprField {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_expr_fields()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_expr_field(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_expr_field(collector, self)
     }
 }
 
@@ -1638,8 +1638,8 @@ impl InvocationCollectorNode for ast::Param {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_params()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_param(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_param(collector, self)
     }
 }
 
@@ -1651,8 +1651,8 @@ impl InvocationCollectorNode for ast::GenericParam {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_generic_params()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_generic_param(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_generic_param(collector, self)
     }
 }
 
@@ -1664,8 +1664,8 @@ impl InvocationCollectorNode for ast::Arm {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_arms()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_arm(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_arm(collector, self)
     }
 }
 
@@ -1677,8 +1677,8 @@ impl InvocationCollectorNode for ast::Stmt {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_stmts()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_stmt(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_stmt(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         match &self.kind {
@@ -1751,8 +1751,8 @@ impl InvocationCollectorNode for ast::Crate {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_crate()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_crate(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_crate(collector, self)
     }
     fn expand_cfg_false(
         &mut self,
@@ -1777,8 +1777,8 @@ impl InvocationCollectorNode for ast::Ty {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_ty()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_ty(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_ty(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ast::TyKind::MacCall(..))
@@ -1800,8 +1800,8 @@ impl InvocationCollectorNode for ast::Pat {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_pat()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_pat(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_pat(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, PatKind::MacCall(..))
@@ -1826,8 +1826,8 @@ impl InvocationCollectorNode for ast::Expr {
     fn descr() -> &'static str {
         "an expression"
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_expr(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_expr(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ExprKind::MacCall(..))
@@ -1850,8 +1850,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_opt_expr()
     }
-    fn walk_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy {
-        walk_expr(visitor, &mut self.wrapped);
+    fn walk_flat_map(mut self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_expr(collector, &mut self.wrapped);
         Some(self.wrapped)
     }
     fn is_mac_call(&self) -> bool {
@@ -1885,8 +1885,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag>
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag)
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_expr(visitor, &mut self.wrapped)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_expr(collector, &mut self.wrapped)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, ast::ExprKind::MacCall(..))
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 556f50a85af..1a526d5bce0 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
             // FIXME: should not be needed anymore when all attrs are parsed
             Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
+            Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
             a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
         }
     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 5fb9514e5ca..d0630383477 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -163,6 +163,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
                     self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target)
                 }
+                &Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
+                    self.check_may_dangle(hir_id, attr_span)
+                }
                 Attribute::Unparsed(_) => {
                     match attr.path().as_slice() {
                         [sym::diagnostic, sym::do_not_recommend, ..] => {
@@ -236,7 +239,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
                         [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
                         [sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
-                        [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr),
                         [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
                         [sym::rustc_allow_incoherent_impl, ..] => {
                             self.check_allow_incoherent_impl(attr, span, target)
@@ -1619,7 +1621,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
-    fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
+    fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {
         if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
             && matches!(
                 param.kind,
@@ -1636,7 +1638,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             return;
         }
 
-        self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() });
+        self.dcx().emit_err(errors::InvalidMayDangle { attr_span });
     }
 
     /// Checks if `#[cold]` is applied to a non-function.
diff --git a/tests/ui/macros/concat-bytes-error.rs b/tests/ui/macros/concat-bytes-error.rs
index db5d3cab0bd..8130fc54d8d 100644
--- a/tests/ui/macros/concat-bytes-error.rs
+++ b/tests/ui/macros/concat-bytes-error.rs
@@ -1,20 +1,44 @@
+//@ edition: 2021
+// 2021 edition for C string literals
+
 #![feature(concat_bytes)]
 
 fn main() {
+    // Identifiers
     concat_bytes!(pie); //~ ERROR expected a byte literal
     concat_bytes!(pie, pie); //~ ERROR expected a byte literal
+
+    // String literals
     concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION b"tnrsi"
+    concat_bytes!(r"tnrsi", r"tnri"); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION br"tnrsi"
+    concat_bytes!(r#"tnrsi"#, r###"tnri"###); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION br#"tnrsi"#
+    concat_bytes!(c"tnrsi", c"tnri"); //~ ERROR cannot concatenate C string literals
+    //~^ SUGGESTION b"tnrsi\0"
+    concat_bytes!(cr"tnrsi", cr"tnri"); //~ ERROR cannot concatenate C string literals
+    concat_bytes!(cr#"tnrsi"#, cr###"tnri"###); //~ ERROR cannot concatenate C string literals
+
+    // Other literals
     concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
     concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [300]
     concat_bytes!('a'); //~ ERROR cannot concatenate character literals
+    //~^ SUGGESTION b'a'
     concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
     concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [42]
     concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [42]
+
+    // Nested items
     concat_bytes!([
         "hi", //~ ERROR cannot concatenate string literals
     ]);
     concat_bytes!([
         'a', //~ ERROR cannot concatenate character literals
+        //~^ SUGGESTION b'a'
     ]);
     concat_bytes!([
         true, //~ ERROR cannot concatenate boolean literals
@@ -38,6 +62,7 @@ fn main() {
         [5, 6, 7], //~ ERROR cannot concatenate doubly nested array
     ]);
     concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [5u16]
     concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
     concat_bytes!([3; ()]); //~ ERROR repeat count is not a positive number
     concat_bytes!([3; -2]); //~ ERROR repeat count is not a positive number
diff --git a/tests/ui/macros/concat-bytes-error.stderr b/tests/ui/macros/concat-bytes-error.stderr
index 3f2c64922e3..447d7a663fd 100644
--- a/tests/ui/macros/concat-bytes-error.stderr
+++ b/tests/ui/macros/concat-bytes-error.stderr
@@ -1,5 +1,5 @@
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:4:19
+  --> $DIR/concat-bytes-error.rs:8:19
    |
 LL |     concat_bytes!(pie);
    |                   ^^^
@@ -7,7 +7,7 @@ LL |     concat_bytes!(pie);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:5:19
+  --> $DIR/concat-bytes-error.rs:9:19
    |
 LL |     concat_bytes!(pie, pie);
    |                   ^^^  ^^^
@@ -15,85 +15,126 @@ LL |     concat_bytes!(pie, pie);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate string literals
-  --> $DIR/concat-bytes-error.rs:6:19
+  --> $DIR/concat-bytes-error.rs:12:19
    |
 LL |     concat_bytes!("tnrsi", "tnri");
    |                   ^^^^^^^ help: try using a byte string: `b"tnrsi"`
 
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:14:19
+   |
+LL |     concat_bytes!(r"tnrsi", r"tnri");
+   |                   ^^^^^^^^ help: try using a byte string: `br"tnrsi"`
+
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:16:19
+   |
+LL |     concat_bytes!(r#"tnrsi"#, r###"tnri"###);
+   |                   ^^^^^^^^^^ help: try using a byte string: `br#"tnrsi"#`
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:18:19
+   |
+LL |     concat_bytes!(c"tnrsi", c"tnri");
+   |                   ^^^^^^^^ help: try using a null-terminated byte string: `b"tnrsi\0"`
+   |
+note: concatenating C strings is ambiguous about including the '\0'
+  --> $DIR/concat-bytes-error.rs:18:19
+   |
+LL |     concat_bytes!(c"tnrsi", c"tnri");
+   |                   ^^^^^^^^
+   = note: concatenating C strings is ambiguous about including the '\0'
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:20:19
+   |
+LL |     concat_bytes!(cr"tnrsi", cr"tnri");
+   |                   ^^^^^^^^^
+   |
+   = note: concatenating C strings is ambiguous about including the '\0'
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:21:19
+   |
+LL |     concat_bytes!(cr#"tnrsi"#, cr###"tnri"###);
+   |                   ^^^^^^^^^^^
+   |
+   = note: concatenating C strings is ambiguous about including the '\0'
+
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:7:19
+  --> $DIR/concat-bytes-error.rs:24:19
    |
 LL |     concat_bytes!(2.8);
    |                   ^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:8:19
+  --> $DIR/concat-bytes-error.rs:25:19
    |
 LL |     concat_bytes!(300);
    |                   ^^^ help: try wrapping the number in an array: `[300]`
 
 error: cannot concatenate character literals
-  --> $DIR/concat-bytes-error.rs:9:19
+  --> $DIR/concat-bytes-error.rs:27:19
    |
 LL |     concat_bytes!('a');
    |                   ^^^ help: try using a byte character: `b'a'`
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:10:19
+  --> $DIR/concat-bytes-error.rs:29:19
    |
 LL |     concat_bytes!(true, false);
    |                   ^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:11:19
+  --> $DIR/concat-bytes-error.rs:30:19
    |
 LL |     concat_bytes!(42, b"va", b'l');
    |                   ^^ help: try wrapping the number in an array: `[42]`
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:12:19
+  --> $DIR/concat-bytes-error.rs:32:19
    |
 LL |     concat_bytes!(42, b"va", b'l', [1, 2]);
    |                   ^^ help: try wrapping the number in an array: `[42]`
 
 error: cannot concatenate string literals
-  --> $DIR/concat-bytes-error.rs:14:9
+  --> $DIR/concat-bytes-error.rs:37:9
    |
 LL |         "hi",
    |         ^^^^
 
 error: cannot concatenate character literals
-  --> $DIR/concat-bytes-error.rs:17:9
+  --> $DIR/concat-bytes-error.rs:40:9
    |
 LL |         'a',
    |         ^^^ help: try using a byte character: `b'a'`
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:20:9
+  --> $DIR/concat-bytes-error.rs:44:9
    |
 LL |         true,
    |         ^^^^
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:23:9
+  --> $DIR/concat-bytes-error.rs:47:9
    |
 LL |         false,
    |         ^^^^^
 
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:26:9
+  --> $DIR/concat-bytes-error.rs:50:9
    |
 LL |         2.6,
    |         ^^^
 
 error: numeric literal is out of bounds
-  --> $DIR/concat-bytes-error.rs:29:9
+  --> $DIR/concat-bytes-error.rs:53:9
    |
 LL |         265,
    |         ^^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:32:9
+  --> $DIR/concat-bytes-error.rs:56:9
    |
 LL |         -33,
    |         ^^^
@@ -101,7 +142,7 @@ LL |         -33,
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:35:9
+  --> $DIR/concat-bytes-error.rs:59:9
    |
 LL |         b"hi!",
    |         ^^^^^^
@@ -110,43 +151,43 @@ LL |         b"hi!",
    = help: try flattening the array
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:38:9
+  --> $DIR/concat-bytes-error.rs:62:9
    |
 LL |         [5, 6, 7],
    |         ^^^^^^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:40:19
+  --> $DIR/concat-bytes-error.rs:64:19
    |
 LL |     concat_bytes!(5u16);
    |                   ^^^^ help: try wrapping the number in an array: `[5u16]`
 
 error: numeric literal is not a `u8`
-  --> $DIR/concat-bytes-error.rs:41:20
+  --> $DIR/concat-bytes-error.rs:66:20
    |
 LL |     concat_bytes!([5u16]);
    |                    ^^^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:42:23
+  --> $DIR/concat-bytes-error.rs:67:23
    |
 LL |     concat_bytes!([3; ()]);
    |                       ^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:43:23
+  --> $DIR/concat-bytes-error.rs:68:23
    |
 LL |     concat_bytes!([3; -2]);
    |                       ^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:44:25
+  --> $DIR/concat-bytes-error.rs:69:25
    |
 LL |     concat_bytes!([pie; -2]);
    |                         ^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:45:20
+  --> $DIR/concat-bytes-error.rs:70:20
    |
 LL |     concat_bytes!([pie; 2]);
    |                    ^^^
@@ -154,28 +195,28 @@ LL |     concat_bytes!([pie; 2]);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:46:20
+  --> $DIR/concat-bytes-error.rs:71:20
    |
 LL |     concat_bytes!([2.2; 0]);
    |                    ^^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:47:25
+  --> $DIR/concat-bytes-error.rs:72:25
    |
 LL |     concat_bytes!([5.5; ()]);
    |                         ^^
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:48:20
+  --> $DIR/concat-bytes-error.rs:73:20
    |
 LL |     concat_bytes!([[1, 2, 3]; 3]);
    |                    ^^^^^^^^^
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:49:20
+  --> $DIR/concat-bytes-error.rs:74:20
    |
 LL |     concat_bytes!([[42; 2]; 3]);
    |                    ^^^^^^^
 
-error: aborting due to 28 previous errors
+error: aborting due to 33 previous errors