aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/diagnostics.rs')
-rw-r--r--crates/ra_ide/src/diagnostics.rs733
1 files changed, 321 insertions, 412 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 46f8c31c7..e029af0dc 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -35,7 +35,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
35 let parse = db.parse(file_id); 35 let parse = db.parse(file_id);
36 let mut res = Vec::new(); 36 let mut res = Vec::new();
37 37
38 res.extend(parse.errors().iter().map(|err| Diagnostic { 38 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
39 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
39 range: err.range(), 40 range: err.range(),
40 message: format!("Syntax Error: {}", err), 41 message: format!("Syntax Error: {}", err),
41 severity: Severity::Error, 42 severity: Severity::Error,
@@ -99,14 +100,6 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
99 fix, 100 fix,
100 }) 101 })
101 }) 102 })
102 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
103 res.borrow_mut().push(Diagnostic {
104 range: sema.diagnostics_range(d).range,
105 message: d.message(),
106 severity: Severity::Error,
107 fix: None,
108 })
109 })
110 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
111 let node = d.ast(db); 104 let node = d.ast(db);
112 let replacement = format!("Ok({})", node.syntax()); 105 let replacement = format!("Ok({})", node.syntax());
@@ -138,7 +131,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
138 131
139fn missing_struct_field_fix( 132fn missing_struct_field_fix(
140 sema: &Semantics<RootDatabase>, 133 sema: &Semantics<RootDatabase>,
141 file_id: FileId, 134 usage_file_id: FileId,
142 d: &hir::diagnostics::NoSuchField, 135 d: &hir::diagnostics::NoSuchField,
143) -> Option<Fix> { 136) -> Option<Fix> {
144 let record_expr = sema.ast(d); 137 let record_expr = sema.ast(d);
@@ -146,25 +139,30 @@ fn missing_struct_field_fix(
146 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?; 139 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?;
147 let def_id = sema.resolve_variant(record_lit)?; 140 let def_id = sema.resolve_variant(record_lit)?;
148 let module; 141 let module;
142 let def_file_id;
149 let record_fields = match VariantDef::from(def_id) { 143 let record_fields = match VariantDef::from(def_id) {
150 VariantDef::Struct(s) => { 144 VariantDef::Struct(s) => {
151 module = s.module(sema.db); 145 module = s.module(sema.db);
152 let source = s.source(sema.db); 146 let source = s.source(sema.db);
147 def_file_id = source.file_id;
153 let fields = source.value.field_def_list()?; 148 let fields = source.value.field_def_list()?;
154 record_field_def_list(fields)? 149 record_field_def_list(fields)?
155 } 150 }
156 VariantDef::Union(u) => { 151 VariantDef::Union(u) => {
157 module = u.module(sema.db); 152 module = u.module(sema.db);
158 let source = u.source(sema.db); 153 let source = u.source(sema.db);
154 def_file_id = source.file_id;
159 source.value.record_field_def_list()? 155 source.value.record_field_def_list()?
160 } 156 }
161 VariantDef::EnumVariant(e) => { 157 VariantDef::EnumVariant(e) => {
162 module = e.module(sema.db); 158 module = e.module(sema.db);
163 let source = e.source(sema.db); 159 let source = e.source(sema.db);
160 def_file_id = source.file_id;
164 let fields = source.value.field_def_list()?; 161 let fields = source.value.field_def_list()?;
165 record_field_def_list(fields)? 162 record_field_def_list(fields)?
166 } 163 }
167 }; 164 };
165 let def_file_id = def_file_id.original_file(sema.db);
168 166
169 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; 167 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
170 if new_field_type.is_unknown() { 168 if new_field_type.is_unknown() {
@@ -179,15 +177,19 @@ fn missing_struct_field_fix(
179 let last_field_syntax = last_field.syntax(); 177 let last_field_syntax = last_field.syntax();
180 let indent = IndentLevel::from_node(last_field_syntax); 178 let indent = IndentLevel::from_node(last_field_syntax);
181 179
182 let mut new_field = format!("\n{}{}", indent, new_field); 180 let mut new_field = new_field.to_string();
181 if usage_file_id != def_file_id {
182 new_field = format!("pub(crate) {}", new_field);
183 }
184 new_field = format!("\n{}{}", indent, new_field);
183 185
184 let needs_comma = !last_field_syntax.to_string().ends_with(","); 186 let needs_comma = !last_field_syntax.to_string().ends_with(',');
185 if needs_comma { 187 if needs_comma {
186 new_field = format!(",{}", new_field); 188 new_field = format!(",{}", new_field);
187 } 189 }
188 190
189 let source_change = SourceFileEdit { 191 let source_change = SourceFileEdit {
190 file_id, 192 file_id: def_file_id,
191 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), 193 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
192 }; 194 };
193 let fix = Fix::new("Create field", source_change.into()); 195 let fix = Fix::new("Create field", source_change.into());
@@ -281,53 +283,21 @@ fn check_struct_shorthand_initialization(
281 283
282#[cfg(test)] 284#[cfg(test)]
283mod tests { 285mod tests {
284 use insta::assert_debug_snapshot;
285 use ra_syntax::SourceFile;
286 use stdx::trim_indent; 286 use stdx::trim_indent;
287 use test_utils::assert_eq_text; 287 use test_utils::assert_eq_text;
288 288
289 use crate::mock_analysis::{analysis_and_position, single_file}; 289 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
290 290 use expect::{expect, Expect};
291 use super::*;
292
293 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
294
295 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
296 let parse = SourceFile::parse(code);
297 let mut diagnostics = Vec::new();
298 for node in parse.tree().syntax().descendants() {
299 func(&mut diagnostics, FileId(0), &node);
300 }
301 assert!(diagnostics.is_empty());
302 }
303
304 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
305 let parse = SourceFile::parse(before);
306 let mut diagnostics = Vec::new();
307 for node in parse.tree().syntax().descendants() {
308 func(&mut diagnostics, FileId(0), &node);
309 }
310 let diagnostic =
311 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
312 let mut fix = diagnostic.fix.unwrap();
313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
314 let actual = {
315 let mut actual = before.to_string();
316 edit.apply(&mut actual);
317 actual
318 };
319 assert_eq_text!(after, &actual);
320 }
321 291
322 /// Takes a multi-file input fixture with annotated cursor positions, 292 /// Takes a multi-file input fixture with annotated cursor positions,
323 /// and checks that: 293 /// and checks that:
324 /// * a diagnostic is produced 294 /// * a diagnostic is produced
325 /// * this diagnostic touches the input cursor position 295 /// * this diagnostic touches the input cursor position
326 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 296 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
327 fn check_apply_diagnostic_fix_from_position(ra_fixture: &str, after: &str) { 297 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
328 let after = trim_indent(after); 298 let after = trim_indent(ra_fixture_after);
329 299
330 let (analysis, file_position) = analysis_and_position(ra_fixture); 300 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
331 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 301 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
332 let mut fix = diagnostic.fix.unwrap(); 302 let mut fix = diagnostic.fix.unwrap();
333 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 303 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -348,16 +318,20 @@ mod tests {
348 ); 318 );
349 } 319 }
350 320
351 fn check_apply_diagnostic_fix(ra_fixture_before: &str, ra_fixture_after: &str) { 321 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
322 /// which has a fix that can apply to other files.
323 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
352 let ra_fixture_after = &trim_indent(ra_fixture_after); 324 let ra_fixture_after = &trim_indent(ra_fixture_after);
353 let (analysis, file_id) = single_file(ra_fixture_before); 325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
354 let before = analysis.file_text(file_id).unwrap(); 326 let current_file_id = file_pos.file_id;
355 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap();
356 let mut fix = diagnostic.fix.unwrap(); 328 let mut fix = diagnostic.fix.unwrap();
357 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 329 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id;
331 let before = analysis.file_text(changed_file_id).unwrap();
358 let actual = { 332 let actual = {
359 let mut actual = before.to_string(); 333 let mut actual = before.to_string();
360 edit.apply(&mut actual); 334 edit.edit.apply(&mut actual);
361 actual 335 actual
362 }; 336 };
363 assert_eq_text!(ra_fixture_after, &actual); 337 assert_eq_text!(ra_fixture_after, &actual);
@@ -365,267 +339,249 @@ mod tests {
365 339
366 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 340 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
367 /// apply to the file containing the cursor. 341 /// apply to the file containing the cursor.
368 fn check_no_diagnostic_for_target_file(ra_fixture: &str) { 342 fn check_no_diagnostics(ra_fixture: &str) {
369 let (analysis, file_position) = analysis_and_position(ra_fixture); 343 let mock = MockAnalysis::with_files(ra_fixture);
370 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 344 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
371 assert_eq!(diagnostics.len(), 0); 345 let analysis = mock.analysis();
372 } 346 let diagnostics = files
373 347 .into_iter()
374 fn check_no_diagnostic(ra_fixture: &str) { 348 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
349 .collect::<Vec<_>>();
350 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
351 }
352
353 fn check_expect(ra_fixture: &str, expect: Expect) {
375 let (analysis, file_id) = single_file(ra_fixture); 354 let (analysis, file_id) = single_file(ra_fixture);
376 let diagnostics = analysis.diagnostics(file_id).unwrap(); 355 let diagnostics = analysis.diagnostics(file_id).unwrap();
377 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 356 expect.assert_debug_eq(&diagnostics)
378 } 357 }
379 358
380 #[test] 359 #[test]
381 fn test_wrap_return_type() { 360 fn test_wrap_return_type() {
382 let before = r#" 361 check_fix(
383 //- /main.rs 362 r#"
384 use core::result::Result::{self, Ok, Err}; 363//- /main.rs
364use core::result::Result::{self, Ok, Err};
385 365
386 fn div(x: i32, y: i32) -> Result<i32, ()> { 366fn div(x: i32, y: i32) -> Result<i32, ()> {
387 if y == 0 { 367 if y == 0 {
388 return Err(()); 368 return Err(());
389 } 369 }
390 x / y<|> 370 x / y<|>
391 } 371}
392 //- /core/lib.rs 372//- /core/lib.rs
393 pub mod result { 373pub mod result {
394 pub enum Result<T, E> { Ok(T), Err(E) } 374 pub enum Result<T, E> { Ok(T), Err(E) }
395 } 375}
396 "#; 376"#,
397 let after = r#" 377 r#"
398 use core::result::Result::{self, Ok, Err}; 378use core::result::Result::{self, Ok, Err};
399 379
400 fn div(x: i32, y: i32) -> Result<i32, ()> { 380fn div(x: i32, y: i32) -> Result<i32, ()> {
401 if y == 0 { 381 if y == 0 {
402 return Err(()); 382 return Err(());
403 } 383 }
404 Ok(x / y) 384 Ok(x / y)
405 } 385}
406 "#; 386"#,
407 check_apply_diagnostic_fix_from_position(before, after); 387 );
408 } 388 }
409 389
410 #[test] 390 #[test]
411 fn test_wrap_return_type_handles_generic_functions() { 391 fn test_wrap_return_type_handles_generic_functions() {
412 let before = r#" 392 check_fix(
413 //- /main.rs 393 r#"
414 use core::result::Result::{self, Ok, Err}; 394//- /main.rs
395use core::result::Result::{self, Ok, Err};
415 396
416 fn div<T>(x: T) -> Result<T, i32> { 397fn div<T>(x: T) -> Result<T, i32> {
417 if x == 0 { 398 if x == 0 {
418 return Err(7); 399 return Err(7);
419 } 400 }
420 <|>x 401 <|>x
421 } 402}
422 //- /core/lib.rs 403//- /core/lib.rs
423 pub mod result { 404pub mod result {
424 pub enum Result<T, E> { Ok(T), Err(E) } 405 pub enum Result<T, E> { Ok(T), Err(E) }
425 } 406}
426 "#; 407"#,
427 let after = r#" 408 r#"
428 use core::result::Result::{self, Ok, Err}; 409use core::result::Result::{self, Ok, Err};
429 410
430 fn div<T>(x: T) -> Result<T, i32> { 411fn div<T>(x: T) -> Result<T, i32> {
431 if x == 0 { 412 if x == 0 {
432 return Err(7); 413 return Err(7);
433 } 414 }
434 Ok(x) 415 Ok(x)
435 } 416}
436 "#; 417"#,
437 check_apply_diagnostic_fix_from_position(before, after); 418 );
438 } 419 }
439 420
440 #[test] 421 #[test]
441 fn test_wrap_return_type_handles_type_aliases() { 422 fn test_wrap_return_type_handles_type_aliases() {
442 let before = r#" 423 check_fix(
443 //- /main.rs 424 r#"
444 use core::result::Result::{self, Ok, Err}; 425//- /main.rs
426use core::result::Result::{self, Ok, Err};
445 427
446 type MyResult<T> = Result<T, ()>; 428type MyResult<T> = Result<T, ()>;
447 429
448 fn div(x: i32, y: i32) -> MyResult<i32> { 430fn div(x: i32, y: i32) -> MyResult<i32> {
449 if y == 0 { 431 if y == 0 {
450 return Err(()); 432 return Err(());
451 } 433 }
452 x <|>/ y 434 x <|>/ y
453 } 435}
454 //- /core/lib.rs 436//- /core/lib.rs
455 pub mod result { 437pub mod result {
456 pub enum Result<T, E> { Ok(T), Err(E) } 438 pub enum Result<T, E> { Ok(T), Err(E) }
457 } 439}
458 "#; 440"#,
459 let after = r#" 441 r#"
460 use core::result::Result::{self, Ok, Err}; 442use core::result::Result::{self, Ok, Err};
461 443
462 type MyResult<T> = Result<T, ()>; 444type MyResult<T> = Result<T, ()>;
463 445
464 fn div(x: i32, y: i32) -> MyResult<i32> { 446fn div(x: i32, y: i32) -> MyResult<i32> {
465 if y == 0 { 447 if y == 0 {
466 return Err(()); 448 return Err(());
467 } 449 }
468 Ok(x / y) 450 Ok(x / y)
469 } 451}
470 "#; 452"#,
471 check_apply_diagnostic_fix_from_position(before, after); 453 );
472 } 454 }
473 455
474 #[test] 456 #[test]
475 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 457 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
476 check_no_diagnostic_for_target_file( 458 check_no_diagnostics(
477 r" 459 r#"
478 //- /main.rs 460//- /main.rs
479 use core::result::Result::{self, Ok, Err}; 461use core::result::Result::{self, Ok, Err};
480 462
481 fn foo() -> Result<(), i32> { 463fn foo() -> Result<(), i32> { 0 }
482 0<|>
483 }
484 464
485 //- /core/lib.rs 465//- /core/lib.rs
486 pub mod result { 466pub mod result {
487 pub enum Result<T, E> { Ok(T), Err(E) } 467 pub enum Result<T, E> { Ok(T), Err(E) }
488 } 468}
489 ", 469"#,
490 ); 470 );
491 } 471 }
492 472
493 #[test] 473 #[test]
494 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 474 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
495 check_no_diagnostic_for_target_file( 475 check_no_diagnostics(
496 r" 476 r#"
497 //- /main.rs 477//- /main.rs
498 use core::result::Result::{self, Ok, Err}; 478use core::result::Result::{self, Ok, Err};
499 479
500 enum SomeOtherEnum { 480enum SomeOtherEnum { Ok(i32), Err(String) }
501 Ok(i32),
502 Err(String),
503 }
504 481
505 fn foo() -> SomeOtherEnum { 482fn foo() -> SomeOtherEnum { 0 }
506 0<|>
507 }
508 483
509 //- /core/lib.rs 484//- /core/lib.rs
510 pub mod result { 485pub mod result {
511 pub enum Result<T, E> { Ok(T), Err(E) } 486 pub enum Result<T, E> { Ok(T), Err(E) }
512 } 487}
513 ", 488"#,
514 ); 489 );
515 } 490 }
516 491
517 #[test] 492 #[test]
518 fn test_fill_struct_fields_empty() { 493 fn test_fill_struct_fields_empty() {
519 let before = r" 494 check_fix(
520 struct TestStruct { 495 r#"
521 one: i32, 496struct TestStruct { one: i32, two: i64 }
522 two: i64,
523 }
524 497
525 fn test_fn() { 498fn test_fn() {
526 let s = TestStruct{}; 499 let s = TestStruct {<|>};
527 } 500}
528 "; 501"#,
529 let after = r" 502 r#"
530 struct TestStruct { 503struct TestStruct { one: i32, two: i64 }
531 one: i32,
532 two: i64,
533 }
534 504
535 fn test_fn() { 505fn test_fn() {
536 let s = TestStruct{ one: (), two: ()}; 506 let s = TestStruct { one: (), two: ()};
537 } 507}
538 "; 508"#,
539 check_apply_diagnostic_fix(before, after); 509 );
540 } 510 }
541 511
542 #[test] 512 #[test]
543 fn test_fill_struct_fields_self() { 513 fn test_fill_struct_fields_self() {
544 let before = r" 514 check_fix(
545 struct TestStruct { 515 r#"
546 one: i32, 516struct TestStruct { one: i32 }
547 }
548 517
549 impl TestStruct { 518impl TestStruct {
550 fn test_fn() { 519 fn test_fn() { let s = Self {<|>}; }
551 let s = Self {}; 520}
552 } 521"#,
553 } 522 r#"
554 "; 523struct TestStruct { one: i32 }
555 let after = r"
556 struct TestStruct {
557 one: i32,
558 }
559 524
560 impl TestStruct { 525impl TestStruct {
561 fn test_fn() { 526 fn test_fn() { let s = Self { one: ()}; }
562 let s = Self { one: ()}; 527}
563 } 528"#,
564 } 529 );
565 ";
566 check_apply_diagnostic_fix(before, after);
567 } 530 }
568 531
569 #[test] 532 #[test]
570 fn test_fill_struct_fields_enum() { 533 fn test_fill_struct_fields_enum() {
571 let before = r" 534 check_fix(
572 enum Expr { 535 r#"
573 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 536enum Expr {
574 } 537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
538}
575 539
576 impl Expr { 540impl Expr {
577 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
578 Expr::Bin { } 542 Expr::Bin {<|> }
579 } 543 }
580 } 544}
581 "; 545"#,
582 let after = r" 546 r#"
583 enum Expr { 547enum Expr {
584 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 548 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
585 } 549}
586 550
587 impl Expr { 551impl Expr {
588 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 552 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
589 Expr::Bin { lhs: (), rhs: () } 553 Expr::Bin { lhs: (), rhs: () }
590 } 554 }
591 } 555}
592 "; 556"#,
593 check_apply_diagnostic_fix(before, after); 557 );
594 } 558 }
595 559
596 #[test] 560 #[test]
597 fn test_fill_struct_fields_partial() { 561 fn test_fill_struct_fields_partial() {
598 let before = r" 562 check_fix(
599 struct TestStruct { 563 r#"
600 one: i32, 564struct TestStruct { one: i32, two: i64 }
601 two: i64,
602 }
603 565
604 fn test_fn() { 566fn test_fn() {
605 let s = TestStruct{ two: 2 }; 567 let s = TestStruct{ two: 2<|> };
606 } 568}
607 "; 569"#,
608 let after = r" 570 r"
609 struct TestStruct { 571struct TestStruct { one: i32, two: i64 }
610 one: i32,
611 two: i64,
612 }
613 572
614 fn test_fn() { 573fn test_fn() {
615 let s = TestStruct{ two: 2, one: () }; 574 let s = TestStruct{ two: 2, one: () };
616 } 575}
617 "; 576",
618 check_apply_diagnostic_fix(before, after); 577 );
619 } 578 }
620 579
621 #[test] 580 #[test]
622 fn test_fill_struct_fields_no_diagnostic() { 581 fn test_fill_struct_fields_no_diagnostic() {
623 check_no_diagnostic( 582 check_no_diagnostics(
624 r" 583 r"
625 struct TestStruct { 584 struct TestStruct { one: i32, two: i64 }
626 one: i32,
627 two: i64,
628 }
629 585
630 fn test_fn() { 586 fn test_fn() {
631 let one = 1; 587 let one = 1;
@@ -637,12 +593,9 @@ mod tests {
637 593
638 #[test] 594 #[test]
639 fn test_fill_struct_fields_no_diagnostic_on_spread() { 595 fn test_fill_struct_fields_no_diagnostic_on_spread() {
640 check_no_diagnostic( 596 check_no_diagnostics(
641 r" 597 r"
642 struct TestStruct { 598 struct TestStruct { one: i32, two: i64 }
643 one: i32,
644 two: i64,
645 }
646 599
647 fn test_fn() { 600 fn test_fn() {
648 let one = 1; 601 let one = 1;
@@ -654,211 +607,143 @@ mod tests {
654 607
655 #[test] 608 #[test]
656 fn test_unresolved_module_diagnostic() { 609 fn test_unresolved_module_diagnostic() {
657 let (analysis, file_id) = single_file("mod foo;"); 610 check_expect(
658 let diagnostics = analysis.diagnostics(file_id).unwrap(); 611 r#"mod foo;"#,
659 assert_debug_snapshot!(diagnostics, @r###" 612 expect![[r#"
660 [ 613 [
661 Diagnostic { 614 Diagnostic {
662 message: "unresolved module", 615 message: "unresolved module",
663 range: 0..8, 616 range: 0..8,
664 severity: Error, 617 severity: Error,
665 fix: Some( 618 fix: Some(
666 Fix { 619 Fix {
667 label: "Create module", 620 label: "Create module",
668 source_change: SourceChange { 621 source_change: SourceChange {
669 source_file_edits: [], 622 source_file_edits: [],
670 file_system_edits: [ 623 file_system_edits: [
671 CreateFile { 624 CreateFile {
672 anchor: FileId( 625 anchor: FileId(
673 1, 626 1,
674 ), 627 ),
675 dst: "foo.rs", 628 dst: "foo.rs",
629 },
630 ],
631 is_snippet: false,
676 }, 632 },
677 ], 633 },
678 is_snippet: false, 634 ),
679 },
680 }, 635 },
681 ), 636 ]
682 }, 637 "#]],
683 ] 638 );
684 "###);
685 } 639 }
686 640
687 #[test] 641 #[test]
688 fn range_mapping_out_of_macros() { 642 fn range_mapping_out_of_macros() {
689 let (analysis, file_id) = single_file( 643 // FIXME: this is very wrong, but somewhat tricky to fix.
690 r" 644 check_fix(
691 fn some() {} 645 r#"
692 fn items() {} 646fn some() {}
693 fn here() {} 647fn items() {}
648fn here() {}
694 649
695 macro_rules! id { 650macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
696 ($($tt:tt)*) => { $($tt)*};
697 }
698 651
699 fn main() { 652fn main() {
700 let _x = id![Foo { a: 42 }]; 653 let _x = id![Foo { a: <|>42 }];
701 } 654}
702 655
703 pub struct Foo { 656pub struct Foo { pub a: i32, pub b: i32 }
704 pub a: i32, 657"#,
705 pub b: i32, 658 r#"
706 } 659fn {a:42, b: ()} {}
707 ", 660fn items() {}
661fn here() {}
662
663macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
664
665fn main() {
666 let _x = id![Foo { a: 42 }];
667}
668
669pub struct Foo { pub a: i32, pub b: i32 }
670"#,
708 ); 671 );
709 let diagnostics = analysis.diagnostics(file_id).unwrap();
710 assert_debug_snapshot!(diagnostics, @r###"
711 [
712 Diagnostic {
713 message: "Missing structure fields:\n- b\n",
714 range: 127..136,
715 severity: Error,
716 fix: Some(
717 Fix {
718 label: "Fill struct fields",
719 source_change: SourceChange {
720 source_file_edits: [
721 SourceFileEdit {
722 file_id: FileId(
723 1,
724 ),
725 edit: TextEdit {
726 indels: [
727 Indel {
728 insert: "{a:42, b: ()}",
729 delete: 3..9,
730 },
731 ],
732 },
733 },
734 ],
735 file_system_edits: [],
736 is_snippet: false,
737 },
738 },
739 ),
740 },
741 ]
742 "###);
743 } 672 }
744 673
745 #[test] 674 #[test]
746 fn test_check_unnecessary_braces_in_use_statement() { 675 fn test_check_unnecessary_braces_in_use_statement() {
747 check_not_applicable( 676 check_no_diagnostics(
748 " 677 r#"
749 use a; 678use a;
750 use a::{c, d::e}; 679use a::{c, d::e};
751 ", 680"#,
752 check_unnecessary_braces_in_use_statement,
753 );
754 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
755 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
756 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
757 check_apply(
758 "use a::{c, d::{e}};",
759 "use a::{c, d::e};",
760 check_unnecessary_braces_in_use_statement,
761 ); 681 );
682 check_fix(r#"use {<|>b};"#, r#"use b;"#);
683 check_fix(r#"use {b<|>};"#, r#"use b;"#);
684 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
685 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
686 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
762 } 687 }
763 688
764 #[test] 689 #[test]
765 fn test_check_struct_shorthand_initialization() { 690 fn test_check_struct_shorthand_initialization() {
766 check_not_applicable( 691 check_no_diagnostics(
767 r#" 692 r#"
768 struct A { 693struct A { a: &'static str }
769 a: &'static str 694fn main() { A { a: "hello" } }
770 } 695"#,
771
772 fn main() {
773 A {
774 a: "hello"
775 }
776 }
777 "#,
778 check_struct_shorthand_initialization,
779 ); 696 );
780 check_not_applicable( 697 check_no_diagnostics(
781 r#" 698 r#"
782 struct A(usize); 699struct A(usize);
783 700fn main() { A { 0: 0 } }
784 fn main() { 701"#,
785 A {
786 0: 0
787 }
788 }
789 "#,
790 check_struct_shorthand_initialization,
791 ); 702 );
792 703
793 check_apply( 704 check_fix(
794 r#" 705 r#"
795struct A { 706struct A { a: &'static str }
796 a: &'static str
797}
798
799fn main() { 707fn main() {
800 let a = "haha"; 708 let a = "haha";
801 A { 709 A { a<|>: a }
802 a: a
803 }
804} 710}
805 "#, 711"#,
806 r#" 712 r#"
807struct A { 713struct A { a: &'static str }
808 a: &'static str
809}
810
811fn main() { 714fn main() {
812 let a = "haha"; 715 let a = "haha";
813 A { 716 A { a }
814 a
815 }
816} 717}
817 "#, 718"#,
818 check_struct_shorthand_initialization,
819 ); 719 );
820 720
821 check_apply( 721 check_fix(
822 r#" 722 r#"
823struct A { 723struct A { a: &'static str, b: &'static str }
824 a: &'static str,
825 b: &'static str
826}
827
828fn main() { 724fn main() {
829 let a = "haha"; 725 let a = "haha";
830 let b = "bb"; 726 let b = "bb";
831 A { 727 A { a<|>: a, b }
832 a: a,
833 b
834 }
835} 728}
836 "#, 729"#,
837 r#" 730 r#"
838struct A { 731struct A { a: &'static str, b: &'static str }
839 a: &'static str,
840 b: &'static str
841}
842
843fn main() { 732fn main() {
844 let a = "haha"; 733 let a = "haha";
845 let b = "bb"; 734 let b = "bb";
846 A { 735 A { a, b }
847 a,
848 b
849 }
850} 736}
851 "#, 737"#,
852 check_struct_shorthand_initialization,
853 ); 738 );
854 } 739 }
855 740
856 #[test] 741 #[test]
857 fn test_add_field_from_usage() { 742 fn test_add_field_from_usage() {
858 check_apply_diagnostic_fix( 743 check_fix(
859 r" 744 r"
860fn main() { 745fn main() {
861 Foo { bar: 3, baz: false}; 746 Foo { bar: 3, baz<|>: false};
862} 747}
863struct Foo { 748struct Foo {
864 bar: i32 749 bar: i32
@@ -875,4 +760,28 @@ struct Foo {
875", 760",
876 ) 761 )
877 } 762 }
763
764 #[test]
765 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file(
767 r"
768 //- /main.rs
769 mod foo;
770
771 fn main() {
772 <|>foo::Foo { bar: 3, baz: false};
773 }
774 //- /foo.rs
775 struct Foo {
776 bar: i32
777 }
778 ",
779 r"
780 struct Foo {
781 bar: i32,
782 pub(crate) baz: bool
783 }
784 ",
785 )
786 }
878} 787}