mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 11:59:49 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
15b867b5db
commit
b2f6fd4f96
217 changed files with 12639 additions and 3059 deletions
|
@ -7,10 +7,15 @@ pub(crate) fn break_outside_of_loop(
|
|||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::BreakOutsideOfLoop,
|
||||
) -> Diagnostic {
|
||||
let construct = if d.is_break { "break" } else { "continue" };
|
||||
let message = if d.bad_value_break {
|
||||
"can't break with a value in this position".to_owned()
|
||||
} else {
|
||||
let construct = if d.is_break { "break" } else { "continue" };
|
||||
format!("{construct} outside of loop")
|
||||
};
|
||||
Diagnostic::new(
|
||||
"break-outside-of-loop",
|
||||
format!("{construct} outside of loop"),
|
||||
message,
|
||||
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
|
||||
)
|
||||
}
|
||||
|
@ -132,6 +137,20 @@ fn foo() {
|
|||
//^^^^^^^^^^^ error: continue outside of loop
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_break_in_for_loop() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn test() {
|
||||
for _ in [()] {
|
||||
break 3;
|
||||
// ^^^^^^^ error: can't break with a value in this position
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
39
crates/ide-diagnostics/src/handlers/expected_function.rs
Normal file
39
crates/ide-diagnostics/src/handlers/expected_function.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
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,
|
||||
)
|
||||
.experimental()
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ use hir::{
|
|||
};
|
||||
use ide_db::{
|
||||
assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
|
||||
source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap,
|
||||
source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap,
|
||||
};
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use hir::InFile;
|
||||
|
||||
use crate::{Diagnostic, DiagnosticsContext};
|
||||
|
||||
// Diagnostic: missing-match-arm
|
||||
|
@ -12,7 +10,7 @@ pub(crate) fn missing_match_arms(
|
|||
Diagnostic::new(
|
||||
"missing-match-arm",
|
||||
format!("missing match arm: {}", d.uncovered_patterns),
|
||||
ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range,
|
||||
ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1038,7 +1036,6 @@ fn main() {
|
|||
#[test]
|
||||
fn reference_patterns_in_fields() {
|
||||
cov_mark::check_count!(validate_match_bailed_out, 2);
|
||||
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
|
|
625
crates/ide-diagnostics/src/handlers/mutability_errors.rs
Normal file
625
crates/ide-diagnostics/src/handlers/mutability_errors.rs
Normal file
|
@ -0,0 +1,625 @@
|
|||
use ide_db::source_change::SourceChange;
|
||||
use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{fix, Diagnostic, DiagnosticsContext, Severity};
|
||||
|
||||
// Diagnostic: need-mut
|
||||
//
|
||||
// This diagnostic is triggered on mutating an immutable variable.
|
||||
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic {
|
||||
let fixes = (|| {
|
||||
if d.local.is_ref(ctx.sema.db) {
|
||||
// There is no simple way to add `mut` to `ref x` and `ref mut x`
|
||||
return None;
|
||||
}
|
||||
let file_id = d.span.file_id.file_id()?;
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
let use_range = d.span.value.text_range();
|
||||
for source in d.local.sources(ctx.sema.db) {
|
||||
let Some(ast) = source.name() else { continue };
|
||||
edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
|
||||
}
|
||||
let edit = edit_builder.finish();
|
||||
Some(vec![fix(
|
||||
"add_mut",
|
||||
"Change it to be mutable",
|
||||
SourceChange::from_text_edit(file_id, edit),
|
||||
use_range,
|
||||
)])
|
||||
})();
|
||||
Diagnostic::new(
|
||||
"need-mut",
|
||||
format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
|
||||
ctx.sema.diagnostics_display_range(d.span.clone()).range,
|
||||
)
|
||||
.with_fixes(fixes)
|
||||
}
|
||||
|
||||
// Diagnostic: unused-mut
|
||||
//
|
||||
// This diagnostic is triggered when a mutable variable isn't actually mutated.
|
||||
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic {
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
let fixes = (|| {
|
||||
let file_id = ast.file_id.file_id()?;
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
let use_range = ast.value.text_range();
|
||||
for source in d.local.sources(ctx.sema.db) {
|
||||
let ast = source.syntax();
|
||||
let Some(mut_token) = token(ast, T![mut]) else { continue };
|
||||
edit_builder.delete(mut_token.text_range());
|
||||
if let Some(token) = mut_token.next_token() {
|
||||
if token.kind() == SyntaxKind::WHITESPACE {
|
||||
edit_builder.delete(token.text_range());
|
||||
}
|
||||
}
|
||||
}
|
||||
let edit = edit_builder.finish();
|
||||
Some(vec![fix(
|
||||
"remove_mut",
|
||||
"Remove unnecessary `mut`",
|
||||
SourceChange::from_text_edit(file_id, edit),
|
||||
use_range,
|
||||
)])
|
||||
})();
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
Diagnostic::new(
|
||||
"unused-mut",
|
||||
"variable does not need to be mutable",
|
||||
ctx.sema.diagnostics_display_range(ast).range,
|
||||
)
|
||||
.severity(Severity::WeakWarning)
|
||||
.experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
|
||||
.with_fixes(fixes)
|
||||
}
|
||||
|
||||
pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
|
||||
parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_diagnostics, check_fix};
|
||||
|
||||
#[test]
|
||||
fn unused_mut_simple() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_false_positive_simple() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = 2;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x = 2;
|
||||
x = 5;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_errors_for_single_variable() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = 2;
|
||||
x = 10;
|
||||
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
x = 5;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
&mut x;
|
||||
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_mut_fix() {
|
||||
check_fix(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mu$0t x = 2;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = 2;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_fix(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let ((mu$0t x, _) | (_, mut x)) = (2, 3);
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let ((x, _) | (_, x)) = (2, 3);
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn need_mut_fix() {
|
||||
check_fix(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = 2;
|
||||
x$0 = 5;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x = 2;
|
||||
x = 5;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_fix(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let ((x, _) | (_, x)) = (2, 3);
|
||||
x =$0 4;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let ((mut x, _) | (_, mut x)) = (2, 3);
|
||||
x = 4;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
check_fix(
|
||||
r#"
|
||||
struct Foo(i32);
|
||||
|
||||
impl Foo {
|
||||
fn foo(self) {
|
||||
self = Fo$0o(5);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo(i32);
|
||||
|
||||
impl Foo {
|
||||
fn foo(mut self) {
|
||||
self = Foo(5);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn need_mut_fix_not_applicable_on_ref() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
let ref x = 2;
|
||||
x = &5;
|
||||
//^^^^^^ error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
let ref mut x = 2;
|
||||
x = &mut 5;
|
||||
//^^^^^^^^^^ error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_mutate() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x = (2, 7);
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(x.1);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x = (2, 7);
|
||||
x.0 = 5;
|
||||
f(x.1);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = (2, 7);
|
||||
x.0 = 5;
|
||||
//^^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
f(x.1);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutable_reference() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
let mut x = &mut 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
*x = 5;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
let x = 2;
|
||||
&mut x;
|
||||
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
let x_own = 2;
|
||||
let ref mut x_ref = x_own;
|
||||
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn method(&mut self, x: i32) {}
|
||||
}
|
||||
fn main() {
|
||||
let x = Foo;
|
||||
x.method(2);
|
||||
//^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_14310() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn clone(mut i: &!) -> ! {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
*i
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_bindings() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
match (2, 3) {
|
||||
(x, mut y) => {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
x = 7;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mutation_in_dead_code() {
|
||||
// This one is interesting. Dead code is not represented at all in the MIR, so
|
||||
// there would be no mutablility error for locals in dead code. Rustc tries to
|
||||
// not emit `unused_mut` in this case, but since it works without `mut`, and
|
||||
// special casing it is not trivial, we emit it.
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
return;
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
loop {}
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum X {}
|
||||
fn g() -> X {
|
||||
loop {}
|
||||
}
|
||||
fn f() -> ! {
|
||||
loop {}
|
||||
}
|
||||
fn main(b: bool) {
|
||||
if b {
|
||||
f();
|
||||
} else {
|
||||
g();
|
||||
}
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main(b: bool) {
|
||||
if b {
|
||||
loop {}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
&mut x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialization_is_not_mutation() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let mut x;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
x = 5;
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main(b: bool) {
|
||||
let mut x;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
if b {
|
||||
x = 1;
|
||||
} else {
|
||||
x = 3;
|
||||
}
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main(b: bool) {
|
||||
let x;
|
||||
if b {
|
||||
x = 1;
|
||||
}
|
||||
x = 3;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x;
|
||||
loop {
|
||||
x = 1;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
f(x);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
loop {
|
||||
let mut x = 1;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(x);
|
||||
if let mut y = 2 {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(y);
|
||||
}
|
||||
match 3 {
|
||||
mut z => f(z),
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_arguments_are_initialized() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(mut x: i32) {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(x: i32) {
|
||||
x = 5;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_loop() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: iterators
|
||||
fn f(x: [(i32, u8); 10]) {
|
||||
for (a, mut b) in x {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
a = 2;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `a`
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overloaded_deref() {
|
||||
// FIXME: check for false negative
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: deref_mut
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
struct Foo;
|
||||
impl Deref for Foo {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &i32 {
|
||||
&5
|
||||
}
|
||||
}
|
||||
impl DerefMut for Foo {
|
||||
fn deref_mut(&mut self) -> &mut i32 {
|
||||
&mut 5
|
||||
}
|
||||
}
|
||||
fn f() {
|
||||
let x = Foo;
|
||||
let y = &*x;
|
||||
let x = Foo;
|
||||
let mut x = Foo;
|
||||
let y: &mut i32 = &mut x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or_pattern() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: option
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7));
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or_pattern_no_terminator() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum Foo {
|
||||
A, B, C, D
|
||||
}
|
||||
|
||||
use Foo::*;
|
||||
|
||||
fn f(inp: (Foo, Foo, Foo, Foo)) {
|
||||
let ((A, B, _, x) | (B, C | D, x, _)) = inp else {
|
||||
return;
|
||||
};
|
||||
x = B;
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respect_allow_unused_mut() {
|
||||
// FIXME: respect
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut x = 2;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
f(x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -115,6 +115,44 @@ mod module {
|
|||
fn main(s: module::Struct) {
|
||||
s.method();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_see_through_top_level_anonymous_const() {
|
||||
// regression test for #14046.
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct S;
|
||||
mod m {
|
||||
const _: () = {
|
||||
impl crate::S {
|
||||
pub(crate) fn method(self) {}
|
||||
pub(crate) const A: usize = 42;
|
||||
}
|
||||
};
|
||||
mod inner {
|
||||
const _: () = {
|
||||
impl crate::S {
|
||||
pub(crate) fn method2(self) {}
|
||||
pub(crate) const B: usize = 42;
|
||||
pub(super) fn private(self) {}
|
||||
pub(super) const PRIVATE: usize = 42;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
S.method();
|
||||
S::A;
|
||||
S.method2();
|
||||
S::B;
|
||||
S.private();
|
||||
//^^^^^^^^^^^ error: function `private` is private
|
||||
S::PRIVATE;
|
||||
//^^^^^^^^^^ error: const `PRIVATE` is private
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,18 @@ fn fixes(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_diagnostics, check_fix};
|
||||
use crate::{
|
||||
tests::{check_diagnostics_with_config, check_fix},
|
||||
DiagnosticsConfig,
|
||||
};
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn check_diagnostics(ra_fixture: &str) {
|
||||
let mut config = DiagnosticsConfig::test_sample();
|
||||
config.disabled.insert("inactive-code".to_string());
|
||||
config.disabled.insert("unresolved-method".to_string());
|
||||
check_diagnostics_with_config(config, ra_fixture)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_filter_map_next_with_find_map2() {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use hir::{db::AstDatabase, HirDisplay, Type};
|
||||
use either::Either;
|
||||
use hir::{db::AstDatabase, HirDisplay, InFile, Type};
|
||||
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
|
||||
use syntax::{
|
||||
ast::{self, BlockExpr, ExprStmt},
|
||||
AstNode,
|
||||
AstNode, AstPtr,
|
||||
};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
|
@ -10,19 +11,23 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}
|
|||
|
||||
// Diagnostic: type-mismatch
|
||||
//
|
||||
// This diagnostic is triggered when the type of an expression does not match
|
||||
// This diagnostic is triggered when the type of an expression or pattern does not match
|
||||
// the expected type.
|
||||
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
|
||||
let display_range = adjusted_display_range::<ast::BlockExpr>(
|
||||
ctx,
|
||||
d.expr.clone().map(|it| it.into()),
|
||||
&|block| {
|
||||
let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
|
||||
cov_mark::hit!(type_mismatch_on_block);
|
||||
Some(r_curly_range)
|
||||
},
|
||||
);
|
||||
|
||||
let display_range = match &d.expr_or_pat {
|
||||
Either::Left(expr) => adjusted_display_range::<ast::BlockExpr>(
|
||||
ctx,
|
||||
expr.clone().map(|it| it.into()),
|
||||
&|block| {
|
||||
let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
|
||||
cov_mark::hit!(type_mismatch_on_block);
|
||||
Some(r_curly_range)
|
||||
},
|
||||
),
|
||||
Either::Right(pat) => {
|
||||
ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
|
||||
}
|
||||
};
|
||||
let mut diag = Diagnostic::new(
|
||||
"type-mismatch",
|
||||
format!(
|
||||
|
@ -42,10 +47,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
|
|||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
|
||||
let mut fixes = Vec::new();
|
||||
|
||||
add_reference(ctx, d, &mut fixes);
|
||||
add_missing_ok_or_some(ctx, d, &mut fixes);
|
||||
remove_semicolon(ctx, d, &mut fixes);
|
||||
str_ref_to_owned(ctx, d, &mut fixes);
|
||||
match &d.expr_or_pat {
|
||||
Either::Left(expr_ptr) => {
|
||||
add_reference(ctx, d, expr_ptr, &mut fixes);
|
||||
add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
|
||||
remove_semicolon(ctx, d, expr_ptr, &mut fixes);
|
||||
str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
|
||||
}
|
||||
Either::Right(_pat_ptr) => {}
|
||||
}
|
||||
|
||||
if fixes.is_empty() {
|
||||
None
|
||||
|
@ -57,9 +67,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assi
|
|||
fn add_reference(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::TypeMismatch,
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
acc: &mut Vec<Assist>,
|
||||
) -> Option<()> {
|
||||
let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
|
||||
let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range;
|
||||
|
||||
let (_, mutability) = d.expected.as_reference()?;
|
||||
let actual_with_ref = Type::reference(&d.actual, mutability);
|
||||
|
@ -71,7 +82,7 @@ fn add_reference(
|
|||
|
||||
let edit = TextEdit::insert(range.start(), ampersands);
|
||||
let source_change =
|
||||
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
|
||||
SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
|
||||
acc.push(fix("add_reference_here", "Add reference here", source_change, range));
|
||||
Some(())
|
||||
}
|
||||
|
@ -79,10 +90,11 @@ fn add_reference(
|
|||
fn add_missing_ok_or_some(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::TypeMismatch,
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
acc: &mut Vec<Assist>,
|
||||
) -> Option<()> {
|
||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
||||
let expr = d.expr.value.to_node(&root);
|
||||
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
|
||||
let expr = expr_ptr.value.to_node(&root);
|
||||
let expr_range = expr.syntax().text_range();
|
||||
let scope = ctx.sema.scope(expr.syntax())?;
|
||||
|
||||
|
@ -109,7 +121,7 @@ fn add_missing_ok_or_some(
|
|||
builder.insert(expr.syntax().text_range().start(), format!("{variant_name}("));
|
||||
builder.insert(expr.syntax().text_range().end(), ")".to_string());
|
||||
let source_change =
|
||||
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
|
||||
SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), builder.finish());
|
||||
let name = format!("Wrap in {variant_name}");
|
||||
acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
|
||||
Some(())
|
||||
|
@ -118,10 +130,11 @@ fn add_missing_ok_or_some(
|
|||
fn remove_semicolon(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::TypeMismatch,
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
acc: &mut Vec<Assist>,
|
||||
) -> Option<()> {
|
||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
||||
let expr = d.expr.value.to_node(&root);
|
||||
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
|
||||
let expr = expr_ptr.value.to_node(&root);
|
||||
if !d.actual.is_unit() {
|
||||
return None;
|
||||
}
|
||||
|
@ -136,7 +149,7 @@ fn remove_semicolon(
|
|||
|
||||
let edit = TextEdit::delete(semicolon_range);
|
||||
let source_change =
|
||||
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
|
||||
SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
|
||||
|
||||
acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
|
||||
Some(())
|
||||
|
@ -145,24 +158,26 @@ fn remove_semicolon(
|
|||
fn str_ref_to_owned(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::TypeMismatch,
|
||||
expr_ptr: &InFile<AstPtr<ast::Expr>>,
|
||||
acc: &mut Vec<Assist>,
|
||||
) -> Option<()> {
|
||||
let expected = d.expected.display(ctx.sema.db);
|
||||
let actual = d.actual.display(ctx.sema.db);
|
||||
|
||||
// FIXME do this properly
|
||||
if expected.to_string() != "String" || actual.to_string() != "&str" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
||||
let expr = d.expr.value.to_node(&root);
|
||||
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
|
||||
let expr = expr_ptr.value.to_node(&root);
|
||||
let expr_range = expr.syntax().text_range();
|
||||
|
||||
let to_owned = format!(".to_owned()");
|
||||
|
||||
let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned);
|
||||
let source_change =
|
||||
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
|
||||
SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit);
|
||||
acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range));
|
||||
|
||||
Some(())
|
||||
|
@ -592,6 +607,21 @@ fn f() -> i32 {
|
|||
let _ = x + y;
|
||||
}
|
||||
//^ error: expected i32, found ()
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_mismatch_pat_smoke_test() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn f() {
|
||||
let &() = &mut ();
|
||||
match &() {
|
||||
&9 => ()
|
||||
//^ error: expected (), found i32
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
148
crates/ide-diagnostics/src/handlers/unresolved_field.rs
Normal file
148
crates/ide-diagnostics/src/handlers/unresolved_field.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use hir::{db::AstDatabase, 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, mvoe 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;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
131
crates/ide-diagnostics/src/handlers/unresolved_method.rs
Normal file
131
crates/ide-diagnostics/src/handlers/unresolved_method.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use hir::{db::AstDatabase, HirDisplay};
|
||||
use ide_db::{
|
||||
assists::{Assist, AssistId, AssistKind},
|
||||
base_db::FileRange,
|
||||
label::Label,
|
||||
source_change::SourceChange,
|
||||
};
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{Diagnostic, DiagnosticsContext};
|
||||
|
||||
// Diagnostic: unresolved-method
|
||||
//
|
||||
// This diagnostic is triggered if a method does not exist on a given type.
|
||||
pub(crate) fn unresolved_method(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::UnresolvedMethodCall,
|
||||
) -> Diagnostic {
|
||||
let field_suffix = if d.field_with_same_name.is_some() {
|
||||
", but a field with a similar name exists"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
Diagnostic::new(
|
||||
"unresolved-method",
|
||||
format!(
|
||||
"no method `{}` on type `{}`{field_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::UnresolvedMethodCall) -> Option<Vec<Assist>> {
|
||||
if let Some(ty) = &d.field_with_same_name {
|
||||
field_fix(ctx, d, ty)
|
||||
} else {
|
||||
// FIXME: add quickfix
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn field_fix(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::UnresolvedMethodCall,
|
||||
ty: &hir::Type,
|
||||
) -> Option<Vec<Assist>> {
|
||||
if !ty.impls_fnonce(ctx.sema.db) {
|
||||
return None;
|
||||
}
|
||||
let expr_ptr = &d.expr;
|
||||
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
|
||||
let expr = expr_ptr.value.to_node(&root);
|
||||
let (file_id, range) = match expr {
|
||||
ast::Expr::MethodCallExpr(mcall) => {
|
||||
let FileRange { range, file_id } =
|
||||
ctx.sema.original_range_opt(mcall.receiver()?.syntax())?;
|
||||
let FileRange { range: range2, file_id: file_id2 } =
|
||||
ctx.sema.original_range_opt(mcall.name_ref()?.syntax())?;
|
||||
if file_id != file_id2 {
|
||||
return None;
|
||||
}
|
||||
(file_id, TextRange::new(range.start(), range2.end()))
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(vec![Assist {
|
||||
id: AssistId("expected-method-found-field-fix", AssistKind::QuickFix),
|
||||
label: Label::new("Use parentheses to call the value of the field".to_string()),
|
||||
group: None,
|
||||
target: range,
|
||||
source_change: Some(SourceChange::from_iter([
|
||||
(file_id, TextEdit::insert(range.start(), "(".to_owned())),
|
||||
(file_id, TextEdit::insert(range.end(), ")".to_owned())),
|
||||
])),
|
||||
trigger_signature_help: false,
|
||||
}])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_diagnostics, check_fix};
|
||||
|
||||
#[test]
|
||||
fn smoke_test() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn main() {
|
||||
().foo();
|
||||
// ^^^^^^^^ error: no method `foo` on type `()`
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callable_field() {
|
||||
check_fix(
|
||||
r#"
|
||||
//- minicore: fn
|
||||
struct Foo { bar: fn() }
|
||||
fn foo() {
|
||||
Foo { bar: foo }.b$0ar();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo { bar: fn() }
|
||||
fn foo() {
|
||||
(Foo { bar: foo }.bar)();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
mod handlers {
|
||||
pub(crate) mod break_outside_of_loop;
|
||||
pub(crate) mod expected_function;
|
||||
pub(crate) mod inactive_code;
|
||||
pub(crate) mod incorrect_case;
|
||||
pub(crate) mod invalid_derive_target;
|
||||
|
@ -36,6 +37,7 @@ mod handlers {
|
|||
pub(crate) mod missing_fields;
|
||||
pub(crate) mod missing_match_arms;
|
||||
pub(crate) mod missing_unsafe;
|
||||
pub(crate) mod mutability_errors;
|
||||
pub(crate) mod no_such_field;
|
||||
pub(crate) mod private_assoc_item;
|
||||
pub(crate) mod private_field;
|
||||
|
@ -43,6 +45,8 @@ mod handlers {
|
|||
pub(crate) mod type_mismatch;
|
||||
pub(crate) mod unimplemented_builtin_macro;
|
||||
pub(crate) mod unresolved_extern_crate;
|
||||
pub(crate) mod unresolved_field;
|
||||
pub(crate) mod unresolved_method;
|
||||
pub(crate) mod unresolved_import;
|
||||
pub(crate) mod unresolved_macro_call;
|
||||
pub(crate) mod unresolved_module;
|
||||
|
@ -248,6 +252,7 @@ pub fn diagnostics(
|
|||
#[rustfmt::skip]
|
||||
let d = match diag {
|
||||
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::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
|
||||
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
|
||||
|
@ -267,7 +272,10 @@ pub fn diagnostics(
|
|||
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
|
||||
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
|
||||
|
||||
AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
|
||||
AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
|
||||
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
|
||||
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue