Skip to content

Commit 73d2470

Browse files
committed
Clamp syntax version to v1.13 minimum in get_project_syntax_version
Fix issue where packages with ancient compat declarations like `julia = "1"` would have their syntax version set to v1.0, causing the parser to reject newer syntax features like `import ... as`. Since syntax versioning was introduced in Julia 1.14, we now clamp the syntax version to at least v1.13 (NON_VERSIONED_SYNTAX) to match the logic in Base.loading.jl. This also fixes a bug in the original function where it referenced `syntax_table["julia_version"]` instead of `p.julia_syntax_version`. Fixes JuliaLang/julia#60273
1 parent 7a82e43 commit 73d2470

File tree

2 files changed

+116
-8
lines changed

2 files changed

+116
-8
lines changed

src/Operations.jl

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ import ...Pkg: usable_io, discover_repo, create_cachedir_tag, manifest_rel_path
2222
# Utils #
2323
#########
2424

25+
# Syntax versioning was first introduced in Julia 1.14, so we clamp the
26+
# syntax version to this minimum to avoid enforcing old syntax rules for
27+
# packages with ancient compat declarations (e.g., compat.julia = "1").
28+
# This mirrors Base.NON_VERSIONED_SYNTAX in loading.jl.
29+
const NON_VERSIONED_SYNTAX = v"1.13"
30+
2531
# Helper functions for yanked package checking
2632
function is_pkgversion_yanked(uuid::UUID, version::VersionNumber, registries::Vector{Registry.RegistryInstance} = Registry.reachable_registries())
2733
for reg in registries
@@ -386,29 +392,44 @@ this precedence order:
386392
2. If `compat.julia` is specified, use the minimum version from the compat range
387393
3. Otherwise, default to the current Julia VERSION
388394
395+
The result is clamped to at least `NON_VERSIONED_SYNTAX` (v1.13) since syntax
396+
versioning was introduced in Julia 1.14. This prevents packages with ancient
397+
compat declarations (e.g., `julia = "1"`) from having their syntax version set
398+
too low, which would cause the parser to reject newer syntax features.
399+
389400
This information is used to populate the `syntax.julia_version` field in the
390401
Manifest.toml, allowing Base's loading system to parse each package with the
391402
correct syntax version.
392403
"""
393404
function get_project_syntax_version(p::Project)::VersionNumber
405+
sv = nothing
406+
394407
# First check syntax.julia_version entry in Project.other
395408
if p.julia_syntax_version !== nothing
396-
return VersionNumber(syntax_table["julia_version"])
397-
end
398-
399-
# If not found, default to minimum(compat["julia"])
400-
if haskey(p.compat, "julia")
409+
sv = p.julia_syntax_version
410+
elseif haskey(p.compat, "julia")
411+
# If not found, default to minimum(compat["julia"])
401412
julia_compat = p.compat["julia"]
402413
# Get the minimum version from the first range
403414
if !isempty(julia_compat.val.ranges)
404415
first_range = first(julia_compat.val.ranges)
405416
lower_bound = first_range.lower
406-
return VersionNumber(lower_bound.t[1], lower_bound.t[2], lower_bound.t[3])
417+
sv = VersionNumber(lower_bound.t[1], lower_bound.t[2], lower_bound.t[3])
407418
end
408419
end
409420

410-
# Finally, if neither of those are set, default to the current Julia version
411-
return VERSION
421+
# If no version was found, default to the current Julia version
422+
if sv === nothing
423+
sv = VERSION
424+
end
425+
426+
# Clamp to at least NON_VERSIONED_SYNTAX since syntax versioning was
427+
# introduced in Julia 1.14
428+
if sv <= NON_VERSIONED_SYNTAX
429+
sv = NON_VERSIONED_SYNTAX
430+
end
431+
432+
return sv
412433
end
413434

414435
# This has to be done after the packages have been downloaded

test/manifests.jl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,4 +648,91 @@ end
648648
end
649649
end
650650

651+
@testset "Syntax version clamping" begin
652+
@testset "get_project_syntax_version clamps to NON_VERSIONED_SYNTAX" begin
653+
# Test that old compat declarations are clamped to v1.13
654+
# This prevents packages with ancient compat like "julia = 1" from
655+
# having their syntax version set too low (which would cause the parser
656+
# to reject newer syntax features like `import ... as`)
657+
658+
mktempdir() do test_dir
659+
# Test 1: compat.julia = "1" should be clamped to v1.13
660+
ancient_compat_dir = joinpath(test_dir, "ancient_compat")
661+
mkpath(ancient_compat_dir)
662+
write(joinpath(ancient_compat_dir, "Project.toml"), """
663+
name = "AncientCompat"
664+
uuid = "12345678-1234-1234-1234-123456789012"
665+
version = "0.1.0"
666+
667+
[compat]
668+
julia = "1"
669+
""")
670+
ancient_project = Pkg.Types.read_project(joinpath(ancient_compat_dir, "Project.toml"))
671+
syntax_version = Pkg.Operations.get_project_syntax_version(ancient_project)
672+
@test syntax_version == Pkg.Operations.NON_VERSIONED_SYNTAX
673+
@test syntax_version == v"1.13"
674+
675+
# Test 2: compat.julia = "1.6" should also be clamped to v1.13 (since 1.6 < 1.13)
676+
old_compat_dir = joinpath(test_dir, "old_compat")
677+
mkpath(old_compat_dir)
678+
write(joinpath(old_compat_dir, "Project.toml"), """
679+
name = "OldCompat"
680+
uuid = "12345678-1234-1234-1234-123456789013"
681+
version = "0.1.0"
682+
683+
[compat]
684+
julia = "1.6"
685+
""")
686+
old_project = Pkg.Types.read_project(joinpath(old_compat_dir, "Project.toml"))
687+
syntax_version = Pkg.Operations.get_project_syntax_version(old_project)
688+
@test syntax_version == v"1.13"
689+
690+
# Test 3: compat.julia = "1.14" should NOT be clamped (since 1.14 > 1.13)
691+
new_compat_dir = joinpath(test_dir, "new_compat")
692+
mkpath(new_compat_dir)
693+
write(joinpath(new_compat_dir, "Project.toml"), """
694+
name = "NewCompat"
695+
uuid = "12345678-1234-1234-1234-123456789014"
696+
version = "0.1.0"
697+
698+
[compat]
699+
julia = "1.14"
700+
""")
701+
new_project = Pkg.Types.read_project(joinpath(new_compat_dir, "Project.toml"))
702+
syntax_version = Pkg.Operations.get_project_syntax_version(new_project)
703+
@test syntax_version == v"1.14"
704+
705+
# Test 4: explicit syntax.julia_version = "1.0" should also be clamped
706+
explicit_old_dir = joinpath(test_dir, "explicit_old")
707+
mkpath(explicit_old_dir)
708+
write(joinpath(explicit_old_dir, "Project.toml"), """
709+
name = "ExplicitOld"
710+
uuid = "12345678-1234-1234-1234-123456789015"
711+
version = "0.1.0"
712+
713+
[syntax]
714+
julia_version = "1.0"
715+
""")
716+
explicit_old_project = Pkg.Types.read_project(joinpath(explicit_old_dir, "Project.toml"))
717+
syntax_version = Pkg.Operations.get_project_syntax_version(explicit_old_project)
718+
@test syntax_version == v"1.13"
719+
720+
# Test 5: explicit syntax.julia_version = "1.14" should NOT be clamped
721+
explicit_new_dir = joinpath(test_dir, "explicit_new")
722+
mkpath(explicit_new_dir)
723+
write(joinpath(explicit_new_dir, "Project.toml"), """
724+
name = "ExplicitNew"
725+
uuid = "12345678-1234-1234-1234-123456789016"
726+
version = "0.1.0"
727+
728+
[syntax]
729+
julia_version = "1.14"
730+
""")
731+
explicit_new_project = Pkg.Types.read_project(joinpath(explicit_new_dir, "Project.toml"))
732+
syntax_version = Pkg.Operations.get_project_syntax_version(explicit_new_project)
733+
@test syntax_version == v"1.14"
734+
end
735+
end
736+
end
737+
651738
end # module

0 commit comments

Comments
 (0)