mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
fix: Smaller spans for unresolved field and method diagnostics
This commit is contained in:
parent
9e82ab54e8
commit
b1a8f83a0c
7 changed files with 99 additions and 18 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1484,9 +1484,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rowan"
|
name = "rowan"
|
||||||
version = "0.15.14"
|
version = "0.15.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9672ea408d491b517a4dc370159ec6dd7cb5c5fd2f41b02883830339109ac76"
|
checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"countme",
|
"countme",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
|
|
@ -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(
|
pub fn original_node_file_range_opt(
|
||||||
self,
|
self,
|
||||||
db: &dyn db::ExpandDatabase,
|
db: &dyn db::ExpandDatabase,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ide_db::{
|
||||||
use syntax::{ast, AstNode, AstPtr};
|
use syntax::{ast, AstNode, AstPtr};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||||
|
|
||||||
// Diagnostic: unresolved-field
|
// Diagnostic: unresolved-field
|
||||||
//
|
//
|
||||||
|
@ -22,15 +22,24 @@ pub(crate) fn unresolved_field(
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
Diagnostic::new_with_syntax_node_ptr(
|
Diagnostic::new(
|
||||||
ctx,
|
|
||||||
DiagnosticCode::RustcHardError("E0559"),
|
DiagnosticCode::RustcHardError("E0559"),
|
||||||
format!(
|
format!(
|
||||||
"no field `{}` on type `{}`{method_suffix}",
|
"no field `{}` on type `{}`{method_suffix}",
|
||||||
d.name.display(ctx.sema.db),
|
d.name.display(ctx.sema.db),
|
||||||
d.receiver.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))
|
.with_fixes(fixes(ctx, d))
|
||||||
.experimental()
|
.experimental()
|
||||||
|
@ -79,7 +88,7 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
().foo;
|
().foo;
|
||||||
// ^^^^^^ error: no field `foo` on type `()`
|
// ^^^ error: no field `foo` on type `()`
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -95,7 +104,7 @@ impl Foo {
|
||||||
}
|
}
|
||||||
fn foo() {
|
fn foo() {
|
||||||
Foo.bar;
|
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 {}
|
impl Bar for Foo {}
|
||||||
fn foo() {
|
fn foo() {
|
||||||
Foo.bar;
|
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() {
|
fn foo() {
|
||||||
Foo.bar;
|
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
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ide_db::{
|
||||||
use syntax::{ast, AstNode, TextRange};
|
use syntax::{ast, AstNode, TextRange};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||||
|
|
||||||
// Diagnostic: unresolved-method
|
// Diagnostic: unresolved-method
|
||||||
//
|
//
|
||||||
|
@ -22,15 +22,24 @@ pub(crate) fn unresolved_method(
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
Diagnostic::new_with_syntax_node_ptr(
|
Diagnostic::new(
|
||||||
ctx,
|
|
||||||
DiagnosticCode::RustcHardError("E0599"),
|
DiagnosticCode::RustcHardError("E0599"),
|
||||||
format!(
|
format!(
|
||||||
"no method `{}` on type `{}`{field_suffix}",
|
"no method `{}` on type `{}`{field_suffix}",
|
||||||
d.name.display(ctx.sema.db),
|
d.name.display(ctx.sema.db),
|
||||||
d.receiver.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))
|
.with_fixes(fixes(ctx, d))
|
||||||
.experimental()
|
.experimental()
|
||||||
|
@ -92,7 +101,41 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
().foo();
|
().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 }
|
struct Foo { bar: i32 }
|
||||||
fn foo() {
|
fn foo() {
|
||||||
Foo { bar: i32 }.bar();
|
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
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -90,7 +90,7 @@ use stdx::never;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::find_node_at_range,
|
algo::find_node_at_range,
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SyntaxNode, SyntaxNodePtr, TextRange,
|
AstPtr, SyntaxNode, SyntaxNodePtr, TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: Make this an enum
|
// FIXME: Make this an enum
|
||||||
|
@ -584,3 +584,16 @@ fn adjusted_display_range<N: AstNode>(
|
||||||
.unwrap_or(range),
|
.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)
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ doctest = false
|
||||||
cov-mark = "2.0.0-pre.1"
|
cov-mark = "2.0.0-pre.1"
|
||||||
either.workspace = true
|
either.workspace = true
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
rowan = "0.15.11"
|
rowan = "0.15.15"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
once_cell = "1.17.0"
|
once_cell = "1.17.0"
|
||||||
indexmap.workspace = true
|
indexmap.workspace = true
|
||||||
|
|
|
@ -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> {
|
impl<N: AstNode> Clone for AstPtr<N> {
|
||||||
fn clone(&self) -> AstPtr<N> {
|
fn clone(&self) -> AstPtr<N> {
|
||||||
AstPtr { raw: self.raw.clone(), _ty: PhantomData }
|
AstPtr { raw: self.raw.clone(), _ty: PhantomData }
|
||||||
|
|
Loading…
Reference in a new issue