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