Skip to content

Commit c116ef2

Browse files
authored
Merge pull request #4 from PyNumLab/codex/parser-public-coverage-extra
codex: add parser public coverage tests
2 parents e13a009 + 405a5b5 commit c116ef2

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

tests/parser/test_parser_public_api_coverage.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)