Skip to content

Commit 3fc5bd7

Browse files
authored
Rollup merge of rust-lang#87599 - Smittyvb:concat_bytes, r=Mark-Simulacrum
Implement concat_bytes! This implements the unstable `concat_bytes!` macro, which has tracking issue rust-lang#87555. It can be used like: ```rust #![feature(concat_bytes)] fn main() { assert_eq!(concat_bytes!(), &[]); assert_eq!(concat_bytes!(b'A', b"BC", [68, b'E', 70]), b"ABCDEF"); } ``` If strings or characters are used where byte strings or byte characters are required, it suggests adding a `b` prefix. If a number is used outside of an array it suggests arrayifying it. If a boolean is used it suggests replacing it with the numeric value of that number. Doubly nested arrays of bytes are disallowed.
2 parents 3c857f4 + eb56693 commit 3fc5bd7

File tree

12 files changed

+421
-0
lines changed

12 files changed

+421
-0
lines changed

Diff for: compiler/rustc_builtin_macros/src/concat_bytes.rs

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use rustc_ast as ast;
2+
use rustc_ast::{ptr::P, tokenstream::TokenStream};
3+
use rustc_data_structures::sync::Lrc;
4+
use rustc_errors::Applicability;
5+
use rustc_expand::base::{self, DummyResult};
6+
7+
/// Emits errors for literal expressions that are invalid inside and outside of an array.
8+
fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
9+
let lit = if let ast::ExprKind::Lit(lit) = &expr.kind {
10+
lit
11+
} else {
12+
unreachable!();
13+
};
14+
match lit.kind {
15+
ast::LitKind::Char(_) => {
16+
let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
17+
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
18+
err.span_suggestion(
19+
expr.span,
20+
"try using a byte character",
21+
format!("b{}", snippet),
22+
Applicability::MachineApplicable,
23+
)
24+
.emit();
25+
}
26+
}
27+
ast::LitKind::Str(_, _) => {
28+
let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals");
29+
// suggestion would be invalid if we are nested
30+
if !is_nested {
31+
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
32+
err.span_suggestion(
33+
expr.span,
34+
"try using a byte string",
35+
format!("b{}", snippet),
36+
Applicability::MachineApplicable,
37+
);
38+
}
39+
}
40+
err.emit();
41+
}
42+
ast::LitKind::Float(_, _) => {
43+
cx.span_err(expr.span, "cannot concatenate float literals");
44+
}
45+
ast::LitKind::Bool(_) => {
46+
cx.span_err(expr.span, "cannot concatenate boolean literals");
47+
}
48+
ast::LitKind::Err(_) => {}
49+
ast::LitKind::Int(_, _) if !is_nested => {
50+
let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
51+
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
52+
err.span_suggestion(
53+
expr.span,
54+
"try wrapping the number in an array",
55+
format!("[{}]", snippet),
56+
Applicability::MachineApplicable,
57+
);
58+
}
59+
err.emit();
60+
}
61+
ast::LitKind::Int(
62+
val,
63+
ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
64+
) => {
65+
assert!(val > u8::MAX.into()); // must be an error
66+
cx.span_err(expr.span, "numeric literal is out of bounds");
67+
}
68+
ast::LitKind::Int(_, _) => {
69+
cx.span_err(expr.span, "numeric literal is not a `u8`");
70+
}
71+
_ => unreachable!(),
72+
}
73+
}
74+
75+
pub fn expand_concat_bytes(
76+
cx: &mut base::ExtCtxt<'_>,
77+
sp: rustc_span::Span,
78+
tts: TokenStream,
79+
) -> Box<dyn base::MacResult + 'static> {
80+
let es = match base::get_exprs_from_tts(cx, sp, tts) {
81+
Some(e) => e,
82+
None => return DummyResult::any(sp),
83+
};
84+
let mut accumulator = Vec::new();
85+
let mut missing_literals = vec![];
86+
let mut has_errors = false;
87+
for e in es {
88+
match e.kind {
89+
ast::ExprKind::Array(ref exprs) => {
90+
for expr in exprs {
91+
match expr.kind {
92+
ast::ExprKind::Array(_) => {
93+
if !has_errors {
94+
cx.span_err(expr.span, "cannot concatenate doubly nested array");
95+
}
96+
has_errors = true;
97+
}
98+
ast::ExprKind::Lit(ref lit) => match lit.kind {
99+
ast::LitKind::Int(
100+
val,
101+
ast::LitIntType::Unsuffixed
102+
| ast::LitIntType::Unsigned(ast::UintTy::U8),
103+
) if val <= u8::MAX.into() => {
104+
accumulator.push(val as u8);
105+
}
106+
107+
ast::LitKind::Byte(val) => {
108+
accumulator.push(val);
109+
}
110+
ast::LitKind::ByteStr(_) => {
111+
if !has_errors {
112+
cx.struct_span_err(
113+
expr.span,
114+
"cannot concatenate doubly nested array",
115+
)
116+
.note("byte strings are treated as arrays of bytes")
117+
.help("try flattening the array")
118+
.emit();
119+
}
120+
has_errors = true;
121+
}
122+
_ => {
123+
if !has_errors {
124+
invalid_type_err(cx, expr, true);
125+
}
126+
has_errors = true;
127+
}
128+
},
129+
_ => {
130+
missing_literals.push(expr.span);
131+
}
132+
}
133+
}
134+
}
135+
ast::ExprKind::Lit(ref lit) => match lit.kind {
136+
ast::LitKind::Byte(val) => {
137+
accumulator.push(val);
138+
}
139+
ast::LitKind::ByteStr(ref bytes) => {
140+
accumulator.extend_from_slice(&bytes);
141+
}
142+
_ => {
143+
if !has_errors {
144+
invalid_type_err(cx, &e, false);
145+
}
146+
has_errors = true;
147+
}
148+
},
149+
ast::ExprKind::Err => {
150+
has_errors = true;
151+
}
152+
_ => {
153+
missing_literals.push(e.span);
154+
}
155+
}
156+
}
157+
if !missing_literals.is_empty() {
158+
let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
159+
err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
160+
err.emit();
161+
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
162+
} else if has_errors {
163+
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
164+
}
165+
let sp = cx.with_def_site_ctxt(sp);
166+
base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::from(accumulator))))
167+
}

Diff for: compiler/rustc_builtin_macros/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod cfg_accessible;
2727
mod cfg_eval;
2828
mod compile_error;
2929
mod concat;
30+
mod concat_bytes;
3031
mod concat_idents;
3132
mod derive;
3233
mod deriving;
@@ -65,6 +66,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
6566
cfg: cfg::expand_cfg,
6667
column: source_util::expand_column,
6768
compile_error: compile_error::expand_compile_error,
69+
concat_bytes: concat_bytes::expand_concat_bytes,
6870
concat_idents: concat_idents::expand_concat_idents,
6971
concat: concat::expand_concat,
7072
env: env::expand_env,

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ symbols! {
439439
compiler_builtins,
440440
compiler_fence,
441441
concat,
442+
concat_bytes,
442443
concat_idents,
443444
conservative_impl_trait,
444445
console,

Diff for: library/core/src/macros/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,34 @@ pub(crate) mod builtin {
967967
($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
968968
}
969969

970+
/// Concatenates literals into a byte slice.
971+
///
972+
/// This macro takes any number of comma-separated literals, and concatenates them all into
973+
/// one, yielding an expression of type `&[u8, _]`, which represents all of the literals
974+
/// concatenated left-to-right. The literals passed can be any combination of:
975+
///
976+
/// - byte literals (`b'r'`)
977+
/// - byte strings (`b"Rust"`)
978+
/// - arrays of bytes/numbers (`[b'A', 66, b'C']`)
979+
///
980+
/// # Examples
981+
///
982+
/// ```
983+
/// #![feature(concat_bytes)]
984+
///
985+
/// # fn main() {
986+
/// let s: &[u8; 6] = concat_bytes!(b'A', b"BC", [68, b'E', 70]);
987+
/// assert_eq!(s, b"ABCDEF");
988+
/// # }
989+
/// ```
990+
#[cfg(not(bootstrap))]
991+
#[unstable(feature = "concat_bytes", issue = "87555")]
992+
#[rustc_builtin_macro]
993+
#[macro_export]
994+
macro_rules! concat_bytes {
995+
($($e:literal),+ $(,)?) => {{ /* compiler built-in */ }};
996+
}
997+
970998
/// Concatenates literals into a static string slice.
971999
///
9721000
/// This macro takes any number of comma-separated literals, yielding an

Diff for: library/core/src/prelude/v1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ pub use crate::{
6060
option_env, stringify, trace_macros,
6161
};
6262

63+
#[unstable(
64+
feature = "concat_bytes",
65+
issue = "87555",
66+
reason = "`concat_bytes` is not stable enough for use and is subject to change"
67+
)]
68+
#[cfg(not(bootstrap))]
69+
#[doc(no_inline)]
70+
pub use crate::concat_bytes;
71+
6372
#[unstable(
6473
feature = "asm",
6574
issue = "72016",

Diff for: library/std/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@
250250
#![feature(cfg_target_thread_local)]
251251
#![feature(char_error_internals)]
252252
#![feature(char_internals)]
253+
#![cfg_attr(not(bootstrap), feature(concat_bytes))]
253254
#![feature(concat_idents)]
254255
#![feature(const_cstr_unchecked)]
255256
#![feature(const_fn_floating_point_arithmetic)]
@@ -576,6 +577,14 @@ pub use core::{
576577
log_syntax, module_path, option_env, stringify, trace_macros,
577578
};
578579

580+
#[unstable(
581+
feature = "concat_bytes",
582+
issue = "87555",
583+
reason = "`concat_bytes` is not stable enough for use and is subject to change"
584+
)]
585+
#[cfg(not(bootstrap))]
586+
pub use core::concat_bytes;
587+
579588
#[stable(feature = "core_primitive", since = "1.43.0")]
580589
pub use core::primitive;
581590

Diff for: library/std/src/prelude/v1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ pub use core::prelude::v1::{
4545
PartialOrd,
4646
};
4747

48+
#[unstable(
49+
feature = "concat_bytes",
50+
issue = "87555",
51+
reason = "`concat_bytes` is not stable enough for use and is subject to change"
52+
)]
53+
#[cfg(not(bootstrap))]
54+
#[doc(no_inline)]
55+
pub use core::prelude::v1::concat_bytes;
56+
4857
#[unstable(
4958
feature = "asm",
5059
issue = "72016",
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let a = concat_bytes!(b'A', b"BC"); //~ ERROR use of unstable library feature 'concat_bytes'
3+
assert_eq!(a, &[65, 66, 67]);
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: use of unstable library feature 'concat_bytes'
2+
--> $DIR/feature-gate-concat_bytes.rs:2:13
3+
|
4+
LL | let a = concat_bytes!(b'A', b"BC");
5+
| ^^^^^^^^^^^^
6+
|
7+
= note: see issue #87555 <https://github.com/rust-lang/rust/issues/87555> for more information
8+
= help: add `#![feature(concat_bytes)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

Diff for: src/test/ui/macros/concat-bytes-error.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![feature(concat_bytes)]
2+
3+
fn main() {
4+
concat_bytes!(pie); //~ ERROR expected a byte literal
5+
concat_bytes!(pie, pie); //~ ERROR expected a byte literal
6+
concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
7+
concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
8+
concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
9+
concat_bytes!('a'); //~ ERROR cannot concatenate character literals
10+
concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
11+
concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
12+
concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
13+
concat_bytes!([
14+
"hi", //~ ERROR cannot concatenate string literals
15+
]);
16+
concat_bytes!([
17+
'a', //~ ERROR cannot concatenate character literals
18+
]);
19+
concat_bytes!([
20+
true, //~ ERROR cannot concatenate boolean literals
21+
]);
22+
concat_bytes!([
23+
false, //~ ERROR cannot concatenate boolean literals
24+
]);
25+
concat_bytes!([
26+
2.6, //~ ERROR cannot concatenate float literals
27+
]);
28+
concat_bytes!([
29+
265, //~ ERROR numeric literal is out of bounds
30+
]);
31+
concat_bytes!([
32+
-33, //~ ERROR expected a byte literal
33+
]);
34+
concat_bytes!([
35+
b"hi!", //~ ERROR cannot concatenate doubly nested array
36+
]);
37+
concat_bytes!([
38+
[5, 6, 7], //~ ERROR cannot concatenate doubly nested array
39+
]);
40+
concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
41+
concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
42+
}

0 commit comments

Comments
 (0)