summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-diagnostics
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /src/tools/rust-analyzer/crates/ide-diagnostics
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-diagnostics')
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs40
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs208
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs181
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs75
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs262
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs18
37 files changed, 835 insertions, 274 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
index e18624fcc..14aa39401 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
@@ -16,6 +16,7 @@ cov-mark = "2.0.0-pre.1"
either = "1.7.0"
itertools = "0.10.5"
serde_json = "1.0.86"
+once_cell = "1.17.0"
# local deps
profile.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 30576c71f..491005403 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: break-outside-of-loop
//
@@ -13,10 +13,11 @@ pub(crate) fn break_outside_of_loop(
let construct = if d.is_break { "break" } else { "continue" };
format!("{construct} outside of loop")
};
- Diagnostic::new(
- "break-outside-of-loop",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0268"),
message,
- ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+ d.expr.clone().map(|it| it.into()),
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
index d2f27664d..e1e5db91c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -1,6 +1,6 @@
use hir::HirDisplay;
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: expected-function
//
@@ -9,10 +9,11 @@ pub(crate) fn expected_function(
ctx: &DiagnosticsContext<'_>,
d: &hir::ExpectedFunction,
) -> Diagnostic {
- Diagnostic::new(
- "expected-function",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0618"),
format!("expected function, found {}", d.found.display(ctx.sema.db)),
- ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range,
+ d.call.clone().map(|it| it.into()),
)
.experimental()
}
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 2b7105362..3b69640af 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
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange};
use syntax::{ast, match_ast, AstNode, SyntaxNode};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, Severity};
+use crate::{fix, Diagnostic, DiagnosticCode};
pub(crate) fn field_shorthand(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
match_ast! {
@@ -46,14 +46,17 @@ fn check_expr_field_shorthand(
let field_range = record_field.syntax().text_range();
acc.push(
- Diagnostic::new("use-field-shorthand", "Shorthand struct initialization", field_range)
- .severity(Severity::WeakWarning)
- .with_fixes(Some(vec![fix(
- "use_expr_field_shorthand",
- "Use struct shorthand initialization",
- SourceChange::from_text_edit(file_id, edit),
- field_range,
- )])),
+ Diagnostic::new(
+ DiagnosticCode::Clippy("redundant_field_names"),
+ "Shorthand struct initialization",
+ field_range,
+ )
+ .with_fixes(Some(vec![fix(
+ "use_expr_field_shorthand",
+ "Use struct shorthand initialization",
+ SourceChange::from_text_edit(file_id, edit),
+ field_range,
+ )])),
);
}
}
@@ -87,14 +90,17 @@ fn check_pat_field_shorthand(
let field_range = record_pat_field.syntax().text_range();
acc.push(
- Diagnostic::new("use-field-shorthand", "Shorthand struct pattern", field_range)
- .severity(Severity::WeakWarning)
- .with_fixes(Some(vec![fix(
- "use_pat_field_shorthand",
- "Use struct field shorthand",
- SourceChange::from_text_edit(file_id, edit),
- field_range,
- )])),
+ Diagnostic::new(
+ DiagnosticCode::Clippy("redundant_field_names"),
+ "Shorthand struct pattern",
+ field_range,
+ )
+ .with_fixes(Some(vec![fix(
+ "use_pat_field_shorthand",
+ "Use struct field shorthand",
+ SourceChange::from_text_edit(file_id, edit),
+ field_range,
+ )])),
);
}
}
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 f558b7256..9eb763d3e 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
@@ -1,7 +1,7 @@
use cfg::DnfExpr;
use stdx::format_to;
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: inactive-code
//
@@ -27,13 +27,12 @@ pub(crate) fn inactive_code(
format_to!(message, ": {}", inactive);
}
}
-
+ // FIXME: This shouldn't be a diagnostic
let res = Diagnostic::new(
- "inactive-code",
+ DiagnosticCode::Ra("inactive-code", Severity::WeakWarning),
message,
ctx.sema.diagnostics_display_range(d.node.clone()).range,
)
- .severity(Severity::WeakWarning)
.with_unused(true);
Some(res)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
index 72af9ebfc..4afb4db03 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
@@ -1,17 +1,17 @@
use hir::InFile;
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: incoherent-impl
//
// This diagnostic is triggered if the targe type of an impl is from a foreign crate.
pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic {
- Diagnostic::new(
- "incoherent-impl",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0210"),
format!("cannot define inherent `impl` for foreign type"),
- ctx.sema.diagnostics_display_range(InFile::new(d.file_id, d.impl_.clone().into())).range,
+ InFile::new(d.file_id, d.impl_.clone().into()),
)
- .severity(Severity::Error)
}
#[cfg(test)]
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 90279e145..235062bf5 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
@@ -1,4 +1,4 @@
-use hir::{db::ExpandDatabase, InFile};
+use hir::{db::ExpandDatabase, CaseType, InFile};
use ide_db::{assists::Assist, defs::NameClass};
use syntax::AstNode;
@@ -6,23 +6,29 @@ use crate::{
// references::rename::rename_with_semantics,
unresolved_fix,
Diagnostic,
+ DiagnosticCode,
DiagnosticsContext,
- Severity,
};
// Diagnostic: incorrect-ident-case
//
// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
- Diagnostic::new(
- "incorrect-ident-case",
+ let code = match d.expected_case {
+ CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"),
+ CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"),
+ // The name is lying. It also covers variants, traits, ...
+ CaseType::UpperCamelCase => DiagnosticCode::RustcLint("non_camel_case_types"),
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ code,
format!(
"{} `{}` should have {} name, e.g. `{}`",
d.ident_type, d.ident_text, d.expected_case, d.suggested_text
),
- ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range,
+ InFile::new(d.file, d.ident.clone().into()),
)
- .severity(Severity::WeakWarning)
.with_fixes(fixes(ctx, d))
}
@@ -149,7 +155,7 @@ impl TestStruct {
check_diagnostics(
r#"
fn FOO() {}
-// ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo`
+// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo`
"#,
);
check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#);
@@ -160,7 +166,7 @@ fn FOO() {}
check_diagnostics(
r#"
fn NonSnakeCaseName() {}
-// ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
+// ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
"#,
);
}
@@ -170,10 +176,10 @@ fn NonSnakeCaseName() {}
check_diagnostics(
r#"
fn foo(SomeParam: u8) {}
- // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param`
+ // ^^^^^^^^^ 💡 warn: Parameter `SomeParam` should have snake_case name, e.g. `some_param`
fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
- // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
+ // ^^^^^^^^^^ 💡 warn: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
"#,
);
}
@@ -184,9 +190,9 @@ fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
r#"
fn foo() {
let SOME_VALUE = 10;
- // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
+ // ^^^^^^^^^^ 💡 warn: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
let AnotherValue = 20;
- // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value`
+ // ^^^^^^^^^^^^ 💡 warn: Variable `AnotherValue` should have snake_case name, e.g. `another_value`
}
"#,
);
@@ -197,10 +203,10 @@ fn foo() {
check_diagnostics(
r#"
struct non_camel_case_name {}
- // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
+ // ^^^^^^^^^^^^^^^^^^^ 💡 warn: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
struct SCREAMING_CASE {}
- // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
+ // ^^^^^^^^^^^^^^ 💡 warn: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
"#,
);
}
@@ -219,7 +225,7 @@ struct AABB {}
check_diagnostics(
r#"
struct SomeStruct { SomeField: u8 }
- // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field`
+ // ^^^^^^^^^ 💡 warn: Field `SomeField` should have snake_case name, e.g. `some_field`
"#,
);
}
@@ -229,10 +235,10 @@ struct SomeStruct { SomeField: u8 }
check_diagnostics(
r#"
enum some_enum { Val(u8) }
- // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
+ // ^^^^^^^^^ 💡 warn: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
enum SOME_ENUM {}
- // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
+ // ^^^^^^^^^ 💡 warn: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
"#,
);
}
@@ -251,7 +257,7 @@ enum AABB {}
check_diagnostics(
r#"
enum SomeEnum { SOME_VARIANT(u8) }
- // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
+ // ^^^^^^^^^^^^ 💡 warn: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
"#,
);
}
@@ -261,7 +267,7 @@ enum SomeEnum { SOME_VARIANT(u8) }
check_diagnostics(
r#"
const some_weird_const: u8 = 10;
- // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
+ // ^^^^^^^^^^^^^^^^ 💡 warn: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
"#,
);
}
@@ -271,7 +277,7 @@ const some_weird_const: u8 = 10;
check_diagnostics(
r#"
static some_weird_const: u8 = 10;
- // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
+ // ^^^^^^^^^^^^^^^^ 💡 warn: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
"#,
);
}
@@ -281,13 +287,13 @@ static some_weird_const: u8 = 10;
check_diagnostics(
r#"
struct someStruct;
- // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
+ // ^^^^^^^^^^ 💡 warn: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
impl someStruct {
fn SomeFunc(&self) {
- // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func`
+ // ^^^^^^^^ 💡 warn: Function `SomeFunc` should have snake_case name, e.g. `some_func`
let WHY_VAR_IS_CAPS = 10;
- // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
+ // ^^^^^^^^^^^^^^^ 💡 warn: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
}
}
"#,
@@ -319,7 +325,7 @@ enum Option { Some, None }
fn main() {
match Option::None {
SOME_VAR @ None => (),
- // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
+ // ^^^^^^^^ 💡 warn: Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
Some => (),
}
}
@@ -461,7 +467,7 @@ mod CheckNonstandardStyle {
#[allow(bad_style)]
mod CheckBadStyle {
- fn HiImABadFnName() {}
+ struct fooo;
}
mod F {
@@ -483,4 +489,156 @@ pub static SomeStatic: u8 = 10;
"#,
);
}
+
+ #[test]
+ fn deny_attributes() {
+ check_diagnostics(
+ r#"
+#[deny(non_snake_case)]
+fn NonSnakeCaseName(some_var: u8) -> u8 {
+ //^^^^^^^^^^^^^^^^ 💡 error: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
+ // cov_flags generated output from elsewhere in this file
+ extern "C" {
+ #[no_mangle]
+ static lower_case: u8;
+ }
+
+ let OtherVar = some_var + 1;
+ //^^^^^^^^ 💡 error: Variable `OtherVar` should have snake_case name, e.g. `other_var`
+ OtherVar
+}
+
+#[deny(nonstandard_style)]
+mod CheckNonstandardStyle {
+ fn HiImABadFnName() {}
+ //^^^^^^^^^^^^^^ 💡 error: Function `HiImABadFnName` should have snake_case name, e.g. `hi_im_abad_fn_name`
+}
+
+#[deny(warnings)]
+mod CheckBadStyle {
+ struct fooo;
+ //^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo`
+}
+
+mod F {
+ #![deny(non_snake_case)]
+ fn CheckItWorksWithModAttr() {}
+ //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr`
+}
+
+#[deny(non_snake_case, non_camel_case_types)]
+pub struct some_type {
+ //^^^^^^^^^ 💡 error: Structure `some_type` should have CamelCase name, e.g. `SomeType`
+ SOME_FIELD: u8,
+ //^^^^^^^^^^ 💡 error: Field `SOME_FIELD` should have snake_case name, e.g. `some_field`
+ SomeField: u16,
+ //^^^^^^^^^ 💡 error: Field `SomeField` should have snake_case name, e.g. `some_field`
+}
+
+#[deny(non_upper_case_globals)]
+pub const some_const: u8 = 10;
+ //^^^^^^^^^^ 💡 error: Constant `some_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST`
+
+#[deny(non_upper_case_globals)]
+pub static SomeStatic: u8 = 10;
+ //^^^^^^^^^^ 💡 error: Static variable `SomeStatic` should have UPPER_SNAKE_CASE name, e.g. `SOME_STATIC`
+ "#,
+ );
+ }
+
+ #[test]
+ fn fn_inner_items() {
+ check_diagnostics(
+ r#"
+fn main() {
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ static bar: bool = true;
+ //^^^ 💡 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`
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ static bar: bool = true;
+ //^^^ 💡 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_LOCAL = 42;
+ //^^^^^^^^^^^ 💡 warn: Variable `INNER_LOCAL` should have snake_case name, e.g. `inner_local`
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn const_body_inner_items() {
+ check_diagnostics(
+ r#"
+const _: () = {
+ static bar: bool = true;
+ //^^^ 💡 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`
+
+ const foo: () = {
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ static bar: bool = true;
+ //^^^ 💡 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`
+ };
+};
+"#,
+ );
+ }
+
+ #[test]
+ fn static_body_inner_items() {
+ check_diagnostics(
+ r#"
+static FOO: () = {
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ fn BAZ() {}
+ //^^^ 💡 warn: Function `BAZ` should have snake_case name, e.g. `baz`
+
+ static bar: () = {
+ //^^^ 💡 warn: Static variable `bar` should have UPPER_SNAKE_CASE name, e.g. `BAR`
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ static bar: bool = true;
+ //^^^ 💡 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`
+ };
+};
+"#,
+ );
+ }
+
+ #[test]
+ fn enum_variant_body_inner_item() {
+ check_diagnostics(
+ r#"
+enum E {
+ A = {
+ const foo: bool = true;
+ //^^^ 💡 warn: Constant `foo` should have UPPER_SNAKE_CASE name, e.g. `FOO`
+ static bar: bool = true;
+ //^^^ 💡 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`
+ 42
+ },
+}
+"#,
+ );
+ }
}
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 c779266bc..1ec17952b 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
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: invalid-derive-target
//
@@ -11,11 +11,10 @@ pub(crate) fn invalid_derive_target(
let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
Diagnostic::new(
- "invalid-derive-target",
+ DiagnosticCode::RustcHardError("E0774"),
"`derive` may only be applied to `struct`s, `enum`s and `union`s",
display_range,
)
- .severity(Severity::Error)
}
#[cfg(test)]
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 04ce1e0fe..a337e2660 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
@@ -17,7 +17,7 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, DiagnosticsConfig, Severity};
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity};
#[derive(Default)]
struct State {
@@ -117,11 +117,10 @@ pub(crate) fn json_in_items(
edit.insert(range.start(), state.result);
acc.push(
Diagnostic::new(
- "json-is-not-rust",
+ DiagnosticCode::Ra("json-is-not-rust", Severity::WeakWarning),
"JSON syntax is not valid as a Rust item",
range,
)
- .severity(Severity::WeakWarning)
.with_fixes(Some(vec![{
let mut scb = SourceChangeBuilder::new(file_id);
let scope = match import_scope {
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 7547779a9..f54cdd63b 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
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: macro-error
//
@@ -6,7 +6,12 @@ use crate::{Diagnostic, DiagnosticsContext};
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
// Use more accurate position if available.
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
- Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
+ Diagnostic::new(
+ DiagnosticCode::Ra("macro-error", Severity::Error),
+ d.message.clone(),
+ display_range,
+ )
+ .experimental()
}
// Diagnostic: macro-error
@@ -16,7 +21,12 @@ pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefErr
// Use more accurate position if available.
let display_range =
ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name);
- Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental()
+ Diagnostic::new(
+ DiagnosticCode::Ra("macro-def-error", Severity::Error),
+ d.message.clone(),
+ display_range,
+ )
+ .experimental()
}
#[cfg(test)]
@@ -41,6 +51,9 @@ macro_rules! compile_error { () => {} }
compile_error!("compile_error macro works");
//^^^^^^^^^^^^^ error: compile_error macro works
+
+ compile_error! { "compile_error macro braced works" }
+//^^^^^^^^^^^^^ error: compile_error macro braced works
"#,
);
}
@@ -67,7 +80,7 @@ macro_rules! m {
fn f() {
m!();
- //^^^^ error: unresolved macro `$crate::private::concat!`
+ //^^^^ error: unresolved macro $crate::private::concat
}
//- /core.rs crate:core
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 cd48bdba0..fc57dde69 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
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: malformed-derive
//
@@ -10,11 +10,10 @@ pub(crate) fn malformed_derive(
let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
Diagnostic::new(
- "malformed-derive",
+ DiagnosticCode::RustcHardError("E0777"),
"malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`",
display_range,
)
- .severity(Severity::Error)
}
#[cfg(test)]
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 c5db8c374..6238c7e09 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
@@ -3,7 +3,7 @@ use syntax::{
AstNode, TextRange,
};
-use crate::{adjusted_display_range, Diagnostic, DiagnosticsContext};
+use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: mismatched-arg-count
//
@@ -14,7 +14,7 @@ pub(crate) fn mismatched_arg_count(
) -> Diagnostic {
let s = if d.expected == 1 { "" } else { "s" };
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
- Diagnostic::new("mismatched-arg-count", message, invalid_args_range(ctx, d))
+ Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d))
}
fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange {
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 60ccc41df..acc31cd11 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
@@ -15,7 +15,7 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, DiagnosticsContext};
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: missing-fields
//
@@ -42,7 +42,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField
.unwrap_or_else(|| d.field_list_parent.clone().either(|it| it.into(), |it| it.into())),
);
- Diagnostic::new("missing-fields", message, ctx.sema.diagnostics_display_range(ptr).range)
+ Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr)
.with_fixes(fixes(ctx, d))
}
@@ -208,7 +208,7 @@ fn get_default_constructor(
}
let krate = ctx.sema.to_module_def(d.file.original_file(ctx.sema.db))?.krate();
- let module = krate.root_module(ctx.sema.db);
+ let module = krate.root_module();
// Look for a ::new() associated function
let has_new_func = ty
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 3f13b97a4..82a9a3bd5 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
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: missing-match-arm
//
@@ -7,10 +7,11 @@ pub(crate) fn missing_match_arms(
ctx: &DiagnosticsContext<'_>,
d: &hir::MissingMatchArms,
) -> Diagnostic {
- Diagnostic::new(
- "missing-match-arm",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0004"),
format!("missing match arm: {}", d.uncovered_patterns),
- ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range,
+ d.scrutinee_expr.clone().map(Into::into),
)
}
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 2026b6fce..70b26009b 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
@@ -4,16 +4,17 @@ use syntax::{ast, SyntaxNode};
use syntax::{match_ast, AstNode};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, DiagnosticsContext};
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: missing-unsafe
//
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
- Diagnostic::new(
- "missing-unsafe",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0133"),
"this operation is unsafe and requires an unsafe function or block",
- ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+ d.expr.clone().map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
}
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 32e321107..3aa4aa970 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
@@ -1,14 +1,15 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
use hir::HirDisplay;
// Diagnostic: moved-out-of-ref
//
// This diagnostic is triggered on moving non copy things out of references.
pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic {
- Diagnostic::new(
- "moved-out-of-ref",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0507"),
format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)),
- ctx.sema.diagnostics_display_range(d.span.clone()).range,
+ d.span.clone(),
)
.experimental() // spans are broken, and I'm not sure how precise we can detect copy types
}
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 f61460e31..e0c3bedce 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
@@ -2,7 +2,7 @@ use ide_db::source_change::SourceChange;
use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, DiagnosticsContext, Severity};
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: need-mut
//
@@ -29,13 +29,15 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
use_range,
)])
})();
- Diagnostic::new(
- "need-mut",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ // FIXME: `E0384` is not the only error that this diagnostic handles
+ DiagnosticCode::RustcHardError("E0384"),
format!(
"cannot mutate immutable variable `{}`",
d.local.name(ctx.sema.db).display(ctx.sema.db)
),
- ctx.sema.diagnostics_display_range(d.span.clone()).range,
+ d.span.clone(),
)
.with_fixes(fixes)
}
@@ -68,12 +70,12 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di
)])
})();
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
- Diagnostic::new(
- "unused-mut",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcLint("unused_mut"),
"variable does not need to be mutable",
- ctx.sema.diagnostics_display_range(ast).range,
+ ast,
)
- .severity(Severity::WeakWarning)
.experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
.with_fixes(fixes)
}
@@ -93,7 +95,7 @@ mod tests {
fn f(_: i32) {}
fn main() {
let mut x = 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
f(x);
}
"#,
@@ -268,7 +270,7 @@ fn main() {
fn f(_: i32) {}
fn main() {
let mut x = (2, 7);
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
f(x.1);
}
"#,
@@ -302,7 +304,7 @@ fn main() {
r#"
fn main() {
let mut x = &mut 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
*x = 5;
}
"#,
@@ -346,7 +348,7 @@ fn main() {
r#"
//- minicore: copy, builtin_impls
fn clone(mut i: &!) -> ! {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
*i
}
"#,
@@ -360,7 +362,7 @@ fn main() {
//- minicore: option
fn main() {
let mut v = &mut Some(2);
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let _ = || match v {
Some(k) => {
*k = 5;
@@ -386,7 +388,7 @@ fn main() {
fn main() {
match (2, 3) {
(x, mut y) => {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x = 7;
//^^^^^ 💡 error: cannot mutate immutable variable `x`
}
@@ -407,7 +409,7 @@ fn main() {
fn main() {
return;
let mut x = 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
&mut x;
}
"#,
@@ -417,7 +419,7 @@ fn main() {
fn main() {
loop {}
let mut x = 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
&mut x;
}
"#,
@@ -438,7 +440,7 @@ fn main(b: bool) {
g();
}
let mut x = 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
&mut x;
}
"#,
@@ -452,7 +454,7 @@ fn main(b: bool) {
return;
}
let mut x = 2;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
&mut x;
}
"#,
@@ -466,7 +468,7 @@ fn main(b: bool) {
fn f(_: i32) {}
fn main() {
let mut x;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x = 5;
f(x);
}
@@ -477,7 +479,7 @@ fn main() {
fn f(_: i32) {}
fn main(b: bool) {
let mut x;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
if b {
x = 1;
} else {
@@ -552,15 +554,15 @@ fn f(_: i32) {}
fn main() {
loop {
let mut x = 1;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
f(x);
if let mut y = 2 {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
f(y);
}
match 3 {
mut z => f(z),
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
}
}
}
@@ -577,9 +579,9 @@ fn main() {
loop {
let c @ (
mut b,
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
mut d
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
);
a = 1;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
@@ -597,7 +599,7 @@ fn main() {
check_diagnostics(
r#"
fn f(mut x: i32) {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
}
"#,
);
@@ -640,7 +642,7 @@ fn f() {
//- minicore: iterators, copy
fn f(x: [(i32, u8); 10]) {
for (a, mut b) in x {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
a = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
}
@@ -657,9 +659,9 @@ fn f(x: [(i32, u8); 10]) {
fn f(x: [(i32, u8); 10]) {
let mut it = x.into_iter();
while let Some((a, mut b)) = it.next() {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
while let Some((c, mut d)) = it.next() {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
a = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
c = 2;
@@ -683,7 +685,7 @@ fn f() {
let x = &mut x;
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
let mut x = x;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x[2] = 5;
}
"#,
@@ -711,13 +713,13 @@ impl IndexMut<usize> for Foo {
}
fn f() {
let mut x = Foo;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let y = &x[2];
let x = Foo;
let y = &mut x[2];
//^💡 error: cannot mutate immutable variable `x`
let mut x = &mut Foo;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let y: &mut (i32, u8) = &mut x[2];
let x = Foo;
let ref mut y = x[7];
@@ -731,7 +733,7 @@ fn f() {
}
let mut x = Foo;
let mut i = 5;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let y = &mut x[i];
}
"#,
@@ -759,7 +761,7 @@ impl DerefMut for Foo {
}
fn f() {
let mut x = Foo;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let y = &*x;
let x = Foo;
let y = &mut *x;
@@ -790,11 +792,27 @@ fn f() {
fn f(_: i32) {}
fn main() {
let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7));
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
f(x);
}
"#,
);
+ check_diagnostics(
+ r#"
+struct Foo(i32);
+
+const X: Foo = Foo(5);
+const Y: Foo = Foo(12);
+
+const fn f(mut a: Foo) -> bool {
+ //^^^^^ 💡 warn: variable does not need to be mutable
+ match a {
+ X | Y => true,
+ _ => false,
+ }
+}
+"#,
+ );
}
#[test]
@@ -842,7 +860,7 @@ pub struct TreeLeaf {
pub fn test() {
let mut tree = Tree::Leaf(
- //^^^^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^^^^ 💡 warn: variable does not need to be mutable
TreeLeaf {
depth: 0,
data: 0
@@ -859,7 +877,7 @@ pub fn test() {
r#"
//- minicore: fn
fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x(2)
}
fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
@@ -867,11 +885,11 @@ fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
//^ 💡 error: cannot mutate immutable variable `x`
}
fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x(2)
}
fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
x(2)
}
"#,
@@ -915,14 +933,14 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
//- minicore: copy, fn
fn f() {
let mut x = 5;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let mut y = 2;
y = 7;
let closure = || {
let mut z = 8;
z = 3;
let mut k = z;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
};
}
"#,
@@ -949,7 +967,7 @@ fn f() {
fn f() {
struct X;
let mut x = X;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let c1 = || x;
let mut x = X;
let c2 = || { x = X; x };
@@ -965,12 +983,12 @@ fn f() {
fn f() {
let mut x = &mut 5;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let closure1 = || { *x = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut 5;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let closure1 = || { *x = 2; &x; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
@@ -979,12 +997,12 @@ fn f() {
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut 5;
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let closure1 = move || { *x = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let mut x = &mut X(1, 2);
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
let closure1 = || { x.0 = 2; };
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
@@ -1001,7 +1019,7 @@ fn f() {
fn x(t: &[u8]) {
match t {
&[a, mut b] | &[a, _, mut b] => {
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
a = 2;
//^^^^^ 💡 error: cannot mutate immutable variable `a`
@@ -1055,7 +1073,7 @@ fn f() {
*x = 7;
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
let mut y = Box::new(5);
- //^^^^^ 💡 weak: variable does not need to be mutable
+ //^^^^^ 💡 warn: variable does not need to be mutable
*x = *y;
//^^^^^^^ 💡 error: cannot mutate immutable variable `x`
let x = Box::new(5);
@@ -1067,6 +1085,33 @@ fn f() {
}
#[test]
+ fn regression_15143() {
+ check_diagnostics(
+ r#"
+ trait Tr {
+ type Ty;
+ }
+
+ struct A;
+
+ impl Tr for A {
+ type Ty = (u32, i64);
+ }
+
+ struct B<T: Tr> {
+ f: <T as Tr>::Ty,
+ }
+
+ fn main(b: B<A>) {
+ let f = b.f.0;
+ f = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `f`
+ }
+ "#,
+ );
+ }
+
+ #[test]
fn allow_unused_mut_for_identifiers_starting_with_underline() {
check_diagnostics(
r#"
@@ -1080,17 +1125,51 @@ fn main() {
}
#[test]
- fn respect_allow_unused_mut() {
- // FIXME: respect
+ fn respect_lint_attributes_for_unused_mut() {
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);
}
+
+fn main2() {
+ #[deny(unused_mut)]
+ let mut x = 2;
+ //^^^^^ 💡 error: variable does not need to be mutable
+ f(x);
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+macro_rules! mac {
+ ($($x:expr),*$(,)*) => ({
+ #[allow(unused_mut)]
+ let mut vec = 2;
+ vec
+ });
+}
+
+fn main2() {
+ let mut x = mac![];
+ //^^^^^ 💡 warn: variable does not need to be mutable
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn regression_15099() {
+ check_diagnostics(
+ r#"
+//- minicore: iterator, range
+fn f() {
+ loop {}
+ for _ in 0..2 {}
+}
"#,
);
}
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 a39eceab2..a34a5824f 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
@@ -6,16 +6,17 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
+use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: no-such-field
//
// 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 {
- Diagnostic::new(
- "no-such-field",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0559"),
"no such field",
- ctx.sema.diagnostics_display_range(d.field.clone().map(|it| it.into())).range,
+ d.field.clone().map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
}
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 4cd85a479..c44d28e77 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,6 +1,6 @@
use either::Either;
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: private-assoc-item
//
@@ -16,8 +16,9 @@ pub(crate) fn private_assoc_item(
.name(ctx.sema.db)
.map(|name| format!("`{}` ", name.display(ctx.sema.db)))
.unwrap_or_default();
- Diagnostic::new(
- "private-assoc-item",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0624"),
format!(
"{} {}is private",
match d.item {
@@ -27,15 +28,13 @@ pub(crate) fn private_assoc_item(
},
name,
),
- ctx.sema
- .diagnostics_display_range(d.expr_or_pat.clone().map(|it| match it {
+ 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) => match it {
- Either::Left(it) => it.into(),
- Either::Right(it) => it.into(),
- },
- }))
- .range,
+ Either::Right(it) => it.into(),
+ },
+ }),
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index de7f51f69..553defcf9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -1,18 +1,19 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: private-field
//
// This diagnostic is triggered if the accessed field is not visible from the current module.
pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic {
// FIXME: add quickfix
- Diagnostic::new(
- "private-field",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0616"),
format!(
"field `{}` of `{}` is private",
d.field.name(ctx.sema.db).display(ctx.sema.db),
d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db)
),
- ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+ d.expr.clone().map(|it| it.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 d3eda3c5e..083ef3e8d 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
@@ -6,7 +6,7 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity};
+use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: replace-filter-map-next-with-find-map
//
@@ -15,12 +15,12 @@ pub(crate) fn replace_filter_map_next_with_find_map(
ctx: &DiagnosticsContext<'_>,
d: &hir::ReplaceFilterMapNextWithFindMap,
) -> Diagnostic {
- Diagnostic::new(
- "replace-filter-map-next-with-find-map",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::Clippy("filter_map_next"),
"replace filter_map(..).next() with find_map(..)",
- ctx.sema.diagnostics_display_range(InFile::new(d.file, d.next_expr.clone().into())).range,
+ InFile::new(d.file, d.next_expr.clone().into()),
)
- .severity(Severity::WeakWarning)
.with_fixes(fixes(ctx, d))
}
@@ -64,7 +64,7 @@ mod tests {
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());
+ config.disabled.insert("E0599".to_string());
check_diagnostics_with_config(config, ra_fixture)
}
@@ -139,4 +139,33 @@ fn foo() {
"#,
)
}
+
+ #[test]
+ fn respect_lint_attributes_for_clippy_equivalent() {
+ check_diagnostics(
+ r#"
+//- minicore: iterators
+
+fn foo() {
+ #[allow(clippy::filter_map_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(..)
+
+fn foo() {
+ 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(..)
+
+"#,
+ );
+ }
}
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 c28f98d83..15bd28c00 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
@@ -7,7 +7,7 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext};
+use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: type-mismatch
//
@@ -39,7 +39,7 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
}
};
let mut diag = Diagnostic::new(
- "type-mismatch",
+ DiagnosticCode::RustcHardError("E0308"),
format!(
"expected {}, found {}",
d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
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 e12bbcf68..4af672271 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
@@ -7,7 +7,7 @@ use ide_db::{
use syntax::AstNode;
use text_edit::TextEdit;
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: typed-hole
//
@@ -26,7 +26,8 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
)
};
- Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes)
+ Diagnostic::new(DiagnosticCode::RustcHardError("typed-hole"), message, display_range.range)
+ .with_fixes(fixes)
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 034e4fcfb..7de9a9a32 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: undeclared-label
pub(crate) fn undeclared_label(
@@ -6,10 +6,11 @@ pub(crate) fn undeclared_label(
d: &hir::UndeclaredLabel,
) -> Diagnostic {
let name = &d.name;
- Diagnostic::new(
- "undeclared-label",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("undeclared-label"),
format!("use of undeclared label `{}`", name.display(ctx.sema.db)),
- ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ d.node.clone().map(|it| it.into()),
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
index e879de75c..bcce72a7d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: unimplemented-builtin-macro
//
@@ -7,10 +7,10 @@ pub(crate) fn unimplemented_builtin_macro(
ctx: &DiagnosticsContext<'_>,
d: &hir::UnimplementedBuiltinMacro,
) -> Diagnostic {
- Diagnostic::new(
- "unimplemented-builtin-macro",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::Ra("unimplemented-builtin-macro", Severity::WeakWarning),
"unimplemented built-in macro".to_string(),
- ctx.sema.diagnostics_display_range(d.node.clone()).range,
+ d.node.clone(),
)
- .severity(Severity::WeakWarning)
}
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 271e7ce73..e04f27c27 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
@@ -14,7 +14,7 @@ use syntax::{
};
use text_edit::TextEdit;
-use crate::{fix, Assist, Diagnostic, DiagnosticsContext, Severity};
+use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: unlinked-file
//
@@ -46,8 +46,7 @@ pub(crate) fn unlinked_file(
.unwrap_or(range);
acc.push(
- Diagnostic::new("unlinked-file", message, range)
- .severity(Severity::WeakWarning)
+ Diagnostic::new(DiagnosticCode::Ra("unlinked-file", Severity::WeakWarning), message, range)
.with_fixes(fixes),
);
}
@@ -119,10 +118,11 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
stack.pop();
'crates: for &krate in ctx.sema.db.relevant_crates(parent_id).iter() {
let crate_def_map = ctx.sema.db.crate_def_map(krate);
- let Some((_, module)) =
- crate_def_map.modules()
- .find(|(_, module)| module.origin.file_id() == Some(parent_id) && !module.origin.is_inline())
- else { continue };
+ let Some((_, module)) = crate_def_map.modules().find(|(_, module)| {
+ module.origin.file_id() == Some(parent_id) && !module.origin.is_inline()
+ }) else {
+ continue;
+ };
if stack.is_empty() {
return make_fixes(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
index 9fedadeae..1c5d6cd09 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unreachable-label
pub(crate) fn unreachable_label(
@@ -6,10 +6,11 @@ pub(crate) fn unreachable_label(
d: &hir::UnreachableLabel,
) -> Diagnostic {
let name = &d.name;
- Diagnostic::new(
- "unreachable-label",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0767"),
format!("use of unreachable label `{}`", name.display(ctx.sema.db)),
- ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ d.node.clone().map(|it| it.into()),
)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
index 74e4a69c6..f8265b632 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-extern-crate
//
@@ -7,10 +7,11 @@ pub(crate) fn unresolved_extern_crate(
ctx: &DiagnosticsContext<'_>,
d: &hir::UnresolvedExternCrate,
) -> Diagnostic {
- Diagnostic::new(
- "unresolved-extern-crate",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("unresolved-extern-crate"),
"unresolved extern crate",
- ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
+ d.decl.clone().map(|it| it.into()),
)
}
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 5e4efa41f..0758706e4 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, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-field
//
@@ -22,14 +22,15 @@ pub(crate) fn unresolved_field(
} else {
""
};
- Diagnostic::new(
- "unresolved-field",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0559"),
format!(
"no field `{}` on type `{}`{method_suffix}",
d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
- ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+ d.expr.clone().map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
.experimental()
@@ -67,7 +68,10 @@ fn method_fix(
}
#[cfg(test)]
mod tests {
- use crate::tests::check_diagnostics;
+ use crate::{
+ tests::{check_diagnostics, check_diagnostics_with_config},
+ DiagnosticsConfig,
+ };
#[test]
fn smoke_test() {
@@ -145,4 +149,11 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn no_diagnostic_for_missing_name() {
+ let mut config = DiagnosticsConfig::test_sample();
+ config.disabled.insert("syntax-error".to_owned());
+ check_diagnostics_with_config(config, "fn foo() { (). }");
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
index e52a88459..6b8026c03 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-import
//
@@ -8,10 +8,11 @@ pub(crate) fn unresolved_import(
ctx: &DiagnosticsContext<'_>,
d: &hir::UnresolvedImport,
) -> Diagnostic {
- Diagnostic::new(
- "unresolved-import",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0432"),
"unresolved import",
- ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
+ d.decl.clone().map(|it| it.into()),
)
// This currently results in false positives in the following cases:
// - `cfg_if!`-generated code in libstd (we don't load the sysroot correctly)
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 3943b51ab..33e7c2e37 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
@@ -1,4 +1,4 @@
-use crate::{Diagnostic, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-macro-call
//
@@ -12,7 +12,7 @@ pub(crate) fn unresolved_macro_call(
let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location);
let bang = if d.is_bang { "!" } else { "" };
Diagnostic::new(
- "unresolved-macro-call",
+ DiagnosticCode::RustcHardError("unresolved-macro-call"),
format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)),
display_range,
)
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 8bbb837e6..ae9f6744c 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, DiagnosticsContext};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-method
//
@@ -22,14 +22,15 @@ pub(crate) fn unresolved_method(
} else {
""
};
- Diagnostic::new(
- "unresolved-method",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0599"),
format!(
"no method `{}` on type `{}`{field_suffix}",
d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
- ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
+ d.expr.clone().map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
.experimental()
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 6e3fd3b42..be24e50c9 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
@@ -3,7 +3,7 @@ use ide_db::{assists::Assist, base_db::AnchoredPathBuf, source_change::FileSyste
use itertools::Itertools;
use syntax::AstNode;
-use crate::{fix, Diagnostic, DiagnosticsContext};
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unresolved-module
//
@@ -12,8 +12,9 @@ pub(crate) fn unresolved_module(
ctx: &DiagnosticsContext<'_>,
d: &hir::UnresolvedModule,
) -> Diagnostic {
- Diagnostic::new(
- "unresolved-module",
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0583"),
match &*d.candidates {
[] => "unresolved module".to_string(),
[candidate] => format!("unresolved module, can't find module file: {candidate}"),
@@ -25,7 +26,7 @@ pub(crate) fn unresolved_module(
)
}
},
- ctx.sema.diagnostics_display_range(d.decl.clone().map(|it| it.into())).range,
+ d.decl.clone().map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
}
@@ -82,8 +83,8 @@ mod baz {}
expect![[r#"
[
Diagnostic {
- code: DiagnosticCode(
- "unresolved-module",
+ code: RustcHardError(
+ "E0583",
),
message: "unresolved module, can't find module file: foo.rs, or foo/mod.rs",
range: 0..8,
@@ -148,6 +149,22 @@ mod baz {}
},
],
),
+ main_node: Some(
+ InFile {
+ file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ value: MODULE@0..8
+ MOD_KW@0..3 "mod"
+ WHITESPACE@3..4 " "
+ NAME@4..7
+ IDENT@4..7 "foo"
+ SEMICOLON@7..8 ";"
+ ,
+ },
+ ),
},
]
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
index ae5cf1358..015a3d6b2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -1,6 +1,6 @@
use hir::db::DefDatabase;
-use crate::{Diagnostic, DiagnosticsContext, Severity};
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: unresolved-proc-macro
//
@@ -41,5 +41,5 @@ pub(crate) fn unresolved_proc_macro(
};
let message = format!("{not_expanded_message}: {message}");
- Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity)
+ Diagnostic::new(DiagnosticCode::Ra("unresolved-proc-macro", severity), message, display_range)
}
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 289ed0458..0aa439f79 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,9 +1,9 @@
use ide_db::{base_db::FileId, source_change::SourceChange};
use itertools::Itertools;
-use syntax::{ast, AstNode, SyntaxNode, TextRange};
+use syntax::{ast, AstNode, SyntaxNode};
use text_edit::TextEdit;
-use crate::{fix, Diagnostic, Severity};
+use crate::{fix, Diagnostic, DiagnosticCode};
// Diagnostic: unnecessary-braces
//
@@ -15,6 +15,11 @@ pub(crate) fn useless_braces(
) -> Option<()> {
let use_tree_list = ast::UseTreeList::cast(node.clone())?;
if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
+ // If there is a `self` inside the bracketed `use`, don't show diagnostic.
+ if single_use_tree.path()?.segment()?.self_token().is_some() {
+ return Some(());
+ }
+
// If there is a comment inside the bracketed `use`,
// assume it is a commented out module path and don't show diagnostic.
if use_tree_list.has_inner_comment() {
@@ -22,21 +27,18 @@ pub(crate) fn useless_braces(
}
let use_range = use_tree_list.syntax().text_range();
- let edit = remove_braces(&single_use_tree).unwrap_or_else(|| {
- let to_replace = single_use_tree.syntax().text().to_string();
- let mut edit_builder = TextEdit::builder();
- edit_builder.delete(use_range);
- edit_builder.insert(use_range.start(), to_replace);
- edit_builder.finish()
- });
+ let to_replace = single_use_tree.syntax().text().to_string();
+ let mut edit_builder = TextEdit::builder();
+ edit_builder.delete(use_range);
+ edit_builder.insert(use_range.start(), to_replace);
+ let edit = edit_builder.finish();
acc.push(
Diagnostic::new(
- "unnecessary-braces",
+ DiagnosticCode::RustcLint("unused_braces"),
"Unnecessary braces in use statement".to_string(),
use_range,
)
- .severity(Severity::WeakWarning)
.with_fixes(Some(vec![fix(
"remove_braces",
"Remove unnecessary braces",
@@ -49,19 +51,12 @@ pub(crate) fn useless_braces(
Some(())
}
-fn remove_braces(single_use_tree: &ast::UseTree) -> Option<TextEdit> {
- let use_tree_list_node = single_use_tree.syntax().parent()?;
- if single_use_tree.path()?.segment()?.self_token().is_some() {
- let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
- let end = use_tree_list_node.text_range().end();
- return Some(TextEdit::delete(TextRange::new(start, end)));
- }
- None
-}
-
#[cfg(test)]
mod tests {
- use crate::tests::{check_diagnostics, check_fix};
+ use crate::{
+ tests::{check_diagnostics, check_diagnostics_with_config, check_fix},
+ DiagnosticsConfig,
+ };
#[test]
fn test_check_unnecessary_braces_in_use_statement() {
@@ -94,6 +89,32 @@ mod a {
}
"#,
);
+ check_diagnostics(
+ r#"
+use a::{self};
+
+mod a {
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+use a::{self as cool_name};
+
+mod a {
+}
+"#,
+ );
+
+ let mut config = DiagnosticsConfig::test_sample();
+ config.disabled.insert("syntax-error".to_string());
+ check_diagnostics_with_config(
+ config,
+ r#"
+mod a { pub mod b {} }
+use a::{b::self};
+"#,
+ );
check_fix(
r#"
mod b {}
@@ -126,16 +147,6 @@ use a::c;
);
check_fix(
r#"
-mod a {}
-use a::{self$0};
-"#,
- r#"
-mod a {}
-use a;
-"#,
- );
- check_fix(
- r#"
mod a { pub mod c {} pub mod d { pub mod e {} } }
use a::{c, d::{e$0}};
"#,
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 55a4a482d..b1b9b4b8e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -67,24 +67,61 @@ mod handlers {
#[cfg(test)]
mod tests;
+use std::collections::HashMap;
+
use hir::{diagnostics::AnyDiagnostic, InFile, Semantics};
use ide_db::{
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
base_db::{FileId, FileRange, SourceDatabase},
+ generated::lints::{LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS},
imports::insert_use::InsertUseConfig,
label::Label,
source_change::SourceChange,
- FxHashSet, RootDatabase,
+ syntax_helpers::node_ext::parse_tt_as_comma_sep_paths,
+ FxHashMap, FxHashSet, RootDatabase,
+};
+use once_cell::sync::Lazy;
+use stdx::never;
+use syntax::{
+ algo::find_node_at_range,
+ ast::{self, AstNode},
+ SyntaxNode, SyntaxNodePtr, TextRange,
};
-use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
// FIXME: Make this an enum
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub struct DiagnosticCode(pub &'static str);
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum DiagnosticCode {
+ RustcHardError(&'static str),
+ RustcLint(&'static str),
+ Clippy(&'static str),
+ Ra(&'static str, Severity),
+}
impl DiagnosticCode {
- pub fn as_str(&self) -> &str {
- self.0
+ pub fn url(&self) -> String {
+ match self {
+ DiagnosticCode::RustcHardError(e) => {
+ format!("https://doc.rust-lang.org/stable/error_codes/{e}.html")
+ }
+ DiagnosticCode::RustcLint(e) => {
+ format!("https://doc.rust-lang.org/rustc/?search={e}")
+ }
+ DiagnosticCode::Clippy(e) => {
+ format!("https://rust-lang.github.io/rust-clippy/master/#/{e}")
+ }
+ DiagnosticCode::Ra(e, _) => {
+ format!("https://rust-analyzer.github.io/manual.html#{e}")
+ }
+ }
+ }
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ DiagnosticCode::RustcHardError(r)
+ | DiagnosticCode::RustcLint(r)
+ | DiagnosticCode::Clippy(r)
+ | DiagnosticCode::Ra(r, _) => r,
+ }
}
}
@@ -97,29 +134,51 @@ pub struct Diagnostic {
pub unused: bool,
pub experimental: bool,
pub fixes: Option<Vec<Assist>>,
+ // The node that will be affected by `#[allow]` and similar attributes.
+ pub main_node: Option<InFile<SyntaxNode>>,
}
impl Diagnostic {
- fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
+ fn new(code: DiagnosticCode, message: impl Into<String>, range: TextRange) -> Diagnostic {
let message = message.into();
Diagnostic {
- code: DiagnosticCode(code),
+ code,
message,
range,
- severity: Severity::Error,
+ severity: match code {
+ DiagnosticCode::RustcHardError(_) => Severity::Error,
+ // FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings.
+ DiagnosticCode::RustcLint(_) => Severity::Warning,
+ // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can
+ // make it normal warning.
+ DiagnosticCode::Clippy(_) => Severity::WeakWarning,
+ DiagnosticCode::Ra(_, s) => s,
+ },
unused: false,
experimental: false,
fixes: None,
+ main_node: None,
}
}
+ fn new_with_syntax_node_ptr(
+ ctx: &DiagnosticsContext<'_>,
+ code: DiagnosticCode,
+ message: impl Into<String>,
+ node: InFile<SyntaxNodePtr>,
+ ) -> Diagnostic {
+ let file_id = node.file_id;
+ Diagnostic::new(code, message, ctx.sema.diagnostics_display_range(node.clone()).range)
+ .with_main_node(node.map(|x| x.to_node(&ctx.sema.parse_or_expand(file_id))))
+ }
+
fn experimental(mut self) -> Diagnostic {
self.experimental = true;
self
}
- fn severity(mut self, severity: Severity) -> Diagnostic {
- self.severity = severity;
+ fn with_main_node(mut self, main_node: InFile<SyntaxNode>) -> Diagnostic {
+ self.main_node = Some(main_node);
self
}
@@ -134,12 +193,12 @@ impl Diagnostic {
}
}
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Severity {
Error,
- // We don't actually emit this one yet, but we should at some point.
- // Warning,
+ Warning,
WeakWarning,
+ Allow,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -155,6 +214,8 @@ impl Default for ExprFillDefaultMode {
#[derive(Debug, Clone)]
pub struct DiagnosticsConfig {
+ /// Whether native diagnostics are enabled.
+ pub enabled: bool,
pub proc_macros_enabled: bool,
pub proc_attr_macros_enabled: bool,
pub disable_experimental: bool,
@@ -171,6 +232,7 @@ impl DiagnosticsConfig {
use ide_db::imports::insert_use::ImportGranularity;
Self {
+ enabled: true,
proc_macros_enabled: Default::default(),
proc_attr_macros_enabled: Default::default(),
disable_experimental: Default::default(),
@@ -194,7 +256,7 @@ struct DiagnosticsContext<'a> {
resolve: &'a AssistResolveStrategy,
}
-impl<'a> DiagnosticsContext<'a> {
+impl DiagnosticsContext<'_> {
fn resolve_precise_location(
&self,
node: &InFile<SyntaxNodePtr>,
@@ -228,11 +290,13 @@ pub fn diagnostics(
let mut res = Vec::new();
// [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
- res.extend(
- parse.errors().iter().take(128).map(|err| {
- Diagnostic::new("syntax-error", format!("Syntax Error: {err}"), err.range())
- }),
- );
+ res.extend(parse.errors().iter().take(128).map(|err| {
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("syntax-error"),
+ format!("Syntax Error: {err}"),
+ err.range(),
+ )
+ }));
let parse = sema.parse(file_id);
@@ -271,7 +335,7 @@ pub fn diagnostics(
res.extend(d.errors.iter().take(32).map(|err| {
{
Diagnostic::new(
- "syntax-error",
+ DiagnosticCode::RustcHardError("syntax-error"),
format!("Syntax Error in Expansion: {err}"),
ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
)
@@ -309,14 +373,168 @@ pub fn diagnostics(
res.push(d)
}
+ let mut diagnostics_of_range =
+ res.iter_mut().filter_map(|x| Some((x.main_node.clone()?, x))).collect::<FxHashMap<_, _>>();
+
+ let mut rustc_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
+ let mut clippy_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
+
+ handle_lint_attributes(
+ &ctx.sema,
+ parse.syntax(),
+ &mut rustc_stack,
+ &mut clippy_stack,
+ &mut diagnostics_of_range,
+ );
+
res.retain(|d| {
- !ctx.config.disabled.contains(d.code.as_str())
+ d.severity != Severity::Allow
+ && !ctx.config.disabled.contains(d.code.as_str())
&& !(ctx.config.disable_experimental && d.experimental)
});
res
}
+// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
+
+static RUSTC_LINT_GROUPS_DICT: Lazy<HashMap<&str, Vec<&str>>> =
+ Lazy::new(|| build_group_dict(DEFAULT_LINT_GROUPS, &["warnings", "__RA_EVERY_LINT"], ""));
+
+static CLIPPY_LINT_GROUPS_DICT: Lazy<HashMap<&str, Vec<&str>>> =
+ Lazy::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::"));
+
+fn build_group_dict(
+ lint_group: &'static [LintGroup],
+ all_groups: &'static [&'static str],
+ prefix: &'static str,
+) -> HashMap<&'static str, Vec<&'static str>> {
+ let mut r: HashMap<&str, Vec<&str>> = HashMap::new();
+ for g in lint_group {
+ for child in g.children {
+ r.entry(child.strip_prefix(prefix).unwrap())
+ .or_default()
+ .push(g.lint.label.strip_prefix(prefix).unwrap());
+ }
+ }
+ for (lint, groups) in r.iter_mut() {
+ groups.push(lint);
+ groups.extend_from_slice(all_groups);
+ }
+ r
+}
+
+fn handle_lint_attributes(
+ sema: &Semantics<'_, RootDatabase>,
+ root: &SyntaxNode,
+ rustc_stack: &mut FxHashMap<String, Vec<Severity>>,
+ clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
+ diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
+) {
+ let file_id = sema.hir_file_for(root);
+ for ev in root.preorder() {
+ match ev {
+ syntax::WalkEvent::Enter(node) => {
+ for attr in node.children().filter_map(ast::Attr::cast) {
+ parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| {
+ stack.push(severity);
+ });
+ }
+ if let Some(x) =
+ diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() })
+ {
+ const EMPTY_LINTS: &[&str] = &[];
+ let (names, stack) = match x.code {
+ DiagnosticCode::RustcLint(name) => (
+ RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+ &mut *rustc_stack,
+ ),
+ DiagnosticCode::Clippy(name) => (
+ CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+ &mut *clippy_stack,
+ ),
+ _ => continue,
+ };
+ for &name in names {
+ if let Some(s) = stack.get(name).and_then(|x| x.last()) {
+ x.severity = *s;
+ }
+ }
+ }
+ if let Some(item) = ast::Item::cast(node.clone()) {
+ if let Some(me) = sema.expand_attr_macro(&item) {
+ for stack in [&mut *rustc_stack, &mut *clippy_stack] {
+ stack
+ .entry("__RA_EVERY_LINT".to_owned())
+ .or_default()
+ .push(Severity::Allow);
+ }
+ handle_lint_attributes(
+ sema,
+ &me,
+ rustc_stack,
+ clippy_stack,
+ diagnostics_of_range,
+ );
+ for stack in [&mut *rustc_stack, &mut *clippy_stack] {
+ stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop();
+ }
+ }
+ }
+ if let Some(mc) = ast::MacroCall::cast(node) {
+ if let Some(me) = sema.expand(&mc) {
+ handle_lint_attributes(
+ sema,
+ &me,
+ rustc_stack,
+ clippy_stack,
+ diagnostics_of_range,
+ );
+ }
+ }
+ }
+ syntax::WalkEvent::Leave(node) => {
+ for attr in node.children().filter_map(ast::Attr::cast) {
+ parse_lint_attribute(attr, rustc_stack, clippy_stack, |stack, severity| {
+ if stack.pop() != Some(severity) {
+ never!("Mismatched serevity in walking lint attributes");
+ }
+ });
+ }
+ }
+ }
+ }
+}
+
+fn parse_lint_attribute(
+ attr: ast::Attr,
+ rustc_stack: &mut FxHashMap<String, Vec<Severity>>,
+ clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
+ job: impl Fn(&mut Vec<Severity>, Severity),
+) {
+ let Some((tag, args_tt)) = attr.as_simple_call() else {
+ return;
+ };
+ let serevity = match tag.as_str() {
+ "allow" => Severity::Allow,
+ "warn" => Severity::Warning,
+ "forbid" | "deny" => Severity::Error,
+ _ => return,
+ };
+ for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() {
+ if let Some(lint) = lint.as_single_name_ref() {
+ job(rustc_stack.entry(lint.to_string()).or_default(), serevity);
+ }
+ if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) {
+ if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) {
+ if tool.to_string() == "clippy" {
+ job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity);
+ }
+ }
+ }
+ }
+}
+
fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
let mut res = unresolved_fix(id, label, target);
res.source_change = Some(source_change);
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 b5cd4e0d6..ee0e03549 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -49,8 +49,11 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
- for edit in source_change.source_file_edits.values() {
+ for (edit, snippet_edit) in source_change.source_file_edits.values() {
edit.apply(&mut actual);
+ if let Some(snippet_edit) = snippet_edit {
+ snippet_edit.apply(&mut actual);
+ }
}
actual
};
@@ -114,6 +117,8 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
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);
@@ -130,14 +135,19 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
)
}
}
- assert_eq!(expected, actual);
+ 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<_>>();
+
+ panic!("Diagnostic test failed.\nFalse negatives: {fneg:?}\nFalse positives: {fpos:?}");
+ }
}
}
#[test]
fn test_disabled_diagnostics() {
let mut config = DiagnosticsConfig::test_sample();
- config.disabled.insert("unresolved-module".into());
+ config.disabled.insert("E0583".into());
let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#);
@@ -159,7 +169,7 @@ fn minicore_smoke_test() {
let source = minicore.source_code();
let mut config = DiagnosticsConfig::test_sample();
// This should be ignored since we conditionaly remove code which creates single item use with braces
- config.disabled.insert("unnecessary-braces".to_string());
+ config.disabled.insert("unused_braces".to_string());
check_diagnostics_with_config(config, &source);
}