2400b36a2e
Spelling This PR corrects misspellings identified by the [check-spelling action](https://github.com/marketplace/actions/check-spelling). The misspellings have been reported at https://github.com/jsoref/rust-analyzer/actions/runs/4699991040#summary-12751355796 The action reports that the changes in this PR would make it happy: https://github.com/jsoref/rust-analyzer/actions/runs/4699991284#summary-12751356293 closes #14567
149 lines
3.3 KiB
Rust
149 lines
3.3 KiB
Rust
use hir::{db::ExpandDatabase, HirDisplay, InFile};
|
|
use ide_db::{
|
|
assists::{Assist, AssistId, AssistKind},
|
|
base_db::FileRange,
|
|
label::Label,
|
|
source_change::SourceChange,
|
|
};
|
|
use syntax::{ast, AstNode, AstPtr};
|
|
use text_edit::TextEdit;
|
|
|
|
use crate::{Diagnostic, DiagnosticsContext};
|
|
|
|
// Diagnostic: unresolved-field
|
|
//
|
|
// This diagnostic is triggered if a field does not exist on a given type.
|
|
pub(crate) fn unresolved_field(
|
|
ctx: &DiagnosticsContext<'_>,
|
|
d: &hir::UnresolvedField,
|
|
) -> Diagnostic {
|
|
let method_suffix = if d.method_with_same_name_exists {
|
|
", but a method with a similar name exists"
|
|
} else {
|
|
""
|
|
};
|
|
Diagnostic::new(
|
|
"unresolved-field",
|
|
format!(
|
|
"no field `{}` on type `{}`{method_suffix}",
|
|
d.name,
|
|
d.receiver.display(ctx.sema.db)
|
|
),
|
|
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
|
|
)
|
|
.with_fixes(fixes(ctx, d))
|
|
.experimental()
|
|
}
|
|
|
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
|
|
if d.method_with_same_name_exists {
|
|
method_fix(ctx, &d.expr)
|
|
} else {
|
|
// FIXME: add quickfix
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
// FIXME: We should fill out the call here, move the cursor and trigger signature help
|
|
fn method_fix(
|
|
ctx: &DiagnosticsContext<'_>,
|
|
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
|
) -> Option<Vec<Assist>> {
|
|
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
|
|
let expr = expr_ptr.value.to_node(&root);
|
|
let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
|
|
Some(vec![Assist {
|
|
id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix),
|
|
label: Label::new("Use parentheses to call the method".to_string()),
|
|
group: None,
|
|
target: range,
|
|
source_change: Some(SourceChange::from_text_edit(
|
|
file_id,
|
|
TextEdit::insert(range.end(), "()".to_owned()),
|
|
)),
|
|
trigger_signature_help: false,
|
|
}])
|
|
}
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::tests::check_diagnostics;
|
|
|
|
#[test]
|
|
fn smoke_test() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn main() {
|
|
().foo;
|
|
// ^^^^^^ error: no field `foo` on type `()`
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_clash() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct Foo;
|
|
impl Foo {
|
|
fn bar(&self) {}
|
|
}
|
|
fn foo() {
|
|
Foo.bar;
|
|
// ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_trait_() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct Foo;
|
|
trait Bar {
|
|
fn bar(&self) {}
|
|
}
|
|
impl Bar for Foo {}
|
|
fn foo() {
|
|
Foo.bar;
|
|
// ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_trait_2() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct Foo;
|
|
trait Bar {
|
|
fn bar(&self);
|
|
}
|
|
impl Bar for Foo {
|
|
fn bar(&self) {}
|
|
}
|
|
fn foo() {
|
|
Foo.bar;
|
|
// ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn no_diagnostic_on_unknown() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn foo() {
|
|
x.foo;
|
|
(&x).foo;
|
|
(&((x,),),).foo;
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
}
|