summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros/src/concat_bytes.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src/concat_bytes.rs')
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
new file mode 100644
index 000000000..a1afec410
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -0,0 +1,189 @@
+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 ast::ExprKind::Lit(lit) = &expr.kind 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!(),
+ }
+}
+
+fn handle_array_element(
+ cx: &mut base::ExtCtxt<'_>,
+ has_errors: &mut bool,
+ missing_literals: &mut Vec<rustc_span::Span>,
+ expr: &P<rustc_ast::Expr>,
+) -> Option<u8> {
+ match expr.kind {
+ ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
+ if !*has_errors {
+ cx.span_err(expr.span, "cannot concatenate doubly nested array");
+ }
+ *has_errors = true;
+ None
+ }
+ 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() => Some(val as u8),
+
+ ast::LitKind::Byte(val) => Some(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;
+ None
+ }
+ _ => {
+ if !*has_errors {
+ invalid_type_err(cx, expr, true);
+ }
+ *has_errors = true;
+ None
+ }
+ },
+ _ => {
+ missing_literals.push(expr.span);
+ None
+ }
+ }
+}
+
+pub fn expand_concat_bytes(
+ cx: &mut base::ExtCtxt<'_>,
+ sp: rustc_span::Span,
+ tts: TokenStream,
+) -> Box<dyn base::MacResult + 'static> {
+ let Some(es) = base::get_exprs_from_tts(cx, sp, tts) else {
+ 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 {
+ if let Some(elem) =
+ handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
+ {
+ accumulator.push(elem);
+ }
+ }
+ }
+ ast::ExprKind::Repeat(ref expr, ref count) => {
+ if let ast::ExprKind::Lit(ast::Lit {
+ kind: ast::LitKind::Int(count_val, _), ..
+ }) = count.value.kind
+ {
+ if let Some(elem) =
+ handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
+ {
+ for _ in 0..count_val {
+ accumulator.push(elem);
+ }
+ }
+ } else {
+ cx.span_err(count.value.span, "repeat count is not a positive number");
+ }
+ }
+ 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))))
+}