@@ -1333,3 +1333,166 @@ def test_directory_namespace_records_missing_and_parent_only_submodule_dependenc
13331333
13341334 assert project .dependencies ["child_mod" ] == {"parent_mod" , "missing_mod" }
13351335
1336+
1337+ def test_program_and_block_data_scope_errors_use_public_parse_paths ():
1338+ with pytest .raises (FortranParseError , match = "Unsupported OpenMP declarative directive in program 'driver'" ):
1339+ parse_fortran_file (
1340+ """
1341+ program driver
1342+ !$omp threadprivate(counter)
1343+ end program driver
1344+ """ ,
1345+ filename = "program_omp_decl.f90" ,
1346+ )
1347+
1348+ with pytest .raises (FortranParseError , match = "Unsupported OpenMP declarative directive in block data 'init_data'" ):
1349+ parse_fortran_file (
1350+ """
1351+ block data init_data
1352+ !$omp threadprivate(seed)
1353+ end block data init_data
1354+ """ ,
1355+ filename = "block_omp_decl.f90" ,
1356+ )
1357+
1358+ with pytest .raises (FortranParseError , match = "Unknown or unsupported datatype declaration in program 'driver'" ):
1359+ parse_fortran_file (
1360+ """
1361+ program driver
1362+ weirdtype state
1363+ end program driver
1364+ """ ,
1365+ filename = "program_unknown_decl.f90" ,
1366+ )
1367+
1368+ with pytest .raises (FortranParseError , match = "Unknown or unsupported datatype declaration in block data 'init_data'" ):
1369+ parse_fortran_file (
1370+ """
1371+ block data init_data
1372+ weirdtype seed
1373+ end block data init_data
1374+ """ ,
1375+ filename = "block_unknown_decl.f90" ,
1376+ )
1377+
1378+
1379+ def test_public_assess_wrap_readiness_alias_and_module_parameter_noise (tmp_path ):
1380+ parser = FortranParser ()
1381+ code = """
1382+ module noisy_params_mod
1383+ integer, parameter :: rk = 8, ignored_token
1384+ contains
1385+ subroutine scale(x)
1386+ real(kind=rk), intent(inout) :: x
1387+ end subroutine scale
1388+ end module noisy_params_mod
1389+ """
1390+
1391+ report = parser .assess_wrap_readiness (code , filename = "noisy_params.f90" )
1392+
1393+ assert report ["wrappable" ] is True
1394+
1395+ source_path = tmp_path / "listed_project.f90"
1396+ source_path .write_text (
1397+ """
1398+ module listed_project_mod
1399+ contains
1400+ subroutine work()
1401+ end subroutine work
1402+ end module listed_project_mod
1403+ """ ,
1404+ encoding = "utf-8" ,
1405+ )
1406+
1407+ project = parse_fortran_project ([source_path ])
1408+
1409+ assert "listed_project_mod" in project .modules
1410+ assert "listed_project_mod.work" in project .procedures
1411+
1412+
1413+ def test_project_resolution_keeps_relevant_local_parameter_variables ():
1414+ project = parse_fortran_project (
1415+ {
1416+ "local_params.f90" : """
1417+ subroutine use_relevant_local_param(e)
1418+ integer, parameter :: n = 8
1419+ integer, parameter :: one = 1.0d+0
1420+ real, intent(inout) :: e(1:+n-one)
1421+ end subroutine use_relevant_local_param
1422+ """
1423+ }
1424+ )
1425+
1426+ proc = project .procedures ["use_relevant_local_param" ]
1427+
1428+ assert proc .arguments [0 ].shape == ["1:+(8)-one" ]
1429+ assert proc .variables ["one" ].value == "1"
1430+
1431+
1432+ def test_project_resolution_uses_file_level_use_only_and_local_parameters (tmp_path ):
1433+ (tmp_path / "params.f90" ).write_text (
1434+ """
1435+ module public_params_mod
1436+ integer, parameter :: rk = selected_real_kind(12)
1437+ integer, parameter :: n = 4
1438+ end module public_params_mod
1439+ """ ,
1440+ encoding = "utf-8" ,
1441+ )
1442+ (tmp_path / "worker.f90" ).write_text (
1443+ """
1444+ subroutine file_level_worker(x, y)
1445+ use public_params_mod, only: rk, , n
1446+ integer, parameter :: local_rk = selected_real_kind(6)
1447+ real(kind=rk), intent(inout) :: x(1:n)
1448+ real(kind=local_rk), intent(out) :: y
1449+ end subroutine file_level_worker
1450+ """ ,
1451+ encoding = "utf-8" ,
1452+ )
1453+ project = parse_fortran_project (tmp_path )
1454+
1455+ proc = project .procedures ["file_level_worker" ]
1456+ args = {arg .name : arg for arg in proc .arguments }
1457+
1458+ assert args ["x" ].kind == "rk"
1459+ assert args ["x" ].shape == ["1:n" ]
1460+ assert args ["y" ].kind == "selected_real_kind(6)"
1461+ assert [mapping .local_name for mapping in proc .uses ["public_params_mod" ]] == ["rk" , "n" ]
1462+
1463+
1464+ def test_public_parse_paths_ignore_empty_declaration_entities ():
1465+ parsed = parse_fortran_file (
1466+ """
1467+ module empty_entity_mod
1468+ integer :: kept, , also_kept
1469+
1470+ type :: state
1471+ real :: x, , y
1472+ end type state
1473+ end module empty_entity_mod
1474+ """ ,
1475+ filename = "empty_entities.f90" ,
1476+ )
1477+
1478+ module = parsed .modules [0 ]
1479+
1480+ assert [var .name for var in module .variables ] == ["kept" , "also_kept" ]
1481+ assert [field .name for field in module .derived_types [0 ].fields ] == ["x" , "y" ]
1482+
1483+
1484+ def test_nested_interface_procedure_without_matching_dummy_stays_publicly_parseable ():
1485+ sig = parse_fortran_file (
1486+ """
1487+ subroutine caller()
1488+ interface
1489+ subroutine helper(x)
1490+ integer, intent(in) :: x
1491+ end subroutine helper
1492+ end interface
1493+ end subroutine caller
1494+ """
1495+ ).procedures [0 ]
1496+
1497+ assert sig .name == "caller"
1498+ assert sig .arguments == []
0 commit comments