|
| 1 | +use insta::assert_snapshot; |
| 2 | + |
1 | 3 | use crate::{
|
2 |
| - ast::{self, SourceRange, SourceRangeFactory}, |
3 |
| - diagnostics::Diagnostic, |
| 4 | + ast::{self, CompilationUnit, SourceRange, SourceRangeFactory}, |
| 5 | + diagnostics::{Diagnostic, Diagnostician}, |
4 | 6 | index::{visitor, Index},
|
5 | 7 | lexer::{self, IdProvider},
|
6 | 8 | parser,
|
7 |
| - test_utils::tests::parse_and_validate, |
| 9 | + resolver::TypeAnnotator, |
| 10 | + test_utils::tests::{compile_to_string, parse_and_validate}, |
| 11 | + typesystem, |
8 | 12 | validation::Validator,
|
| 13 | + SourceCode, |
9 | 14 | };
|
10 | 15 |
|
11 | 16 | #[test]
|
@@ -533,3 +538,146 @@ fn automatically_generated_output_types_in_different_files_dont_cause_duplicatio
|
533 | 538 | let diagnostics = validator.diagnostics();
|
534 | 539 | assert_eq!(diagnostics, vec![]);
|
535 | 540 | }
|
| 541 | + |
| 542 | +#[test] |
| 543 | +fn duplicate_with_generic() { |
| 544 | + // a version of the test-util function that does not import the built-in and std-types |
| 545 | + // (or they will cause a duplication issue) |
| 546 | + fn do_index(src: &str, id_provider: IdProvider, file_name: &str) -> (Index, CompilationUnit) { |
| 547 | + let mut index = Index::default(); |
| 548 | + let (mut unit, ..) = parser::parse( |
| 549 | + lexer::lex_with_ids(src, id_provider.clone(), SourceRangeFactory::internal()), |
| 550 | + ast::LinkageType::Internal, |
| 551 | + file_name, |
| 552 | + ); |
| 553 | + ast::pre_process(&mut unit, id_provider.clone()); |
| 554 | + index.import(visitor::visit(&unit, id_provider)); |
| 555 | + (index, unit) |
| 556 | + } |
| 557 | + |
| 558 | + // GIVEN a generic function defined in its own file |
| 559 | + let ids = IdProvider::default(); |
| 560 | + let (index1, unit1) = do_index( |
| 561 | + r#" |
| 562 | + {external} |
| 563 | + FUNCTION foo <T: ANY_INT> : DATE |
| 564 | + VAR_INPUT |
| 565 | + a : T; |
| 566 | + b : T; |
| 567 | + c : T; |
| 568 | + END_VAR |
| 569 | + END_FUNCTION |
| 570 | + "#, |
| 571 | + ids.clone(), |
| 572 | + "file1.st", |
| 573 | + ); |
| 574 | + |
| 575 | + // AND another file that calls that generic function and implicitely |
| 576 | + // create type-specific foo-implementations |
| 577 | + let (index2, unit2) = do_index( |
| 578 | + r#" |
| 579 | + PROGRAM prg1 |
| 580 | + foo(INT#1, SINT#2, SINT#3); |
| 581 | + foo(DINT#1, SINT#2, SINT#3); |
| 582 | + foo(INT#1, SINT#2, SINT#3); |
| 583 | + foo(INT#1, SINT#2, SINT#3); |
| 584 | + END_PROGRAM |
| 585 | + "#, |
| 586 | + ids.clone(), |
| 587 | + "file2.st", |
| 588 | + ); |
| 589 | + |
| 590 | + // AND another file that calls that generic function and implicitely |
| 591 | + // create type-specific foo-implementations |
| 592 | + let (index3, unit3) = do_index( |
| 593 | + r#" |
| 594 | + PROGRAM prg2 |
| 595 | + foo(INT#1, SINT#2, SINT#3); |
| 596 | + foo(DINT#1, SINT#2, SINT#3); |
| 597 | + foo(INT#1, SINT#2, SINT#3); |
| 598 | + foo(INT#1, SINT#2, SINT#3); |
| 599 | + END_PROGRAM |
| 600 | + "#, |
| 601 | + ids, |
| 602 | + "file3.st", |
| 603 | + ); |
| 604 | + // WHEN the index is combined |
| 605 | + let mut global_index = Index::default(); |
| 606 | + for data_type in typesystem::get_builtin_types() { |
| 607 | + global_index.register_type(data_type); |
| 608 | + } |
| 609 | + global_index.import(index1); //import file 1 |
| 610 | + global_index.import(index2); //import file 2 |
| 611 | + global_index.import(index3); //import file 3 |
| 612 | + |
| 613 | + // AND the resolvers does its job |
| 614 | + let (mut annotations1, _) = TypeAnnotator::visit_unit(&global_index, &unit1); |
| 615 | + let (mut annotations2, _) = TypeAnnotator::visit_unit(&global_index, &unit2); |
| 616 | + let (mut annotations3, _) = TypeAnnotator::visit_unit(&global_index, &unit3); |
| 617 | + global_index.import(std::mem::take(&mut annotations1.new_index)); |
| 618 | + global_index.import(std::mem::take(&mut annotations2.new_index)); |
| 619 | + global_index.import(std::mem::take(&mut annotations3.new_index)); |
| 620 | + |
| 621 | + // THEN the index should contain 5 pous, 2 were dynamically generated by the visitor (foo__INT & foo__DINT) |
| 622 | + assert_eq!( |
| 623 | + vec!["foo", "prg1", "prg2", "foo__INT", "foo__DINT"], |
| 624 | + global_index |
| 625 | + .get_pous() |
| 626 | + .values() |
| 627 | + .map(|it| it.get_name()) |
| 628 | + .collect::<Vec<_>>() |
| 629 | + ); |
| 630 | + |
| 631 | + // AND there should be no duplication diagnostics |
| 632 | + let mut validator = Validator::new(); |
| 633 | + validator.perform_global_validation(&global_index); |
| 634 | + let diagnostics = validator.diagnostics(); |
| 635 | + assert_eq!(diagnostics, vec![]); |
| 636 | +} |
| 637 | + |
| 638 | +#[test] |
| 639 | +fn duplicate_with_generic_ir() { |
| 640 | + // GIVEN several files with calls to a generic function |
| 641 | + let file1: SourceCode = r" |
| 642 | + {external} |
| 643 | + FUNCTION foo <T: ANY_INT> : DATE |
| 644 | + VAR_INPUT |
| 645 | + a : T; |
| 646 | + b : T; |
| 647 | + c : T; |
| 648 | + END_VAR |
| 649 | + END_FUNCTION |
| 650 | + " |
| 651 | + .into(); |
| 652 | + |
| 653 | + let file2: SourceCode = r" |
| 654 | + PROGRAM prg1 |
| 655 | + foo(INT#1, SINT#2, SINT#3); |
| 656 | + foo(DINT#1, SINT#2, SINT#3); |
| 657 | + foo(INT#1, SINT#2, SINT#3); |
| 658 | + foo(INT#1, SINT#2, SINT#3); |
| 659 | + END_PROGRAM |
| 660 | + " |
| 661 | + .into(); |
| 662 | + let file3: SourceCode = r" |
| 663 | + PROGRAM prg2 |
| 664 | + foo(INT#1, SINT#2, SINT#3); |
| 665 | + foo(DINT#1, SINT#2, SINT#3); |
| 666 | + foo(INT#1, SINT#2, SINT#3); |
| 667 | + foo(INT#1, SINT#2, SINT#3); |
| 668 | + END_PROGRAM |
| 669 | + " |
| 670 | + .into(); |
| 671 | + // WHEN we compile |
| 672 | + let ir = compile_to_string( |
| 673 | + vec![file1, file2, file3], |
| 674 | + vec![], |
| 675 | + None, |
| 676 | + Diagnostician::default(), |
| 677 | + ) |
| 678 | + .unwrap(); |
| 679 | + |
| 680 | + // THEN we expect only 1 declaration per type-specific implementation of the generic function |
| 681 | + // although file2 & file3 both discovered them independently |
| 682 | + assert_snapshot!(ir); |
| 683 | +} |
0 commit comments