diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d624d37577..9bdde5e6e0 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -67,10 +67,7 @@ impl SourceAnalyzer { let scopes = db.expr_scopes(def); let scope = match offset { None => scope_for(&scopes, &source_map, node), - Some(offset) => { - let file_id = node.file_id.original_file(db.upcast()); - scope_for_offset(db, &scopes, &source_map, InFile::new(file_id.into(), offset)) - } + Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset), }; let resolver = resolver_for_scope(db.upcast(), def, scope); SourceAnalyzer { @@ -91,10 +88,7 @@ impl SourceAnalyzer { let scopes = db.expr_scopes(def); let scope = match offset { None => scope_for(&scopes, &source_map, node), - Some(offset) => { - let file_id = node.file_id.original_file(db.upcast()); - scope_for_offset(db, &scopes, &source_map, InFile::new(file_id.into(), offset)) - } + Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset), }; let resolver = resolver_for_scope(db.upcast(), def, scope); SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer: None, file_id } @@ -585,14 +579,15 @@ fn scope_for_offset( db: &dyn HirDatabase, scopes: &ExprScopes, source_map: &BodySourceMap, - offset: InFile, + from_file: HirFileId, + offset: TextSize, ) -> Option { scopes .scope_by_expr() .iter() .filter_map(|(id, scope)| { let InFile { file_id, value } = source_map.expr_syntax(*id).ok()?; - if offset.file_id == file_id { + if from_file == file_id { let root = db.parse_or_expand(file_id)?; let node = value.to_node(&root); return Some((node.syntax().text_range(), scope)); @@ -602,17 +597,15 @@ fn scope_for_offset( let source = iter::successors(file_id.call_node(db.upcast()), |it| { it.file_id.call_node(db.upcast()) }) - .find(|it| it.file_id == offset.file_id) + .find(|it| it.file_id == from_file) .filter(|it| it.value.kind() == SyntaxKind::MACRO_CALL)?; Some((source.value.text_range(), scope)) }) - .filter(|(expr_range, _scope)| { - expr_range.start() <= offset.value && offset.value <= expr_range.end() - }) + .filter(|(expr_range, _scope)| expr_range.start() <= offset && offset <= expr_range.end()) // find containing scope .min_by_key(|(expr_range, _scope)| expr_range.len()) .map(|(expr_range, scope)| { - adjust(db, scopes, source_map, expr_range, offset).unwrap_or(*scope) + adjust(db, scopes, source_map, expr_range, from_file, offset).unwrap_or(*scope) }) } @@ -623,7 +616,8 @@ fn adjust( scopes: &ExprScopes, source_map: &BodySourceMap, expr_range: TextRange, - offset: InFile, + from_file: HirFileId, + offset: TextSize, ) -> Option { let child_scopes = scopes .scope_by_expr() @@ -631,7 +625,7 @@ fn adjust( .filter_map(|(id, scope)| { let source = source_map.expr_syntax(*id).ok()?; // FIXME: correctly handle macro expansion - if source.file_id != offset.file_id { + if source.file_id != from_file { return None; } let root = source.file_syntax(db.upcast()); @@ -639,7 +633,7 @@ fn adjust( Some((node.syntax().text_range(), scope)) }) .filter(|&(range, _)| { - range.start() <= offset.value && expr_range.contains_range(range) && range != expr_range + range.start() <= offset && expr_range.contains_range(range) && range != expr_range }); child_scopes diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 01a2f96fd1..b371c97880 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -506,7 +506,11 @@ impl<'a> CompletionContext<'a> { let original_token = original_file.syntax().token_at_offset(offset).left_biased()?; let token = sema.descend_into_macros_single(original_token.clone()); - let scope = sema.scope_at_offset(&token.parent()?, offset)?; + + // adjust for macro input, this still fails if there is no token written yet + let scope_offset = if original_token == token { offset } else { token.text_range().end() }; + let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?; + let krate = scope.krate(); let module = scope.module(); diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index f1557107e0..39cb41485b 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -782,3 +782,31 @@ fn main() { "#]], ) } + +#[test] +fn completes_locals_from_macros() { + check( + r#" + +macro_rules! x { + ($x:ident, $expr:expr) => { + let $x = 0; + $expr + }; +} +fn main() { + x! { + foobar, { + f$0 + } + }; +} +"#, + expect![[r#" + fn main() fn() + lc foobar i32 + ma x!(…) macro_rules! x + bt u32 + "#]], + ) +}