1 mod sourcegen;
2 
3 use expect_test::Expect;
4 use ide_db::{
5     assists::AssistResolveStrategy,
6     base_db::{fixture::WithFixture, SourceDatabaseExt},
7     RootDatabase,
8 };
9 use stdx::trim_indent;
10 use test_utils::{assert_eq_text, extract_annotations};
11 
12 use crate::{DiagnosticsConfig, Severity};
13 
14 /// Takes a multi-file input fixture with annotated cursor positions,
15 /// and checks that:
16 ///  * a diagnostic is produced
17 ///  * the first diagnostic fix trigger range touches the input cursor position
18 ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
19 #[track_caller]
check_fix(ra_fixture_before: &str, ra_fixture_after: &str)20 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
21     check_nth_fix(0, ra_fixture_before, ra_fixture_after);
22 }
23 /// Takes a multi-file input fixture with annotated cursor positions,
24 /// and checks that:
25 ///  * a diagnostic is produced
26 ///  * every diagnostic fixes trigger range touches the input cursor position
27 ///  * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>)28 pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
29     for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
30         check_nth_fix(i, ra_fixture_before, ra_fixture_after)
31     }
32 }
33 
34 #[track_caller]
check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str)35 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
36     let after = trim_indent(ra_fixture_after);
37 
38     let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
39     let diagnostic = super::diagnostics(
40         &db,
41         &DiagnosticsConfig::default(),
42         &AssistResolveStrategy::All,
43         file_position.file_id,
44     )
45     .pop()
46     .expect("no diagnostics");
47     let fix = &diagnostic.fixes.expect("diagnostic misses fixes")[nth];
48     let actual = {
49         let source_change = fix.source_change.as_ref().unwrap();
50         let file_id = *source_change.source_file_edits.keys().next().unwrap();
51         let mut actual = db.file_text(file_id).to_string();
52 
53         for edit in source_change.source_file_edits.values() {
54             edit.apply(&mut actual);
55         }
56         actual
57     };
58 
59     assert_eq_text!(&after, &actual);
60     assert!(
61         fix.target.contains_inclusive(file_position.offset),
62         "diagnostic fix range {:?} does not touch cursor position {:?}",
63         fix.target,
64         file_position.offset
65     );
66 }
67 
68 /// Checks that there's a diagnostic *without* fix at `$0`.
check_no_fix(ra_fixture: &str)69 pub(crate) fn check_no_fix(ra_fixture: &str) {
70     let (db, file_position) = RootDatabase::with_position(ra_fixture);
71     let diagnostic = super::diagnostics(
72         &db,
73         &DiagnosticsConfig::default(),
74         &AssistResolveStrategy::All,
75         file_position.file_id,
76     )
77     .pop()
78     .unwrap();
79     assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
80 }
81 
check_expect(ra_fixture: &str, expect: Expect)82 pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
83     let (db, file_id) = RootDatabase::with_single_file(ra_fixture);
84     let diagnostics = super::diagnostics(
85         &db,
86         &DiagnosticsConfig::default(),
87         &AssistResolveStrategy::All,
88         file_id,
89     );
90     expect.assert_debug_eq(&diagnostics)
91 }
92 
93 #[track_caller]
check_diagnostics(ra_fixture: &str)94 pub(crate) fn check_diagnostics(ra_fixture: &str) {
95     let mut config = DiagnosticsConfig::default();
96     config.disabled.insert("inactive-code".to_string());
97     check_diagnostics_with_config(config, ra_fixture)
98 }
99 
100 #[track_caller]
check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str)101 pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
102     let (db, files) = RootDatabase::with_many_files(ra_fixture);
103     for file_id in files {
104         let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
105 
106         let expected = extract_annotations(&*db.file_text(file_id));
107         let mut actual = diagnostics
108             .into_iter()
109             .map(|d| {
110                 let mut annotation = String::new();
111                 if let Some(fixes) = &d.fixes {
112                     assert!(!fixes.is_empty());
113                     annotation.push_str("�� ")
114                 }
115                 annotation.push_str(match d.severity {
116                     Severity::Error => "error",
117                     Severity::WeakWarning => "weak",
118                 });
119                 annotation.push_str(": ");
120                 annotation.push_str(&d.message);
121                 (d.range, annotation)
122             })
123             .collect::<Vec<_>>();
124         actual.sort_by_key(|(range, _)| range.start());
125         assert_eq!(expected, actual);
126     }
127 }
128 
129 #[test]
test_disabled_diagnostics()130 fn test_disabled_diagnostics() {
131     let mut config = DiagnosticsConfig::default();
132     config.disabled.insert("unresolved-module".into());
133 
134     let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#);
135 
136     let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
137     assert!(diagnostics.is_empty());
138 
139     let diagnostics = super::diagnostics(
140         &db,
141         &DiagnosticsConfig::default(),
142         &AssistResolveStrategy::All,
143         file_id,
144     );
145     assert!(!diagnostics.is_empty());
146 }
147