mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-03 10:33:34 +00:00
Merge #9230
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:
commit
f9e67d692d
@ -14,8 +14,7 @@ use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
|
|||||||
pub use hir_ty::{
|
pub use hir_ty::{
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
|
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
|
||||||
MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
|
MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||||
ReplaceFilterMapNextWithFindMap,
|
|
||||||
},
|
},
|
||||||
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
|
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
|
||||||
};
|
};
|
||||||
@ -251,3 +250,78 @@ impl Diagnostic for UnimplementedBuiltinMacro {
|
|||||||
self
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,13 +36,14 @@ use std::{iter, sync::Arc};
|
|||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
|
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
|
||||||
use diagnostics::{
|
use diagnostics::{
|
||||||
InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
|
BreakOutsideOfLoop, InactiveCode, MacroError, MissingUnsafe, NoSuchField,
|
||||||
UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
|
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
||||||
|
UnresolvedModule, UnresolvedProcMacro,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
adt::{ReprKind, VariantData},
|
adt::{ReprKind, VariantData},
|
||||||
body::BodyDiagnostic,
|
body::{BodyDiagnostic, SyntheticSyntax},
|
||||||
expr::{BindingAnnotation, LabelId, Pat, PatId},
|
expr::{BindingAnnotation, LabelId, Pat, PatId},
|
||||||
item_tree::ItemTreeNode,
|
item_tree::ItemTreeNode,
|
||||||
lang_item::LangItemTarget,
|
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_module_item(db, krate, self.id.into(), sink);
|
||||||
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
|
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,10 @@ use crate::{
|
|||||||
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
|
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(
|
pub fn validate_module_item(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
@ -33,38 +36,8 @@ pub fn validate_module_item(
|
|||||||
pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
|
pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
|
||||||
let _p = profile::span("validate_body");
|
let _p = profile::span("validate_body");
|
||||||
let infer = db.infer(owner);
|
let infer = db.infer(owner);
|
||||||
infer.add_diagnostics(db, owner, sink);
|
|
||||||
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
|
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
|
||||||
validator.validate_body(db);
|
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
|
// 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
|
// Diagnostic: mismatched-arg-count
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
|
// 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);
|
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]
|
#[test]
|
||||||
fn missing_record_pat_field_diagnostic() {
|
fn missing_record_pat_field_diagnostic() {
|
||||||
check_diagnostics(
|
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]
|
#[test]
|
||||||
fn missing_semicolon() {
|
fn missing_semicolon() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
|
//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
|
||||||
//! unsafe blocks.
|
//! unsafe blocks.
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
expr::{Expr, ExprId, UnaryOp},
|
expr::{Expr, ExprId, UnaryOp},
|
||||||
@ -10,52 +8,25 @@ use hir_def::{
|
|||||||
DefWithBodyId,
|
DefWithBodyId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
|
||||||
db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
|
|
||||||
Interner, TyExt, TyKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(super) struct UnsafeValidator<'a, 'b: 'a> {
|
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
|
||||||
owner: DefWithBodyId,
|
let infer = db.infer(def);
|
||||||
infer: Arc<InferenceResult>,
|
|
||||||
sink: &'a mut DiagnosticSink<'b>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> UnsafeValidator<'a, 'b> {
|
// let unsafe_expressions = ;
|
||||||
pub(super) fn new(
|
let is_unsafe = match def {
|
||||||
owner: DefWithBodyId,
|
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
|
||||||
infer: Arc<InferenceResult>,
|
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
|
||||||
sink: &'a mut DiagnosticSink<'b>,
|
};
|
||||||
) -> UnsafeValidator<'a, 'b> {
|
if is_unsafe {
|
||||||
UnsafeValidator { owner, infer, sink }
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
|
unsafe_expressions(db, &infer, def)
|
||||||
let def = self.owner;
|
.into_iter()
|
||||||
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
|
.filter(|it| !it.inside_unsafe_block)
|
||||||
let is_unsafe = match self.owner {
|
.map(|it| it.expr)
|
||||||
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
|
.collect()
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct UnsafeExpr {
|
pub(crate) struct UnsafeExpr {
|
||||||
@ -126,92 +97,3 @@ fn walk_unsafe(
|
|||||||
walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
|
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
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -35,11 +35,9 @@ use stdx::impl_from;
|
|||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
|
|
||||||
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
||||||
use crate::diagnostics_sink::DiagnosticSink;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
|
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
|
||||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
|
Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
|
||||||
TyBuilder, TyExt, TyKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// 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) struct TypeError;
|
||||||
pub(crate) type InferResult = Result<InferOk, 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.
|
/// A mismatch between an expected and an inferred type.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct TypeMismatch {
|
pub struct TypeMismatch {
|
||||||
@ -140,7 +144,7 @@ pub struct InferenceResult {
|
|||||||
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
||||||
/// For each associated item record what it resolves to
|
/// For each associated item record what it resolves to
|
||||||
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
|
assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
|
||||||
diagnostics: Vec<InferenceDiagnostic>,
|
pub diagnostics: Vec<InferenceDiagnostic>,
|
||||||
pub type_of_expr: ArenaMap<ExprId, Ty>,
|
pub type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
/// For each pattern record the type it resolves to.
|
/// For each pattern record the type it resolves to.
|
||||||
///
|
///
|
||||||
@ -191,14 +195,6 @@ impl InferenceResult {
|
|||||||
_ => None,
|
_ => 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 {
|
impl Index<ExprId> for InferenceResult {
|
||||||
@ -804,43 +800,3 @@ impl std::ops::BitOrAssign for Diverges {
|
|||||||
*self = *self | other;
|
*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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -50,7 +50,7 @@ use crate::{db::HirDatabase, utils::generics};
|
|||||||
pub use autoderef::autoderef;
|
pub use autoderef::autoderef;
|
||||||
pub use builder::TyBuilder;
|
pub use builder::TyBuilder;
|
||||||
pub use chalk_ext::*;
|
pub use chalk_ext::*;
|
||||||
pub use infer::{could_unify, InferenceResult};
|
pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
|
||||||
pub use interner::Interner;
|
pub use interner::Interner;
|
||||||
pub use lower::{
|
pub use lower::{
|
||||||
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
|
||||||
|
@ -305,6 +305,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
|
use hir::diagnostics::DiagnosticCode;
|
||||||
use ide_assists::AssistResolveStrategy;
|
use ide_assists::AssistResolveStrategy;
|
||||||
use stdx::trim_indent;
|
use stdx::trim_indent;
|
||||||
use test_utils::{assert_eq_text, extract_annotations};
|
use test_utils::{assert_eq_text, extract_annotations};
|
||||||
@ -410,7 +411,12 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected = extract_annotations(&*analysis.file_text(file_id).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);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,6 +722,223 @@ $0
|
|||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
//- /foo.rs
|
//- /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
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user