Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/expand_macro.rs')
| -rw-r--r-- | crates/ide/src/expand_macro.rs | 132 |
1 files changed, 113 insertions, 19 deletions
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index a939ed214a..79fdf75b7f 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -1,9 +1,10 @@ +use hir::db::ExpandDatabase; use hir::{InFile, MacroFileIdExt, Semantics}; +use ide_db::base_db::CrateId; use ide_db::{ - helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, FileId, - RootDatabase, + helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase, }; -use span::Edition; +use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize}; use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; use crate::FilePosition; @@ -27,6 +28,7 @@ pub struct ExpandedMacro { pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); + let krate = sema.file_to_module_def(position.file_id)?.krate().into(); let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 1, @@ -61,8 +63,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .take_while(|it| it != &token) .filter(|it| it.kind() == T![,]) .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); + let expansion = expansions.get(idx)?.clone(); + let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?; + let expansion_span_map = db.expansion_span_map(expansion_file_id); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansion, + &expansion_span_map, + krate, + ); Some(ExpandedMacro { name, expansion }) }); @@ -71,6 +82,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< } let mut anc = tok.parent_ancestors(); + let mut span_map = SpanMap::empty(); let (name, expanded, kind) = loop { let node = anc.next()?; @@ -85,7 +97,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .unwrap_or(Edition::CURRENT), ) .to_string(), - expand_macro_recur(&sema, &item)?, + expand_macro_recur(&sema, &item, &mut span_map, TextSize::new(0))?, SyntaxKind::MACRO_ITEMS, ); } @@ -95,14 +107,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< name.push('!'); let syntax_kind = mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); - break (name, expand_macro_recur(&sema, &ast::Item::MacroCall(mac))?, syntax_kind); + break ( + name, + expand_macro_recur( + &sema, + &ast::Item::MacroCall(mac), + &mut span_map, + TextSize::new(0), + )?, + syntax_kind, + ); } }; // FIXME: // macro expansion may lose all white space information // But we hope someday we can use ra_fmt for that - let expansion = format(db, kind, position.file_id, expanded); + let expansion = format(db, kind, position.file_id, expanded, &span_map, krate); Some(ExpandedMacro { name, expansion }) } @@ -110,6 +131,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, macro_call: &ast::Item, + result_span_map: &mut SpanMap<SyntaxContextId>, + offset_in_original_node: TextSize, ) -> Option<SyntaxNode> { let expanded = match macro_call { item @ ast::Item::MacroCall(macro_call) => sema @@ -118,29 +141,60 @@ fn expand_macro_recur( .clone_for_update(), item => sema.expand_attr_macro(item)?.clone_for_update(), }; - expand(sema, expanded) + let file_id = + sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file"); + let expansion_span_map = sema.db.expansion_span_map(file_id); + result_span_map.merge( + TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), + expanded.text_range().len(), + &expansion_span_map, + ); + Some(expand(sema, expanded, result_span_map, u32::from(offset_in_original_node) as i32)) } -fn expand(sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode) -> Option<SyntaxNode> { +fn expand( + sema: &Semantics<'_, RootDatabase>, + expanded: SyntaxNode, + result_span_map: &mut SpanMap<SyntaxContextId>, + mut offset_in_original_node: i32, +) -> SyntaxNode { let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); for child in children { - if let Some(new_node) = expand_macro_recur(sema, &child) { + if let Some(new_node) = expand_macro_recur( + sema, + &child, + result_span_map, + TextSize::new( + (offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32)) + as u32, + ), + ) { + offset_in_original_node = offset_in_original_node + + (u32::from(new_node.text_range().len()) as i32) + - (u32::from(child.syntax().text_range().len()) as i32); // check if the whole original syntax is replaced if expanded == *child.syntax() { - return Some(new_node); + return new_node; } replacements.push((child, new_node)); } } replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - Some(expanded) + expanded } -fn format(db: &RootDatabase, kind: SyntaxKind, file_id: FileId, expanded: SyntaxNode) -> String { - let expansion = insert_ws_into(expanded).to_string(); +fn format( + db: &RootDatabase, + kind: SyntaxKind, + file_id: FileId, + expanded: SyntaxNode, + span_map: &SpanMap<SyntaxContextId>, + krate: CrateId, +) -> String { + let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string(); _format(db, kind, file_id, &expansion).unwrap_or(expansion) } @@ -498,7 +552,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -524,7 +578,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); } @@ -539,7 +593,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -550,7 +604,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -563,4 +617,44 @@ struct Foo {} }"#]], ); } + + #[test] + fn dollar_crate() { + check( + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + ( $i:ident ) => { $crate::Foo; $crate::Foo; $i::Foo; }; +} +//- /b.rs crate:b deps:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { a::m!($crate); $crate::Foo; $crate::Foo; }; +} +//- /c.rs crate:c deps:b,a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { b::m!(); $crate::Foo; $crate::Foo; }; +} +fn bar() { + m$0!(); +} +"#, + expect![[r#" +m! +a::Foo; +a::Foo; +b::Foo; +; +b::Foo; +b::Foo; +; +crate::Foo; +crate::Foo;"#]], + ); + } } |