From 26fc7593b7b6448ae34b557bb2dbcdae579a5dbb Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Nov 2025 19:26:03 +0000 Subject: [PATCH] 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 --- src/Operations.jl | 37 +++++++++++++++++++++++++++++-------- test/manifests.jl | 20 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/Operations.jl b/src/Operations.jl index 3cf9c77fe5..acc150f7fe 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -22,6 +22,12 @@ import ...Pkg: usable_io, discover_repo, create_cachedir_tag, manifest_rel_path # Utils # ######### +# Syntax versioning was first introduced in Julia 1.14, so we clamp the +# syntax version to this minimum to avoid enforcing old syntax rules for +# packages with ancient compat declarations (e.g., compat.julia = "1"). +# This mirrors Base.NON_VERSIONED_SYNTAX in loading.jl. +const NON_VERSIONED_SYNTAX = v"1.13" + # Helper functions for yanked package checking function is_pkgversion_yanked(uuid::UUID, version::VersionNumber, registries::Vector{Registry.RegistryInstance} = Registry.reachable_registries()) for reg in registries @@ -386,29 +392,44 @@ this precedence order: 2. If `compat.julia` is specified, use the minimum version from the compat range 3. Otherwise, default to the current Julia VERSION +The result is clamped to at least `NON_VERSIONED_SYNTAX` (v1.13) since syntax +versioning was introduced in Julia 1.14. This prevents packages with ancient +compat declarations (e.g., `julia = "1"`) from having their syntax version set +too low, which would cause the parser to reject newer syntax features. + This information is used to populate the `syntax.julia_version` field in the Manifest.toml, allowing Base's loading system to parse each package with the correct syntax version. """ function get_project_syntax_version(p::Project)::VersionNumber + sv = nothing + # First check syntax.julia_version entry in Project.other if p.julia_syntax_version !== nothing - return VersionNumber(syntax_table["julia_version"]) - end - - # If not found, default to minimum(compat["julia"]) - if haskey(p.compat, "julia") + sv = p.julia_syntax_version + elseif haskey(p.compat, "julia") + # If not found, default to minimum(compat["julia"]) julia_compat = p.compat["julia"] # Get the minimum version from the first range if !isempty(julia_compat.val.ranges) first_range = first(julia_compat.val.ranges) lower_bound = first_range.lower - return VersionNumber(lower_bound.t[1], lower_bound.t[2], lower_bound.t[3]) + sv = VersionNumber(lower_bound.t[1], lower_bound.t[2], lower_bound.t[3]) end end - # Finally, if neither of those are set, default to the current Julia version - return VERSION + # If no version was found, default to the current Julia version + if sv === nothing + sv = VERSION + end + + # Clamp to at least NON_VERSIONED_SYNTAX since syntax versioning was + # introduced in Julia 1.14 + if sv <= NON_VERSIONED_SYNTAX + sv = NON_VERSIONED_SYNTAX + end + + return sv end # This has to be done after the packages have been downloaded diff --git a/test/manifests.jl b/test/manifests.jl index a63afc3d12..407a35ae94 100644 --- a/test/manifests.jl +++ b/test/manifests.jl @@ -648,4 +648,24 @@ end end end +@testset "Syntax version clamping" begin + # Test that old compat/syntax declarations are clamped to v1.13 (NON_VERSIONED_SYNTAX) + test_cases = [ + ("[compat]\njulia = \"1\"", v"1.13"), # ancient compat clamped + ("[compat]\njulia = \"1.6\"", v"1.13"), # old compat clamped + ("[compat]\njulia = \"1.14\"", v"1.14"), # new compat not clamped + ("[syntax]\njulia_version = \"1.0\"", v"1.13"), # explicit old clamped + ("[syntax]\njulia_version = \"1.14\"", v"1.14"), # explicit new not clamped + ] + mktempdir() do test_dir + for (i, (section, expected)) in enumerate(test_cases) + pkg_dir = joinpath(test_dir, "Pkg$i") + mkpath(pkg_dir) + write(joinpath(pkg_dir, "Project.toml"), "name = \"Pkg$i\"\nuuid = \"12345678-1234-1234-1234-12345678901$i\"\n$section\n") + project = Pkg.Types.read_project(joinpath(pkg_dir, "Project.toml")) + @test Pkg.Operations.get_project_syntax_version(project) == expected + end + end +end + end # module