3513: Completion in macros r=matklad a=flodiebold

I experimented a bit with completion in macros. It's kind of working, but there are a lot of rough edges.

 - I'm trying to expand the macro call with the inserted fake token. This requires some hacky additions on the HIR level to be able to do "hypothetical" expansions. There should probably be a nicer API for this, if we want to do it this way. I'm not sure whether it's worth it, because we still can't do a lot if the original macro call didn't expand in nearly the same way. E.g. if we have something like `println!("", x<|>)` the expansions will look the same and everything is fine; but in that case we could maybe have achieved the same result in a simpler way. If we have something like `m!(<|>)` where `m!()` doesn't even expand or expands to something very different, we don't really know what to do anyway.
 - Relatedly, there are a lot of cases where this doesn't work because either the original call or the hypothetical call doesn't expand. E.g. if we have `m!(x.<|>)` the original token tree doesn't parse as an expression; if we have `m!(match x { <|> })` the hypothetical token tree doesn't parse. It would be nice if we could have better error recovery in these cases.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2020-03-09 08:56:58 +00:00 committed by GitHub
commit beb4f49541
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 529 additions and 43 deletions

View file

@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors};
use hir_def::{
resolver::{self, HasResolver, Resolver},
TraitId,
AsMacroCall, TraitId,
};
use hir_expand::ExpansionInfo;
use ra_db::{FileId, FileRange};
@ -70,6 +70,20 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
Some(node)
}
pub fn expand_hypothetical(
&self,
actual_macro_call: &ast::MacroCall,
hypothetical_args: &ast::TokenTree,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
let macro_call =
self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
let macro_call_id = macro_call
.as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map)
}
pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
let parent = token.parent();
let parent = self.find_file(parent);
@ -104,6 +118,25 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
node.ancestors_with_macros(self.db).map(|it| it.value)
}
pub fn ancestors_at_offset_with_macros(
&self,
node: &SyntaxNode,
offset: TextUnit,
) -> impl Iterator<Item = SyntaxNode> + '_ {
use itertools::Itertools;
node.token_at_offset(offset)
.map(|token| self.ancestors_with_macros(token.parent()))
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
}
pub fn find_node_at_offset_with_macros<N: AstNode>(
&self,
node: &SyntaxNode,
offset: TextUnit,
) -> Option<N> {
self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
}
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
self.analyze(expr.syntax()).type_of(self.db, &expr)
}