about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/source_analyzer.rs30
-rw-r--r--crates/ide-completion/src/context.rs6
-rw-r--r--crates/ide-completion/src/tests/special.rs28
3 files changed, 45 insertions, 19 deletions
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index d624d375774..9bdde5e6e0f 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<TextSize>,
+    from_file: HirFileId,
+    offset: TextSize,
 ) -> Option<ScopeId> {
     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<TextSize>,
+    from_file: HirFileId,
+    offset: TextSize,
 ) -> Option<ScopeId> {
     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 01a2f96fd12..b371c978808 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 f1557107e0d..39cb41485b1 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
+        "#]],
+    )
+}