mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Auto merge of #14990 - HKalbasi:diagnostic-map, r=HKalbasi
Map our diagnostics to rustc and clippy's ones And control their severity by lint attributes `#[allow]`, `#[deny]` and ... . It doesn't work with proc macros and I would like to fix that before merge but I don't know how to do it.
This commit is contained in:
commit
45272efec5
44 changed files with 628 additions and 251 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -760,6 +760,7 @@ dependencies = [
|
|||
"hir",
|
||||
"ide-db",
|
||||
"itertools",
|
||||
"once_cell",
|
||||
"profile",
|
||||
"serde_json",
|
||||
"sourcegen",
|
||||
|
|
|
@ -5,7 +5,7 @@ mod unsafe_check;
|
|||
mod decl_check;
|
||||
|
||||
pub use crate::diagnostics::{
|
||||
decl_check::{incorrect_case, IncorrectCase},
|
||||
decl_check::{incorrect_case, CaseType, IncorrectCase},
|
||||
expr::{
|
||||
record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
|
||||
},
|
||||
|
|
|
@ -57,11 +57,11 @@ pub fn incorrect_case(
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum CaseType {
|
||||
// `some_var`
|
||||
/// `some_var`
|
||||
LowerSnakeCase,
|
||||
// `SOME_CONST`
|
||||
/// `SOME_CONST`
|
||||
UpperSnakeCase,
|
||||
// `SomeStruct`
|
||||
/// `SomeStruct`
|
||||
UpperCamelCase,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//!
|
||||
//! This probably isn't the best way to do this -- ideally, diagnostics should
|
||||
//! be expressed in terms of hir types themselves.
|
||||
pub use hir_ty::diagnostics::{IncoherentImpl, IncorrectCase};
|
||||
pub use hir_ty::diagnostics::{CaseType, IncoherentImpl, IncorrectCase};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
|
|
|
@ -89,11 +89,11 @@ use crate::db::{DefDatabase, HirDatabase};
|
|||
pub use crate::{
|
||||
attrs::{HasAttrs, Namespace},
|
||||
diagnostics::{
|
||||
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
|
||||
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
|
||||
MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
|
||||
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
|
||||
ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
|
||||
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
|
||||
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
|
||||
MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||
MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem,
|
||||
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
|
||||
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
|
||||
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
|
||||
UnresolvedProcMacro, UnusedMut,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,60 @@ 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`
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,7 +792,7 @@ 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);
|
||||
}
|
||||
"#,
|
||||
|
@ -842,7 +844,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 +861,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 +869,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 +917,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 +951,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 +967,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 +981,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 +1003,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 +1057,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);
|
||||
|
@ -1080,19 +1082,40 @@ 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]
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(..)
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 ";"
|
||||
,
|
||||
},
|
||||
),
|
||||
},
|
||||
]
|
||||
"#]],
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use itertools::Itertools;
|
|||
use syntax::{ast, AstNode, SyntaxNode, TextRange};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{fix, Diagnostic, Severity};
|
||||
use crate::{fix, Diagnostic, DiagnosticCode};
|
||||
|
||||
// Diagnostic: unnecessary-braces
|
||||
//
|
||||
|
@ -32,11 +32,10 @@ pub(crate) fn useless_braces(
|
|||
|
||||
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",
|
||||
|
|
|
@ -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)]
|
||||
|
@ -231,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);
|
||||
|
||||
|
@ -274,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),
|
||||
)
|
||||
|
@ -312,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);
|
||||
|
|
|
@ -114,6 +114,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 +132,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 +166,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -842,11 +842,7 @@ impl GlobalState {
|
|||
d.code.as_str().to_string(),
|
||||
)),
|
||||
code_description: Some(lsp_types::CodeDescription {
|
||||
href: lsp_types::Url::parse(&format!(
|
||||
"https://rust-analyzer.github.io/manual.html#{}",
|
||||
d.code.as_str()
|
||||
))
|
||||
.unwrap(),
|
||||
href: lsp_types::Url::parse(&d.code.url()).unwrap(),
|
||||
}),
|
||||
source: Some("rust-analyzer".to_string()),
|
||||
message: d.message,
|
||||
|
|
|
@ -94,7 +94,10 @@ pub(crate) fn document_highlight_kind(
|
|||
pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
|
||||
match severity {
|
||||
Severity::Error => lsp_types::DiagnosticSeverity::ERROR,
|
||||
Severity::Warning => lsp_types::DiagnosticSeverity::WARNING,
|
||||
Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT,
|
||||
// unreachable
|
||||
Severity::Allow => lsp_types::DiagnosticSeverity::INFORMATION,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue