about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--crates/hir-expand/src/files.rs15
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_field.rs25
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs55
-rw-r--r--crates/ide-diagnostics/src/lib.rs15
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ptr.rs1
7 files changed, 99 insertions, 18 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c6c1e1e3c96..46efbdd93c9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1484,9 +1484,9 @@ dependencies = [
 
 [[package]]
 name = "rowan"
-version = "0.15.14"
+version = "0.15.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9672ea408d491b517a4dc370159ec6dd7cb5c5fd2f41b02883830339109ac76"
+checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
 dependencies = [
  "countme",
  "hashbrown",
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 7e55f6be1e4..89f0685d5b6 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -314,6 +314,21 @@ impl InFile<TextRange> {
         }
     }
 
+    pub fn original_node_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
+        match self.file_id.repr() {
+            HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
+            HirFileIdRepr::MacroFile(mac_file) => {
+                match ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) {
+                    Some((it, SyntaxContextId::ROOT)) => it,
+                    _ => {
+                        let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
+                        loc.kind.original_call_range(db)
+                    }
+                }
+            }
+        }
+    }
+
     pub fn original_node_file_range_opt(
         self,
         db: &dyn db::ExpandDatabase,
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 0758706e45a..32145941218 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -8,7 +8,7 @@ use ide_db::{
 use syntax::{ast, AstNode, AstPtr};
 use text_edit::TextEdit;
 
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: unresolved-field
 //
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_field(
     } else {
         ""
     };
-    Diagnostic::new_with_syntax_node_ptr(
-        ctx,
+    Diagnostic::new(
         DiagnosticCode::RustcHardError("E0559"),
         format!(
             "no field `{}` on type `{}`{method_suffix}",
             d.name.display(ctx.sema.db),
             d.receiver.display(ctx.sema.db)
         ),
-        d.expr.clone().map(|it| it.into()),
+        adjusted_display_range_new(ctx, d.expr, &|expr| {
+            Some(
+                match expr {
+                    ast::Expr::MethodCallExpr(it) => it.name_ref(),
+                    ast::Expr::FieldExpr(it) => it.name_ref(),
+                    _ => None,
+                }?
+                .syntax()
+                .text_range(),
+            )
+        }),
     )
     .with_fixes(fixes(ctx, d))
     .experimental()
@@ -79,7 +88,7 @@ mod tests {
             r#"
 fn main() {
     ().foo;
- // ^^^^^^ error: no field `foo` on type `()`
+    // ^^^ error: no field `foo` on type `()`
 }
 "#,
         );
@@ -95,7 +104,7 @@ impl Foo {
 }
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
@@ -112,7 +121,7 @@ trait Bar {
 impl Bar for Foo {}
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
@@ -131,7 +140,7 @@ impl Bar for Foo {
 }
 fn foo() {
     Foo.bar;
- // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
+     // ^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index ae9f6744c40..464b0a710ea 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -8,7 +8,7 @@ use ide_db::{
 use syntax::{ast, AstNode, TextRange};
 use text_edit::TextEdit;
 
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
 
 // Diagnostic: unresolved-method
 //
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_method(
     } else {
         ""
     };
-    Diagnostic::new_with_syntax_node_ptr(
-        ctx,
+    Diagnostic::new(
         DiagnosticCode::RustcHardError("E0599"),
         format!(
             "no method `{}` on type `{}`{field_suffix}",
             d.name.display(ctx.sema.db),
             d.receiver.display(ctx.sema.db)
         ),
-        d.expr.clone().map(|it| it.into()),
+        adjusted_display_range_new(ctx, d.expr, &|expr| {
+            Some(
+                match expr {
+                    ast::Expr::MethodCallExpr(it) => it.name_ref(),
+                    ast::Expr::FieldExpr(it) => it.name_ref(),
+                    _ => None,
+                }?
+                .syntax()
+                .text_range(),
+            )
+        }),
     )
     .with_fixes(fixes(ctx, d))
     .experimental()
@@ -92,7 +101,41 @@ mod tests {
             r#"
 fn main() {
     ().foo();
- // ^^^^^^^^ error: no method `foo` on type `()`
+    // ^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn smoke_test_in_macro_def_site() {
+        check_diagnostics(
+            r#"
+macro_rules! m {
+    ($rcv:expr) => {
+        $rcv.foo()
+    }
+}
+fn main() {
+    m!(());
+ // ^^^^^^ error: no method `foo` on type `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn smoke_test_in_macro_call_site() {
+        check_diagnostics(
+            r#"
+macro_rules! m {
+    ($ident:ident) => {
+        ().$ident()
+    }
+}
+fn main() {
+    m!(foo);
+    // ^^^ error: no method `foo` on type `()`
 }
 "#,
         );
@@ -105,7 +148,7 @@ fn main() {
 struct Foo { bar: i32 }
 fn foo() {
     Foo { bar: i32 }.bar();
- // ^^^^^^^^^^^^^^^^^^^^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
+                  // ^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists
 }
 "#,
         );
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 6cfd5f18320..6541bf60579 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -90,7 +90,7 @@ use stdx::never;
 use syntax::{
     algo::find_node_at_range,
     ast::{self, AstNode},
-    SyntaxNode, SyntaxNodePtr, TextRange,
+    AstPtr, SyntaxNode, SyntaxNodePtr, TextRange,
 };
 
 // FIXME: Make this an enum
@@ -584,3 +584,16 @@ fn adjusted_display_range<N: AstNode>(
             .unwrap_or(range),
     }
 }
+
+// FIXME Replace the one above with this one?
+fn adjusted_display_range_new<N: AstNode>(
+    ctx: &DiagnosticsContext<'_>,
+    diag_ptr: InFile<AstPtr<N>>,
+    adj: &dyn Fn(N) -> Option<TextRange>,
+) -> FileRange {
+    let source_file = ctx.sema.parse_or_expand(diag_ptr.file_id);
+    let node = diag_ptr.value.to_node(&source_file);
+    diag_ptr
+        .with_value(adj(node).unwrap_or_else(|| diag_ptr.value.text_range()))
+        .original_node_file_range_rooted(ctx.sema.db)
+}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 3b55921dc75..7a7c0d267fe 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 cov-mark = "2.0.0-pre.1"
 either.workspace = true
 itertools.workspace = true
-rowan = "0.15.11"
+rowan = "0.15.15"
 rustc-hash = "1.1.0"
 once_cell = "1.17.0"
 indexmap.workspace = true
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index 07641b203bb..8750147ee11 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -33,6 +33,7 @@ impl<N: AstNode + std::fmt::Debug> std::fmt::Debug for AstPtr<N> {
     }
 }
 
+impl<N: AstNode> Copy for AstPtr<N> {}
 impl<N: AstNode> Clone for AstPtr<N> {
     fn clone(&self) -> AstPtr<N> {
         AstPtr { raw: self.raw.clone(), _ty: PhantomData }