diff options
Diffstat (limited to 'crates/hir_ty')
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 185 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 62 | ||||
-rw-r--r-- | crates/hir_ty/src/lib.rs | 2 |
3 files changed, 10 insertions, 239 deletions
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( | |||
33 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | 33 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { |
34 | let _p = profile::span("validate_body"); | 34 | let _p = profile::span("validate_body"); |
35 | let infer = db.infer(owner); | 35 | let infer = db.infer(owner); |
36 | infer.add_diagnostics(db, owner, sink); | ||
37 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | 36 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); |
38 | validator.validate_body(db); | 37 | validator.validate_body(db); |
39 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | 38 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); |
40 | validator.validate_body(db); | 39 | validator.validate_body(db); |
41 | } | 40 | } |
42 | 41 | ||
43 | // Diagnostic: no-such-field | ||
44 | // | ||
45 | // This diagnostic is triggered if created structure does not have field provided in record. | ||
46 | #[derive(Debug)] | ||
47 | pub struct NoSuchField { | ||
48 | pub file: HirFileId, | ||
49 | pub field: AstPtr<ast::RecordExprField>, | ||
50 | } | ||
51 | |||
52 | impl Diagnostic for NoSuchField { | ||
53 | fn code(&self) -> DiagnosticCode { | ||
54 | DiagnosticCode("no-such-field") | ||
55 | } | ||
56 | |||
57 | fn message(&self) -> String { | ||
58 | "no such field".to_string() | ||
59 | } | ||
60 | |||
61 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
62 | InFile::new(self.file, self.field.clone().into()) | ||
63 | } | ||
64 | |||
65 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
66 | self | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // Diagnostic: missing-structure-fields | 42 | // Diagnostic: missing-structure-fields |
71 | // | 43 | // |
72 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | 44 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. |
@@ -247,30 +219,6 @@ impl Diagnostic for RemoveThisSemicolon { | |||
247 | } | 219 | } |
248 | } | 220 | } |
249 | 221 | ||
250 | // Diagnostic: break-outside-of-loop | ||
251 | // | ||
252 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. | ||
253 | #[derive(Debug)] | ||
254 | pub struct BreakOutsideOfLoop { | ||
255 | pub file: HirFileId, | ||
256 | pub expr: AstPtr<ast::Expr>, | ||
257 | } | ||
258 | |||
259 | impl Diagnostic for BreakOutsideOfLoop { | ||
260 | fn code(&self) -> DiagnosticCode { | ||
261 | DiagnosticCode("break-outside-of-loop") | ||
262 | } | ||
263 | fn message(&self) -> String { | ||
264 | "break outside of loop".to_string() | ||
265 | } | ||
266 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
267 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
268 | } | ||
269 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
270 | self | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // Diagnostic: missing-unsafe | 222 | // Diagnostic: missing-unsafe |
275 | // | 223 | // |
276 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. | 224 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. |
@@ -531,129 +479,6 @@ mod tests { | |||
531 | } | 479 | } |
532 | 480 | ||
533 | #[test] | 481 | #[test] |
534 | fn no_such_field_diagnostics() { | ||
535 | check_diagnostics( | ||
536 | r#" | ||
537 | struct S { foo: i32, bar: () } | ||
538 | impl S { | ||
539 | fn new() -> S { | ||
540 | S { | ||
541 | //^ Missing structure fields: | ||
542 | //| - bar | ||
543 | foo: 92, | ||
544 | baz: 62, | ||
545 | //^^^^^^^ no such field | ||
546 | } | ||
547 | } | ||
548 | } | ||
549 | "#, | ||
550 | ); | ||
551 | } | ||
552 | #[test] | ||
553 | fn no_such_field_with_feature_flag_diagnostics() { | ||
554 | check_diagnostics( | ||
555 | r#" | ||
556 | //- /lib.rs crate:foo cfg:feature=foo | ||
557 | struct MyStruct { | ||
558 | my_val: usize, | ||
559 | #[cfg(feature = "foo")] | ||
560 | bar: bool, | ||
561 | } | ||
562 | |||
563 | impl MyStruct { | ||
564 | #[cfg(feature = "foo")] | ||
565 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
566 | Self { my_val, bar } | ||
567 | } | ||
568 | #[cfg(not(feature = "foo"))] | ||
569 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
570 | Self { my_val } | ||
571 | } | ||
572 | } | ||
573 | "#, | ||
574 | ); | ||
575 | } | ||
576 | |||
577 | #[test] | ||
578 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
579 | check_diagnostics( | ||
580 | r#" | ||
581 | //- /lib.rs crate:foo cfg:feature=foo | ||
582 | enum Foo { | ||
583 | #[cfg(not(feature = "foo"))] | ||
584 | Buz, | ||
585 | #[cfg(feature = "foo")] | ||
586 | Bar, | ||
587 | Baz | ||
588 | } | ||
589 | |||
590 | fn test_fn(f: Foo) { | ||
591 | match f { | ||
592 | Foo::Bar => {}, | ||
593 | Foo::Baz => {}, | ||
594 | } | ||
595 | } | ||
596 | "#, | ||
597 | ); | ||
598 | } | ||
599 | |||
600 | #[test] | ||
601 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
602 | check_diagnostics( | ||
603 | r#" | ||
604 | //- /lib.rs crate:foo cfg:feature=foo | ||
605 | struct S { | ||
606 | #[cfg(feature = "foo")] | ||
607 | foo: u32, | ||
608 | #[cfg(not(feature = "foo"))] | ||
609 | bar: u32, | ||
610 | } | ||
611 | |||
612 | impl S { | ||
613 | #[cfg(feature = "foo")] | ||
614 | fn new(foo: u32) -> Self { | ||
615 | Self { foo } | ||
616 | } | ||
617 | #[cfg(not(feature = "foo"))] | ||
618 | fn new(bar: u32) -> Self { | ||
619 | Self { bar } | ||
620 | } | ||
621 | fn new2(bar: u32) -> Self { | ||
622 | #[cfg(feature = "foo")] | ||
623 | { Self { foo: bar } } | ||
624 | #[cfg(not(feature = "foo"))] | ||
625 | { Self { bar } } | ||
626 | } | ||
627 | fn new2(val: u32) -> Self { | ||
628 | Self { | ||
629 | #[cfg(feature = "foo")] | ||
630 | foo: val, | ||
631 | #[cfg(not(feature = "foo"))] | ||
632 | bar: val, | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | "#, | ||
637 | ); | ||
638 | } | ||
639 | |||
640 | #[test] | ||
641 | fn no_such_field_with_type_macro() { | ||
642 | check_diagnostics( | ||
643 | r#" | ||
644 | macro_rules! Type { () => { u32 }; } | ||
645 | struct Foo { bar: Type![] } | ||
646 | |||
647 | impl Foo { | ||
648 | fn new() -> Self { | ||
649 | Foo { bar: 0 } | ||
650 | } | ||
651 | } | ||
652 | "#, | ||
653 | ); | ||
654 | } | ||
655 | |||
656 | #[test] | ||
657 | fn missing_record_pat_field_diagnostic() { | 482 | fn missing_record_pat_field_diagnostic() { |
658 | check_diagnostics( | 483 | check_diagnostics( |
659 | r#" | 484 | r#" |
@@ -735,16 +560,6 @@ pub struct Claims { | |||
735 | } | 560 | } |
736 | 561 | ||
737 | #[test] | 562 | #[test] |
738 | fn break_outside_of_loop() { | ||
739 | check_diagnostics( | ||
740 | r#" | ||
741 | fn foo() { break; } | ||
742 | //^^^^^ break outside of loop | ||
743 | "#, | ||
744 | ); | ||
745 | } | ||
746 | |||
747 | #[test] | ||
748 | fn missing_semicolon() { | 563 | fn missing_semicolon() { |
749 | check_diagnostics( | 564 | check_diagnostics( |
750 | r#" | 565 | r#" |
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; | |||
35 | use syntax::SmolStr; | 35 | use syntax::SmolStr; |
36 | 36 | ||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::diagnostics_sink::DiagnosticSink; | ||
39 | use crate::{ | 38 | use crate::{ |
40 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 39 | db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, |
41 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, | 40 | to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, |
42 | TyBuilder, TyExt, TyKind, | ||
43 | }; | 41 | }; |
44 | 42 | ||
45 | // This lint has a false positive here. See the link below for details. | 43 | // This lint has a false positive here. See the link below for details. |
@@ -111,6 +109,12 @@ pub(crate) struct InferOk { | |||
111 | pub(crate) struct TypeError; | 109 | pub(crate) struct TypeError; |
112 | pub(crate) type InferResult = Result<InferOk, TypeError>; | 110 | pub(crate) type InferResult = Result<InferOk, TypeError>; |
113 | 111 | ||
112 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
113 | pub enum InferenceDiagnostic { | ||
114 | NoSuchField { expr: ExprId }, | ||
115 | BreakOutsideOfLoop { expr: ExprId }, | ||
116 | } | ||
117 | |||
114 | /// A mismatch between an expected and an inferred type. | 118 | /// A mismatch between an expected and an inferred type. |
115 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 119 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
116 | pub struct TypeMismatch { | 120 | pub struct TypeMismatch { |
@@ -140,7 +144,7 @@ pub struct InferenceResult { | |||
140 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 144 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
141 | /// For each associated item record what it resolves to | 145 | /// For each associated item record what it resolves to |
142 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, | 146 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, |
143 | diagnostics: Vec<InferenceDiagnostic>, | 147 | pub diagnostics: Vec<InferenceDiagnostic>, |
144 | pub type_of_expr: ArenaMap<ExprId, Ty>, | 148 | pub type_of_expr: ArenaMap<ExprId, Ty>, |
145 | /// For each pattern record the type it resolves to. | 149 | /// For each pattern record the type it resolves to. |
146 | /// | 150 | /// |
@@ -191,14 +195,6 @@ impl InferenceResult { | |||
191 | _ => None, | 195 | _ => None, |
192 | }) | 196 | }) |
193 | } | 197 | } |
194 | pub fn add_diagnostics( | ||
195 | &self, | ||
196 | db: &dyn HirDatabase, | ||
197 | owner: DefWithBodyId, | ||
198 | sink: &mut DiagnosticSink, | ||
199 | ) { | ||
200 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
201 | } | ||
202 | } | 198 | } |
203 | 199 | ||
204 | impl Index<ExprId> for InferenceResult { | 200 | impl Index<ExprId> for InferenceResult { |
@@ -804,43 +800,3 @@ impl std::ops::BitOrAssign for Diverges { | |||
804 | *self = *self | other; | 800 | *self = *self | other; |
805 | } | 801 | } |
806 | } | 802 | } |
807 | |||
808 | mod diagnostics { | ||
809 | use hir_def::{expr::ExprId, DefWithBodyId}; | ||
810 | |||
811 | use crate::{ | ||
812 | db::HirDatabase, | ||
813 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
814 | diagnostics_sink::DiagnosticSink, | ||
815 | }; | ||
816 | |||
817 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
818 | pub(super) enum InferenceDiagnostic { | ||
819 | NoSuchField { expr: ExprId }, | ||
820 | BreakOutsideOfLoop { expr: ExprId }, | ||
821 | } | ||
822 | |||
823 | impl InferenceDiagnostic { | ||
824 | pub(super) fn add_to( | ||
825 | &self, | ||
826 | db: &dyn HirDatabase, | ||
827 | owner: DefWithBodyId, | ||
828 | sink: &mut DiagnosticSink, | ||
829 | ) { | ||
830 | match self { | ||
831 | InferenceDiagnostic::NoSuchField { expr } => { | ||
832 | let (_, source_map) = db.body_with_source_map(owner); | ||
833 | let field = source_map.field_syntax(*expr); | ||
834 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | ||
835 | } | ||
836 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
837 | let (_, source_map) = db.body_with_source_map(owner); | ||
838 | let ptr = source_map | ||
839 | .expr_syntax(*expr) | ||
840 | .expect("break outside of loop in synthetic syntax"); | ||
841 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
842 | } | ||
843 | } | ||
844 | } | ||
845 | } | ||
846 | } | ||
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}; | |||
50 | pub use autoderef::autoderef; | 50 | pub use autoderef::autoderef; |
51 | pub use builder::TyBuilder; | 51 | pub use builder::TyBuilder; |
52 | pub use chalk_ext::*; | 52 | pub use chalk_ext::*; |
53 | pub use infer::{could_unify, InferenceResult}; | 53 | pub use infer::{could_unify, InferenceDiagnostic, InferenceResult}; |
54 | pub use interner::Interner; | 54 | pub use interner::Interner; |
55 | pub use lower::{ | 55 | pub use lower::{ |
56 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, | 56 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, |