Skip to content

Commit c693bc2

Browse files
authored
Rollup merge of #142698 - tgross35:concat-byte-cstr-diag, r=petrochenkov
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.
2 parents 6ed5d48 + 044c99d commit c693bc2

File tree

5 files changed

+136
-39
lines changed

5 files changed

+136
-39
lines changed

compiler/rustc_builtin_macros/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ builtin_macros_concat_bytes_bad_repeat = repeat count is not a positive number
104104
builtin_macros_concat_bytes_invalid = cannot concatenate {$lit_kind} literals
105105
.byte_char = try using a byte character
106106
.byte_str = try using a byte string
107+
.c_str = try using a null-terminated byte string
108+
.c_str_note = concatenating C strings is ambiguous about including the '\0'
107109
.number_array = try wrapping the number in an array
108110
109111
builtin_macros_concat_bytes_missing_literal = expected a byte literal

compiler/rustc_builtin_macros/src/concat_bytes.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_ast::ptr::P;
22
use rustc_ast::tokenstream::TokenStream;
3-
use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
3+
use rustc_ast::{ExprKind, LitIntType, LitKind, StrStyle, UintTy, token};
44
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
55
use rustc_session::errors::report_lit_error;
66
use rustc_span::{ErrorGuaranteed, Span};
@@ -21,15 +21,32 @@ fn invalid_type_err(
2121
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
2222
let dcx = cx.dcx();
2323
match LitKind::from_token_lit(token_lit) {
24-
Ok(LitKind::CStr(_, _)) => {
24+
Ok(LitKind::CStr(_, style)) => {
2525
// Avoid ambiguity in handling of terminal `NUL` by refusing to
2626
// concatenate C string literals as bytes.
27-
dcx.emit_err(errors::ConcatCStrLit { span })
27+
let sugg = if let Some(mut as_bstr) = snippet
28+
&& style == StrStyle::Cooked
29+
&& as_bstr.starts_with('c')
30+
&& as_bstr.ends_with('"')
31+
{
32+
// Suggest`c"foo"` -> `b"foo\0"` if we can
33+
as_bstr.replace_range(0..1, "b");
34+
as_bstr.pop();
35+
as_bstr.push_str(r#"\0""#);
36+
Some(ConcatBytesInvalidSuggestion::CStrLit { span, as_bstr })
37+
} else {
38+
// No suggestion for a missing snippet, raw strings, or if for some reason we have
39+
// a span that doesn't match `c"foo"` (possible if a proc macro assigns a span
40+
// that doesn't actually point to a C string).
41+
None
42+
};
43+
// We can only provide a suggestion if we have a snip and it is not a raw string
44+
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "C string", sugg, cs_note: Some(()) })
2845
}
2946
Ok(LitKind::Char(_)) => {
3047
let sugg =
3148
snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
32-
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
49+
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg, cs_note: None })
3350
}
3451
Ok(LitKind::Str(_, _)) => {
3552
// suggestion would be invalid if we are nested
@@ -38,18 +55,21 @@ fn invalid_type_err(
3855
} else {
3956
None
4057
};
41-
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
58+
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg, cs_note: None })
4259
}
4360
Ok(LitKind::Float(_, _)) => {
44-
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
45-
}
46-
Ok(LitKind::Bool(_)) => {
47-
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
61+
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None, cs_note: None })
4862
}
63+
Ok(LitKind::Bool(_)) => dcx.emit_err(ConcatBytesInvalid {
64+
span,
65+
lit_kind: "boolean",
66+
sugg: None,
67+
cs_note: None,
68+
}),
4969
Ok(LitKind::Int(_, _)) if !is_nested => {
5070
let sugg =
5171
snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
52-
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
72+
dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg, cs_note: None })
5373
}
5474
Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
5575
assert!(val.get() > u8::MAX.into()); // must be an error

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ pub(crate) struct ConcatBytesInvalid {
215215
pub(crate) lit_kind: &'static str,
216216
#[subdiagnostic]
217217
pub(crate) sugg: Option<ConcatBytesInvalidSuggestion>,
218+
#[note(builtin_macros_c_str_note)]
219+
pub(crate) cs_note: Option<()>,
218220
}
219221

220222
#[derive(Subdiagnostic)]
@@ -239,6 +241,13 @@ pub(crate) enum ConcatBytesInvalidSuggestion {
239241
span: Span,
240242
snippet: String,
241243
},
244+
#[note(builtin_macros_c_str_note)]
245+
#[suggestion(builtin_macros_c_str, code = "{as_bstr}", applicability = "machine-applicable")]
246+
CStrLit {
247+
#[primary_span]
248+
span: Span,
249+
as_bstr: String,
250+
},
242251
#[suggestion(
243252
builtin_macros_number_array,
244253
code = "[{snippet}]",

tests/ui/macros/concat-bytes-error.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1+
//@ edition: 2021
2+
// 2021 edition for C string literals
3+
14
#![feature(concat_bytes)]
25

36
fn main() {
7+
// Identifiers
48
concat_bytes!(pie); //~ ERROR expected a byte literal
59
concat_bytes!(pie, pie); //~ ERROR expected a byte literal
10+
11+
// String literals
612
concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
13+
//~^ SUGGESTION b"tnrsi"
14+
concat_bytes!(r"tnrsi", r"tnri"); //~ ERROR cannot concatenate string literals
15+
//~^ SUGGESTION br"tnrsi"
16+
concat_bytes!(r#"tnrsi"#, r###"tnri"###); //~ ERROR cannot concatenate string literals
17+
//~^ SUGGESTION br#"tnrsi"#
18+
concat_bytes!(c"tnrsi", c"tnri"); //~ ERROR cannot concatenate C string literals
19+
//~^ SUGGESTION b"tnrsi\0"
20+
concat_bytes!(cr"tnrsi", cr"tnri"); //~ ERROR cannot concatenate C string literals
21+
concat_bytes!(cr#"tnrsi"#, cr###"tnri"###); //~ ERROR cannot concatenate C string literals
22+
23+
// Other literals
724
concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
825
concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
26+
//~^ SUGGESTION [300]
927
concat_bytes!('a'); //~ ERROR cannot concatenate character literals
28+
//~^ SUGGESTION b'a'
1029
concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
1130
concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
31+
//~^ SUGGESTION [42]
1232
concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
33+
//~^ SUGGESTION [42]
34+
35+
// Nested items
1336
concat_bytes!([
1437
"hi", //~ ERROR cannot concatenate string literals
1538
]);
1639
concat_bytes!([
1740
'a', //~ ERROR cannot concatenate character literals
41+
//~^ SUGGESTION b'a'
1842
]);
1943
concat_bytes!([
2044
true, //~ ERROR cannot concatenate boolean literals
@@ -38,6 +62,7 @@ fn main() {
3862
[5, 6, 7], //~ ERROR cannot concatenate doubly nested array
3963
]);
4064
concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
65+
//~^ SUGGESTION [5u16]
4166
concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
4267
concat_bytes!([3; ()]); //~ ERROR repeat count is not a positive number
4368
concat_bytes!([3; -2]); //~ ERROR repeat count is not a positive number

0 commit comments

Comments
 (0)