• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use rustc_ast as ast;
2 use rustc_ast::{ptr::P, tokenstream::TokenStream};
3 use rustc_expand::base::{self, DummyResult};
4 use rustc_session::errors::report_lit_error;
5 use rustc_span::Span;
6 
7 use crate::errors;
8 
9 /// Emits errors for literal expressions that are invalid inside and outside of an array.
invalid_type_err( cx: &mut base::ExtCtxt<'_>, token_lit: ast::token::Lit, span: Span, is_nested: bool, )10 fn invalid_type_err(
11     cx: &mut base::ExtCtxt<'_>,
12     token_lit: ast::token::Lit,
13     span: Span,
14     is_nested: bool,
15 ) {
16     use errors::{
17         ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
18     };
19     let snippet = cx.sess.source_map().span_to_snippet(span).ok();
20     match ast::LitKind::from_token_lit(token_lit) {
21         Ok(ast::LitKind::CStr(_, _)) => {
22             // FIXME(c_str_literals): should concatenation of C string literals
23             // include the null bytes in the end?
24             cx.emit_err(errors::ConcatCStrLit { span: span });
25         }
26         Ok(ast::LitKind::Char(_)) => {
27             let sugg =
28                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
29             cx.sess.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg });
30         }
31         Ok(ast::LitKind::Str(_, _)) => {
32             // suggestion would be invalid if we are nested
33             let sugg = if !is_nested {
34                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
35             } else {
36                 None
37             };
38             cx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg });
39         }
40         Ok(ast::LitKind::Float(_, _)) => {
41             cx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None });
42         }
43         Ok(ast::LitKind::Bool(_)) => {
44             cx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None });
45         }
46         Ok(ast::LitKind::Err) => {}
47         Ok(ast::LitKind::Int(_, _)) if !is_nested => {
48             let sugg =
49                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span: span, snippet });
50             cx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg });
51         }
52         Ok(ast::LitKind::Int(
53             val,
54             ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
55         )) => {
56             assert!(val > u8::MAX.into()); // must be an error
57             cx.emit_err(ConcatBytesOob { span });
58         }
59         Ok(ast::LitKind::Int(_, _)) => {
60             cx.emit_err(ConcatBytesNonU8 { span });
61         }
62         Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(),
63         Err(err) => {
64             report_lit_error(&cx.sess.parse_sess, err, token_lit, span);
65         }
66     }
67 }
68 
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>69 fn handle_array_element(
70     cx: &mut base::ExtCtxt<'_>,
71     has_errors: &mut bool,
72     missing_literals: &mut Vec<rustc_span::Span>,
73     expr: &P<rustc_ast::Expr>,
74 ) -> Option<u8> {
75     match expr.kind {
76         ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
77             if !*has_errors {
78                 cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
79             }
80             *has_errors = true;
81             None
82         }
83         ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
84             Ok(ast::LitKind::Int(
85                 val,
86                 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
87             )) if val <= u8::MAX.into() => Some(val as u8),
88 
89             Ok(ast::LitKind::Byte(val)) => Some(val),
90             Ok(ast::LitKind::ByteStr(..)) => {
91                 if !*has_errors {
92                     cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true });
93                 }
94                 *has_errors = true;
95                 None
96             }
97             _ => {
98                 if !*has_errors {
99                     invalid_type_err(cx, token_lit, expr.span, true);
100                 }
101                 *has_errors = true;
102                 None
103             }
104         },
105         ast::ExprKind::IncludedBytes(..) => {
106             if !*has_errors {
107                 cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
108             }
109             *has_errors = true;
110             None
111         }
112         _ => {
113             missing_literals.push(expr.span);
114             None
115         }
116     }
117 }
118 
expand_concat_bytes( cx: &mut base::ExtCtxt<'_>, sp: rustc_span::Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'static>119 pub fn expand_concat_bytes(
120     cx: &mut base::ExtCtxt<'_>,
121     sp: rustc_span::Span,
122     tts: TokenStream,
123 ) -> Box<dyn base::MacResult + 'static> {
124     let Some(es) = base::get_exprs_from_tts(cx, tts) else {
125         return DummyResult::any(sp);
126     };
127     let mut accumulator = Vec::new();
128     let mut missing_literals = vec![];
129     let mut has_errors = false;
130     for e in es {
131         match &e.kind {
132             ast::ExprKind::Array(exprs) => {
133                 for expr in exprs {
134                     if let Some(elem) =
135                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
136                     {
137                         accumulator.push(elem);
138                     }
139                 }
140             }
141             ast::ExprKind::Repeat(expr, count) => {
142                 if let ast::ExprKind::Lit(token_lit) = count.value.kind
143                 && let Ok(ast::LitKind::Int(count_val, _)) =
144                     ast::LitKind::from_token_lit(token_lit)
145                 {
146                     if let Some(elem) =
147                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
148                     {
149                         for _ in 0..count_val {
150                             accumulator.push(elem);
151                         }
152                     }
153                 } else {
154                     cx.emit_err(errors::ConcatBytesBadRepeat {span: count.value.span });
155                 }
156             }
157             &ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
158                 Ok(ast::LitKind::Byte(val)) => {
159                     accumulator.push(val);
160                 }
161                 Ok(ast::LitKind::ByteStr(ref bytes, _)) => {
162                     accumulator.extend_from_slice(&bytes);
163                 }
164                 _ => {
165                     if !has_errors {
166                         invalid_type_err(cx, token_lit, e.span, false);
167                     }
168                     has_errors = true;
169                 }
170             },
171             ast::ExprKind::IncludedBytes(bytes) => {
172                 accumulator.extend_from_slice(bytes);
173             }
174             ast::ExprKind::Err => {
175                 has_errors = true;
176             }
177             _ => {
178                 missing_literals.push(e.span);
179             }
180         }
181     }
182     if !missing_literals.is_empty() {
183         cx.emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
184         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
185     } else if has_errors {
186         return base::MacEager::expr(DummyResult::raw_expr(sp, true));
187     }
188     let sp = cx.with_def_site_ctxt(sp);
189     base::MacEager::expr(cx.expr_byte_str(sp, accumulator))
190 }
191