mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			189 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use rustc_ast::ptr::P;
 | |
| use rustc_ast::tokenstream::TokenStream;
 | |
| use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
 | |
| use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 | |
| use rustc_session::errors::report_lit_error;
 | |
| use rustc_span::{ErrorGuaranteed, Span};
 | |
| 
 | |
| use crate::errors;
 | |
| use crate::util::get_exprs_from_tts;
 | |
| 
 | |
| /// Emits errors for literal expressions that are invalid inside and outside of an array.
 | |
| fn invalid_type_err(
 | |
|     cx: &ExtCtxt<'_>,
 | |
|     token_lit: token::Lit,
 | |
|     span: Span,
 | |
|     is_nested: bool,
 | |
| ) -> ErrorGuaranteed {
 | |
|     use errors::{
 | |
|         ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
 | |
|     };
 | |
|     let snippet = cx.sess.source_map().span_to_snippet(span).ok();
 | |
|     let dcx = cx.dcx();
 | |
|     match LitKind::from_token_lit(token_lit) {
 | |
|         Ok(LitKind::CStr(_, _)) => {
 | |
|             // Avoid ambiguity in handling of terminal `NUL` by refusing to
 | |
|             // concatenate C string literals as bytes.
 | |
|             dcx.emit_err(errors::ConcatCStrLit { span })
 | |
|         }
 | |
|         Ok(LitKind::Char(_)) => {
 | |
|             let sugg =
 | |
|                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
 | |
|             dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
 | |
|         }
 | |
|         Ok(LitKind::Str(_, _)) => {
 | |
|             // suggestion would be invalid if we are nested
 | |
|             let sugg = if !is_nested {
 | |
|                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
 | |
|             } else {
 | |
|                 None
 | |
|             };
 | |
|             dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
 | |
|         }
 | |
|         Ok(LitKind::Float(_, _)) => {
 | |
|             dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
 | |
|         }
 | |
|         Ok(LitKind::Bool(_)) => {
 | |
|             dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
 | |
|         }
 | |
|         Ok(LitKind::Int(_, _)) if !is_nested => {
 | |
|             let sugg =
 | |
|                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
 | |
|             dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
 | |
|         }
 | |
|         Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
 | |
|             assert!(val.get() > u8::MAX.into()); // must be an error
 | |
|             dcx.emit_err(ConcatBytesOob { span })
 | |
|         }
 | |
|         Ok(LitKind::Int(_, _)) => dcx.emit_err(ConcatBytesNonU8 { span }),
 | |
|         Ok(LitKind::ByteStr(..) | LitKind::Byte(_)) => unreachable!(),
 | |
|         Ok(LitKind::Err(guar)) => guar,
 | |
|         Err(err) => report_lit_error(&cx.sess.psess, err, token_lit, span),
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Returns `expr` as a *single* byte literal if applicable.
 | |
| ///
 | |
| /// Otherwise, returns `None`, and either pushes the `expr`'s span to `missing_literals` or
 | |
| /// updates `guar` accordingly.
 | |
| fn handle_array_element(
 | |
|     cx: &ExtCtxt<'_>,
 | |
|     guar: &mut Option<ErrorGuaranteed>,
 | |
|     missing_literals: &mut Vec<rustc_span::Span>,
 | |
|     expr: &P<rustc_ast::Expr>,
 | |
| ) -> Option<u8> {
 | |
|     let dcx = cx.dcx();
 | |
| 
 | |
|     match expr.kind {
 | |
|         ExprKind::Lit(token_lit) => {
 | |
|             match LitKind::from_token_lit(token_lit) {
 | |
|                 Ok(LitKind::Int(
 | |
|                     val,
 | |
|                     LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8),
 | |
|                 )) if let Ok(val) = u8::try_from(val.get()) => {
 | |
|                     return Some(val);
 | |
|                 }
 | |
|                 Ok(LitKind::Byte(val)) => return Some(val),
 | |
|                 Ok(LitKind::ByteStr(..)) => {
 | |
|                     guar.get_or_insert_with(|| {
 | |
|                         dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true })
 | |
|                     });
 | |
|                 }
 | |
|                 _ => {
 | |
|                     guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, expr.span, true));
 | |
|                 }
 | |
|             };
 | |
|         }
 | |
|         ExprKind::Array(_) | ExprKind::Repeat(_, _) => {
 | |
|             guar.get_or_insert_with(|| {
 | |
|                 dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
 | |
|             });
 | |
|         }
 | |
|         ExprKind::IncludedBytes(..) => {
 | |
|             guar.get_or_insert_with(|| {
 | |
|                 dcx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false })
 | |
|             });
 | |
|         }
 | |
|         _ => missing_literals.push(expr.span),
 | |
|     }
 | |
| 
 | |
|     None
 | |
| }
 | |
| 
 | |
| pub(crate) fn expand_concat_bytes(
 | |
|     cx: &mut ExtCtxt<'_>,
 | |
|     sp: Span,
 | |
|     tts: TokenStream,
 | |
| ) -> MacroExpanderResult<'static> {
 | |
|     let ExpandResult::Ready(mac) = get_exprs_from_tts(cx, tts) else {
 | |
|         return ExpandResult::Retry(());
 | |
|     };
 | |
|     let es = match mac {
 | |
|         Ok(es) => es,
 | |
|         Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)),
 | |
|     };
 | |
|     let mut accumulator = Vec::new();
 | |
|     let mut missing_literals = vec![];
 | |
|     let mut guar = None;
 | |
|     for e in es {
 | |
|         match &e.kind {
 | |
|             ExprKind::Array(exprs) => {
 | |
|                 for expr in exprs {
 | |
|                     if let Some(elem) =
 | |
|                         handle_array_element(cx, &mut guar, &mut missing_literals, expr)
 | |
|                     {
 | |
|                         accumulator.push(elem);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             ExprKind::Repeat(expr, count) => {
 | |
|                 if let ExprKind::Lit(token_lit) = count.value.kind
 | |
|                     && let Ok(LitKind::Int(count_val, _)) = LitKind::from_token_lit(token_lit)
 | |
|                 {
 | |
|                     if let Some(elem) =
 | |
|                         handle_array_element(cx, &mut guar, &mut missing_literals, expr)
 | |
|                     {
 | |
|                         for _ in 0..count_val.get() {
 | |
|                             accumulator.push(elem);
 | |
|                         }
 | |
|                     }
 | |
|                 } else {
 | |
|                     guar = Some(
 | |
|                         cx.dcx().emit_err(errors::ConcatBytesBadRepeat { span: count.value.span }),
 | |
|                     );
 | |
|                 }
 | |
|             }
 | |
|             &ExprKind::Lit(token_lit) => match LitKind::from_token_lit(token_lit) {
 | |
|                 Ok(LitKind::Byte(val)) => {
 | |
|                     accumulator.push(val);
 | |
|                 }
 | |
|                 Ok(LitKind::ByteStr(ref bytes, _)) => {
 | |
|                     accumulator.extend_from_slice(bytes);
 | |
|                 }
 | |
|                 _ => {
 | |
|                     guar.get_or_insert_with(|| invalid_type_err(cx, token_lit, e.span, false));
 | |
|                 }
 | |
|             },
 | |
|             ExprKind::IncludedBytes(bytes) => {
 | |
|                 accumulator.extend_from_slice(bytes);
 | |
|             }
 | |
|             ExprKind::Err(guarantee) => {
 | |
|                 guar = Some(*guarantee);
 | |
|             }
 | |
|             ExprKind::Dummy => cx.dcx().span_bug(e.span, "concatenating `ExprKind::Dummy`"),
 | |
|             _ => {
 | |
|                 missing_literals.push(e.span);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     ExpandResult::Ready(if !missing_literals.is_empty() {
 | |
|         let guar = cx.dcx().emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
 | |
|         MacEager::expr(DummyResult::raw_expr(sp, Some(guar)))
 | |
|     } else if let Some(guar) = guar {
 | |
|         MacEager::expr(DummyResult::raw_expr(sp, Some(guar)))
 | |
|     } else {
 | |
|         let sp = cx.with_def_site_ctxt(sp);
 | |
|         MacEager::expr(cx.expr_byte_str(sp, accumulator))
 | |
|     })
 | |
| }
 | 
