mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Add unsafe diagnostics and unsafe highlighting
This commit is contained in:
parent
9d1e2c4d9d
commit
0b95bed83f
5 changed files with 163 additions and 2 deletions
|
@ -36,6 +36,7 @@ use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{DefDatabase, HirDatabase},
|
db::{DefDatabase, HirDatabase},
|
||||||
|
diagnostics::UnsafeValidator,
|
||||||
has_source::HasSource,
|
has_source::HasSource,
|
||||||
CallableDef, HirDisplay, InFile, Name,
|
CallableDef, HirDisplay, InFile, Name,
|
||||||
};
|
};
|
||||||
|
@ -677,7 +678,9 @@ impl Function {
|
||||||
let _p = profile("Function::diagnostics");
|
let _p = profile("Function::diagnostics");
|
||||||
let infer = db.infer(self.id.into());
|
let infer = db.infer(self.id.into());
|
||||||
infer.add_diagnostics(db, self.id, sink);
|
infer.add_diagnostics(db, self.id, sink);
|
||||||
let mut validator = ExprValidator::new(self.id, infer, sink);
|
let mut validator = ExprValidator::new(self.id, infer.clone(), sink);
|
||||||
|
validator.validate_body(db);
|
||||||
|
let mut validator = UnsafeValidator::new(&self, infer, sink);
|
||||||
validator.validate_body(db);
|
validator.validate_body(db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,53 @@
|
||||||
pub use hir_def::diagnostics::UnresolvedModule;
|
pub use hir_def::diagnostics::UnresolvedModule;
|
||||||
pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
|
pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
|
||||||
pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField};
|
pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField};
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::code_model::Function;
|
||||||
|
use crate::db::HirDatabase;
|
||||||
|
use crate::has_source::HasSource;
|
||||||
|
use hir_ty::{
|
||||||
|
diagnostics::{MissingUnsafe, UnnecessaryUnsafe},
|
||||||
|
expr::unsafe_expressions,
|
||||||
|
InferenceResult,
|
||||||
|
};
|
||||||
|
use ra_syntax::AstPtr;
|
||||||
|
|
||||||
|
pub struct UnsafeValidator<'a, 'b: 'a> {
|
||||||
|
func: &'a Function,
|
||||||
|
infer: Arc<InferenceResult>,
|
||||||
|
sink: &'a mut DiagnosticSink<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> UnsafeValidator<'a, 'b> {
|
||||||
|
pub fn new(
|
||||||
|
func: &'a Function,
|
||||||
|
infer: Arc<InferenceResult>,
|
||||||
|
sink: &'a mut DiagnosticSink<'b>,
|
||||||
|
) -> UnsafeValidator<'a, 'b> {
|
||||||
|
UnsafeValidator { func, infer, sink }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_body(&mut self, db: &dyn HirDatabase) {
|
||||||
|
let def = self.func.id.into();
|
||||||
|
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
|
||||||
|
let func_data = db.function_data(self.func.id);
|
||||||
|
let unnecessary = func_data.is_unsafe && unsafe_expressions.len() == 0;
|
||||||
|
let missing = !func_data.is_unsafe && unsafe_expressions.len() > 0;
|
||||||
|
if !(unnecessary || missing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let in_file = self.func.source(db);
|
||||||
|
let file = in_file.file_id;
|
||||||
|
let fn_def = AstPtr::new(&in_file.value);
|
||||||
|
let fn_name = func_data.name.clone().into();
|
||||||
|
|
||||||
|
if unnecessary {
|
||||||
|
self.sink.push(UnnecessaryUnsafe { file, fn_def, fn_name })
|
||||||
|
} else {
|
||||||
|
self.sink.push(MissingUnsafe { file, fn_def, fn_name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -169,3 +169,61 @@ impl AstDiagnostic for BreakOutsideOfLoop {
|
||||||
ast::Expr::cast(node).unwrap()
|
ast::Expr::cast(node).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MissingUnsafe {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub fn_def: AstPtr<ast::FnDef>,
|
||||||
|
pub fn_name: Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for MissingUnsafe {
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Missing unsafe marker on fn `{}`", self.fn_name)
|
||||||
|
}
|
||||||
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile { file_id: self.file, value: self.fn_def.clone().into() }
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstDiagnostic for MissingUnsafe {
|
||||||
|
type AST = ast::FnDef;
|
||||||
|
|
||||||
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST {
|
||||||
|
let root = db.parse_or_expand(self.source().file_id).unwrap();
|
||||||
|
let node = self.source().value.to_node(&root);
|
||||||
|
ast::FnDef::cast(node).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnnecessaryUnsafe {
|
||||||
|
pub file: HirFileId,
|
||||||
|
pub fn_def: AstPtr<ast::FnDef>,
|
||||||
|
pub fn_name: Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for UnnecessaryUnsafe {
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("Unnecessary unsafe marker on fn `{}`", self.fn_name)
|
||||||
|
}
|
||||||
|
fn source(&self) -> InFile<SyntaxNodePtr> {
|
||||||
|
InFile { file_id: self.file, value: self.fn_def.clone().into() }
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &(dyn Any + Send + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstDiagnostic for UnnecessaryUnsafe {
|
||||||
|
type AST = ast::FnDef;
|
||||||
|
|
||||||
|
fn ast(&self, db: &impl AstDatabase) -> Self::AST {
|
||||||
|
let root = db.parse_or_expand(self.source().file_id).unwrap();
|
||||||
|
let node = self.source().value.to_node(&root);
|
||||||
|
ast::FnDef::cast(node).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId};
|
use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId, FunctionId};
|
||||||
use hir_expand::diagnostics::DiagnosticSink;
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
use ra_syntax::{ast, AstPtr};
|
use ra_syntax::{ast, AstPtr};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
@ -312,3 +312,25 @@ pub fn record_pattern_missing_fields(
|
||||||
}
|
}
|
||||||
Some((variant_def, missed_fields, exhaustive))
|
Some((variant_def, missed_fields, exhaustive))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unsafe_expressions(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
infer: &InferenceResult,
|
||||||
|
def: DefWithBodyId,
|
||||||
|
) -> Vec<ExprId> {
|
||||||
|
let mut unsafe_expr_ids = vec![];
|
||||||
|
let body = db.body(def);
|
||||||
|
for (id, expr) in body.exprs.iter() {
|
||||||
|
if let Expr::Call { callee, .. } = expr {
|
||||||
|
if infer
|
||||||
|
.method_resolution(*callee)
|
||||||
|
.map(|func| db.function_data(func).is_unsafe)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
unsafe_expr_ids.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe_expr_ids
|
||||||
|
}
|
||||||
|
|
|
@ -370,3 +370,31 @@ fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
|
||||||
fs::write(dst_file, &actual_html).unwrap();
|
fs::write(dst_file, &actual_html).unwrap();
|
||||||
assert_eq_text!(expected_html, actual_html);
|
assert_eq_text!(expected_html, actual_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsafe_highlighting() {
|
||||||
|
let (analysis, file_id) = single_file(
|
||||||
|
r#"
|
||||||
|
unsafe fn unsafe_fn() {}
|
||||||
|
|
||||||
|
struct HasUnsafeFn;
|
||||||
|
|
||||||
|
impl HasUnsafeFn {
|
||||||
|
unsafe fn unsafe_method(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
unsafe_fn();
|
||||||
|
HasUnsafeFn.unsafe_method();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.trim(),
|
||||||
|
);
|
||||||
|
let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html");
|
||||||
|
let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
|
||||||
|
let expected_html = &read_text(&dst_file);
|
||||||
|
fs::write(dst_file, &actual_html).unwrap();
|
||||||
|
assert_eq_text!(expected_html, actual_html);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue