X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=crates%2Fhir_expand%2Fsrc%2Fbuiltin_fn_macro.rs;h=bf6cc9989a6bfa6d873c2885bb8a8ba3f5d39e5a;hb=494fab202a7ec00f79636dcece8ca5e2742efff0;hp=57e66e5cc4c6aef6ac27a2cc450fc3597f3aa2bc;hpb=40159150aa89b11965fcbebb007d83d76d2f3db3;p=rust.git diff --git a/crates/hir_expand/src/builtin_fn_macro.rs b/crates/hir_expand/src/builtin_fn_macro.rs index 57e66e5cc4c..bf6cc9989a6 100644 --- a/crates/hir_expand/src/builtin_fn_macro.rs +++ b/crates/hir_expand/src/builtin_fn_macro.rs @@ -1,15 +1,16 @@ //! Builtin macro -use crate::{ - db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId, - MacroDefKind, -}; use base_db::{AnchoredPath, Edition, FileId}; use cfg::CfgExpr; use either::Either; -use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; +use mbe::{parse_exprs_with_sep, parse_to_token_tree}; use syntax::ast::{self, AstToken}; +use crate::{ + db::AstDatabase, name, quote, AstId, CrateId, ExpandError, ExpandResult, MacroCallId, + MacroCallLoc, MacroDefId, MacroDefKind, +}; + macro_rules! register_builtin { ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -42,7 +43,7 @@ pub fn expand( db: &dyn AstDatabase, arg_id: MacroCallId, tt: &tt::Subtree, - ) -> ExpandResult> { + ) -> ExpandResult { let expander = match *self { $( EagerExpander::$e_kind => $e_expand, )* }; @@ -60,7 +61,7 @@ fn find_by_name(ident: &name::Name) -> Option compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, + (concat_bytes, ConcatBytes) => concat_bytes_expand, (include, Include) => include_expand, (include_bytes, IncludeBytes) => include_bytes_expand, (include_str, IncludeStr) => include_str_expand, @@ -257,7 +259,7 @@ fn format_args_expand( let mut args = parse_exprs_with_sep(tt, ','); if args.is_empty() { - return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule); + return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule.into()); } for arg in &mut args { // Remove `key =`. @@ -358,32 +360,38 @@ fn unquote_str(lit: &tt::Literal) -> Option { token.value().map(|it| it.into_owned()) } +fn unquote_byte_string(lit: &tt::Literal) -> Option> { + let lit = ast::make::tokens::literal(&lit.to_string()); + let token = ast::ByteString::cast(lit)?; + token.value().map(|it| it.into_owned()) +} + fn compile_error_expand( _db: &dyn AstDatabase, _id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => { let text = it.text.as_str(); if text.starts_with('"') && text.ends_with('"') { // FIXME: does not handle raw strings - mbe::ExpandError::Other(text[1..text.len() - 1].to_string()) + ExpandError::Other(text[1..text.len() - 1].into()) } else { - mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()) + ExpandError::Other("`compile_error!` argument must be a string".into()) } } - _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()), + _ => ExpandError::Other("`compile_error!` argument must be a string".into()), }; - ExpandResult { value: Some(ExpandedEager::new(quote! {})), err: Some(err) } + ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) } } fn concat_expand( _db: &dyn AstDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let mut err = None; let mut text = String::new(); for (i, mut t) in tt.token_trees.iter().enumerate() { @@ -414,18 +422,86 @@ fn concat_expand( } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { - err.get_or_insert(mbe::ExpandError::UnexpectedToken); + err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); + } + } + } + ExpandResult { value: ExpandedEager::new(quote!(#text)), err } +} + +fn concat_bytes_expand( + _db: &dyn AstDatabase, + _arg_id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { + let mut bytes = Vec::new(); + let mut err = None; + 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()); + match token.kind() { + syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()), + syntax::SyntaxKind::BYTE_STRING => { + let components = unquote_byte_string(lit).unwrap_or_else(|| Vec::new()); + components.into_iter().for_each(|x| bytes.push(x.to_string())); + } + _ => { + err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); + break; + } + } + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), + tt::TokenTree::Subtree(tree) + if tree.delimiter_kind() == Some(tt::DelimiterKind::Bracket) => + { + if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) { + err.get_or_insert(e); + break; + } + } + _ => { + err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); + break; + } + } + } + let ident = tt::Ident { text: bytes.join(", ").into(), id: tt::TokenId::unspecified() }; + ExpandResult { value: ExpandedEager::new(quote!([#ident])), err } +} + +fn concat_bytes_expand_subtree( + tree: &tt::Subtree, + bytes: &mut Vec, +) -> Result<(), ExpandError> { + for (ti, tt) in tree.token_trees.iter().enumerate() { + match tt { + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + let lit = ast::make::tokens::literal(&lit.to_string()); + match lit.kind() { + syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => { + bytes.push(lit.text().to_string()) + } + _ => { + return Err(mbe::ExpandError::UnexpectedToken.into()); + } + } + } + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if ti % 2 == 1 && punct.char == ',' => (), + _ => { + return Err(mbe::ExpandError::UnexpectedToken.into()); } } } - ExpandResult { value: Some(ExpandedEager::new(quote!(#text))), err } + Ok(()) } fn concat_idents_expand( _db: &dyn AstDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let mut err = None; let mut ident = String::new(); for (i, t) in tt.token_trees.iter().enumerate() { @@ -435,12 +511,12 @@ fn concat_idents_expand( } tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), _ => { - err.get_or_insert(mbe::ExpandError::UnexpectedToken); + err.get_or_insert(mbe::ExpandError::UnexpectedToken.into()); } } } let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() }; - ExpandResult { value: Some(ExpandedEager::new(quote!(#ident))), err } + ExpandResult { value: ExpandedEager::new(quote!(#ident)), err } } fn relative_file( @@ -448,35 +524,35 @@ fn relative_file( call_id: MacroCallId, path_str: &str, allow_recursion: bool, -) -> Result { +) -> Result { let call_site = call_id.as_file().original_file(db); let path = AnchoredPath { anchor: call_site, path: path_str }; let res = db .resolve_path(path) - .ok_or_else(|| mbe::ExpandError::Other(format!("failed to load file `{}`", path_str)))?; + .ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?; // Prevent include itself if res == call_site && !allow_recursion { - Err(mbe::ExpandError::Other(format!("recursive inclusion of `{}`", path_str))) + Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into())) } else { Ok(res) } } -fn parse_string(tt: &tt::Subtree) -> Result { +fn parse_string(tt: &tt::Subtree) -> Result { tt.token_trees .get(0) .and_then(|tt| match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it), _ => None, }) - .ok_or(mbe::ExpandError::ConversionError) + .ok_or(mbe::ExpandError::ConversionError.into()) } fn include_expand( db: &dyn AstDatabase, arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let res = (|| { let path = parse_string(tt)?; let file_id = relative_file(db, arg_id, &path, false)?; @@ -488,7 +564,7 @@ fn include_expand( match res { Ok((subtree, file_id)) => { - ExpandResult::ok(Some(ExpandedEager { subtree, included_file: Some(file_id) })) + ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) }) } Err(e) => ExpandResult::only_err(e), } @@ -498,7 +574,7 @@ fn include_bytes_expand( _db: &dyn AstDatabase, _arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { if let Err(e) = parse_string(tt) { return ExpandResult::only_err(e); } @@ -511,14 +587,14 @@ fn include_bytes_expand( id: tt::TokenId::unspecified(), }))], }; - ExpandResult::ok(Some(ExpandedEager::new(res))) + ExpandResult::ok(ExpandedEager::new(res)) } fn include_str_expand( db: &dyn AstDatabase, arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let path = match parse_string(tt) { Ok(it) => it, Err(e) => return ExpandResult::only_err(e), @@ -531,14 +607,14 @@ fn include_str_expand( let file_id = match relative_file(db, arg_id, &path, true) { Ok(file_id) => file_id, Err(_) => { - return ExpandResult::ok(Some(ExpandedEager::new(quote!("")))); + return ExpandResult::ok(ExpandedEager::new(quote!(""))); } }; let text = db.file_text(file_id); let text = &*text; - ExpandResult::ok(Some(ExpandedEager::new(quote!(#text)))) + ExpandResult::ok(ExpandedEager::new(quote!(#text))) } fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option { @@ -550,7 +626,7 @@ fn env_expand( db: &dyn AstDatabase, arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let key = match parse_string(tt) { Ok(it) => it, Err(e) => return ExpandResult::only_err(e), @@ -561,7 +637,7 @@ fn env_expand( // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. if key == "OUT_DIR" { - err = Some(mbe::ExpandError::Other( + err = Some(ExpandError::Other( r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(), )); } @@ -574,14 +650,14 @@ fn env_expand( }); let expanded = quote! { #s }; - ExpandResult { value: Some(ExpandedEager::new(expanded)), err } + ExpandResult { value: ExpandedEager::new(expanded), err } } fn option_env_expand( db: &dyn AstDatabase, arg_id: MacroCallId, tt: &tt::Subtree, -) -> ExpandResult> { +) -> ExpandResult { let key = match parse_string(tt) { Ok(it) => it, Err(e) => return ExpandResult::only_err(e), @@ -592,5 +668,5 @@ fn option_env_expand( Some(s) => quote! { std::option::Some(#s) }, }; - ExpandResult::ok(Some(ExpandedEager::new(expanded))) + ExpandResult::ok(ExpandedEager::new(expanded)) }