@@ -482,3 +482,136 @@ fn test_program_use_statement_parsing() { |
| 482 | 482 | assert_eq!(program.uses[1].module_name, "ANOTHER_MODULE"); |
| 483 | 483 | assert!(program.uses[1].only.is_some()); |
| 484 | 484 | } |
| 485 | + |
| 486 | +#[test] |
| 487 | +fn test_use_only_rejects_private_symbols() { |
| 488 | + // Attempting to import a PRIVATE symbol via USE...ONLY should fail |
| 489 | + let source = r#" |
| 490 | + MODULE my_module |
| 491 | + PRIVATE |
| 492 | + INTEGER :: secret_var = 42 |
| 493 | + PUBLIC :: public_var |
| 494 | + INTEGER :: public_var = 100 |
| 495 | + END MODULE my_module |
| 496 | + |
| 497 | + PROGRAM test_private |
| 498 | + USE my_module, ONLY: secret_var |
| 499 | + IMPLICIT NONE |
| 500 | + PRINT *, secret_var |
| 501 | + END PROGRAM |
| 502 | + "#; |
| 503 | + |
| 504 | + let mut lexer = Lexer::new(source); |
| 505 | + let tokens = lexer.tokenize().expect("Should tokenize"); |
| 506 | + let mut parser = Parser::new(tokens); |
| 507 | + let unit = parser.parse_compilation_unit().expect("Should parse"); |
| 508 | + |
| 509 | + let mut compiler = Compiler::new(); |
| 510 | + let result = compiler.compile_unit(&unit); |
| 511 | + |
| 512 | + // Should fail because secret_var is PRIVATE |
| 513 | + assert!(result.is_err(), "Should reject importing PRIVATE symbol"); |
| 514 | + let err_msg = format!("{:?}", result.unwrap_err()); |
| 515 | + assert!( |
| 516 | + err_msg.contains("PRIVATE") || err_msg.contains("secret_var"), |
| 517 | + "Error should mention PRIVATE visibility, got: {}", err_msg |
| 518 | + ); |
| 519 | +} |
| 520 | + |
| 521 | +#[test] |
| 522 | +fn test_use_only_allows_public_symbols() { |
| 523 | + // Importing a PUBLIC symbol via USE...ONLY should succeed |
| 524 | + let source = r#" |
| 525 | + MODULE my_module |
| 526 | + PRIVATE |
| 527 | + INTEGER :: secret_var = 42 |
| 528 | + PUBLIC :: public_var |
| 529 | + INTEGER :: public_var = 100 |
| 530 | + END MODULE my_module |
| 531 | + |
| 532 | + PROGRAM test_public |
| 533 | + USE my_module, ONLY: public_var |
| 534 | + IMPLICIT NONE |
| 535 | + PRINT *, public_var |
| 536 | + END PROGRAM |
| 537 | + "#; |
| 538 | + |
| 539 | + let vm = compile_and_run_unit(source).expect("Should compile and run"); |
| 540 | + let output = vm.output().join("\n"); |
| 541 | + assert!(output.contains("100"), "Expected public_var value 100, got: {}", output); |
| 542 | +} |
| 543 | + |
| 544 | +#[test] |
| 545 | +fn test_parse_type_with_component_visibility() { |
| 546 | + // Test that component visibility attributes are parsed correctly |
| 547 | + let source = r#" |
| 548 | + MODULE mymod |
| 549 | + IMPLICIT NONE |
| 550 | + TYPE :: MyType |
| 551 | + INTEGER, PUBLIC :: x |
| 552 | + INTEGER, PRIVATE :: y |
| 553 | + REAL :: z |
| 554 | + END TYPE MyType |
| 555 | + END MODULE mymod |
| 556 | + "#; |
| 557 | + |
| 558 | + let module = parse_module(source).expect("Should parse successfully"); |
| 559 | + assert_eq!(module.name, "MYMOD"); |
| 560 | + |
| 561 | + // Find the type definition in the declarations |
| 562 | + let type_decl = module.declarations.iter().find(|d| { |
| 563 | + matches!(d, Declaration::DerivedType(t) if t.name == "MYTYPE") |
| 564 | + }); |
| 565 | + assert!(type_decl.is_some(), "Should have a DerivedType declaration"); |
| 566 | + |
| 567 | + if let Some(Declaration::DerivedType(type_def)) = type_decl { |
| 568 | + assert_eq!(type_def.components.len(), 3); |
| 569 | + |
| 570 | + // Check component x has PUBLIC visibility |
| 571 | + let comp_x = type_def.components.iter().find(|c| c.name == "X"); |
| 572 | + assert!(comp_x.is_some()); |
| 573 | + assert_eq!(comp_x.unwrap().visibility, Some(Visibility::Public)); |
| 574 | + |
| 575 | + // Check component y has PRIVATE visibility |
| 576 | + let comp_y = type_def.components.iter().find(|c| c.name == "Y"); |
| 577 | + assert!(comp_y.is_some()); |
| 578 | + assert_eq!(comp_y.unwrap().visibility, Some(Visibility::Private)); |
| 579 | + |
| 580 | + // Check component z has no explicit visibility (None) |
| 581 | + let comp_z = type_def.components.iter().find(|c| c.name == "Z"); |
| 582 | + assert!(comp_z.is_some()); |
| 583 | + assert_eq!(comp_z.unwrap().visibility, None); |
| 584 | + } |
| 585 | +} |
| 586 | + |
| 587 | +#[test] |
| 588 | +fn test_type_component_visibility_in_runtime() { |
| 589 | + // Test that component visibility is tracked in the runtime type definition |
| 590 | + let source = r#" |
| 591 | + MODULE test_mod |
| 592 | + IMPLICIT NONE |
| 593 | + TYPE :: Point |
| 594 | + REAL, PUBLIC :: x |
| 595 | + REAL, PRIVATE :: internal_val |
| 596 | + END TYPE Point |
| 597 | + CONTAINS |
| 598 | + FUNCTION create_point() RESULT(p) |
| 599 | + TYPE(Point) :: p |
| 600 | + p%x = 10.0 |
| 601 | + p%internal_val = 5.0 |
| 602 | + END FUNCTION |
| 603 | + END MODULE test_mod |
| 604 | + |
| 605 | + PROGRAM test_visibility |
| 606 | + USE test_mod |
| 607 | + IMPLICIT NONE |
| 608 | + TYPE(Point) :: pt |
| 609 | + pt = create_point() |
| 610 | + PRINT *, pt%x |
| 611 | + END PROGRAM |
| 612 | + "#; |
| 613 | + |
| 614 | + let vm = compile_and_run_unit(source).expect("Should compile and run"); |
| 615 | + let output = vm.output().join("\n"); |
| 616 | + assert!(output.contains("10"), "Expected pt%x = 10, got: {}", output); |
| 617 | +} |