about summary refs log tree commit diff
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros')
-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
3 files changed, 41 insertions, 10 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}]",