about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2025-06-18 20:08:15 -0400
committerTrevor Gross <tmgross@umich.edu>2025-06-19 16:13:20 +0000
commit044c99df78da9464e2ad0a90494af1e584123ab4 (patch)
tree0aa346ba15153954fb12cfebb05791e8281ac20a
parent342f07ab31d523c17eb1fb8fe8aee48674309e23 (diff)
downloadrust-044c99df78da9464e2ad0a90494af1e584123ab4.tar.gz
rust-044c99df78da9464e2ad0a90494af1e584123ab4.zip
Improve diagnostics for `concat_bytes!` with C string literals
Use the same error as other invalid types for `concat_bytes!`, rather
than using `ConcatCStrLit` from `concat!`. Also add more information
with a note about why this doesn't work, and a suggestion to use a
null-terminated byte string instead.
-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--tests/ui/macros/concat-bytes-error.rs7
-rw-r--r--tests/ui/macros/concat-bytes-error.stderr71
5 files changed, 86 insertions, 43 deletions
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/tests/ui/macros/concat-bytes-error.rs b/tests/ui/macros/concat-bytes-error.rs
index f7f291f446f..8130fc54d8d 100644
--- a/tests/ui/macros/concat-bytes-error.rs
+++ b/tests/ui/macros/concat-bytes-error.rs
@@ -15,9 +15,10 @@ fn main() {
     //~^ 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 a C string literal
-    concat_bytes!(cr"tnrsi", cr"tnri"); //~ ERROR  cannot concatenate a C string literal
-    concat_bytes!(cr#"tnrsi"#, cr###"tnri"###); //~ ERROR  cannot concatenate a C string literal
+    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
diff --git a/tests/ui/macros/concat-bytes-error.stderr b/tests/ui/macros/concat-bytes-error.stderr
index fd498a023a1..447d7a663fd 100644
--- a/tests/ui/macros/concat-bytes-error.stderr
+++ b/tests/ui/macros/concat-bytes-error.stderr
@@ -32,98 +32,109 @@ error: cannot concatenate string literals
 LL |     concat_bytes!(r#"tnrsi"#, r###"tnri"###);
    |                   ^^^^^^^^^^ help: try using a byte string: `br#"tnrsi"#`
 
-error: cannot concatenate a C string literal
+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 a C string literal
-  --> $DIR/concat-bytes-error.rs:19:19
+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 a C string literal
-  --> $DIR/concat-bytes-error.rs:20:19
+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:23:19
+  --> $DIR/concat-bytes-error.rs:24:19
    |
 LL |     concat_bytes!(2.8);
    |                   ^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:24: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:26: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:28:19
+  --> $DIR/concat-bytes-error.rs:29:19
    |
 LL |     concat_bytes!(true, false);
    |                   ^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:29: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:31: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:36:9
+  --> $DIR/concat-bytes-error.rs:37:9
    |
 LL |         "hi",
    |         ^^^^
 
 error: cannot concatenate character literals
-  --> $DIR/concat-bytes-error.rs:39: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:43:9
+  --> $DIR/concat-bytes-error.rs:44:9
    |
 LL |         true,
    |         ^^^^
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:46:9
+  --> $DIR/concat-bytes-error.rs:47:9
    |
 LL |         false,
    |         ^^^^^
 
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:49:9
+  --> $DIR/concat-bytes-error.rs:50:9
    |
 LL |         2.6,
    |         ^^^
 
 error: numeric literal is out of bounds
-  --> $DIR/concat-bytes-error.rs:52:9
+  --> $DIR/concat-bytes-error.rs:53:9
    |
 LL |         265,
    |         ^^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:55:9
+  --> $DIR/concat-bytes-error.rs:56:9
    |
 LL |         -33,
    |         ^^^
@@ -131,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:58:9
+  --> $DIR/concat-bytes-error.rs:59:9
    |
 LL |         b"hi!",
    |         ^^^^^^
@@ -140,43 +151,43 @@ LL |         b"hi!",
    = help: try flattening the array
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:61:9
+  --> $DIR/concat-bytes-error.rs:62:9
    |
 LL |         [5, 6, 7],
    |         ^^^^^^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:63: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:65: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:66: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:67: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:68:25
+  --> $DIR/concat-bytes-error.rs:69:25
    |
 LL |     concat_bytes!([pie; -2]);
    |                         ^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:69:20
+  --> $DIR/concat-bytes-error.rs:70:20
    |
 LL |     concat_bytes!([pie; 2]);
    |                    ^^^
@@ -184,25 +195,25 @@ 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:70: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:71:25
+  --> $DIR/concat-bytes-error.rs:72:25
    |
 LL |     concat_bytes!([5.5; ()]);
    |                         ^^
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:72: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:73:20
+  --> $DIR/concat-bytes-error.rs:74:20
    |
 LL |     concat_bytes!([[42; 2]; 3]);
    |                    ^^^^^^^