diff options
-rw-r--r-- | crates/hir/src/diagnostics.rs | 54 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 22 | ||||
-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 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 141 |
6 files changed, 222 insertions, 244 deletions
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}; | |||
14 | pub use hir_ty::{ | 14 | pub use hir_ty::{ |
15 | diagnostics::{ | 15 | diagnostics::{ |
16 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, | 16 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, |
17 | MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon, | 17 | MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, |
18 | ReplaceFilterMapNextWithFindMap, | ||
19 | }, | 18 | }, |
20 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, | 19 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, |
21 | }; | 20 | }; |
@@ -251,3 +250,54 @@ impl Diagnostic for UnimplementedBuiltinMacro { | |||
251 | self | 250 | self |
252 | } | 251 | } |
253 | } | 252 | } |
253 | |||
254 | // Diagnostic: no-such-field | ||
255 | // | ||
256 | // This diagnostic is triggered if created structure does not have field provided in record. | ||
257 | #[derive(Debug)] | ||
258 | pub struct NoSuchField { | ||
259 | pub file: HirFileId, | ||
260 | pub field: AstPtr<ast::RecordExprField>, | ||
261 | } | ||
262 | |||
263 | impl Diagnostic for NoSuchField { | ||
264 | fn code(&self) -> DiagnosticCode { | ||
265 | DiagnosticCode("no-such-field") | ||
266 | } | ||
267 | |||
268 | fn message(&self) -> String { | ||
269 | "no such field".to_string() | ||
270 | } | ||
271 | |||
272 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
273 | InFile::new(self.file, self.field.clone().into()) | ||
274 | } | ||
275 | |||
276 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
277 | self | ||
278 | } | ||
279 | } | ||
280 | |||
281 | // Diagnostic: break-outside-of-loop | ||
282 | // | ||
283 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. | ||
284 | #[derive(Debug)] | ||
285 | pub struct BreakOutsideOfLoop { | ||
286 | pub file: HirFileId, | ||
287 | pub expr: AstPtr<ast::Expr>, | ||
288 | } | ||
289 | |||
290 | impl Diagnostic for BreakOutsideOfLoop { | ||
291 | fn code(&self) -> DiagnosticCode { | ||
292 | DiagnosticCode("break-outside-of-loop") | ||
293 | } | ||
294 | fn message(&self) -> String { | ||
295 | "break outside of loop".to_string() | ||
296 | } | ||
297 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
298 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
299 | } | ||
300 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
301 | self | ||
302 | } | ||
303 | } | ||
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}; | |||
36 | use arrayvec::ArrayVec; | 36 | use arrayvec::ArrayVec; |
37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
38 | use diagnostics::{ | 38 | use diagnostics::{ |
39 | InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, | 39 | BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro, |
40 | UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, | 40 | UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, |
41 | UnresolvedProcMacro, | ||
41 | }; | 42 | }; |
42 | use either::Either; | 43 | use either::Either; |
43 | use hir_def::{ | 44 | use hir_def::{ |
@@ -1042,6 +1043,23 @@ impl Function { | |||
1042 | } | 1043 | } |
1043 | } | 1044 | } |
1044 | 1045 | ||
1046 | let infer = db.infer(self.id.into()); | ||
1047 | let (_, source_map) = db.body_with_source_map(self.id.into()); | ||
1048 | for d in &infer.diagnostics { | ||
1049 | match d { | ||
1050 | hir_ty::InferenceDiagnostic::NoSuchField { expr } => { | ||
1051 | let field = source_map.field_syntax(*expr); | ||
1052 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | ||
1053 | } | ||
1054 | hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
1055 | let ptr = source_map | ||
1056 | .expr_syntax(*expr) | ||
1057 | .expect("break outside of loop in synthetic syntax"); | ||
1058 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
1059 | } | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1045 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | 1063 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); |
1046 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | 1064 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); |
1047 | } | 1065 | } |
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, |
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 { | |||
305 | #[cfg(test)] | 305 | #[cfg(test)] |
306 | mod tests { | 306 | mod tests { |
307 | use expect_test::Expect; | 307 | use expect_test::Expect; |
308 | use hir::diagnostics::DiagnosticCode; | ||
308 | use ide_assists::AssistResolveStrategy; | 309 | use ide_assists::AssistResolveStrategy; |
309 | use stdx::trim_indent; | 310 | use stdx::trim_indent; |
310 | use test_utils::{assert_eq_text, extract_annotations}; | 311 | use test_utils::{assert_eq_text, extract_annotations}; |
@@ -410,7 +411,12 @@ mod tests { | |||
410 | .unwrap(); | 411 | .unwrap(); |
411 | 412 | ||
412 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | 413 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); |
413 | let actual = diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>(); | 414 | let mut actual = diagnostics |
415 | .into_iter() | ||
416 | .filter(|d| d.code != Some(DiagnosticCode("inactive-code"))) | ||
417 | .map(|d| (d.range, d.message)) | ||
418 | .collect::<Vec<_>>(); | ||
419 | actual.sort_by_key(|(range, _)| range.start()); | ||
414 | assert_eq!(expected, actual); | 420 | assert_eq!(expected, actual); |
415 | } | 421 | } |
416 | 422 | ||
@@ -719,4 +725,137 @@ mod foo; | |||
719 | "#, | 725 | "#, |
720 | ); | 726 | ); |
721 | } | 727 | } |
728 | |||
729 | #[test] | ||
730 | fn break_outside_of_loop() { | ||
731 | check_diagnostics( | ||
732 | r#" | ||
733 | fn foo() { break; } | ||
734 | //^^^^^ break outside of loop | ||
735 | "#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn no_such_field_diagnostics() { | ||
741 | check_diagnostics( | ||
742 | r#" | ||
743 | struct S { foo: i32, bar: () } | ||
744 | impl S { | ||
745 | fn new() -> S { | ||
746 | S { | ||
747 | //^ Missing structure fields: | ||
748 | //| - bar | ||
749 | foo: 92, | ||
750 | baz: 62, | ||
751 | //^^^^^^^ no such field | ||
752 | } | ||
753 | } | ||
754 | } | ||
755 | "#, | ||
756 | ); | ||
757 | } | ||
758 | #[test] | ||
759 | fn no_such_field_with_feature_flag_diagnostics() { | ||
760 | check_diagnostics( | ||
761 | r#" | ||
762 | //- /lib.rs crate:foo cfg:feature=foo | ||
763 | struct MyStruct { | ||
764 | my_val: usize, | ||
765 | #[cfg(feature = "foo")] | ||
766 | bar: bool, | ||
767 | } | ||
768 | |||
769 | impl MyStruct { | ||
770 | #[cfg(feature = "foo")] | ||
771 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
772 | Self { my_val, bar } | ||
773 | } | ||
774 | #[cfg(not(feature = "foo"))] | ||
775 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
776 | Self { my_val } | ||
777 | } | ||
778 | } | ||
779 | "#, | ||
780 | ); | ||
781 | } | ||
782 | |||
783 | #[test] | ||
784 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
785 | check_diagnostics( | ||
786 | r#" | ||
787 | //- /lib.rs crate:foo cfg:feature=foo | ||
788 | enum Foo { | ||
789 | #[cfg(not(feature = "foo"))] | ||
790 | Buz, | ||
791 | #[cfg(feature = "foo")] | ||
792 | Bar, | ||
793 | Baz | ||
794 | } | ||
795 | |||
796 | fn test_fn(f: Foo) { | ||
797 | match f { | ||
798 | Foo::Bar => {}, | ||
799 | Foo::Baz => {}, | ||
800 | } | ||
801 | } | ||
802 | "#, | ||
803 | ); | ||
804 | } | ||
805 | |||
806 | #[test] | ||
807 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
808 | check_diagnostics( | ||
809 | r#" | ||
810 | //- /lib.rs crate:foo cfg:feature=foo | ||
811 | struct S { | ||
812 | #[cfg(feature = "foo")] | ||
813 | foo: u32, | ||
814 | #[cfg(not(feature = "foo"))] | ||
815 | bar: u32, | ||
816 | } | ||
817 | |||
818 | impl S { | ||
819 | #[cfg(feature = "foo")] | ||
820 | fn new(foo: u32) -> Self { | ||
821 | Self { foo } | ||
822 | } | ||
823 | #[cfg(not(feature = "foo"))] | ||
824 | fn new(bar: u32) -> Self { | ||
825 | Self { bar } | ||
826 | } | ||
827 | fn new2(bar: u32) -> Self { | ||
828 | #[cfg(feature = "foo")] | ||
829 | { Self { foo: bar } } | ||
830 | #[cfg(not(feature = "foo"))] | ||
831 | { Self { bar } } | ||
832 | } | ||
833 | fn new2(val: u32) -> Self { | ||
834 | Self { | ||
835 | #[cfg(feature = "foo")] | ||
836 | foo: val, | ||
837 | #[cfg(not(feature = "foo"))] | ||
838 | bar: val, | ||
839 | } | ||
840 | } | ||
841 | } | ||
842 | "#, | ||
843 | ); | ||
844 | } | ||
845 | |||
846 | #[test] | ||
847 | fn no_such_field_with_type_macro() { | ||
848 | check_diagnostics( | ||
849 | r#" | ||
850 | macro_rules! Type { () => { u32 }; } | ||
851 | struct Foo { bar: Type![] } | ||
852 | |||
853 | impl Foo { | ||
854 | fn new() -> Self { | ||
855 | Foo { bar: 0 } | ||
856 | } | ||
857 | } | ||
858 | "#, | ||
859 | ); | ||
860 | } | ||
722 | } | 861 | } |