From b1a8f83a0ca07a89f2461d93bea2a4b25236f3d8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 8 Dec 2023 18:46:36 +0100 Subject: [PATCH] fix: Smaller spans for unresolved field and method diagnostics --- Cargo.lock | 4 +- crates/hir-expand/src/files.rs | 15 +++++ .../src/handlers/unresolved_field.rs | 25 ++++++--- .../src/handlers/unresolved_method.rs | 55 +++++++++++++++++-- crates/ide-diagnostics/src/lib.rs | 15 ++++- crates/syntax/Cargo.toml | 2 +- crates/syntax/src/ptr.rs | 1 + 7 files changed, 99 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6c1e1e3c9..46efbdd93c 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 7e55f6be1e..89f0685d5b 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -314,6 +314,21 @@ impl InFile { } } + 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 0758706e45..3214594121 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 ae9f6744c4..464b0a710e 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 6cfd5f1832..6541bf6057 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( .unwrap_or(range), } } + +// FIXME Replace the one above with this one? +fn adjusted_display_range_new( + ctx: &DiagnosticsContext<'_>, + diag_ptr: InFile>, + adj: &dyn Fn(N) -> Option, +) -> 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 3b55921dc7..7a7c0d267f 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 07641b203b..8750147ee1 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs @@ -33,6 +33,7 @@ impl std::fmt::Debug for AstPtr { } } +impl Copy for AstPtr {} impl Clone for AstPtr { fn clone(&self) -> AstPtr { AstPtr { raw: self.raw.clone(), _ty: PhantomData }