Skip to content

Commit 24e9812

Browse files
committed
Try to complete within macros
1 parent aff82cf commit 24e9812

File tree

9 files changed

+339
-38
lines changed

9 files changed

+339
-38
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_hir/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ log = "0.4.8"
1212
rustc-hash = "1.1.0"
1313
either = "1.5.3"
1414

15+
itertools = "0.8.2"
16+
1517
ra_syntax = { path = "../ra_syntax" }
1618
ra_db = { path = "../ra_db" }
1719
ra_prof = { path = "../ra_prof" }

crates/ra_hir/src/semantics.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ use std::{cell::RefCell, fmt, iter::successors};
66

77
use hir_def::{
88
resolver::{self, HasResolver, Resolver},
9-
TraitId,
9+
AsMacroCall, TraitId,
1010
};
1111
use hir_expand::ExpansionInfo;
1212
use ra_db::{FileId, FileRange};
1313
use ra_prof::profile;
1414
use ra_syntax::{
15-
algo::skip_trivia_token, ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextUnit,
15+
algo::{self, skip_trivia_token},
16+
ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextUnit,
1617
};
1718
use rustc_hash::{FxHashMap, FxHashSet};
1819

@@ -70,6 +71,37 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
7071
Some(node)
7172
}
7273

74+
pub fn expand_hypothetical(
75+
&self,
76+
actual_macro_call: &ast::MacroCall,
77+
hypothetical_call: &ast::MacroCall,
78+
token_to_map: SyntaxToken,
79+
) -> Option<(SyntaxNode, SyntaxToken)> {
80+
let macro_call =
81+
self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
82+
let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
83+
let macro_call_id = macro_call
84+
.as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
85+
let macro_file = macro_call_id.as_file().macro_file().unwrap();
86+
let (tt, tmap_1) =
87+
hir_expand::syntax_node_to_token_tree(hypothetical_call.token_tree().unwrap().syntax())
88+
.unwrap();
89+
let range = token_to_map
90+
.text_range()
91+
.checked_sub(hypothetical_call.token_tree().unwrap().syntax().text_range().start())?;
92+
let token_id = tmap_1.token_by_range(range)?;
93+
let macro_def = hir_expand::db::expander(self.db, macro_call_id)?;
94+
let (node, tmap_2) = hir_expand::db::parse_macro_with_arg(
95+
self.db,
96+
macro_file,
97+
Some(std::sync::Arc::new((tt, tmap_1))),
98+
)?;
99+
let token_id = macro_def.0.map_id_down(token_id);
100+
let range = tmap_2.range_by_token(token_id)?.by_kind(token_to_map.kind())?;
101+
let token = algo::find_covering_element(&node.syntax_node(), range).into_token()?;
102+
Some((node.syntax_node(), token))
103+
}
104+
73105
pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
74106
let parent = token.parent();
75107
let parent = self.find_file(parent);
@@ -104,6 +136,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
104136
node.ancestors_with_macros(self.db).map(|it| it.value)
105137
}
106138

139+
pub fn ancestors_at_offset_with_macros(
140+
&self,
141+
node: &SyntaxNode,
142+
offset: TextUnit,
143+
) -> impl Iterator<Item = SyntaxNode> + '_ {
144+
use itertools::Itertools;
145+
node.token_at_offset(offset)
146+
.map(|token| self.ancestors_with_macros(token.parent()))
147+
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
148+
}
149+
150+
pub fn find_node_at_offset_with_macros<N: AstNode>(
151+
&self,
152+
node: &SyntaxNode,
153+
offset: TextUnit,
154+
) -> Option<N> {
155+
self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
156+
}
157+
107158
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
108159
self.analyze(expr.syntax()).type_of(self.db, &expr)
109160
}

crates/ra_hir_expand/src/db.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,43 @@ pub(crate) fn macro_arg(
129129
pub(crate) fn macro_expand(
130130
db: &dyn AstDatabase,
131131
id: MacroCallId,
132+
) -> Result<Arc<tt::Subtree>, String> {
133+
macro_expand_with_arg(db, id, None)
134+
}
135+
136+
// TODO hack
137+
pub fn expander(
138+
db: &dyn AstDatabase,
139+
id: MacroCallId,
140+
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
141+
let lazy_id = match id {
142+
MacroCallId::LazyMacro(id) => id,
143+
MacroCallId::EagerMacro(_id) => {
144+
// TODO
145+
unimplemented!()
146+
}
147+
};
148+
149+
let loc = db.lookup_intern_macro(lazy_id);
150+
let macro_rules = db.macro_def(loc.def)?;
151+
Some(macro_rules)
152+
}
153+
154+
pub(crate) fn macro_expand_with_arg(
155+
db: &dyn AstDatabase,
156+
id: MacroCallId,
157+
arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
132158
) -> Result<Arc<tt::Subtree>, String> {
133159
let lazy_id = match id {
134160
MacroCallId::LazyMacro(id) => id,
135161
MacroCallId::EagerMacro(id) => {
162+
// TODO
136163
return Ok(db.lookup_intern_eager_expansion(id).subtree);
137164
}
138165
};
139166

140167
let loc = db.lookup_intern_macro(lazy_id);
141-
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
168+
let macro_arg = arg.or_else(|| db.macro_arg(id)).ok_or("Fail to args in to tt::TokenTree")?;
142169

143170
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
144171
let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
@@ -162,12 +189,24 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
162189
pub(crate) fn parse_macro(
163190
db: &dyn AstDatabase,
164191
macro_file: MacroFile,
192+
) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
193+
parse_macro_with_arg(db, macro_file, None)
194+
}
195+
196+
pub fn parse_macro_with_arg(
197+
db: &dyn AstDatabase,
198+
macro_file: MacroFile,
199+
arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
165200
) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
166201
let _p = profile("parse_macro_query");
167202

168203
let macro_call_id = macro_file.macro_call_id;
169-
let tt = db
170-
.macro_expand(macro_call_id)
204+
let expansion = if let Some(arg) = arg {
205+
macro_expand_with_arg(db, macro_call_id, Some(arg))
206+
} else {
207+
db.macro_expand(macro_call_id)
208+
};
209+
let tt = expansion
171210
.map_err(|err| {
172211
// Note:
173212
// The final goal we would like to make all parse_macro success,
@@ -185,15 +224,13 @@ pub(crate) fn parse_macro(
185224
.collect::<Vec<_>>()
186225
.join("\n");
187226

188-
log::warn!(
227+
eprintln!(
189228
"fail on macro_parse: (reason: {} macro_call: {:#}) parents: {}",
190-
err,
191-
node.value,
192-
parents
229+
err, node.value, parents
193230
);
194231
}
195232
_ => {
196-
log::warn!("fail on macro_parse: (reason: {})", err);
233+
eprintln!("fail on macro_parse: (reason: {})", err);
197234
}
198235
}
199236
})

crates/ra_hir_expand/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ impl HirFileId {
157157
}
158158
}
159159
}
160+
161+
pub fn macro_file(self) -> Option<MacroFile> {
162+
match self.0 {
163+
HirFileIdRepr::FileId(_) => None,
164+
HirFileIdRepr::MacroFile(m) => Some(m),
165+
}
166+
}
160167
}
161168

162169
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -296,7 +303,7 @@ pub struct ExpansionInfo {
296303
exp_map: Arc<mbe::TokenMap>,
297304
}
298305

299-
pub use mbe::Origin;
306+
pub use mbe::{syntax_node_to_token_tree, Origin};
300307
use ra_parser::FragmentKind;
301308

302309
impl ExpansionInfo {

crates/ra_ide/src/completion/complete_dot.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,4 +584,102 @@ mod tests {
584584
"###
585585
);
586586
}
587+
588+
#[test]
589+
fn works_in_simple_macro_1() {
590+
assert_debug_snapshot!(
591+
do_ref_completion(
592+
r"
593+
macro_rules! m { ($e:expr) => { $e } }
594+
struct A { the_field: u32 }
595+
fn foo(a: A) {
596+
m!(a.x<|>)
597+
}
598+
",
599+
),
600+
@r###"
601+
[
602+
CompletionItem {
603+
label: "the_field",
604+
source_range: [156; 157),
605+
delete: [156; 157),
606+
insert: "the_field",
607+
kind: Field,
608+
detail: "u32",
609+
},
610+
]
611+
"###
612+
);
613+
}
614+
615+
#[test]
616+
fn works_in_simple_macro_recursive() {
617+
assert_debug_snapshot!(
618+
do_ref_completion(
619+
r"
620+
macro_rules! m { ($e:expr) => { $e } }
621+
struct A { the_field: u32 }
622+
fn foo(a: A) {
623+
m!(a.x<|>)
624+
}
625+
",
626+
),
627+
@r###"
628+
[
629+
CompletionItem {
630+
label: "the_field",
631+
source_range: [156; 157),
632+
delete: [156; 157),
633+
insert: "the_field",
634+
kind: Field,
635+
detail: "u32",
636+
},
637+
]
638+
"###
639+
);
640+
}
641+
642+
#[test]
643+
fn works_in_simple_macro_2() {
644+
// this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
645+
assert_debug_snapshot!(
646+
do_ref_completion(
647+
r"
648+
macro_rules! m { ($e:expr) => { $e } }
649+
struct A { the_field: u32 }
650+
fn foo(a: A) {
651+
m!(a.<|>)
652+
}
653+
",
654+
),
655+
@r###"[]"###
656+
);
657+
}
658+
659+
#[test]
660+
fn works_in_simple_macro_recursive_1() {
661+
assert_debug_snapshot!(
662+
do_ref_completion(
663+
r"
664+
macro_rules! m { ($e:expr) => { $e } }
665+
struct A { the_field: u32 }
666+
fn foo(a: A) {
667+
m!(m!(m!(a.x<|>)))
668+
}
669+
",
670+
),
671+
@r###"
672+
[
673+
CompletionItem {
674+
label: "the_field",
675+
source_range: [162; 163),
676+
delete: [162; 163),
677+
insert: "the_field",
678+
kind: Field,
679+
detail: "u32",
680+
},
681+
]
682+
"###
683+
);
684+
}
587685
}

crates/ra_ide/src/completion/complete_path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Completion of paths, including when writing a single name.
1+
//! Completion of paths, i.e. `some::prefix::<|>`.
22
33
use hir::{Adt, PathResolution, ScopeDef};
44
use ra_syntax::AstNode;

crates/ra_ide/src/completion/complete_scope.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! FIXME: write short doc here
1+
//! Completion of names from the current scope, e.g. locals and imported items.
22
33
use crate::completion::{CompletionContext, Completions};
44

@@ -797,4 +797,72 @@ mod tests {
797797
"###
798798
)
799799
}
800+
801+
#[test]
802+
fn completes_in_simple_macro_1() {
803+
assert_debug_snapshot!(
804+
do_reference_completion(
805+
r"
806+
macro_rules! m { ($e:expr) => { $e } }
807+
fn quux(x: i32) {
808+
let y = 92;
809+
m!(<|>);
810+
}
811+
"
812+
),
813+
@"[]"
814+
);
815+
}
816+
817+
#[test]
818+
fn completes_in_simple_macro_2() {
819+
assert_debug_snapshot!(
820+
do_reference_completion(
821+
r"
822+
macro_rules! m { ($e:expr) => { $e } }
823+
fn quux(x: i32) {
824+
let y = 92;
825+
m!(x<|>);
826+
}
827+
"
828+
),
829+
@r###"
830+
[
831+
CompletionItem {
832+
label: "m!",
833+
source_range: [145; 146),
834+
delete: [145; 146),
835+
insert: "m!($0)",
836+
kind: Macro,
837+
detail: "macro_rules! m",
838+
},
839+
CompletionItem {
840+
label: "quux(…)",
841+
source_range: [145; 146),
842+
delete: [145; 146),
843+
insert: "quux(${1:x})$0",
844+
kind: Function,
845+
lookup: "quux",
846+
detail: "fn quux(x: i32)",
847+
},
848+
CompletionItem {
849+
label: "x",
850+
source_range: [145; 146),
851+
delete: [145; 146),
852+
insert: "x",
853+
kind: Binding,
854+
detail: "i32",
855+
},
856+
CompletionItem {
857+
label: "y",
858+
source_range: [145; 146),
859+
delete: [145; 146),
860+
insert: "y",
861+
kind: Binding,
862+
detail: "i32",
863+
},
864+
]
865+
"###
866+
);
867+
}
800868
}

0 commit comments

Comments
 (0)