Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/builtin_fn_macro.rs')
-rw-r--r--crates/hir-expand/src/builtin_fn_macro.rs199
1 files changed, 113 insertions, 86 deletions
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 97867dfc66..32befb7a7f 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,13 +1,14 @@
//! Builtin macro
+use ::tt::SmolStr;
use base_db::{AnchoredPath, FileId};
use cfg::CfgExpr;
use either::Either;
use intern::sym;
-use itertools::Itertools;
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
-use syntax::ast::{self, AstToken};
+use stdx::format_to;
+use syntax::unescape::{unescape_byte, unescape_char, unescape_unicode, Mode};
use crate::{
db::ExpandDatabase,
@@ -177,8 +178,10 @@ fn line_expand(
ExpandResult::ok(tt::Subtree {
delimiter: tt::Delimiter::invisible_spanned(span),
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
- text: "0u32".into(),
+ text: "0".into(),
span,
+ kind: tt::LitKind::Integer,
+ suffix: Some(Box::new("u32".into())),
}))]),
})
}
@@ -444,27 +447,6 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
}
}
-fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
- let span = lit.span;
- let lit = ast::make::tokens::literal(&lit.to_string());
- let token = ast::String::cast(lit)?;
- token.value().ok().map(|it| (it.into_owned(), span))
-}
-
-fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
- let span = lit.span;
- let lit = ast::make::tokens::literal(&lit.to_string());
- let token = ast::Char::cast(lit)?;
- token.value().ok().zip(Some(span))
-}
-
-fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
- let span = lit.span;
- let lit = ast::make::tokens::literal(&lit.to_string());
- let token = ast::ByteString::cast(lit)?;
- token.value().ok().map(|it| (it.into_owned(), span))
-}
-
fn compile_error_expand(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
@@ -472,10 +454,16 @@ fn compile_error_expand(
span: Span,
) -> ExpandResult<tt::Subtree> {
let err = match &*tt.token_trees {
- [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
- Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()),
- None => ExpandError::other("`compile_error!` argument must be a string"),
- },
+ [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text,
+ span: _,
+ kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
+ suffix: _,
+ }))] =>
+ // FIXME: Use the span here!
+ {
+ ExpandError::other(Box::from(&*unescape_str(text)))
+ }
_ => ExpandError::other("`compile_error!` argument must be a string"),
};
@@ -507,20 +495,33 @@ fn concat_expand(
}
}
}
-
match t {
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
// concat works with string and char literals, so remove any quotes.
// It also works with integer, float and boolean literals, so just use the rest
// as-is.
- if let Some((c, span)) = unquote_char(it) {
- text.push(c);
- record_span(span);
- } else {
- let (component, span) =
- unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span));
- text.push_str(&component);
- record_span(span);
+ match it.kind {
+ tt::LitKind::Char => {
+ if let Ok(c) = unescape_char(&it.text) {
+ text.extend(c.escape_default());
+ }
+ record_span(it.span);
+ }
+ tt::LitKind::Integer | tt::LitKind::Float => format_to!(text, "{}", it.text),
+ tt::LitKind::Str => {
+ text.push_str(&it.text);
+ record_span(it.span);
+ }
+ tt::LitKind::StrRaw(_) => {
+ format_to!(text, "{}", it.text.escape_debug());
+ record_span(it.span);
+ }
+ tt::LitKind::Byte
+ | tt::LitKind::ByteStr
+ | tt::LitKind::ByteStrRaw(_)
+ | tt::LitKind::CStr
+ | tt::LitKind::CStrRaw(_)
+ | tt::LitKind::Err(_) => err = Some(ExpandError::other("unexpected literal")),
}
}
// handle boolean literals
@@ -544,9 +545,9 @@ fn concat_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
- call_site: Span,
+ _: Span,
) -> ExpandResult<tt::Subtree> {
- let mut bytes = Vec::new();
+ let mut bytes = String::new();
let mut err = None;
let mut span: Option<Span> = None;
let mut record_span = |s: Span| match &mut span {
@@ -556,14 +557,21 @@ fn concat_bytes_expand(
};
for (i, t) in tt.token_trees.iter().enumerate() {
match t {
- tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
- let token = ast::make::tokens::literal(&lit.to_string());
- record_span(lit.span);
- match token.kind() {
- syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
- syntax::SyntaxKind::BYTE_STRING => {
- let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it);
- components.into_iter().for_each(|it| bytes.push(it.to_string()));
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, span, kind, suffix: _ })) => {
+ record_span(*span);
+ match kind {
+ tt::LitKind::Byte => {
+ if let Ok(b) = unescape_byte(text) {
+ bytes.extend(
+ b.escape_ascii().filter_map(|it| char::from_u32(it as u32)),
+ );
+ }
+ }
+ tt::LitKind::ByteStr => {
+ bytes.push_str(text);
+ }
+ tt::LitKind::ByteStrRaw(_) => {
+ bytes.extend(text.escape_debug());
}
_ => {
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
@@ -584,51 +592,49 @@ fn concat_bytes_expand(
}
}
}
- let value = tt::Subtree {
- delimiter: tt::Delimiter {
- open: call_site,
- close: call_site,
- kind: tt::DelimiterKind::Bracket,
- },
- token_trees: {
- Itertools::intersperse_with(
- bytes.into_iter().map(|it| {
- tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
- text: it.into(),
- span: span.unwrap_or(call_site),
- }))
- }),
- || {
- tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
- char: ',',
- spacing: tt::Spacing::Alone,
- span: call_site,
- }))
- },
- )
- .collect()
+ let span = span.unwrap_or(tt.delimiter.open);
+ ExpandResult {
+ value: tt::Subtree {
+ delimiter: tt::Delimiter::invisible_spanned(span),
+ token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text: bytes.into(),
+ span,
+ kind: tt::LitKind::ByteStr,
+ suffix: None,
+ }))]
+ .into(),
},
- };
- ExpandResult { value, err }
+ err,
+ }
}
fn concat_bytes_expand_subtree(
tree: &tt::Subtree,
- bytes: &mut Vec<String>,
+ bytes: &mut String,
mut record_span: impl FnMut(Span),
) -> Result<(), ExpandError> {
for (ti, tt) in tree.token_trees.iter().enumerate() {
match tt {
- tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
- let lit = ast::make::tokens::literal(&it.to_string());
- match lit.kind() {
- syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
- record_span(it.span);
- bytes.push(lit.text().to_owned())
- }
- _ => {
- return Err(mbe::ExpandError::UnexpectedToken.into());
- }
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text,
+ span,
+ kind: tt::LitKind::Byte,
+ suffix: _,
+ })) => {
+ if let Ok(b) = unescape_byte(text) {
+ bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
+ }
+ record_span(*span);
+ }
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text,
+ span,
+ kind: tt::LitKind::Integer,
+ suffix: _,
+ })) => {
+ record_span(*span);
+ if let Ok(b) = text.parse::<u8>() {
+ bytes.extend(b.escape_ascii().filter_map(|it| char::from_u32(it as u32)));
}
}
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (),
@@ -660,7 +666,7 @@ fn concat_idents_expand(
}
}
// FIXME merge spans
- let ident = tt::Ident { text: ident.into(), span };
+ let ident = tt::Ident { text: ident.into(), span, is_raw: tt::IdentIsRaw::No };
ExpandResult { value: quote!(span =>#ident), err }
}
@@ -683,11 +689,16 @@ fn relative_file(
}
}
-fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> {
+fn parse_string(tt: &tt::Subtree) -> Result<(SmolStr, Span), ExpandError> {
tt.token_trees
.first()
.and_then(|tt| match tt {
- tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text,
+ span,
+ kind: tt::LitKind::Str,
+ suffix: _,
+ })) => Some((unescape_str(text), *span)),
_ => None,
})
.ok_or(mbe::ExpandError::ConversionError.into())
@@ -738,6 +749,8 @@ fn include_bytes_expand(
token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
text: r#"b"""#.into(),
span,
+ kind: tt::LitKind::ByteStrRaw(1),
+ suffix: None,
}))]),
};
ExpandResult::ok(res)
@@ -848,3 +861,17 @@ fn quote_expand(
ExpandError::other("quote! is not implemented"),
)
}
+
+fn unescape_str(s: &SmolStr) -> SmolStr {
+ if s.contains('\\') {
+ let mut buf = String::with_capacity(s.len());
+ unescape_unicode(s, Mode::Str, &mut |_, c| {
+ if let Ok(c) = c {
+ buf.push(c)
+ }
+ });
+ buf.into()
+ } else {
+ s.clone()
+ }
+}