mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 03:38:29 +00:00
internal: move missing_fields diagnostics
This commit is contained in:
parent
efa069d288
commit
c6509a4592
@ -16,7 +16,7 @@ pub use crate::diagnostics_sink::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! diagnostics {
|
macro_rules! diagnostics {
|
||||||
($($diag:ident)*) => {
|
($($diag:ident),*) => {
|
||||||
pub enum AnyDiagnostic {$(
|
pub enum AnyDiagnostic {$(
|
||||||
$diag(Box<$diag>),
|
$diag(Box<$diag>),
|
||||||
)*}
|
)*}
|
||||||
@ -31,7 +31,7 @@ macro_rules! diagnostics {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics![UnresolvedModule];
|
diagnostics![UnresolvedModule, MissingFields];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnresolvedModule {
|
pub struct UnresolvedModule {
|
||||||
@ -321,17 +321,6 @@ impl Diagnostic for MissingUnsafe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic: missing-structure-fields
|
|
||||||
//
|
|
||||||
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// ```rust
|
|
||||||
// struct A { a: u8, b: u8 }
|
|
||||||
//
|
|
||||||
// let a = A { a: 10 };
|
|
||||||
// ```
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MissingFields {
|
pub struct MissingFields {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
@ -340,34 +329,6 @@ pub struct MissingFields {
|
|||||||
pub missed_fields: Vec<Name>,
|
pub missed_fields: Vec<Name>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic for MissingFields {
|
|
||||||
fn code(&self) -> DiagnosticCode {
|
|
||||||
DiagnosticCode("missing-structure-fields")
|
|
||||||
}
|
|
||||||
fn message(&self) -> String {
|
|
||||||
let mut buf = String::from("Missing structure fields:\n");
|
|
||||||
for field in &self.missed_fields {
|
|
||||||
format_to!(buf, "- {}\n", field);
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
|
||||||
InFile {
|
|
||||||
file_id: self.file,
|
|
||||||
value: self
|
|
||||||
.field_list_parent_path
|
|
||||||
.clone()
|
|
||||||
.map(SyntaxNodePtr::from)
|
|
||||||
.unwrap_or_else(|| self.field_list_parent.clone().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diagnostic: missing-pat-fields
|
// Diagnostic: missing-pat-fields
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
|
// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
|
||||||
|
@ -609,23 +609,21 @@ impl Module {
|
|||||||
}
|
}
|
||||||
for decl in self.declarations(db) {
|
for decl in self.declarations(db) {
|
||||||
match decl {
|
match decl {
|
||||||
crate::ModuleDef::Function(f) => f.diagnostics(db, sink, internal_diagnostics),
|
ModuleDef::Function(f) => acc.extend(f.diagnostics(db, sink, internal_diagnostics)),
|
||||||
crate::ModuleDef::Module(m) => {
|
ModuleDef::Module(m) => {
|
||||||
// Only add diagnostics from inline modules
|
// Only add diagnostics from inline modules
|
||||||
if def_map[m.id.local_id].origin.is_inline() {
|
if def_map[m.id.local_id].origin.is_inline() {
|
||||||
acc.extend(m.diagnostics(db, sink, internal_diagnostics))
|
acc.extend(m.diagnostics(db, sink, internal_diagnostics))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => decl.diagnostics(db, sink),
|
||||||
decl.diagnostics(db, sink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for impl_def in self.impl_defs(db) {
|
for impl_def in self.impl_defs(db) {
|
||||||
for item in impl_def.items(db) {
|
for item in impl_def.items(db) {
|
||||||
if let AssocItem::Function(f) = item {
|
if let AssocItem::Function(f) = item {
|
||||||
f.diagnostics(db, sink, internal_diagnostics);
|
acc.extend(f.diagnostics(db, sink, internal_diagnostics));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1033,7 +1031,8 @@ impl Function {
|
|||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
sink: &mut DiagnosticSink,
|
sink: &mut DiagnosticSink,
|
||||||
internal_diagnostics: bool,
|
internal_diagnostics: bool,
|
||||||
) {
|
) -> Vec<AnyDiagnostic> {
|
||||||
|
let mut acc: Vec<AnyDiagnostic> = Vec::new();
|
||||||
let krate = self.module(db).id.krate();
|
let krate = self.module(db).id.krate();
|
||||||
|
|
||||||
let source_map = db.body_with_source_map(self.id.into()).1;
|
let source_map = db.body_with_source_map(self.id.into()).1;
|
||||||
@ -1114,14 +1113,17 @@ impl Function {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|idx| variant_data.fields()[idx].name.clone())
|
.map(|idx| variant_data.fields()[idx].name.clone())
|
||||||
.collect();
|
.collect();
|
||||||
sink.push(MissingFields {
|
acc.push(
|
||||||
file: source_ptr.file_id,
|
MissingFields {
|
||||||
field_list_parent: AstPtr::new(record_expr),
|
file: source_ptr.file_id,
|
||||||
field_list_parent_path: record_expr
|
field_list_parent: AstPtr::new(record_expr),
|
||||||
.path()
|
field_list_parent_path: record_expr
|
||||||
.map(|path| AstPtr::new(&path)),
|
.path()
|
||||||
missed_fields,
|
.map(|path| AstPtr::new(&path)),
|
||||||
})
|
missed_fields,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1234,6 +1236,7 @@ impl Function {
|
|||||||
for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
|
for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
|
||||||
sink.push(diag)
|
sink.push(diag)
|
||||||
}
|
}
|
||||||
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this function declaration has a definition.
|
/// Whether this function declaration has a definition.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
//! original files. So we need to map the ranges.
|
//! original files. So we need to map the ranges.
|
||||||
|
|
||||||
mod unresolved_module;
|
mod unresolved_module;
|
||||||
|
mod missing_fields;
|
||||||
|
|
||||||
mod fixes;
|
mod fixes;
|
||||||
mod field_shorthand;
|
mod field_shorthand;
|
||||||
@ -123,9 +124,6 @@ pub(crate) fn diagnostics(
|
|||||||
}
|
}
|
||||||
let res = RefCell::new(res);
|
let res = RefCell::new(res);
|
||||||
let sink_builder = DiagnosticSinkBuilder::new()
|
let sink_builder = DiagnosticSinkBuilder::new()
|
||||||
.on::<hir::diagnostics::MissingFields, _>(|d| {
|
|
||||||
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
|
|
||||||
})
|
|
||||||
.on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
|
.on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
|
||||||
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
|
res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
|
||||||
})
|
})
|
||||||
@ -232,7 +230,8 @@ pub(crate) fn diagnostics(
|
|||||||
let ctx = DiagnosticsContext { config, sema, resolve };
|
let ctx = DiagnosticsContext { config, sema, resolve };
|
||||||
for diag in diags {
|
for diag in diags {
|
||||||
let d = match diag {
|
let d = match diag {
|
||||||
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::render(&ctx, &d),
|
AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
|
||||||
|
AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
|
||||||
};
|
};
|
||||||
if let Some(code) = d.code {
|
if let Some(code) = d.code {
|
||||||
if ctx.config.disabled.contains(code.as_str()) {
|
if ctx.config.disabled.contains(code.as_str()) {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
//! The same module also has all curret custom fixes for the diagnostics implemented.
|
//! The same module also has all curret custom fixes for the diagnostics implemented.
|
||||||
mod change_case;
|
mod change_case;
|
||||||
mod create_field;
|
mod create_field;
|
||||||
mod fill_missing_fields;
|
|
||||||
mod remove_semicolon;
|
mod remove_semicolon;
|
||||||
mod replace_with_find_map;
|
mod replace_with_find_map;
|
||||||
mod wrap_tail_expr;
|
mod wrap_tail_expr;
|
||||||
|
@ -1,53 +1,77 @@
|
|||||||
use hir::{db::AstDatabase, diagnostics::MissingFields, Semantics};
|
use hir::{db::AstDatabase, InFile};
|
||||||
use ide_assists::AssistResolveStrategy;
|
use ide_assists::Assist;
|
||||||
use ide_db::{source_change::SourceChange, RootDatabase};
|
use ide_db::source_change::SourceChange;
|
||||||
use syntax::{algo, ast::make, AstNode};
|
use stdx::format_to;
|
||||||
|
use syntax::{algo, ast::make, AstNode, SyntaxNodePtr};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{
|
use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
|
||||||
diagnostics::{fix, fixes::DiagnosticWithFixes},
|
|
||||||
Assist,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl DiagnosticWithFixes for MissingFields {
|
// Diagnostic: missing-structure-fields
|
||||||
fn fixes(
|
//
|
||||||
&self,
|
// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
|
||||||
sema: &Semantics<RootDatabase>,
|
//
|
||||||
_resolve: &AssistResolveStrategy,
|
// Example:
|
||||||
) -> Option<Vec<Assist>> {
|
//
|
||||||
// Note that although we could add a diagnostics to
|
// ```rust
|
||||||
// fill the missing tuple field, e.g :
|
// struct A { a: u8, b: u8 }
|
||||||
// `struct A(usize);`
|
//
|
||||||
// `let a = A { 0: () }`
|
// let a = A { a: 10 };
|
||||||
// but it is uncommon usage and it should not be encouraged.
|
// ```
|
||||||
if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
|
pub(super) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
|
||||||
return None;
|
let mut message = String::from("Missing structure fields:\n");
|
||||||
}
|
for field in &d.missed_fields {
|
||||||
|
format_to!(message, "- {}\n", field);
|
||||||
let root = sema.db.parse_or_expand(self.file)?;
|
|
||||||
let field_list_parent = self.field_list_parent.to_node(&root);
|
|
||||||
let old_field_list = field_list_parent.record_expr_field_list()?;
|
|
||||||
let new_field_list = old_field_list.clone_for_update();
|
|
||||||
for f in self.missed_fields.iter() {
|
|
||||||
let field =
|
|
||||||
make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
|
|
||||||
.clone_for_update();
|
|
||||||
new_field_list.add_field(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
let edit = {
|
|
||||||
let mut builder = TextEdit::builder();
|
|
||||||
algo::diff(old_field_list.syntax(), new_field_list.syntax())
|
|
||||||
.into_text_edit(&mut builder);
|
|
||||||
builder.finish()
|
|
||||||
};
|
|
||||||
Some(vec![fix(
|
|
||||||
"fill_missing_fields",
|
|
||||||
"Fill struct fields",
|
|
||||||
SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
|
|
||||||
sema.original_range(field_list_parent.syntax()).range,
|
|
||||||
)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ptr = InFile::new(
|
||||||
|
d.file,
|
||||||
|
d.field_list_parent_path
|
||||||
|
.clone()
|
||||||
|
.map(SyntaxNodePtr::from)
|
||||||
|
.unwrap_or_else(|| d.field_list_parent.clone().into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Diagnostic::new(
|
||||||
|
"missing-structure-fields",
|
||||||
|
message,
|
||||||
|
ctx.sema.diagnostics_display_range(ptr).range,
|
||||||
|
)
|
||||||
|
.with_fixes(fixes(ctx, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
|
||||||
|
// Note that although we could add a diagnostics to
|
||||||
|
// fill the missing tuple field, e.g :
|
||||||
|
// `struct A(usize);`
|
||||||
|
// `let a = A { 0: () }`
|
||||||
|
// but it is uncommon usage and it should not be encouraged.
|
||||||
|
if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let root = ctx.sema.db.parse_or_expand(d.file)?;
|
||||||
|
let field_list_parent = d.field_list_parent.to_node(&root);
|
||||||
|
let old_field_list = field_list_parent.record_expr_field_list()?;
|
||||||
|
let new_field_list = old_field_list.clone_for_update();
|
||||||
|
for f in d.missed_fields.iter() {
|
||||||
|
let field =
|
||||||
|
make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
|
||||||
|
.clone_for_update();
|
||||||
|
new_field_list.add_field(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
let edit = {
|
||||||
|
let mut builder = TextEdit::builder();
|
||||||
|
algo::diff(old_field_list.syntax(), new_field_list.syntax()).into_text_edit(&mut builder);
|
||||||
|
builder.finish()
|
||||||
|
};
|
||||||
|
Some(vec![fix(
|
||||||
|
"fill_missing_fields",
|
||||||
|
"Fill struct fields",
|
||||||
|
SourceChange::from_text_edit(d.file.original_file(ctx.sema.db), edit),
|
||||||
|
ctx.sema.original_range(field_list_parent.syntax()).range,
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
@ -8,7 +8,10 @@ use crate::diagnostics::{fix, Diagnostic, DiagnosticsContext};
|
|||||||
// Diagnostic: unresolved-module
|
// Diagnostic: unresolved-module
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
|
||||||
pub(super) fn render(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Diagnostic {
|
pub(super) fn unresolved_module(
|
||||||
|
ctx: &DiagnosticsContext<'_>,
|
||||||
|
d: &hir::UnresolvedModule,
|
||||||
|
) -> Diagnostic {
|
||||||
Diagnostic::new(
|
Diagnostic::new(
|
||||||
"unresolved-module",
|
"unresolved-module",
|
||||||
"unresolved module",
|
"unresolved module",
|
||||||
|
Loading…
Reference in New Issue
Block a user