feat: add unresolved-ident diagnostic
This commit is contained in:
parent
d818b531c9
commit
a492d9d164
@ -221,6 +221,9 @@ pub enum InferenceDiagnostic {
|
|||||||
UnresolvedAssocItem {
|
UnresolvedAssocItem {
|
||||||
id: ExprOrPatId,
|
id: ExprOrPatId,
|
||||||
},
|
},
|
||||||
|
UnresolvedIdent {
|
||||||
|
expr: ExprId,
|
||||||
|
},
|
||||||
// FIXME: This should be emitted in body lowering
|
// FIXME: This should be emitted in body lowering
|
||||||
BreakOutsideOfLoop {
|
BreakOutsideOfLoop {
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
path::{GenericArg, GenericArgs},
|
path::{GenericArg, GenericArgs, Path},
|
||||||
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{name, Name};
|
use hir_expand::name::{name, Name};
|
||||||
@ -439,7 +439,17 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
|||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||||
let ty = self.infer_path(p, tgt_expr.into()).unwrap_or_else(|| self.err_ty());
|
let ty = match self.infer_path(p, tgt_expr.into()) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident()) {
|
||||||
|
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
|
||||||
|
expr: tgt_expr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.err_ty()
|
||||||
|
}
|
||||||
|
};
|
||||||
self.resolver.reset_to_guard(g);
|
self.resolver.reset_to_guard(g);
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ fn from(d: $diag) -> AnyDiagnostic {
|
|||||||
UnresolvedMacroCall,
|
UnresolvedMacroCall,
|
||||||
UnresolvedMethodCall,
|
UnresolvedMethodCall,
|
||||||
UnresolvedModule,
|
UnresolvedModule,
|
||||||
|
UnresolvedIdent,
|
||||||
UnresolvedProcMacro,
|
UnresolvedProcMacro,
|
||||||
UnusedMut,
|
UnusedMut,
|
||||||
UnusedVariable,
|
UnusedVariable,
|
||||||
@ -242,6 +243,11 @@ pub struct UnresolvedAssocItem {
|
|||||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
|
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, Either<ast::Pat, ast::SelfParam>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnresolvedIdent {
|
||||||
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PrivateField {
|
pub struct PrivateField {
|
||||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||||
@ -588,6 +594,10 @@ pub(crate) fn inference_diagnostic(
|
|||||||
};
|
};
|
||||||
UnresolvedAssocItem { expr_or_pat }.into()
|
UnresolvedAssocItem { expr_or_pat }.into()
|
||||||
}
|
}
|
||||||
|
&InferenceDiagnostic::UnresolvedIdent { expr } => {
|
||||||
|
let expr = expr_syntax(expr);
|
||||||
|
UnresolvedIdent { expr }.into()
|
||||||
|
}
|
||||||
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
||||||
let expr = expr_syntax(expr);
|
let expr = expr_syntax(expr);
|
||||||
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
|
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
|
||||||
|
@ -60,6 +60,7 @@ fn f() {
|
|||||||
#[cfg(a)] let x = 0; // let statement
|
#[cfg(a)] let x = 0; // let statement
|
||||||
//^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
|
//^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
|
||||||
|
|
||||||
|
fn abc() {}
|
||||||
abc(#[cfg(a)] 0);
|
abc(#[cfg(a)] 0);
|
||||||
//^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
|
//^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
|
||||||
let x = Struct {
|
let x = Struct {
|
||||||
|
@ -634,7 +634,8 @@ struct TestStruct { one: i32, two: i64 }
|
|||||||
|
|
||||||
fn test_fn() {
|
fn test_fn() {
|
||||||
let one = 1;
|
let one = 1;
|
||||||
let s = TestStruct{ ..a };
|
let a = TestStruct{ one, two: 2 };
|
||||||
|
let _ = TestStruct{ ..a };
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,9 @@ pub(crate) fn missing_match_arms(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
tests::{check_diagnostics, check_diagnostics_with_config},
|
tests::{
|
||||||
|
check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
|
||||||
|
},
|
||||||
DiagnosticsConfig,
|
DiagnosticsConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -282,7 +284,7 @@ fn mismatched_types() {
|
|||||||
cov_mark::check_count!(validate_match_bailed_out, 4);
|
cov_mark::check_count!(validate_match_bailed_out, 4);
|
||||||
// Match statements with arms that don't match the
|
// Match statements with arms that don't match the
|
||||||
// expression pattern do not fire this diagnostic.
|
// expression pattern do not fire this diagnostic.
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
enum Either { A, B }
|
enum Either { A, B }
|
||||||
enum Either2 { C, D }
|
enum Either2 { C, D }
|
||||||
@ -307,6 +309,7 @@ fn main() {
|
|||||||
match Unresolved::Bar { Unresolved::Baz => () }
|
match Unresolved::Bar { Unresolved::Baz => () }
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,11 +400,11 @@ fn main() {
|
|||||||
match loop {} {
|
match loop {} {
|
||||||
Either::A => (),
|
Either::A => (),
|
||||||
}
|
}
|
||||||
match loop { break Foo::A } {
|
match loop { break Either::A } {
|
||||||
//^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
|
//^^^^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
|
||||||
Either::A => (),
|
Either::A => (),
|
||||||
}
|
}
|
||||||
match loop { break Foo::A } {
|
match loop { break Either::A } {
|
||||||
Either::A => (),
|
Either::A => (),
|
||||||
Either::B => (),
|
Either::B => (),
|
||||||
}
|
}
|
||||||
@ -977,7 +980,7 @@ fn f(ty: Enum) {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unexpected_ty_fndef() {
|
fn unexpected_ty_fndef() {
|
||||||
cov_mark::check!(validate_match_bailed_out);
|
cov_mark::check!(validate_match_bailed_out);
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r"
|
r"
|
||||||
enum Exp {
|
enum Exp {
|
||||||
Tuple(()),
|
Tuple(()),
|
||||||
@ -987,6 +990,7 @@ fn f() {
|
|||||||
Exp::Tuple => {}
|
Exp::Tuple => {}
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix};
|
use crate::tests::{check_diagnostics_with_disabled, check_fix};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_unnecessary_else_for_return() {
|
fn remove_unnecessary_else_for_return() {
|
||||||
@ -110,7 +110,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
&["needless_return"],
|
&["needless_return", "E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -148,7 +148,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
&["needless_return"],
|
&["needless_return", "E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -227,7 +227,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
&["needless_return"],
|
&["needless_return", "E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -293,7 +293,7 @@ fn test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_unnecessary_else_for_break() {
|
fn remove_unnecessary_else_for_break() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn test() {
|
fn test() {
|
||||||
loop {
|
loop {
|
||||||
@ -306,6 +306,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -334,7 +335,7 @@ fn test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_unnecessary_else_for_continue() {
|
fn remove_unnecessary_else_for_continue() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn test() {
|
fn test() {
|
||||||
loop {
|
loop {
|
||||||
@ -347,6 +348,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -375,7 +377,7 @@ fn test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_unnecessary_else_for_never() {
|
fn remove_unnecessary_else_for_never() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn test() {
|
fn test() {
|
||||||
if foo {
|
if foo {
|
||||||
@ -390,6 +392,7 @@ fn never() -> ! {
|
|||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
check_fix(
|
check_fix(
|
||||||
r#"
|
r#"
|
||||||
@ -422,7 +425,7 @@ fn never() -> ! {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_diagnostic_if_no_else_branch() {
|
fn no_diagnostic_if_no_else_branch() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn test() {
|
fn test() {
|
||||||
if foo {
|
if foo {
|
||||||
@ -432,12 +435,13 @@ fn test() {
|
|||||||
do_something_else();
|
do_something_else();
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_diagnostic_if_no_divergence() {
|
fn no_diagnostic_if_no_divergence() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn test() {
|
fn test() {
|
||||||
if foo {
|
if foo {
|
||||||
@ -447,6 +451,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,7 +467,7 @@ fn test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
&["needless_return"],
|
&["needless_return", "E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +38,12 @@ fn foo() {
|
|||||||
fn while_let_loop_with_label_in_condition() {
|
fn while_let_loop_with_label_in_condition() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let mut optional = Some(0);
|
let mut optional = Some(0);
|
||||||
|
|
||||||
'my_label: while let Some(a) = match optional {
|
'my_label: while let Some(_) = match optional {
|
||||||
None => break 'my_label,
|
None => break 'my_label,
|
||||||
Some(val) => Some(val),
|
Some(val) => Some(val),
|
||||||
} {
|
} {
|
||||||
@ -59,8 +61,8 @@ fn for_loop() {
|
|||||||
r#"
|
r#"
|
||||||
//- minicore: iterator
|
//- minicore: iterator
|
||||||
fn foo() {
|
fn foo() {
|
||||||
'xxx: for _ in unknown {
|
'xxx: for _ in [] {
|
||||||
'yyy: for _ in unknown {
|
'yyy: for _ in [] {
|
||||||
break 'xxx;
|
break 'xxx;
|
||||||
continue 'yyy;
|
continue 'yyy;
|
||||||
break 'zzz;
|
break 'zzz;
|
||||||
|
@ -78,7 +78,9 @@ fn method_fix(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
tests::{check_diagnostics, check_diagnostics_with_config},
|
tests::{
|
||||||
|
check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
|
||||||
|
},
|
||||||
DiagnosticsConfig,
|
DiagnosticsConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ fn foo() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_diagnostic_on_unknown() {
|
fn no_diagnostic_on_unknown() {
|
||||||
check_diagnostics(
|
check_diagnostics_with_disabled(
|
||||||
r#"
|
r#"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
x.foo;
|
x.foo;
|
||||||
@ -156,6 +158,7 @@ fn foo() {
|
|||||||
(&((x,),),).foo;
|
(&((x,),),).foo;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
&["E0425"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
crates/ide-diagnostics/src/handlers/unresolved_ident.rs
Normal file
46
crates/ide-diagnostics/src/handlers/unresolved_ident.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||||
|
|
||||||
|
// Diagnostic: unresolved-ident
|
||||||
|
//
|
||||||
|
// This diagnostic is triggered if an expr-position ident is invalid.
|
||||||
|
pub(crate) fn unresolved_ident(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::UnresolvedIdent,
|
||||||
|
) -> Diagnostic {
|
||||||
|
Diagnostic::new_with_syntax_node_ptr(
|
||||||
|
ctx,
|
||||||
|
DiagnosticCode::RustcHardError("E0425"),
|
||||||
|
"no such value in this scope",
|
||||||
|
d.expr.map(Into::into),
|
||||||
|
)
|
||||||
|
.experimental()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests::check_diagnostics;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let _ = x;
|
||||||
|
//^ error: no such value in this scope
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn present() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let x = 5;
|
||||||
|
let _ = x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -335,8 +335,8 @@ fn field() {
|
|||||||
r#"
|
r#"
|
||||||
struct Foo { bar: i32 }
|
struct Foo { bar: i32 }
|
||||||
fn foo() {
|
fn foo() {
|
||||||
Foo { bar: i32 }.bar();
|
Foo { bar: 0 }.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
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -59,6 +59,7 @@ mod handlers {
|
|||||||
pub(crate) mod unresolved_assoc_item;
|
pub(crate) mod unresolved_assoc_item;
|
||||||
pub(crate) mod unresolved_extern_crate;
|
pub(crate) mod unresolved_extern_crate;
|
||||||
pub(crate) mod unresolved_field;
|
pub(crate) mod unresolved_field;
|
||||||
|
pub(crate) mod unresolved_ident;
|
||||||
pub(crate) mod unresolved_import;
|
pub(crate) mod unresolved_import;
|
||||||
pub(crate) mod unresolved_macro_call;
|
pub(crate) mod unresolved_macro_call;
|
||||||
pub(crate) mod unresolved_method;
|
pub(crate) mod unresolved_method;
|
||||||
@ -377,6 +378,7 @@ pub fn diagnostics(
|
|||||||
AnyDiagnostic::UnresolvedAssocItem(d) => handlers::unresolved_assoc_item::unresolved_assoc_item(&ctx, &d),
|
AnyDiagnostic::UnresolvedAssocItem(d) => handlers::unresolved_assoc_item::unresolved_assoc_item(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
|
AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
|
AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
|
||||||
|
AnyDiagnostic::UnresolvedIdent(d) => handlers::unresolved_ident::unresolved_ident(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
|
AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
|
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
|
||||||
AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
|
AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
|
||||||
|
Loading…
Reference in New Issue
Block a user