summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-diagnostics')
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs82
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs129
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs129
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs106
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs79
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs98
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs55
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs67
32 files changed, 1090 insertions, 244 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
index 14aa39401..f4055024c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
@@ -13,9 +13,9 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-either = "1.7.0"
-itertools = "0.10.5"
-serde_json = "1.0.86"
+either.workspace = true
+itertools.workspace = true
+serde_json.workspace = true
once_cell = "1.17.0"
# local deps
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs
index 3b69640af..45fc6f8e6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs
@@ -1,7 +1,10 @@
//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
//! expressions and patterns.
-use ide_db::{base_db::FileId, source_change::SourceChange};
+use ide_db::{
+ base_db::{FileId, FileRange},
+ source_change::SourceChange,
+};
use syntax::{ast, match_ast, AstNode, SyntaxNode};
use text_edit::TextEdit;
@@ -49,7 +52,7 @@ fn check_expr_field_shorthand(
Diagnostic::new(
DiagnosticCode::Clippy("redundant_field_names"),
"Shorthand struct initialization",
- field_range,
+ FileRange { file_id, range: field_range },
)
.with_fixes(Some(vec![fix(
"use_expr_field_shorthand",
@@ -93,7 +96,7 @@ fn check_pat_field_shorthand(
Diagnostic::new(
DiagnosticCode::Clippy("redundant_field_names"),
"Shorthand struct pattern",
- field_range,
+ FileRange { file_id, range: field_range },
)
.with_fixes(Some(vec![fix(
"use_pat_field_shorthand",
@@ -166,7 +169,7 @@ fn main() {
check_diagnostics(
r#"
struct A { a: &'static str }
-fn f(a: A) { let A { a: hello } = a; }
+fn f(a: A) { let A { a: _hello } = a; }
"#,
);
check_diagnostics(
@@ -181,12 +184,14 @@ fn f(a: A) { let A { 0: 0 } = a; }
struct A { a: &'static str }
fn f(a: A) {
let A { a$0: a } = a;
+ _ = a;
}
"#,
r#"
struct A { a: &'static str }
fn f(a: A) {
let A { a } = a;
+ _ = a;
}
"#,
);
@@ -196,12 +201,14 @@ fn f(a: A) {
struct A { a: &'static str, b: &'static str }
fn f(a: A) {
let A { a$0: a, b } = a;
+ _ = (a, b);
}
"#,
r#"
struct A { a: &'static str, b: &'static str }
fn f(a: A) {
let A { a, b } = a;
+ _ = (a, b);
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 9eb763d3e..3b2e15a17 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -31,7 +31,7 @@ pub(crate) fn inactive_code(
let res = Diagnostic::new(
DiagnosticCode::Ra("inactive-code", Severity::WeakWarning),
message,
- ctx.sema.diagnostics_display_range(d.node.clone()).range,
+ ctx.sema.diagnostics_display_range(d.node.clone()),
)
.with_unused(true);
Some(res)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 235062bf5..0f12e814b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -113,6 +113,31 @@ fn some_fn() {
}
"#,
);
+
+ check_fix(
+ r#"
+static S: i32 = M::A;
+
+mod $0M {
+ pub const A: i32 = 10;
+}
+
+mod other {
+ use crate::M::A;
+}
+"#,
+ r#"
+static S: i32 = m::A;
+
+mod m {
+ pub const A: i32 = 10;
+}
+
+mod other {
+ use crate::m::A;
+}
+"#,
+ );
}
#[test]
@@ -175,10 +200,10 @@ fn NonSnakeCaseName() {}
fn incorrect_function_params() {
check_diagnostics(
r#"
-fn foo(SomeParam: u8) {}
+fn foo(SomeParam: u8) { _ = SomeParam; }
// ^^^^^^^^^ 💡 warn: Parameter `SomeParam` should have snake_case name, e.g. `some_param`
-fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
+fn foo2(ok_param: &str, CAPS_PARAM: u8) { _ = (ok_param, CAPS_PARAM); }
// ^^^^^^^^^^ 💡 warn: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
"#,
);
@@ -188,6 +213,7 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
fn incorrect_variable_names() {
check_diagnostics(
r#"
+#[allow(unused)]
fn foo() {
let SOME_VALUE = 10;
// ^^^^^^^^^^ 💡 warn: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
@@ -294,6 +320,7 @@ impl someStruct {
// ^^^^^^^^ 💡 warn: Function `SomeFunc` should have snake_case name, e.g. `some_func`
let WHY_VAR_IS_CAPS = 10;
// ^^^^^^^^^^^^^^^ 💡 warn: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
+ _ = WHY_VAR_IS_CAPS;
}
}
"#,
@@ -306,6 +333,7 @@ impl someStruct {
r#"
enum Option { Some, None }
+#[allow(unused)]
fn main() {
match Option::None {
None => (),
@@ -322,6 +350,7 @@ fn main() {
r#"
enum Option { Some, None }
+#[allow(unused)]
fn main() {
match Option::None {
SOME_VAR @ None => (),
@@ -349,7 +378,9 @@ enum E {
}
mod F {
- fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
+ fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {
+ _ = BAD_NAME_HI;
+ }
}
"#,
);
@@ -395,7 +426,7 @@ fn qualify() {
#[test] // Issue #8809.
fn parenthesized_parameter() {
- check_diagnostics(r#"fn f((O): _) {}"#)
+ check_diagnostics(r#"fn f((O): _) { _ = O; }"#)
}
#[test]
@@ -472,7 +503,9 @@ mod CheckBadStyle {
mod F {
#![allow(non_snake_case)]
- fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
+ fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {
+ _ = BAD_NAME_HI;
+ }
}
#[allow(non_snake_case, non_camel_case_types)]
@@ -510,17 +543,20 @@ fn NonSnakeCaseName(some_var: u8) -> u8 {
#[deny(nonstandard_style)]
mod CheckNonstandardStyle {
+ //^^^^^^^^^^^^^^^^^^^^^ 💡 error: Module `CheckNonstandardStyle` should have snake_case name, e.g. `check_nonstandard_style`
fn HiImABadFnName() {}
//^^^^^^^^^^^^^^ 💡 error: Function `HiImABadFnName` should have snake_case name, e.g. `hi_im_abad_fn_name`
}
#[deny(warnings)]
mod CheckBadStyle {
+ //^^^^^^^^^^^^^ 💡 error: Module `CheckBadStyle` should have snake_case name, e.g. `check_bad_style`
struct fooo;
//^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo`
}
mod F {
+ //^ 💡 warn: Module `F` should have snake_case name, e.g. `f`
#![deny(non_snake_case)]
fn CheckItWorksWithModAttr() {}
//^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr`
@@ -563,12 +599,12 @@ fn main() {
//^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR`
fn BAZ() {
//^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz`
- let INNER_INNER = 42;
- //^^^^^^^^^^^ 💡 warn: Variable `INNER_INNER` should have snake_case name, e.g. `inner_inner`
+ let _INNER_INNER = 42;
+ //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_INNER` should have snake_case name, e.g. `_inner_inner`
}
- let INNER_LOCAL = 42;
- //^^^^^^^^^^^ 💡 warn: Variable `INNER_LOCAL` should have snake_case name, e.g. `inner_local`
+ let _INNER_LOCAL = 42;
+ //^^^^^^^^^^^^ 💡 warn: Variable `_INNER_LOCAL` should have snake_case name, e.g. `_inner_local`
}
}
"#,
@@ -641,4 +677,30 @@ enum E {
"#,
);
}
+
+ #[test]
+ fn module_name_inline() {
+ check_diagnostics(
+ r#"
+mod M {
+ //^ 💡 warn: Module `M` should have snake_case name, e.g. `m`
+ mod IncorrectCase {}
+ //^^^^^^^^^^^^^ 💡 warn: Module `IncorrectCase` should have snake_case name, e.g. `incorrect_case`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn module_name_decl() {
+ check_diagnostics(
+ r#"
+//- /Foo.rs
+
+//- /main.rs
+mod Foo;
+ //^^^ 💡 warn: Module `Foo` should have snake_case name, e.g. `foo`
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
index 1ec17952b..f68f5b44b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
@@ -8,7 +8,7 @@ pub(crate) fn invalid_derive_target(
ctx: &DiagnosticsContext<'_>,
d: &hir::InvalidDeriveTarget,
) -> Diagnostic {
- let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
+ let display_range = ctx.sema.diagnostics_display_range(d.node.clone());
Diagnostic::new(
DiagnosticCode::RustcHardError("E0774"),
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index a337e2660..d330973aa 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -3,7 +3,7 @@
use hir::{PathResolution, Semantics};
use ide_db::{
- base_db::FileId,
+ base_db::{FileId, FileRange},
helpers::mod_path_to_ast,
imports::insert_use::{insert_use, ImportScope},
source_change::SourceChangeBuilder,
@@ -119,7 +119,7 @@ pub(crate) fn json_in_items(
Diagnostic::new(
DiagnosticCode::Ra("json-is-not-rust", Severity::WeakWarning),
"JSON syntax is not valid as a Rust item",
- range,
+ FileRange { file_id, range },
)
.with_fixes(Some(vec![{
let mut scb = SourceChangeBuilder::new(file_id);
@@ -136,6 +136,7 @@ pub(crate) fn json_in_items(
it,
config.insert_use.prefix_kind,
config.prefer_no_std,
+ config.prefer_prelude,
) {
insert_use(&scope, mod_path_to_ast(&it), &config.insert_use);
}
@@ -148,6 +149,7 @@ pub(crate) fn json_in_items(
it,
config.insert_use.prefix_kind,
config.prefer_no_std,
+ config.prefer_prelude,
) {
insert_use(&scope, mod_path_to_ast(&it), &config.insert_use);
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 7ca0a0eab..099de4528 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -60,9 +60,6 @@ macro_rules! compile_error { () => {} }
#[test]
fn eager_macro_concat() {
- // FIXME: this is incorrectly handling `$crate`, resulting in a wrong diagnostic.
- // See: https://github.com/rust-lang/rust-analyzer/issues/10300
-
check_diagnostics(
r#"
//- /lib.rs crate:lib deps:core
@@ -80,7 +77,6 @@ macro_rules! m {
fn f() {
m!();
- //^^^^ error: unresolved macro $crate::private::concat
}
//- /core.rs crate:core
@@ -268,4 +264,24 @@ fn f() {
"#,
)
}
+
+ #[test]
+ fn include_does_not_break_diagnostics() {
+ let mut config = DiagnosticsConfig::test_sample();
+ config.disabled.insert("inactive-code".to_string());
+ config.disabled.insert("unlinked-file".to_string());
+ check_diagnostics_with_config(
+ config,
+ r#"
+//- minicore: include
+//- /lib.rs crate:lib
+include!("include-me.rs");
+//- /include-me.rs
+/// long doc that pushes the diagnostic range beyond the first file's text length
+ #[err]
+//^^^^^^error: unresolved macro `err`
+mod prim_never {}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
index fc57dde69..6202d1585 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs
@@ -7,7 +7,7 @@ pub(crate) fn malformed_derive(
ctx: &DiagnosticsContext<'_>,
d: &hir::MalformedDerive,
) -> Diagnostic {
- let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
+ let display_range = ctx.sema.diagnostics_display_range(d.node.clone());
Diagnostic::new(
DiagnosticCode::RustcHardError("E0777"),
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 8265e0b1c..829601802 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -1,8 +1,9 @@
use either::Either;
use hir::InFile;
+use ide_db::base_db::FileRange;
use syntax::{
ast::{self, HasArgList},
- AstNode, SyntaxNodePtr, TextRange,
+ AstNode, SyntaxNodePtr,
};
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -23,12 +24,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count(
Diagnostic::new(
DiagnosticCode::RustcHardError("E0023"),
message,
- invalid_args_range(
- ctx,
- d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
- d.expected,
- d.found,
- ),
+ invalid_args_range(ctx, d.expr_or_pat.clone().map(Into::into), d.expected, d.found),
)
}
@@ -53,7 +49,7 @@ fn invalid_args_range(
source: InFile<SyntaxNodePtr>,
expected: usize,
found: usize,
-) -> TextRange {
+) -> FileRange {
adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| {
let (text_range, r_paren_token, expected_arg) = match expr {
Either::Left(ast::Expr::CallExpr(call)) => {
@@ -131,7 +127,7 @@ fn f() { zero(); }
fn simple_free_fn_one() {
check_diagnostics(
r#"
-fn one(arg: u8) {}
+fn one(_arg: u8) {}
fn f() { one(); }
//^^ error: expected 1 argument, found 0
"#,
@@ -139,7 +135,7 @@ fn f() { one(); }
check_diagnostics(
r#"
-fn one(arg: u8) {}
+fn one(_arg: u8) {}
fn f() { one(1); }
"#,
);
@@ -176,7 +172,7 @@ fn f() {
check_diagnostics(
r#"
struct S;
-impl S { fn method(&self, arg: u8) {} }
+impl S { fn method(&self, _arg: u8) {} }
fn f() {
S.method();
@@ -187,7 +183,7 @@ impl S { fn method(&self, arg: u8) {} }
check_diagnostics(
r#"
struct S;
-impl S { fn method(&self, arg: u8) {} }
+impl S { fn method(&self, _arg: u8) {} }
fn f() {
S::method(&S, 0);
@@ -335,8 +331,8 @@ struct S;
impl S {
fn method(#[cfg(NEVER)] self) {}
- fn method2(#[cfg(NEVER)] self, arg: u8) {}
- fn method3(self, #[cfg(NEVER)] arg: u8) {}
+ fn method2(#[cfg(NEVER)] self, _arg: u8) {}
+ fn method3(self, #[cfg(NEVER)] _arg: u8) {}
}
extern "C" {
@@ -365,8 +361,8 @@ fn main() {
r#"
#[rustc_legacy_const_generics(1, 3)]
fn mixed<const N1: &'static str, const N2: bool>(
- a: u8,
- b: i8,
+ _a: u8,
+ _b: i8,
) {}
fn f() {
@@ -376,8 +372,8 @@ fn f() {
#[rustc_legacy_const_generics(1, 3)]
fn b<const N1: u8, const N2: u8>(
- a: u8,
- b: u8,
+ _a: u8,
+ _b: u8,
) {}
fn g() {
@@ -403,7 +399,7 @@ fn f(
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
S(e, f, .., g, d): S
// ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
-) {}
+) { _ = (a, b, c, d, e, f, g); }
"#,
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index acc31cd11..cb38bc54d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -1,7 +1,7 @@
use either::Either;
use hir::{
db::{ExpandDatabase, HirDatabase},
- known, AssocItem, HirDisplay, InFile, Type,
+ known, AssocItem, HirDisplay, HirFileIdExt, InFile, Type,
};
use ide_db::{
assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
@@ -39,7 +39,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField
d.field_list_parent_path
.clone()
.map(SyntaxNodePtr::from)
- .unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
+ .unwrap_or_else(|| d.field_list_parent.clone().into()),
);
Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr)
@@ -58,10 +58,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let root = ctx.sema.db.parse_or_expand(d.file);
- let current_module = match &d.field_list_parent {
- Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
- Either::Right(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
- };
+ let current_module =
+ ctx.sema.scope(d.field_list_parent.to_node(&root).syntax()).map(|it| it.module());
let build_text_edit = |parent_syntax, new_syntax: &SyntaxNode, old_syntax| {
let edit = {
@@ -87,9 +85,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
)])
};
- match &d.field_list_parent {
- Either::Left(record_expr) => {
- let field_list_parent = record_expr.to_node(&root);
+ match &d.field_list_parent.to_node(&root) {
+ Either::Left(field_list_parent) => {
let missing_fields = ctx.sema.record_literal_missing_fields(&field_list_parent);
let mut locals = FxHashMap::default();
@@ -125,6 +122,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
ctx.sema.db,
item_for_path_search(ctx.sema.db, item_in_ns)?,
ctx.config.prefer_no_std,
+ ctx.config.prefer_prelude,
)?;
use_trivial_constructor(
@@ -152,8 +150,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
old_field_list.syntax(),
)
}
- Either::Right(record_pat) => {
- let field_list_parent = record_pat.to_node(&root);
+ Either::Right(field_list_parent) => {
let missing_fields = ctx.sema.record_pattern_missing_fields(&field_list_parent);
let old_field_list = field_list_parent.record_pat_field_list()?;
@@ -290,6 +287,7 @@ fn x(a: S) {
struct S { s: u32 }
fn x(a: S) {
let S { ref s } = a;
+ _ = s;
}
",
)
@@ -626,7 +624,7 @@ struct TestStruct { one: i32, two: i64 }
fn test_fn() {
let one = 1;
- let s = TestStruct{ one, two: 2 };
+ let _s = TestStruct{ one, two: 2 };
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 06b03d3d1..ef6a273ed 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -17,14 +17,32 @@ pub(crate) fn missing_match_arms(
#[cfg(test)]
mod tests {
- use crate::tests::check_diagnostics;
+ use crate::{
+ tests::{check_diagnostics, check_diagnostics_with_config},
+ DiagnosticsConfig,
+ };
+ #[track_caller]
fn check_diagnostics_no_bails(ra_fixture: &str) {
cov_mark::check_count!(validate_match_bailed_out, 0);
crate::tests::check_diagnostics(ra_fixture)
}
#[test]
+ fn empty_body() {
+ let mut config = DiagnosticsConfig::test_sample();
+ config.disabled.insert("syntax-error".to_string());
+ check_diagnostics_with_config(
+ config,
+ r#"
+fn main() {
+ match 0;
+}
+"#,
+ );
+ }
+
+ #[test]
fn empty_tuple() {
check_diagnostics_no_bails(
r#"
@@ -564,6 +582,7 @@ fn bang(never: !) {
r#"
enum Option<T> { Some(T), None }
+#[allow(unused)]
fn main() {
// `Never` is deliberately not defined so that it's an uninferred type.
match Option::<Never>::None {
@@ -719,7 +738,7 @@ fn main() {
r#"
struct S { a: char}
fn main(v: S) {
- match v { S{ a } => {} }
+ match v { S{ a } => { _ = a; } }
match v { S{ a: _x } => {} }
match v { S{ a: 'a' } => {} }
match v { S{..} => {} }
@@ -901,7 +920,7 @@ enum E{ A, B }
fn foo() {
match &E::A {
E::A => {}
- x => {}
+ _x => {}
}
}",
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 70b26009b..f93a35cf1 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -1,4 +1,5 @@
use hir::db::ExpandDatabase;
+use hir::HirFileIdExt;
use ide_db::{assists::Assist, source_change::SourceChange};
use syntax::{ast, SyntaxNode};
use syntax::{match_ast, AstNode};
@@ -100,9 +101,9 @@ mod tests {
r#"
fn main() {
let x = &5 as *const usize;
- unsafe { let y = *x; }
- let z = *x;
-} //^^💡 error: this operation is unsafe and requires an unsafe function or block
+ unsafe { let _y = *x; }
+ let _z = *x;
+} //^^💡 error: this operation is unsafe and requires an unsafe function or block
"#,
)
}
@@ -116,13 +117,13 @@ struct HasUnsafe;
impl HasUnsafe {
unsafe fn unsafe_fn(&self) {
let x = &5 as *const usize;
- let y = *x;
+ let _y = *x;
}
}
unsafe fn unsafe_fn() {
let x = &5 as *const usize;
- let y = *x;
+ let _y = *x;
}
fn main() {
@@ -152,10 +153,10 @@ struct Ty {
static mut STATIC_MUT: Ty = Ty { a: 0 };
fn main() {
- let x = STATIC_MUT.a;
- //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ let _x = STATIC_MUT.a;
+ //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
unsafe {
- let x = STATIC_MUT.a;
+ let _x = STATIC_MUT.a;
}
}
"#,
@@ -187,13 +188,13 @@ fn main() {
r#"
fn main() {
let x = &5 as *const usize;
- let z = *x$0;
+ let _z = *x$0;
}
"#,
r#"
fn main() {
let x = &5 as *const usize;
- let z = unsafe { *x };
+ let _z = unsafe { *x };
}
"#,
);
@@ -231,7 +232,7 @@ struct S(usize);
impl S {
unsafe fn func(&self) {
let x = &self.0 as *const usize;
- let z = *x;
+ let _z = *x;
}
}
fn main() {
@@ -244,7 +245,7 @@ struct S(usize);
impl S {
unsafe fn func(&self) {
let x = &self.0 as *const usize;
- let z = *x;
+ let _z = *x;
}
}
fn main() {
@@ -267,7 +268,7 @@ struct Ty {
static mut STATIC_MUT: Ty = Ty { a: 0 };
fn main() {
- let x = STATIC_MUT$0.a;
+ let _x = STATIC_MUT$0.a;
}
"#,
r#"
@@ -278,7 +279,7 @@ struct Ty {
static mut STATIC_MUT: Ty = Ty { a: 0 };
fn main() {
- let x = unsafe { STATIC_MUT.a };
+ let _x = unsafe { STATIC_MUT.a };
}
"#,
)
@@ -382,16 +383,16 @@ fn main() {
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x;
- x = STATIC_MUT$0;
+ let _x;
+ _x = STATIC_MUT$0;
}
"#,
r#"
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x;
- x = unsafe { STATIC_MUT };
+ let _x;
+ _x = unsafe { STATIC_MUT };
}
"#,
)
@@ -405,14 +406,14 @@ fn main() {
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = STATIC_MUT$0 + 1;
+ let _x = STATIC_MUT$0 + 1;
}
"#,
r#"
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = unsafe { STATIC_MUT } + 1;
+ let _x = unsafe { STATIC_MUT } + 1;
}
"#,
)
@@ -425,14 +426,14 @@ fn main() {
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = &STATIC_MUT$0;
+ let _x = &STATIC_MUT$0;
}
"#,
r#"
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = unsafe { &STATIC_MUT };
+ let _x = unsafe { &STATIC_MUT };
}
"#,
)
@@ -445,14 +446,14 @@ fn main() {
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = &&STATIC_MUT$0;
+ let _x = &&STATIC_MUT$0;
}
"#,
r#"
static mut STATIC_MUT: u8 = 0;
fn main() {
- let x = unsafe { &&STATIC_MUT };
+ let _x = unsafe { &&STATIC_MUT };
}
"#,
)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 3aa4aa970..886aefeb5 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -29,6 +29,7 @@ fn main() {
let a = &X;
let b = *a;
//^ error: cannot move `X` out of reference
+ _ = b;
}
"#,
);
@@ -46,6 +47,7 @@ fn main() {
let b = a.0;
//^ error: cannot move `X` out of reference
let y = a.1;
+ _ = (b, y);
}
"#,
);
@@ -59,8 +61,8 @@ fn main() {
struct X;
fn main() {
static S: X = X;
- let s = S;
- //^ error: cannot move `X` out of reference
+ let _s = S;
+ //^^ error: cannot move `X` out of reference
}
"#,
);
@@ -165,7 +167,7 @@ enum X {
fn main() {
let x = &X::Bar;
- let c = || match *x {
+ let _c = || match *x {
X::Foo(t) => t,
_ => 5,
};
@@ -173,4 +175,19 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn regression_15787() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, slice, copy
+fn foo(mut slice: &[u32]) -> usize {
+ slice = match slice {
+ [0, rest @ ..] | rest => rest,
+ };
+ slice.len()
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index d056e5c85..187511149 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -324,6 +324,7 @@ fn main() {
let x_own = 2;
let ref mut x_ref = x_own;
//^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own`
+ _ = x_ref;
}
"#,
);
@@ -331,7 +332,7 @@ fn main() {
r#"
struct Foo;
impl Foo {
- fn method(&mut self, x: i32) {}
+ fn method(&mut self, _x: i32) {}
}
fn main() {
let x = Foo;
@@ -391,6 +392,7 @@ fn main() {
//^^^^^ 💡 warn: variable does not need to be mutable
x = 7;
//^^^^^ 💡 error: cannot mutate immutable variable `x`
+ _ = y;
}
}
}
@@ -404,12 +406,14 @@ fn main() {
// there would be no mutability 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.
+
+ // Update: now MIR based `unused-variable` is taking over `unused-mut` for the same reason.
check_diagnostics(
r#"
fn main() {
return;
let mut x = 2;
- //^^^^^ 💡 warn: variable does not need to be mutable
+ //^^^^^ warn: unused variable
&mut x;
}
"#,
@@ -419,7 +423,7 @@ fn main() {
fn main() {
loop {}
let mut x = 2;
- //^^^^^ 💡 warn: variable does not need to be mutable
+ //^^^^^ warn: unused variable
&mut x;
}
"#,
@@ -440,7 +444,7 @@ fn main(b: bool) {
g();
}
let mut x = 2;
- //^^^^^ 💡 warn: variable does not need to be mutable
+ //^^^^^ warn: unused variable
&mut x;
}
"#,
@@ -454,7 +458,7 @@ fn main(b: bool) {
return;
}
let mut x = 2;
- //^^^^^ 💡 warn: variable does not need to be mutable
+ //^^^^^ warn: unused variable
&mut x;
}
"#,
@@ -536,6 +540,7 @@ fn main() {
(k @ 5, ref mut t) if { continue; } => {
//^^^^^^^^^ 💡 error: cannot mutate immutable variable `z`
*t = 5;
+ _ = k;
}
_ => {
let y = (1, 2);
@@ -588,6 +593,7 @@ fn main() {
b = 1;
c = (2, 3);
d = 3;
+ _ = (c, b, d);
}
}
"#,
@@ -600,6 +606,7 @@ fn main() {
r#"
fn f(mut x: i32) {
//^^^^^ 💡 warn: variable does not need to be mutable
+ f(x + 2);
}
"#,
);
@@ -615,8 +622,11 @@ fn f(x: i32) {
r#"
fn f((x, y): (i32, i32)) {
let t = [0; 2];
- x = 5;
- //^^^^^ 💡 error: cannot mutate immutable variable `x`
+ x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+ _ = x;
+ _ = y;
+ _ = t;
}
"#,
);
@@ -645,6 +655,7 @@ fn f(x: [(i32, u8); 10]) {
//^^^^^ 💡 warn: variable does not need to be mutable
a = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
+ _ = b;
}
}
"#,
@@ -666,6 +677,7 @@ fn f(x: [(i32, u8); 10]) {
//^^^^^ 💡 error: cannot mutate immutable variable `a`
c = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `c`
+ _ = (b, d);
}
}
}
@@ -696,18 +708,18 @@ fn f() {
fn overloaded_index() {
check_diagnostics(
r#"
-//- minicore: index
+//- minicore: index, copy
use core::ops::{Index, IndexMut};
struct Foo;
impl Index<usize> for Foo {
type Output = (i32, u8);
- fn index(&self, index: usize) -> &(i32, u8) {
+ fn index(&self, _index: usize) -> &(i32, u8) {
&(5, 2)
}
}
impl IndexMut<usize> for Foo {
- fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
+ fn index_mut(&mut self, _index: usize) -> &mut (i32, u8) {
&mut (5, 2)
}
}
@@ -715,26 +727,32 @@ fn f() {
let mut x = Foo;
//^^^^^ 💡 warn: variable does not need to be mutable
let y = &x[2];
+ _ = (x, y);
let x = Foo;
let y = &mut x[2];
//^💡 error: cannot mutate immutable variable `x`
+ _ = (x, y);
let mut x = &mut Foo;
//^^^^^ 💡 warn: variable does not need to be mutable
let y: &mut (i32, u8) = &mut x[2];
+ _ = (x, y);
let x = Foo;
let ref mut y = x[7];
//^ 💡 error: cannot mutate immutable variable `x`
+ _ = (x, y);
let (ref mut y, _) = x[3];
//^ 💡 error: cannot mutate immutable variable `x`
+ _ = y;
match x[10] {
//^ 💡 error: cannot mutate immutable variable `x`
- (ref y, _) => (),
- (_, ref mut y) => (),
+ (ref y, 5) => _ = y,
+ (_, ref mut y) => _ = y,
}
let mut x = Foo;
let mut i = 5;
//^^^^^ 💡 warn: variable does not need to be mutable
let y = &mut x[i];
+ _ = y;
}
"#,
);
@@ -744,7 +762,7 @@ fn f() {
fn overloaded_deref() {
check_diagnostics(
r#"
-//- minicore: deref_mut
+//- minicore: deref_mut, copy
use core::ops::{Deref, DerefMut};
struct Foo;
@@ -763,21 +781,27 @@ fn f() {
let mut x = Foo;
//^^^^^ 💡 warn: variable does not need to be mutable
let y = &*x;
+ _ = (x, y);
let x = Foo;
let y = &mut *x;
//^^ 💡 error: cannot mutate immutable variable `x`
+ _ = (x, y);
let x = Foo;
+ //^ warn: unused variable
let x = Foo;
let y: &mut (i32, u8) = &mut x;
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ _ = (x, y);
let ref mut y = *x;
//^^ 💡 error: cannot mutate immutable variable `x`
+ _ = y;
let (ref mut y, _) = *x;
//^^ 💡 error: cannot mutate immutable variable `x`
+ _ = y;
match *x {
//^^ 💡 error: cannot mutate immutable variable `x`
- (ref y, _) => (),
- (_, ref mut y) => (),
+ (ref y, 5) => _ = y,
+ (_, ref mut y) => _ = y,
}
}
"#,
@@ -866,6 +890,7 @@ pub fn test() {
data: 0
}
);
+ _ = tree;
}
"#,
);
@@ -925,6 +950,7 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
let x = X;
let closure4 = || { x.mutate(); };
//^ 💡 error: cannot mutate immutable variable `x`
+ _ = (closure2, closure3, closure4);
}
"#,
);
@@ -941,7 +967,9 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
z = 3;
let mut k = z;
//^^^^^ 💡 warn: variable does not need to be mutable
+ _ = k;
};
+ _ = (x, closure);
}
"#,
);
@@ -958,6 +986,7 @@ fn f() {
}
}
};
+ _ = closure;
}
"#,
);
@@ -972,7 +1001,8 @@ fn f() {
let mut x = X;
let c2 = || { x = X; x };
let mut x = X;
- let c2 = move || { x = X; };
+ let c3 = move || { x = X; };
+ _ = (c1, c2, c3);
}
"#,
);
@@ -1023,7 +1053,7 @@ fn x(t: &[u8]) {
a = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
-
+ _ = b;
}
_ => {}
}
@@ -1079,6 +1109,7 @@ fn f() {
let x = Box::new(5);
let closure = || *x = 2;
//^ 💡 error: cannot mutate immutable variable `x`
+ _ = closure;
}
"#,
);
@@ -1156,6 +1187,7 @@ macro_rules! mac {
fn main2() {
let mut x = mac![];
//^^^^^ 💡 warn: variable does not need to be mutable
+ _ = x;
}
"#,
);
@@ -1196,4 +1228,20 @@ fn foo(mut foo: Foo) {
"#,
);
}
+
+ #[test]
+ fn regression_15670() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+
+pub struct A {}
+pub unsafe fn foo(a: *mut A) {
+ let mut b = || -> *mut A { &mut *a };
+ //^^^^^ 💡 warn: variable does not need to be mutable
+ let _ = b();
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 290c16c9d..0abcbffe7 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,5 +1,5 @@
use either::Either;
-use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics};
+use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics};
use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
use syntax::{
ast::{self, edit::IndentLevel, make},
@@ -13,7 +13,7 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if created structure does not have field provided in record.
pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
- let node = d.field.clone().map(|it| it.either(Into::into, Into::into));
+ let node = d.field.clone().map(Into::into);
if d.private {
// FIXME: quickfix to add required visibility
Diagnostic::new_with_syntax_node_ptr(
@@ -35,15 +35,13 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
// FIXME: quickfix for pattern
- match &d.field.value {
- Either::Left(ptr) => {
- let root = ctx.sema.db.parse_or_expand(d.field.file_id);
- missing_record_expr_field_fixes(
- &ctx.sema,
- d.field.file_id.original_file(ctx.sema.db),
- &ptr.to_node(&root),
- )
- }
+ let root = ctx.sema.db.parse_or_expand(d.field.file_id);
+ match &d.field.value.to_node(&root) {
+ Either::Left(node) => missing_record_expr_field_fixes(
+ &ctx.sema,
+ d.field.file_id.original_file(ctx.sema.db),
+ node,
+ ),
_ => None,
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index c44d28e77..a828b8b4f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -1,5 +1,3 @@
-use either::Either;
-
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: private-assoc-item
@@ -28,13 +26,7 @@ pub(crate) fn private_assoc_item(
},
name,
),
- d.expr_or_pat.clone().map(|it| match it {
- Either::Left(it) => it.into(),
- Either::Right(it) => match it {
- Either::Left(it) => it.into(),
- Either::Right(it) => it.into(),
- },
- }),
+ d.expr_or_pat.clone().map(Into::into),
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 083ef3e8d..258ac6cd8 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -1,4 +1,4 @@
-use hir::{db::ExpandDatabase, InFile};
+use hir::{db::ExpandDatabase, HirFileIdExt, InFile};
use ide_db::source_change::SourceChange;
use syntax::{
ast::{self, HasArgList},
@@ -74,8 +74,8 @@ mod tests {
r#"
//- minicore: iterators
fn foo() {
- let m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
-} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
+ let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
+} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
"#,
);
}
@@ -117,7 +117,7 @@ fn foo() {
fn foo() {
let mut m = core::iter::repeat(())
.filter_map(|()| Some(92));
- let n = m.next();
+ let _n = m.next();
}
"#,
);
@@ -148,22 +148,22 @@ fn foo() {
fn foo() {
#[allow(clippy::filter_map_next)]
- let m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
+ let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
}
#[deny(clippy::filter_map_next)]
fn foo() {
- let m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
-} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..)
+ let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
+} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: replace filter_map(..).next() with find_map(..)
fn foo() {
- let m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
-} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
+ let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
+} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 weak: replace filter_map(..).next() with find_map(..)
#[warn(clippy::filter_map_next)]
fn foo() {
- let m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
-} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..)
+ let _m = core::iter::repeat(()).filter_map(|()| Some(92)).next();
+} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 💡 warn: replace filter_map(..).next() with find_map(..)
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
new file mode 100644
index 000000000..251a64529
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
@@ -0,0 +1,129 @@
+use hir::InFile;
+use syntax::ast;
+
+use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
+
+// Diagnostic: trait-impl-incorrect-safety
+//
+// Diagnoses incorrect safety annotations of trait impls.
+pub(crate) fn trait_impl_incorrect_safety(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TraitImplIncorrectSafety,
+) -> Diagnostic {
+ Diagnostic::new(
+ DiagnosticCode::Ra("trait-impl-incorrect-safety", Severity::Error),
+ if d.should_be_safe {
+ "unsafe impl for safe trait"
+ } else {
+ "impl for unsafe trait needs to be unsafe"
+ },
+ adjusted_display_range::<ast::Impl>(
+ ctx,
+ InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() },
+ &|impl_| {
+ if d.should_be_safe {
+ Some(match (impl_.unsafe_token(), impl_.impl_token()) {
+ (None, None) => return None,
+ (None, Some(t)) | (Some(t), None) => t.text_range(),
+ (Some(t1), Some(t2)) => t1.text_range().cover(t2.text_range()),
+ })
+ } else {
+ impl_.impl_token().map(|t| t.text_range())
+ }
+ },
+ ),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn simple() {
+ check_diagnostics(
+ r#"
+trait Safe {}
+unsafe trait Unsafe {}
+
+ impl Safe for () {}
+
+ impl Unsafe for () {}
+//^^^^ error: impl for unsafe trait needs to be unsafe
+
+ unsafe impl Safe for () {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+
+ unsafe impl Unsafe for () {}
+"#,
+ );
+ }
+
+ #[test]
+ fn drop_may_dangle() {
+ check_diagnostics(
+ r#"
+#[lang = "drop"]
+trait Drop {}
+struct S<T>;
+struct L<'l>;
+
+ impl<T> Drop for S<T> {}
+
+ impl<#[may_dangle] T> Drop for S<T> {}
+//^^^^ error: impl for unsafe trait needs to be unsafe
+
+ unsafe impl<T> Drop for S<T> {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+
+ unsafe impl<#[may_dangle] T> Drop for S<T> {}
+
+ impl<'l> Drop for L<'l> {}
+
+ impl<#[may_dangle] 'l> Drop for L<'l> {}
+//^^^^ error: impl for unsafe trait needs to be unsafe
+
+ unsafe impl<'l> Drop for L<'l> {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+
+ unsafe impl<#[may_dangle] 'l> Drop for L<'l> {}
+"#,
+ );
+ }
+
+ #[test]
+ fn negative() {
+ check_diagnostics(
+ r#"
+trait Trait {}
+
+ impl !Trait for () {}
+
+ unsafe impl !Trait for () {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+
+unsafe trait UnsafeTrait {}
+
+ impl !UnsafeTrait for () {}
+
+ unsafe impl !UnsafeTrait for () {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+
+"#,
+ );
+ }
+
+ #[test]
+ fn inherent() {
+ check_diagnostics(
+ r#"
+struct S;
+
+ impl S {}
+
+ unsafe impl S {}
+//^^^^^^^^^^^ error: unsafe impl for safe trait
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
new file mode 100644
index 000000000..56188cddf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
@@ -0,0 +1,129 @@
+use hir::InFile;
+use itertools::Itertools;
+use syntax::{ast, AstNode};
+
+use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: trait-impl-missing-assoc_item
+//
+// Diagnoses missing trait items in a trait impl.
+pub(crate) fn trait_impl_missing_assoc_item(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TraitImplMissingAssocItems,
+) -> Diagnostic {
+ let missing = d.missing.iter().format_with(", ", |(name, item), f| {
+ f(&match *item {
+ hir::AssocItem::Function(_) => "`fn ",
+ hir::AssocItem::Const(_) => "`const ",
+ hir::AssocItem::TypeAlias(_) => "`type ",
+ })?;
+ f(&name.display(ctx.sema.db))?;
+ f(&"`")
+ });
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0046"),
+ format!("not all trait items implemented, missing: {missing}"),
+ adjusted_display_range::<ast::Impl>(
+ ctx,
+ InFile { file_id: d.file_id, value: d.impl_.syntax_node_ptr() },
+ &|impl_| impl_.trait_().map(|t| t.syntax().text_range()),
+ ),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn trait_with_default_value() {
+ check_diagnostics(
+ r#"
+trait Marker {
+ const FLAG: bool = false;
+}
+struct Foo;
+impl Marker for Foo {}
+ "#,
+ )
+ }
+
+ #[test]
+ fn simple() {
+ check_diagnostics(
+ r#"
+trait Trait {
+ const C: ();
+ type T;
+ fn f();
+}
+
+impl Trait for () {
+ const C: () = ();
+ type T = ();
+ fn f() {}
+}
+
+impl Trait for () {
+ //^^^^^ error: not all trait items implemented, missing: `const C`
+ type T = ();
+ fn f() {}
+}
+
+impl Trait for () {
+ //^^^^^ error: not all trait items implemented, missing: `const C`, `type T`, `fn f`
+}
+
+"#,
+ );
+ }
+
+ #[test]
+ fn default() {
+ check_diagnostics(
+ r#"
+trait Trait {
+ const C: ();
+ type T = ();
+ fn f() {}
+}
+
+impl Trait for () {
+ const C: () = ();
+ type T = ();
+ fn f() {}
+}
+
+impl Trait for () {
+ //^^^^^ error: not all trait items implemented, missing: `const C`
+ type T = ();
+ fn f() {}
+}
+
+impl Trait for () {
+ //^^^^^ error: not all trait items implemented, missing: `const C`
+ type T = ();
+ }
+
+impl Trait for () {
+ //^^^^^ error: not all trait items implemented, missing: `const C`
+}
+
+"#,
+ );
+ }
+
+ #[test]
+ fn negative_impl() {
+ check_diagnostics(
+ r#"
+trait Trait {
+ fn item();
+}
+
+// Negative impls don't require any items (in fact, the forbid providing any)
+impl !Trait for () {}
+"#,
+ )
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
new file mode 100644
index 000000000..159d87d26
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
@@ -0,0 +1,106 @@
+use hir::InFile;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: trait-impl-orphan
+//
+// Only traits defined in the current crate can be implemented for arbitrary types
+pub(crate) fn trait_impl_orphan(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TraitImplOrphan,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0117"),
+ format!("only traits defined in the current crate can be implemented for arbitrary types"),
+ InFile::new(d.file_id, d.impl_.clone().into()),
+ )
+ // Not yet checked for false positives
+ .experimental()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn simple() {
+ check_diagnostics(
+ r#"
+//- /foo.rs crate:foo
+pub trait Foo {}
+//- /bar.rs crate:bar
+pub struct Bar;
+//- /main.rs crate:main deps:foo,bar
+struct LocalType;
+trait LocalTrait {}
+ impl foo::Foo for bar::Bar {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types
+impl foo::Foo for LocalType {}
+impl LocalTrait for bar::Bar {}
+"#,
+ );
+ }
+
+ #[test]
+ fn generics() {
+ check_diagnostics(
+ r#"
+//- /foo.rs crate:foo
+pub trait Foo<T> {}
+//- /bar.rs crate:bar
+pub struct Bar<T>(T);
+//- /main.rs crate:main deps:foo,bar
+struct LocalType<T>;
+trait LocalTrait<T> {}
+ impl<T> foo::Foo<T> for bar::Bar<T> {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types
+
+ impl<T> foo::Foo<T> for bar::Bar<LocalType<T>> {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types
+
+ impl<T> foo::Foo<LocalType<T>> for bar::Bar<T> {}
+
+ impl<T> foo::Foo<bar::Bar<LocalType<T>>> for bar::Bar<LocalType<T>> {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types
+"#,
+ );
+ }
+
+ #[test]
+ fn fundamental() {
+ check_diagnostics(
+ r#"
+//- /foo.rs crate:foo
+pub trait Foo<T> {}
+//- /bar.rs crate:bar
+pub struct Bar<T>(T);
+#[lang = "owned_box"]
+#[fundamental]
+pub struct Box<T>(T);
+//- /main.rs crate:main deps:foo,bar
+struct LocalType;
+ impl<T> foo::Foo<T> for bar::Box<T> {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: only traits defined in the current crate can be implemented for arbitrary types
+ impl<T> foo::Foo<T> for &LocalType {}
+ impl<T> foo::Foo<T> for bar::Box<LocalType> {}
+"#,
+ );
+ }
+
+ #[test]
+ fn dyn_object() {
+ check_diagnostics(
+ r#"
+//- /foo.rs crate:foo
+pub trait Foo<T> {}
+//- /bar.rs crate:bar
+pub struct Bar;
+//- /main.rs crate:main deps:foo,bar
+trait LocalTrait {}
+impl<T> foo::Foo<T> for dyn LocalTrait {}
+impl<T> foo::Foo<dyn LocalTrait> for Bar {}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
new file mode 100644
index 000000000..820014391
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -0,0 +1,79 @@
+use hir::{Const, Function, HasSource, TypeAlias};
+use ide_db::base_db::FileRange;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: trait-impl-redundant-assoc_item
+//
+// Diagnoses redundant trait items in a trait impl.
+pub(crate) fn trait_impl_redundant_assoc_item(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TraitImplRedundantAssocItems,
+) -> Diagnostic {
+ let name = d.assoc_item.0.clone();
+ let assoc_item = d.assoc_item.1;
+ let db = ctx.sema.db;
+
+ let default_range = d.impl_.syntax_node_ptr().text_range();
+ let trait_name = d.trait_.name(db).to_smol_str();
+
+ let (redundant_item_name, diagnostic_range) = match assoc_item {
+ hir::AssocItem::Function(id) => (
+ format!("`fn {}`", name.display(db)),
+ Function::from(id)
+ .source(db)
+ .map(|it| it.syntax().value.text_range())
+ .unwrap_or(default_range),
+ ),
+ hir::AssocItem::Const(id) => (
+ format!("`const {}`", name.display(db)),
+ Const::from(id)
+ .source(db)
+ .map(|it| it.syntax().value.text_range())
+ .unwrap_or(default_range),
+ ),
+ hir::AssocItem::TypeAlias(id) => (
+ format!("`type {}`", name.display(db)),
+ TypeAlias::from(id)
+ .source(db)
+ .map(|it| it.syntax().value.text_range())
+ .unwrap_or(default_range),
+ ),
+ };
+
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0407"),
+ format!("{redundant_item_name} is not a member of trait `{trait_name}`"),
+ FileRange { file_id: d.file_id.file_id().unwrap(), range: diagnostic_range },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn trait_with_default_value() {
+ check_diagnostics(
+ r#"
+trait Marker {
+ const FLAG: bool = false;
+ fn boo();
+ fn foo () {}
+}
+struct Foo;
+impl Marker for Foo {
+ type T = i32;
+ //^^^^^^^^^^^^^ error: `type T` is not a member of trait `Marker`
+
+ const FLAG: bool = true;
+
+ fn bar() {}
+ //^^^^^^^^^^^ error: `fn bar` is not a member of trait `Marker`
+
+ fn boo() {}
+}
+ "#,
+ )
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 15bd28c00..70beb9468 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,4 @@
-use either::Either;
-use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type};
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
ast::{self, BlockExpr, ExprStmt},
@@ -14,9 +13,11 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, Dia
// 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 = match &d.expr_or_pat {
- Either::Left(expr) => {
- adjusted_display_range::<ast::Expr>(ctx, expr.clone().map(|it| it.into()), &|expr| {
+ let display_range = match &d.expr_or_pat.value {
+ expr if ast::Expr::can_cast(expr.kind()) => adjusted_display_range::<ast::Expr>(
+ ctx,
+ InFile { file_id: d.expr_or_pat.file_id, value: expr.syntax_node_ptr() },
+ &|expr| {
let salient_token_range = match expr {
ast::Expr::IfExpr(it) => it.if_token()?.text_range(),
ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(),
@@ -32,11 +33,12 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
cov_mark::hit!(type_mismatch_range_adjustment);
Some(salient_token_range)
- })
- }
- Either::Right(pat) => {
- ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
- }
+ },
+ ),
+ pat => ctx.sema.diagnostics_display_range(InFile {
+ file_id: d.expr_or_pat.file_id,
+ value: pat.syntax_node_ptr(),
+ }),
};
let mut diag = Diagnostic::new(
DiagnosticCode::RustcHardError("E0308"),
@@ -57,14 +59,12 @@ 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();
- 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 let Some(expr_ptr) = d.expr_or_pat.value.clone().cast::<ast::Expr>() {
+ let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr.clone() };
+ 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);
}
if fixes.is_empty() {
@@ -80,7 +80,7 @@ fn add_reference(
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range;
+ let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into()));
let (_, mutability) = d.expected.as_reference()?;
let actual_with_ref = Type::reference(&d.actual, mutability);
@@ -90,10 +90,9 @@ fn add_reference(
let ampersands = format!("&{}", mutability.as_keyword_for_ref());
- let edit = TextEdit::insert(range.start(), ampersands);
- let source_change =
- 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));
+ let edit = TextEdit::insert(range.range.start(), ampersands);
+ let source_change = SourceChange::from_text_edit(range.file_id, edit);
+ acc.push(fix("add_reference_here", "Add reference here", source_change, range.range));
Some(())
}
@@ -205,7 +204,7 @@ fn main() {
test(123);
//^^^ 💡 error: expected &i32, found i32
}
-fn test(arg: &i32) {}
+fn test(_arg: &i32) {}
"#,
);
}
@@ -217,13 +216,13 @@ fn test(arg: &i32) {}
fn main() {
test(123$0);
}
-fn test(arg: &i32) {}
+fn test(_arg: &i32) {}
"#,
r#"
fn main() {
test(&123);
}
-fn test(arg: &i32) {}
+fn test(_arg: &i32) {}
"#,
);
}
@@ -235,13 +234,13 @@ fn test(arg: &i32) {}
fn main() {
test($0123);
}
-fn test(arg: &mut i32) {}
+fn test(_arg: &mut i32) {}
"#,
r#"
fn main() {
test(&mut 123);
}
-fn test(arg: &mut i32) {}
+fn test(_arg: &mut i32) {}
"#,
);
}
@@ -254,13 +253,13 @@ fn test(arg: &mut i32) {}
fn main() {
test($0[1, 2, 3]);
}
-fn test(arg: &[i32]) {}
+fn test(_arg: &[i32]) {}
"#,
r#"
fn main() {
test(&[1, 2, 3]);
}
-fn test(arg: &[i32]) {}
+fn test(_arg: &[i32]) {}
"#,
);
}
@@ -274,24 +273,26 @@ struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
type Target = Bar;
+ fn deref(&self) -> &Self::Target { loop {} }
}
fn main() {
test($0Foo);
}
-fn test(arg: &Bar) {}
+fn test(_arg: &Bar) {}
"#,
r#"
struct Foo;
struct Bar;
impl core::ops::Deref for Foo {
type Target = Bar;
+ fn deref(&self) -> &Self::Target { loop {} }
}
fn main() {
test(&Foo);
}
-fn test(arg: &Bar) {}
+fn test(_arg: &Bar) {}
"#,
);
}
@@ -305,7 +306,7 @@ fn main() {
}
struct Test;
impl Test {
- fn call_by_ref(&self, arg: &i32) {}
+ fn call_by_ref(&self, _arg: &i32) {}
}
"#,
r#"
@@ -314,7 +315,7 @@ fn main() {
}
struct Test;
impl Test {
- fn call_by_ref(&self, arg: &i32) {}
+ fn call_by_ref(&self, _arg: &i32) {}
}
"#,
);
@@ -345,7 +346,7 @@ macro_rules! thousand {
1000_u64
};
}
-fn test(foo: &u64) {}
+fn test(_foo: &u64) {}
fn main() {
test($0thousand!());
}
@@ -356,7 +357,7 @@ macro_rules! thousand {
1000_u64
};
}
-fn test(foo: &u64) {}
+fn test(_foo: &u64) {}
fn main() {
test(&thousand!());
}
@@ -369,12 +370,12 @@ fn main() {
check_fix(
r#"
fn main() {
- let test: &mut i32 = $0123;
+ let _test: &mut i32 = $0123;
}
"#,
r#"
fn main() {
- let test: &mut i32 = &mut 123;
+ let _test: &mut i32 = &mut 123;
}
"#,
);
@@ -411,7 +412,7 @@ fn div(x: i32, y: i32) -> Option<i32> {
fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
loop {}
}
- fn run(t: Rate<5>) {
+ fn run(_t: Rate<5>) {
}
fn main() {
run(f()) // FIXME: remove this error
@@ -426,7 +427,7 @@ fn div(x: i32, y: i32) -> Option<i32> {
check_diagnostics(
r#"
pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
- fn run(t: Rate<u32, 1, 1>) {
+ fn run(_t: Rate<u32, 1, 1>) {
}
fn main() {
run(Rate::<_, _, _>(5));
@@ -650,7 +651,7 @@ fn h() {
r#"
struct X<T>(T);
-fn foo(x: X<Unknown>) {}
+fn foo(_x: X<Unknown>) {}
fn test1() {
// Unknown might be `i32`, so we should not emit type mismatch here.
foo(X(42));
@@ -736,4 +737,19 @@ fn g() { return; }
"#,
);
}
+
+ #[test]
+ fn smoke_test_inner_items() {
+ check_diagnostics(
+ r#"
+fn f() {
+ fn inner() -> i32 {
+ return;
+ // ^^^^^^ error: expected i32, found ()
+ 0
+ }
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 4af672271..a740e332b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -26,14 +26,14 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
)
};
- Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range.range)
+ Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range)
.with_fixes(fixes)
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
let db = ctx.sema.db;
let root = db.parse_or_expand(d.expr.file_id);
- let original_range =
+ let (original_range, _) =
d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?;
let mut assists = vec![];
@@ -142,8 +142,8 @@ fn t<T>() -> T { loop {} }
check_diagnostics(
r#"
fn main() {
- let x = [(); _];
- let y: [(); 10] = [(); _];
+ let _x = [(); _];
+ let _y: [(); 10] = [(); _];
_ = 0;
(_,) = (1,);
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index e04f27c27..becc24ab2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -4,7 +4,7 @@ use std::iter;
use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
use ide_db::{
- base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
+ base_db::{FileId, FileLoader, FileRange, SourceDatabase, SourceDatabaseExt},
source_change::SourceChange,
RootDatabase,
};
@@ -46,8 +46,12 @@ pub(crate) fn unlinked_file(
.unwrap_or(range);
acc.push(
- Diagnostic::new(DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), message, range)
- .with_fixes(fixes),
+ Diagnostic::new(
+ DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning),
+ message,
+ FileRange { file_id, range },
+ )
+ .with_fixes(fixes),
);
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 0758706e4..321459412 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -8,7 +8,7 @@ use ide_db::{
use syntax::{ast, AstNode, AstPtr};
use text_edit::TextEdit;
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-field
//
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_field(
} else {
""
};
- Diagnostic::new_with_syntax_node_ptr(
- ctx,
+ Diagnostic::new(
DiagnosticCode::RustcHardError("E0559"),
format!(
"no field `{}` on type `{}`{method_suffix}",
d.name.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))
.experimental()
@@ -79,7 +88,7 @@ mod tests {
r#"
fn main() {
().foo;
- // ^^^^^^ error: no field `foo` on type `()`
+ // ^^^ error: no field `foo` on type `()`
}
"#,
);
@@ -95,7 +104,7 @@ impl Foo {
}
fn foo() {
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 {}
fn foo() {
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() {
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
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 33e7c2e37..c8ff54cba 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -70,4 +70,16 @@ self::m!(); self::m2!();
"#,
);
}
+
+ #[test]
+ fn regression_panic_with_inner_attribute_in_presence_of_unresolved_crate() {
+ check_diagnostics(
+ r#"
+ mod _test_inner {
+ #![empty_attr]
+ //^^^^^^^^^^^^^^ error: unresolved macro `empty_attr`
+ }
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index ae9f6744c..464b0a710 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -8,7 +8,7 @@ use ide_db::{
use syntax::{ast, AstNode, TextRange};
use text_edit::TextEdit;
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use crate::{adjusted_display_range_new, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-method
//
@@ -22,15 +22,24 @@ pub(crate) fn unresolved_method(
} else {
""
};
- Diagnostic::new_with_syntax_node_ptr(
- ctx,
+ Diagnostic::new(
DiagnosticCode::RustcHardError("E0599"),
format!(
"no method `{}` on type `{}`{field_suffix}",
d.name.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))
.experimental()
@@ -92,7 +101,41 @@ mod tests {
r#"
fn main() {
().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 }
fn foo() {
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
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
index be24e50c9..e90d385ba 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
@@ -1,4 +1,4 @@
-use hir::db::ExpandDatabase;
+use hir::{db::ExpandDatabase, HirFileIdExt};
use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSystemEdit};
use itertools::Itertools;
use syntax::AstNode;
@@ -87,7 +87,12 @@ mod baz {}
"E0583",
),
message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs",
- range: 0..8,
+ range: FileRange {
+ file_id: FileId(
+ 0,
+ ),
+ range: 0..8,
+ },
severity: Error,
unused: false,
experimental: false,
@@ -150,11 +155,9 @@ mod baz {}
],
),
main_node: Some(
- InFile {
+ InFileWrapper {
file_id: FileId(
- FileId(
- 0,
- ),
+ 0,
),
value: MODULE@0..8
MOD_KW@0..3 "mod"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
new file mode 100644
index 000000000..28ccf474b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -0,0 +1,111 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: unused-variables
+//
+// This diagnostic is triggered when a local variable is not used.
+pub(crate) fn unused_variables(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::UnusedVariable,
+) -> Diagnostic {
+ let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcLint("unused_variables"),
+ "unused variable",
+ ast,
+ )
+ .experimental()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn unused_variables_simple() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+struct Foo { f1: i32, f2: i64 }
+
+fn f(kkk: i32) {}
+ //^^^ warn: unused variable
+fn main() {
+ let a = 2;
+ //^ warn: unused variable
+ let b = 5;
+ // note: `unused variable` implies `unused mut`, so we should not emit both at the same time.
+ let mut c = f(b);
+ //^^^^^ warn: unused variable
+ let (d, e) = (3, 5);
+ //^ warn: unused variable
+ let _ = e;
+ let f1 = 2;
+ let f2 = 5;
+ let f = Foo { f1, f2 };
+ match f {
+ Foo { f1, f2 } => {
+ //^^ warn: unused variable
+ _ = f2;
+ }
+ }
+ let g = false;
+ if g {}
+ let h: fn() -> i32 = || 2;
+ let i = h();
+ //^ warn: unused variable
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unused_self() {
+ check_diagnostics(
+ r#"
+struct S {
+}
+impl S {
+ fn owned_self(self, u: i32) {}
+ //^ warn: unused variable
+ fn ref_self(&self, u: i32) {}
+ //^ warn: unused variable
+ fn ref_mut_self(&mut self, u: i32) {}
+ //^ warn: unused variable
+ fn owned_mut_self(mut self) {}
+ //^^^^^^^^ 💡 warn: variable does not need to be mutable
+
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn allow_unused_variables_for_identifiers_starting_with_underline() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let _x = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn respect_lint_attributes_for_unused_variables() {
+ check_diagnostics(
+ r#"
+fn main() {
+ #[allow(unused_variables)]
+ let x = 2;
+}
+
+#[deny(unused)]
+fn main2() {
+ let x = 2;
+ //^ error: unused variable
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
index c4ac59ec2..8dce2af23 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
@@ -1,5 +1,8 @@
use hir::InFile;
-use ide_db::{base_db::FileId, source_change::SourceChange};
+use ide_db::{
+ base_db::{FileId, FileRange},
+ source_change::SourceChange,
+};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxNode};
use text_edit::TextEdit;
@@ -38,7 +41,7 @@ pub(crate) fn useless_braces(
Diagnostic::new(
DiagnosticCode::RustcLint("unused_braces"),
"Unnecessary braces in use statement".to_string(),
- use_range,
+ FileRange { file_id, range: use_range },
)
.with_main_node(InFile::new(file_id.into(), node.clone()))
.with_fixes(Some(vec![fix(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index ebe197a67..579386c72 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -23,7 +23,7 @@
//! There are also a couple of ad-hoc diagnostics implemented directly here, we
//! don't yet have a great pattern for how to do them properly.
-#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
+#![warn(rust_2018_idioms, unused_lifetimes)]
mod handlers {
pub(crate) mod break_outside_of_loop;
@@ -44,6 +44,10 @@ mod handlers {
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
pub(crate) mod replace_filter_map_next_with_find_map;
+ pub(crate) mod trait_impl_orphan;
+ pub(crate) mod trait_impl_incorrect_safety;
+ pub(crate) mod trait_impl_missing_assoc_item;
+ pub(crate) mod trait_impl_redundant_assoc_item;
pub(crate) mod typed_hole;
pub(crate) mod type_mismatch;
pub(crate) mod unimplemented_builtin_macro;
@@ -56,6 +60,7 @@ mod handlers {
pub(crate) mod unresolved_proc_macro;
pub(crate) mod undeclared_label;
pub(crate) mod unreachable_label;
+ pub(crate) mod unused_variables;
// The handlers below are unusual, the implement the diagnostics as well.
pub(crate) mod field_shorthand;
@@ -85,11 +90,11 @@ use stdx::never;
use syntax::{
algo::find_node_at_range,
ast::{self, AstNode},
- SyntaxNode, SyntaxNodePtr, TextRange,
+ AstPtr, SyntaxNode, SyntaxNodePtr, TextRange,
};
// FIXME: Make this an enum
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DiagnosticCode {
RustcHardError(&'static str),
RustcLint(&'static str),
@@ -129,7 +134,7 @@ impl DiagnosticCode {
pub struct Diagnostic {
pub code: DiagnosticCode,
pub message: String,
- pub range: TextRange,
+ pub range: FileRange,
pub severity: Severity,
pub unused: bool,
pub experimental: bool,
@@ -139,7 +144,7 @@ pub struct Diagnostic {
}
impl Diagnostic {
- fn new(code: DiagnosticCode, message: impl Into<String>, range: TextRange) -> Diagnostic {
+ fn new(code: DiagnosticCode, message: impl Into<String>, range: FileRange) -> Diagnostic {
let message = message.into();
Diagnostic {
code,
@@ -168,7 +173,7 @@ impl Diagnostic {
node: InFile<SyntaxNodePtr>,
) -> Diagnostic {
let file_id = node.file_id;
- Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()).range)
+ Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()))
.with_main_node(node.map(|x| x.to_node(&ctx.sema.parse_or_expand(file_id))))
}
@@ -193,7 +198,7 @@ impl Diagnostic {
}
}
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Severity {
Error,
Warning,
@@ -224,6 +229,7 @@ pub struct DiagnosticsConfig {
// FIXME: We may want to include a whole `AssistConfig` here
pub insert_use: InsertUseConfig,
pub prefer_no_std: bool,
+ pub prefer_prelude: bool,
}
impl DiagnosticsConfig {
@@ -246,6 +252,7 @@ impl DiagnosticsConfig {
skip_glob_imports: false,
},
prefer_no_std: false,
+ prefer_prelude: true,
}
}
}
@@ -261,7 +268,7 @@ impl DiagnosticsContext<'_> {
&self,
node: &InFile<SyntaxNodePtr>,
precise_location: Option<TextRange>,
- ) -> TextRange {
+ ) -> FileRange {
let sema = &self.sema;
(|| {
let precise_location = precise_location?;
@@ -274,10 +281,11 @@ impl DiagnosticsContext<'_> {
}
})()
.unwrap_or_else(|| sema.diagnostics_display_range(node.clone()))
- .range
}
}
+/// Request diagnostics for the given [`FileId`]. The produced diagnostics may point to other files
+/// due to macros.
pub fn diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
@@ -294,7 +302,7 @@ pub fn diagnostics(
Diagnostic::new(
DiagnosticCode::RustcHardError("syntax-error"),
format!("Syntax Error: {err}"),
- err.range(),
+ FileRange { file_id, range: err.range() },
)
}));
@@ -355,6 +363,10 @@ pub fn diagnostics(
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
+ AnyDiagnostic::TraitImplIncorrectSafety(d) => handlers::trait_impl_incorrect_safety::trait_impl_incorrect_safety(&ctx, &d),
+ AnyDiagnostic::TraitImplMissingAssocItems(d) => handlers::trait_impl_missing_assoc_item::trait_impl_missing_assoc_item(&ctx, &d),
+ AnyDiagnostic::TraitImplRedundantAssocItems(d) => handlers::trait_impl_redundant_assoc_item::trait_impl_redundant_assoc_item(&ctx, &d),
+ AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d),
AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),
@@ -368,6 +380,7 @@ 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::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
+ AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d),
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
};
@@ -559,12 +572,28 @@ fn adjusted_display_range<N: AstNode>(
ctx: &DiagnosticsContext<'_>,
diag_ptr: InFile<SyntaxNodePtr>,
adj: &dyn Fn(N) -> Option<TextRange>,
-) -> TextRange {
+) -> FileRange {
let FileRange { file_id, range } = ctx.sema.diagnostics_display_range(diag_ptr);
let source_file = ctx.sema.db.parse(file_id);
- find_node_at_range::<N>(&source_file.syntax_node(), range)
- .filter(|it| it.syntax().text_range() == range)
- .and_then(adj)
- .unwrap_or(range)
+ FileRange {
+ file_id,
+ range: find_node_at_range::<N>(&source_file.syntax_node(), range)
+ .filter(|it| it.syntax().text_range() == range)
+ .and_then(adj)
+ .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)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
index ee0e03549..48e0363c9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -5,8 +5,9 @@ use expect_test::Expect;
use ide_db::{
assists::AssistResolveStrategy,
base_db::{fixture::WithFixture, SourceDatabaseExt},
- RootDatabase,
+ LineIndexDatabase, RootDatabase,
};
+use itertools::Itertools;
use stdx::trim_indent;
use test_utils::{assert_eq_text, extract_annotations, MiniCore};
@@ -43,7 +44,8 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id)
.pop()
.expect("no diagnostics");
- let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
+ let fix =
+ &diagnostic.fixes.expect(&format!("{:?} diagnostic misses fixes", diagnostic.code))[nth];
let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
@@ -102,32 +104,39 @@ pub(crate) fn check_diagnostics(ra_fixture: &str) {
#[track_caller]
pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
let (db, files) = RootDatabase::with_many_files(ra_fixture);
+ let mut annotations = files
+ .iter()
+ .copied()
+ .flat_map(|file_id| {
+ super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id).into_iter().map(
+ |d| {
+ let mut annotation = String::new();
+ if let Some(fixes) = &d.fixes {
+ assert!(!fixes.is_empty());
+ annotation.push_str("💡 ")
+ }
+ annotation.push_str(match d.severity {
+ Severity::Error => "error",
+ Severity::WeakWarning => "weak",
+ Severity::Warning => "warn",
+ Severity::Allow => "allow",
+ });
+ annotation.push_str(": ");
+ annotation.push_str(&d.message);
+ (d.range, annotation)
+ },
+ )
+ })
+ .map(|(diagnostic, annotation)| (diagnostic.file_id, (diagnostic.range, annotation)))
+ .into_group_map();
for file_id in files {
- let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
+ let line_index = db.line_index(file_id);
+ let mut actual = annotations.remove(&file_id).unwrap_or_default();
let expected = extract_annotations(&db.file_text(file_id));
- let mut actual = diagnostics
- .into_iter()
- .map(|d| {
- let mut annotation = String::new();
- if let Some(fixes) = &d.fixes {
- assert!(!fixes.is_empty());
- annotation.push_str("💡 ")
- }
- annotation.push_str(match d.severity {
- Severity::Error => "error",
- Severity::WeakWarning => "weak",
- Severity::Warning => "warn",
- Severity::Allow => "allow",
- });
- annotation.push_str(": ");
- annotation.push_str(&d.message);
- (d.range, annotation)
- })
- .collect::<Vec<_>>();
actual.sort_by_key(|(range, _)| range.start());
if expected.is_empty() {
- // makes minicore smoke test debugable
+ // makes minicore smoke test debuggable
for (e, _) in &actual {
eprintln!(
"Code in range {e:?} = {}",
@@ -136,8 +145,16 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
}
}
if expected != actual {
- let fneg = expected.iter().filter(|x| !actual.contains(x)).collect::<Vec<_>>();
- let fpos = actual.iter().filter(|x| !expected.contains(x)).collect::<Vec<_>>();
+ let fneg = expected
+ .iter()
+ .filter(|x| !actual.contains(x))
+ .map(|(range, s)| (line_index.line_col(range.start()), range, s))
+ .collect::<Vec<_>>();
+ let fpos = actual
+ .iter()
+ .filter(|x| !expected.contains(x))
+ .map(|(range, s)| (line_index.line_col(range.start()), range, s))
+ .collect::<Vec<_>>();
panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}");
}