From f8009666beaedb34197da9c87cc54d8ca65203b8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 12 Jun 2021 17:17:23 +0300 Subject: internal: move inference diagnostics to hir --- crates/hir/src/diagnostics.rs | 54 +++++++++++- crates/hir/src/lib.rs | 22 ++++- crates/hir_ty/src/diagnostics.rs | 185 --------------------------------------- crates/hir_ty/src/infer.rs | 62 ++----------- crates/hir_ty/src/lib.rs | 2 +- crates/ide/src/diagnostics.rs | 141 ++++++++++++++++++++++++++++- 6 files changed, 222 insertions(+), 244 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 2cdbd172a..2edb53765 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -14,8 +14,7 @@ use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; pub use hir_ty::{ diagnostics::{ IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, - MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, - ReplaceFilterMapNextWithFindMap, + MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, }, diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, }; @@ -251,3 +250,54 @@ impl Diagnostic for UnimplementedBuiltinMacro { self } } + +// Diagnostic: no-such-field +// +// This diagnostic is triggered if created structure does not have field provided in record. +#[derive(Debug)] +pub struct NoSuchField { + pub file: HirFileId, + pub field: AstPtr, +} + +impl Diagnostic for NoSuchField { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("no-such-field") + } + + fn message(&self) -> String { + "no such field".to_string() + } + + fn display_source(&self) -> InFile { + InFile::new(self.file, self.field.clone().into()) + } + + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} + +// Diagnostic: break-outside-of-loop +// +// This diagnostic is triggered if the `break` keyword is used outside of a loop. +#[derive(Debug)] +pub struct BreakOutsideOfLoop { + pub file: HirFileId, + pub expr: AstPtr, +} + +impl Diagnostic for BreakOutsideOfLoop { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("break-outside-of-loop") + } + fn message(&self) -> String { + "break outside of loop".to_string() + } + fn display_source(&self) -> InFile { + InFile { file_id: self.file, value: self.expr.clone().into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 0bb3767c1..bce626b03 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -36,8 +36,9 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, Edition, FileId}; use diagnostics::{ - InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, - UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, + BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro, + UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, + UnresolvedProcMacro, }; use either::Either; use hir_def::{ @@ -1042,6 +1043,23 @@ impl Function { } } + let infer = db.infer(self.id.into()); + let (_, source_map) = db.body_with_source_map(self.id.into()); + for d in &infer.diagnostics { + match d { + hir_ty::InferenceDiagnostic::NoSuchField { expr } => { + let field = source_map.field_syntax(*expr); + sink.push(NoSuchField { file: field.file_id, field: field.value }) + } + hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { + let ptr = source_map + .expr_syntax(*expr) + .expect("break outside of loop in synthetic syntax"); + sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) + } + } + } + hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); hir_ty::diagnostics::validate_body(db, self.id.into(), sink); } diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 283894704..28d6ca567 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs @@ -33,40 +33,12 @@ pub fn validate_module_item( pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { let _p = profile::span("validate_body"); let infer = db.infer(owner); - infer.add_diagnostics(db, owner, sink); let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); validator.validate_body(db); let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); validator.validate_body(db); } -// Diagnostic: no-such-field -// -// This diagnostic is triggered if created structure does not have field provided in record. -#[derive(Debug)] -pub struct NoSuchField { - pub file: HirFileId, - pub field: AstPtr, -} - -impl Diagnostic for NoSuchField { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("no-such-field") - } - - fn message(&self) -> String { - "no such field".to_string() - } - - fn display_source(&self) -> InFile { - InFile::new(self.file, self.field.clone().into()) - } - - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } -} - // Diagnostic: missing-structure-fields // // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. @@ -247,30 +219,6 @@ impl Diagnostic for RemoveThisSemicolon { } } -// Diagnostic: break-outside-of-loop -// -// This diagnostic is triggered if the `break` keyword is used outside of a loop. -#[derive(Debug)] -pub struct BreakOutsideOfLoop { - pub file: HirFileId, - pub expr: AstPtr, -} - -impl Diagnostic for BreakOutsideOfLoop { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("break-outside-of-loop") - } - fn message(&self) -> String { - "break outside of loop".to_string() - } - fn display_source(&self) -> InFile { - InFile { file_id: self.file, value: self.expr.clone().into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } -} - // Diagnostic: missing-unsafe // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. @@ -530,129 +478,6 @@ mod tests { assert_eq!(annotations, actual); } - #[test] - fn no_such_field_diagnostics() { - check_diagnostics( - r#" -struct S { foo: i32, bar: () } -impl S { - fn new() -> S { - S { - //^ Missing structure fields: - //| - bar - foo: 92, - baz: 62, - //^^^^^^^ no such field - } - } -} -"#, - ); - } - #[test] - fn no_such_field_with_feature_flag_diagnostics() { - check_diagnostics( - r#" -//- /lib.rs crate:foo cfg:feature=foo -struct MyStruct { - my_val: usize, - #[cfg(feature = "foo")] - bar: bool, -} - -impl MyStruct { - #[cfg(feature = "foo")] - pub(crate) fn new(my_val: usize, bar: bool) -> Self { - Self { my_val, bar } - } - #[cfg(not(feature = "foo"))] - pub(crate) fn new(my_val: usize, _bar: bool) -> Self { - Self { my_val } - } -} -"#, - ); - } - - #[test] - fn no_such_field_enum_with_feature_flag_diagnostics() { - check_diagnostics( - r#" -//- /lib.rs crate:foo cfg:feature=foo -enum Foo { - #[cfg(not(feature = "foo"))] - Buz, - #[cfg(feature = "foo")] - Bar, - Baz -} - -fn test_fn(f: Foo) { - match f { - Foo::Bar => {}, - Foo::Baz => {}, - } -} -"#, - ); - } - - #[test] - fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { - check_diagnostics( - r#" -//- /lib.rs crate:foo cfg:feature=foo -struct S { - #[cfg(feature = "foo")] - foo: u32, - #[cfg(not(feature = "foo"))] - bar: u32, -} - -impl S { - #[cfg(feature = "foo")] - fn new(foo: u32) -> Self { - Self { foo } - } - #[cfg(not(feature = "foo"))] - fn new(bar: u32) -> Self { - Self { bar } - } - fn new2(bar: u32) -> Self { - #[cfg(feature = "foo")] - { Self { foo: bar } } - #[cfg(not(feature = "foo"))] - { Self { bar } } - } - fn new2(val: u32) -> Self { - Self { - #[cfg(feature = "foo")] - foo: val, - #[cfg(not(feature = "foo"))] - bar: val, - } - } -} -"#, - ); - } - - #[test] - fn no_such_field_with_type_macro() { - check_diagnostics( - r#" -macro_rules! Type { () => { u32 }; } -struct Foo { bar: Type![] } - -impl Foo { - fn new() -> Self { - Foo { bar: 0 } - } -} -"#, - ); - } - #[test] fn missing_record_pat_field_diagnostic() { check_diagnostics( @@ -734,16 +559,6 @@ pub struct Claims { ); } - #[test] - fn break_outside_of_loop() { - check_diagnostics( - r#" -fn foo() { break; } - //^^^^^ break outside of loop -"#, - ); - } - #[test] fn missing_semicolon() { check_diagnostics( diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0e9f777da..7b57593e4 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -35,11 +35,9 @@ use stdx::impl_from; use syntax::SmolStr; use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; -use crate::diagnostics_sink::DiagnosticSink; use crate::{ - db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, - TyBuilder, TyExt, TyKind, + db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, + to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, }; // This lint has a false positive here. See the link below for details. @@ -111,6 +109,12 @@ pub(crate) struct InferOk { pub(crate) struct TypeError; pub(crate) type InferResult = Result; +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum InferenceDiagnostic { + NoSuchField { expr: ExprId }, + BreakOutsideOfLoop { expr: ExprId }, +} + /// A mismatch between an expected and an inferred type. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TypeMismatch { @@ -140,7 +144,7 @@ pub struct InferenceResult { variant_resolutions: FxHashMap, /// For each associated item record what it resolves to assoc_resolutions: FxHashMap, - diagnostics: Vec, + pub diagnostics: Vec, pub type_of_expr: ArenaMap, /// For each pattern record the type it resolves to. /// @@ -191,14 +195,6 @@ impl InferenceResult { _ => None, }) } - pub fn add_diagnostics( - &self, - db: &dyn HirDatabase, - owner: DefWithBodyId, - sink: &mut DiagnosticSink, - ) { - self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) - } } impl Index for InferenceResult { @@ -804,43 +800,3 @@ impl std::ops::BitOrAssign for Diverges { *self = *self | other; } } - -mod diagnostics { - use hir_def::{expr::ExprId, DefWithBodyId}; - - use crate::{ - db::HirDatabase, - diagnostics::{BreakOutsideOfLoop, NoSuchField}, - diagnostics_sink::DiagnosticSink, - }; - - #[derive(Debug, PartialEq, Eq, Clone)] - pub(super) enum InferenceDiagnostic { - NoSuchField { expr: ExprId }, - BreakOutsideOfLoop { expr: ExprId }, - } - - impl InferenceDiagnostic { - pub(super) fn add_to( - &self, - db: &dyn HirDatabase, - owner: DefWithBodyId, - sink: &mut DiagnosticSink, - ) { - match self { - InferenceDiagnostic::NoSuchField { expr } => { - let (_, source_map) = db.body_with_source_map(owner); - let field = source_map.field_syntax(*expr); - sink.push(NoSuchField { file: field.file_id, field: field.value }) - } - InferenceDiagnostic::BreakOutsideOfLoop { expr } => { - let (_, source_map) = db.body_with_source_map(owner); - let ptr = source_map - .expr_syntax(*expr) - .expect("break outside of loop in synthetic syntax"); - sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) - } - } - } - } -} diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 50e0d6333..0c6b19653 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -50,7 +50,7 @@ use crate::{db::HirDatabase, utils::generics}; pub use autoderef::autoderef; pub use builder::TyBuilder; pub use chalk_ext::*; -pub use infer::{could_unify, InferenceResult}; +pub use infer::{could_unify, InferenceDiagnostic, InferenceResult}; pub use interner::Interner; pub use lower::{ associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index d5c954b8b..7febd6eaf 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -305,6 +305,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { #[cfg(test)] mod tests { use expect_test::Expect; + use hir::diagnostics::DiagnosticCode; use ide_assists::AssistResolveStrategy; use stdx::trim_indent; use test_utils::{assert_eq_text, extract_annotations}; @@ -410,7 +411,12 @@ mod tests { .unwrap(); let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); - let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::>(); + let mut actual = diagnostics + .into_iter() + .filter(|d| d.code != Some(DiagnosticCode("inactive-code"))) + .map(|d| (d.range, d.message)) + .collect::>(); + actual.sort_by_key(|(range, _)| range.start()); assert_eq!(expected, actual); } @@ -716,6 +722,139 @@ $0 mod foo; //- /foo.rs +"#, + ); + } + + #[test] + fn break_outside_of_loop() { + check_diagnostics( + r#" +fn foo() { break; } + //^^^^^ break outside of loop +"#, + ); + } + + #[test] + fn no_such_field_diagnostics() { + check_diagnostics( + r#" +struct S { foo: i32, bar: () } +impl S { + fn new() -> S { + S { + //^ Missing structure fields: + //| - bar + foo: 92, + baz: 62, + //^^^^^^^ no such field + } + } +} +"#, + ); + } + #[test] + fn no_such_field_with_feature_flag_diagnostics() { + check_diagnostics( + r#" +//- /lib.rs crate:foo cfg:feature=foo +struct MyStruct { + my_val: usize, + #[cfg(feature = "foo")] + bar: bool, +} + +impl MyStruct { + #[cfg(feature = "foo")] + pub(crate) fn new(my_val: usize, bar: bool) -> Self { + Self { my_val, bar } + } + #[cfg(not(feature = "foo"))] + pub(crate) fn new(my_val: usize, _bar: bool) -> Self { + Self { my_val } + } +} +"#, + ); + } + + #[test] + fn no_such_field_enum_with_feature_flag_diagnostics() { + check_diagnostics( + r#" +//- /lib.rs crate:foo cfg:feature=foo +enum Foo { + #[cfg(not(feature = "foo"))] + Buz, + #[cfg(feature = "foo")] + Bar, + Baz +} + +fn test_fn(f: Foo) { + match f { + Foo::Bar => {}, + Foo::Baz => {}, + } +} +"#, + ); + } + + #[test] + fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { + check_diagnostics( + r#" +//- /lib.rs crate:foo cfg:feature=foo +struct S { + #[cfg(feature = "foo")] + foo: u32, + #[cfg(not(feature = "foo"))] + bar: u32, +} + +impl S { + #[cfg(feature = "foo")] + fn new(foo: u32) -> Self { + Self { foo } + } + #[cfg(not(feature = "foo"))] + fn new(bar: u32) -> Self { + Self { bar } + } + fn new2(bar: u32) -> Self { + #[cfg(feature = "foo")] + { Self { foo: bar } } + #[cfg(not(feature = "foo"))] + { Self { bar } } + } + fn new2(val: u32) -> Self { + Self { + #[cfg(feature = "foo")] + foo: val, + #[cfg(not(feature = "foo"))] + bar: val, + } + } +} +"#, + ); + } + + #[test] + fn no_such_field_with_type_macro() { + check_diagnostics( + r#" +macro_rules! Type { () => { u32 }; } +struct Foo { bar: Type![] } + +impl Foo { + fn new() -> Self { + Foo { bar: 0 } + } +} "#, ); } -- cgit v1.2.3 From 0413d51317a2470c94f82d93e66a3d009fce41a3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 12 Jun 2021 17:39:46 +0300 Subject: internal: move missing unsafe diagnostic to hir --- crates/hir/src/diagnostics.rs | 24 +++++ crates/hir/src/lib.rs | 20 +++- crates/hir_ty/src/diagnostics.rs | 31 +----- crates/hir_ty/src/diagnostics/unsafe_check.rs | 148 +++----------------------- crates/hir_ty/src/infer.rs | 4 +- crates/ide/src/diagnostics.rs | 84 +++++++++++++++ 6 files changed, 145 insertions(+), 166 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 2edb53765..e888fc23b 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -301,3 +301,27 @@ impl Diagnostic for BreakOutsideOfLoop { self } } + +// Diagnostic: missing-unsafe +// +// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. +#[derive(Debug)] +pub struct MissingUnsafe { + pub file: HirFileId, + pub expr: AstPtr, +} + +impl Diagnostic for MissingUnsafe { + fn code(&self) -> DiagnosticCode { + DiagnosticCode("missing-unsafe") + } + fn message(&self) -> String { + format!("This operation is unsafe and requires an unsafe function or block") + } + fn display_source(&self) -> InFile { + InFile { file_id: self.file, value: self.expr.clone().into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bce626b03..a21a9da21 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -36,14 +36,14 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, Edition, FileId}; use diagnostics::{ - BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro, - UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, - UnresolvedProcMacro, + BreakOutsideOfLoop, InactiveCode, MacroError, MissingUnsafe, NoSuchField, + UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, + UnresolvedModule, UnresolvedProcMacro, }; use either::Either; use hir_def::{ adt::{ReprKind, VariantData}, - body::BodyDiagnostic, + body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, LabelId, Pat, PatId}, item_tree::ItemTreeNode, lang_item::LangItemTarget, @@ -1060,6 +1060,18 @@ impl Function { } } + for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) { + match source_map.as_ref().expr_syntax(expr) { + Ok(in_file) => { + sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) + } + Err(SyntheticSyntax) => { + // FIXME: The `expr` was desugared, report or assert that + // this dosen't happen. + } + } + } + hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); hir_ty::diagnostics::validate_body(db, self.id.into(), sink); } diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 28d6ca567..12131d9bc 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs @@ -17,7 +17,10 @@ use crate::{ diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, }; -pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; +pub use crate::diagnostics::{ + expr::{record_literal_missing_fields, record_pattern_missing_fields}, + unsafe_check::missing_unsafe, +}; pub fn validate_module_item( db: &dyn HirDatabase, @@ -35,8 +38,6 @@ pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut Diag let infer = db.infer(owner); let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); validator.validate_body(db); - let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); - validator.validate_body(db); } // Diagnostic: missing-structure-fields @@ -219,30 +220,6 @@ impl Diagnostic for RemoveThisSemicolon { } } -// Diagnostic: missing-unsafe -// -// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. -#[derive(Debug)] -pub struct MissingUnsafe { - pub file: HirFileId, - pub expr: AstPtr, -} - -impl Diagnostic for MissingUnsafe { - fn code(&self) -> DiagnosticCode { - DiagnosticCode("missing-unsafe") - } - fn message(&self) -> String { - format!("This operation is unsafe and requires an unsafe function or block") - } - fn display_source(&self) -> InFile { - InFile { file_id: self.file, value: self.expr.clone().into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } -} - // Diagnostic: mismatched-arg-count // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index c3c483425..a4054cef9 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs @@ -1,8 +1,6 @@ //! Provides validations for unsafe code. Currently checks if unsafe functions are missing //! unsafe blocks. -use std::sync::Arc; - use hir_def::{ body::Body, expr::{Expr, ExprId, UnaryOp}, @@ -10,52 +8,25 @@ use hir_def::{ DefWithBodyId, }; -use crate::{ - db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult, - Interner, TyExt, TyKind, -}; +use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind}; -pub(super) struct UnsafeValidator<'a, 'b: 'a> { - owner: DefWithBodyId, - infer: Arc, - sink: &'a mut DiagnosticSink<'b>, -} +pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { + let infer = db.infer(def); -impl<'a, 'b> UnsafeValidator<'a, 'b> { - pub(super) fn new( - owner: DefWithBodyId, - infer: Arc, - sink: &'a mut DiagnosticSink<'b>, - ) -> UnsafeValidator<'a, 'b> { - UnsafeValidator { owner, infer, sink } + // let unsafe_expressions = ; + let is_unsafe = match def { + DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), + DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, + }; + if is_unsafe { + return Vec::new(); } - pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { - let def = self.owner; - let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); - let is_unsafe = match self.owner { - DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), - DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, - }; - if is_unsafe - || unsafe_expressions - .iter() - .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) - .count() - == 0 - { - return; - } - - let (_, body_source) = db.body_with_source_map(def); - for unsafe_expr in unsafe_expressions { - if !unsafe_expr.inside_unsafe_block { - if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) { - self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) - } - } - } - } + unsafe_expressions(db, &infer, def) + .into_iter() + .filter(|it| !it.inside_unsafe_block) + .map(|it| it.expr) + .collect() } pub(crate) struct UnsafeExpr { @@ -126,92 +97,3 @@ fn walk_unsafe( walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); }); } - -#[cfg(test)] -mod tests { - use crate::diagnostics::tests::check_diagnostics; - - #[test] - fn missing_unsafe_diagnostic_with_raw_ptr() { - check_diagnostics( - r#" -fn main() { - let x = &5 as *const usize; - unsafe { let y = *x; } - let z = *x; -} //^^ This operation is unsafe and requires an unsafe function or block -"#, - ) - } - - #[test] - fn missing_unsafe_diagnostic_with_unsafe_call() { - check_diagnostics( - r#" -struct HasUnsafe; - -impl HasUnsafe { - unsafe fn unsafe_fn(&self) { - let x = &5 as *const usize; - let y = *x; - } -} - -unsafe fn unsafe_fn() { - let x = &5 as *const usize; - let y = *x; -} - -fn main() { - unsafe_fn(); - //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - HasUnsafe.unsafe_fn(); - //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - unsafe { - unsafe_fn(); - HasUnsafe.unsafe_fn(); - } -} -"#, - ); - } - - #[test] - fn missing_unsafe_diagnostic_with_static_mut() { - check_diagnostics( - r#" -struct Ty { - a: u8, -} - -static mut STATIC_MUT: Ty = Ty { a: 0 }; - -fn main() { - let x = STATIC_MUT.a; - //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block - unsafe { - let x = STATIC_MUT.a; - } -} -"#, - ); - } - - #[test] - fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { - check_diagnostics( - r#" -extern "rust-intrinsic" { - pub fn bitreverse(x: u32) -> u32; // Safe intrinsic - pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic -} - -fn main() { - let _ = bitreverse(12); - let _ = floorf32(12.0); - //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block -} -"#, - ); - } -} diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 7b57593e4..2c667da25 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -36,8 +36,8 @@ use syntax::SmolStr; use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; use crate::{ - db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, - to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, + db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, + Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, }; // This lint has a false positive here. See the link below for details. diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 7febd6eaf..b791747c2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -855,6 +855,90 @@ impl Foo { Foo { bar: 0 } } } +"#, + ); + } + + #[test] + fn missing_unsafe_diagnostic_with_raw_ptr() { + check_diagnostics( + r#" +fn main() { + let x = &5 as *const usize; + unsafe { let y = *x; } + let z = *x; +} //^^ This operation is unsafe and requires an unsafe function or block +"#, + ) + } + + #[test] + fn missing_unsafe_diagnostic_with_unsafe_call() { + check_diagnostics( + r#" +struct HasUnsafe; + +impl HasUnsafe { + unsafe fn unsafe_fn(&self) { + let x = &5 as *const usize; + let y = *x; + } +} + +unsafe fn unsafe_fn() { + let x = &5 as *const usize; + let y = *x; +} + +fn main() { + unsafe_fn(); + //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block + HasUnsafe.unsafe_fn(); + //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block + unsafe { + unsafe_fn(); + HasUnsafe.unsafe_fn(); + } +} +"#, + ); + } + + #[test] + fn missing_unsafe_diagnostic_with_static_mut() { + check_diagnostics( + r#" +struct Ty { + a: u8, +} + +static mut STATIC_MUT: Ty = Ty { a: 0 }; + +fn main() { + let x = STATIC_MUT.a; + //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block + unsafe { + let x = STATIC_MUT.a; + } +} +"#, + ); + } + + #[test] + fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { + check_diagnostics( + r#" +extern "rust-intrinsic" { + pub fn bitreverse(x: u32) -> u32; // Safe intrinsic + pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic +} + +fn main() { + let _ = bitreverse(12); + let _ = floorf32(12.0); + //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block +} "#, ); } -- cgit v1.2.3