9230: internal: move inference diagnostics to hir r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2021-06-12 14:40:28 +00:00 committed by GitHub
commit f9e67d692d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 362 additions and 405 deletions

View File

@ -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,78 @@ 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<ast::RecordExprField>,
}
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<SyntaxNodePtr> {
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<ast::Expr>,
}
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<SyntaxNodePtr> {
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.
#[derive(Debug)]
pub struct MissingUnsafe {
pub file: HirFileId,
pub expr: AstPtr<ast::Expr>,
}
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<SyntaxNodePtr> {
InFile { file_id: self.file, value: self.expr.clone().into() }
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}

View File

@ -36,13 +36,14 @@ 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, 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,
@ -1042,6 +1043,35 @@ 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 })
}
}
}
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);
}

View File

@ -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,
@ -33,38 +36,8 @@ 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<ast::RecordExprField>,
}
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<SyntaxNodePtr> {
InFile::new(self.file, self.field.clone().into())
}
fn as_any(&self) -> &(dyn Any + Send + 'static) {
self
}
}
// Diagnostic: missing-structure-fields
@ -247,54 +220,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<ast::Expr>,
}
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<SyntaxNodePtr> {
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.
#[derive(Debug)]
pub struct MissingUnsafe {
pub file: HirFileId,
pub expr: AstPtr<ast::Expr>,
}
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<SyntaxNodePtr> {
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.
@ -530,129 +455,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 +536,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(

View File

@ -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<InferenceResult>,
sink: &'a mut DiagnosticSink<'b>,
}
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let infer = db.infer(def);
impl<'a, 'b> UnsafeValidator<'a, 'b> {
pub(super) fn new(
owner: DefWithBodyId,
infer: Arc<InferenceResult>,
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
}
"#,
);
}
}

View File

@ -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<InferOk, TypeError>;
#[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<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
diagnostics: Vec<InferenceDiagnostic>,
pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
/// 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<ExprId> 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 })
}
}
}
}
}

View File

@ -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,

View File

@ -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::<Vec<_>>();
let mut actual = diagnostics
.into_iter()
.filter(|d| d.code != Some(DiagnosticCode("inactive-code")))
.map(|d| (d.range, d.message))
.collect::<Vec<_>>();
actual.sort_by_key(|(range, _)| range.start());
assert_eq!(expected, actual);
}
@ -716,6 +722,223 @@ $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 }
}
}
"#,
);
}
#[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
}
"#,
);
}