From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_builtin_macros/src/concat_bytes.rs | 189 ++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 compiler/rustc_builtin_macros/src/concat_bytes.rs (limited to 'compiler/rustc_builtin_macros/src/concat_bytes.rs') 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, 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, + expr: &P, +) -> Option { + 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 { + 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)))) +} -- cgit v1.2.3