diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index b50eb347c8..631358f41a 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -99,6 +99,7 @@ register_builtin! { EAGER: (concat, Concat) => concat_expand, (include, Include) => include_expand, + (include_str, IncludeStr) => include_str_expand, (env, Env) => env_expand, (option_env, OptionEnv) => option_env_expand } @@ -292,11 +293,16 @@ fn concat_expand( Ok((quote!(#text), FragmentKind::Expr)) } -fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option { +fn relative_file( + db: &dyn AstDatabase, + call_id: MacroCallId, + path: &str, + allow_recursion: bool, +) -> Option { let call_site = call_id.as_file().original_file(db); let res = db.resolve_path(call_site, path)?; // Prevent include itself - if res == call_site { + if res == call_site && !allow_recursion { None } else { Some(res) @@ -319,8 +325,8 @@ fn include_expand( tt: &tt::Subtree, ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { let path = parse_string(tt)?; - let file_id = - relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?; + let file_id = relative_file(db, arg_id.into(), &path, false) + .ok_or_else(|| mbe::ExpandError::ConversionError)?; // FIXME: // Handle include as expression @@ -331,6 +337,30 @@ fn include_expand( Ok((res, FragmentKind::Items)) } +fn include_str_expand( + db: &dyn AstDatabase, + arg_id: EagerMacroId, + tt: &tt::Subtree, +) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { + let path = parse_string(tt)?; + + // FIXME: we're not able to read excluded files (which is most of them because + // it's unusual to `include_str!` a Rust file), but we can return an empty string. + // Ideally, we'd be able to offer a precise expansion if the user asks for macro + // expansion. + let file_id = match relative_file(db, arg_id.into(), &path, true) { + Some(file_id) => file_id, + None => { + return Ok((quote!(""), FragmentKind::Expr)); + } + }; + + let text = db.file_text(file_id); + let text = &*text; + + Ok((quote!(#text), FragmentKind::Expr)) +} + fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option { let krate = db.lookup_intern_eager_expansion(arg_id).krate; db.crate_graph()[krate].env.get(key) diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 660bdfe336..b475c8cc72 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -191,6 +191,7 @@ pub mod known { stringify, concat, include, + include_str, format_args, format_args_nl, env, diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 58839b14aa..cc079790ee 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -494,6 +494,7 @@ fn main() { } //- /src/main.rs #[rustc_builtin_macro] macro_rules! include {} +#[rustc_builtin_macro] macro_rules! include_str {} #[rustc_builtin_macro] macro_rules! concat {} #[rustc_builtin_macro] macro_rules! env {} @@ -512,6 +513,7 @@ fn main() { let va = A; let vb = B; let should_be_str = message(); + let another_str = include_str!("main.rs"); } "###, ) @@ -523,36 +525,19 @@ fn main() { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(18, 10), + Position::new(19, 10), + ), + work_done_progress_params: Default::default(), + }); + assert!(res.to_string().contains("&str")); + let res = server.send_request::(HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(20, 10), ), work_done_progress_params: Default::default(), }); assert!(res.to_string().contains("&str")); - server.request::( - GotoDefinitionParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/main.rs"), - Position::new(16, 9), - ), - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }, - json!([{ - "originSelectionRange": { - "end": { "character": 10, "line": 16 }, - "start": { "character": 8, "line": 16 } - }, - "targetRange": { - "end": { "character": 9, "line": 7 }, - "start": { "character": 0, "line": 6 } - }, - "targetSelectionRange": { - "end": { "character": 8, "line": 7 }, - "start": { "character": 7, "line": 7 } - }, - "targetUri": "file:///[..]src/main.rs" - }]), - ); server.request::( GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( @@ -568,12 +553,37 @@ fn main() { "start": { "character": 8, "line": 17 } }, "targetRange": { - "end": { "character": 9, "line": 11 }, - "start": { "character": 0, "line":10 } + "end": { "character": 9, "line": 8 }, + "start": { "character": 0, "line": 7 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 11 }, - "start": { "character": 7, "line": 11 } + "end": { "character": 8, "line": 8 }, + "start": { "character": 7, "line": 8 } + }, + "targetUri": "file:///[..]src/main.rs" + }]), + ); + server.request::( + GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(18, 9), + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }, + json!([{ + "originSelectionRange": { + "end": { "character": 10, "line": 18 }, + "start": { "character": 8, "line": 18 } + }, + "targetRange": { + "end": { "character": 9, "line": 12 }, + "start": { "character": 0, "line":11 } + }, + "targetSelectionRange": { + "end": { "character": 8, "line": 12 }, + "start": { "character": 7, "line": 12 } }, "targetUri": "file:///[..]src/main.rs" }]),