mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Remove this semicolon
This commit is contained in:
parent
e2e6b709e6
commit
cb66bb8ff9
5 changed files with 74 additions and 8 deletions
|
@ -5,5 +5,5 @@ pub use hir_expand::diagnostics::{
|
||||||
};
|
};
|
||||||
pub use hir_ty::diagnostics::{
|
pub use hir_ty::diagnostics::{
|
||||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
|
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
|
||||||
NoSuchField,
|
NoSuchField, RemoveThisSemicolon
|
||||||
};
|
};
|
||||||
|
|
|
@ -216,6 +216,30 @@ impl Diagnostic for MissingOkInTailExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RemoveThisSemicolon {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub expr: AstPtr<ast::Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for RemoveThisSemicolon {
|
||||||
|
fn code(&self) -> DiagnosticCode {
|
||||||
|
DiagnosticCode("remove-this-semicolon")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self) -> String {
|
||||||
|
"Remove this semicolon".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile { file_id: self.file, value: self.expr.clone().into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Diagnostic: break-outside-of-loop
|
// Diagnostic: break-outside-of-loop
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if `break` keyword is used outside of a loop.
|
// This diagnostic is triggered if `break` keyword is used outside of a loop.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
|
use hir_def::{AdtId, DefWithBodyId, expr::Statement, path::path, resolver::HasResolver};
|
||||||
use hir_expand::diagnostics::DiagnosticSink;
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use syntax::{ast, AstPtr};
|
use syntax::{ast, AstPtr};
|
||||||
|
@ -23,6 +23,8 @@ pub(crate) use hir_def::{
|
||||||
LocalFieldId, VariantId,
|
LocalFieldId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::RemoveThisSemicolon;
|
||||||
|
|
||||||
pub(super) struct ExprValidator<'a, 'b: 'a> {
|
pub(super) struct ExprValidator<'a, 'b: 'a> {
|
||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
infer: Arc<InferenceResult>,
|
infer: Arc<InferenceResult>,
|
||||||
|
@ -78,6 +80,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
let body_expr = &body[body.body_expr];
|
let body_expr = &body[body.body_expr];
|
||||||
if let Expr::Block { tail: Some(t), .. } = body_expr {
|
if let Expr::Block { tail: Some(t), .. } = body_expr {
|
||||||
self.validate_results_in_tail_expr(body.body_expr, *t, db);
|
self.validate_results_in_tail_expr(body.body_expr, *t, db);
|
||||||
|
} else {
|
||||||
|
if let Expr::Block { statements, .. } = body_expr {
|
||||||
|
if let Some(Statement::Expr(id)) = statements.last() {
|
||||||
|
self.validate_missing_tail_expr(body.body_expr, *id, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +325,23 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId, db: &dyn HirDatabase) {
|
||||||
|
let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
|
||||||
|
Some(m) => m,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(possible_tail_ty) = self.infer.type_of_expr.get(possible_tail_id) {
|
||||||
|
if mismatch.actual == Ty::unit() && mismatch.expected == *possible_tail_ty {
|
||||||
|
let (_, source_map) = db.body_with_source_map(self.owner.into());
|
||||||
|
|
||||||
|
if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
|
||||||
|
self.sink.push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn record_literal_missing_fields(
|
pub fn record_literal_missing_fields(
|
||||||
|
|
|
@ -131,6 +131,9 @@ pub(crate) fn diagnostics(
|
||||||
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
.on::<hir::diagnostics::NoSuchField, _>(|d| {
|
||||||
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
|
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
|
||||||
})
|
})
|
||||||
|
.on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
|
||||||
|
res.borrow_mut().push(diagnostic_with_fix(d, &sema));
|
||||||
|
})
|
||||||
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
|
.on::<hir::diagnostics::IncorrectCase, _>(|d| {
|
||||||
res.borrow_mut().push(warning_with_fix(d, &sema));
|
res.borrow_mut().push(warning_with_fix(d, &sema));
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,7 @@ use hir::{
|
||||||
db::AstDatabase,
|
db::AstDatabase,
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
|
Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField,
|
||||||
UnresolvedModule,
|
RemoveThisSemicolon, UnresolvedModule,
|
||||||
},
|
},
|
||||||
HasSource, HirDisplay, Semantics, VariantDef,
|
HasSource, HirDisplay, Semantics, VariantDef,
|
||||||
};
|
};
|
||||||
|
@ -13,11 +13,7 @@ use ide_db::{
|
||||||
source_change::{FileSystemEdit, SourceFileEdit},
|
source_change::{FileSystemEdit, SourceFileEdit},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{AstNode, Direction, T, algo, ast::{self, ExprStmt, edit::IndentLevel, make}};
|
||||||
algo,
|
|
||||||
ast::{self, edit::IndentLevel, make},
|
|
||||||
AstNode,
|
|
||||||
};
|
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
|
use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition};
|
||||||
|
@ -102,6 +98,24 @@ impl DiagnosticWithFix for MissingOkInTailExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DiagnosticWithFix for RemoveThisSemicolon {
|
||||||
|
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
|
||||||
|
let root = sema.db.parse_or_expand(self.file)?;
|
||||||
|
|
||||||
|
let semicolon = self.expr.to_node(&root)
|
||||||
|
.syntax()
|
||||||
|
.siblings_with_tokens(Direction::Next)
|
||||||
|
.filter_map(|it| it.into_token())
|
||||||
|
.find(|it| it.kind() == T![;])?
|
||||||
|
.text_range();
|
||||||
|
|
||||||
|
let edit = TextEdit::delete(semicolon);
|
||||||
|
let source_change = SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
|
||||||
|
|
||||||
|
Some(Fix::new("Remove this semicolon", source_change, semicolon))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DiagnosticWithFix for IncorrectCase {
|
impl DiagnosticWithFix for IncorrectCase {
|
||||||
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
|
fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
|
||||||
let root = sema.db.parse_or_expand(self.file)?;
|
let root = sema.db.parse_or_expand(self.file)?;
|
||||||
|
|
Loading…
Reference in a new issue