Rollup merge of #87599 - Smittyvb:concat_bytes, r=Mark-Simulacrum
Implement concat_bytes! This implements the unstable `concat_bytes!` macro, which has tracking issue #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.
This commit is contained in:
commit
3fc5bd7abc
167
compiler/rustc_builtin_macros/src/concat_bytes.rs
Normal file
167
compiler/rustc_builtin_macros/src/concat_bytes.rs
Normal file
@ -0,0 +1,167 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::{ptr::P, tokenstream::TokenStream};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_expand::base::{self, DummyResult};
|
||||
|
||||
/// Emits errors for literal expressions that are invalid inside and outside of an array.
|
||||
fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
|
||||
let lit = if let ast::ExprKind::Lit(lit) = &expr.kind {
|
||||
lit
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
match lit.kind {
|
||||
ast::LitKind::Char(_) => {
|
||||
let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
|
||||
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"try using a byte character",
|
||||
format!("b{}", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
ast::LitKind::Str(_, _) => {
|
||||
let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals");
|
||||
// suggestion would be invalid if we are nested
|
||||
if !is_nested {
|
||||
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"try using a byte string",
|
||||
format!("b{}", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
ast::LitKind::Float(_, _) => {
|
||||
cx.span_err(expr.span, "cannot concatenate float literals");
|
||||
}
|
||||
ast::LitKind::Bool(_) => {
|
||||
cx.span_err(expr.span, "cannot concatenate boolean literals");
|
||||
}
|
||||
ast::LitKind::Err(_) => {}
|
||||
ast::LitKind::Int(_, _) if !is_nested => {
|
||||
let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
|
||||
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
|
||||
err.span_suggestion(
|
||||
expr.span,
|
||||
"try wrapping the number in an array",
|
||||
format!("[{}]", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
ast::LitKind::Int(
|
||||
val,
|
||||
ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
|
||||
) => {
|
||||
assert!(val > u8::MAX.into()); // must be an error
|
||||
cx.span_err(expr.span, "numeric literal is out of bounds");
|
||||
}
|
||||
ast::LitKind::Int(_, _) => {
|
||||
cx.span_err(expr.span, "numeric literal is not a `u8`");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_concat_bytes(
|
||||
cx: &mut base::ExtCtxt<'_>,
|
||||
sp: rustc_span::Span,
|
||||
tts: TokenStream,
|
||||
) -> Box<dyn base::MacResult + 'static> {
|
||||
let es = match base::get_exprs_from_tts(cx, sp, tts) {
|
||||
Some(e) => e,
|
||||
None => return DummyResult::any(sp),
|
||||
};
|
||||
let mut accumulator = Vec::new();
|
||||
let mut missing_literals = vec![];
|
||||
let mut has_errors = false;
|
||||
for e in es {
|
||||
match e.kind {
|
||||
ast::ExprKind::Array(ref exprs) => {
|
||||
for expr in exprs {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Array(_) => {
|
||||
if !has_errors {
|
||||
cx.span_err(expr.span, "cannot concatenate doubly nested array");
|
||||
}
|
||||
has_errors = true;
|
||||
}
|
||||
ast::ExprKind::Lit(ref lit) => match lit.kind {
|
||||
ast::LitKind::Int(
|
||||
val,
|
||||
ast::LitIntType::Unsuffixed
|
||||
| ast::LitIntType::Unsigned(ast::UintTy::U8),
|
||||
) if val <= u8::MAX.into() => {
|
||||
accumulator.push(val as u8);
|
||||
}
|
||||
|
||||
ast::LitKind::Byte(val) => {
|
||||
accumulator.push(val);
|
||||
}
|
||||
ast::LitKind::ByteStr(_) => {
|
||||
if !has_errors {
|
||||
cx.struct_span_err(
|
||||
expr.span,
|
||||
"cannot concatenate doubly nested array",
|
||||
)
|
||||
.note("byte strings are treated as arrays of bytes")
|
||||
.help("try flattening the array")
|
||||
.emit();
|
||||
}
|
||||
has_errors = true;
|
||||
}
|
||||
_ => {
|
||||
if !has_errors {
|
||||
invalid_type_err(cx, expr, true);
|
||||
}
|
||||
has_errors = true;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
missing_literals.push(expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Lit(ref lit) => match lit.kind {
|
||||
ast::LitKind::Byte(val) => {
|
||||
accumulator.push(val);
|
||||
}
|
||||
ast::LitKind::ByteStr(ref bytes) => {
|
||||
accumulator.extend_from_slice(&bytes);
|
||||
}
|
||||
_ => {
|
||||
if !has_errors {
|
||||
invalid_type_err(cx, &e, false);
|
||||
}
|
||||
has_errors = true;
|
||||
}
|
||||
},
|
||||
ast::ExprKind::Err => {
|
||||
has_errors = true;
|
||||
}
|
||||
_ => {
|
||||
missing_literals.push(e.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !missing_literals.is_empty() {
|
||||
let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal");
|
||||
err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
|
||||
err.emit();
|
||||
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
|
||||
} else if has_errors {
|
||||
return base::MacEager::expr(DummyResult::raw_expr(sp, true));
|
||||
}
|
||||
let sp = cx.with_def_site_ctxt(sp);
|
||||
base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::from(accumulator))))
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
mod cfg_eval;
|
||||
mod compile_error;
|
||||
mod concat;
|
||||
mod concat_bytes;
|
||||
mod concat_idents;
|
||||
mod derive;
|
||||
mod deriving;
|
||||
@ -65,6 +66,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
||||
cfg: cfg::expand_cfg,
|
||||
column: source_util::expand_column,
|
||||
compile_error: compile_error::expand_compile_error,
|
||||
concat_bytes: concat_bytes::expand_concat_bytes,
|
||||
concat_idents: concat_idents::expand_concat_idents,
|
||||
concat: concat::expand_concat,
|
||||
env: env::expand_env,
|
||||
|
@ -439,6 +439,7 @@
|
||||
compiler_builtins,
|
||||
compiler_fence,
|
||||
concat,
|
||||
concat_bytes,
|
||||
concat_idents,
|
||||
conservative_impl_trait,
|
||||
console,
|
||||
|
@ -967,6 +967,34 @@ macro_rules! concat_idents {
|
||||
($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
|
||||
/// Concatenates literals into a byte slice.
|
||||
///
|
||||
/// This macro takes any number of comma-separated literals, and concatenates them all into
|
||||
/// one, yielding an expression of type `&[u8, _]`, which represents all of the literals
|
||||
/// concatenated left-to-right. The literals passed can be any combination of:
|
||||
///
|
||||
/// - byte literals (`b'r'`)
|
||||
/// - byte strings (`b"Rust"`)
|
||||
/// - arrays of bytes/numbers (`[b'A', 66, b'C']`)
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(concat_bytes)]
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let s: &[u8; 6] = concat_bytes!(b'A', b"BC", [68, b'E', 70]);
|
||||
/// assert_eq!(s, b"ABCDEF");
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "concat_bytes", issue = "87555")]
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
macro_rules! concat_bytes {
|
||||
($($e:literal),+ $(,)?) => {{ /* compiler built-in */ }};
|
||||
}
|
||||
|
||||
/// Concatenates literals into a static string slice.
|
||||
///
|
||||
/// This macro takes any number of comma-separated literals, yielding an
|
||||
|
@ -60,6 +60,15 @@
|
||||
option_env, stringify, trace_macros,
|
||||
};
|
||||
|
||||
#[unstable(
|
||||
feature = "concat_bytes",
|
||||
issue = "87555",
|
||||
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::concat_bytes;
|
||||
|
||||
#[unstable(
|
||||
feature = "asm",
|
||||
issue = "72016",
|
||||
|
@ -250,6 +250,7 @@
|
||||
#![feature(cfg_target_thread_local)]
|
||||
#![feature(char_error_internals)]
|
||||
#![feature(char_internals)]
|
||||
#![cfg_attr(not(bootstrap), feature(concat_bytes))]
|
||||
#![feature(concat_idents)]
|
||||
#![feature(const_cstr_unchecked)]
|
||||
#![feature(const_fn_floating_point_arithmetic)]
|
||||
@ -576,6 +577,14 @@ pub mod task {
|
||||
log_syntax, module_path, option_env, stringify, trace_macros,
|
||||
};
|
||||
|
||||
#[unstable(
|
||||
feature = "concat_bytes",
|
||||
issue = "87555",
|
||||
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[cfg(not(bootstrap))]
|
||||
pub use core::concat_bytes;
|
||||
|
||||
#[stable(feature = "core_primitive", since = "1.43.0")]
|
||||
pub use core::primitive;
|
||||
|
||||
|
@ -45,6 +45,15 @@
|
||||
PartialOrd,
|
||||
};
|
||||
|
||||
#[unstable(
|
||||
feature = "concat_bytes",
|
||||
issue = "87555",
|
||||
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[cfg(not(bootstrap))]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::concat_bytes;
|
||||
|
||||
#[unstable(
|
||||
feature = "asm",
|
||||
issue = "72016",
|
||||
|
4
src/test/ui/feature-gates/feature-gate-concat_bytes.rs
Normal file
4
src/test/ui/feature-gates/feature-gate-concat_bytes.rs
Normal file
@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
let a = concat_bytes!(b'A', b"BC"); //~ ERROR use of unstable library feature 'concat_bytes'
|
||||
assert_eq!(a, &[65, 66, 67]);
|
||||
}
|
12
src/test/ui/feature-gates/feature-gate-concat_bytes.stderr
Normal file
12
src/test/ui/feature-gates/feature-gate-concat_bytes.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0658]: use of unstable library feature 'concat_bytes'
|
||||
--> $DIR/feature-gate-concat_bytes.rs:2:13
|
||||
|
|
||||
LL | let a = concat_bytes!(b'A', b"BC");
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #87555 <https://github.com/rust-lang/rust/issues/87555> for more information
|
||||
= help: add `#![feature(concat_bytes)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
42
src/test/ui/macros/concat-bytes-error.rs
Normal file
42
src/test/ui/macros/concat-bytes-error.rs
Normal file
@ -0,0 +1,42 @@
|
||||
#![feature(concat_bytes)]
|
||||
|
||||
fn main() {
|
||||
concat_bytes!(pie); //~ ERROR expected a byte literal
|
||||
concat_bytes!(pie, pie); //~ ERROR expected a byte literal
|
||||
concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
|
||||
concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
|
||||
concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
|
||||
concat_bytes!('a'); //~ ERROR cannot concatenate character literals
|
||||
concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
|
||||
concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
|
||||
concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
|
||||
concat_bytes!([
|
||||
"hi", //~ ERROR cannot concatenate string literals
|
||||
]);
|
||||
concat_bytes!([
|
||||
'a', //~ ERROR cannot concatenate character literals
|
||||
]);
|
||||
concat_bytes!([
|
||||
true, //~ ERROR cannot concatenate boolean literals
|
||||
]);
|
||||
concat_bytes!([
|
||||
false, //~ ERROR cannot concatenate boolean literals
|
||||
]);
|
||||
concat_bytes!([
|
||||
2.6, //~ ERROR cannot concatenate float literals
|
||||
]);
|
||||
concat_bytes!([
|
||||
265, //~ ERROR numeric literal is out of bounds
|
||||
]);
|
||||
concat_bytes!([
|
||||
-33, //~ ERROR expected a byte literal
|
||||
]);
|
||||
concat_bytes!([
|
||||
b"hi!", //~ ERROR cannot concatenate doubly nested array
|
||||
]);
|
||||
concat_bytes!([
|
||||
[5, 6, 7], //~ ERROR cannot concatenate doubly nested array
|
||||
]);
|
||||
concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
|
||||
concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
|
||||
}
|
131
src/test/ui/macros/concat-bytes-error.stderr
Normal file
131
src/test/ui/macros/concat-bytes-error.stderr
Normal file
@ -0,0 +1,131 @@
|
||||
error: expected a byte literal
|
||||
--> $DIR/concat-bytes-error.rs:4:19
|
||||
|
|
||||
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
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | concat_bytes!("tnrsi", "tnri");
|
||||
| ^^^^^^^ help: try using a byte string: `b"tnrsi"`
|
||||
|
||||
error: cannot concatenate float literals
|
||||
--> $DIR/concat-bytes-error.rs:7:19
|
||||
|
|
||||
LL | concat_bytes!(2.8);
|
||||
| ^^^
|
||||
|
||||
error: cannot concatenate numeric literals
|
||||
--> $DIR/concat-bytes-error.rs:8: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
|
||||
|
|
||||
LL | concat_bytes!('a');
|
||||
| ^^^ help: try using a byte character: `b'a'`
|
||||
|
||||
error: cannot concatenate boolean literals
|
||||
--> $DIR/concat-bytes-error.rs:10:19
|
||||
|
|
||||
LL | concat_bytes!(true, false);
|
||||
| ^^^^
|
||||
|
||||
error: cannot concatenate numeric literals
|
||||
--> $DIR/concat-bytes-error.rs:11: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
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | "hi",
|
||||
| ^^^^
|
||||
|
||||
error: cannot concatenate character literals
|
||||
--> $DIR/concat-bytes-error.rs:17:9
|
||||
|
|
||||
LL | 'a',
|
||||
| ^^^ help: try using a byte character: `b'a'`
|
||||
|
||||
error: cannot concatenate boolean literals
|
||||
--> $DIR/concat-bytes-error.rs:20:9
|
||||
|
|
||||
LL | true,
|
||||
| ^^^^
|
||||
|
||||
error: cannot concatenate boolean literals
|
||||
--> $DIR/concat-bytes-error.rs:23:9
|
||||
|
|
||||
LL | false,
|
||||
| ^^^^^
|
||||
|
||||
error: cannot concatenate float literals
|
||||
--> $DIR/concat-bytes-error.rs:26:9
|
||||
|
|
||||
LL | 2.6,
|
||||
| ^^^
|
||||
|
||||
error: numeric literal is out of bounds
|
||||
--> $DIR/concat-bytes-error.rs:29:9
|
||||
|
|
||||
LL | 265,
|
||||
| ^^^
|
||||
|
||||
error: expected a byte literal
|
||||
--> $DIR/concat-bytes-error.rs:32:9
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | b"hi!",
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: byte strings are treated as arrays of bytes
|
||||
= help: try flattening the array
|
||||
|
||||
error: cannot concatenate doubly nested array
|
||||
--> $DIR/concat-bytes-error.rs:38:9
|
||||
|
|
||||
LL | [5, 6, 7],
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: cannot concatenate numeric literals
|
||||
--> $DIR/concat-bytes-error.rs:40: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
|
||||
|
|
||||
LL | concat_bytes!([5u16]);
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 20 previous errors
|
||||
|
7
src/test/ui/macros/concat-bytes.rs
Normal file
7
src/test/ui/macros/concat-bytes.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// run-pass
|
||||
#![feature(concat_bytes)]
|
||||
|
||||
fn main() {
|
||||
assert_eq!(concat_bytes!(), &[]);
|
||||
assert_eq!(concat_bytes!(b'A', b"BC", [68, b'E', 70]), b"ABCDEF");
|
||||
}
|
Loading…
Reference in New Issue
Block a user