aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/diagnostics.rs')
-rw-r--r--crates/ide/src/diagnostics.rs63
1 files changed, 42 insertions, 21 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 4172f6cae..27d347dbd 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -28,7 +28,7 @@ use unlinked_file::UnlinkedFile;
28 28
29use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange}; 29use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
30 30
31use self::fixes::DiagnosticWithFix; 31use self::fixes::DiagnosticWithFixes;
32 32
33#[derive(Debug)] 33#[derive(Debug)]
34pub struct Diagnostic { 34pub struct Diagnostic {
@@ -36,14 +36,14 @@ pub struct Diagnostic {
36 pub message: String, 36 pub message: String,
37 pub range: TextRange, 37 pub range: TextRange,
38 pub severity: Severity, 38 pub severity: Severity,
39 pub fix: Option<Assist>, 39 pub fixes: Option<Vec<Assist>>,
40 pub unused: bool, 40 pub unused: bool,
41 pub code: Option<DiagnosticCode>, 41 pub code: Option<DiagnosticCode>,
42} 42}
43 43
44impl Diagnostic { 44impl Diagnostic {
45 fn error(range: TextRange, message: String) -> Self { 45 fn error(range: TextRange, message: String) -> Self {
46 Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None } 46 Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
47 } 47 }
48 48
49 fn hint(range: TextRange, message: String) -> Self { 49 fn hint(range: TextRange, message: String) -> Self {
@@ -51,14 +51,14 @@ impl Diagnostic {
51 message, 51 message,
52 range, 52 range,
53 severity: Severity::WeakWarning, 53 severity: Severity::WeakWarning,
54 fix: None, 54 fixes: None,
55 unused: false, 55 unused: false,
56 code: None, 56 code: None,
57 } 57 }
58 } 58 }
59 59
60 fn with_fix(self, fix: Option<Assist>) -> Self { 60 fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self {
61 Self { fix, ..self } 61 Self { fixes, ..self }
62 } 62 }
63 63
64 fn with_unused(self, unused: bool) -> Self { 64 fn with_unused(self, unused: bool) -> Self {
@@ -154,7 +154,7 @@ pub(crate) fn diagnostics(
154 // Override severity and mark as unused. 154 // Override severity and mark as unused.
155 res.borrow_mut().push( 155 res.borrow_mut().push(
156 Diagnostic::hint(range, d.message()) 156 Diagnostic::hint(range, d.message())
157 .with_fix(d.fix(&sema, resolve)) 157 .with_fixes(d.fixes(&sema, resolve))
158 .with_code(Some(d.code())), 158 .with_code(Some(d.code())),
159 ); 159 );
160 }) 160 })
@@ -210,23 +210,23 @@ pub(crate) fn diagnostics(
210 res.into_inner() 210 res.into_inner()
211} 211}
212 212
213fn diagnostic_with_fix<D: DiagnosticWithFix>( 213fn diagnostic_with_fix<D: DiagnosticWithFixes>(
214 d: &D, 214 d: &D,
215 sema: &Semantics<RootDatabase>, 215 sema: &Semantics<RootDatabase>,
216 resolve: &AssistResolveStrategy, 216 resolve: &AssistResolveStrategy,
217) -> Diagnostic { 217) -> Diagnostic {
218 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 218 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
219 .with_fix(d.fix(&sema, resolve)) 219 .with_fixes(d.fixes(&sema, resolve))
220 .with_code(Some(d.code())) 220 .with_code(Some(d.code()))
221} 221}
222 222
223fn warning_with_fix<D: DiagnosticWithFix>( 223fn warning_with_fix<D: DiagnosticWithFixes>(
224 d: &D, 224 d: &D,
225 sema: &Semantics<RootDatabase>, 225 sema: &Semantics<RootDatabase>,
226 resolve: &AssistResolveStrategy, 226 resolve: &AssistResolveStrategy,
227) -> Diagnostic { 227) -> Diagnostic {
228 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 228 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
229 .with_fix(d.fix(&sema, resolve)) 229 .with_fixes(d.fixes(&sema, resolve))
230 .with_code(Some(d.code())) 230 .with_code(Some(d.code()))
231} 231}
232 232
@@ -256,12 +256,12 @@ fn check_unnecessary_braces_in_use_statement(
256 256
257 acc.push( 257 acc.push(
258 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) 258 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
259 .with_fix(Some(fix( 259 .with_fixes(Some(vec![fix(
260 "remove_braces", 260 "remove_braces",
261 "Remove unnecessary braces", 261 "Remove unnecessary braces",
262 SourceChange::from_text_edit(file_id, edit), 262 SourceChange::from_text_edit(file_id, edit),
263 use_range, 263 use_range,
264 ))), 264 )])),
265 ); 265 );
266 } 266 }
267 267
@@ -309,9 +309,23 @@ mod tests {
309 /// Takes a multi-file input fixture with annotated cursor positions, 309 /// Takes a multi-file input fixture with annotated cursor positions,
310 /// and checks that: 310 /// and checks that:
311 /// * a diagnostic is produced 311 /// * a diagnostic is produced
312 /// * this diagnostic fix trigger range touches the input cursor position 312 /// * the first diagnostic fix trigger range touches the input cursor position
313 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 313 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
314 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 314 pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
315 check_nth_fix(0, ra_fixture_before, ra_fixture_after);
316 }
317 /// Takes a multi-file input fixture with annotated cursor positions,
318 /// and checks that:
319 /// * a diagnostic is produced
320 /// * every diagnostic fixes trigger range touches the input cursor position
321 /// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
322 pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
323 for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
324 check_nth_fix(i, ra_fixture_before, ra_fixture_after)
325 }
326 }
327
328 fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
315 let after = trim_indent(ra_fixture_after); 329 let after = trim_indent(ra_fixture_after);
316 330
317 let (analysis, file_position) = fixture::position(ra_fixture_before); 331 let (analysis, file_position) = fixture::position(ra_fixture_before);
@@ -324,9 +338,9 @@ mod tests {
324 .unwrap() 338 .unwrap()
325 .pop() 339 .pop()
326 .unwrap(); 340 .unwrap();
327 let fix = diagnostic.fix.unwrap(); 341 let fix = &diagnostic.fixes.unwrap()[nth];
328 let actual = { 342 let actual = {
329 let source_change = fix.source_change.unwrap(); 343 let source_change = fix.source_change.as_ref().unwrap();
330 let file_id = *source_change.source_file_edits.keys().next().unwrap(); 344 let file_id = *source_change.source_file_edits.keys().next().unwrap();
331 let mut actual = analysis.file_text(file_id).unwrap().to_string(); 345 let mut actual = analysis.file_text(file_id).unwrap().to_string();
332 346
@@ -344,7 +358,6 @@ mod tests {
344 file_position.offset 358 file_position.offset
345 ); 359 );
346 } 360 }
347
348 /// Checks that there's a diagnostic *without* fix at `$0`. 361 /// Checks that there's a diagnostic *without* fix at `$0`.
349 fn check_no_fix(ra_fixture: &str) { 362 fn check_no_fix(ra_fixture: &str) {
350 let (analysis, file_position) = fixture::position(ra_fixture); 363 let (analysis, file_position) = fixture::position(ra_fixture);
@@ -357,7 +370,7 @@ mod tests {
357 .unwrap() 370 .unwrap()
358 .pop() 371 .pop()
359 .unwrap(); 372 .unwrap();
360 assert!(diagnostic.fix.is_none(), "got a fix when none was expected: {:?}", diagnostic); 373 assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
361 } 374 }
362 375
363 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 376 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
@@ -393,7 +406,7 @@ mod tests {
393 message: "unresolved macro `foo::bar!`", 406 message: "unresolved macro `foo::bar!`",
394 range: 5..8, 407 range: 5..8,
395 severity: Error, 408 severity: Error,
396 fix: None, 409 fixes: None,
397 unused: false, 410 unused: false,
398 code: Some( 411 code: Some(
399 DiagnosticCode( 412 DiagnosticCode(
@@ -542,18 +555,26 @@ mod a {
542 #[test] 555 #[test]
543 fn unlinked_file_prepend_first_item() { 556 fn unlinked_file_prepend_first_item() {
544 cov_mark::check!(unlinked_file_prepend_before_first_item); 557 cov_mark::check!(unlinked_file_prepend_before_first_item);
545 check_fix( 558 // Only tests the first one for `pub mod` since the rest are the same
559 check_fixes(
546 r#" 560 r#"
547//- /main.rs 561//- /main.rs
548fn f() {} 562fn f() {}
549//- /foo.rs 563//- /foo.rs
550$0 564$0
551"#, 565"#,
552 r#" 566 vec![
567 r#"
553mod foo; 568mod foo;
554 569
555fn f() {} 570fn f() {}
556"#, 571"#,
572 r#"
573pub mod foo;
574
575fn f() {}
576"#,
577 ],
557 ); 578 );
558 } 579 }
559 580