mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Diagnose call expression on non-callable things
This commit is contained in:
parent
3ba876a4a6
commit
3c7a0aa00e
6 changed files with 83 additions and 14 deletions
|
@ -170,6 +170,7 @@ pub enum InferenceDiagnostic {
|
||||||
// FIXME: Make this proper
|
// FIXME: Make this proper
|
||||||
BreakOutsideOfLoop { expr: ExprId, is_break: bool, bad_value_break: bool },
|
BreakOutsideOfLoop { expr: ExprId, is_break: bool, bad_value_break: bool },
|
||||||
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize },
|
||||||
|
ExpectedFunction { call_expr: ExprId, found: Ty },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mismatch between an expected and an inferred type.
|
/// A mismatch between an expected and an inferred type.
|
||||||
|
@ -505,6 +506,14 @@ impl<'a> InferenceContext<'a> {
|
||||||
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
|
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
|
||||||
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
|
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
|
||||||
}
|
}
|
||||||
|
for diagnostic in &mut result.diagnostics {
|
||||||
|
match diagnostic {
|
||||||
|
InferenceDiagnostic::ExpectedFunction { found, .. } => {
|
||||||
|
*found = table.resolve_completely(found.clone())
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
for (_, subst) in result.method_resolutions.values_mut() {
|
for (_, subst) in result.method_resolutions.values_mut() {
|
||||||
*subst = table.resolve_completely(subst.clone());
|
*subst = table.resolve_completely(subst.clone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,7 +364,13 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
(params, ret_ty)
|
(params, ret_ty)
|
||||||
}
|
}
|
||||||
None => (Vec::new(), self.err_ty()), // FIXME diagnostic
|
None => {
|
||||||
|
self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
|
||||||
|
call_expr: tgt_expr,
|
||||||
|
found: callee_ty.clone(),
|
||||||
|
});
|
||||||
|
(Vec::new(), self.err_ty())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
|
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
|
||||||
self.register_obligations_for_call(&callee_ty);
|
self.register_obligations_for_call(&callee_ty);
|
||||||
|
|
|
@ -31,6 +31,7 @@ macro_rules! diagnostics {
|
||||||
|
|
||||||
diagnostics![
|
diagnostics![
|
||||||
BreakOutsideOfLoop,
|
BreakOutsideOfLoop,
|
||||||
|
ExpectedFunction,
|
||||||
InactiveCode,
|
InactiveCode,
|
||||||
IncorrectCase,
|
IncorrectCase,
|
||||||
InvalidDeriveTarget,
|
InvalidDeriveTarget,
|
||||||
|
@ -130,6 +131,12 @@ pub struct PrivateAssocItem {
|
||||||
pub item: AssocItem,
|
pub item: AssocItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ExpectedFunction {
|
||||||
|
pub call: InFile<AstPtr<ast::Expr>>,
|
||||||
|
pub found: Type,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PrivateField {
|
pub struct PrivateField {
|
||||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
|
|
@ -84,9 +84,9 @@ use crate::db::{DefDatabase, HirDatabase};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
attrs::{HasAttrs, Namespace},
|
attrs::{HasAttrs, Namespace},
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
|
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
|
||||||
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
|
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||||
MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
|
MissingMatchArms, MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField,
|
||||||
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
||||||
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
|
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
|
||||||
UnresolvedProcMacro,
|
UnresolvedProcMacro,
|
||||||
|
@ -1377,8 +1377,8 @@ impl DefWithBody {
|
||||||
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
|
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
|
||||||
for d in &infer.diagnostics {
|
for d in &infer.diagnostics {
|
||||||
match d {
|
match d {
|
||||||
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
|
&hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
|
||||||
let field = source_map.field_syntax(*expr);
|
let field = source_map.field_syntax(expr);
|
||||||
acc.push(NoSuchField { field }.into())
|
acc.push(NoSuchField { field }.into())
|
||||||
}
|
}
|
||||||
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
|
&hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
|
||||||
|
@ -1391,15 +1391,10 @@ impl DefWithBody {
|
||||||
.expect("break outside of loop in synthetic syntax");
|
.expect("break outside of loop in synthetic syntax");
|
||||||
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
|
acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
|
||||||
}
|
}
|
||||||
hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
|
||||||
match source_map.expr_syntax(*call_expr) {
|
match source_map.expr_syntax(call_expr) {
|
||||||
Ok(source_ptr) => acc.push(
|
Ok(source_ptr) => acc.push(
|
||||||
MismatchedArgCount {
|
MismatchedArgCount { call_expr: source_ptr, expected, found }.into(),
|
||||||
call_expr: source_ptr,
|
|
||||||
expected: *expected,
|
|
||||||
found: *found,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
),
|
||||||
Err(SyntheticSyntax) => (),
|
Err(SyntheticSyntax) => (),
|
||||||
}
|
}
|
||||||
|
@ -1423,6 +1418,18 @@ impl DefWithBody {
|
||||||
let item = item.into();
|
let item = item.into();
|
||||||
acc.push(PrivateAssocItem { expr_or_pat, item }.into())
|
acc.push(PrivateAssocItem { expr_or_pat, item }.into())
|
||||||
}
|
}
|
||||||
|
hir_ty::InferenceDiagnostic::ExpectedFunction { call_expr, found } => {
|
||||||
|
let call_expr =
|
||||||
|
source_map.expr_syntax(*call_expr).expect("unexpected synthetic");
|
||||||
|
|
||||||
|
acc.push(
|
||||||
|
ExpectedFunction {
|
||||||
|
call: call_expr,
|
||||||
|
found: Type::new(db, DefWithBodyId::from(self), found.clone()),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (pat_or_expr, mismatch) in infer.type_mismatches() {
|
for (pat_or_expr, mismatch) in infer.type_mismatches() {
|
||||||
|
|
38
crates/ide-diagnostics/src/handlers/expected_function.rs
Normal file
38
crates/ide-diagnostics/src/handlers/expected_function.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use hir::HirDisplay;
|
||||||
|
|
||||||
|
use crate::{Diagnostic, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: expected-function
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if a call is made on something that is not callable.
|
||||||
|
pub(crate) fn expected_function(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::ExpectedFunction,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new(
|
||||||
|
"expected-function",
|
||||||
|
format!("expected function, found {}", d.found.display(ctx.sema.db)),
|
||||||
|
ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn smoke_test() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let x = 3;
|
||||||
|
x();
|
||||||
|
// ^^^ error: expected function, found i32
|
||||||
|
""();
|
||||||
|
// ^^^^ error: expected function, found &str
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
mod handlers {
|
mod handlers {
|
||||||
pub(crate) mod break_outside_of_loop;
|
pub(crate) mod break_outside_of_loop;
|
||||||
|
pub(crate) mod expected_function;
|
||||||
pub(crate) mod inactive_code;
|
pub(crate) mod inactive_code;
|
||||||
pub(crate) mod incorrect_case;
|
pub(crate) mod incorrect_case;
|
||||||
pub(crate) mod invalid_derive_target;
|
pub(crate) mod invalid_derive_target;
|
||||||
|
@ -248,6 +249,7 @@ pub fn diagnostics(
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
|
||||||
|
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
|
||||||
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
|
||||||
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
||||||
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
|
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
|
||||||
|
|
Loading…
Reference in a new issue