From ec6c7edf8d6fccef0af75d71ff8cbe679bce06b1 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Sun, 31 May 2026 21:42:23 +0200
Subject: [PATCH 01/14] unpin Zig version
---
.github/workflows/artifacts.yml | 2 +-
.github/workflows/coverage.yml | 2 +-
.github/workflows/main.yml | 2 +-
README.md | 2 +-
build.zig | 13 -------------
5 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml
index 3c93d190de..af2773a082 100644
--- a/.github/workflows/artifacts.yml
+++ b/.github/workflows/artifacts.yml
@@ -39,7 +39,7 @@ jobs:
- uses: mlugg/setup-zig@v2
if: env.SKIP_DEPLOY != 'true'
with:
- version: "0.17.0-dev.387+31f157d80" # https://github.com/zigtools/zls/issues/3208
+ version: master
- name: Install APT packages
if: env.SKIP_DEPLOY != 'true'
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 69bb16a5a4..433d25f3c3 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -16,7 +16,7 @@ jobs:
- uses: mlugg/setup-zig@v2
with:
- version: "0.17.0-dev.387+31f157d80" # https://github.com/zigtools/zls/issues/3208
+ version: master
- name: Install kcov
run: |
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2d1a556e96..6407ff3647 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -20,7 +20,7 @@ jobs:
- uses: mlugg/setup-zig@v2
with:
- version: "0.17.0-dev.387+31f157d80" # https://github.com/zigtools/zls/issues/3208
+ version: master
- name: Run zig fmt
if: matrix.os == 'ubuntu-22.04'
diff --git a/README.md b/README.md
index 03a90dbb9f..9dd2c5a86f 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ The complete installation guide is available on the [Zigtools website](https://z
### Build From Source
-The default branch of ZLS targets `0.17.0-dev.387+31f157d80` until [#3208](https://github.com/zigtools/zls/issues/3208) has been resolved.
+The default branch of ZLS targets [Zig master](https://ziglang.org/download/).
```bash
git clone https://github.com/zigtools/zls
diff --git a/build.zig b/build.zig
index 7b2dac45f7..278448caeb 100644
--- a/build.zig
+++ b/build.zig
@@ -601,19 +601,6 @@ fn release(b: *Build, release_artifacts: []const *Build.Step.Compile, released_z
const Build = blk: {
@setEvalBranchQuota(10_000);
- {
- const version = std.SemanticVersion.parse("0.17.0-dev.601+0ff175b69") catch unreachable;
- if (builtin.zig_version.order(version) != .lt) {
- const message = std.fmt.comptimePrint(
- \\The used Zig version ({s}) is not yet supported by ZLS.
- \\
- \\For more information please visit the following issue:
- \\> https://github.com/zigtools/zls/issues/3208
- , .{builtin.zig_version_string});
- @compileError(message);
- }
- }
-
const min_build_zig = std.SemanticVersion.parse(minimum_build_zig_version) catch unreachable;
const min_runtime_zig = std.SemanticVersion.parse(minimum_runtime_zig_version) catch unreachable;
From 3df5a93f7fdd744a2b742d1c56ec1558916c4a90 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 22:52:02 +0200
Subject: [PATCH 02/14] set minimum zig version to `0.17.0-dev.1936+5a625d5f3`
---
build.zig | 7 ++-----
build.zig.zon | 2 +-
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/build.zig b/build.zig
index 278448caeb..b3f0a6f38f 100644
--- a/build.zig
+++ b/build.zig
@@ -5,11 +5,8 @@ const zls_version = std.SemanticVersion.parse(@import("build.zig.zon").version)
const minimum_build_zig_version = @import("build.zig.zon").minimum_zig_version;
-/// Specify the minimum Zig version that is usable with ZLS:
-/// `std.builtin` -> `std.lang` migration progress
-///
-/// A breaking change to the Zig Build System should be handled by updating ZLS's build runner (see src\build_runner)
-const minimum_runtime_zig_version = "0.17.0-dev.274+7eb79daff";
+/// Specifies the minimum Zig version that is runtime compatible with ZLS
+const minimum_runtime_zig_version = "0.17.0-dev.1936+5a625d5f3";
const release_targets = [_]std.Target.Query{
.{ .cpu_arch = .aarch64, .os_tag = .linux },
diff --git a/build.zig.zon b/build.zig.zon
index 16c470d2e1..e1f292a0d3 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -10,7 +10,7 @@
// nix flake update --commit-lock-file
// ```
// If you do not use Nix, a ZLS maintainer can take care of this.
- .minimum_zig_version = "0.17.0-dev.292+fc1c83a36",
+ .minimum_zig_version = "0.17.0-dev.1936+5a625d5f3",
// Must be kept in sync with the `deps.nix` for the Nix flake.
// If you do not use Nix, a ZLS maintainer can take care of this.
.dependencies = .{
From 1f3839fc2624ccc724e7b1854af5e7d36795ba1e Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 22:52:41 +0200
Subject: [PATCH 03/14] flake.lock: Update
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Flake lock file updates:
• Updated input 'nixpkgs':
'github:NixOS/nixpkgs/8fd9daa3db09ced9700431c5b7ad0e8ba199b575?narHash=sha256-Ti%2BZBvW6yrWWAg2szExVTwCd4qOJ3KlVr1tFHfyfi8Q%3D' (2026-05-10)
→ 'github:NixOS/nixpkgs/b6018f87da91d19d0ab4cf979885689b469cdd41?narHash=sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI%3D' (2026-06-30)
• Updated input 'zig-flake':
'github:silversquirl/zig-flake/824cb996143ba0f42653b5312a1d3cb8822c3947?narHash=sha256-zm8q9v7KmBjtMTP1x6kk2Fh3XrAySDnZl3dMZl0Jqkc%3D' (2026-05-11)
→ 'github:silversquirl/zig-flake/61642c0d25af42802ba19ea9716dfc161778564e?narHash=sha256-QJHb%2BckgPtKE8Up3imHRJKdnM2YJiDLcDkmJhTwFmsI%3D' (2026-09-03)
---
flake.lock | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/flake.lock b/flake.lock
index d6c3dc5111..8df5d630fe 100644
--- a/flake.lock
+++ b/flake.lock
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
- "lastModified": 1778430510,
- "narHash": "sha256-Ti+ZBvW6yrWWAg2szExVTwCd4qOJ3KlVr1tFHfyfi8Q=",
+ "lastModified": 1782847189,
+ "narHash": "sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "8fd9daa3db09ced9700431c5b7ad0e8ba199b575",
+ "rev": "b6018f87da91d19d0ab4cf979885689b469cdd41",
"type": "github"
},
"original": {
@@ -29,11 +29,11 @@
]
},
"locked": {
- "lastModified": 1778524253,
- "narHash": "sha256-zm8q9v7KmBjtMTP1x6kk2Fh3XrAySDnZl3dMZl0Jqkc=",
+ "lastModified": 1788458932,
+ "narHash": "sha256-QJHb+ckgPtKE8Up3imHRJKdnM2YJiDLcDkmJhTwFmsI=",
"owner": "silversquirl",
"repo": "zig-flake",
- "rev": "824cb996143ba0f42653b5312a1d3cb8822c3947",
+ "rev": "61642c0d25af42802ba19ea9716dfc161778564e",
"type": "github"
},
"original": {
From bc7774bb8ee6d018331b8ef856add431912469fe Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Sat, 8 Aug 2026 00:29:44 +0200
Subject: [PATCH 04/14] update dependencies
---
build.zig.zon | 12 ++++++------
deps.nix | 18 +++++++++---------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/build.zig.zon b/build.zig.zon
index e1f292a0d3..123567fe74 100644
--- a/build.zig.zon
+++ b/build.zig.zon
@@ -15,16 +15,16 @@
// If you do not use Nix, a ZLS maintainer can take care of this.
.dependencies = .{
.known_folders = .{
- .url = "https://github.com/ziglibs/known-folders/archive/207c34a16e4365edc20d92c7892f962b3bed46e8.tar.gz",
- .hash = "known_folders-0.0.0-Fy-PJsbKAACbDh9bBxR0MMThxZSS6A9RH4apWphNHY70",
+ .url = "https://github.com/ziglibs/known-folders/archive/6da0d0c41b78b9ed2d34fa364fcb81b5ebece6c4.tar.gz",
+ .hash = "known_folders-0.0.0-Fy-PJiDLAAB98m3uYUzatrTb2mO2fpvwx2zpSroEtfbO",
},
.diffz = .{
- .url = "https://github.com/ziglibs/diffz/archive/d080c1eb782fff15068cabb3b82da85ce6054b74.tar.gz",
- .hash = "diffz-0.0.1-G2tlIfLNAQCc06RFk0tFGj2M-X-id4WHFkMVw2JoMILR",
+ .url = "https://github.com/ziglibs/diffz/archive/aac8aa99c436ab8277b0711922aad062c0167b12.tar.gz",
+ .hash = "diffz-0.0.1-G2tlISvOAQDORzPTSxDgiKwlHuADKeJMdJrw4kRfLufj",
},
.lsp_kit = .{
- .url = "https://github.com/zigtools/lsp-kit/archive/b886a2b0d5cee85ecbcc3089b863f7517cc9ff7f.tar.gz",
- .hash = "lsp_kit-0.1.0-bi_PL3IyDACfp1xdTnkiOHEok2YpPCCCJHuuOcNzjl1D",
+ .url = "https://github.com/zigtools/lsp-kit/archive/d148676ace1eaecf3c703ce7d350d0e94e502ed7.tar.gz",
+ .hash = "lsp_kit-0.1.0-bi_PLwc2DACoR7VTz_qKFXUvtx7veHvWcTQvP9pDFLpk",
},
.tracy = .{
.url = "https://github.com/wolfpld/tracy/archive/refs/tags/v0.13.1.tar.gz",
diff --git a/deps.nix b/deps.nix
index b9215ded0a..acdd63f23a 100644
--- a/deps.nix
+++ b/deps.nix
@@ -5,24 +5,24 @@
}:
linkFarm "zig-packages" [
{
- name = "known_folders-0.0.0-Fy-PJsbKAACbDh9bBxR0MMThxZSS6A9RH4apWphNHY70";
+ name = "known_folders-0.0.0-Fy-PJiDLAAB98m3uYUzatrTb2mO2fpvwx2zpSroEtfbO";
path = fetchzip {
- url = "https://github.com/ziglibs/known-folders/archive/207c34a16e4365edc20d92c7892f962b3bed46e8.tar.gz";
- hash = "sha256-9hMnEc3ktnFTZT28hjaOkafjSUXEI+SNIO9GMcJrBfA=";
+ url = "https://github.com/ziglibs/known-folders/archive/6da0d0c41b78b9ed2d34fa364fcb81b5ebece6c4.tar.gz";
+ hash = "sha256-F17Ivvsyyla2CBIh+kImc1O3AtD2yTDAGamzMVbVzuw=";
};
}
{
- name = "diffz-0.0.1-G2tlIfLNAQCc06RFk0tFGj2M-X-id4WHFkMVw2JoMILR";
+ name = "diffz-0.0.1-G2tlISvOAQDORzPTSxDgiKwlHuADKeJMdJrw4kRfLufj";
path = fetchzip {
- url = "https://github.com/ziglibs/diffz/archive/d080c1eb782fff15068cabb3b82da85ce6054b74.tar.gz";
- hash = "sha256-a9O9Wt8QkdMnSznWiowRoMlQm1JCFuxLwZR3SDHsGHs=";
+ url = "https://github.com/ziglibs/diffz/archive/aac8aa99c436ab8277b0711922aad062c0167b12.tar.gz";
+ hash = "sha256-GQ4iCZSSpVvdDQteoi02keLSqSoDOSQPH8eihum++Z0=";
};
}
{
- name = "lsp_kit-0.1.0-bi_PL3IyDACfp1xdTnkiOHEok2YpPCCCJHuuOcNzjl1D";
+ name = "lsp_kit-0.1.0-bi_PLwc2DACoR7VTz_qKFXUvtx7veHvWcTQvP9pDFLpk";
path = fetchzip {
- url = "https://github.com/zigtools/lsp-kit/archive/b886a2b0d5cee85ecbcc3089b863f7517cc9ff7f.tar.gz";
- hash = "sha256-367wPydvnpl9RYlTrXwk4bZ/ui9DbYjeY/VDYs7ZJRs=";
+ url = "https://github.com/zigtools/lsp-kit/archive/d148676ace1eaecf3c703ce7d350d0e94e502ed7.tar.gz";
+ hash = "sha256-O4LSGT/0/2gFbXcOt0Panj4a26J5gA1NkmO0+7PfkJg=";
};
}
]
From 9d5250bee812ccab4ed2189395d7565f9c03c31d Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 22:15:52 +0200
Subject: [PATCH 05/14] update langref.html.in
---
src/tools/langref.html.in | 892 +++++++++-----------------------------
1 file changed, 214 insertions(+), 678 deletions(-)
diff --git a/src/tools/langref.html.in b/src/tools/langref.html.in
index 17f3be04a0..37a7ac4c3b 100644
--- a/src/tools/langref.html.in
+++ b/src/tools/langref.html.in
@@ -315,7 +315,7 @@
0.9.1 |
0.10.1 |
0.11.0 |
- 0.12.0 |
+ 0.12.1 |
0.13.0 |
0.14.1 |
0.15.2 |
@@ -814,7 +814,7 @@
A destructuring expression may only appear within a block (i.e. not at {#link|Namespace#} scope).
The left hand side of the assignment must consist of a comma separated list,
- each element of which may be either an lvalue (for instance, an existing `var`) or a variable declaration:
+ each element of which may be either an lvalue (for instance, an existing {#syntax#}var{#endsyntax#}) or a variable declaration:
{#code|destructuring_mixed.zig#}
@@ -1119,6 +1119,10 @@
otherwise the optimizer figures out all the values at compile-time,
which operates in strict mode.
{#code|float_mode_exe.zig#}
+ {#shell_samp#}$ zig build-exe float_mode_exe.zig float_mode_obj.o -O fast
+$ ./float_mode_exe
+optimized = 0.001
+strict = 0.0009765625{#end_shell_samp#}
{#see_also|@setFloatMode|Division by Zero#}
{#header_close#}
@@ -1370,7 +1374,8 @@ a /= b{#endsyntax#}
Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.optimized Mode|Floating Point Operations#}.
Signed integer operands must be comptime-known and positive. In other cases, use
{#link|@divTrunc#},
- {#link|@divFloor#}, or
+ {#link|@divFloor#},
+ {#link|@divCeil#}, or
{#link|@divExact#} instead.
Invokes {#link|Peer Type Resolution#} for the operands.
@@ -1917,7 +1922,7 @@ or
Vectors generally support the same builtin operators as their underlying base types.
- The only exception to this is the keywords `and` and `or` on vectors of bools, since
+ The only exception to this is the keywords {#syntax#}and{#endsyntax#} and {#syntax#}or{#endsyntax#} on vectors of bools, since
these operators affect control flow, which is not allowed for vectors.
All other operations are performed element-wise, and return a vector of the same length
as the input vectors. This includes:
@@ -1955,7 +1960,7 @@ or
TODO talk about C ABI interop
TODO consider suggesting std.MultiArrayList
- {#see_also|@splat|@shuffle|@select|@reduce#}
+ {#see_also|@splat|@shuffle|@select|@reduce|Slicing by Length#}
{#header_open|Relationship with Arrays#}
Vectors and {#link|Arrays#} each have a well-defined bit layout
@@ -2150,6 +2155,24 @@ or
{#see_also|Pointers|for|Arrays#}
+ {#header_open|Slicing by Length#}
+
Even though Zig only has syntax for slicing based on start and end indices, by slicing twice,
+ one can express a slice by length operation.
+ The pattern {#syntax#}[a .. a + b]{#endsyntax#} is always better expressed
+ {#syntax#}[a..][0..b]{#endsyntax#} because:
+
+ - Slices are represented in memory as a pointer and length. Despite
+ syntactically appearing as twice the work, it is actually one less
+ subtraction in machine code.
+ - If {#syntax#}a{#endsyntax#} is known at runtime and
+ {#syntax#}b{#endsyntax#} is known at {#link|comptime#}, the former
+ results in a slice but the latter results in a single-item
+ {#link|pointer|Pointers#} to an {#link|array|Arrays#}, a generally more
+ safe type because the length is compile-time known.
+
+ {#code|slicing_by_length.zig#}
+ {#header_close#}
+
{#header_open|Sentinel-Terminated Slices#}
The syntax {#syntax#}[:x]T{#endsyntax#} is a slice which has a runtime-known length
@@ -2252,6 +2275,7 @@ or
This even works at {#link|comptime#}:
{#code|test_packed_structs.zig#}
+
The backing integer can be inferred or explicitly provided. When
inferred, it will be unsigned. When explicitly provided, its bit width
@@ -2260,6 +2284,12 @@ or
{#code|test_missized_packed_struct.zig#}
+
+ A {#syntax#}packed struct{#endsyntax#} can be converted to and from its backing
+ integer using {#link|@backingInt#} and {#link|@fromBackingInt#}:
+
+ {#code|test_packed_struct_backing_int.zig#}
+
Zig allows the address to be taken of a non-byte-aligned field:
@@ -2382,7 +2412,7 @@ or
{#header_open|enum#}
{#code|test_enums.zig#}
- {#see_also|@typeInfo|@tagName|@sizeOf#}
+ {#see_also|@backingInt|@fromBackingInt|@typeInfo|@tagName|@sizeOf|noreturn#}
{#header_open|extern enum#}
@@ -2412,9 +2442,7 @@ or
The enum must specify a tag type and cannot consume every enumeration value.
- {#link|@enumFromInt#} on a non-exhaustive enum involves the safety semantics
- of {#link|@intCast#} to the integer tag type, but beyond that always results in
- a well-defined enum value.
+ {#link|@fromBackingInt#} on a non-exhaustive enum always results in a valid enum value.
A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.
@@ -2442,7 +2470,8 @@ or
{#code|test_simple_union.zig#}
- In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
+ In order to use {#link|switch#} with a union, it must be a {#link|tagged union|Tagged union#}
+ or a {#link|packed union#}.
To initialize a union when the tag is a {#link|comptime#}-known name, see {#link|@unionInit#}.
@@ -2471,7 +2500,7 @@ or
Unions with inferred enum tag types can also assign ordinal values to their inferred tag.
This requires the tag to specify an explicit integer type.
- {#link|@intFromEnum#} can be used to access the ordinal value corresponding to the active field.
+ {#link|@backingInt#} can be used to access the ordinal value corresponding to the active field.
{#code|test_tagged_union_with_tag_values.zig#}
@@ -2502,6 +2531,7 @@ or
{#code|test_packed_union_equality.zig#}
+ {#see_also|@backingInt|@fromBackingInt#}
{#header_close#}
{#header_open|Anonymous Union Literals#}
@@ -2904,8 +2934,8 @@ or
{#header_open|inline fn#}
Adding the {#syntax#}inline{#endsyntax#} keyword to a function definition makes that
- function become semantically inlined at the callsite. This is
- not a hint to be possibly observed by optimization passes, but has
+ function become semantically inlined at the callsite. {#link|Inline Is Not A Hint#}
+ to be possibly observed by optimization passes, but has
implications on the types and values involved in the function call.
@@ -2917,16 +2947,22 @@ or
If {#syntax#}inline{#endsyntax#} is removed, the test fails with the compile error
instead of passing.
- It is generally better to let the compiler decide when to inline a
- function, except for these scenarios:
+
+ {#header_open|Inline Is Not A Hint#}
+ Use of {#syntax#}inline{#endsyntax#} restricts what the
+ compiler is allowed to do. This can harm binary size, compilation speed,
+ and even runtime performance. In exchange, these use cases are addressed:
- - To change how many stack frames are in the call stack, for debugging purposes.
- - To force comptime-ness of the arguments to propagate to the return value of the function, as in the above example.
- - Real world performance measurements demand it.
+ - Ability to ensure certain stack frames are in the call stack, for
+ debugging purposes, or for integrating with {#link|Assembly#}.
+ - Forcing arguments from the callsite that happen to be
+ {#link|comptime#} to propagate to the return value of the function, as
+ in the above example.
+ - Working around limitations of compiler backend implementations.
- Note that {#syntax#}inline{#endsyntax#} actually restricts
- what the compiler is allowed to do. This can harm binary size,
- compilation speed, and even runtime performance.
+ For an alternative to {#syntax#}inline{#endsyntax#} that merely hints to the compiler
+ that inlining a function may be valuable, use {#link|@branchHint#}.
+ {#header_close#}
{#header_close#}
{#header_open|Function Reflection#}
@@ -3567,14 +3603,14 @@ void do_a_thing(struct Foo *foo) {
- {#link|@bitCast#} - change type but maintain bit representation
- {#link|@alignCast#} - make a pointer have more alignment
- - {#link|@enumFromInt#} - obtain an enum value based on its integer tag value
+ - {#link|@fromBackingInt#} - obtain an enum or a packed struct/union value based on its backing integer
- {#link|@errorFromInt#} - obtain an error code based on its integer value
- {#link|@errorCast#} - convert to a smaller error set
- {#link|@floatCast#} - convert a larger float to a smaller float
- {#link|@floatFromInt#} - convert an integer to a float value
- {#link|@intCast#} - convert between integer types
- {#link|@intFromBool#} - convert true to 1 and false to 0
- - {#link|@intFromEnum#} - obtain the integer tag value of an enum or tagged union
+ - {#link|@backingInt#} - obtain the backing integer value of an enum or a packed struct/union
- {#link|@intFromError#} - obtain the integer value of an error code
- {#link|@round#}, {#link|@floor#}, {#link|@ceil#}, {#link|@trunc#} - float to integer conversion
- {#link|@intFromPtr#} - obtain the address of a pointer
@@ -4413,6 +4449,18 @@ comptime {
{#see_also|@atomicLoad|@atomicRmw|@cmpxchgWeak|@cmpxchgStrong#}
{#header_close#}
+ {#header_open|@backingInt#}
+
{#syntax#}@backingInt(enum_or_bitpack: T) BackingInt(T){#endsyntax#}
+
+ Converts an {#link|enum#}, a {#link|packed struct#} or a {#link|packed union#} value
+ to its backing integer.
+
+
+ Also works with {#link|tagged unions|Tagged union#}, acting on the active enum tag value.
+
+ {#see_also|@fromBackingInt|@bitCast#}
+ {#header_close#}
+
{#header_open|@bitCast#}
{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}
@@ -4433,8 +4481,18 @@ comptime {
Convert {#syntax#}i32{#endsyntax#} to {#syntax#}u32{#endsyntax#} preserving twos complement
- Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time. It's a compile error to bitcast a value of undefined layout; this means that, besides the restriction from types which possess dedicated casting builtins (enums, pointers, error sets), bare structs, error unions, slices, optionals, and any other type without a well-defined memory layout, also cannot be used in this operation.
+ Works at compile-time if {#syntax#}value{#endsyntax#} is known at compile time.
+ It's a compile error to bitcast a value of undefined layout; this means that,
+ besides the restriction from types which possess dedicated casting builtins
+ (pointers, error sets), bare structs, error unions, slices, optionals, and any
+ other type without a well-defined memory layout, also cannot be used in this
+ operation.
+
+
+ Attempting to convert an integer with no corresponding tag value to an
+ {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
+ {#see_also|@ptrCast|@intFromPtr|@ptrFromInt|@errorCast|@intFromError|@errorFromInt|@backingInt|@fromBackingInt#}
{#header_close#}
{#header_open|@bitOffsetOf#}
@@ -4717,7 +4775,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@divExact(a, b) * b == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.
- {#see_also|@divTrunc|@divFloor#}
+ {#see_also|@divTrunc|@divFloor|@divCeil#}
{#header_close#}
{#header_open|@divFloor#}
{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}
@@ -4731,7 +4789,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.
- {#see_also|@divTrunc|@divExact#}
+ {#see_also|@divTrunc|@divCeil|@divExact#}
{#header_close#}
{#header_open|@divTrunc#}
{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}
@@ -4745,7 +4803,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}
For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.
- {#see_also|@divFloor|@divExact#}
+ {#see_also|@divFloor|@divCeil|@divExact#}
+ {#header_close#}
+ {#header_open|@divCeil#}
+ {#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}
+
+ Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and
+ {#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}.
+
+
+ - {#syntax#}@divCeil(5, 3) == 2{#endsyntax#}
+ - {#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}
+
+ For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.
+ {#see_also|@divFloor|@divTrunc|@divExact#}
{#header_close#}
{#header_open|@embedFile#}
@@ -4766,6 +4837,9 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_open|@enumFromInt#}
{#syntax#}@enumFromInt(integer: anytype) anytype{#endsyntax#}
+ Deprecated. Use {#link|@fromBackingInt#} or {#link|@bitCast#} instead.
+
+
Converts an integer into an {#link|enum#} value. The return type is the inferred result type.
@@ -4921,11 +4995,41 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_close#}
+ {#header_open|@fromBackingInt#}
+ {#syntax#}@fromBackingInt(backing_int: BackingInt(T)) T{#endsyntax#}
+
+ Converts an integer into a {#link|enum#}, a {#link|packed struct#} or a
+ {#link|packed union#} value. The return type is the inferred result type.
+
+
+ Attempting to convert an integer with no corresponding tag value to an
+ {#syntax#}enum{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}.
+ Note that a {#link|non-exhaustive enum|Non-exhaustive enum#} has corresponding values for
+ all integers in the enum's integer tag type: the {#syntax#}_{#endsyntax#} value represents
+ all the remaining unnamed integers in the enum's tag type.
+
+ {#see_also|@backingInt|@bitCast#}
+ {#header_close#}
+
{#header_open|@hasDecl#}
{#syntax#}@hasDecl(comptime Namespace: type, comptime name: []const u8) bool{#endsyntax#}
- Returns whether or not a {#link|Namespace#} has a declaration matching {#syntax#}name{#endsyntax#}.
+ Returns whether or not a {#link|Namespace#} has a public declaration matching {#syntax#}name{#endsyntax#}.
{#code|test_hasDecl_builtin.zig#}
+ Caution: using {#syntax#}@hasDecl{#endsyntax#} to implement
+ conditional compilation based on the presence or absence of declarations
+ in an API runs the risk of introducing dead code into the codebase,
+ unnoticed. For example, if the field is ever renamed, code which uses
+ {#syntax#}@hasDecl{#endsyntax#} will compile successfully while failing
+ to enable or disable an intended block of code. Even a simple typo in
+ the field name passed to {#syntax#}@hasDecl{#endsyntax#} would go
+ unnoticed by the compiler. Thus, it is safer to use alternative methods
+ to check for the presence or absence of a particular declaration where
+ possible. For example, if the target operating system would indicate whether a particular
+ function is available, it is more maintainable to check
+ {#syntax#}@import("builtin").target.os.tag{#endsyntax#} rather than
+ {#syntax#}@hasDecl{#endsyntax#}.
+
{#see_also|@hasField#}
{#header_close#}
@@ -5003,12 +5107,11 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_open|@intFromEnum#}
{#syntax#}@intFromEnum(enum_or_tagged_union: anytype) anytype{#endsyntax#}
- Converts an enumeration value into its integer tag type. When a tagged union is passed,
- the tag value is used as the enumeration value.
+ Deprecated. Use {#link|@backingInt#} or {#link|@bitCast#} instead.
- If there is only one possible enum value, the result is a {#syntax#}comptime_int{#endsyntax#}
- known at {#link|comptime#}.
+ Converts an enumeration value into its integer tag type. When a tagged union is passed,
+ the tag value is used as the enumeration value.
{#see_also|@enumFromInt#}
{#header_close#}
@@ -5045,7 +5148,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer.
{#syntax#}value{#endsyntax#} can be {#syntax#}*T{#endsyntax#} or {#syntax#}?*T{#endsyntax#}.
- To convert the other way, use {#link|@ptrFromInt#}
+ {#see_also|@ptrFromInt#}
{#header_close#}
{#header_open|@max#}
@@ -5249,6 +5352,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
is zero, this invokes safety-checked {#link|Illegal Behavior#}.
+ {#see_also|@intFromPtr#}
{#header_close#}
{#header_open|@rem#}
@@ -5456,19 +5560,14 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_close#}
{#header_open|@splat#}
- {#syntax#}@splat(scalar: anytype) anytype{#endsyntax#}
+ {#syntax#}@splat(element: anytype) anytype{#endsyntax#}
Produces an array or vector where each element is the value
- {#syntax#}scalar{#endsyntax#}. The return type and thus the length of the
- vector is inferred.
+ {#syntax#}element{#endsyntax#}. The return type, including the number of
+ elements, is inferred.
{#code|test_splat_builtin.zig#}
-
-
- {#syntax#}scalar{#endsyntax#} must be an {#link|integer|Integers#}, {#link|bool|Primitive Types#},
- {#link|float|Floats#}, or {#link|pointer|Pointers#}.
-
- {#see_also|Vectors|@shuffle#}
+ {#see_also|Vectors|Arrays|@shuffle#}
{#header_close#}
{#header_open|@reduce#}
@@ -5752,7 +5851,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_open|@Fn#}
{#syntax#}@Fn(
comptime param_types: []const type,
- comptime param_attrs: *const [param_types.len]std.lang.Type.Fn.Param.Attributes,
+ comptime param_attrs: *const [param_types.len]std.lang.Type.Fn.ParamAttributes,
comptime ReturnType: type,
comptime attrs: std.lang.Type.Fn.Attributes,
) type{#endsyntax#}
@@ -5765,7 +5864,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
comptime BackingInt: ?type,
comptime field_names: []const []const u8,
comptime field_types: *const [field_names.len]type,
- comptime field_attrs: *const [field_names.len]std.lang.Type.StructField.Attributes,
+ comptime field_attrs: *const [field_names.len]std.lang.Type.Struct.FieldAttributes,
) type{#endsyntax#}
Returns a {#link|struct#} type with the properties specified by the arguments.
{#header_close#}
@@ -5773,11 +5872,11 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#header_open|@Union#}
{#syntax#}@Union(
comptime layout: std.lang.Type.ContainerLayout,
- /// Either the integer tag type, or the integer backing type, depending on `layout`.
+ /// Either the enum tag type, or the integer backing type, depending on `layout`.
comptime ArgType: ?type,
comptime field_names: []const []const u8,
comptime field_types: *const [field_names.len]type,
- comptime field_attrs: *const [field_names.len]std.lang.Type.UnionField.Attributes,
+ comptime field_attrs: *const [field_names.len]std.lang.Type.Union.FieldAttributes,
) type{#endsyntax#}
Returns a {#link|union#} type with the properties specified by the arguments.
{#header_close#}
@@ -5792,6 +5891,50 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
Returns an {#link|enum#} type with the properties specified by the arguments.
{#header_close#}
+ {#header_open|@SpirvType#}
+ {#syntax#}@SpirvType(comptime options: std.lang.Type.Spirv) type{#endsyntax#}
+
+ Returns a SPIR-V type with the properties specified by the arguments.
+
+
+
+
+
+ | Tag |
+ SPIR-V Equivalent |
+ Description |
+
+
+
+
+ .sampler |
+ OpTypeSampler |
+ An opaque sampler |
+
+
+ .image |
+ OpTypeImage |
+ An opaque image |
+
+
+ .sampled_image |
+ OpTypeSampledImage |
+ An opaque image combined with a sampler |
+
+
+ .runtime_array |
+ OpTypeRuntimeArray |
+
+ An array whose length is determined at runtime.
+ The resulting type supports indexing and exposes a {#syntax#}.len{#endsyntax#} field.
+ It may only appear as the last field of an {#link|extern struct#}.
+ |
+
+
+
+
+ {#header_close#}
+
{#header_open|@typeInfo#}
{#syntax#}@typeInfo(comptime T: type) std.lang.Type{#endsyntax#}
@@ -6038,6 +6181,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}/{#endsyntax#} (division)
{#link|@divTrunc#} (division)
{#link|@divFloor#} (division)
+ {#link|@divCeil#} (division)
{#link|@divExact#} (division)
Example with addition at compile-time:
@@ -6055,6 +6199,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#syntax#}@import("std").math.mul{#endsyntax#}
{#syntax#}@import("std").math.divTrunc{#endsyntax#}
{#syntax#}@import("std").math.divFloor{#endsyntax#}
+ {#syntax#}@import("std").math.divCeil{#endsyntax#}
{#syntax#}@import("std").math.divExact{#endsyntax#}
{#syntax#}@import("std").math.shl{#endsyntax#}
@@ -7135,46 +7280,27 @@ fn readU32Be() u32 {}
{#header_close#}
{#header_close#}
+
{#header_open|Source Encoding#}
- Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.
- Throughout all zig source code (including in comments), some code points are never allowed:
+ Zig source code is UTF-8 encoded. Invalid UTF-8 byte sequences are not allowed anywhere.
+ Some code points are never allowed, even in {#link|Comments#}:
- - Ascii control characters, except for U+000a (LF), U+000d (CR), and U+0009 (HT): U+0000 - U+0008, U+000b - U+000c, U+000e - U+0001f, U+007f.
- - Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).
+ - ASCII control characters, except for U+000a (LF): U+0000...U+0009, U+000b...U+0001f, U+007f.
+ - Non-ASCII Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).
+ - Byte order marks: U+FEFF (BOM).
- LF (byte value 0x0a, code point U+000a, {#syntax#}'\n'{#endsyntax#}) is the line terminator in Zig source code.
- This byte value terminates every line of zig source code except the last line of the file.
- It is recommended that non-empty source files end with an empty line, which means the last byte would be 0x0a (LF).
-
-
- Each LF may be immediately preceded by a single CR (byte value 0x0d, code point U+000d, {#syntax#}'\r'{#endsyntax#})
- to form a Windows style line ending, but this is discouraged. Note that in multiline strings, CRLF sequences will
- be encoded as LF when compiled into a zig program.
- A CR in any other context is not allowed.
-
-
- HT hard tabs (byte value 0x09, code point U+0009, {#syntax#}'\t'{#endsyntax#}) are interchangeable with
- SP spaces (byte value 0x20, code point U+0020, {#syntax#}' '{#endsyntax#}) as a token separator,
- but use of hard tabs is discouraged. See {#link|Grammar#}.
-
-
- For compatibility with other tools, the compiler ignores a UTF-8-encoded byte order mark (U+FEFF)
- if it is the first Unicode code point in the source text. A byte order mark is not allowed anywhere else in the source.
-
-
- Note that running zig fmt on a source file will implement all recommendations mentioned here.
-
-
- Note that a tool reading Zig source code can make assumptions if the source code is assumed to be correct Zig code.
- For example, when identifying the ends of lines, a tool can use a naive search such as /\n/,
- or an advanced
- search such as /\r\n?|[\n\u0085\u2028\u2029]/, and in either case line endings will be correctly identified.
- For another example, when identifying the whitespace before the first token on a line,
- a tool can either use a naive search such as /[ \t]/,
- or an advanced search such as /\s/,
- and in either case whitespace will be correctly identified.
+ LF (byte value 0x0a, code point U+000a, {#syntax#}'\n'{#endsyntax#}) is
+ the line terminator in Zig source code. This byte value terminates every
+ line of Zig source code, including last line of the file.
+ These conservative rules mean that third party tools reading
+ already-validated Zig source code may make simplifying assumptions, such
+ as naively separating lines based on {#syntax#}'\n'{#endsyntax#}.
+ However, tooling such as zig fmt provides convenience
+ functionality to convert invalid source encodings to valid source
+ encodings, for instance by stripping byte order marks and carriage
+ returns.
{#header_close#}
{#header_open|Keyword Reference#}
@@ -7378,7 +7504,7 @@ fn readU32Be() u32 {}
{#syntax#}errdefer{#endsyntax#}
- {#syntax#}errdefer{#endsyntax#} will execute an expression when control flow leaves the current block if the function returns an error, the errdefer expression can capture the unwrapped value.
+ {#syntax#}errdefer{#endsyntax#} will execute an expression when control flow leaves the current block if the function returns an error.
- See also {#link|errdefer#}
@@ -7725,614 +7851,24 @@ fn readU32Be() u32 {}
{#header_open|Appendix#}
{#header_open|Grammar#}
- {#syntax_block|peg|grammar.peg#}
-Root <- skip ContainerMembers eof
-
-# *** Top level ***
-ContainerMembers <- container_doc_comment? ContainerDeclaration* (ContainerField COMMA)* (ContainerField / ContainerDeclaration*)
-
-ContainerDeclaration <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl
-
-TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
-
-ComptimeDecl <- KEYWORD_comptime Block
-
-Decl
- <- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
- / KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON
- / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
-
-FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr !ExprSuffix
-
-VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection?
-
-GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON
-
-ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)?
-
-# *** Block Level ***
-BlockStatement
- <- Statement
- / KEYWORD_defer BlockExprStatement
- / KEYWORD_errdefer BlockExprStatement
- / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement
-
-Statement
- <- ExprStatement
- / KEYWORD_suspend BlockExprStatement
- / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON
-
-ExprStatement
- <- IfStatement
- / LabeledStatement
- / KEYWORD_nosuspend BlockExprStatement
- / KEYWORD_comptime BlockExpr
-
-IfStatement
- <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
- / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
-
-LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr)
-
-LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
-
-ForStatement
- <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else )
- / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement )
-
-WhileStatement
- <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
- / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
-
-BlockExprStatement
- <- BlockExpr
- / !BlockExpr AssignExpr SEMICOLON
-
-BlockExpr <- BlockLabel? Block
-
-# An assignment or a destructure whose LHS are all lvalue expressions or variable declarations.
-VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
-
-# *** Expression Level ***
-
-# An assignment or a destructure whose LHS are all lvalue expressions.
-AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)?
-
-SingleAssignExpr <- Expr (AssignOp Expr)?
-
-Expr <- BoolOrExpr
-
-BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
-
-BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
-
-CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
-
-BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
-
-BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
-
-AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
-
-MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
-
-PrefixExpr <- PrefixOp* PrimaryExpr
-
-PrimaryExpr
- <- AsmExpr
- / IfExpr
- / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
- / KEYWORD_comptime Expr !ExprSuffix
- / KEYWORD_nosuspend Expr !ExprSuffix
- / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
- / KEYWORD_resume Expr !ExprSuffix
- / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart)
- / BlockLabel? LoopExpr
- / Block
- / CurlySuffixExpr
-
-IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
-
-Block <- LBRACE BlockStatement* RBRACE
-
-LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
-
-ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix
-
-WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
-
-CurlySuffixExpr <- TypeExpr InitList?
-
-InitList
- <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
- / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
- / LBRACE RBRACE
-
-TypeExpr <- PrefixTypeOp* ErrorUnionExpr
-
-ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
-
-SuffixExpr
- <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
-
-PrimaryTypeExpr
- <- BUILTINIDENTIFIER FnCallArguments
- / CHAR_LITERAL
- / ContainerDecl
- / DOT IDENTIFIER
- / DOT InitList
- / ErrorSetDecl
- / FLOAT
- / FnProto
- / GroupedExpr
- / LabeledTypeExpr
- / IDENTIFIER !(COLON LabelableExpr)
- / IfTypeExpr
- / INTEGER
- / KEYWORD_comptime TypeExpr !ExprSuffix
- / KEYWORD_error DOT IDENTIFIER
- / KEYWORD_anyframe
- / KEYWORD_unreachable
- / STRINGLITERAL
-
-ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
-
-ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
-
-GroupedExpr <- LPAREN Expr RPAREN
-
-IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
-
-LabeledTypeExpr
- <- BlockLabel Block
- / BlockLabel? LoopTypeExpr
- / BlockLabel? SwitchExpr
-
-LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
-
-ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix
-
-WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
-
-SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
-
-# *** Assembly ***
-AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
-
-AsmOutput <- COLON AsmOutputList AsmInput?
-
-AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
-
-AsmInput <- COLON AsmInputList AsmClobbers?
-
-AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN
-
-AsmClobbers <- COLON Expr
-
-# *** Helper grammar ***
-BreakLabel <- COLON IDENTIFIER
-
-BlockLabel <- IDENTIFIER COLON
-
-FieldInit <- DOT IDENTIFIER EQUAL Expr
-
-WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
-
-LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
-
-AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN
-
-# Fn specific
-CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
-
-ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType
-
-ParamType
- <- KEYWORD_anytype
- / TypeExpr
-
-# Control flow prefixes
-IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
-
-WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
-
-ForPrefix <- KEYWORD_for LPAREN ForArgumentsList RPAREN PtrListPayload
-
-# Payloads
-Payload <- PIPE IDENTIFIER PIPE
-
-PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
-
-PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
-
-PtrListPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* COMMA? PIPE
-
-# Switch specific
-SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? SingleAssignExpr
-
-SwitchCase
- <- SwitchItem (COMMA SwitchItem)* COMMA?
- / KEYWORD_else
-
-SwitchItem <- Expr (DOT3 Expr)?
-
-# For specific
-ForArgumentsList <- ForItem (COMMA ForItem)* COMMA?
-
-ForItem <- Expr (DOT2 Expr?)?
-
-# Operators
-AssignOp
- <- ASTERISKEQUAL
- / ASTERISKPIPEEQUAL
- / SLASHEQUAL
- / PERCENTEQUAL
- / PLUSEQUAL
- / PLUSPIPEEQUAL
- / MINUSEQUAL
- / MINUSPIPEEQUAL
- / LARROW2EQUAL
- / LARROW2PIPEEQUAL
- / RARROW2EQUAL
- / AMPERSANDEQUAL
- / CARETEQUAL
- / PIPEEQUAL
- / ASTERISKPERCENTEQUAL
- / PLUSPERCENTEQUAL
- / MINUSPERCENTEQUAL
- / EQUAL
-
-CompareOp
- <- EQUALEQUAL
- / EXCLAMATIONMARKEQUAL
- / LARROW
- / RARROW
- / LARROWEQUAL
- / RARROWEQUAL
-
-BitwiseOp
- <- AMPERSAND
- / CARET
- / PIPE
- / KEYWORD_orelse
- / KEYWORD_catch Payload?
-
-BitShiftOp
- <- LARROW2
- / RARROW2
- / LARROW2PIPE
-
-AdditionOp
- <- PLUS
- / MINUS
- / PLUS2
- / PLUSPERCENT
- / MINUSPERCENT
- / PLUSPIPE
- / MINUSPIPE
-
-MultiplyOp
- <- PIPE2
- / ASTERISK
- / SLASH
- / PERCENT
- / ASTERISKPERCENT
- / ASTERISKPIPE
-
-PrefixOp
- <- EXCLAMATIONMARK
- / MINUS
- / TILDE
- / MINUSPERCENT
- / AMPERSAND
- / KEYWORD_try
-
-PrefixTypeOp
- <- QUESTIONMARK
- / KEYWORD_anyframe MINUSRARROW
- / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
- / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
- / ArrayTypeStart
-
-SuffixOp
- <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET
- / DOT IDENTIFIER
- / DOTASTERISK
- / DOTQUESTIONMARK
-
-FnCallArguments <- LPAREN ExprList RPAREN
-
-ExprSuffix
- <- KEYWORD_or
- / KEYWORD_and
- / CompareOp
- / BitwiseOp
- / BitShiftOp
- / AdditionOp
- / MultiplyOp
- / EXCLAMATIONMARK
- / SuffixOp
- / FnCallArguments
-
-LabelableExpr
- <- Block
- / SwitchExpr
- / LoopExpr
-
-# Ptr specific
-SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
-
-SinglePtrTypeStart <- ASTERISK
-
-ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
-
-ArrayTypeStart <- LBRACKET Expr !ASTERISK (COLON Expr)? RBRACKET
-
-# ContainerDecl specific
-ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
-
-ContainerDeclType
- <- KEYWORD_struct (LPAREN Expr RPAREN)?
- / KEYWORD_opaque
- / KEYWORD_enum (LPAREN Expr RPAREN)?
- / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / !KEYWORD_enum Expr) RPAREN)?
-
-# Alignment
-ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
-
-BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN
-
-# Lists
-IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)?
-
-SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
-
-AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
-
-AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
-
-ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)?
-
-ExprList <- (Expr COMMA)* Expr?
-
-# *** Tokens ***
-eof <- !.
-bin <- [01]
-bin_ <- '_'? bin
-oct <- [0-7]
-oct_ <- '_'? oct
-hex <- [0-9a-fA-F]
-hex_ <- '_'? hex
-dec <- [0-9]
-dec_ <- '_'? dec
-
-bin_int <- bin bin_*
-oct_int <- oct oct_*
-dec_int <- dec dec_*
-hex_int <- hex hex_*
-
-ox80_oxBF <- [\200-\277]
-oxF4 <- '\364'
-ox80_ox8F <- [\200-\217]
-oxF1_oxF3 <- [\361-\363]
-oxF0 <- '\360'
-ox90_0xBF <- [\220-\277]
-oxEE_oxEF <- [\356-\357]
-oxED <- '\355'
-ox80_ox9F <- [\200-\237]
-oxE1_oxEC <- [\341-\354]
-oxE0 <- '\340'
-oxA0_oxBF <- [\240-\277]
-oxC2_oxDF <- [\302-\337]
-
-# From https://lemire.me/blog/2018/05/09/how-quickly-can-you-check-that-a-string-is-valid-unicode-utf-8/
-# First Byte Second Byte Third Byte Fourth Byte
-# [0x00,0x7F]
-# [0xC2,0xDF] [0x80,0xBF]
-# 0xE0 [0xA0,0xBF] [0x80,0xBF]
-# [0xE1,0xEC] [0x80,0xBF] [0x80,0xBF]
-# 0xED [0x80,0x9F] [0x80,0xBF]
-# [0xEE,0xEF] [0x80,0xBF] [0x80,0xBF]
-# 0xF0 [0x90,0xBF] [0x80,0xBF] [0x80,0xBF]
-# [0xF1,0xF3] [0x80,0xBF] [0x80,0xBF] [0x80,0xBF]
-# 0xF4 [0x80,0x8F] [0x80,0xBF] [0x80,0xBF]
-
-multibyte_utf8 <-
- oxF4 ox80_ox8F ox80_oxBF ox80_oxBF
- / oxF1_oxF3 ox80_oxBF ox80_oxBF ox80_oxBF
- / oxF0 ox90_0xBF ox80_oxBF ox80_oxBF
- / oxEE_oxEF ox80_oxBF ox80_oxBF
- / oxED ox80_ox9F ox80_oxBF
- / oxE1_oxEC ox80_oxBF ox80_oxBF
- / oxE0 oxA0_oxBF ox80_oxBF
- / oxC2_oxDF ox80_oxBF
-
-non_control_ascii <- [\040-\176]
-non_control_utf8 <- [\040-\377]
-
-char_escape
- <- "\\x" hex hex
- / "\\u{" hex+ "}"
- / "\\" [nr\\t'"]
-char_char
- <- multibyte_utf8
- / char_escape
- / ![\\'\n] non_control_ascii
-
-string_char
- <- multibyte_utf8
- / char_escape
- / ![\\"\n] non_control_ascii
-
-container_doc_comment <- ('//!' non_control_utf8* [ \n]* skip)+
-doc_comment <- ('///' non_control_utf8* [ \n]* skip)+
-line_comment <- '//' ![!/] non_control_utf8* / '////' non_control_utf8*
-line_string <- '\\\\' non_control_utf8* [ \n]*
-skip <- ([ \n] / line_comment)*
-
-CHAR_LITERAL <- ['] char_char ['] skip
-FLOAT
- <- '0x' hex_int '.' hex_int ([pP] [-+]? dec_int)? skip
- / dec_int '.' dec_int ([eE] [-+]? dec_int)? skip
- / '0x' hex_int [pP] [-+]? dec_int skip
- / dec_int [eE] [-+]? dec_int skip
-INTEGER
- <- '0b' bin_int skip
- / '0o' oct_int skip
- / '0x' hex_int skip
- / dec_int skip
-STRINGLITERALSINGLE <- ["] string_char* ["] skip
-STRINGLITERAL
- <- STRINGLITERALSINGLE
- / (line_string skip)+
-IDENTIFIER
- <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip
- / '@' STRINGLITERALSINGLE
-BUILTINIDENTIFIER <- '@'[A-Za-z_][A-Za-z0-9_]* skip
-
-
-AMPERSAND <- '&' ![=] skip
-AMPERSANDEQUAL <- '&=' skip
-ASTERISK <- '*' ![*%=|] skip
-ASTERISKEQUAL <- '*=' skip
-ASTERISKPERCENT <- '*%' ![=] skip
-ASTERISKPERCENTEQUAL <- '*%=' skip
-ASTERISKPIPE <- '*|' ![=] skip
-ASTERISKPIPEEQUAL <- '*|=' skip
-CARET <- '^' ![=] skip
-CARETEQUAL <- '^=' skip
-COLON <- ':' skip
-COMMA <- ',' skip
-DOT <- '.' ![*.?] skip
-DOT2 <- '..' ![.] skip
-DOT3 <- '...' skip
-DOTASTERISK <- '.*' skip
-DOTQUESTIONMARK <- '.?' skip
-EQUAL <- '=' ![>=] skip
-EQUALEQUAL <- '==' skip
-EQUALRARROW <- '=>' skip
-EXCLAMATIONMARK <- '!' ![=] skip
-EXCLAMATIONMARKEQUAL <- '!=' skip
-LARROW <- '<' ![<=] skip
-LARROW2 <- '<<' ![=|] skip
-LARROW2EQUAL <- '<<=' skip
-LARROW2PIPE <- '<<|' ![=] skip
-LARROW2PIPEEQUAL <- '<<|=' skip
-LARROWEQUAL <- '<=' skip
-LBRACE <- '{' skip
-LBRACKET <- '[' skip
-LPAREN <- '(' skip
-MINUS <- '-' ![%=>|] skip
-MINUSEQUAL <- '-=' skip
-MINUSPERCENT <- '-%' ![=] skip
-MINUSPERCENTEQUAL <- '-%=' skip
-MINUSPIPE <- '-|' ![=] skip
-MINUSPIPEEQUAL <- '-|=' skip
-MINUSRARROW <- '->' skip
-PERCENT <- '%' ![=] skip
-PERCENTEQUAL <- '%=' skip
-PIPE <- '|' ![|=] skip
-PIPE2 <- '||' skip
-PIPEEQUAL <- '|=' skip
-PLUS <- '+' ![%+=|] skip
-PLUS2 <- '++' skip
-PLUSEQUAL <- '+=' skip
-PLUSPERCENT <- '+%' ![=] skip
-PLUSPERCENTEQUAL <- '+%=' skip
-PLUSPIPE <- '+|' ![=] skip
-PLUSPIPEEQUAL <- '+|=' skip
-LETTERC <- 'c' skip
-QUESTIONMARK <- '?' skip
-RARROW <- '>' ![>=] skip
-RARROW2 <- '>>' ![=] skip
-RARROW2EQUAL <- '>>=' skip
-RARROWEQUAL <- '>=' skip
-RBRACE <- '}' skip
-RBRACKET <- ']' skip
-RPAREN <- ')' skip
-SEMICOLON <- ';' skip
-SLASH <- '/' ![=] skip
-SLASHEQUAL <- '/=' skip
-TILDE <- '~' skip
-
-end_of_word <- ![a-zA-Z0-9_] skip
-KEYWORD_addrspace <- 'addrspace' end_of_word
-KEYWORD_align <- 'align' end_of_word
-KEYWORD_allowzero <- 'allowzero' end_of_word
-KEYWORD_and <- 'and' end_of_word
-KEYWORD_anyframe <- 'anyframe' end_of_word
-KEYWORD_anytype <- 'anytype' end_of_word
-KEYWORD_asm <- 'asm' end_of_word
-KEYWORD_break <- 'break' end_of_word
-KEYWORD_callconv <- 'callconv' end_of_word
-KEYWORD_catch <- 'catch' end_of_word
-KEYWORD_comptime <- 'comptime' end_of_word
-KEYWORD_const <- 'const' end_of_word
-KEYWORD_continue <- 'continue' end_of_word
-KEYWORD_defer <- 'defer' end_of_word
-KEYWORD_else <- 'else' end_of_word
-KEYWORD_enum <- 'enum' end_of_word
-KEYWORD_errdefer <- 'errdefer' end_of_word
-KEYWORD_error <- 'error' end_of_word
-KEYWORD_export <- 'export' end_of_word
-KEYWORD_extern <- 'extern' end_of_word
-KEYWORD_fn <- 'fn' end_of_word
-KEYWORD_for <- 'for' end_of_word
-KEYWORD_if <- 'if' end_of_word
-KEYWORD_inline <- 'inline' end_of_word
-KEYWORD_noalias <- 'noalias' end_of_word
-KEYWORD_nosuspend <- 'nosuspend' end_of_word
-KEYWORD_noinline <- 'noinline' end_of_word
-KEYWORD_opaque <- 'opaque' end_of_word
-KEYWORD_or <- 'or' end_of_word
-KEYWORD_orelse <- 'orelse' end_of_word
-KEYWORD_packed <- 'packed' end_of_word
-KEYWORD_pub <- 'pub' end_of_word
-KEYWORD_resume <- 'resume' end_of_word
-KEYWORD_return <- 'return' end_of_word
-KEYWORD_linksection <- 'linksection' end_of_word
-KEYWORD_struct <- 'struct' end_of_word
-KEYWORD_suspend <- 'suspend' end_of_word
-KEYWORD_switch <- 'switch' end_of_word
-KEYWORD_test <- 'test' end_of_word
-KEYWORD_threadlocal <- 'threadlocal' end_of_word
-KEYWORD_try <- 'try' end_of_word
-KEYWORD_union <- 'union' end_of_word
-KEYWORD_unreachable <- 'unreachable' end_of_word
-KEYWORD_var <- 'var' end_of_word
-KEYWORD_volatile <- 'volatile' end_of_word
-KEYWORD_while <- 'while' end_of_word
-
-keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
- / KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm
- / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
- / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
- / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
- / KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
- / KEYWORD_inline / KEYWORD_noalias / KEYWORD_nosuspend / KEYWORD_noinline
- / KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed
- / KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
- / KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test
- / KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable
- / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
- {#end_syntax_block#}
+ {#grammar#}
{#header_close#}
{#header_open|Zen#}
- Communicate intent precisely.
- Edge cases matter.
- Favor reading code over writing code.
- - Only one obvious way to do things.
+ - There is an idiomatic way to do it.
- Runtime crashes are better than bugs.
- Compile errors are better than runtime crashes.
- Incremental improvements.
- Avoid local maximums.
- Reduce the amount one must remember.
- - Focus on code rather than style.
- - Resource allocation may fail; resource deallocation must succeed.
- - Memory is a resource.
- - Together we serve the users.
+ - Focus on logic, not style.
+ - Resource allocation may fail.
+ - Resource deallocation must succeed.
+ Together, we serve the users!
{#header_close#}
{#header_close#}
From d97d8b3944cd34d64f9886a444248a8e8d379089 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 23:09:22 +0200
Subject: [PATCH 06/14] update build system code
---
build.zig | 164 +++++++++++++++++++------------
tests/add_analysis_cases.zig | 31 +++---
tests/add_build_runner_cases.zig | 33 ++++---
3 files changed, 130 insertions(+), 98 deletions(-)
diff --git a/build.zig b/build.zig
index b3f0a6f38f..2ded4914b5 100644
--- a/build.zig
+++ b/build.zig
@@ -65,9 +65,9 @@ pub fn build(b: *Build) !void {
const test_options = b.addOptions();
test_options.step.name = "ZLS test options";
- test_options.addOptionPath("zig_exe_path", .{ .cwd_relative = b.graph.zig_exe });
- test_options.addOptionPath("zig_lib_path", .{ .cwd_relative = b.fmt("{f}", .{b.graph.zig_lib_directory}) });
- test_options.addOptionPath("global_cache_path", .{ .cwd_relative = b.cache_root.join(b.allocator, &.{"zls"}) catch @panic("OOM") });
+ test_options.addOptionPath("zig_exe_path", .zig_exe);
+ test_options.addOptionPath("zig_lib_path", .zig_lib);
+ test_options.addOptionPath("global_cache_path", std.Build.LazyPath.cache_root.join(b.allocator, "zls") catch @panic("OOM"));
break :blk test_options.createModule();
};
@@ -115,18 +115,22 @@ pub fn build(b: *Build) !void {
{ // zig build gen
const gen_step = b.step("gen", "Regenerate config files");
+ const update_source = b.addUpdateSourceFiles();
+ gen_step.dependOn(&update_source.step);
+
const gen_cmd = b.addRunArtifact(gen_exe);
- if (b.args) |args| {
- gen_cmd.addArgs(args);
- gen_step.dependOn(&gen_cmd.step);
- } else {
- const update_source = b.addUpdateSourceFiles();
- gen_cmd.addArg("--generate-config");
- update_source.addCopyFileToSource(gen_cmd.addOutputFileArg("Config.zig"), "src/Config.zig");
- gen_cmd.addArg("--generate-schema");
- update_source.addCopyFileToSource(gen_cmd.addOutputFileArg("schema.json"), "schema.json");
- gen_step.dependOn(&update_source.step);
- }
+ gen_cmd.addArg("--generate-config");
+ update_source.addCopyFileToSource(gen_cmd.addOutputFileArg("Config.zig"), "src/Config.zig");
+ gen_cmd.addArg("--generate-schema");
+ update_source.addCopyFileToSource(gen_cmd.addOutputFileArg("schema.json"), "schema.json");
+ gen_cmd.addPassthruArgs();
+ }
+
+ { // zig build config-gen
+ const gen_step = b.step("config-gen", "Run src/tools/config_gen.zig");
+ const gen_cmd = b.addRunArtifact(gen_exe);
+ gen_cmd.addPassthruArgs();
+ gen_step.dependOn(&gen_cmd.step);
}
{ // zig build release
@@ -168,7 +172,7 @@ pub fn build(b: *Build) !void {
artifact.* = b.addExecutable(.{
.name = "zls",
.root_module = exe_module,
- .max_rss = if (optimize == .Debug and target_query.os_tag == .wasi) 2_600_000_000 else 2_000_000_000,
+ .max_rss = if (optimize == .debug and target_query.os_tag == .wasi) 2_600_000_000 else 2_000_000_000,
.use_llvm = use_llvm,
.use_lld = use_llvm,
});
@@ -252,20 +256,6 @@ pub fn build(b: *Build) !void {
.use_lld = use_llvm,
});
- if (target.result.cpu.arch.isWasm() and b.enable_wasmtime) {
- // Zig's build system integration with wasmtime does not support adding custom preopen directories so it is done manually.
- const args: []const ?[]const u8 = &.{
- "wasmtime",
- "--dir=.",
- b.fmt("--dir={f}::/lib", .{b.graph.zig_lib_directory}),
- b.fmt("--dir={s}::/cache", .{b.cache_root.join(b.allocator, &.{"zls"}) catch @panic("OOM")}),
- "--",
- null,
- };
- tests.setExecCmd(args);
- src_tests.setExecCmd(args);
- }
-
blk: { // zig build test, zig build test-build-runner, zig build test-analysis
const test_step = b.step("test", "Run all the tests");
const test_build_runner_step = b.step("test-build-runner", "Run all the build runner tests");
@@ -278,8 +268,10 @@ pub fn build(b: *Build) !void {
const run_tests = b.addRunArtifact(tests);
const run_src_tests = b.addRunArtifact(src_tests);
- run_tests.skip_foreign_checks = target.result.cpu.arch.isWasm() and b.enable_wasmtime;
- run_src_tests.skip_foreign_checks = target.result.cpu.arch.isWasm() and b.enable_wasmtime;
+ for ([_]*Build.Step.Run{ run_tests, run_src_tests }) |run| {
+ run.setPreopen("/lib", .zig_lib);
+ run.setPreopen("/cache", Build.LazyPath.cache_root.join(b.allocator, "zls") catch @panic("OOM"));
+ }
// Setup dependencies of `zig build test`
test_step.dependOn(&run_tests.step);
@@ -300,21 +292,23 @@ pub fn build(b: *Build) !void {
run_test_steps.append(b.allocator, step.cast(std.Build.Step.Run).?) catch @panic("OOM");
}
- const kcov_bin = b.findProgram(&.{"kcov"}, &.{}) catch "kcov";
+ const kcov_bin = b.findProgramLazy(.{ .names = &.{"kcov"} });
const merge_step = std.Build.Step.Run.create(b, "merge coverage");
- merge_step.addArgs(&.{ kcov_bin, "--merge" });
+ merge_step.addFileArg(kcov_bin);
+ merge_step.addArg("--merge");
merge_step.rename_step_with_output_arg = false;
- const merged_coverage_output = merge_step.addOutputFileArg(".");
+ const merged_coverage_output = merge_step.addOutputFileArg2(".", .{});
for (run_test_steps.items) |run_step| {
run_step.setName(b.fmt("{s} (collect coverage)", .{run_step.step.name}));
// prepend the kcov exec args
const argv = run_step.argv.toOwnedSlice(b.allocator) catch @panic("OOM");
- run_step.addArgs(&.{ kcov_bin, "--collect-only" });
- run_step.addPrefixedDirectoryArg("--include-pattern=", b.path("src"));
- merge_step.addDirectoryArg(run_step.addOutputFileArg(run_step.producer.?.name));
+ run_step.addFileArg(kcov_bin);
+ run_step.addArg("--collect-only");
+ run_step.addDirectoryArg2(b.path("src"), .{ .prefix = "--include-pattern=" });
+ merge_step.addDirectoryArg(run_step.addOutputFileArg2(run_step.producer.?.name, .{}));
run_step.argv.appendSlice(b.allocator, argv) catch @panic("OOM");
}
@@ -327,7 +321,6 @@ pub fn build(b: *Build) !void {
}
}
-/// Returns `MAJOR.MINOR.PATCH-dev` when `git describe` failed.
fn getVersion(b: *Build) std.SemanticVersion {
const version_string = b.option([]const u8, "version-string", "Override the version of this build. Must be a semantic version.");
if (version_string) |semver_string| {
@@ -338,27 +331,68 @@ fn getVersion(b: *Build) std.SemanticVersion {
if (zls_version.pre == null) return zls_version;
+ // Ensure git version changes get picked up.
+ git: {
+ const io = b.graph.io;
+ const git_file = b.root.openFile(io, ".git", .{ .allow_directory = false }) catch |err| switch (err) {
+ error.IsDir => {
+ b.dependOnFileContents(b.path(".git/logs/HEAD"));
+ break :git;
+ },
+ error.FileNotFound => {
+ // Need a way to indicate that the build.zig logic depends on the absence of a file.
+ b.graph.poisonCache();
+ break :git;
+ },
+ else => |e| std.process.fatal("Failed to read '.git' directory: {t}", .{e}),
+ };
+ defer git_file.close(io);
+ var line_buffer: ["gitdir: ".len + std.Io.Dir.max_path_bytes + 1]u8 = undefined;
+ var git_file_reader = git_file.reader(io, &line_buffer);
+ if (std.mem.cutPrefix(u8, std.mem.trimEnd(u8, git_file_reader.interface.allocRemaining(
+ b.graph.arena,
+ .limited("gitdir: ".len + std.Io.Dir.max_path_bytes + "\r\n".len),
+ ) catch |e| @panic(@errorName(e)), "\r\n"), "gitdir: ")) |git_dir| {
+ const head_file = b.pathJoin(&.{ git_dir, "logs", "HEAD" });
+ b.dependOnFileContents(if (std.Io.Dir.path.isAbsolute(head_file))
+ b.graph.cwdRelativePath(head_file)
+ else
+ b.path(head_file));
+ }
+ }
+
+ if (!std.process.can_spawn) {
+ std.process.fatal("version info cannot be retrieved from git. ZLS version must be provided using -Dversion-string", .{});
+ }
+
+ const root_path = b.fmt("{f}", .{b.root});
const argv: []const []const u8 = &.{
- "git", "-C", b.pathFromRoot("."), "--git-dir", ".git", "describe", "--match", "*.*.*", "--tags",
+ "git", "-C", root_path, "--git-dir", ".git", "describe", "--match", "*.*.*", "--tags",
};
- var code: u8 = undefined;
- const git_describe_untrimmed = b.runAllowFail(argv, &code, .ignore) catch |err| {
- const argv_joined = std.mem.join(b.allocator, " ", argv) catch @panic("OOM");
+ const run_result = b.runFallible(argv, .{ .stderr_behavior = .ignore });
+ const git_describe = switch (run_result) {
+ .success => |stdout| std.mem.trim(u8, stdout, " \n\r"),
+ .spawn_failed => |err| err,
+ .bad_exit_code => error.BadExitCode,
+ .crashed => error.Crashed,
+ } catch |err| {
+ b.graph.poisonCache();
+ const cmd: std.zig.SubprocessCommand = .{ .argv = argv, .cwd = root_path };
std.log.warn(
- \\Failed to run git describe to resolve ZLS version: {}
- \\command: {s}
+ \\Failed to run 'git describe' to resolve ZLS version: {t}
+ \\command: {f}
\\
\\Consider passing the -Dversion-string flag to specify the ZLS version.
- , .{ err, argv_joined });
+ , .{ err, cmd });
return zls_version;
};
- const git_describe = std.mem.trim(u8, git_describe_untrimmed, " \n\r");
-
- switch (std.mem.count(u8, git_describe, "-")) {
+ switch (std.mem.countScalar(u8, git_describe, '-')) {
0 => {
// Tagged release version (e.g. 0.10.0).
- std.debug.assert(std.mem.eql(u8, git_describe, b.fmt("{f}", .{zls_version}))); // tagged release must match version string
+ if (!std.mem.eql(u8, git_describe, b.fmt("{f}", .{zls_version}))) {
+ std.process.fatal("ZLS version '{f}' does not match Git tag '{s}'", .{ zls_version, git_describe });
+ }
return zls_version;
},
2 => {
@@ -368,9 +402,18 @@ fn getVersion(b: *Build) std.SemanticVersion {
const commit_height = it.next().?;
const commit_id = it.next().?;
- const ancestor_ver = std.SemanticVersion.parse(tagged_ancestor) catch unreachable;
- std.debug.assert(zls_version.order(ancestor_ver) == .gt); // ZLS version must be greater than its previous version
- std.debug.assert(std.mem.startsWith(u8, commit_id, "g")); // commit hash is prefixed with a 'g'
+ if (std.SemanticVersion.parse(tagged_ancestor)) |ancestor_ver| {
+ if (zls_version.order(ancestor_ver) != .gt) {
+ std.process.fatal("ZLS version '{f}' must be greater than tagged ancestor '{f}'", .{ zls_version, ancestor_ver });
+ }
+ } else |_| {
+ // Ignore tagged ancestor if it's not a semantic version.
+ }
+
+ // Check that the commit hash is prefixed with a 'g' (a Git convention).
+ if (!std.mem.startsWith(u8, commit_id, "g")) {
+ std.process.fatal("Unexpected `git describe` output: {s}", .{git_describe});
+ }
return .{
.major = zls_version.major,
@@ -380,10 +423,7 @@ fn getVersion(b: *Build) std.SemanticVersion {
.build = commit_id[1..],
};
},
- else => {
- std.debug.print("Unexpected 'git describe' output: '{s}'\n", .{git_describe});
- std.process.exit(1);
- },
+ else => std.process.fatal("Unexpected 'git describe' output: '{s}'", .{git_describe}),
}
}
@@ -485,7 +525,7 @@ fn release(b: *Build, release_artifacts: []const *Build.Step.Compile, released_z
const release_minisign = b.option(bool, "release-minisign", "Sign release artifacts with Minisign") orelse false;
if (released_zls_version.pre != null and released_zls_version.build == null) {
- release_step.addError("Cannot build release because the ZLS version could not be resolved", .{}) catch @panic("OOM");
+ release_step.dependOn(&b.addFail("Cannot build release because the ZLS version could not be resolved").step);
return;
}
@@ -522,7 +562,7 @@ fn release(b: *Build, release_artifacts: []const *Build.Step.Compile, released_z
switch (extension) {
.zip => {
compress_cmd.addArgs(&.{ "7z", "a", "-mx=9" });
- compressed_artifacts.putNoClobber(b.allocator, file_name, compress_cmd.addOutputFileArg(file_name)) catch @panic("OOM");
+ compressed_artifacts.putNoClobber(b.allocator, file_name, compress_cmd.addOutputFileArg2(file_name, .{})) catch @panic("OOM");
compress_cmd.addArtifactArg(exe);
compress_cmd.addFileArg(exe.getEmittedPdb());
compress_cmd.addFileArg(b.path("LICENSE"));
@@ -534,10 +574,10 @@ fn release(b: *Build, release_artifacts: []const *Build.Step.Compile, released_z
compress_cmd.setEnvironmentVariable("XZ_OPT", "-9");
compress_cmd.addArgs(&.{ "tar", "caf" });
compressed_artifacts.putNoClobber(b.allocator, file_name, compress_cmd.addOutputFileArg(file_name)) catch @panic("OOM");
- compress_cmd.addPrefixedDirectoryArg("-C", exe.getEmittedBinDirectory());
+ compress_cmd.addDirectoryArg2(exe.getEmittedBinDirectory(), .{ .prefix = "-C" });
compress_cmd.addArg(exe_name);
- compress_cmd.addPrefixedDirectoryArg("-C", b.path("."));
+ compress_cmd.addDirectoryArg2(b.path("."), .{ .prefix = "-C", .make_absolute = true });
compress_cmd.addArg("LICENSE");
compress_cmd.addArg("README.md");
@@ -565,8 +605,8 @@ fn release(b: *Build, release_artifacts: []const *Build.Step.Compile, released_z
const minising_cmd = b.addSystemCommand(&.{ "minisign", "-Sm" });
minising_cmd.clearEnvironment();
minising_cmd.addFileArg(file_path);
- minising_cmd.addPrefixedFileArg("-s", .{ .cwd_relative = "minisign.key" });
- const minising_file_path = minising_cmd.addPrefixedOutputFileArg("-x", minisign_basename);
+ minising_cmd.addFileArg2(b.graph.cwdRelativePath("minisign.key"), .{ .prefix = "-s" });
+ const minising_file_path = minising_cmd.addOutputFileArg2(minisign_basename, .{ .prefix = "-x" });
const install_minising = b.addInstallFileWithDir(minising_file_path, install_dir, minisign_basename);
release_step.dependOn(&install_minising.step);
diff --git a/tests/add_analysis_cases.zig b/tests/add_analysis_cases.zig
index cd436f6c48..c01284ad11 100644
--- a/tests/add_analysis_cases.zig
+++ b/tests/add_analysis_cases.zig
@@ -10,8 +10,7 @@ pub fn addCases(
test_step: *std.Build.Step,
test_filters: []const []const u8,
) void {
- const cases_dir = b.path("tests/analysis");
- const cases_path_from_root = b.pathFromRoot("tests/analysis");
+ const cases_path = "tests/analysis";
const check_exe = b.addExecutable(.{
.name = "analysis_check",
@@ -25,16 +24,17 @@ pub fn addCases(
}),
});
- // https://github.com/ziglang/zig/issues/20605
- var dir = std.Io.Dir.cwd().openDir(b.graph.io, cases_path_from_root, .{ .iterate = true }) catch |err|
- std.debug.panic("failed to open '{s}': {}", .{ cases_path_from_root, err });
+ b.dependOnDirectory(b.path(cases_path));
+
+ var dir = b.root.openDir(b.graph.io, cases_path, .{ .iterate = true }) catch |err|
+ std.debug.panic("failed to open '{f}': {}", .{ b.path(cases_path), err });
defer dir.close(b.graph.io);
var it = dir.iterate();
while (true) {
const entry = it.next(b.graph.io) catch |err|
- std.debug.panic("failed to walk directory '{s}': {}", .{ cases_path_from_root, err }) orelse break;
+ std.debug.panic("failed to walk directory '{f}': {}", .{ b.path(cases_path), err }) orelse break;
if (entry.kind != .file) continue;
if (!std.mem.eql(u8, std.Io.Dir.path.extension(entry.name), ".zig")) continue;
@@ -46,32 +46,23 @@ pub fn addCases(
const run_check = std.Build.Step.Run.create(b, b.fmt("run analysis on {s}", .{entry.name}));
run_check.producer = check_exe;
- if (target.result.cpu.arch.isWasm() and b.enable_wasmtime) {
- run_check.skip_foreign_checks = true;
- run_check.addArgs(&.{
- "wasmtime",
- "--dir=.",
- b.fmt("--dir={f}::/lib", .{b.graph.zig_lib_directory}),
- "--",
- });
- }
-
run_check.addArtifactArg(check_exe);
if (target.query.eql(b.graph.host.query)) {
run_check.addArg("--zig-exe-path");
- run_check.addFileArg(.{ .cwd_relative = b.graph.zig_exe });
+ run_check.addFileArg(.zig_exe);
}
+ run_check.setPreopen("/lib", .zig_lib);
if (!target.result.cpu.arch.isWasm()) {
run_check.addArg("--zig-lib-path");
- run_check.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.graph.zig_lib_directory}) });
+ run_check.addDirectoryArg(.zig_lib);
}
- const input_file = cases_dir.path(b, entry.name);
+ const input_file = b.path(cases_path).path(b, entry.name);
if (!target.result.cpu.arch.isWasm()) {
run_check.addFileArg(input_file);
} else {
// pass a relative file path when running with wasmtime
- run_check.setCwd(cases_dir);
+ run_check.setCwd(b.path(cases_path));
run_check.addArg(entry.name);
run_check.addFileInput(input_file);
}
diff --git a/tests/add_build_runner_cases.zig b/tests/add_build_runner_cases.zig
index 94a661774d..7e19e92308 100644
--- a/tests/add_build_runner_cases.zig
+++ b/tests/add_build_runner_cases.zig
@@ -8,8 +8,9 @@ pub fn addCases(
test_step: *std.Build.Step,
test_filters: []const []const u8,
) void {
- const cases_dir = b.path("tests/build_runner_cases");
- const cases_path_from_root = b.pathFromRoot("tests/build_runner_cases");
+ const cases_path = "tests/build_runner_cases";
+
+ const global_cache: std.Build.LazyPath = .{ .relative = .{ .base = .global_cache } };
const check_exe = b.addExecutable(.{
.name = "build_runner_check",
@@ -22,16 +23,17 @@ pub fn addCases(
}),
});
- // https://github.com/ziglang/zig/issues/20605
- var dir = std.Io.Dir.cwd().openDir(b.graph.io, cases_path_from_root, .{ .iterate = true }) catch |err|
- std.debug.panic("failed to open '{s}': {}", .{ cases_path_from_root, err });
+ b.dependOnDirectory(b.path(cases_path));
+
+ var dir = b.root.openDir(b.graph.io, cases_path, .{ .iterate = true }) catch |err|
+ std.debug.panic("failed to open '{f}': {}", .{ b.path(cases_path), err });
defer dir.close(b.graph.io);
var it = dir.iterate();
while (true) {
const entry = it.next(b.graph.io) catch |err|
- std.debug.panic("failed to walk directory '{s}': {}", .{ cases_path_from_root, err }) orelse break;
+ std.debug.panic("failed to walk directory '{f}': {}", .{ b.path(cases_path), err }) orelse break;
if (entry.kind != .file) continue;
if (!std.mem.eql(u8, std.Io.Dir.path.extension(entry.name), ".zig")) continue;
@@ -40,23 +42,22 @@ pub fn addCases(
if (std.mem.find(u8, entry.name, test_filter) != null) break;
} else if (test_filters.len > 0) continue;
- const build_file = cases_dir.path(b, entry.name);
- const build_config_json_path = b.fmt("{s}/{s}.json", .{ cases_path_from_root, std.Io.Dir.path.stem(entry.name) });
- const expected_build_config_json = cases_dir.path(b, build_config_json_path);
+ const build_file = b.path(cases_path).path(b, entry.name);
+ const expected_build_config_json = b.path(cases_path).path(b, std.Io.Dir.path.stem(entry.name));
const build_cmd = std.Build.Step.Run.create(b, b.fmt("run build runner ({s})", .{entry.name}));
- build_cmd.addFileArg(.{ .cwd_relative = b.graph.zig_exe });
+ build_cmd.addFileArg(.zig_exe);
build_cmd.addArg("build");
build_cmd.addArg("--build-file");
build_cmd.addFileArg(build_file);
build_cmd.addArg("--build-runner");
build_cmd.addFileArg(b.path("src/build_runner/build_runner.zig"));
build_cmd.addArg("--cache-dir");
- build_cmd.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.cache_root}) });
+ build_cmd.addDirectoryArg(.cache_root);
build_cmd.addArg("--global-cache-dir");
- build_cmd.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.graph.global_cache_root}) });
+ build_cmd.addDirectoryArg(global_cache);
build_cmd.addArg("--zig-lib-dir");
- build_cmd.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.graph.zig_lib_directory}) });
+ build_cmd.addDirectoryArg(.zig_lib);
build_cmd.addFileInput(b.path("src/build_runner/shared.zig"));
@@ -64,13 +65,13 @@ pub fn addCases(
const run_diff = b.addRunArtifact(check_exe);
run_diff.setName(b.fmt("run {s} ({s})", .{ check_exe.name, entry.name }));
- run_diff.setCwd(cases_dir);
+ run_diff.setCwd(b.path(cases_path));
run_diff.addFileArg(expected_build_config_json);
run_diff.addFileArg(actual_build_config_json);
run_diff.addArg("--cache-dir");
- run_diff.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.cache_root}) });
+ run_diff.addDirectoryArg(.cache_root);
run_diff.addArg("--global-cache-dir");
- run_diff.addDirectoryArg(.{ .cwd_relative = b.fmt("{f}", .{b.graph.global_cache_root}) });
+ run_diff.addDirectoryArg(global_cache);
test_step.dependOn(&run_diff.step);
}
From 3501ad77d3989a1fd94f10b3dec49f3c2484ba92 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 22:40:33 +0200
Subject: [PATCH 07/14] update to latest Zig version
---
src/DiagnosticsCollection.zig | 14 +++++-----
src/DocumentScope.zig | 6 ++---
src/DocumentStore.zig | 4 ++-
src/Server.zig | 8 +++---
src/TrigramStore.zig | 4 +--
src/analyser/InternPool.zig | 40 ++++++++++++++---------------
src/analyser/error_msg.zig | 1 +
src/analyser/segmented_list.zig | 2 +-
src/ast.zig | 41 ++++++++++++++++++++++--------
src/configuration.zig | 36 +++++++++++++-------------
src/features/code_actions.zig | 9 +++----
src/features/diagnostics.zig | 8 +++---
src/features/document_symbol.zig | 4 +--
src/features/inlay_hints.zig | 4 +++
src/features/semantic_tokens.zig | 6 ++---
src/main.zig | 2 +-
src/offsets.zig | 8 +++---
src/print_ast.zig | 8 +++---
src/tools/config_gen.zig | 2 +-
tests/ErrorBuilder.zig | 2 +-
tests/analysis/builtins.zig | 12 ++++-----
tests/lsp_features/completion.zig | 3 ++-
tests/lsp_features/diagnostics.zig | 16 ++++++------
tests/utility/ast.zig | 2 +-
tests/utility/position_context.zig | 2 +-
25 files changed, 136 insertions(+), 108 deletions(-)
diff --git a/src/DiagnosticsCollection.zig b/src/DiagnosticsCollection.zig
index a6184b0c5f..5e89cebbb0 100644
--- a/src/DiagnosticsCollection.zig
+++ b/src/DiagnosticsCollection.zig
@@ -404,7 +404,7 @@ fn convertErrorBundleToLSPDiangostics(
.range = src_range,
.severity = .Error,
.source = "zls",
- .message = message,
+ .message = .{ .string = message },
.tags = if (tags.items.len != 0) tags.items else null,
.relatedInformation = relatedInformation,
});
@@ -517,7 +517,7 @@ test DiagnosticsCollection {
try std.testing.expectEqual(1, diagnostics.items.len);
try std.testing.expectEqual(lsp.types.Diagnostic.Severity.Error, diagnostics.items[0].severity);
- try std.testing.expectEqualStrings("Living For The City", diagnostics.items[0].message);
+ try std.testing.expectEqualStrings("Living For The City", diagnostics.items[0].message.string);
try std.testing.expectEqual(null, diagnostics.items[0].relatedInformation);
}
@@ -528,7 +528,7 @@ test DiagnosticsCollection {
try collection.collectLspDiagnosticsForDocument(uri, .@"utf-8", arena, &diagnostics);
try std.testing.expectEqual(1, diagnostics.items.len);
- try std.testing.expectEqualStrings("Living For The City", diagnostics.items[0].message);
+ try std.testing.expectEqualStrings("Living For The City", diagnostics.items[0].message.string);
}
{
@@ -538,7 +538,7 @@ test DiagnosticsCollection {
try collection.collectLspDiagnosticsForDocument(uri, .@"utf-8", arena, &diagnostics);
try std.testing.expectEqual(1, diagnostics.items.len);
- try std.testing.expectEqualStrings("You Haven't Done Nothin'", diagnostics.items[0].message);
+ try std.testing.expectEqualStrings("You Haven't Done Nothin'", diagnostics.items[0].message.string);
}
{
@@ -558,8 +558,8 @@ test DiagnosticsCollection {
try collection.collectLspDiagnosticsForDocument(uri, .@"utf-8", arena, &diagnostics);
try std.testing.expectEqual(2, diagnostics.items.len);
- try std.testing.expectEqualStrings("You Haven't Done Nothin'", diagnostics.items[0].message);
- try std.testing.expectEqualStrings("As", diagnostics.items[1].message);
+ try std.testing.expectEqualStrings("You Haven't Done Nothin'", diagnostics.items[0].message.string);
+ try std.testing.expectEqualStrings("As", diagnostics.items[1].message.string);
}
}
@@ -596,7 +596,7 @@ test "DiagnosticsCollection - compile_log_text" {
\\Compile Log Output:
\\@as(comptime_int, 7)
\\@as(comptime_int, 13)
- , diagnostics.items[0].message);
+ , diagnostics.items[0].message.string);
try std.testing.expectEqual(null, diagnostics.items[0].relatedInformation);
}
diff --git a/src/DocumentScope.zig b/src/DocumentScope.zig
index f23c8ba508..a6ca091e9e 100644
--- a/src/DocumentScope.zig
+++ b/src/DocumentScope.zig
@@ -98,8 +98,8 @@ pub const Declaration = union(enum) {
error_token: Ast.TokenIndex,
comptime {
- for (std.meta.fields(Declaration)) |field| {
- std.debug.assert(@sizeOf(field.type) <= 8); // a Declaration without the union tag must be less than 8 bytes
+ for (std.meta.fieldTypes(Declaration)) |T| {
+ std.debug.assert(@sizeOf(T) <= 8); // a Declaration without the union tag must be less than 8 bytes
}
}
@@ -340,7 +340,7 @@ const ScopeContext = struct {
if (std.mem.eql(u8, name, "_")) return;
defer std.debug.assert(pushed.context.doc_scope.declarations.len == pushed.context.doc_scope.declaration_lookup_map.count());
- if (@import("builtin").mode == .Debug) {
+ if (@import("builtin").mode == .debug) {
// Check that nameToken works
std.debug.assert(identifier_token == declaration.nameToken(pushed.context.tree));
}
diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig
index cc5fd07ed6..3e7f4f5bfc 100644
--- a/src/DocumentStore.zig
+++ b/src/DocumentStore.zig
@@ -449,7 +449,9 @@ pub const Handle = struct {
const tracy_zone = tracy.traceNamed(@src(), "Ast.parse");
defer tracy_zone.end();
- var tree = try Ast.parse(allocator, new_text, mode);
+ var tree = try Ast.parse(allocator, new_text, .{
+ .mode = mode,
+ });
errdefer tree.deinit(allocator);
// remove unused capacity
diff --git a/src/Server.zig b/src/Server.zig
index 94e87e9de8..18b3c4d07c 100644
--- a/src/Server.zig
+++ b/src/Server.zig
@@ -983,11 +983,11 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
server.showMessage(.Error, "{s}", .{msg});
}
- inline for (std.meta.fields(Config)) |field| {
- if (@field(result.did_change, field.name)) {
+ inline for (comptime std.meta.fieldNames(Config)) |field_name| {
+ if (@field(result.did_change, field_name)) {
var runtime_known_field_name: []const u8 = ""; // avoid unnecessary function instantiations of `std.Io.Writer.print`
- runtime_known_field_name = field.name;
- const new_value = @field(server.config_manager.config, field.name);
+ runtime_known_field_name = field_name;
+ const new_value = @field(server.config_manager.config, field_name);
log.info("Set config option '{s}' to {f}", .{ runtime_known_field_name, std.json.fmt(new_value, .{}) });
}
}
diff --git a/src/TrigramStore.zig b/src/TrigramStore.zig
index 87e7eecefb..57a230014b 100644
--- a/src/TrigramStore.zig
+++ b/src/TrigramStore.zig
@@ -96,7 +96,7 @@ pub fn init(
.simple_var_decl,
.aligned_var_decl,
=> {
- const in_function = in_function_stack.getLast() orelse false;
+ const in_function = in_function_stack.last() orelse false;
if (in_function) continue;
const main_token = tree.nodeMainToken(node);
@@ -297,7 +297,7 @@ fn appendOneTrigram(
const gop = try store.trigram_to_declarations.getOrPutValue(allocator, trigram, .empty);
- if (gop.value_ptr.getLast() != declaration_index) {
+ if (gop.value_ptr.last() != declaration_index) {
try gop.value_ptr.append(allocator, declaration_index);
}
}
diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig
index 19e0817d31..44950930bb 100644
--- a/src/analyser/InternPool.zig
+++ b/src/analyser/InternPool.zig
@@ -1144,7 +1144,7 @@ pub fn init(io: std.Io, gpa: Allocator) Allocator.Error!InternPool {
try ip.map.ensureTotalCapacity(gpa, items.len);
try ip.items.ensureTotalCapacity(gpa, items.len);
- if (builtin.is_test or builtin.mode == .Debug) {
+ if (builtin.is_test or builtin.mode == .debug) {
// detect wrong value for extra_count
try ip.extra.ensureTotalCapacityPrecise(gpa, extra_count);
} else {
@@ -1546,9 +1546,9 @@ fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 {
const size = @divExact(@sizeOf(T), 4);
try ip.extra.ensureUnusedCapacity(ip.gpa, size);
- inline for (std.meta.fields(T)) |field| {
- const item = @field(extra, field.name);
- switch (field.type) {
+ inline for (comptime std.meta.fieldNames(T), comptime std.meta.fieldTypes(T)) |field_name, FieldType| {
+ const item = @field(extra, field_name);
+ switch (FieldType) {
Index,
Decl.Index,
Decl.OptionalIndex,
@@ -1574,7 +1574,7 @@ fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 {
LimbSlice,
=> ip.extra.appendSliceAssumeCapacity(&.{ item.start, item.len }),
- else => @compileError("unexpected: " ++ @typeName(field.type)),
+ else => @compileError("unexpected: " ++ @typeName(FieldType)),
}
}
return result;
@@ -1583,10 +1583,10 @@ fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 {
fn extraData(ip: *const InternPool, comptime T: type, index: u32) T {
var result: T = undefined;
var i: u32 = 0;
- inline for (std.meta.fields(T)) |field| {
+ inline for (comptime std.meta.fieldNames(T), comptime std.meta.fieldTypes(T)) |field_name, FieldType| {
const item = ip.extra.items[index + i];
i += 1;
- @field(result, field.name) = switch (field.type) {
+ @field(result, field_name) = switch (FieldType) {
Index,
StringPool.String,
StringPool.OptionalString,
@@ -1620,7 +1620,7 @@ fn extraData(ip: *const InternPool, comptime T: type, index: u32) T {
break :blk .{ .start = item, .len = ip.extra.items[index + i] };
},
- else => @compileError("unexpected: " ++ @typeName(field.type)),
+ else => @compileError("unexpected: " ++ @typeName(FieldType)),
};
}
return result;
@@ -1894,7 +1894,7 @@ fn coerceInt(
}
pub fn resolvePeerTypes(ip: *InternPool, types: []const Index, target: std.Target) Allocator.Error!Index {
- if (builtin.mode == .Debug) {
+ if (builtin.mode == .debug) {
for (types) |ty| {
assert(ip.isType(ty));
}
@@ -3325,16 +3325,16 @@ pub fn intInfo(ip: *InternPool, ty: Index, target: std.Target) std.lang.Type.Int
.usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() },
.isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() },
- .c_char_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.char) },
- .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short) },
- .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort) },
- .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int) },
- .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint) },
- .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long) },
- .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong) },
- .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong) },
- .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) },
- .c_longdouble_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longdouble) },
+ .c_char_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.char).? },
+ .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short).? },
+ .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort).? },
+ .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int).? },
+ .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint).? },
+ .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long).? },
+ .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong).? },
+ .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong).? },
+ .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong).? },
+ .c_longdouble_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longdouble).? },
// TODO revisit this when error sets support custom int types (comment taken from zig codebase)
.anyerror_type => return .{ .signedness = .unsigned, .bits = 16 },
@@ -3394,7 +3394,7 @@ pub fn floatBits(ip: *InternPool, ty: Index, target: std.Target) u16 {
.f64_type => 64,
.f80_type => 80,
.f128_type, .comptime_float_type => 128,
- .c_longdouble_type => target.cTypeBitSize(.longdouble),
+ .c_longdouble_type => target.cTypeBitSize(.longdouble).?,
else => unreachable,
};
diff --git a/src/analyser/error_msg.zig b/src/analyser/error_msg.zig
index 78fba2c60c..0f1f0d6857 100644
--- a/src/analyser/error_msg.zig
+++ b/src/analyser/error_msg.zig
@@ -92,6 +92,7 @@ pub const ErrorMsg = union(enum) {
.@"anyframe" => "anyframe",
.vector => "vector",
.enum_literal => "enum literal",
+ .spirv => "SPIR-V type",
};
try writer.print(
"expected {s} type, found '{f}'",
diff --git a/src/analyser/segmented_list.zig b/src/analyser/segmented_list.zig
index 6b8fd449c0..8771b1f421 100644
--- a/src/analyser/segmented_list.zig
+++ b/src/analyser/segmented_list.zig
@@ -123,7 +123,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
pub const prealloc_count = prealloc_item_count;
fn AtType(comptime SelfType: type) type {
- if (@typeInfo(SelfType).pointer.is_const) {
+ if (@typeInfo(SelfType).pointer.attrs.@"const") {
return *const T;
} else {
return *T;
diff --git a/src/ast.zig b/src/ast.zig
index 6ffa3d70f7..e2a2ba2011 100644
--- a/src/ast.zig
+++ b/src/ast.zig
@@ -22,31 +22,50 @@ fn fullPtrTypeComponents(tree: *const Ast, info: full.PtrType.Components) full.P
.allowzero_token = null,
.const_token = null,
.volatile_token = null,
+ .duplicate_token = null,
.ast = info,
};
// We need to be careful that we don't iterate over any sub-expressions
// here while looking for modifiers as that could result in false
// positives. Therefore, start after a sentinel if there is one and
// skip over any align node and bit range nodes.
- var i = if (info.sentinel.unwrap()) |sentinel| lastToken(tree, sentinel) + 1 else switch (size) {
- .many, .c => info.main_token + 1,
- else => info.main_token,
- };
+ var i = (if (info.sentinel.unwrap()) |sentinel| tree.lastToken(sentinel) + 1 else switch (size) {
+ .one => info.main_token,
+ .slice => info.main_token + 1,
+ .many => info.main_token + 2,
+ .c => info.main_token + 3,
+ }) + 1;
const end = tree.firstToken(info.child_type);
while (i < end) : (i += 1) {
switch (tree.tokenTag(i)) {
- .keyword_allowzero => result.allowzero_token = i,
- .keyword_const => result.const_token = i,
- .keyword_volatile => result.volatile_token = i,
+ .keyword_allowzero => {
+ if (result.allowzero_token != null) {
+ result.duplicate_token = i;
+ }
+ result.allowzero_token = i;
+ },
+ .keyword_const => {
+ if (result.const_token != null) {
+ result.duplicate_token = i;
+ }
+ result.const_token = i;
+ },
+ .keyword_volatile => {
+ if (result.volatile_token != null) {
+ result.duplicate_token = i;
+ }
+ result.volatile_token = i;
+ },
.keyword_align => {
- const align_node = info.align_node.unwrap().?;
if (info.bit_range_end.unwrap()) |bit_range_end| {
+ std.debug.assert(info.bit_range_start != .none);
i = lastToken(tree, bit_range_end) + 1;
- } else {
+ } else if (info.align_node.unwrap()) |align_node| {
i = lastToken(tree, align_node) + 1;
- }
+ } else unreachable;
},
- else => {},
+ .keyword_addrspace => i = lastToken(tree, info.addrspace_node.unwrap().?) + 1,
+ else => unreachable,
}
}
return result;
diff --git a/src/configuration.zig b/src/configuration.zig
index 0e3c44d9ab..138903bc70 100644
--- a/src/configuration.zig
+++ b/src/configuration.zig
@@ -97,8 +97,8 @@ pub const Manager = struct {
defer manager.impl.arena = arena_allocator.state;
var duped: UnresolvedConfig = .{};
- inline for (std.meta.fields(UnresolvedConfig)) |field| {
- @field(duped, field.name) = try option.dupe(field.type, @field(config, field.name), arena_allocator.allocator());
+ inline for (comptime std.meta.fieldNames(UnresolvedConfig), comptime std.meta.fieldTypes(UnresolvedConfig)) |field_name, FieldType| {
+ @field(duped, field_name) = try option.dupe(FieldType, @field(config, field_name), arena_allocator.allocator());
}
manager.impl.configs.set(tag, duped);
manager.impl.is_dirty = true;
@@ -111,8 +111,8 @@ pub const Manager = struct {
config: *const Config,
) error{OutOfMemory}!void {
var cfg: UnresolvedConfig = .{};
- inline for (std.meta.fields(Config)) |field| {
- @field(cfg, field.name) = @field(config, field.name);
+ inline for (comptime std.meta.fieldNames(Config)) |field_name| {
+ @field(cfg, field_name) = @field(config, field_name);
}
try manager.setConfiguration(tag, &cfg);
}
@@ -150,9 +150,9 @@ pub const Manager = struct {
.global_cache_path = if (builtin.os.tag == .wasi) "/cache" else null,
};
for (manager.impl.configs.values) |unresolved_config| {
- inline for (std.meta.fields(UnresolvedConfig)) |field| {
- if (@field(unresolved_config, field.name)) |new_value| {
- @field(config, field.name) = new_value;
+ inline for (comptime std.meta.fieldNames(UnresolvedConfig)) |field_name| {
+ if (@field(unresolved_config, field_name)) |new_value| {
+ @field(config, field_name) = new_value;
}
}
}
@@ -364,15 +364,15 @@ pub const Manager = struct {
var did_change: DidConfigChange = .{};
- inline for (std.meta.fields(Config)) |field| {
- const old_value = &@field(manager.config, field.name);
- const new_value = @field(config, field.name);
+ inline for (comptime std.meta.fieldNames(Config), comptime std.meta.fieldTypes(Config)) |field_name, FieldType| {
+ const old_value = &@field(manager.config, field_name);
+ const new_value = @field(config, field_name);
- const is_eql = option.eql(field.type, old_value.*, new_value);
- @field(did_change, field.name) = !is_eql;
+ const is_eql = option.eql(FieldType, old_value.*, new_value);
+ @field(did_change, field_name) = !is_eql;
if (!is_eql) {
- old_value.* = try option.dupe(field.type, new_value, arena_allocator.allocator());
+ old_value.* = try option.dupe(FieldType, new_value, arena_allocator.allocator());
}
}
@@ -698,11 +698,11 @@ comptime {
/// The same struct as `Config` but every field is optional.
pub const UnresolvedConfig = blk: {
const struct_info: std.lang.Type.Struct = @typeInfo(Config).@"struct";
- var field_types: [struct_info.fields.len]type = undefined;
- var field_attrs: [struct_info.fields.len]std.lang.Type.StructField.Attributes = undefined;
- for (&field_types, &field_attrs, struct_info.fields) |*ty, *attr, field| {
- ty.* = if (@typeInfo(field.type) != .optional) ?field.type else field.type;
- attr.* = .{ .default_value_ptr = &@as(ty.*, null) };
+ var field_types = struct_info.field_types[0..].*;
+ var field_attrs: [field_types.len]std.lang.Type.Struct.FieldAttributes = undefined;
+ for (&field_types, &field_attrs) |*field_type, *attr| {
+ field_type.* = if (@typeInfo(field_type.*) != .optional) ?field_type.* else field_type.*;
+ attr.* = .{ .default_value_ptr = &@as(field_type.*, null) };
}
break :blk @Struct(.auto, null, std.meta.fieldNames(Config), &field_types, &field_attrs);
};
diff --git a/src/features/code_actions.zig b/src/features/code_actions.zig
index eb334657f6..5a6a1c4f71 100644
--- a/src/features/code_actions.zig
+++ b/src/features/code_actions.zig
@@ -296,7 +296,7 @@ pub fn collectAutoDiscardDiagnostics(
.severity = .Information,
.code = null,
.source = "zls",
- .message = "auto discard for unused variable",
+ .message = .{ .string = "auto discard for unused variable" },
.relatedInformation = related_info,
});
}
@@ -1133,10 +1133,9 @@ const DiagnosticKind = union(enum) {
}
fn parseEnum(comptime T: type, message: []const u8) ?T {
- inline for (std.meta.fields(T)) |field| {
- if (std.mem.startsWith(u8, message, field.name)) {
- // is there a better way to achieve this?
- return @as(T, @enumFromInt(field.value));
+ inline for (comptime std.meta.fieldNames(T)) |field_name| {
+ if (std.mem.startsWith(u8, message, field_name)) {
+ return @field(T, field_name);
}
}
diff --git a/src/features/diagnostics.zig b/src/features/diagnostics.zig
index 58aac91c13..2690a9ae88 100644
--- a/src/features/diagnostics.zig
+++ b/src/features/diagnostics.zig
@@ -181,7 +181,7 @@ fn collectWarnStyleDiagnostics(
.severity = .Hint,
.code = .{ .string = "dot_slash_import" },
.source = "zls",
- .message = "A ./ is not needed in imports",
+ .message = .{ .string = "A ./ is not needed in imports" },
});
}
}
@@ -212,7 +212,7 @@ fn collectWarnStyleDiagnostics(
.severity = .Hint,
.code = .{ .string = "bad_style" },
.source = "zls",
- .message = "Functions should be camelCase",
+ .message = .{ .string = "Functions should be camelCase" },
});
} else if (is_type_function and !isPascalCase(func_name)) {
try diagnostics.append(arena, .{
@@ -220,7 +220,7 @@ fn collectWarnStyleDiagnostics(
.severity = .Hint,
.code = .{ .string = "bad_style" },
.source = "zls",
- .message = "Type functions should be PascalCase",
+ .message = .{ .string = "Type functions should be PascalCase" },
});
}
}
@@ -270,7 +270,7 @@ fn collectGlobalVarDiagnostics(
.severity = .Hint,
.code = .{ .string = "highlight_global_var_declarations" },
.source = "zls",
- .message = "Global var declaration",
+ .message = .{ .string = "Global var declaration" },
});
},
else => {},
diff --git a/src/features/document_symbol.zig b/src/features/document_symbol.zig
index 6212f3bed6..ce8c85f1ba 100644
--- a/src/features/document_symbol.zig
+++ b/src/features/document_symbol.zig
@@ -69,8 +69,8 @@ pub fn getDocumentSymbols(
},
};
- try stack.append(arena, stack.getLast().?);
- const stack_entry: *StackEntry = &stack.items[stack.items.len - 1];
+ try stack.append(arena, stack.last().?);
+ const stack_entry = stack.lastPtr().?;
const symbol: Symbol = switch (tree.nodeTag(node)) {
.global_var_decl,
diff --git a/src/features/inlay_hints.zig b/src/features/inlay_hints.zig
index de5cae7966..ad2ec842a5 100644
--- a/src/features/inlay_hints.zig
+++ b/src/features/inlay_hints.zig
@@ -49,6 +49,7 @@ const excluded_builtins_set: std.EnumArray(std.zig.BuiltinFn.Tag, bool) = .init(
.c_va_copy = false,
.c_va_end = false,
.c_va_start = false,
+ .div_ceil = true,
.div_exact = true,
.div_floor = true,
.div_trunc = true,
@@ -73,6 +74,8 @@ const excluded_builtins_set: std.EnumArray(std.zig.BuiltinFn.Tag, bool) = .init(
.import = true,
.in_comptime = true, // no parameters
.int_cast = true,
+ .backing_int = true,
+ .from_backing_int = true,
.enum_from_int = true,
.error_from_int = true,
.float_from_int = true,
@@ -132,6 +135,7 @@ const excluded_builtins_set: std.EnumArray(std.zig.BuiltinFn.Tag, bool) = .init(
.Struct = false,
.Union = false,
.Enum = false,
+ .SpirvType = true,
.type_info = true,
.type_name = true,
.TypeOf = true, // variadic
diff --git a/src/features/semantic_tokens.zig b/src/features/semantic_tokens.zig
index 909ccbe76e..b371a3319f 100644
--- a/src/features/semantic_tokens.zig
+++ b/src/features/semantic_tokens.zig
@@ -63,14 +63,14 @@ pub const TokenModifiers = packed struct(u16) {
pub fn format(modifiers: TokenModifiers, writer: *std.Io.Writer) std.Io.Writer.Error!void {
try writer.writeAll(".{");
var i: usize = 0;
- inline for (std.meta.fields(TokenModifiers)) |field| {
- if ((comptime !std.mem.eql(u8, field.name, "_")) and @field(modifiers, field.name)) {
+ inline for (comptime std.meta.fieldNames(TokenModifiers)) |field_name| {
+ if ((comptime !std.mem.eql(u8, field_name, "_")) and @field(modifiers, field_name)) {
if (i == 0) {
try writer.writeAll(" .");
} else {
try writer.writeAll(", .");
}
- try writer.writeAll(field.name);
+ try writer.writeAll(field_name);
try writer.writeAll(" = true");
i += 1;
}
diff --git a/src/main.zig b/src/main.zig
index 7979b1b538..27582cb63b 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -46,7 +46,7 @@ var log_transport: ?*zls.lsp.Transport = null;
var log_stderr: bool = true;
/// Log messages to the given file.
var log_file: ?std.Io.File = null;
-var log_level: std.log.Level = if (zig_builtin.mode == .Debug) .debug else .info;
+var log_level: std.log.Level = if (zig_builtin.mode == .debug) .debug else .info;
fn logFn(
comptime level: std.log.Level,
diff --git a/src/offsets.zig b/src/offsets.zig
index 6b77589810..e34d13044f 100644
--- a/src/offsets.zig
+++ b/src/offsets.zig
@@ -177,7 +177,7 @@ pub fn sourceIndexToTokenIndex(tree: *const Ast, source_index: usize) SourceInde
}
test sourceIndexToTokenIndex {
- var tree: Ast = try .parse(std.testing.allocator, " a bb; ", .zig);
+ var tree: Ast = try .parse(std.testing.allocator, " a bb; ", .{});
defer tree.deinit(std.testing.allocator);
try std.testing.expectEqualSlices(
@@ -203,7 +203,7 @@ test sourceIndexToTokenIndex {
}
test "sourceIndexToTokenIndex - token at end" {
- var tree: Ast = try .parse(std.testing.allocator, " a", .zig);
+ var tree: Ast = try .parse(std.testing.allocator, " a", .{});
defer tree.deinit(std.testing.allocator);
try std.testing.expectEqualSlices(
@@ -327,7 +327,7 @@ pub fn identifierTokenAndLocFromIndex(tree: *const Ast, source_index: usize) ?st
test identifierLocFromIndex {
var tree = try Ast.parse(std.testing.allocator,
\\ name @builtin @"escaped" @"s p a c e" end
- , .zig);
+ , .{});
defer tree.deinit(std.testing.allocator);
try std.testing.expectEqualSlices(
@@ -441,7 +441,7 @@ test tokenToLoc {
}
fn testTokenToLoc(text: [:0]const u8, token_index: Ast.TokenIndex, start: usize, end: usize) !void {
- var tree = try Ast.parse(std.testing.allocator, text, .zig);
+ var tree = try Ast.parse(std.testing.allocator, text, .{});
defer tree.deinit(std.testing.allocator);
const actual = tokenToLoc(&tree, token_index);
diff --git a/src/print_ast.zig b/src/print_ast.zig
index ae44fc5188..a0115ba437 100644
--- a/src/print_ast.zig
+++ b/src/print_ast.zig
@@ -858,7 +858,9 @@ pub fn main(init: std.process.Init) !u8 {
defer gpa.free(source);
const mode: Ast.Mode = if (std.mem.endsWith(u8, file_path, ".zon")) .zon else .zig;
- var tree: Ast = try .parse(gpa, source, mode);
+ var tree: Ast = try .parse(gpa, source, .{
+ .mode = mode,
+ });
defer tree.deinit(gpa);
try renderToFile(
@@ -879,7 +881,7 @@ test PrintAst {
\\}
;
- var tree: Ast = try .parse(std.testing.allocator, source, .zig);
+ var tree: Ast = try .parse(std.testing.allocator, source, .{});
defer tree.deinit(std.testing.allocator);
var aw: std.Io.Writer.Allocating = .init(std.testing.allocator);
@@ -936,7 +938,7 @@ test PrintAst {
const printed_source = try aw.toOwnedSliceSentinel(0);
defer std.testing.allocator.free(printed_source);
- var printed_tree: Ast = try .parse(std.testing.allocator, printed_source, .zig);
+ var printed_tree: Ast = try .parse(std.testing.allocator, printed_source, .{});
defer printed_tree.deinit(std.testing.allocator);
try std.testing.expectEqual(0, printed_tree.errors.len);
diff --git a/src/tools/config_gen.zig b/src/tools/config_gen.zig
index adebe8b859..02899d1920 100644
--- a/src/tools/config_gen.zig
+++ b/src/tools/config_gen.zig
@@ -167,7 +167,7 @@ fn generateConfigFile(
const source_unformatted = try aw.toOwnedSliceSentinel(0);
defer allocator.free(source_unformatted);
- var tree: std.zig.Ast = try .parse(allocator, source_unformatted, .zig);
+ var tree: std.zig.Ast = try .parse(allocator, source_unformatted, .{});
defer tree.deinit(allocator);
std.debug.assert(tree.errors.len == 0);
diff --git a/tests/ErrorBuilder.zig b/tests/ErrorBuilder.zig
index b4cebee2c2..8a434d6f86 100644
--- a/tests/ErrorBuilder.zig
+++ b/tests/ErrorBuilder.zig
@@ -107,7 +107,7 @@ pub fn removeUnusedFiles(builder: *ErrorBuilder) void {
//
fn assertFmt(ok: bool, comptime fmt_str: []const u8, args: anytype) void {
- if (builtin.mode == .Debug or builtin.is_test) {
+ if (builtin.mode == .debug or builtin.is_test) {
if (!ok) std.debug.panic(fmt_str, args);
} else {
std.debug.assert(ok);
diff --git a/tests/analysis/builtins.zig b/tests/analysis/builtins.zig
index 4d575fcf07..9955d3dd44 100644
--- a/tests/analysis/builtins.zig
+++ b/tests/analysis/builtins.zig
@@ -181,21 +181,21 @@ const Struct = @Struct(.auto, undefined, &.{"foo"}, &.{i32}, &.{.{}});
// ^^^^^^ (type)()
// ^^^^^ (ContainerLayout)()
// ^ ([1][]const u8)()
-// ^ ([?]Attributes)()
-// ^ (Attributes)()
+// ^ ([?]FieldAttributes)()
+// ^ (FieldAttributes)()
const Union = @Union(.auto, undefined, &.{"foo"}, &.{i32}, &.{.{}});
// ^^^^^ (type)()
// ^^^^^ (ContainerLayout)()
// ^ ([1][]const u8)()
-// ^ ([?]Attributes)()
-// ^ (Attributes)()
+// ^ ([?]FieldAttributes)()
+// ^ (FieldAttributes)()
const Enum = @Enum(undefined, .exhaustive, undefined, undefined);
// ^^^^ (type)()
// ^^^^^^^^^^^ (Mode)()
const Fn = @Fn(&.{i32}, &.{.{}}, undefined, .{});
// ^^ (type)()
-// ^ ([?]Attributes)()
-// ^ (Attributes)()
+// ^ ([?]ParamAttributes)()
+// ^ (ParamAttributes)()
// ^ (Attributes)()
const type_enum_literal: @EnumLiteral() = .foo;
diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig
index 5dcdca0b78..71175dcffd 100644
--- a/tests/lsp_features/completion.zig
+++ b/tests/lsp_features/completion.zig
@@ -3173,7 +3173,7 @@ test "builtin fns return type" {
.{ .label = "null", .kind = .Field, .detail = "void" },
.{ .label = "optional", .kind = .Field, .detail = "Optional" },
.{ .label = "error_union", .kind = .Field, .detail = "ErrorUnion" },
- .{ .label = "error_set", .kind = .Field, .detail = "?[]const Error" },
+ .{ .label = "error_set", .kind = .Field, .detail = "ErrorSet" },
.{ .label = "@\"enum\"", .kind = .Field, .detail = "Enum" },
.{ .label = "@\"union\"", .kind = .Field, .detail = "Union" },
.{ .label = "@\"fn\"", .kind = .Field, .detail = "Fn" },
@@ -3182,6 +3182,7 @@ test "builtin fns return type" {
.{ .label = "@\"anyframe\"", .kind = .Field, .detail = "AnyFrame" },
.{ .label = "vector", .kind = .Field, .detail = "Vector" },
.{ .label = "enum_literal", .kind = .Field, .detail = "void" },
+ .{ .label = "spirv", .kind = .Struct, .detail = "Spirv" }, // why is only this .Struct?
});
}
diff --git a/tests/lsp_features/diagnostics.zig b/tests/lsp_features/diagnostics.zig
index 286f00c453..7538da6983 100644
--- a/tests/lsp_features/diagnostics.zig
+++ b/tests/lsp_features/diagnostics.zig
@@ -25,7 +25,7 @@ test "ast error" {
},
.severity = .Error,
.source = "zls",
- .message = "expected ';' after declaration",
+ .message = .{ .string = "expected ';' after declaration" },
},
}, .{});
}
@@ -44,7 +44,7 @@ test "ast-check" {
.severity = .Error,
.tags = &.{.Unnecessary},
.source = "zls",
- .message = "unused local constant",
+ .message = .{ .string = "unused local constant" },
},
}, .{});
@@ -61,7 +61,7 @@ test "ast-check" {
.severity = .Error,
.tags = &.{.Unnecessary},
.source = "zls",
- .message = "unused local variable",
+ .message = .{ .string = "unused local variable" },
},
}, .{});
}
@@ -78,7 +78,7 @@ test "warn style" {
.code = .{ .string = "dot_slash_import" },
.severity = .Hint,
.source = "zls",
- .message = "A ./ is not needed in imports",
+ .message = .{ .string = "A ./ is not needed in imports" },
},
}, .{ .warn_style = true });
try testDiagnostics(
@@ -93,7 +93,7 @@ test "warn style" {
.code = .{ .string = "bad_style" },
.severity = .Hint,
.source = "zls",
- .message = "Functions should be camelCase",
+ .message = .{ .string = "Functions should be camelCase" },
},
.{
.range = .{
@@ -103,7 +103,7 @@ test "warn style" {
.code = .{ .string = "bad_style" },
.severity = .Hint,
.source = "zls",
- .message = "Type functions should be PascalCase",
+ .message = .{ .string = "Type functions should be PascalCase" },
},
}, .{ .warn_style = true });
}
@@ -120,7 +120,7 @@ test "highlight global var decls" {
.code = .{ .string = "highlight_global_var_declarations" },
.severity = .Hint,
.source = "zls",
- .message = "Global var declaration",
+ .message = .{ .string = "Global var declaration" },
},
}, .{ .highlight_global_var_declarations = true });
}
@@ -139,7 +139,7 @@ test "autofix comment" {
},
.severity = .Information,
.source = "zls",
- .message = "auto discard for unused variable",
+ .message = .{ .string = "auto discard for unused variable" },
.relatedInformation = &.{
.{
.location = .{
diff --git a/tests/utility/ast.zig b/tests/utility/ast.zig
index c9bbacc9bd..7049155eaf 100644
--- a/tests/utility/ast.zig
+++ b/tests/utility/ast.zig
@@ -85,7 +85,7 @@ fn testNodesAtLoc(source: []const u8) !void {
const new_source = try allocator.dupeSentinel(u8, ccp.new_source, 0);
defer allocator.free(new_source);
- var tree: std.zig.Ast = try .parse(allocator, new_source, .zig);
+ var tree: std.zig.Ast = try .parse(allocator, new_source, .{});
defer tree.deinit(allocator);
const nodes = try ast.nodesAtLoc(allocator, &tree, inner_loc);
diff --git a/tests/utility/position_context.zig b/tests/utility/position_context.zig
index 0bc2eb7537..e42260efb9 100644
--- a/tests/utility/position_context.zig
+++ b/tests/utility/position_context.zig
@@ -731,7 +731,7 @@ fn testContext(source: []const u8, expected_tag: std.meta.Tag(Analyser.PositionC
const new_source = try allocator.dupeSentinel(u8, phr.new_source, 0);
defer allocator.free(new_source);
- var tree: std.zig.Ast = try .parse(allocator, new_source, .zig);
+ var tree: std.zig.Ast = try .parse(allocator, new_source, .{});
defer tree.deinit(allocator);
const ctx = try Analyser.getPositionContext(allocator, &tree, cursor_index, lookahead);
From 8c7fac463f982f46fbd64aad822066b75d603b79 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Fri, 4 Sep 2026 16:51:58 +0200
Subject: [PATCH 08/14] remove build runner
won't be missed
---
.github/workflows/build_runner.yml | 46 -
build.zig | 8 +-
schema.json | 5 -
src/Config.zig | 3 -
src/DocumentStore.zig | 136 +-
src/Server.zig | 27 +-
src/build_runner/build_runner.zig | 1243 -----------------
src/build_runner/check.zig | 283 ----
src/build_runner/shared.zig | 158 ---
src/configuration.zig | 364 ++++-
src/features/diagnostics.zig | 252 ----
src/tools/config.json | 6 -
src/zls.zig | 3 -
tests/add_build_runner_cases.zig | 78 --
tests/analysis_check.zig | 1 -
tests/build_runner_cases/add_module.json | 14 -
tests/build_runner_cases/add_module.zig | 7 -
tests/build_runner_cases/add_options.json | 19 -
tests/build_runner_cases/add_options.zig | 11 -
tests/build_runner_cases/empty.json | 10 -
tests/build_runner_cases/empty.zig | 5 -
.../module_root_source_file_collision.json | 31 -
.../module_root_source_file_collision.zig | 37 -
.../module_self_import.json | 16 -
.../build_runner_cases/module_self_import.zig | 9 -
.../multiple_module_import_names.json | 27 -
.../multiple_module_import_names.zig | 18 -
.../no_root_source_file.json | 19 -
.../no_root_source_file.zig | 19 -
.../public_module_with_generated_file.json | 14 -
.../public_module_with_generated_file.zig | 10 -
tests/build_runner_check.zig | 113 --
32 files changed, 306 insertions(+), 2686 deletions(-)
delete mode 100644 .github/workflows/build_runner.yml
delete mode 100644 src/build_runner/build_runner.zig
delete mode 100644 src/build_runner/check.zig
delete mode 100644 src/build_runner/shared.zig
delete mode 100644 tests/add_build_runner_cases.zig
delete mode 100644 tests/build_runner_cases/add_module.json
delete mode 100644 tests/build_runner_cases/add_module.zig
delete mode 100644 tests/build_runner_cases/add_options.json
delete mode 100644 tests/build_runner_cases/add_options.zig
delete mode 100644 tests/build_runner_cases/empty.json
delete mode 100644 tests/build_runner_cases/empty.zig
delete mode 100644 tests/build_runner_cases/module_root_source_file_collision.json
delete mode 100644 tests/build_runner_cases/module_root_source_file_collision.zig
delete mode 100644 tests/build_runner_cases/module_self_import.json
delete mode 100644 tests/build_runner_cases/module_self_import.zig
delete mode 100644 tests/build_runner_cases/multiple_module_import_names.json
delete mode 100644 tests/build_runner_cases/multiple_module_import_names.zig
delete mode 100644 tests/build_runner_cases/no_root_source_file.json
delete mode 100644 tests/build_runner_cases/no_root_source_file.zig
delete mode 100644 tests/build_runner_cases/public_module_with_generated_file.json
delete mode 100644 tests/build_runner_cases/public_module_with_generated_file.zig
delete mode 100644 tests/build_runner_check.zig
diff --git a/.github/workflows/build_runner.yml b/.github/workflows/build_runner.yml
deleted file mode 100644
index 58d424464e..0000000000
--- a/.github/workflows/build_runner.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: BuildRunner
-
-on:
- push:
- branches:
- - master
- pull_request:
- paths:
- - ".github/workflows/build_runner.yml"
- - "src/build_runner/**"
- # Ensure that the build runner checks get run when the minimum Zig version gets modified
- - "build.zig"
- - "build.zig.zon"
- workflow_dispatch:
-
-jobs:
- check_build_runner:
- # re-enable once there is a tagged release or mach nominated version that the build runner supports
- if: false
- strategy:
- fail-fast: false
- matrix:
- include:
- - zig-version: 0.16.0
- # - zig-version: mach-latest
- # Zig master is handled by 'zig build test' in the main CI
- runs-on: ubuntu-22.04
- timeout-minutes: 10
-
- steps:
- - uses: actions/checkout@v4
-
- - uses: mlugg/setup-zig@v2
- with:
- version: ${{ matrix.zig-version }}
-
- - name: Create temp zig project
- run: |
- mkdir $RUNNER_TEMP/TEMP_ZIG_PROJECT
- cd $RUNNER_TEMP/TEMP_ZIG_PROJECT
- zig init
-
- - name: Check build_runner builds
- run: |
- cd $RUNNER_TEMP/TEMP_ZIG_PROJECT
- zig build --build-runner $GITHUB_WORKSPACE/src/build_runner/build_runner.zig
diff --git a/build.zig b/build.zig
index 2ded4914b5..9caeee023e 100644
--- a/build.zig
+++ b/build.zig
@@ -256,13 +256,11 @@ pub fn build(b: *Build) !void {
.use_lld = use_llvm,
});
- blk: { // zig build test, zig build test-build-runner, zig build test-analysis
+ blk: { // zig build test, zig build test-analysis
const test_step = b.step("test", "Run all the tests");
- const test_build_runner_step = b.step("test-build-runner", "Run all the build runner tests");
const test_analysis_step = b.step("test-analysis", "Run all the analysis tests");
// Create run steps
- @import("tests/add_build_runner_cases.zig").addCases(b, test_build_runner_step, test_filters);
@import("tests/add_analysis_cases.zig").addCases(b, target, optimize, test_analysis_step, test_filters);
const run_tests = b.addRunArtifact(tests);
@@ -277,7 +275,6 @@ pub fn build(b: *Build) !void {
test_step.dependOn(&run_tests.step);
test_step.dependOn(&run_src_tests.step);
test_step.dependOn(test_analysis_step);
- if (target.query.eql(b.graph.host.query)) test_step.dependOn(test_build_runner_step);
if (!coverage) break :blk;
@@ -285,9 +282,6 @@ pub fn build(b: *Build) !void {
var run_test_steps: std.ArrayList(*std.Build.Step.Run) = .empty;
run_test_steps.append(b.allocator, run_tests) catch @panic("OOM");
run_test_steps.append(b.allocator, run_src_tests) catch @panic("OOM");
- for (test_build_runner_step.dependencies.items) |step| {
- run_test_steps.append(b.allocator, step.cast(std.Build.Step.Run).?) catch @panic("OOM");
- }
for (test_analysis_step.dependencies.items) |step| {
run_test_steps.append(b.allocator, step.cast(std.Build.Step.Run).?) catch @panic("OOM");
}
diff --git a/schema.json b/schema.json
index c7ee96244a..8d79c3edf8 100644
--- a/schema.json
+++ b/schema.json
@@ -117,11 +117,6 @@
"type": "string",
"default": null
},
- "build_runner_path": {
- "description": "Specify a custom build runner to resolve build system information.",
- "type": "string",
- "default": null
- },
"global_cache_path": {
"description": "Path to a directory that will be used as zig's cache. Will default to `${KnownFolders.Cache}/zls`.",
"type": "string",
diff --git a/src/Config.zig b/src/Config.zig
index 846e0e73b9..fd576eb4bb 100644
--- a/src/Config.zig
+++ b/src/Config.zig
@@ -75,9 +75,6 @@ zig_lib_path: ?[]const u8 = null,
/// Specify the path to the Zig executable (not the directory). If unset, zig is looked up in `PATH`. e.g. `/path/to/zig-templeos-armless-1.0.0/zig`.
zig_exe_path: ?[]const u8 = null,
-/// Specify a custom build runner to resolve build system information.
-build_runner_path: ?[]const u8 = null,
-
/// Path to a directory that will be used as zig's cache. Will default to `${KnownFolders.Cache}/zls`.
global_cache_path: ?[]const u8 = null,
diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig
index 3e7f4f5bfc..3ae09102c8 100644
--- a/src/DocumentStore.zig
+++ b/src/DocumentStore.zig
@@ -9,12 +9,13 @@ const log = std.log.scoped(.store);
const lsp = @import("lsp");
const Ast = std.zig.Ast;
const BuildAssociatedConfig = @import("BuildAssociatedConfig.zig");
-pub const BuildConfig = @import("build_runner/shared.zig").BuildConfig;
const tracy = @import("tracy");
const DocumentScope = @import("DocumentScope.zig");
const DiagnosticsCollection = @import("DiagnosticsCollection.zig");
const TrigramStore = @import("TrigramStore.zig");
+const BuildConfig = @compileError("https://github.com/zigtools/zls/issues/3208");
+
const DocumentStore = @This();
io: std.Io,
@@ -34,13 +35,13 @@ lsp_capabilities: struct {
supports_inlay_hints_refresh: bool = false,
} = .{},
-pub const supports_build_system = std.process.can_spawn;
+/// https://github.com/zigtools/zls/issues/3208
+pub const supports_build_system = false and std.process.can_spawn;
pub const Config = struct {
environ_map: *const std.process.Environ.Map,
zig_exe_path: ?[]const u8,
zig_lib_dir: ?std.Build.Cache.Directory,
- build_runner_path: ?[]const u8,
builtin_path: ?[]const u8,
global_cache_dir: ?std.Build.Cache.Directory,
wasi_preopens: switch (builtin.os.tag) {
@@ -918,7 +919,6 @@ pub fn invalidateBuildFile(self: *DocumentStore, build_file_uri: Uri) void {
comptime std.debug.assert(supports_build_system);
if (self.config.zig_exe_path == null) return;
- if (self.config.build_runner_path == null) return;
if (self.config.global_cache_dir == null) return;
if (self.config.zig_lib_dir == null) return;
@@ -1171,6 +1171,7 @@ fn invalidateBuildFileWorker(self: *DocumentStore, build_file: *BuildFile) std.I
build_file.impl.version += 1;
const new_version = build_file.impl.version;
+ const loadBuildConfiguration = if (true) @compileError("https://github.com/zigtools/zls/issues/3208");
const build_config = loadBuildConfiguration(self, build_file.uri, new_version) catch |err| switch (err) {
error.Canceled => return error.Canceled,
else => |e| {
@@ -1282,133 +1283,6 @@ fn loadBuildAssociatedConfiguration(io: std.Io, allocator: std.mem.Allocator, bu
);
}
-fn prepareBuildRunnerArgs(self: *DocumentStore, build_file_uri: Uri) error{OutOfMemory}![][]const u8 {
- const tracy_zone = tracy.trace(@src());
- defer tracy_zone.end();
-
- const base_args = &[_][]const u8{
- self.config.zig_exe_path.?,
- "build",
- "--build-runner",
- self.config.build_runner_path.?,
- "--zig-lib-dir",
- self.config.zig_lib_dir.?.path orelse ".",
- };
-
- var args: std.ArrayList([]const u8) = try .initCapacity(self.allocator, base_args.len);
- errdefer {
- for (args.items) |arg| self.allocator.free(arg);
- args.deinit(self.allocator);
- }
-
- for (base_args) |arg| {
- args.appendAssumeCapacity(try self.allocator.dupe(u8, arg));
- }
-
- if (self.getBuildFile(build_file_uri)) |build_file| blk: {
- const build_config = build_file.build_associated_config orelse break :blk;
- const build_options = build_config.value.build_options orelse break :blk;
-
- try args.ensureUnusedCapacity(self.allocator, build_options.len);
- for (build_options) |option| {
- args.appendAssumeCapacity(try option.formatParam(self.allocator));
- }
- }
-
- return try args.toOwnedSlice(self.allocator);
-}
-
-/// Runs the build.zig and extracts include directories and packages
-fn loadBuildConfiguration(self: *DocumentStore, build_file_uri: Uri, build_file_version: u32) !std.json.Parsed(BuildConfig) {
- const tracy_zone = tracy.trace(@src());
- defer tracy_zone.end();
-
- std.debug.assert(self.config.zig_exe_path != null);
- std.debug.assert(self.config.build_runner_path != null);
- std.debug.assert(self.config.global_cache_dir != null);
- std.debug.assert(self.config.zig_lib_dir != null);
-
- const build_file_path = try build_file_uri.toFsPath(self.allocator);
- defer self.allocator.free(build_file_path);
-
- const cwd = std.Io.Dir.path.dirname(build_file_path).?;
-
- const args = try self.prepareBuildRunnerArgs(build_file_uri);
- defer {
- for (args) |arg| self.allocator.free(arg);
- self.allocator.free(args);
- }
-
- const zig_run_result = blk: {
- const tracy_zone2 = tracy.trace(@src());
- defer tracy_zone2.end();
- break :blk try std.process.run(
- self.allocator,
- self.io,
- .{
- .argv = args,
- .cwd = .{ .path = cwd },
- .stderr_limit = .limited(16 * 1024 * 1024),
- .stdout_limit = .limited(16 * 1024 * 1024),
- },
- );
- };
- defer self.allocator.free(zig_run_result.stdout);
- defer self.allocator.free(zig_run_result.stderr);
-
- const is_ok = switch (zig_run_result.term) {
- .exited => |exit_code| exit_code == 0,
- else => false,
- };
-
- const diagnostic_tag: DiagnosticsCollection.Tag = tag: {
- var hasher: std.hash.Wyhash = .init(47); // Chosen by the following prompt: Pwease give a wandom nyumbew
- hasher.update(build_file_uri.raw);
- break :tag @enumFromInt(@as(u32, @truncate(hasher.final())));
- };
-
- if (!is_ok) {
- const joined = try std.mem.join(self.allocator, " ", args);
- defer self.allocator.free(joined);
-
- log.err(
- "Failed to execute build runner to collect build configuration, command:\ncd {s};{s}\nError: {s}",
- .{ cwd, joined, zig_run_result.stderr },
- );
-
- var error_bundle = try @import("features/diagnostics.zig").getErrorBundleFromStderr(
- self.allocator,
- zig_run_result.stderr,
- false,
- .{ .dynamic = .{ .document_store = self, .base_path = cwd } },
- );
- defer error_bundle.deinit(self.allocator);
-
- try self.diagnostics_collection.pushErrorBundle(diagnostic_tag, build_file_version, cwd, error_bundle);
- try self.diagnostics_collection.publishDiagnostics();
- return error.RunFailed;
- } else {
- try self.diagnostics_collection.pushErrorBundle(diagnostic_tag, build_file_version, null, .empty);
- try self.diagnostics_collection.publishDiagnostics();
- }
-
- const parse_options: std.json.ParseOptions = .{
- // We ignore unknown fields so people can roll
- // their own build runners in libraries with
- // the only requirement being general adherence
- // to the BuildConfig type
- .ignore_unknown_fields = true,
- .allocate = .alloc_always,
- };
-
- return std.json.parseFromSlice(
- BuildConfig,
- self.allocator,
- zig_run_result.stdout,
- parse_options,
- ) catch return error.InvalidBuildConfig;
-}
-
/// Checks if the build.zig file is accessible in dir.
fn buildDotZigExists(io: std.Io, dir_path: []const u8) std.Io.Cancelable!bool {
var dir = std.Io.Dir.openDirAbsolute(io, dir_path, .{}) catch |err| switch (err) {
diff --git a/src/Server.zig b/src/Server.zig
index 18b3c4d07c..b96966e24d 100644
--- a/src/Server.zig
+++ b/src/Server.zig
@@ -20,7 +20,6 @@ const diff = @import("diff.zig");
const Uri = @import("Uri.zig");
const InternPool = @import("analyser/analyser.zig").InternPool;
const DiagnosticsCollection = @import("DiagnosticsCollection.zig");
-const build_runner_shared = @import("build_runner/shared.zig");
const signature_help = @import("features/signature_help.zig");
const references = @import("features/references.zig");
@@ -35,8 +34,12 @@ const hover_handler = @import("features/hover.zig");
const selection_range = @import("features/selection_range.zig");
const diagnostics_gen = @import("features/diagnostics.zig");
-const BuildOnSave = diagnostics_gen.BuildOnSave;
-const BuildOnSaveSupport = build_runner_shared.BuildOnSaveSupport;
+const BuildOnSave = @compileError("https://github.com/zigtools/zls/issues/3208");
+const BuildOnSaveSupport = struct {
+ pub inline fn isSupportedComptime() bool {
+ return false;
+ }
+};
const log = std.log.scoped(.server);
@@ -825,7 +828,6 @@ const Workspace = struct {
const zig_exe_path = config.zig_exe_path orelse return;
const zig_lib_path = config.zig_lib_path orelse return;
- const build_runner_path = config.build_runner_path orelse return;
const workspace_path = workspace.uri.toFsPath(args.server.allocator) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
@@ -842,7 +844,6 @@ const Workspace = struct {
.check_step_only = config.enable_build_on_save == null,
.zig_exe_path = zig_exe_path,
.zig_lib_path = zig_lib_path,
- .build_runner_path = build_runner_path,
.collection = &args.server.diagnostics_collection,
}) catch |err| switch (err) {
error.Canceled => return error.Canceled,
@@ -994,7 +995,6 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
const new_zig_exe_path: bool = result.did_change.zig_exe_path;
const new_zig_lib_path: bool = result.did_change.zig_lib_path;
- const new_build_runner_path: bool = result.did_change.build_runner_path;
const new_enable_build_on_save: bool = result.did_change.enable_build_on_save;
const new_build_on_save_args: bool = result.did_change.build_on_save_args;
const new_force_autofix: bool = result.did_change.force_autofix;
@@ -1010,7 +1010,6 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
const should_restart =
new_zig_exe_path or
new_zig_lib_path or
- new_build_runner_path or
new_enable_build_on_save or
new_build_on_save_args;
@@ -1023,7 +1022,7 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
}
if (DocumentStore.supports_build_system) {
- if (new_zig_exe_path or new_zig_lib_path or new_build_runner_path) {
+ if (new_zig_exe_path or new_zig_lib_path) {
for (server.document_store.build_files.keys()) |build_file_uri| {
server.document_store.invalidateBuildFile(build_file_uri);
}
@@ -1060,12 +1059,11 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
check: {
if (server.status != .initialized) break :check;
- switch (server.config_manager.build_runner_supported) {
- .yes, .no_dont_error => break :check,
- .no => {},
- }
+ const zig_exe = server.config_manager.zig_exe orelse break :check;
+
+ if (zig_exe.supported) break :check;
- const zig_version = server.config_manager.zig_exe.?.version;
+ const zig_version = zig_exe.version;
const zls_version = build_options.version;
const zig_version_is_tagged = zig_version.pre == null;
@@ -1100,8 +1098,6 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void
log.warn("'enable_build_on_save' is ignored because Zig could not be found", .{});
} else if (!server.client_capabilities.supports_publish_diagnostics) {
log.warn("'enable_build_on_save' is ignored because it is not supported by {s}", .{server.client_capabilities.client_name orelse "your editor"});
- } else if (server.status == .initialized and server.config_manager.build_runner_supported == .no and server.config_manager.config.build_runner_path == null) {
- log.warn("'enable_build_on_save' is ignored because no build runner is available", .{});
} else if (server.status == .initialized and server.config_manager.zig_exe != null) {
switch (BuildOnSaveSupport.isSupportedRuntime(server.config_manager.zig_exe.?.version)) {
.supported => {},
@@ -1131,7 +1127,6 @@ fn createDocumentStoreConfig(config_manager: *const configuration.Manager) Docum
.environ_map = config_manager.environ_map,
.zig_exe_path = config_manager.config.zig_exe_path,
.zig_lib_dir = config_manager.zig_lib_dir,
- .build_runner_path = config_manager.config.build_runner_path,
.builtin_path = config_manager.config.builtin_path,
.global_cache_dir = config_manager.global_cache_dir,
.wasi_preopens = config_manager.wasi_preopens,
diff --git a/src/build_runner/build_runner.zig b/src/build_runner/build_runner.zig
deleted file mode 100644
index e78338379c..0000000000
--- a/src/build_runner/build_runner.zig
+++ /dev/null
@@ -1,1243 +0,0 @@
-//! PLEASE READ THE FOLLOWING MESSAGE BEFORE EDITING THIS FILE:
-//!
-//! To support the Zig build system, ZLS uses a custom build runner which
-//! interfaces with the compiler that is installed on the user's system.
-//!
-//! The Zig version on the user's system may not be the same version that ZLS
-//! has been built with. The minimum runtime Zig version in the `build.zig`
-//! specifies which Zig versions need to be supported by this build runner.
-//!
-//! When a breaking change occurs, this is what need to be done:
-//! - If it is a tagged release (e.g 0.x.y) , update the build runner code to handle multiple Zig versions.
-//! - If it is a development version (e.g 0.x.y-dev), bump the minimum runtime Zig version.
-//!
-//! Handling multiple Zig versions can be achieved with one of the following strategies:
-//! - use `@hasDecl` or `@hasField` (recommended)
-//! - use `builtin.zig_version`
-//!
-//! The build runner can be tested on ZLS's `build.zig` with the following command:
-//! `zig build --build-runner src/build_runner/build_runner.zig`
-//!
-//! To test with a different `build.zig`, use one of the following commands:
-//! `zig build --build-file /path/to/build.zig --build-runner /path/to/zls/src/build_runner/build_runner.zig`
-//! `zig build --build-runner /path/to/zls/src/build_runner/build_runner.zig` (if the cwd contains build.zig)
-//!
-
-const std = @import("std");
-const builtin = @import("builtin");
-const Io = std.Io;
-const assert = std.debug.assert;
-const mem = std.mem;
-const process = std.process;
-const File = Io.File;
-const Step = std.Build.Step;
-const Allocator = std.mem.Allocator;
-const fatal = std.process.fatal;
-const runner = @This();
-
-pub const root = @import("@build");
-pub const dependencies = @import("@dependencies");
-
-pub const std_options: std.Options = .{
- .side_channels_mitigations = .none,
- .http_disable_tls = true,
- .networking = false,
-};
-
-pub fn main(init: process.Init.Minimal) !void {
- // The build runner is often short-lived, but thanks to `--watch` and `--webui`, that's not
- // always the case. So, we do need a true gpa for some things.
- var debug_gpa_state: std.heap.DebugAllocator(.{}) = .init;
- defer _ = debug_gpa_state.deinit();
- const gpa = debug_gpa_state.allocator();
-
- var threaded: Io.Threaded = .init(gpa, .{
- .environ = init.environ,
- .argv0 = .init(init.args),
- });
- defer threaded.deinit();
- const io = threaded.io();
-
- // ...but we'll back our arena by `std.heap.page_allocator` for efficiency.
- var arena_instance: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- const args = try init.args.toSlice(arena);
-
- // skip my own exe name
- var arg_idx: usize = 1;
-
- const zig_exe = nextArg(args, &arg_idx) orelse fatal("missing zig compiler path", .{});
- const zig_lib_dir = nextArg(args, &arg_idx) orelse fatal("missing zig lib directory path", .{});
- const build_root = nextArg(args, &arg_idx) orelse fatal("missing build root directory path", .{});
- const cache_root = nextArg(args, &arg_idx) orelse fatal("missing cache root directory path", .{});
- const global_cache_root = nextArg(args, &arg_idx) orelse fatal("missing global cache root directory path", .{});
-
- const cwd: Io.Dir = .cwd();
-
- const zig_lib_directory: std.Build.Cache.Directory = .{
- .path = zig_lib_dir,
- .handle = try cwd.openDir(io, zig_lib_dir, .{}),
- };
-
- const build_root_directory: std.Build.Cache.Directory = .{
- .path = build_root,
- .handle = try cwd.openDir(io, build_root, .{}),
- };
-
- const local_cache_directory: std.Build.Cache.Directory = .{
- .path = cache_root,
- .handle = try cwd.createDirPathOpen(io, cache_root, .{}),
- };
-
- const global_cache_directory: std.Build.Cache.Directory = .{
- .path = global_cache_root,
- .handle = try cwd.createDirPathOpen(io, global_cache_root, .{}),
- };
-
- var graph: std.Build.Graph = .{
- .io = io,
- .arena = arena,
- .cache = .{
- .io = io,
- .gpa = gpa,
- .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
- .cwd = try process.currentPathAlloc(io, arena),
- },
- .zig_exe = zig_exe,
- .environ_map = try init.environ.createMap(arena),
- .global_cache_root = global_cache_directory,
- .zig_lib_directory = zig_lib_directory,
- .host = .{
- .query = .{},
- .result = try std.zig.system.resolveTargetQuery(io, .{}),
- },
- .time_report = false,
- };
-
- graph.cache.addPrefix(.{ .path = null, .handle = cwd });
- graph.cache.addPrefix(build_root_directory);
- graph.cache.addPrefix(local_cache_directory);
- graph.cache.addPrefix(global_cache_directory);
- graph.cache.hash.addBytes(builtin.zig_version_string);
-
- const builder = try std.Build.create(
- &graph,
- build_root_directory,
- local_cache_directory,
- dependencies.root_deps,
- );
-
- var targets: std.ArrayList([]const u8) = .empty;
-
- var install_prefix: ?[]const u8 = null;
- var dir_list: std.Build.DirList = .{};
- var max_rss: u64 = 0;
- var skip_oom_steps = false;
- var output_tmp_nonce: ?[16]u8 = null;
- var watch = false;
- var check_step_only = false;
- var debounce_interval_ms: u16 = 50;
-
- while (nextArg(args, &arg_idx)) |arg| {
- if (mem.startsWith(u8, arg, "-Z")) {
- if (arg.len != 18) fatalWithHint("bad argument: '{s}'", .{arg});
- output_tmp_nonce = arg[2..18].*;
- } else if (mem.startsWith(u8, arg, "-D")) {
- const option_contents = arg[2..];
- if (option_contents.len == 0)
- fatalWithHint("expected option name after '-D'", .{});
- if (mem.findScalar(u8, option_contents, '=')) |name_end| {
- const option_name = option_contents[0..name_end];
- const option_value = option_contents[name_end + 1 ..];
- if (try builder.addUserInputOption(option_name, option_value))
- fatal(" access the help menu with 'zig build -h'", .{});
- } else {
- if (try builder.addUserInputFlag(option_contents))
- fatal(" access the help menu with 'zig build -h'", .{});
- }
- } else if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "--verbose")) {
- builder.verbose = true;
- } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) {
- install_prefix = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.startsWith(u8, arg, "-fsys=")) {
- const name = arg["-fsys=".len..];
- graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM");
- } else if (mem.startsWith(u8, arg, "-fno-sys=")) {
- const name = arg["-fno-sys=".len..];
- graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM");
- } else if (mem.eql(u8, arg, "--release")) {
- builder.release_mode = .any;
- } else if (mem.startsWith(u8, arg, "--release=")) {
- const text = arg["--release=".len..];
- builder.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {
- fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{
- arg, text,
- });
- };
- } else if (mem.eql(u8, arg, "--prefix-lib-dir")) {
- dir_list.lib_dir = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--prefix-exe-dir")) {
- dir_list.exe_dir = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--prefix-include-dir")) {
- dir_list.include_dir = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--sysroot")) {
- builder.sysroot = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--maxrss")) {
- const max_rss_text = nextArgOrFatal(args, &arg_idx);
- max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err| {
- std.debug.print("invalid byte size: '{s}': {s}\n", .{
- max_rss_text, @errorName(err),
- });
- process.exit(1);
- };
- } else if (mem.eql(u8, arg, "--skip-oom-steps")) {
- skip_oom_steps = true;
- } else if (mem.eql(u8, arg, "--test-timeout")) {
- const next_arg = nextArgOrFatal(args, &arg_idx);
- _ = next_arg;
- } else if (mem.eql(u8, arg, "--search-prefix")) {
- const search_prefix = nextArgOrFatal(args, &arg_idx);
- builder.addSearchPrefix(search_prefix);
- } else if (mem.eql(u8, arg, "--libc")) {
- builder.libc_file = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--color")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected [auto|on|off] after '{s}'", .{arg});
- _ = next_arg;
- } else if (mem.eql(u8, arg, "--error-style")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected style after '{s}'", .{arg});
- _ = next_arg;
- } else if (mem.eql(u8, arg, "--multiline-errors")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected style after '{s}'", .{arg});
- _ = next_arg;
- } else if (mem.eql(u8, arg, "--summary")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected [all|new|failures|line|none] after '{s}'", .{arg});
- _ = next_arg;
- } else if (mem.eql(u8, arg, "--seed")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected u32 after '{s}'", .{arg});
- graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
- fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{
- next_arg, @errorName(err),
- });
- };
- } else if (mem.eql(u8, arg, "--build-id")) {
- builder.build_id = .fast;
- } else if (mem.startsWith(u8, arg, "--build-id=")) {
- const style = arg["--build-id=".len..];
- builder.build_id = std.zig.BuildId.parse(style) catch |err| {
- fatal("unable to parse --build-id style '{s}': {s}", .{
- style, @errorName(err),
- });
- };
- } else if (mem.eql(u8, arg, "--debounce")) {
- const next_arg = nextArg(args, &arg_idx) orelse
- fatalWithHint("expected u16 after '{s}'", .{arg});
- debounce_interval_ms = std.fmt.parseUnsigned(u16, next_arg, 0) catch |err| {
- fatal("unable to parse debounce interval '{s}' as unsigned 16-bit integer: {t}\n", .{
- next_arg, err,
- });
- };
- } else if (mem.eql(u8, arg, "--webui")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.startsWith(u8, arg, "--webui=")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.eql(u8, arg, "--debug-log")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
- builder.debug_pkg_config = true;
- } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| {
- graph.debug_compiler_runtime_libs =
- std.meta.stringToEnum(std.lang.OptimizeMode, rest) orelse
- fatal("unrecognized optimization mode: '{s}'", .{rest});
- } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
- builder.debug_compile_errors = true;
- } else if (mem.eql(u8, arg, "--debug-incremental")) {
- builder.debug_incremental = true;
- } else if (mem.eql(u8, arg, "--system")) {
- // The usage text shows another argument after this parameter
- // but it is handled by the parent process. The build runner
- // only sees this flag.
- graph.system_package_mode = true;
- } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
- // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
- builder.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
- } else if (mem.eql(u8, arg, "--verbose-link")) {
- builder.verbose_link = true;
- } else if (mem.eql(u8, arg, "--verbose-air")) {
- builder.verbose_air = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
- builder.verbose_llvm_ir = "-";
- } else if (mem.startsWith(u8, arg, "--verbose-llvm-ir=")) {
- builder.verbose_llvm_ir = arg["--verbose-llvm-ir=".len..];
- } else if (mem.startsWith(u8, arg, "--verbose-llvm-bc=")) {
- builder.verbose_llvm_bc = arg["--verbose-llvm-bc=".len..];
- } else if (mem.eql(u8, arg, "--verbose-cc")) {
- builder.verbose_cc = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
- builder.verbose_llvm_cpu_features = true;
- } else if (mem.eql(u8, arg, "--watch")) {
- watch = true;
- } else if (mem.eql(u8, arg, "--check-only")) { // ZLS only
- check_step_only = true;
- } else if (mem.eql(u8, arg, "--time-report")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.startsWith(u8, arg, "--fuzz")) {
- fatal("argument '{s}' is not available", .{arg});
- } else if (mem.eql(u8, arg, "-fincremental")) {
- graph.incremental = true;
- } else if (mem.eql(u8, arg, "-fno-incremental")) {
- graph.incremental = false;
- } else if (mem.eql(u8, arg, "-fwine")) {
- builder.enable_wine = true;
- } else if (mem.eql(u8, arg, "-fno-wine")) {
- builder.enable_wine = false;
- } else if (mem.eql(u8, arg, "-fqemu")) {
- builder.enable_qemu = true;
- } else if (mem.eql(u8, arg, "-fno-qemu")) {
- builder.enable_qemu = false;
- } else if (mem.eql(u8, arg, "-fwasmtime")) {
- builder.enable_wasmtime = true;
- } else if (mem.eql(u8, arg, "-fno-wasmtime")) {
- builder.enable_wasmtime = false;
- } else if (mem.eql(u8, arg, "-frosetta")) {
- builder.enable_rosetta = true;
- } else if (mem.eql(u8, arg, "-fno-rosetta")) {
- builder.enable_rosetta = false;
- } else if (mem.eql(u8, arg, "-fdarling")) {
- builder.enable_darling = true;
- } else if (mem.eql(u8, arg, "-fno-darling")) {
- builder.enable_darling = false;
- } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
- graph.allow_so_scripts = true;
- } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
- graph.allow_so_scripts = false;
- } else if (mem.eql(u8, arg, "-freference-trace")) {
- builder.reference_trace = 256;
- } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
- const num = arg["-freference-trace=".len..];
- builder.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
- std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
- process.exit(1);
- };
- } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
- builder.reference_trace = null;
- } else if (mem.cutPrefix(u8, arg, "-j")) |text| {
- const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
- fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
- if (n < 1) fatal("number of jobs must be at least 1", .{});
- threaded.setAsyncLimit(.limited(n));
- } else if (mem.eql(u8, arg, "--")) {
- builder.args = argsRest(args, arg_idx);
- break;
- } else {
- fatalWithHint("unrecognized argument: '{s}'", .{arg});
- }
- } else {
- try targets.append(arena, arg);
- }
- }
-
- const main_progress_node = std.Progress.start(io, .{
- .disable_printing = true,
- });
- defer main_progress_node.end();
-
- builder.resolveInstallPrefix(install_prefix, dir_list);
- {
- var prog_node = main_progress_node.start("Configure", 0);
- defer prog_node.end();
- try builder.runBuild(root);
- createModuleDependencies(builder) catch @panic("OOM");
- }
-
- if (graph.needed_lazy_dependencies.entries.len != 0) {
- var buffer: std.ArrayList(u8) = .empty;
- for (graph.needed_lazy_dependencies.keys()) |k| {
- try buffer.appendSlice(arena, k);
- try buffer.append(arena, '\n');
- }
- const s = std.Io.Dir.path.sep_str;
- const tmp_sub_path = "tmp" ++ s ++ (output_tmp_nonce orelse fatal("missing -Z arg", .{}));
- local_cache_directory.handle.writeFile(io, .{
- .sub_path = tmp_sub_path,
- .data = buffer.items,
- .flags = .{ .exclusive = true },
- }) catch |err| {
- fatal("unable to write configuration results to '{f}{s}': {s}", .{
- local_cache_directory, tmp_sub_path, @errorName(err),
- });
- };
- process.exit(3); // Indicate configure phase failed with meaningful stdout.
- }
-
- if (builder.validateUserInputDidItFail()) {
- fatal(" access the help menu with 'zig build -h'", .{});
- }
-
- validateSystemLibraryOptions(builder);
-
- var run: Run = .{
- .gpa = gpa,
-
- .available_rss = max_rss,
- .max_rss_is_default = false,
- .max_rss_mutex = .init,
- .skip_oom_steps = skip_oom_steps,
- .watch = watch,
- .memory_blocked_steps = .empty,
-
- .cycle = 0,
- };
- defer run.memory_blocked_steps.deinit(gpa);
-
- if (run.available_rss == 0) {
- run.available_rss = process.totalSystemMemory() catch std.math.maxInt(u64);
- run.max_rss_is_default = true;
- }
-
- if (!watch) {
- try extractBuildInformation(
- builder,
- arena,
- main_progress_node,
- &run,
- );
- std.process.exit(0);
- }
-
- var w = try Watch.init(io, graph.cache.cwd);
-
- const message_thread = try std.Thread.spawn(.{}, struct {
- fn do(ww: *Watch) void {
- while (true) {
- var buffer: [1]u8 = undefined;
- var stdin_reader = Io.File.stdin().reader(ww.io, &buffer);
- const byte = stdin_reader.interface.takeByte() catch |err| switch (err) {
- error.ReadFailed => process.exit(1),
- error.EndOfStream => process.exit(0),
- };
- switch (byte) {
- '\x00' => ww.trigger(),
- else => process.exit(1),
- }
- }
- }
- }.do, .{&w});
- message_thread.detach();
-
- var step_stack = try resolveStepNames(gpa, builder, targets.items, check_step_only);
- defer step_stack.deinit(gpa);
- if (step_stack.count() == 0) {
- // This means that `enable_build_on_save == null` and the project contains no "check" step.
- return;
- }
-
- const starting_steps = try gpa.dupe(*Step, step_stack.keys());
- defer gpa.free(starting_steps);
-
- prepare(builder, &step_stack, &run) catch |err| switch (err) {
- error.DependencyLoopDetected => process.exit(1),
- else => |e| return e,
- };
-
- rebuild: while (true) : (run.cycle += 1) {
- try runSteps(
- builder,
- &step_stack,
- main_progress_node,
- &run,
- );
-
- try w.update(gpa, step_stack.keys());
-
- // Wait until a file system notification arrives. Read all such events
- // until the buffer is empty. Then wait for a debounce interval, resetting
- // if any more events come in. After the debounce interval has passed,
- // trigger a rebuild on all steps with modified inputs, as well as their
- // recursive dependants.
- var debounce_timeout: Io.Timeout = .none;
- while (true) switch (try w.wait(gpa, debounce_timeout)) {
- .timeout => {
- markFailedStepsDirty(gpa, step_stack.keys());
- continue :rebuild;
- },
- .dirty => if (debounce_timeout == .none) {
- debounce_timeout = .{ .duration = .{ .raw = .fromMilliseconds(debounce_interval_ms), .clock = .real } };
- },
- .clean => {},
- };
- }
-}
-
-fn markFailedStepsDirty(gpa: Allocator, all_steps: []const *Step) void {
- for (all_steps) |step| switch (step.state) {
- .dependency_failure, .failure, .skipped => _ = step.invalidateResult(gpa),
- else => continue,
- };
- // Now that all dirty steps have been found, the remaining steps that
- // succeeded from last run shall be marked "cached".
- for (all_steps) |step| switch (step.state) {
- .success => step.result_cached = true,
- else => continue,
- };
-}
-
-/// A wrapper around `std.Build.Watch` that supports manually triggering recompilations.
-const Watch = struct {
- io: std.Io,
- fs_watch: std.Build.Watch,
- supports_fs_watch: bool,
- manual_event: Io.Event,
- steps: []const *Step,
-
- fn init(io: std.Io, cwd_path: []const u8) !Watch {
- return .{
- .io = io,
- .fs_watch = if (@TypeOf(std.Build.Watch) != void) try std.Build.Watch.init(cwd_path) else {},
- .supports_fs_watch = @TypeOf(std.Build.Watch) != void and shared.BuildOnSaveSupport.isSupportedRuntime(builtin.zig_version) == .supported,
- .manual_event = .unset,
- .steps = &.{},
- };
- }
-
- fn update(w: *Watch, gpa: Allocator, steps: []const *Step) !void {
- if (@TypeOf(std.Build.Watch) != void and w.supports_fs_watch) {
- return try w.fs_watch.update(gpa, steps);
- }
- w.steps = steps;
- }
-
- fn trigger(w: *Watch) void {
- if (w.supports_fs_watch) {
- @panic("received manualy filesystem event even though std.Build.Watch is supported");
- }
- w.manual_event.set(w.io);
- }
-
- fn wait(w: *Watch, gpa: Allocator, timeout: Io.Timeout) !std.Build.Watch.WaitResult {
- if (@TypeOf(std.Build.Watch) != void and w.supports_fs_watch) {
- return try w.fs_watch.wait(gpa, w.io, switch (timeout) {
- .none => .none,
- .duration => |d| .{ .ms = @intCast(d.raw.toMilliseconds()) },
- .deadline => unreachable,
- });
- }
- w.manual_event.waitTimeout(w.io, timeout) catch |err| switch (err) {
- error.Canceled => unreachable,
- error.Timeout => return .timeout,
- };
- w.manual_event.reset();
- markStepsDirty(gpa, w.steps);
- return .dirty;
- }
-
- fn markStepsDirty(gpa: Allocator, all_steps: []const *Step) void {
- for (all_steps) |step| switch (step.state) {
- .precheck_done => continue,
- else => _ = step.invalidateResult(gpa),
- };
- }
-};
-
-const Run = struct {
- gpa: Allocator,
-
- available_rss: usize,
- max_rss_is_default: bool,
- max_rss_mutex: Io.Mutex,
- skip_oom_steps: bool,
- watch: bool,
- /// Allocated into `gpa`.
- memory_blocked_steps: std.ArrayList(*Step),
-
- cycle: u32,
-};
-
-fn resolveStepNames(
- gpa: Allocator,
- b: *std.Build,
- step_names: []const []const u8,
- check_step_only: bool,
-) !std.array_hash_map.Auto(*Step, void) {
- var starting_steps: std.array_hash_map.Auto(*Step, void) = .empty;
- errdefer starting_steps.deinit(gpa);
-
- if (step_names.len == 0) {
- if (b.top_level_steps.get("check")) |tls| {
- try starting_steps.put(gpa, &tls.step, {});
- } else if (!check_step_only) {
- try starting_steps.put(gpa, b.default_step, {});
- }
- } else {
- try starting_steps.ensureUnusedCapacity(gpa, step_names.len);
- for (0..step_names.len) |i| {
- const step_name = step_names[step_names.len - i - 1];
- const s = b.top_level_steps.get(step_name) orelse {
- std.debug.print("no step named '{s}'\n access the help menu with 'zig build -h'\n", .{step_name});
- process.exit(1);
- };
- starting_steps.putAssumeCapacity(&s.step, {});
- }
- }
-
- return starting_steps;
-}
-
-fn prepare(
- b: *std.Build,
- unpopulated_step_stack: *std.array_hash_map.Auto(*Step, void),
- run: *Run,
-) error{ OutOfMemory, DependencyLoopDetected }!void {
- const gpa = run.gpa;
-
- const starting_steps = try gpa.dupe(*Step, unpopulated_step_stack.keys());
- defer gpa.free(starting_steps);
-
- var rng = std.Random.DefaultPrng.init(b.graph.random_seed);
- const rand = rng.random();
- rand.shuffle(*Step, starting_steps);
-
- for (starting_steps) |s| {
- try constructGraphAndCheckForDependencyLoop(gpa, b, s, unpopulated_step_stack, rand);
- }
- const step_stack = unpopulated_step_stack;
-
- {
- // Check that we have enough memory to complete the build.
- var any_problems = false;
- for (step_stack.keys()) |s| {
- if (s.max_rss == 0) continue;
- if (s.max_rss > run.available_rss) {
- if (run.skip_oom_steps) {
- s.state = .skipped_oom;
- for (s.dependants.items) |dependant| {
- dependant.pending_deps -= 1;
- }
- } else {
- std.debug.print("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory\n", .{
- s.owner.dep_prefix, s.name, s.max_rss, run.available_rss,
- });
- any_problems = true;
- }
- }
- }
- if (any_problems) {
- if (run.max_rss_is_default) {
- std.debug.print("note: use --maxrss to override the default", .{});
- }
- }
- }
-}
-
-fn runSteps(
- b: *std.Build,
- step_stack: *const std.array_hash_map.Auto(*Step, void),
- parent_prog_node: std.Progress.Node,
- run: *Run,
-) (Io.Cancelable || mem.Allocator.Error)!void {
- const gpa = run.gpa;
- const io = b.graph.io;
-
- // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
- // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
- // a step is initial when it actually became ready due to an earlier initial step.
- var initial_set: std.ArrayList(*Step) = .empty;
- defer initial_set.deinit(gpa);
- try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
- for (step_stack.keys()) |s| {
- if (s.state == .precheck_done and s.pending_deps == 0) {
- initial_set.appendAssumeCapacity(s);
- }
- }
-
- const step_prog = parent_prog_node.start("steps", step_stack.count());
- defer step_prog.end();
-
- var group: Io.Group = .init;
- defer group.cancel(io);
- // Start working on all of the initial steps...
- for (initial_set.items) |s| try stepReady(&group, b, step_stack, s, step_prog, run);
- // ...and `makeStep` will trigger every other step when their last dependency finishes.
- try group.await(io);
-}
-
-/// Traverse the dependency graph depth-first and make it undirected by having
-/// steps know their dependants (they only know dependencies at start).
-/// Along the way, check that there is no dependency loop, and record the steps
-/// in traversal order in `step_stack`.
-/// Each step has its dependencies traversed in random order, this accomplishes
-/// two things:
-/// - `step_stack` will be in randomized-depth-first order, so the build runner
-/// spawns initial steps in a random order
-/// - each step's `dependants` list is also filled in a random order, so that
-/// when it finishes executing in `makeStep`, it spawns next steps to run in
-/// random order
-fn constructGraphAndCheckForDependencyLoop(
- gpa: Allocator,
- b: *std.Build,
- s: *Step,
- step_stack: *std.array_hash_map.Auto(*Step, void),
- rand: std.Random,
-) !void {
- switch (s.state) {
- .precheck_started => {
- return error.DependencyLoopDetected;
- },
- .precheck_unstarted => {
- s.state = .precheck_started;
-
- try step_stack.ensureUnusedCapacity(gpa, s.dependencies.items.len);
-
- // We dupe to avoid shuffling the steps in the summary, it depends
- // on s.dependencies' order.
- const deps = gpa.dupe(*Step, s.dependencies.items) catch @panic("OOM");
- defer gpa.free(deps);
-
- rand.shuffle(*Step, deps);
-
- for (deps) |dep| {
- try step_stack.put(gpa, dep, {});
- try dep.dependants.append(b.allocator, s);
- constructGraphAndCheckForDependencyLoop(gpa, b, dep, step_stack, rand) catch |err| {
- return err;
- };
- }
-
- s.state = .precheck_done;
- s.pending_deps = @intCast(s.dependencies.items.len);
- },
- .precheck_done => {},
-
- // These don't happen until we actually run the step graph.
- .dependency_failure => unreachable,
- .success => unreachable,
- .failure => unreachable,
- .skipped => unreachable,
- .skipped_oom => unreachable,
- }
-}
-
-/// Runs the "make" function of the single step `s`, updates its state, and then spawns newly-ready
-/// dependant steps in `group`. If `s` makes an RSS claim (i.e. `s.max_rss != 0`), the caller must
-/// have already subtracted this value from `run.available_rss`. This function will release the RSS
-/// claim (i.e. add `s.max_rss` back into `run.available_rss`) and queue any viable memory-blocked
-/// steps after "make" completes for `s`.
-fn makeStep(
- group: *Io.Group,
- b: *std.Build,
- steps_stack: *const std.array_hash_map.Auto(*Step, void),
- s: *Step,
- root_prog_node: std.Progress.Node,
- run: *Run,
-) Io.Cancelable!void {
- const io = b.graph.io;
- const gpa = run.gpa;
-
- {
- const step_prog_node = root_prog_node.start(s.name, 0);
- defer step_prog_node.end();
-
- const new_state: Step.State = for (s.dependencies.items) |dep| {
- switch (@atomicLoad(Step.State, &dep.state, .monotonic)) {
- .precheck_unstarted => unreachable,
- .precheck_started => unreachable,
- .precheck_done => unreachable,
-
- .failure,
- .dependency_failure,
- .skipped_oom,
- => break .dependency_failure,
-
- .success, .skipped => {},
- }
- } else if (s.make(.{
- .progress_node = step_prog_node,
- .watch = run.watch,
- .web_server = null,
- .unit_test_timeout_ns = null,
- .gpa = gpa,
- })) state: {
- break :state .success;
- } else |err| switch (err) {
- error.MakeFailed => .failure,
- error.MakeSkipped => .skipped,
- };
-
- @atomicStore(Step.State, &s.state, new_state, .monotonic);
-
- switch (new_state) {
- .precheck_unstarted => unreachable,
- .precheck_started => unreachable,
- .precheck_done => unreachable,
-
- .failure,
- .dependency_failure,
- .skipped_oom,
- => {
- std.Progress.setStatus(.failure_working);
- },
-
- .success,
- .skipped,
- => {},
- }
- }
-
- if (run.watch) {
- const step_id: u32 = @intCast(steps_stack.getIndex(s).?);
- // missing fields:
- // - result_error_msgs
- // - result_stderr
- serveWatchErrorBundle(b.graph.io, step_id, run.cycle, s.result_error_bundle) catch @panic("failed to send watch errors");
- }
-
- if (s.max_rss != 0) {
- var dispatch_set: std.ArrayList(*Step) = .empty;
- defer dispatch_set.deinit(gpa);
-
- // Release our RSS claim and kick off some blocked steps if possible. We use `dispatch_set`
- // as a staging buffer to avoid recursing into `makeStep` while `run.max_rss_mutex` is held.
- {
- try run.max_rss_mutex.lock(io);
- defer run.max_rss_mutex.unlock(io);
- run.available_rss += s.max_rss;
- dispatch_set.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len) catch @panic("OOM");
- while (run.memory_blocked_steps.getLast()) |candidate| {
- if (run.available_rss < candidate.max_rss) break;
- assert(run.memory_blocked_steps.pop() == candidate);
- dispatch_set.appendAssumeCapacity(candidate);
- }
- }
- for (dispatch_set.items) |candidate| {
- group.async(io, makeStep, .{ group, b, steps_stack, candidate, root_prog_node, run });
- }
- }
-
- for (s.dependants.items) |dependant| {
- // `.acq_rel` synchronizes with itself to ensure all dependencies' final states are visible when this hits 0.
- if (@atomicRmw(u32, &dependant.pending_deps, .Sub, 1, .acq_rel) == 1) {
- try stepReady(group, b, steps_stack, dependant, root_prog_node, run);
- }
- }
-}
-
-fn stepReady(
- group: *Io.Group,
- b: *std.Build,
- steps_stack: *const std.array_hash_map.Auto(*Step, void),
- s: *Step,
- root_prog_node: std.Progress.Node,
- run: *Run,
-) !void {
- const io = b.graph.io;
- if (s.max_rss != 0) {
- try run.max_rss_mutex.lock(io);
- defer run.max_rss_mutex.unlock(io);
- if (run.available_rss < s.max_rss) {
- // Running this step right now could possibly exceed the allotted RSS.
- run.memory_blocked_steps.append(run.gpa, s) catch @panic("OOM");
- return;
- }
- run.available_rss -= s.max_rss;
- }
- group.async(io, makeStep, .{ group, b, steps_stack, s, root_prog_node, run });
-}
-
-fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
- if (idx.* >= args.len) return null;
- defer idx.* += 1;
- return args[idx.*];
-}
-
-fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
- return nextArg(args, idx) orelse {
- std.debug.print("expected argument after '{s}'\n access the help menu with 'zig build -h'\n", .{args[idx.* - 1]});
- process.exit(1);
- };
-}
-
-fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 {
- if (idx >= args.len) return null;
- return args[idx..];
-}
-
-fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
- std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args);
- process.exit(1);
-}
-
-fn validateSystemLibraryOptions(b: *std.Build) void {
- var bad = false;
- for (b.graph.system_library_options.keys(), b.graph.system_library_options.values()) |k, v| {
- switch (v) {
- .user_disabled, .user_enabled => {
- // The user tried to enable or disable a system library integration, but
- // the build script did not recognize that option.
- std.debug.print("system library name not recognized by build script: '{s}'\n", .{k});
- bad = true;
- },
- .declared_disabled, .declared_enabled => {},
- }
- }
- if (bad) {
- std.debug.print(" access the help menu with 'zig build -h'\n", .{});
- process.exit(1);
- }
-}
-
-/// Starting from all top-level steps in `b`, traverses the entire step graph
-/// and adds all step dependencies implied by module graphs.
-fn createModuleDependencies(b: *std.Build) Allocator.Error!void {
- const arena = b.graph.arena;
-
- var all_steps: std.array_hash_map.Auto(*Step, void) = .empty;
- var next_step_idx: usize = 0;
-
- try all_steps.ensureUnusedCapacity(arena, b.top_level_steps.count());
- for (b.top_level_steps.values()) |tls| {
- all_steps.putAssumeCapacityNoClobber(&tls.step, {});
- }
-
- while (next_step_idx < all_steps.count()) {
- const step = all_steps.keys()[next_step_idx];
- next_step_idx += 1;
-
- // Set up any implied dependencies for this step. It's important that we do this first, so
- // that the loop below discovers steps implied by the module graph.
- try createModuleDependenciesForStep(step);
-
- try all_steps.ensureUnusedCapacity(arena, step.dependencies.items.len);
- for (step.dependencies.items) |other_step| {
- all_steps.putAssumeCapacity(other_step, {});
- }
- }
-}
-
-/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
-/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
-fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
- const root_module = if (step.cast(Step.Compile)) |cs| root: {
- break :root cs.root_module;
- } else return; // not a compile step so no module dependencies
-
- // Starting from `root_module`, discover all modules in this graph.
- const modules = root_module.getGraph().modules;
-
- // For each of those modules, set up the implied step dependencies.
- for (modules) |mod| {
- if (mod.root_source_file) |lp| lp.addStepDependencies(step);
- for (mod.include_dirs.items) |include_dir| switch (include_dir) {
- .path,
- .path_system,
- .path_after,
- .framework_path,
- .framework_path_system,
- .embed_path,
- => |lp| lp.addStepDependencies(step),
-
- .other_step => |other| {
- other.getEmittedIncludeTree().addStepDependencies(step);
- step.dependOn(&other.step);
- },
-
- .config_header_step => |other| step.dependOn(&other.step),
- };
- for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
- for (mod.rpaths.items) |rpath| switch (rpath) {
- .lazy_path => |lp| lp.addStepDependencies(step),
- .special => {},
- };
- for (mod.link_objects.items) |link_object| switch (link_object) {
- .static_path,
- .assembly_file,
- => |lp| lp.addStepDependencies(step),
- .other_step => |other| step.dependOn(&other.step),
- .system_lib => {},
- .c_source_file => |source| source.file.addStepDependencies(step),
- .c_source_files => |source_files| source_files.root.addStepDependencies(step),
- .win32_resource_file => |rc_source| {
- rc_source.file.addStepDependencies(step);
- for (rc_source.include_paths) |lp| lp.addStepDependencies(step);
- },
- };
- }
-}
-
-//
-//
-// ZLS code
-//
-//
-
-const shared = @import("shared.zig");
-const BuildConfig = shared.BuildConfig;
-
-fn extractBuildInformation(
- b: *std.Build,
- arena: Allocator,
- main_progress_node: std.Progress.Node,
- run: *Run,
-) !void {
- const helper = struct {
- fn addLazyPathStepDependencies(allocator: Allocator, set: *std.array_hash_map.Auto(*Step, void), lazy_path: std.Build.LazyPath) !void {
- switch (lazy_path) {
- .src_path, .cwd_relative, .dependency => {},
- .generated => |gen| try set.put(allocator, gen.file.step, {}),
- }
- }
- /// Only adds the necessary dependencies to resolve the `root_source_file`. Does not include dependencies of imported modules.
- fn addModuleDependencies(allocator: Allocator, set: *std.array_hash_map.Auto(*Step, void), module: *std.Build.Module) !void {
- if (module.root_source_file) |root_source_file| {
- try addLazyPathStepDependencies(allocator, set, root_source_file);
- }
- }
- fn processModule(
- allocator: Allocator,
- modules: *std.array_hash_map.String(BuildConfig.Module),
- module: *std.Build.Module,
- compile: ?*Step.Compile,
- ) !void {
- _ = compile;
- const root_source_file = module.root_source_file orelse return;
-
- const cwd = module.owner.graph.cache.cwd;
-
- const root_source_file_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, root_source_file.getPath2(module.owner, null) });
-
- // All modules with the same root source file are merged. This limitation may be lifted in the future.
- const gop = try modules.getOrPutValue(allocator, root_source_file_path, .{
- .import_table = .{},
- });
-
- for (module.import_table.keys(), module.import_table.values()) |name, import| {
- const import_root_source_file = import.root_source_file orelse continue;
- const gop_import = try gop.value_ptr.import_table.map.getOrPut(allocator, name);
- // This does not account for the possibility of collisions (i.e. modules with same root source file import different modules under the same name).
- if (!gop_import.found_existing) {
- gop_import.value_ptr.* = try std.Io.Dir.path.resolve(allocator, &.{ cwd, import_root_source_file.getPath2(import.owner, null) });
- }
- }
- }
- };
- const gpa = run.gpa;
-
- // The value tracks whether the step is a decendant of the "install" step.
- var all_steps: std.array_hash_map.Auto(*Step, bool) = .empty;
- defer all_steps.deinit(gpa);
-
- // collect all steps that are decendants of the "install" step.
- {
- try all_steps.putNoClobber(gpa, b.getInstallStep(), true);
-
- var i: usize = 0;
- while (i < all_steps.count()) : (i += 1) {
- const step = all_steps.keys()[i];
-
- try all_steps.ensureUnusedCapacity(gpa, step.dependencies.items.len);
- for (step.dependencies.items) |other_step| {
- all_steps.putAssumeCapacity(other_step, true);
- }
- }
- }
-
- // collect all other steps
- {
- var i: usize = all_steps.count();
-
- try all_steps.ensureUnusedCapacity(gpa, b.top_level_steps.count());
- for (b.top_level_steps.values()) |tls| {
- all_steps.putAssumeCapacity(&tls.step, true);
- }
-
- while (i < all_steps.count()) : (i += 1) {
- const step = all_steps.keys()[i];
-
- try all_steps.ensureUnusedCapacity(gpa, step.dependencies.items.len);
- for (step.dependencies.items) |other_step| {
- all_steps.putAssumeCapacity(other_step, false);
- }
- }
- }
-
- // Collect all steps that need to be run so that we can resolve the lazy paths we are interested in (e.g. root_source_file).
- {
- var needed_steps: std.array_hash_map.Auto(*Step, void) = .empty;
- defer needed_steps.deinit(gpa);
-
- var modules: std.array_hash_map.Auto(*std.Build.Module, void) = .empty;
- defer modules.deinit(gpa);
-
- // collect all modules of root modules
- for (b.modules.values()) |root_module| {
- const graph = root_module.getGraph();
- try modules.ensureUnusedCapacity(gpa, graph.modules.len);
- for (graph.modules) |module| modules.putAssumeCapacity(module, {});
- }
-
- // collect all modules of `Step.Compile`
- for (all_steps.keys()) |step| {
- const compile = step.cast(Step.Compile) orelse continue;
- const graph = compile.root_module.getGraph();
- try modules.ensureUnusedCapacity(gpa, graph.modules.len);
- for (graph.modules) |module| modules.putAssumeCapacity(module, {});
- }
-
- // collect all dependencies of all found modules
- for (modules.keys()) |module| {
- try helper.addModuleDependencies(gpa, &needed_steps, module);
- }
-
- prepare(b, &needed_steps, run) catch |err| switch (err) {
- error.DependencyLoopDetected => process.exit(1),
- else => |e| return e,
- };
-
- try runSteps(
- b,
- &needed_steps,
- main_progress_node,
- run,
- );
- }
-
- // We collect modules in the following order:
- // - public modules (`std.Build.addModule`)
- // - modules that are reachable from the "install" step
- // - all other reachable modules
- var modules: std.array_hash_map.String(BuildConfig.Module) = .empty;
-
- for (b.modules.values()) |root_module| {
- const graph = root_module.getGraph();
- for (graph.modules) |module| {
- try helper.processModule(arena, &modules, module, null);
- }
- }
-
- // We loop twice through all steps so that decendants of the "install" step are processed first.
- for ([_]bool{ true, false }) |want_install_step_decendant| {
- for (all_steps.keys(), all_steps.values()) |step, is_install_step_decendant| {
- if (is_install_step_decendant != want_install_step_decendant) continue;
-
- const compile = step.cast(Step.Compile) orelse continue;
- const graph = compile.root_module.getGraph();
- for (graph.modules) |module| {
- try helper.processModule(arena, &modules, module, compile);
- }
- }
- }
-
- var compilations: std.ArrayList(BuildConfig.Compile) = .empty;
- for (all_steps.keys()) |step| {
- const compile = step.cast(Step.Compile) orelse continue;
- const root_source_file = compile.root_module.root_source_file orelse continue;
- const root_source_file_path = try std.Io.Dir.path.resolve(arena, &.{ b.graph.cache.cwd, root_source_file.getPath2(compile.root_module.owner, null) });
- try compilations.append(arena, .{
- .root_module = root_source_file_path,
- });
- }
-
- // Sample `@dependencies` structure:
- // pub const packages = struct {
- // pub const @"1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5" = struct {
- // pub const build_root = "/home/rad/.cache/zig/p/1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5";
- // pub const build_zig = @import("1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5");
- // pub const deps: []const struct { []const u8, []const u8 } = &.{};
- // };
- // ...
- // };
- // pub const root_deps: []const struct { []const u8, []const u8 } = &.{
- // .{ "known_folders", "1220bb12c9bfe291eed1afe6a2070c7c39918ab1979f24a281bba39dfb23f5bcd544" },
- // .{ "diffz", "122089a8247a693cad53beb161bde6c30f71376cd4298798d45b32740c3581405864" },
- // };
-
- // Collect the dependencies from `build.zig.zon`
- var root_dependencies: std.array_hash_map.String([]const u8) = .empty;
- for (dependencies.root_deps) |root_dep| {
- inline for (comptime std.meta.declarations(dependencies.packages)) |package| blk: {
- if (std.mem.eql(u8, package.name, root_dep[1])) {
- const package_info = @field(dependencies.packages, package.name);
- if (!@hasDecl(package_info, "build_root")) break :blk;
- if (!@hasDecl(package_info, "build_zig")) break :blk;
- try root_dependencies.put(
- arena,
- root_dep[0],
- try std.Io.Dir.path.join(arena, &.{ package_info.build_root, "build.zig" }),
- );
- }
- }
- }
-
- var available_options: std.array_hash_map.String(BuildConfig.AvailableOption) = .empty;
- try available_options.ensureTotalCapacity(arena, b.available_options_map.count());
-
- var it = b.available_options_map.iterator();
- while (it.next()) |available_option| {
- available_options.putAssumeCapacityNoClobber(available_option.key_ptr.*, available_option.value_ptr.*);
- }
-
- const stringified_build_config = try std.json.Stringify.valueAlloc(
- gpa,
- BuildConfig{
- .dependencies = .{ .map = root_dependencies },
- .modules = .{ .map = modules },
- .compilations = compilations.items,
- .top_level_steps = b.top_level_steps.keys(),
- .available_options = .{ .map = available_options },
- },
- .{ .whitespace = .indent_2 },
- );
-
- var file_writer = Io.File.stdout().writer(b.graph.io, &.{});
- file_writer.interface.writeAll(stringified_build_config) catch return file_writer.err.?;
-}
-
-fn serveWatchErrorBundle(
- io: std.Io,
- step_id: u32,
- cycle: u32,
- error_bundle: std.zig.ErrorBundle,
-) Io.File.Writer.Error!void {
- const bytes_len = @sizeOf(shared.ServerToClient.ErrorBundle) + @sizeOf(u32) * error_bundle.extra.len + error_bundle.string_bytes.len;
-
- var header: shared.ServerToClient.Header = .{
- .tag = .watch_error_bundle,
- .bytes_len = @intCast(bytes_len),
- };
-
- var error_bundle_header: shared.ServerToClient.ErrorBundle = .{
- .step_id = step_id,
- .cycle = cycle,
- .extra_len = @intCast(error_bundle.extra.len),
- .string_bytes_len = @intCast(error_bundle.string_bytes.len),
- };
-
- const need_bswap = builtin.target.cpu.arch.endian() != .little;
-
- if (need_bswap) {
- std.mem.byteSwapAllFields(shared.ServerToClient.Header, &header);
- std.mem.byteSwapAllFields(shared.ServerToClient.ErrorBundle, &error_bundle_header);
- std.mem.byteSwapAllElements(u32, @constCast(error_bundle.extra)); // trust me bro
- }
-
- var file_writer = Io.File.stdout().writer(io, &.{});
- const writer = &file_writer.interface;
-
- var data = [_][]const u8{
- std.mem.asBytes(&header),
- std.mem.asBytes(&error_bundle_header),
- std.mem.sliceAsBytes(error_bundle.extra),
- error_bundle.string_bytes,
- };
- writer.writeVecAll(&data) catch return file_writer.err.?;
-}
diff --git a/src/build_runner/check.zig b/src/build_runner/check.zig
deleted file mode 100644
index 822116cf1f..0000000000
--- a/src/build_runner/check.zig
+++ /dev/null
@@ -1,283 +0,0 @@
-const std = @import("std");
-
-pub fn isBuildRunnerSupported(runtime_zig_version: std.SemanticVersion) bool {
- const build_options = @import("build_options");
- const is_zls_version_tagged = build_options.version.pre == null;
- const min_runtime_zig_version = comptime std.SemanticVersion.parse(build_options.minimum_runtime_zig_version_string) catch unreachable;
- return isBuildRunnerSupportedInternal(
- min_runtime_zig_version,
- runtime_zig_version,
- is_zls_version_tagged,
- );
-}
-
-fn isBuildRunnerSupportedInternal(
- minimum_zig_version: std.SemanticVersion,
- param_zig_version: std.SemanticVersion,
- /// If set, a non-tagged zig version cannot be used when `minimum_runtime_zig_version` is a tagged version.
- /// Example: Zig `0.13.0-dev` cannot be used when the minimum Zig version is `0.12.0`.
- ///
- /// Will be set iff ZLS is a tagged release.
- strict: bool,
-) bool {
- const minimum_version_is_tagged = minimum_zig_version.pre == null;
-
- var zig_version = param_zig_version;
- var version_is_tagged = zig_version.pre == null;
-
- if (!version_is_tagged and zig_version.patch != 0) {
- // A zig version like `0.12.2-dev` has the same compatibility as `0.12.1`
- zig_version.patch -= 1;
- zig_version.pre = null;
- version_is_tagged = true;
- }
-
- if (strict and !version_is_tagged) {
- std.debug.assert(minimum_version_is_tagged);
- // A tagged release of ZLS must be used with a tagged release of Zig.
- return false;
- }
-
- if (zig_version.major != minimum_zig_version.major) return false;
-
- if (minimum_version_is_tagged) {
- if (version_is_tagged) {
- if (zig_version.order(minimum_zig_version) == .lt) return false;
- const next_minor_release: std.SemanticVersion = .{
- .major = minimum_zig_version.major,
- .minor = minimum_zig_version.minor + 1,
- .patch = 0,
- };
- return zig_version.order(next_minor_release) == .lt;
- } else {
- std.debug.assert(zig_version.patch == 0);
- return zig_version.minor == 1 + minimum_zig_version.minor;
- }
- } else {
- if (version_is_tagged) return false;
- if (zig_version.minor != minimum_zig_version.minor) return false;
- return zig_version.order(minimum_zig_version) != .lt;
- }
-}
-
-test {
- // The build runner must support the Zig version that ZLS is being built with
- const current_zig_version = @import("builtin").zig_version;
- try std.testing.expect(isBuildRunnerSupported(current_zig_version));
- const is_zls_version_tagged_release = current_zig_version.pre == null;
-
- if (is_zls_version_tagged_release) {
- // A tagged release of ZLS should support the same tagged release of Zig
- // Example: ZLS 0.12.0 should support Zig 0.12.x -- It is possible that ZLS requires a minimum patch version
- try std.testing.expect(isBuildRunnerSupported(.{ .major = current_zig_version.major, .minor = current_zig_version.minor, .patch = 999 }));
- }
-}
-
-// Version order for reference:
-// 0.11.0-dev < 0.11.0 < 0.12.0-dev < 0.12.0 < 0.13.0-dev < 0.13.0
-
-test isBuildRunnerSupportedInternal {
- var did_fail = false;
- for (test_cases) |test_case| {
- const minimum_runtime_zig_version: std.SemanticVersion = try .parse(test_case.minimum_runtime_zig_version);
- const runtime_zig_version: std.SemanticVersion = try .parse(test_case.runtime_zig_version);
- const minimum_runtime_version_is_tagged = minimum_runtime_zig_version.pre == null;
- const expected_if_strict, const expected_if_not_strict = switch (test_case.is_supported) {
- .yes => .{ true, true },
- .no => .{ false, false },
- .if_not_strict => .{ false, true },
- };
-
- const actual_if_not_strict = isBuildRunnerSupportedInternal(minimum_runtime_zig_version, runtime_zig_version, false);
- if (expected_if_not_strict != actual_if_not_strict) {
- std.debug.print("minimum={f}, actual={f}, strict={} -> expected {} but got {}\n", .{
- minimum_runtime_zig_version,
- runtime_zig_version,
- false,
- expected_if_not_strict,
- actual_if_not_strict,
- });
- did_fail = true;
- }
-
- if (minimum_runtime_version_is_tagged) {
- const actual_if_strict = isBuildRunnerSupportedInternal(minimum_runtime_zig_version, runtime_zig_version, true);
- if (expected_if_strict != actual_if_strict) {
- std.debug.print("minimum={f}, actual={f}, strict={} -> expected {} but got {}\n", .{
- minimum_runtime_zig_version,
- runtime_zig_version,
- true,
- expected_if_strict,
- actual_if_strict,
- });
- did_fail = true;
- }
- }
- }
- if (did_fail) return error.Unexpected;
-}
-
-const test_cases: []const struct {
- minimum_runtime_zig_version: []const u8,
- runtime_zig_version: []const u8,
- is_supported: enum { yes, no, if_not_strict },
-} = &.{
- // Minimum Zig Version: 0.12.0
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.11.0-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.11.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.12.0-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.12.0",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.12.1-dev",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.12.1",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.13.0-dev",
- .is_supported = .if_not_strict,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.13.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0",
- .runtime_zig_version = "0.13.1-dev",
- .is_supported = .no,
- },
- // Minimum Zig Version: 0.12.1
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.11.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.11.1",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.0-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.1-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.1",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.2-dev",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.12.2",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.13.0-dev",
- .is_supported = .if_not_strict,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.13.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.13.1-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.1",
- .runtime_zig_version = "0.13.1",
- .is_supported = .no,
- },
- // Minimum Zig Version: 0.12.0-dev.5
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.11.0-dev",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.11.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.0-dev.1",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.0-dev.4",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.0-dev.5",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.0-dev.10",
- .is_supported = .yes,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.0",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.12.1",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.13.0-dev.10",
- .is_supported = .no,
- },
- .{
- .minimum_runtime_zig_version = "0.12.0-dev.5",
- .runtime_zig_version = "0.13.0",
- .is_supported = .no,
- },
-};
diff --git a/src/build_runner/shared.zig b/src/build_runner/shared.zig
deleted file mode 100644
index 486fe5a08e..0000000000
--- a/src/build_runner/shared.zig
+++ /dev/null
@@ -1,158 +0,0 @@
-//! Contains shared code between ZLS and it's custom build runner
-
-const std = @import("std");
-const builtin = @import("builtin");
-
-pub const BuildConfig = struct {
- /// The `dependencies` in `build.zig.zon`.
- dependencies: std.json.ArrayHashMap([]const u8),
- /// The key is the `root_source_file`.
- /// All modules with the same root source file are merged. This limitation may be lifted in the future.
- modules: std.json.ArrayHashMap(Module),
- /// List of all compilations units.
- compilations: []const Compile,
- /// The names of all top level steps.
- top_level_steps: []const []const u8,
- available_options: std.json.ArrayHashMap(AvailableOption),
-
- pub const Module = struct {
- import_table: std.json.ArrayHashMap([]const u8),
- };
-
- pub const Compile = struct {
- /// Key in `BuildConfig.modules`.
- root_module: []const u8,
-
- // may contain additional information in the future like `target` or `link_libc`.
- };
-
- /// Equivalent to `std.Build.AvailableOption` which is not accessible because it non-pub.
- pub const AvailableOption = @FieldType(@FieldType(std.Build, "available_options_map").KV, "value");
-};
-
-pub const ServerToClient = struct {
- pub const Header = extern struct {
- tag: Tag,
- bytes_len: u32,
-
- pub const Tag = enum(u32) {
- /// Body is an ErrorBundle.
- watch_error_bundle,
-
- _,
- };
- };
-
- /// Trailing:
- /// * extra: [extra_len]u32,
- /// * string_bytes: [string_bytes_len]u8,
- /// See `std.zig.ErrorBundle`.
- pub const ErrorBundle = extern struct {
- step_id: u32,
- cycle: u32,
- extra_len: u32,
- string_bytes_len: u32,
- };
-};
-
-pub const BuildOnSaveSupport = union(enum) {
- supported,
- invalid_linux_kernel_version: if (builtin.os.tag == .linux) @FieldType(std.os.linux.utsname, "release") else noreturn,
- unsupported_linux_kernel_version: if (builtin.os.tag == .linux) std.SemanticVersion else noreturn,
- unsupported_zig_version: if (@TypeOf(os_support) == std.SemanticVersion) void else noreturn,
- unsupported_os: if (@TypeOf(os_support) == bool and !os_support) void else noreturn,
-
- // We can't rely on `std.Build.Watch.have_impl` because we need to
- // check the runtime Zig version instead of Zig version that ZLS
- // has been built with.
- pub const os_support = switch (builtin.os.tag) {
- .linux,
- .windows,
- .dragonfly,
- .freebsd,
- .netbsd,
- .openbsd,
- .ios,
- .macos,
- .tvos,
- .visionos,
- .watchos,
- .haiku,
- => true,
- else => false,
- };
-
- /// std.build.Watch requires `AT_HANDLE_FID` which is Linux 6.5+
- /// https://github.com/ziglang/zig/issues/20720
- pub const minimum_linux_version: std.SemanticVersion = .{ .major = 6, .minor = 5, .patch = 0 };
-
- /// Returns true if is comptime known that build on save is supported.
- pub inline fn isSupportedComptime() bool {
- if (!std.process.can_spawn) return false;
- if (builtin.single_threaded) return false;
- return true;
- }
-
- pub fn isSupportedRuntime(runtime_zig_version: std.SemanticVersion) BuildOnSaveSupport {
- comptime std.debug.assert(isSupportedComptime());
-
- if (builtin.os.tag == .linux) blk: {
- var utsname: std.os.linux.utsname = undefined;
- std.debug.assert(std.os.linux.uname(&utsname) == 0);
- const unparsed_version = std.mem.sliceTo(&utsname.release, 0);
- const version = parseUnameKernelVersion(unparsed_version) catch
- return .{ .invalid_linux_kernel_version = utsname.release };
-
- if (version.order(minimum_linux_version) != .lt) break :blk;
- std.debug.assert(version.build == null and version.pre == null); // Otherwise, returning the `std.SemanticVersion` would be unsafe
- return .{
- .unsupported_linux_kernel_version = version,
- };
- }
-
- switch (@TypeOf(os_support)) {
- bool => {
- if (!os_support) {
- return .unsupported_os;
- }
- },
- std.SemanticVersion => {
- if (runtime_zig_version.order(os_support) == .lt) {
- return .unsupported_zig_version;
- }
- },
- else => unreachable,
- }
-
- return .supported;
- }
-};
-
-/// Parses a Linux Kernel Version. The result will ignore pre-release and build metadata.
-fn parseUnameKernelVersion(kernel_version: []const u8) !std.SemanticVersion {
- const extra_index = for (kernel_version, 0..) |c, i| {
- switch (c) {
- '-', '+' => break i,
- else => continue,
- }
- } else null;
- const required = kernel_version[0..(extra_index orelse kernel_version.len)];
- var it = std.mem.splitScalar(u8, required, '.');
- return .{
- .major = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
- .minor = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
- .patch = try std.fmt.parseUnsigned(usize, it.next() orelse return error.InvalidVersion, 10),
- };
-}
-
-test parseUnameKernelVersion {
- try std.testing.expectFmt("5.17.0", "{f}", .{try parseUnameKernelVersion("5.17.0")});
- try std.testing.expectFmt("6.12.9", "{f}", .{try parseUnameKernelVersion("6.12.9-rc7")});
- try std.testing.expectFmt("6.6.71", "{f}", .{try parseUnameKernelVersion("6.6.71-42-generic")});
- try std.testing.expectFmt("5.15.167", "{f}", .{try parseUnameKernelVersion("5.15.167.4-microsoft-standard-WSL2")}); // WSL2
- try std.testing.expectFmt("4.4.0", "{f}", .{try parseUnameKernelVersion("4.4.0-20241-Microsoft")}); // WSL1
-
- try std.testing.expectError(error.InvalidCharacter, parseUnameKernelVersion(""));
- try std.testing.expectError(error.InvalidVersion, parseUnameKernelVersion("5"));
- try std.testing.expectError(error.InvalidVersion, parseUnameKernelVersion("5.5"));
-}
diff --git a/src/configuration.zig b/src/configuration.zig
index 138903bc70..54cb586106 100644
--- a/src/configuration.zig
+++ b/src/configuration.zig
@@ -17,6 +17,8 @@ pub const Manager = struct {
path: []const u8,
version: std.SemanticVersion,
env: Env,
+ /// Whether the `version` satifies the minimum runtime zig version.
+ supported: bool,
},
zig_lib_dir: ?std.Build.Cache.Directory,
global_cache_dir: ?std.Build.Cache.Directory,
@@ -24,14 +26,6 @@ pub const Manager = struct {
.wasi => std.process.Preopens,
else => void,
},
- build_runner_supported: union(enum) {
- /// If returned, guarantees `zig_exe != null`.
- yes,
- /// no suitable build runner could be resolved based on the `zig_exe`
- /// If returned, guarantees `zig_exe != null`.
- no,
- no_dont_error,
- },
impl: struct {
is_dirty: bool,
configs: std.EnumArray(Tag, UnresolvedConfig),
@@ -52,7 +46,6 @@ pub const Manager = struct {
.zig_exe = null,
.zig_lib_dir = null,
.global_cache_dir = null,
- .build_runner_supported = .no_dont_error,
.wasi_preopens = switch (builtin.os.tag) {
.wasi => try std.process.Preopens.init(arena_allocator.allocator()),
else => {},
@@ -182,10 +175,15 @@ pub const Manager = struct {
break :unresolved_zig;
};
+ const build_options = @import("build_options");
+ const is_zls_version_tagged = build_options.version.pre == null;
+ const min_runtime_zig_version = comptime std.SemanticVersion.parse(build_options.minimum_runtime_zig_version_string) catch unreachable;
+
manager.zig_exe = .{
.path = zig_env.zig_exe,
.version = zig_version,
.env = zig_env,
+ .supported = zigVersionCheck(min_runtime_zig_version, zig_version, is_zls_version_tagged),
};
}
config.zig_exe_path = if (manager.zig_exe) |exe| exe.path else null;
@@ -253,71 +251,6 @@ pub const Manager = struct {
comptime unreachable;
}
- if (config.build_runner_path == null) blk: {
- if (!std.process.can_spawn) break :blk;
- const zig_exe = manager.zig_exe orelse break :blk;
- const global_cache_dir = manager.global_cache_dir orelse break :blk;
-
- if (!@import("build_runner/check.zig").isBuildRunnerSupported(zig_exe.version)) {
- manager.build_runner_supported = .no;
- break :blk;
- }
-
- const build_runner_source = @embedFile("build_runner/build_runner.zig");
- const build_runner_config_source = @embedFile("build_runner/shared.zig");
-
- const build_runner_hash = get_hash: {
- const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
-
- var hasher: Hasher = .init(&@splat(0));
- hasher.update(build_runner_source);
- hasher.update(build_runner_config_source);
- break :get_hash hasher.finalResult();
- };
-
- const cache_path = try global_cache_dir.join(manager.allocator, &.{ "build_runner", &std.fmt.bytesToHex(build_runner_hash, .lower) });
- defer manager.allocator.free(cache_path);
-
- std.debug.assert(std.Io.Dir.path.isAbsolute(cache_path));
- var cache_dir = std.Io.Dir.cwd().createDirPathOpen(io, cache_path, .{}) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- else => {
- log.err("failed to open directory '{s}': {}", .{ cache_path, err });
- break :blk;
- },
- };
- defer cache_dir.close(io);
-
- cache_dir.writeFile(io, .{
- .sub_path = "shared.zig",
- .data = build_runner_config_source,
- .flags = .{ .exclusive = true },
- }) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- error.PathAlreadyExists => {},
- else => {
- log.err("failed to write file '{s}/shared.zig': {}", .{ cache_path, err });
- break :blk;
- },
- };
-
- cache_dir.writeFile(io, .{
- .sub_path = "build_runner.zig",
- .data = build_runner_source,
- .flags = .{ .exclusive = true },
- }) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- error.PathAlreadyExists => {},
- else => {
- log.err("failed to write file '{s}/build_runner.zig': {}", .{ cache_path, err });
- break :blk;
- },
- };
-
- config.build_runner_path = try std.Io.Dir.path.join(arena, &.{ cache_path, "build_runner.zig" });
- manager.build_runner_supported = .yes;
- }
-
if (config.builtin_path == null) blk: {
if (!std.process.can_spawn) break :blk;
const zig_exe = manager.zig_exe orelse break :blk;
@@ -672,7 +605,6 @@ pub const FileConfigInfo = struct {
pub const file_system_config_options: []const FileConfigInfo = &.{
.{ .name = "zig_exe_path", .kind = .file, .is_accessible = true },
.{ .name = "builtin_path", .kind = .file, .is_accessible = true },
- .{ .name = "build_runner_path", .kind = .file, .is_accessible = true },
.{ .name = "zig_lib_path", .kind = .directory, .is_accessible = true },
.{ .name = "global_cache_path", .kind = .directory, .is_accessible = false },
};
@@ -775,3 +707,285 @@ pub fn findZig(
}
return null;
}
+
+fn zigVersionCheck(
+ minimum_zig_version: std.SemanticVersion,
+ param_zig_version: std.SemanticVersion,
+ /// If set, a non-tagged zig version cannot be used when `minimum_runtime_zig_version` is a tagged version.
+ /// Example: Zig `0.13.0-dev` cannot be used when the minimum Zig version is `0.12.0`.
+ ///
+ /// Will be set iff ZLS is a tagged release.
+ strict: bool,
+) bool {
+ const minimum_version_is_tagged = minimum_zig_version.pre == null;
+
+ var zig_version = param_zig_version;
+ var version_is_tagged = zig_version.pre == null;
+
+ if (!version_is_tagged and zig_version.patch != 0) {
+ // A zig version like `0.12.2-dev` has the same compatibility as `0.12.1`
+ zig_version.patch -= 1;
+ zig_version.pre = null;
+ version_is_tagged = true;
+ }
+
+ if (strict and !version_is_tagged) {
+ std.debug.assert(minimum_version_is_tagged);
+ // A tagged release of ZLS must be used with a tagged release of Zig.
+ return false;
+ }
+
+ if (zig_version.major != minimum_zig_version.major) return false;
+
+ if (minimum_version_is_tagged) {
+ if (version_is_tagged) {
+ if (zig_version.order(minimum_zig_version) == .lt) return false;
+ const next_minor_release: std.SemanticVersion = .{
+ .major = minimum_zig_version.major,
+ .minor = minimum_zig_version.minor + 1,
+ .patch = 0,
+ };
+ return zig_version.order(next_minor_release) == .lt;
+ } else {
+ std.debug.assert(zig_version.patch == 0);
+ return zig_version.minor == 1 + minimum_zig_version.minor;
+ }
+ } else {
+ if (version_is_tagged) return false;
+ if (zig_version.minor != minimum_zig_version.minor) return false;
+ return zig_version.order(minimum_zig_version) != .lt;
+ }
+}
+
+test {
+ const build_options = @import("build_options");
+ const current_zig_version = @import("builtin").zig_version;
+ const is_zls_version_tagged = build_options.version.pre == null;
+ const min_runtime_zig_version = comptime std.SemanticVersion.parse(build_options.minimum_runtime_zig_version_string) catch unreachable;
+
+ // The build runner must support the Zig version that ZLS is being built with
+ try std.testing.expect(zigVersionCheck(
+ min_runtime_zig_version,
+ current_zig_version,
+ is_zls_version_tagged,
+ ));
+
+ if (is_zls_version_tagged) {
+ // A tagged release of ZLS should support the same tagged release of Zig
+ // Example: ZLS 0.12.0 should support Zig 0.12.x -- It is possible that ZLS requires a minimum patch version
+ try std.testing.expect(zigVersionCheck(
+ .{ .major = current_zig_version.major, .minor = current_zig_version.minor, .patch = 999 },
+ min_runtime_zig_version,
+ is_zls_version_tagged,
+ ));
+ }
+}
+
+// Version order for reference:
+// 0.11.0-dev < 0.11.0 < 0.12.0-dev < 0.12.0 < 0.13.0-dev < 0.13.0
+
+test zigVersionCheck {
+ var did_fail = false;
+ for (test_cases) |test_case| {
+ const minimum_runtime_zig_version: std.SemanticVersion = try .parse(test_case.minimum_runtime_zig_version);
+ const runtime_zig_version: std.SemanticVersion = try .parse(test_case.runtime_zig_version);
+ const minimum_runtime_version_is_tagged = minimum_runtime_zig_version.pre == null;
+ const expected_if_strict, const expected_if_not_strict = switch (test_case.is_supported) {
+ .yes => .{ true, true },
+ .no => .{ false, false },
+ .if_not_strict => .{ false, true },
+ };
+
+ const actual_if_not_strict = zigVersionCheck(minimum_runtime_zig_version, runtime_zig_version, false);
+ if (expected_if_not_strict != actual_if_not_strict) {
+ std.debug.print("minimum={f}, actual={f}, strict={} -> expected {} but got {}\n", .{
+ minimum_runtime_zig_version,
+ runtime_zig_version,
+ false,
+ expected_if_not_strict,
+ actual_if_not_strict,
+ });
+ did_fail = true;
+ }
+
+ if (minimum_runtime_version_is_tagged) {
+ const actual_if_strict = zigVersionCheck(minimum_runtime_zig_version, runtime_zig_version, true);
+ if (expected_if_strict != actual_if_strict) {
+ std.debug.print("minimum={f}, actual={f}, strict={} -> expected {} but got {}\n", .{
+ minimum_runtime_zig_version,
+ runtime_zig_version,
+ true,
+ expected_if_strict,
+ actual_if_strict,
+ });
+ did_fail = true;
+ }
+ }
+ }
+ if (did_fail) return error.Unexpected;
+}
+
+const test_cases: []const struct {
+ minimum_runtime_zig_version: []const u8,
+ runtime_zig_version: []const u8,
+ is_supported: enum { yes, no, if_not_strict },
+} = &.{
+ // Minimum Zig Version: 0.12.0
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.11.0-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.11.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.12.0-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.12.0",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.12.1-dev",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.12.1",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.13.0-dev",
+ .is_supported = .if_not_strict,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.13.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0",
+ .runtime_zig_version = "0.13.1-dev",
+ .is_supported = .no,
+ },
+ // Minimum Zig Version: 0.12.1
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.11.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.11.1",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.0-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.1-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.1",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.2-dev",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.12.2",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.13.0-dev",
+ .is_supported = .if_not_strict,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.13.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.13.1-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.1",
+ .runtime_zig_version = "0.13.1",
+ .is_supported = .no,
+ },
+ // Minimum Zig Version: 0.12.0-dev.5
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.11.0-dev",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.11.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.0-dev.1",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.0-dev.4",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.0-dev.5",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.0-dev.10",
+ .is_supported = .yes,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.0",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.12.1",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.13.0-dev.10",
+ .is_supported = .no,
+ },
+ .{
+ .minimum_runtime_zig_version = "0.12.0-dev.5",
+ .runtime_zig_version = "0.13.0",
+ .is_supported = .no,
+ },
+};
diff --git a/src/features/diagnostics.zig b/src/features/diagnostics.zig
index 2690a9ae88..1c259010ad 100644
--- a/src/features/diagnostics.zig
+++ b/src/features/diagnostics.zig
@@ -495,255 +495,3 @@ pub fn getErrorBundleFromStderr(
return try error_bundle.toOwnedBundle("");
}
-
-pub const BuildOnSave = struct {
- io: std.Io,
- allocator: std.mem.Allocator,
- worker: std.Io.Future(void),
- worker_state: *WorkerState,
-
- const shared = @import("../build_runner/shared.zig");
- const ServerToClient = shared.ServerToClient;
-
- const WorkerState = struct {
- mutex: std.Io.Mutex,
- child_process: std.process.Child,
- };
-
- pub const InitOptions = struct {
- io: std.Io,
- allocator: std.mem.Allocator,
- workspace_path: []const u8,
- build_on_save_args: []const []const u8,
- check_step_only: bool,
- zig_exe_path: []const u8,
- zig_lib_path: []const u8,
- build_runner_path: []const u8,
-
- collection: *DiagnosticsCollection,
- };
-
- pub fn init(options: InitOptions) error{ Canceled, ConcurrencyUnavailable, OutOfMemory }!?BuildOnSave {
- const base_args: []const []const u8 = &.{
- options.zig_exe_path,
- "build",
- "--build-runner",
- options.build_runner_path,
- "--zig-lib-dir",
- options.zig_lib_path,
- "--watch",
- };
- var argv: std.ArrayList([]const u8) = try .initCapacity(
- options.allocator,
- base_args.len + options.build_on_save_args.len + @intFromBool(options.check_step_only),
- );
- defer argv.deinit(options.allocator);
-
- argv.appendSliceAssumeCapacity(base_args);
- if (options.check_step_only) argv.appendAssumeCapacity("--check-only");
- argv.appendSliceAssumeCapacity(options.build_on_save_args);
-
- var child_process = std.process.spawn(options.io, .{
- .argv = argv.items,
- .stdin = .pipe,
- .stdout = .pipe,
- .stderr = .pipe,
- .cwd = .{ .path = options.workspace_path },
- }) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- else => {
- log.err("failed to spawn zig build-on-save process: {}", .{err});
- return null;
- },
- };
- errdefer child_process.kill(options.io);
-
- const duped_workspace_path = try options.allocator.dupe(u8, options.workspace_path);
- errdefer options.allocator.free(duped_workspace_path);
-
- const worker_state = try options.allocator.create(WorkerState);
- errdefer options.allocator.destroy(worker_state);
-
- worker_state.* = .{
- .mutex = .init,
- .child_process = child_process,
- };
-
- const worker = try options.io.concurrent(loop, .{
- options.io,
- options.allocator,
- worker_state,
- options.collection,
- duped_workspace_path,
- });
- errdefer comptime unreachable;
-
- return .{
- .io = options.io,
- .allocator = options.allocator,
- .worker = worker,
- .worker_state = worker_state,
- };
- }
-
- pub fn deinit(self: *BuildOnSave) void {
- self.worker.cancel(self.io);
- self.allocator.destroy(self.worker_state);
- self.* = undefined;
- }
-
- pub fn sendManualWatchUpdate(build_on_save: *BuildOnSave) void {
- const io = build_on_save.io;
-
- build_on_save.worker_state.mutex.lockUncancelable(io);
- defer build_on_save.worker_state.mutex.unlock(io);
- if (build_on_save.worker_state.child_process.stdin) |stdin| {
- const old_cancel_protect = io.swapCancelProtection(.blocked);
- defer _ = io.swapCancelProtection(old_cancel_protect);
- stdin.writeStreamingAll(io, "\x00") catch {};
- }
- }
-
- fn loop(
- io: std.Io,
- allocator: std.mem.Allocator,
- state: *WorkerState,
- collection: *DiagnosticsCollection,
- workspace_path: []const u8,
- ) void {
- defer allocator.free(workspace_path);
-
- var multi_reader_buffer: std.Io.File.MultiReader.Buffer(2) = undefined;
- var multi_reader: std.Io.File.MultiReader = undefined;
- defer multi_reader.deinit();
- multi_reader.init(
- allocator,
- io,
- multi_reader_buffer.toStreams(),
- &.{ state.child_process.stdout.?, state.child_process.stderr.? },
- );
-
- const stdout = multi_reader.reader(0);
- const stderr = multi_reader.reader(1);
-
- var diagnostic_tags: std.array_hash_map.Auto(DiagnosticsCollection.Tag, void) = .empty;
- defer diagnostic_tags.deinit(allocator);
-
- defer {
- for (diagnostic_tags.keys()) |tag| collection.clearErrorBundle(tag);
- collection.publishDiagnostics() catch {
- // cancellation should be fine since we are returning anyway
- };
- }
-
- var did_print_status = false;
- outer: while (true) {
- while (stdout.bufferedLen() < @sizeOf(ServerToClient.Header)) {
- multi_reader.fill(64, .none) catch |err| switch (err) {
- error.Canceled, error.EndOfStream => break :outer,
- else => break :outer log.err("failed to receive message from zig build-on-save runner: {}", .{err}),
- };
- }
- const header = stdout.takeStruct(ServerToClient.Header, .little) catch unreachable;
- while (stdout.bufferedLen() < header.bytes_len) {
- multi_reader.fill(64, .none) catch |err| switch (err) {
- error.Canceled => break :outer,
- else => break :outer log.err("failed to receive message from zig build-on-save runner: {}", .{err}),
- };
- }
- multi_reader.checkAnyError() catch |err| switch (err) {
- error.Canceled => break :outer,
- else => break :outer log.err("failed to receive message from zig build-on-save runner: {}", .{err}),
- };
- const body = stdout.take(header.bytes_len) catch unreachable;
-
- if (!did_print_status) {
- log.info("Build-On-Save is running for '{s}'", .{workspace_path});
- did_print_status = true;
- }
-
- switch (header.tag) {
- .watch_error_bundle => {
- handleWatchErrorBundle(
- allocator,
- body,
- collection,
- workspace_path,
- &diagnostic_tags,
- ) catch |err| switch (err) {
- error.Canceled => break :outer,
- else => |e| log.err("failed to handle error bundle message from zig build-on-save runner: {}", .{e}),
- };
- },
- else => |tag| {
- log.warn("received unexpected message from zig build-on-save runner: {}", .{tag});
- break :outer;
- },
- }
- }
-
- const old_cancel_protect = io.swapCancelProtection(.blocked);
- defer _ = io.swapCancelProtection(old_cancel_protect);
-
- state.mutex.lockUncancelable(io);
- defer state.mutex.unlock(io);
-
- state.child_process.stdin.?.close(io);
- state.child_process.stdin = null;
-
- const term = state.child_process.wait(io) catch |err| {
- log.warn("Failed to await zig build-on-save runner: {}", .{err});
- return;
- };
-
- const stderr_msg_prefix = if (stderr.bufferedLen() > 0) " and stderr:\n" else "";
-
- switch (term) {
- .exited => |code| if (code != 0) log.warn("zig build-on-save runner exited with with code: {d}{s}{s}", .{ code, stderr_msg_prefix, stderr.buffered() }),
- .signal => |sig| std.log.err("zig build-on-save runner failed with signal {t}{s}{s}", .{ sig, stderr_msg_prefix, stderr.buffered() }),
- .stopped => |sig| std.log.err("zig build-on-save runner stopped with signal {d}{s}{s}", .{ sig, stderr_msg_prefix, stderr.buffered() }),
- .unknown => |code| std.log.err("zig build-on-save runner failed for unknown reason with code {d}{s}{s}", .{ code, stderr_msg_prefix, stderr.buffered() }),
- }
- }
-
- fn handleWatchErrorBundle(
- allocator: std.mem.Allocator,
- body: []u8,
- collection: *DiagnosticsCollection,
- workspace_path: []const u8,
- diagnostic_tags: *std.array_hash_map.Auto(DiagnosticsCollection.Tag, void),
- ) (error{ OutOfMemory, InvalidMessage } || std.Io.File.Writer.Error)!void {
- var reader: std.Io.Reader = .fixed(body);
-
- const header = reader.takeStruct(ServerToClient.ErrorBundle, .little) catch return error.InvalidMessage;
-
- const extra = reader.readSliceEndianAlloc(allocator, u32, header.extra_len, .little) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.EndOfStream => return error.InvalidMessage,
- error.ReadFailed => unreachable,
- };
- defer allocator.free(extra);
-
- const string_bytes = reader.readAlloc(allocator, header.string_bytes_len) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.EndOfStream => return error.InvalidMessage,
- error.ReadFailed => unreachable,
- };
- defer allocator.free(string_bytes);
-
- if (reader.bufferedLen() != 0) return error.InvalidMessage; // ensure that we read the entire body
-
- const error_bundle: std.zig.ErrorBundle = .{ .string_bytes = string_bytes, .extra = extra };
-
- var hasher: std.hash.Wyhash = .init(0);
- hasher.update(workspace_path);
- std.hash.autoHash(&hasher, header.step_id);
-
- const diagnostic_tag: DiagnosticsCollection.Tag = @enumFromInt(@as(u32, @truncate(hasher.final())));
-
- try diagnostic_tags.put(allocator, diagnostic_tag, {});
-
- try collection.pushErrorBundle(diagnostic_tag, header.cycle, workspace_path, error_bundle);
- try collection.publishDiagnostics();
- }
-};
diff --git a/src/tools/config.json b/src/tools/config.json
index 3bfd193df6..e0febf3b06 100644
--- a/src/tools/config.json
+++ b/src/tools/config.json
@@ -131,12 +131,6 @@
"type": "?[]const u8",
"default": null
},
- {
- "name": "build_runner_path",
- "description": "Specify a custom build runner to resolve build system information.",
- "type": "?[]const u8",
- "default": null
- },
{
"name": "global_cache_path",
"description": "Path to a directory that will be used as zig's cache. Will default to `${KnownFolders.Cache}/zls`.",
diff --git a/src/zls.zig b/src/zls.zig
index 485a1e7145..657f6e655e 100644
--- a/src/zls.zig
+++ b/src/zls.zig
@@ -36,7 +36,4 @@ pub const signature_help = @import("features/signature_help.zig");
comptime {
const std = @import("std");
std.testing.refAllDecls(@This());
-
- std.testing.refAllDecls(@import("build_runner/check.zig"));
- std.testing.refAllDecls(@import("build_runner/shared.zig"));
}
diff --git a/tests/add_build_runner_cases.zig b/tests/add_build_runner_cases.zig
deleted file mode 100644
index 7e19e92308..0000000000
--- a/tests/add_build_runner_cases.zig
+++ /dev/null
@@ -1,78 +0,0 @@
-//! This file is imported by `../build.zig` to add build runner tests to the build system.
-//! See the `./build_runner_cases` subdirectory.
-
-const std = @import("std");
-
-pub fn addCases(
- b: *std.Build,
- test_step: *std.Build.Step,
- test_filters: []const []const u8,
-) void {
- const cases_path = "tests/build_runner_cases";
-
- const global_cache: std.Build.LazyPath = .{ .relative = .{ .base = .global_cache } };
-
- const check_exe = b.addExecutable(.{
- .name = "build_runner_check",
- .root_module = b.createModule(.{
- .root_source_file = b.path("tests/build_runner_check.zig"),
- .target = b.graph.host,
- .imports = &.{
- .{ .name = "zls", .module = b.modules.get("zls").? },
- },
- }),
- });
-
- b.dependOnDirectory(b.path(cases_path));
-
- var dir = b.root.openDir(b.graph.io, cases_path, .{ .iterate = true }) catch |err|
- std.debug.panic("failed to open '{f}': {}", .{ b.path(cases_path), err });
- defer dir.close(b.graph.io);
-
- var it = dir.iterate();
-
- while (true) {
- const entry = it.next(b.graph.io) catch |err|
- std.debug.panic("failed to walk directory '{f}': {}", .{ b.path(cases_path), err }) orelse break;
-
- if (entry.kind != .file) continue;
- if (!std.mem.eql(u8, std.Io.Dir.path.extension(entry.name), ".zig")) continue;
-
- for (test_filters) |test_filter| {
- if (std.mem.find(u8, entry.name, test_filter) != null) break;
- } else if (test_filters.len > 0) continue;
-
- const build_file = b.path(cases_path).path(b, entry.name);
- const expected_build_config_json = b.path(cases_path).path(b, std.Io.Dir.path.stem(entry.name));
-
- const build_cmd = std.Build.Step.Run.create(b, b.fmt("run build runner ({s})", .{entry.name}));
- build_cmd.addFileArg(.zig_exe);
- build_cmd.addArg("build");
- build_cmd.addArg("--build-file");
- build_cmd.addFileArg(build_file);
- build_cmd.addArg("--build-runner");
- build_cmd.addFileArg(b.path("src/build_runner/build_runner.zig"));
- build_cmd.addArg("--cache-dir");
- build_cmd.addDirectoryArg(.cache_root);
- build_cmd.addArg("--global-cache-dir");
- build_cmd.addDirectoryArg(global_cache);
- build_cmd.addArg("--zig-lib-dir");
- build_cmd.addDirectoryArg(.zig_lib);
-
- build_cmd.addFileInput(b.path("src/build_runner/shared.zig"));
-
- const actual_build_config_json = build_cmd.captureStdOut(.{});
-
- const run_diff = b.addRunArtifact(check_exe);
- run_diff.setName(b.fmt("run {s} ({s})", .{ check_exe.name, entry.name }));
- run_diff.setCwd(b.path(cases_path));
- run_diff.addFileArg(expected_build_config_json);
- run_diff.addFileArg(actual_build_config_json);
- run_diff.addArg("--cache-dir");
- run_diff.addDirectoryArg(.cache_root);
- run_diff.addArg("--global-cache-dir");
- run_diff.addDirectoryArg(global_cache);
-
- test_step.dependOn(&run_diff.step);
- }
-}
diff --git a/tests/analysis_check.zig b/tests/analysis_check.zig
index 7ada71e535..af882f40e2 100644
--- a/tests/analysis_check.zig
+++ b/tests/analysis_check.zig
@@ -103,7 +103,6 @@ pub fn main(init: std.process.Init) Error!void {
.environ_map = &environ_map,
.zig_exe_path = zig_exe_path,
.zig_lib_dir = zig_lib_dir,
- .build_runner_path = null,
.builtin_path = null,
.global_cache_dir = null,
.wasi_preopens = switch (builtin.target.os.tag) {
diff --git a/tests/build_runner_cases/add_module.json b/tests/build_runner_cases/add_module.json
deleted file mode 100644
index cd2bb42097..0000000000
--- a/tests/build_runner_cases/add_module.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "root.zig": {
- "import_table": {}
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/add_module.zig b/tests/build_runner_cases/add_module.zig
deleted file mode 100644
index 30bef6678c..0000000000
--- a/tests/build_runner_cases/add_module.zig
+++ /dev/null
@@ -1,7 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- _ = b.addModule("foo", .{
- .root_source_file = b.path("root.zig"),
- });
-}
diff --git a/tests/build_runner_cases/add_options.json b/tests/build_runner_cases/add_options.json
deleted file mode 100644
index e91002f54b..0000000000
--- a/tests/build_runner_cases/add_options.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "root.zig": {
- "import_table": {
- "options": ".zig-local-cache/options.zig"
- }
- },
- ".zig-local-cache/options.zig": {
- "import_table": {}
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/add_options.zig b/tests/build_runner_cases/add_options.zig
deleted file mode 100644
index 10b98eb10b..0000000000
--- a/tests/build_runner_cases/add_options.zig
+++ /dev/null
@@ -1,11 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const module = b.addModule("root", .{
- .root_source_file = b.path("root.zig"),
- });
-
- const options = b.addOptions();
- options.addOption(bool, "enabled", true);
- module.addOptions("options", options);
-}
diff --git a/tests/build_runner_cases/empty.json b/tests/build_runner_cases/empty.json
deleted file mode 100644
index 8faf4b9590..0000000000
--- a/tests/build_runner_cases/empty.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "dependencies": {},
- "modules": {},
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/empty.zig b/tests/build_runner_cases/empty.zig
deleted file mode 100644
index 5b3ba7fda8..0000000000
--- a/tests/build_runner_cases/empty.zig
+++ /dev/null
@@ -1,5 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- _ = b;
-}
diff --git a/tests/build_runner_cases/module_root_source_file_collision.json b/tests/build_runner_cases/module_root_source_file_collision.json
deleted file mode 100644
index 529d905338..0000000000
--- a/tests/build_runner_cases/module_root_source_file_collision.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "main.zig": {
- "import_table": {
- "collision": "first.zig",
- "first": "first.zig",
- "second": "second.zig"
- }
- },
- "first.zig": {
- "import_table": {}
- },
- "second.zig": {
- "import_table": {}
- },
- "third.zig": {
- "import_table": {}
- }
- },
- "compilations": [
- {
- "root_module": "main.zig"
- }
- ],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/module_root_source_file_collision.zig b/tests/build_runner_cases/module_root_source_file_collision.zig
deleted file mode 100644
index cb5b4d5b27..0000000000
--- a/tests/build_runner_cases/module_root_source_file_collision.zig
+++ /dev/null
@@ -1,37 +0,0 @@
-//! There are three different module with `main.zig` as their root source file
-//! and every one of them defines an import with the name `collision`.
-
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const first = b.createModule(.{ .root_source_file = b.path("first.zig") });
- const second = b.createModule(.{ .root_source_file = b.path("second.zig") });
- const third = b.createModule(.{ .root_source_file = b.path("third.zig") });
-
- const exe = b.addExecutable(.{
- .name = "exe",
- .root_module = b.createModule(.{
- .root_source_file = b.path("main.zig"),
- .target = b.graph.host,
- .imports = &.{
- .{ .name = "collision", .module = third },
- },
- }),
- });
- b.installArtifact(exe);
-
- _ = b.addModule("foo", .{
- .root_source_file = b.path("main.zig"),
- .imports = &.{
- .{ .name = "collision", .module = first },
- .{ .name = "first", .module = first },
- },
- });
- _ = b.addModule("bar", .{
- .root_source_file = b.path("main.zig"),
- .imports = &.{
- .{ .name = "collision", .module = second },
- .{ .name = "second", .module = second },
- },
- });
-}
diff --git a/tests/build_runner_cases/module_self_import.json b/tests/build_runner_cases/module_self_import.json
deleted file mode 100644
index 08ca05c395..0000000000
--- a/tests/build_runner_cases/module_self_import.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "root.zig": {
- "import_table": {
- "bar": "root.zig"
- }
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/module_self_import.zig b/tests/build_runner_cases/module_self_import.zig
deleted file mode 100644
index dc29a955f7..0000000000
--- a/tests/build_runner_cases/module_self_import.zig
+++ /dev/null
@@ -1,9 +0,0 @@
-const std = @import("std");
-
-// https://github.com/zigtools/zls/issues/2117
-pub fn build(b: *std.Build) void {
- const module = b.addModule("foo", .{
- .root_source_file = b.path("root.zig"),
- });
- module.addImport("bar", module);
-}
diff --git a/tests/build_runner_cases/multiple_module_import_names.json b/tests/build_runner_cases/multiple_module_import_names.json
deleted file mode 100644
index 7b3be8ba7c..0000000000
--- a/tests/build_runner_cases/multiple_module_import_names.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "foo.zig": {
- "import_table": {
- "bar_in_foo": "bar.zig"
- }
- },
- "bar.zig": {
- "import_table": {
- "foo_in_bar": "foo.zig"
- }
- },
- "main.zig": {
- "import_table": {
- "foo_in_main": "foo.zig",
- "bar_in_main": "bar.zig"
- }
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/multiple_module_import_names.zig b/tests/build_runner_cases/multiple_module_import_names.zig
deleted file mode 100644
index 352813d9b5..0000000000
--- a/tests/build_runner_cases/multiple_module_import_names.zig
+++ /dev/null
@@ -1,18 +0,0 @@
-const std = @import("std");
-
-// https://github.com/zigtools/zls/issues/2118
-pub fn build(b: *std.Build) void {
- const foo = b.addModule("foo", .{ .root_source_file = b.path("foo.zig") });
- const bar = b.addModule("bar", .{ .root_source_file = b.path("bar.zig") });
-
- foo.addImport("bar_in_foo", bar);
- bar.addImport("foo_in_bar", foo);
-
- _ = b.addModule("main", .{
- .root_source_file = b.path("main.zig"),
- .imports = &.{
- .{ .name = "foo_in_main", .module = foo },
- .{ .name = "bar_in_main", .module = bar },
- },
- });
-}
diff --git a/tests/build_runner_cases/no_root_source_file.json b/tests/build_runner_cases/no_root_source_file.json
deleted file mode 100644
index ea65f5e235..0000000000
--- a/tests/build_runner_cases/no_root_source_file.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "baz.zig": {
- "import_table": {}
- },
- "bar.zig": {
- "import_table": {
- "with-root-source-file": "baz.zig"
- }
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/no_root_source_file.zig b/tests/build_runner_cases/no_root_source_file.zig
deleted file mode 100644
index 6c2628d170..0000000000
--- a/tests/build_runner_cases/no_root_source_file.zig
+++ /dev/null
@@ -1,19 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const with_root_source_file = b.createModule(.{ .root_source_file = b.path("baz.zig") });
- const without_root_source_file = b.createModule(.{});
- _ = b.addModule("foo", .{
- .imports = &.{
- .{ .name = "with-root-source-file", .module = with_root_source_file },
- .{ .name = "without-root-source-file", .module = without_root_source_file },
- },
- });
- _ = b.addModule("bar", .{
- .root_source_file = b.path("bar.zig"),
- .imports = &.{
- .{ .name = "with-root-source-file", .module = with_root_source_file },
- .{ .name = "without-root-source-file", .module = without_root_source_file },
- },
- });
-}
diff --git a/tests/build_runner_cases/public_module_with_generated_file.json b/tests/build_runner_cases/public_module_with_generated_file.json
deleted file mode 100644
index d89b8f79d6..0000000000
--- a/tests/build_runner_cases/public_module_with_generated_file.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- ".zig-local-cache/generated.zig": {
- "import_table": {}
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/public_module_with_generated_file.zig b/tests/build_runner_cases/public_module_with_generated_file.zig
deleted file mode 100644
index e03c2f002e..0000000000
--- a/tests/build_runner_cases/public_module_with_generated_file.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const write_files = b.addWriteFiles();
- const generated = write_files.add("generated.zig", "");
-
- _ = b.addModule("root", .{
- .root_source_file = generated,
- });
-}
diff --git a/tests/build_runner_check.zig b/tests/build_runner_check.zig
deleted file mode 100644
index 3ea9458646..0000000000
--- a/tests/build_runner_check.zig
+++ /dev/null
@@ -1,113 +0,0 @@
-//! This file implements a standalone executable that is used by
-//! `add_build_runner_cases.zig` to run build runner tests.
-//! See the `./build_runner_cases` subdirectory.
-
-const std = @import("std");
-const zls = @import("zls");
-
-pub fn main(init: std.process.Init) !u8 {
- const io = init.io;
- const gpa = init.gpa;
- const arena_allocator = init.arena.allocator();
- const args = try init.minimal.args.toSlice(arena_allocator);
-
- if (args.len != 7) @panic("invalid arguments");
-
- const cwd = try std.process.currentPathAlloc(io, gpa);
- defer gpa.free(cwd);
-
- const expected = std.Io.Dir.cwd().readFileAlloc(io, args[1], gpa, .limited(16 * 1024 * 1024)) catch |err|
- std.debug.panic("could no open/read file '{s}': {}", .{ args[1], err });
- defer gpa.free(expected);
-
- const actual_unsanitized = std.Io.Dir.cwd().readFileAlloc(io, args[2], gpa, .limited(16 * 1024 * 1024)) catch |err|
- std.debug.panic("could no open/read file '{s}': {}", .{ args[2], err });
- defer gpa.free(actual_unsanitized);
-
- std.debug.assert(std.mem.eql(u8, args[3], "--cache-dir"));
- const local_cache_dir = try std.Io.Dir.path.resolve(gpa, &.{ cwd, args[4] });
- defer gpa.free(local_cache_dir);
-
- std.debug.assert(std.mem.eql(u8, args[5], "--global-cache-dir"));
- const global_cache_dir = try std.Io.Dir.path.resolve(gpa, &.{ cwd, args[6] });
- defer gpa.free(global_cache_dir);
-
- const actual_sanitized = sanitized: {
- const parsed = try std.json.parseFromSlice(zls.DocumentStore.BuildConfig, gpa, actual_unsanitized, .{});
- defer parsed.deinit();
-
- var new: zls.DocumentStore.BuildConfig = parsed.value;
- const arena = parsed.arena.allocator();
-
- for (new.dependencies.map.keys()) |*str| str.* = try sanitizePath(arena, str.*, cwd, local_cache_dir, global_cache_dir);
- try new.dependencies.map.reIndex(arena);
-
- for (new.modules.map.keys()) |*str| str.* = try sanitizePath(arena, str.*, cwd, local_cache_dir, global_cache_dir);
- try new.modules.map.reIndex(arena);
-
- for (new.modules.map.values()) |*mod| {
- for (mod.import_table.map.values()) |*str| str.* = try sanitizePath(arena, str.*, cwd, local_cache_dir, global_cache_dir);
- try mod.import_table.map.reIndex(arena);
- }
-
- for (new.compilations) |*compile| {
- @as(*[]const u8, @constCast(&compile.root_module)).* = try sanitizePath(arena, compile.root_module, cwd, local_cache_dir, global_cache_dir);
- }
-
- break :sanitized try std.json.Stringify.valueAlloc(
- gpa,
- new,
- .{ .whitespace = .indent_2 },
- );
- };
- defer gpa.free(actual_sanitized);
-
- if (std.mem.eql(u8, expected, actual_sanitized)) return 0;
-
- zls.testing.renderLineDiff(gpa, expected, actual_sanitized);
-
- return 1;
-}
-
-fn stripBasePath(base_dir: []const u8, path: []const u8) ?[]const u8 {
- if (!std.mem.startsWith(u8, path, base_dir)) return null;
- if (!std.mem.startsWith(u8, path[base_dir.len..], std.Io.Dir.path.sep_str)) return null;
- return path[base_dir.len + std.Io.Dir.path.sep_str.len ..];
-}
-
-fn sanitizePath(
- arena: std.mem.Allocator,
- path: []const u8,
- cwd: []const u8,
- local_cache_dir: []const u8,
- global_cache_dir: []const u8,
-) error{OutOfMemory}![]const u8 {
- const new = try arena.dupe(u8, new: {
- if (stripBasePath(cwd, path)) |foo| {
- break :new foo;
- }
- if (stripBasePath(local_cache_dir, path)) |to| {
- var it = std.Io.Dir.path.componentIterator(to);
- _ = it.next().?; // skip cache namespace (o, c, h, z, tmp, ...)
- std.debug.assert(it.next().?.name.len == std.Build.Cache.hex_digest_len);
- break :new try std.fmt.allocPrint(arena, ".zig-local-cache/{s}", .{to[it.end_index + 1 ..]});
- }
- if (stripBasePath(global_cache_dir, path)) |to| {
- var it = std.Io.Dir.path.componentIterator(to);
- _ = it.next().?; // skip cache namespace (o, c, h, z, tmp, ...)
- std.debug.assert(it.next().?.name.len == std.Build.Cache.hex_digest_len);
- break :new try std.fmt.allocPrint(arena, ".zig-global-cache/{s}", .{to[it.end_index + 1 ..]});
- }
- std.debug.assert(!std.Io.Dir.path.isAbsolute(path)); // got an absolute path that is not in cwd or any cache dir
- break :new path;
- });
-
- // Convert windows style '\\' path separators to posix style '/'.
- if (std.Io.Dir.path.sep == '\\') {
- for (new) |*c| {
- if (c.* == std.Io.Dir.path.sep) c.* = '/';
- }
- }
-
- return new;
-}
From 93430bc33d0baeb9b97b5506977894c3d77c586b Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 22:40:48 +0200
Subject: [PATCH 09/14] workaround ziglang/zig#35766
---
build.zig | 5 +++--
tests/context.zig | 10 ++++++----
tests/lifecycle.zig | 5 +++--
tests/lsp_features/completion.zig | 6 ++++++
4 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/build.zig b/build.zig
index 9caeee023e..07c75fb379 100644
--- a/build.zig
+++ b/build.zig
@@ -66,8 +66,9 @@ pub fn build(b: *Build) !void {
test_options.step.name = "ZLS test options";
test_options.addOptionPath("zig_exe_path", .zig_exe);
- test_options.addOptionPath("zig_lib_path", .zig_lib);
- test_options.addOptionPath("global_cache_path", std.Build.LazyPath.cache_root.join(b.allocator, "zls") catch @panic("OOM"));
+ // https://codeberg.org/ziglang/zig/issues/35766
+ // test_options.addOptionPath("zig_lib_path", .zig_lib);
+ // test_options.addOptionPath("global_cache_path", std.Build.LazyPath.cache_root.join(b.allocator, "zls") catch @panic("OOM"));
break :blk test_options.createModule();
};
diff --git a/tests/context.zig b/tests/context.zig
index b3db24a05f..2f9419037c 100644
--- a/tests/context.zig
+++ b/tests/context.zig
@@ -35,8 +35,9 @@ pub const Context = struct {
const cwd = try std.process.currentPathAlloc(io, allocator);
defer allocator.free(cwd);
config.zig_exe_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, test_options.zig_exe_path });
- config.zig_lib_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, test_options.zig_lib_path });
- config.global_cache_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, test_options.global_cache_path });
+ // https://codeberg.org/ziglang/zig/issues/35766
+ // config.zig_lib_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, test_options.zig_lib_path });
+ // config.global_cache_path = try std.Io.Dir.path.resolve(allocator, &.{ cwd, test_options.global_cache_path });
}
const environ_map = try cached_config_arena.allocator().create(std.process.Environ.Map);
@@ -58,8 +59,9 @@ pub const Context = struct {
std.debug.assert(server.config_manager.zig_lib_dir != null);
std.debug.assert(server.document_store.config.zig_lib_dir != null);
- std.debug.assert(server.config_manager.global_cache_dir != null);
- std.debug.assert(server.document_store.config.global_cache_dir != null);
+ // https://codeberg.org/ziglang/zig/issues/35766
+ // std.debug.assert(server.config_manager.global_cache_dir != null);
+ // std.debug.assert(server.document_store.config.global_cache_dir != null);
var context: Context = .{
.server = server,
diff --git a/tests/lifecycle.zig b/tests/lifecycle.zig
index e89c8ed8f3..4dc52cf155 100644
--- a/tests/lifecycle.zig
+++ b/tests/lifecycle.zig
@@ -21,8 +21,9 @@ test "LSP lifecycle" {
try config_manager.setConfiguration(.frontend, &.{
.zig_exe_path = try std.Io.Dir.path.resolve(arena, &.{ cwd, test_options.zig_exe_path }),
- .zig_lib_path = try std.Io.Dir.path.resolve(arena, &.{ cwd, test_options.zig_lib_path }),
- .global_cache_path = try std.Io.Dir.path.resolve(arena, &.{ cwd, test_options.global_cache_path }),
+ // https://codeberg.org/ziglang/zig/issues/35766
+ // .zig_lib_path = try std.Io.Dir.path.resolve(arena, &.{ cwd, test_options.zig_lib_path }),
+ // .global_cache_path = try std.Io.Dir.path.resolve(arena, &.{ cwd, test_options.global_cache_path }),
});
}
diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig
index 71175dcffd..a0440acc45 100644
--- a/tests/lsp_features/completion.zig
+++ b/tests/lsp_features/completion.zig
@@ -3685,6 +3685,9 @@ test "empty doc comment" {
test "filesystem" {
if (@import("builtin").target.cpu.arch.isWasm()) return error.SkipZigTest;
+ // https://codeberg.org/ziglang/zig/issues/35766
+ if (true) return error.SkipZigTest;
+
try testCompletion(
\\const foo = @import("");
, &.{
@@ -3702,6 +3705,9 @@ test "filesystem" {
test "filesystem string literal ends with non ASCII symbol" {
if (@import("builtin").target.cpu.arch.isWasm()) return error.SkipZigTest;
+ // https://codeberg.org/ziglang/zig/issues/35766
+ if (true) return error.SkipZigTest;
+
try testCompletion(
\\const foo = @import(" 🠁
, &.{
From 0e2d9a0e67bde2a551633b6f42b6a57de3a28e5b Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Sat, 8 Aug 2026 00:24:31 +0200
Subject: [PATCH 10/14] replace `@enumFromInt` and `@intFromEnum` with
`@fromBackingInt` and `@backingInt`
---
src/DiagnosticsCollection.zig | 4 +-
src/DocumentScope.zig | 48 +++++-----
src/DocumentStore.zig | 2 +-
src/Server.zig | 24 ++---
src/TrigramStore.zig | 20 ++--
src/analyser/InternPool.zig | 124 ++++++++++++-------------
src/analyser/string_pool.zig | 18 ++--
src/analysis.zig | 22 ++---
src/ast.zig | 40 ++++----
src/features/code_actions.zig | 10 +-
src/features/completions.zig | 2 +-
src/features/diagnostics.zig | 2 +-
src/features/folding_range.zig | 2 +-
src/features/semantic_tokens.zig | 2 +-
src/features/workspace_symbols.zig | 6 +-
src/main.zig | 2 +-
src/testing.zig | 6 +-
tests/analysis/switch.zig | 2 +-
tests/lsp_features/semantic_tokens.zig | 2 +-
19 files changed, 169 insertions(+), 169 deletions(-)
diff --git a/src/DiagnosticsCollection.zig b/src/DiagnosticsCollection.zig
index 5e89cebbb0..fa47bdec4b 100644
--- a/src/DiagnosticsCollection.zig
+++ b/src/DiagnosticsCollection.zig
@@ -551,8 +551,8 @@ test DiagnosticsCollection {
}
{
- try collection.pushErrorBundle(@enumFromInt(16), 4, null, eb2);
- try collection.pushErrorBundle(@enumFromInt(17), 4, null, eb3);
+ try collection.pushErrorBundle(@fromBackingInt(16), 4, null, eb2);
+ try collection.pushErrorBundle(@fromBackingInt(17), 4, null, eb3);
var diagnostics: std.ArrayList(lsp.types.Diagnostic) = .empty;
try collection.collectLspDiagnosticsForDocument(uri, .@"utf-8", arena, &diagnostics);
diff --git a/src/DocumentScope.zig b/src/DocumentScope.zig
index a6ca091e9e..bb131e2605 100644
--- a/src/DocumentScope.zig
+++ b/src/DocumentScope.zig
@@ -126,7 +126,7 @@ pub const Declaration = union(enum) {
pub fn getVarDeclNode(self: AssignDestructure, tree: *const Ast) Ast.Node.Index {
const extra_index = tree.nodeData(self.node).extra_and_node[0];
- return @enumFromInt(tree.extra_data[@intFromEnum(extra_index) + 1 ..][self.index]);
+ return @fromBackingInt(tree.extra_data[@backingInt(extra_index) + 1 ..][self.index]);
}
pub fn getFullVarDecl(self: AssignDestructure, tree: *const Ast) Ast.full.VarDecl {
@@ -151,7 +151,7 @@ pub const Declaration = union(enum) {
_,
pub fn toOptional(index: Index) OptionalIndex {
- return @enumFromInt(@intFromEnum(index));
+ return @fromBackingInt(@backingInt(index));
}
};
@@ -161,7 +161,7 @@ pub const Declaration = union(enum) {
pub fn unwrap(index: OptionalIndex) ?Index {
if (index == .none) return null;
- return @enumFromInt(@intFromEnum(index));
+ return @fromBackingInt(@backingInt(index));
}
};
@@ -291,7 +291,7 @@ pub const Scope = struct {
_,
pub fn toOptional(index: Index) OptionalIndex {
- return @enumFromInt(@intFromEnum(index));
+ return @fromBackingInt(@backingInt(index));
}
};
@@ -302,7 +302,7 @@ pub const Scope = struct {
pub fn unwrap(index: OptionalIndex) ?Index {
if (index == .none) return null;
- return @enumFromInt(@intFromEnum(index));
+ return @fromBackingInt(@backingInt(index));
}
};
};
@@ -357,10 +357,10 @@ const ScopeContext = struct {
if (gop.found_existing) return;
try doc_scope.declarations.append(allocator, declaration);
- const declaration_index: Declaration.Index = @enumFromInt(doc_scope.declarations.len - 1);
+ const declaration_index: Declaration.Index = @fromBackingInt(@intCast(doc_scope.declarations.len - 1));
- const data = &doc_scope.scopes.items(.data)[@intFromEnum(pushed.scope)];
- const child_declarations = &doc_scope.scopes.items(.child_declarations)[@intFromEnum(pushed.scope)];
+ const data = &doc_scope.scopes.items(.data)[@backingInt(pushed.scope)];
+ const child_declarations = &doc_scope.scopes.items(.child_declarations)[@backingInt(pushed.scope)];
if (!data.is_child_decls_small) {
try context.child_declarations_scratch.append(allocator, declaration_index);
@@ -386,7 +386,7 @@ const ScopeContext = struct {
const allocator = context.allocator;
const slice = context.doc_scope.scopes.slice();
- const data = slice.items(.data)[@intFromEnum(pushed.scope)];
+ const data = slice.items(.data)[@backingInt(pushed.scope)];
if (!data.is_child_decls_small) {
const declaration_start = context.doc_scope.extra.items.len;
@@ -394,7 +394,7 @@ const ScopeContext = struct {
const declaration_end = context.doc_scope.extra.items.len;
context.child_declarations_scratch.items.len = pushed.declarations_start;
- slice.items(.child_declarations)[@intFromEnum(pushed.scope)] = .{
+ slice.items(.child_declarations)[@backingInt(pushed.scope)] = .{
.other = .{
.start = @intCast(declaration_start),
.end = @intCast(declaration_end),
@@ -408,7 +408,7 @@ const ScopeContext = struct {
const scope_end = context.doc_scope.extra.items.len;
context.child_scopes_scratch.items.len = pushed.scopes_start;
- slice.items(.child_scopes)[@intFromEnum(pushed.scope)] = .{
+ slice.items(.child_scopes)[@backingInt(pushed.scope)] = .{
.other = .{
.start = @intCast(scope_start),
.end = @intCast(scope_end),
@@ -440,7 +440,7 @@ const ScopeContext = struct {
.small = @splat(.none),
},
});
- const new_scope_index: Scope.Index = @enumFromInt(context.doc_scope.scopes.len - 1);
+ const new_scope_index: Scope.Index = @fromBackingInt(@intCast(context.doc_scope.scopes.len - 1));
if (context.current_scope.unwrap()) |parent_scope| {
try context.pushChildScope(parent_scope, new_scope_index);
}
@@ -462,8 +462,8 @@ const ScopeContext = struct {
const doc_scope = context.doc_scope;
const allocator = context.allocator;
- const data = &doc_scope.scopes.items(.data)[@intFromEnum(scope_index)];
- const child_scopes = &doc_scope.scopes.items(.child_scopes)[@intFromEnum(scope_index)];
+ const data = &doc_scope.scopes.items(.data)[@backingInt(scope_index)];
+ const child_scopes = &doc_scope.scopes.items(.child_scopes)[@backingInt(scope_index)];
if (!data.is_child_scopes_small) {
try context.child_scopes_scratch.append(allocator, child_scope_index);
@@ -1245,14 +1245,14 @@ pub fn getScopeTag(
doc_scope: DocumentScope,
scope: Scope.Index,
) Scope.Tag {
- return doc_scope.scopes.items(.data)[@intFromEnum(scope)].tag;
+ return doc_scope.scopes.items(.data)[@backingInt(scope)].tag;
}
pub fn getScopeParent(
doc_scope: DocumentScope,
scope: Scope.Index,
) Scope.OptionalIndex {
- return doc_scope.scopes.items(.parent_scope)[@intFromEnum(scope)];
+ return doc_scope.scopes.items(.parent_scope)[@backingInt(scope)];
}
pub fn getScopeAstNode(
@@ -1261,7 +1261,7 @@ pub fn getScopeAstNode(
) ?Ast.Node.Index {
const slice = doc_scope.scopes.slice();
- const data = slice.items(.data)[@intFromEnum(scope)];
+ const data = slice.items(.data)[@backingInt(scope)];
return switch (data.tag) {
.container, .function, .block => data.data.ast_node,
@@ -1274,7 +1274,7 @@ pub fn getScopeDeclaration(
lookup: DeclarationLookup,
) Declaration.OptionalIndex {
return if (doc_scope.declaration_lookup_map.getIndex(lookup)) |idx|
- @enumFromInt(idx)
+ @fromBackingInt(@intCast(idx))
else
.none;
}
@@ -1285,8 +1285,8 @@ pub fn getScopeDeclarationsConst(
) []const Declaration.Index {
const slice = doc_scope.scopes.slice();
- if (slice.items(.data)[@intFromEnum(scope)].is_child_decls_small) {
- const small = &slice.items(.child_declarations)[@intFromEnum(scope)].small;
+ if (slice.items(.data)[@backingInt(scope)].is_child_decls_small) {
+ const small = &slice.items(.child_declarations)[@backingInt(scope)].small;
for (0..Scope.ChildDeclarations.small_size) |idx| {
if (small[idx] == .none) {
@@ -1296,7 +1296,7 @@ pub fn getScopeDeclarationsConst(
return @ptrCast(small[0..Scope.ChildDeclarations.small_size]);
} else {
- const other = slice.items(.child_declarations)[@intFromEnum(scope)].other;
+ const other = slice.items(.child_declarations)[@backingInt(scope)].other;
return @ptrCast(doc_scope.extra.items[other.start..other.end]);
}
}
@@ -1307,8 +1307,8 @@ pub fn getScopeChildScopesConst(
) []const Scope.Index {
const slice = doc_scope.scopes.slice();
- if (slice.items(.data)[@intFromEnum(scope)].is_child_scopes_small) {
- const small = &slice.items(.child_scopes)[@intFromEnum(scope)].small;
+ if (slice.items(.data)[@backingInt(scope)].is_child_scopes_small) {
+ const small = &slice.items(.child_scopes)[@backingInt(scope)].small;
for (0..Scope.ChildScopes.small_size) |idx| {
if (small[idx] == .none) {
@@ -1318,7 +1318,7 @@ pub fn getScopeChildScopesConst(
return @ptrCast(small[0..Scope.ChildScopes.small_size]);
} else {
- const other = slice.items(.child_scopes)[@intFromEnum(scope)].other;
+ const other = slice.items(.child_scopes)[@backingInt(scope)].other;
return @ptrCast(doc_scope.extra.items[other.start..other.end]);
}
}
diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig
index 3ae09102c8..94247da787 100644
--- a/src/DocumentStore.zig
+++ b/src/DocumentStore.zig
@@ -480,7 +480,7 @@ pub const Handle = struct {
const node_tags = tree.nodes.items(.tag);
for (node_tags, 0..) |tag, i| {
- const node: Ast.Node.Index = @enumFromInt(i);
+ const node: Ast.Node.Index = @fromBackingInt(@intCast(i));
switch (tag) {
.builtin_call,
diff --git a/src/Server.zig b/src/Server.zig
index b96966e24d..f03d2b3149 100644
--- a/src/Server.zig
+++ b/src/Server.zig
@@ -1888,19 +1888,19 @@ fn processMessageReportError(server: *Server, arena_state: std.heap.ArenaAllocat
switch (message) {
.request => |request| {
const json_message = server.sendToClientResponseError(request.id, .{
- .code = @enumFromInt(switch (err) {
+ .code = @fromBackingInt(switch (err) {
error.Canceled => unreachable, // checked above
- error.OutOfMemory => @intFromEnum(types.ErrorCodes.InternalError),
- error.ParseError => @intFromEnum(types.ErrorCodes.ParseError),
- error.InvalidRequest => @intFromEnum(types.ErrorCodes.InvalidRequest),
- error.MethodNotFound => @intFromEnum(types.ErrorCodes.MethodNotFound),
- error.InvalidParams => @intFromEnum(types.ErrorCodes.InvalidParams),
- error.InternalError => @intFromEnum(types.ErrorCodes.InternalError),
- error.ServerNotInitialized => @intFromEnum(types.ErrorCodes.ServerNotInitialized),
- error.RequestFailed => @intFromEnum(types.LSPErrorCodes.RequestFailed),
- error.ServerCancelled => @intFromEnum(types.LSPErrorCodes.ServerCancelled),
- error.ContentModified => @intFromEnum(types.LSPErrorCodes.ContentModified),
- error.RequestCancelled => @intFromEnum(types.LSPErrorCodes.RequestCancelled),
+ error.OutOfMemory => @backingInt(types.ErrorCodes.InternalError),
+ error.ParseError => @backingInt(types.ErrorCodes.ParseError),
+ error.InvalidRequest => @backingInt(types.ErrorCodes.InvalidRequest),
+ error.MethodNotFound => @backingInt(types.ErrorCodes.MethodNotFound),
+ error.InvalidParams => @backingInt(types.ErrorCodes.InvalidParams),
+ error.InternalError => @backingInt(types.ErrorCodes.InternalError),
+ error.ServerNotInitialized => @backingInt(types.ErrorCodes.ServerNotInitialized),
+ error.RequestFailed => @backingInt(types.LSPErrorCodes.RequestFailed),
+ error.ServerCancelled => @backingInt(types.LSPErrorCodes.ServerCancelled),
+ error.ContentModified => @backingInt(types.LSPErrorCodes.ContentModified),
+ error.RequestCancelled => @backingInt(types.LSPErrorCodes.RequestCancelled),
}),
.message = @errorName(err),
}) catch |send_err| switch (send_err) {
diff --git a/src/TrigramStore.zig b/src/TrigramStore.zig
index 57a230014b..9865863eee 100644
--- a/src/TrigramStore.zig
+++ b/src/TrigramStore.zig
@@ -293,7 +293,7 @@ fn appendOneTrigram(
allocator: std.mem.Allocator,
trigram: Trigram,
) error{OutOfMemory}!void {
- const declaration_index: Declaration.Index = @enumFromInt(store.declarations.len);
+ const declaration_index: Declaration.Index = @fromBackingInt(@intCast(store.declarations.len));
const gop = try store.trigram_to_declarations.getOrPutValue(allocator, trigram, .empty);
@@ -455,7 +455,7 @@ fn mergeIntersection(
out_idx += 1;
a_idx += 1;
b_idx += 1;
- } else if (@intFromEnum(a_val) < @intFromEnum(b_val)) {
+ } else if (@backingInt(a_val) < @backingInt(b_val)) {
a_idx += 1;
} else {
b_idx += 1;
@@ -484,7 +484,7 @@ const CuckooFilter = struct {
pub fn oddHash(fingerprint: Fingerprint) u32 {
assert(fingerprint != .none);
- return precomputed_odd_hashes[@intFromEnum(fingerprint)];
+ return precomputed_odd_hashes[@backingInt(fingerprint)];
}
};
@@ -493,10 +493,10 @@ const CuckooFilter = struct {
_,
pub fn alternate(index: BucketIndex, fingerprint: Fingerprint, len: u32) BucketIndex {
- assert(@intFromEnum(index) < len);
+ assert(@backingInt(index) < len);
assert(fingerprint != .none);
- const signed_index: i64 = @intFromEnum(index);
+ const signed_index: i64 = @backingInt(index);
const odd_hash: i64 = fingerprint.oddHash();
const unbounded = switch (parity(signed_index)) {
@@ -507,7 +507,7 @@ const CuckooFilter = struct {
assert(parity(signed_index) != parity(bounded));
- return @enumFromInt(bounded);
+ return @fromBackingInt(bounded);
}
};
@@ -523,10 +523,10 @@ const CuckooFilter = struct {
index_1: u32,
} = @bitCast(std.hash.Murmur2_64.hash(&trigram));
- const index_1: BucketIndex = @enumFromInt(split.index_1 % len);
+ const index_1: BucketIndex = @fromBackingInt(split.index_1 % len);
const fingerprint: Fingerprint = if (split.fingerprint == .none)
- @enumFromInt(1)
+ @fromBackingInt(1)
else
split.fingerprint;
@@ -582,7 +582,7 @@ const CuckooFilter = struct {
}
fn bucketAt(filter: CuckooFilter, index: BucketIndex) *Bucket {
- return &filter.buckets[@intFromEnum(index)];
+ return &filter.buckets[@backingInt(index)];
}
fn appendToBucket(filter: CuckooFilter, index: BucketIndex, fingerprint: Fingerprint) bool {
@@ -639,7 +639,7 @@ const CuckooFilter = struct {
}
fn parity(integer: anytype) enum(u1) { even, odd } {
- return @enumFromInt(integer & 1);
+ return @fromBackingInt(@intCast(integer & 1));
}
};
diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig
index 44950930bb..ce8bbd5ab3 100644
--- a/src/analyser/InternPool.zig
+++ b/src/analyser/InternPool.zig
@@ -664,7 +664,7 @@ pub const Index = enum(u32) {
assert(index < slice.len);
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return @enumFromInt(ip.extra.items[slice.start + index]);
+ return @fromBackingInt(ip.extra.items[slice.start + index]);
}
pub fn dupe(slice: Slice, allocator: Allocator, ip: *InternPool) error{OutOfMemory}![]Index {
@@ -703,9 +703,9 @@ pub const Index = enum(u32) {
// comptime {
// const Zir = @import("../stage2/Zir.zig");
-// assert(@intFromEnum(Zir.Inst.Ref.generic_poison_type) == @intFromEnum(Index.generic_poison_type));
-// assert(@intFromEnum(Zir.Inst.Ref.undef) == @intFromEnum(Index.undefined_value));
-// assert(@intFromEnum(Zir.Inst.Ref.one_usize) == @intFromEnum(Index.one_usize));
+// assert(@backingInt(Zir.Inst.Ref.generic_poison_type) == @backingInt(Index.generic_poison_type));
+// assert(@backingInt(Zir.Inst.Ref.undef) == @backingInt(Index.undefined_value));
+// assert(@backingInt(Zir.Inst.Ref.one_usize) == @backingInt(Index.one_usize));
// }
pub const StringSlice = struct {
@@ -722,7 +722,7 @@ pub const StringSlice = struct {
assert(index < slice.len);
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return @enumFromInt(ip.extra.items[slice.start + index]);
+ return @fromBackingInt(ip.extra.items[slice.start + index]);
}
pub fn dupe(slice: StringSlice, allocator: Allocator, ip: *InternPool) error{OutOfMemory}![]String {
@@ -961,7 +961,7 @@ pub const Decl = struct {
_,
pub fn toOptional(i: Decl.Index) OptionalIndex {
- return @enumFromInt(@intFromEnum(i));
+ return @fromBackingInt(@backingInt(i));
}
};
@@ -975,7 +975,7 @@ pub const Decl = struct {
pub fn unwrap(oi: OptionalIndex) ?Decl.Index {
if (oi == .none) return null;
- return @enumFromInt(@intFromEnum(oi));
+ return @fromBackingInt(@backingInt(oi));
}
};
};
@@ -1152,7 +1152,7 @@ pub fn init(io: std.Io, gpa: Allocator) Allocator.Error!InternPool {
}
for (items, 0..) |item, i| {
- assert(@intFromEnum(item.index) == i);
+ assert(@backingInt(item.index) == i);
assert(item.index == ip.get(item.key) catch unreachable);
}
@@ -1195,27 +1195,27 @@ pub fn indexToKey(ip: *InternPool, index: Index) Key {
fn indexToKeyNoLock(ip: *const InternPool, index: Index) Key {
assert(index != .none);
- const item = ip.items.get(@intFromEnum(index));
+ const item = ip.items.get(@backingInt(index));
const data = item.data;
return switch (item.tag) {
- .simple_type => .{ .simple_type = @enumFromInt(data) },
- .simple_value => .{ .simple_value = @enumFromInt(data) },
+ .simple_type => .{ .simple_type = @fromBackingInt(data) },
+ .simple_value => .{ .simple_value = @fromBackingInt(data) },
.type_int_signed => .{ .int_type = .{ .signedness = .signed, .bits = @intCast(data) } },
.type_int_unsigned => .{ .int_type = .{ .signedness = .unsigned, .bits = @intCast(data) } },
.type_pointer => .{ .pointer_type = ip.extraData(Key.Pointer, data) },
.type_array => .{ .array_type = ip.extraData(Key.Array, data) },
- .type_optional => .{ .optional_type = .{ .payload_type = @enumFromInt(data) } },
- .type_anyframe => .{ .anyframe_type = .{ .child = @enumFromInt(data) } },
+ .type_optional => .{ .optional_type = .{ .payload_type = @fromBackingInt(data) } },
+ .type_anyframe => .{ .anyframe_type = .{ .child = @fromBackingInt(data) } },
.type_error_union => .{ .error_union_type = ip.extraData(Key.ErrorUnion, data) },
.type_error_set => .{ .error_set_type = ip.extraData(Key.ErrorSet, data) },
.type_function => .{ .function_type = ip.extraData(Key.Function, data) },
.type_tuple => .{ .tuple_type = ip.extraData(Key.Tuple, data) },
.type_vector => .{ .vector_type = ip.extraData(Key.Vector, data) },
- .type_struct => .{ .struct_type = @enumFromInt(data) },
- .type_enum => .{ .enum_type = @enumFromInt(data) },
- .type_union => .{ .union_type = @enumFromInt(data) },
+ .type_struct => .{ .struct_type = @fromBackingInt(data) },
+ .type_enum => .{ .enum_type = @fromBackingInt(data) },
+ .type_union => .{ .union_type = @fromBackingInt(data) },
.int_u64 => .{ .int_u64_value = ip.extraData(Key.U64Value, data) },
.int_i64 => .{ .int_i64_value = ip.extraData(Key.I64Value, data) },
@@ -1245,9 +1245,9 @@ fn indexToKeyNoLock(ip: *const InternPool, index: Index) Key {
.aggregate_value => .{ .aggregate = ip.extraData(Key.Aggregate, data) },
.union_value => .{ .union_value = ip.extraData(Key.UnionValue, data) },
.error_value => .{ .error_value = ip.extraData(Key.ErrorValue, data) },
- .null_value => .{ .null_value = .{ .ty = @enumFromInt(data) } },
- .undefined_value => .{ .undefined_value = .{ .ty = @enumFromInt(data) } },
- .unknown_value => .{ .unknown_value = .{ .ty = @enumFromInt(data) } },
+ .null_value => .{ .null_value = .{ .ty = @fromBackingInt(data) } },
+ .undefined_value => .{ .undefined_value = .{ .ty = @fromBackingInt(data) } },
+ .unknown_value => .{ .unknown_value = .{ .ty = @fromBackingInt(data) } },
};
}
@@ -1262,23 +1262,23 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
defer ip.lock.unlockShared(ip.io);
const index = ip.map.getIndexAdapted(key, adapter) orelse break :not_found;
- return @enumFromInt(index);
+ return @fromBackingInt(@intCast(index));
}
ip.lock.lockUncancelable(ip.io);
defer ip.lock.unlock(ip.io);
const gop = try ip.map.getOrPutAdapted(ip.gpa, key, adapter);
- if (gop.found_existing) return @enumFromInt(gop.index);
+ if (gop.found_existing) return @fromBackingInt(@intCast(gop.index));
const item: Item = switch (key) {
.simple_type => |simple| .{
.tag = .simple_type,
- .data = @intFromEnum(simple),
+ .data = @backingInt(simple),
},
.simple_value => |simple| .{
.tag = .simple_value,
- .data = @intFromEnum(simple),
+ .data = @backingInt(simple),
},
.int_type => |int_ty| switch (int_ty.signedness) {
.signed => .{ .tag = .type_int_signed, .data = int_ty.bits },
@@ -1294,11 +1294,11 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
},
.struct_type => |struct_index| .{
.tag = .type_struct,
- .data = @intFromEnum(struct_index),
+ .data = @backingInt(struct_index),
},
.optional_type => |optional_ty| .{
.tag = .type_optional,
- .data = @intFromEnum(optional_ty.payload_type),
+ .data = @backingInt(optional_ty.payload_type),
},
.error_union_type => |error_union_ty| .{
.tag = .type_error_union,
@@ -1310,7 +1310,7 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
},
.enum_type => |enum_index| .{
.tag = .type_enum,
- .data = @intFromEnum(enum_index),
+ .data = @backingInt(enum_index),
},
.function_type => |function_ty| .{
.tag = .type_function,
@@ -1318,7 +1318,7 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
},
.union_type => |union_index| .{
.tag = .type_union,
- .data = @intFromEnum(union_index),
+ .data = @backingInt(union_index),
},
.tuple_type => |tuple_ty| .{
.tag = .type_tuple,
@@ -1330,7 +1330,7 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
},
.anyframe_type => |anyframe_ty| .{
.tag = .type_anyframe,
- .data = @intFromEnum(anyframe_ty.child),
+ .data = @backingInt(anyframe_ty.child),
},
.int_u64_value => |int_val| .{
@@ -1398,23 +1398,23 @@ pub fn get(ip: *InternPool, key: Key) Allocator.Error!Index {
},
.null_value => |null_val| .{
.tag = .null_value,
- .data = @intFromEnum(null_val.ty),
+ .data = @backingInt(null_val.ty),
},
.undefined_value => |undefined_val| .{
.tag = .undefined_value,
- .data = @intFromEnum(undefined_val.ty),
+ .data = @backingInt(undefined_val.ty),
},
.unknown_value => |unknown_val| blk: {
assert(unknown_val.ty != .type_type); // use .unknown_type instead
break :blk .{
.tag = .unknown_value,
- .data = @intFromEnum(unknown_val.ty),
+ .data = @backingInt(unknown_val.ty),
};
},
};
try ip.items.append(ip.gpa, item);
- return @enumFromInt(ip.items.len - 1);
+ return @fromBackingInt(@intCast(ip.items.len - 1));
}
pub fn contains(ip: *InternPool, key: Key) ?Index {
@@ -1425,7 +1425,7 @@ pub fn contains(ip: *InternPool, key: Key) ?Index {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
const index = ip.map.getIndexAdapted(key, adapter) orelse return null;
- return @enumFromInt(index);
+ return @fromBackingInt(@intCast(index));
}
pub fn getIndexSlice(ip: *InternPool, data: []const Index) error{OutOfMemory}!Index.Slice {
@@ -1473,67 +1473,67 @@ fn getLimbSlice(ip: *InternPool, data: []const std.math.big.Limb) error{OutOfMem
pub fn getDecl(ip: *InternPool, index: InternPool.Decl.Index) *const InternPool.Decl {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.decls.at(@intFromEnum(index));
+ return ip.decls.at(@backingInt(index));
}
pub fn getDeclMut(ip: *InternPool, index: InternPool.Decl.Index) *InternPool.Decl {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.decls.at(@intFromEnum(index));
+ return ip.decls.at(@backingInt(index));
}
pub fn getStruct(ip: *InternPool, index: Struct.Index) *const Struct {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.structs.at(@intFromEnum(index));
+ return ip.structs.at(@backingInt(index));
}
pub fn getStructMut(ip: *InternPool, index: Struct.Index) *Struct {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.structs.at(@intFromEnum(index));
+ return ip.structs.at(@backingInt(index));
}
pub fn getEnum(ip: *InternPool, index: Enum.Index) *const Enum {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.enums.at(@intFromEnum(index));
+ return ip.enums.at(@backingInt(index));
}
pub fn getEnumMut(ip: *InternPool, index: Enum.Index) *Enum {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.enums.at(@intFromEnum(index));
+ return ip.enums.at(@backingInt(index));
}
pub fn getUnion(ip: *InternPool, index: Union.Index) *const Union {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.unions.at(@intFromEnum(index));
+ return ip.unions.at(@backingInt(index));
}
pub fn getUnionMut(ip: *InternPool, index: Union.Index) *Union {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.unions.at(@intFromEnum(index));
+ return ip.unions.at(@backingInt(index));
}
pub fn createDecl(ip: *InternPool, decl: Decl) Allocator.Error!Decl.Index {
ip.lock.lockUncancelable(ip.io);
defer ip.lock.unlock(ip.io);
try ip.decls.append(ip.gpa, decl);
- return @enumFromInt(ip.decls.count() - 1);
+ return @fromBackingInt(@intCast(ip.decls.count() - 1));
}
pub fn createStruct(ip: *InternPool, struct_info: Struct) Allocator.Error!Struct.Index {
ip.lock.lockUncancelable(ip.io);
defer ip.lock.unlock(ip.io);
try ip.structs.append(ip.gpa, struct_info);
- return @enumFromInt(ip.structs.count() - 1);
+ return @fromBackingInt(@intCast(ip.structs.count() - 1));
}
pub fn createEnum(ip: *InternPool, enum_info: Enum) Allocator.Error!Enum.Index {
ip.lock.lockUncancelable(ip.io);
defer ip.lock.unlock(ip.io);
try ip.enums.append(ip.gpa, enum_info);
- return @enumFromInt(ip.enums.count() - 1);
+ return @fromBackingInt(@intCast(ip.enums.count() - 1));
}
pub fn createUnion(ip: *InternPool, union_info: Union) Allocator.Error!Union.Index {
ip.lock.lockUncancelable(ip.io);
defer ip.lock.unlock(ip.io);
try ip.unions.append(ip.gpa, union_info);
- return @enumFromInt(ip.unions.count() - 1);
+ return @fromBackingInt(@intCast(ip.unions.count() - 1));
}
fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 {
@@ -1555,7 +1555,7 @@ fn addExtra(ip: *InternPool, comptime T: type, extra: T) Allocator.Error!u32 {
StringPool.String,
StringPool.OptionalString,
std.lang.Type.Pointer.Size,
- => ip.extra.appendAssumeCapacity(@intFromEnum(item)),
+ => ip.extra.appendAssumeCapacity(@backingInt(item)),
u32,
i32,
@@ -1595,7 +1595,7 @@ fn extraData(ip: *const InternPool, comptime T: type, index: u32) T {
std.lang.Type.Pointer.Size,
// std.lang.AddressSpace,
// std.lang.CallingConvention,
- => @enumFromInt(item),
+ => @fromBackingInt(item),
u32,
i32,
@@ -1633,7 +1633,7 @@ const KeyAdapter = struct {
pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
_ = b_void;
- return a.eqlNoLock(ctx.ip.indexToKeyNoLock(@enumFromInt(b_map_index)), ctx.ip);
+ return a.eqlNoLock(ctx.ip.indexToKeyNoLock(@fromBackingInt(@intCast(b_map_index))), ctx.ip);
}
pub fn hash(ctx: @This(), a: Key) u32 {
@@ -2962,8 +2962,8 @@ fn panicOrElse(comptime T: type, message: []const u8, value: T) T {
pub fn zigTypeTag(ip: *InternPool, index: Index) ?std.lang.TypeId {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return switch (ip.items.items(.tag)[@intFromEnum(index)]) {
- .simple_type => switch (@as(SimpleType, @enumFromInt(ip.items.items(.data)[@intFromEnum(index)]))) {
+ return switch (ip.items.items(.tag)[@backingInt(index)]) {
+ .simple_type => switch (@as(SimpleType, @fromBackingInt(ip.items.items(.data)[@backingInt(index)]))) {
.f16,
.f32,
.f64,
@@ -3055,9 +3055,9 @@ pub fn zigTypeTag(ip: *InternPool, index: Index) ?std.lang.TypeId {
pub fn typeOf(ip: *InternPool, index: Index) Index {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- const data = ip.items.items(.data)[@intFromEnum(index)];
- return switch (ip.items.items(.tag)[@intFromEnum(index)]) {
- .simple_value => switch (@as(SimpleValue, @enumFromInt(data))) {
+ const data = ip.items.items(.data)[@backingInt(index)];
+ return switch (ip.items.items(.tag)[@backingInt(index)]) {
+ .simple_value => switch (@as(SimpleValue, @fromBackingInt(data))) {
.undefined_value => .undefined_type,
.void_value => .void_type,
.unreachable_value => .noreturn_type,
@@ -3101,20 +3101,20 @@ pub fn typeOf(ip: *InternPool, index: Index) Index {
.aggregate_value,
.union_value,
.error_value,
- => @enumFromInt(ip.extra.items[ip.items.items(.data)[@intFromEnum(index)]]),
+ => @fromBackingInt(ip.extra.items[ip.items.items(.data)[@backingInt(index)]]),
// the type is the `data` field
.null_value,
.undefined_value,
.unknown_value,
- => @enumFromInt(ip.items.items(.data)[@intFromEnum(index)]),
+ => @fromBackingInt(ip.items.items(.data)[@backingInt(index)]),
};
}
pub fn isType(ip: *InternPool, ty: Index) bool {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return switch (ip.items.items(.tag)[@intFromEnum(ty)]) {
+ return switch (ip.items.items(.tag)[@backingInt(ty)]) {
.simple_type,
.type_int_signed,
.type_int_unsigned,
@@ -3151,7 +3151,7 @@ pub fn isType(ip: *InternPool, ty: Index) bool {
.null_value,
.undefined_value,
=> false,
- .unknown_value => .unknown_type == @as(Index, @enumFromInt(ip.items.items(.data)[@intFromEnum(ty)])),
+ .unknown_value => .unknown_type == @as(Index, @fromBackingInt(ip.items.items(.data)[@backingInt(ty)])),
};
}
@@ -3161,7 +3161,7 @@ pub fn isUndefined(ip: *InternPool, index: Index) bool {
else => {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.items.items(.tag)[@intFromEnum(index)] == .undefined_value;
+ return ip.items.items(.tag)[@backingInt(index)] == .undefined_value;
},
}
}
@@ -3172,7 +3172,7 @@ pub fn isUnknown(ip: *InternPool, index: Index) bool {
else => {
ip.lock.lockSharedUncancelable(ip.io);
defer ip.lock.unlockShared(ip.io);
- return ip.items.items(.tag)[@intFromEnum(index)] == .unknown_value;
+ return ip.items.items(.tag)[@backingInt(index)] == .unknown_value;
},
}
}
@@ -4895,10 +4895,10 @@ test "test thread safety of InternPool" {
// insert float_32_value from 0 to count + random work
for (0..count) |i| {
_ = intern_pool.get(.{ .float_32_value = @floatFromInt(i) }) catch @panic("OOM");
- _ = intern_pool.indexToKey(@enumFromInt(i));
+ _ = intern_pool.indexToKey(@fromBackingInt(@intCast(i)));
}
for (0..count) |i| {
- _ = intern_pool.indexToKey(@enumFromInt(i));
+ _ = intern_pool.indexToKey(@fromBackingInt(@intCast(i)));
}
}
};
@@ -4923,7 +4923,7 @@ test "test thread safety of InternPool" {
// test that every Index stores a unique float_32_value
for (0..size) |i| {
- const index: Index = @enumFromInt(index_start + i);
+ const index: Index = @fromBackingInt(@intCast(index_start + i));
const key = ip.indexToKey(index);
const value: usize = @intFromFloat(key.float_32_value);
try std.testing.expect(value < size);
diff --git a/src/analyser/string_pool.zig b/src/analyser/string_pool.zig
index 1ba65449d9..0b4984ed96 100644
--- a/src/analyser/string_pool.zig
+++ b/src/analyser/string_pool.zig
@@ -34,7 +34,7 @@ pub fn StringPool(comptime config: Config) type {
_,
pub fn toOptional(self: String) OptionalString {
- return @enumFromInt(@intFromEnum(self));
+ return @fromBackingInt(@backingInt(self));
}
pub fn fmt(self: String, io: std.Io, pool: *Pool) std.fmt.Alt(FormatContext, print) {
@@ -48,7 +48,7 @@ pub fn StringPool(comptime config: Config) type {
pub fn unwrap(self: OptionalString) ?String {
if (self == .none) return null;
- return @enumFromInt(@intFromEnum(self));
+ return @fromBackingInt(@backingInt(self));
}
};
@@ -69,7 +69,7 @@ pub fn StringPool(comptime config: Config) type {
};
const index = pool.map.getKeyAdapted(str, adapter) orelse return null;
- return @enumFromInt(index);
+ return @fromBackingInt(index);
}
/// Asserts that `str` contains no null bytes.
@@ -94,7 +94,7 @@ pub fn StringPool(comptime config: Config) type {
pool.bytes.ensureUnusedCapacity(allocator, str.len + 1) catch {
// If allocation fails, try to do the lookup anyway.
const index = pool.map.getKeyAdapted(str, adapter) orelse return error.OutOfMemory;
- return @enumFromInt(index);
+ return @fromBackingInt(index);
};
const gop = try pool.map.getOrPutContextAdapted(
@@ -109,7 +109,7 @@ pub fn StringPool(comptime config: Config) type {
pool.bytes.appendAssumeCapacity(0);
gop.key_ptr.* = start_index;
}
- return @enumFromInt(gop.key_ptr.*);
+ return @fromBackingInt(gop.key_ptr.*);
}
/// Caller owns the memory.
@@ -117,7 +117,7 @@ pub fn StringPool(comptime config: Config) type {
pool.mutex.lockUncancelable(io);
defer pool.mutex.unlock(io);
const string_bytes: [*:0]u8 = @ptrCast(pool.bytes.items.ptr);
- const start = @intFromEnum(index);
+ const start = @backingInt(index);
return try allocator.dupe(u8, std.mem.sliceTo(string_bytes + start, 0));
}
@@ -126,7 +126,7 @@ pub fn StringPool(comptime config: Config) type {
pool.mutex.lockUncancelable(io);
defer pool.mutex.unlock(io);
const string_bytes: [*:0]u8 = @ptrCast(pool.bytes.items.ptr);
- const start = @intFromEnum(index);
+ const start = @backingInt(index);
return try allocator.dupeSentinel(u8, std.mem.sliceTo(string_bytes + start, 0), 0);
}
@@ -161,9 +161,9 @@ pub fn StringPool(comptime config: Config) type {
/// returns the underlying slice from an interned string
/// equal strings are guaranteed to share the same storage
pub fn stringToSliceUnsafe(pool: *Pool, index: String) [:0]const u8 {
- assert(@intFromEnum(index) < pool.bytes.items.len);
+ assert(@backingInt(index) < pool.bytes.items.len);
const string_bytes: [*:0]u8 = @ptrCast(pool.bytes.items.ptr);
- const start = @intFromEnum(index);
+ const start = @backingInt(index);
return std.mem.sliceTo(string_bytes + start, 0);
}
diff --git a/src/analysis.zig b/src/analysis.zig
index 7b526719d7..273b6521d2 100644
--- a/src/analysis.zig
+++ b/src/analysis.zig
@@ -2853,7 +2853,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
var big_int: std.math.big.int.Managed = try .init(analyser.gpa);
defer big_int.deinit();
const prefix_length: usize = if (base != .decimal) 2 else 0;
- big_int.setString(@intFromEnum(base), bytes[prefix_length..]) catch |err| switch (err) {
+ big_int.setString(@backingInt(base), bytes[prefix_length..]) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => break :blk null,
};
@@ -3396,7 +3396,7 @@ pub const Type = struct {
}
pub fn hashWithHasher(data: Data, hasher: anytype) void {
- hasher.update(&.{@intFromEnum(data)});
+ hasher.update(&.{@backingInt(data)});
switch (data) {
.pointer => |info| {
std.hash.autoHash(hasher, info.size);
@@ -3462,7 +3462,7 @@ pub const Type = struct {
}
pub fn eql(a: Data, b: Data) bool {
- if (@intFromEnum(a) != @intFromEnum(b)) return false;
+ if (@backingInt(a) != @backingInt(b)) return false;
switch (a) {
.pointer => |a_type| {
@@ -6054,7 +6054,7 @@ pub fn collectDeclarationsOfContainer(
const scope_decls = document_scope.getScopeDeclarationsConst(scope);
for (scope_decls) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
const decl_with_handle: DeclWithHandle = .{ .decl = decl, .handle = handle, .container_type = container_type };
if (handle != original_handle and !decl_with_handle.isPublic()) continue;
@@ -6121,7 +6121,7 @@ pub fn collectAllSymbolsAtSourceIndex(
while (scope_iterator.next().unwrap()) |scope_index| {
const scope_decls = document_scope.getScopeDeclarationsConst(scope_index);
for (scope_decls) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
if (decl == .ast_node and handle.tree.nodeTag(decl.ast_node).isContainerField()) continue;
if (decl == .label) continue;
try decl_collection.append(analyser.arena, .{ .decl = decl, .handle = handle });
@@ -6145,7 +6145,7 @@ pub const EnclosingScopeIterator = struct {
source_index: usize,
fn compare(ctx: @This(), scope_index: Scope.Index) std.math.Order {
- const child_scope = ctx.scope_locs[@intFromEnum(scope_index)];
+ const child_scope = ctx.scope_locs[@backingInt(scope_index)];
if (ctx.source_index < child_scope.start) return .lt;
if (child_scope.end < ctx.source_index) return .gt;
return .eq;
@@ -6176,7 +6176,7 @@ pub fn iterateLabels(handle: *DocumentStore.Handle, source_index: usize, comptim
var scope_iterator = iterateEnclosingScopes(document_scope, source_index);
while (scope_iterator.next().unwrap()) |scope_index| {
for (document_scope.getScopeDeclarationsConst(scope_index)) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
if (decl != .label) continue;
try callback(context, .{ .decl = decl, .handle = handle });
}
@@ -6272,7 +6272,7 @@ pub fn lookupLabel(
.name = symbol,
.kind = .label,
}).unwrap() orelse continue;
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
std.debug.assert(decl == .label);
@@ -6297,7 +6297,7 @@ pub fn lookupSymbolGlobal(
.name = symbol,
.kind = .field,
}).unwrap()) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
std.debug.assert(decl == .ast_node);
var field = tree.fullContainerField(decl.ast_node).?;
@@ -6314,7 +6314,7 @@ pub fn lookupSymbolGlobal(
.name = symbol,
.kind = .other,
}).unwrap()) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
return .{ .decl = decl, .handle = handle };
}
@@ -6342,7 +6342,7 @@ pub fn lookupSymbolContainer(
.name = symbol,
.kind = kind,
}).unwrap()) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
return .{ .decl = decl, .handle = handle, .container_type = container_type };
}
diff --git a/src/ast.zig b/src/ast.zig
index e2a2ba2011..76a41738be 100644
--- a/src/ast.zig
+++ b/src/ast.zig
@@ -344,7 +344,7 @@ pub fn whileFull(tree: *const Ast, node: Node.Index) full.While {
}
pub fn forSimple(tree: *const Ast, node: Node.Index) full.For {
- const data = &tree.nodes.items(.data)[@intFromEnum(node)].node_and_node;
+ const data = &tree.nodes.items(.data)[@backingInt(node)].node_and_node;
return fullForComponents(tree, .{
.for_token = tree.nodeMainToken(node),
.inputs = (&data[0])[0..1],
@@ -356,8 +356,8 @@ pub fn forSimple(tree: *const Ast, node: Node.Index) full.For {
pub fn forFull(tree: *const Ast, node: Node.Index) full.For {
const extra_index, const extra = tree.nodeData(node).@"for";
const inputs = tree.extraDataSliceWithLen(extra_index, extra.inputs, Node.Index);
- const then_expr: Node.Index = @enumFromInt(tree.extra_data[@intFromEnum(extra_index) + extra.inputs]);
- const else_expr: Node.OptionalIndex = if (extra.has_else) @enumFromInt(tree.extra_data[@intFromEnum(extra_index) + extra.inputs + 1]) else .none;
+ const then_expr: Node.Index = @fromBackingInt(tree.extra_data[@backingInt(extra_index) + extra.inputs]);
+ const else_expr: Node.OptionalIndex = if (extra.has_else) @fromBackingInt(tree.extra_data[@backingInt(extra_index) + extra.inputs + 1]) else .none;
return fullForComponents(tree, .{
.for_token = tree.nodeMainToken(node),
.inputs = inputs,
@@ -415,13 +415,13 @@ fn findMatchingRBrace(tree: *const Ast, start: Ast.TokenIndex) ?Ast.TokenIndex {
pub fn isKeyword(tag: std.zig.Token.Tag) bool {
const tags = std.zig.Token.keywords.values();
- var first_keyword: std.meta.Tag(std.zig.Token.Tag) = @intFromEnum(tags[0]);
- var last_keyword: std.meta.Tag(std.zig.Token.Tag) = @intFromEnum(tags[tags.len - 1]);
+ var first_keyword: std.meta.Tag(std.zig.Token.Tag) = @backingInt(tags[0]);
+ var last_keyword: std.meta.Tag(std.zig.Token.Tag) = @backingInt(tags[tags.len - 1]);
for (std.zig.Token.keywords.values()) |token_tag| {
- first_keyword = @min(@intFromEnum(token_tag), first_keyword);
- last_keyword = @max(@intFromEnum(token_tag), last_keyword);
+ first_keyword = @min(@backingInt(token_tag), first_keyword);
+ last_keyword = @max(@backingInt(token_tag), last_keyword);
}
- return first_keyword <= @intFromEnum(tag) and @intFromEnum(tag) <= last_keyword;
+ return first_keyword <= @backingInt(tag) and @backingInt(tag) <= last_keyword;
}
test isKeyword {
@@ -643,8 +643,8 @@ pub fn lastToken(tree: *const Ast, node: Node.Index) Ast.TokenIndex {
},
.@"for" => {
const extra_index, const extra = tree.nodeData(n).@"for";
- const index = @intFromEnum(extra_index) + extra.inputs + @intFromBool(extra.has_else);
- n = @enumFromInt(tree.extra_data[index]);
+ const index = @backingInt(extra_index) + extra.inputs + @intFromBool(extra.has_else);
+ n = @fromBackingInt(tree.extra_data[index]);
},
.@"asm" => {
_, const extra_index = tree.nodeData(n).node_and_extra;
@@ -1249,9 +1249,9 @@ pub const Iterator = union(enum) {
.assign_destructure => {
const extra_index, const value_expr = tree.nodeData(node).extra_and_node;
- const variable_count = tree.extra_data[@intFromEnum(extra_index)];
- const sub_range_start: Ast.ExtraIndex = @enumFromInt(@intFromEnum(extra_index) + 1);
- const sub_range_end: Ast.ExtraIndex = @enumFromInt(@intFromEnum(sub_range_start) + variable_count);
+ const variable_count = tree.extra_data[@backingInt(extra_index)];
+ const sub_range_start: Ast.ExtraIndex = @fromBackingInt(@backingInt(extra_index) + 1);
+ const sub_range_end: Ast.ExtraIndex = @fromBackingInt(@backingInt(sub_range_start) + variable_count);
return .{ .sub_range = .{
.items = .{ .start = sub_range_start, .end = sub_range_end },
.suffix = .{ value_expr.toOptional(), .none },
@@ -1362,10 +1362,10 @@ pub const Iterator = union(enum) {
.@"for",
=> {
const extra_index, const extra = tree.nodeData(node).@"for";
- const then_expr: Node.Index = @enumFromInt(tree.extra_data[@intFromEnum(extra_index) + extra.inputs]);
- const else_expr: Node.OptionalIndex = if (extra.has_else) @enumFromInt(tree.extra_data[@intFromEnum(extra_index) + extra.inputs + 1]) else .none;
+ const then_expr: Node.Index = @fromBackingInt(tree.extra_data[@backingInt(extra_index) + extra.inputs]);
+ const else_expr: Node.OptionalIndex = if (extra.has_else) @fromBackingInt(tree.extra_data[@backingInt(extra_index) + extra.inputs + 1]) else .none;
return .{ .sub_range = .{
- .items = .{ .start = extra_index, .end = @enumFromInt(@intFromEnum(extra_index) + extra.inputs) },
+ .items = .{ .start = extra_index, .end = @fromBackingInt(@backingInt(extra_index) + extra.inputs) },
.suffix = .{ then_expr.toOptional(), else_expr },
} };
},
@@ -1443,7 +1443,7 @@ pub const Iterator = union(enum) {
}
const items = tree.extraDataSlice(sub_range.items, Ast.Node.Index);
if (items.len > 0) {
- defer sub_range.items.start = @enumFromInt(@intFromEnum(sub_range.items.start) + 1);
+ defer sub_range.items.start = @fromBackingInt(@backingInt(sub_range.items.start) + 1);
return items[0];
}
const first = sub_range.suffix[0].unwrap() orelse return null;
@@ -1486,8 +1486,8 @@ pub const Iterator = union(enum) {
}
const items = tree.extraDataSlice(asm_state.items, Ast.Node.Index);
- var i: usize = 0;
- defer asm_state.items.start = @enumFromInt(@intFromEnum(asm_state.items.start) + i);
+ var i: u32 = 0;
+ defer asm_state.items.start = @fromBackingInt(@backingInt(asm_state.items.start) + i);
while (i < items.len) {
defer i += 1;
switch (tree.nodeTag(items[i])) {
@@ -1647,7 +1647,7 @@ pub fn nodesOverlappingIndexIncludingParseErrors(allocator: std.mem.Allocator, t
var node_locs: std.ArrayList(NodeLoc) = .empty;
defer node_locs.deinit(allocator);
for (0..tree.nodes.len) |i| {
- const node: Ast.Node.Index = @enumFromInt(i);
+ const node: Ast.Node.Index = @fromBackingInt(@intCast(i));
const loc = offsets.nodeToLoc(tree, node);
if (loc.start <= source_index and source_index <= loc.end) {
try node_locs.append(allocator, .{ .node = node, .loc = loc });
diff --git a/src/features/code_actions.zig b/src/features/code_actions.zig
index 5a6a1c4f71..50d96c77e3 100644
--- a/src/features/code_actions.zig
+++ b/src/features/code_actions.zig
@@ -736,7 +736,7 @@ pub const ImportDecl = struct {
pub fn lessThan(context: *const Ast, lhs: ImportDecl, rhs: ImportDecl) bool {
const lhs_kind = lhs.getKind();
const rhs_kind = rhs.getKind();
- if (lhs_kind != rhs_kind) return @intFromEnum(lhs_kind) < @intFromEnum(rhs_kind);
+ if (lhs_kind != rhs_kind) return @backingInt(lhs_kind) < @backingInt(rhs_kind);
if (sort_public_decls_first) {
const node_tokens = context.nodes.items(.main_token);
@@ -801,9 +801,9 @@ pub const ImportDecl = struct {
/// returns true if there should be an empty line between these two imports
/// assumes `lessThan(void, lhs, rhs) == true`
pub fn addSeperator(lhs: ImportDecl, rhs: ImportDecl) bool {
- const lhs_kind = @intFromEnum(lhs.getKind());
- const rhs_kind = @intFromEnum(rhs.getKind());
- if (rhs_kind <= @intFromEnum(Kind.build_options)) return false;
+ const lhs_kind = @backingInt(lhs.getKind());
+ const rhs_kind = @backingInt(rhs.getKind());
+ if (rhs_kind <= @backingInt(Kind.build_options)) return false;
return lhs_kind != rhs_kind;
}
@@ -903,7 +903,7 @@ pub fn getImportsDecls(builder: *Builder, allocator: std.mem.Allocator) error{Ou
.kind = .other,
}).unwrap() orelse continue :next_decl;
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
if (decl != .ast_node) continue :next_decl;
const decl_found = decl.ast_node;
diff --git a/src/features/completions.zig b/src/features/completions.zig
index ef1291f090..ae15852f75 100644
--- a/src/features/completions.zig
+++ b/src/features/completions.zig
@@ -1490,7 +1490,7 @@ fn collectContainerFields(
const scope_decls = document_scope.getScopeDeclarationsConst(scope_handle.scope);
for (scope_decls) |decl_index| {
- const decl = document_scope.declarations.get(@intFromEnum(decl_index));
+ const decl = document_scope.declarations.get(@backingInt(decl_index));
if (decl != .ast_node) continue;
const decl_handle: Analyser.DeclWithHandle = .{ .decl = decl, .handle = scope_handle.handle, .container_type = container };
const maybe_resolved_ty = type_maybe orelse try decl_handle.resolveType(builder.analyser);
diff --git a/src/features/diagnostics.zig b/src/features/diagnostics.zig
index 1c259010ad..ef64f6c853 100644
--- a/src/features/diagnostics.zig
+++ b/src/features/diagnostics.zig
@@ -160,7 +160,7 @@ fn collectWarnStyleDiagnostics(
defer tracy_zone.end();
for (0..tree.nodes.len) |i| {
- const node: Ast.Node.Index = @enumFromInt(i);
+ const node: Ast.Node.Index = @fromBackingInt(@intCast(i));
if (ast.isBuiltinCall(tree, node)) {
const builtin_token = tree.nodeMainToken(node);
const call_name = tree.tokenSlice(builtin_token);
diff --git a/src/features/folding_range.zig b/src/features/folding_range.zig
index e16681ab69..3fa8c306ef 100644
--- a/src/features/folding_range.zig
+++ b/src/features/folding_range.zig
@@ -227,7 +227,7 @@ pub fn generateFoldingRanges(allocator: std.mem.Allocator, tree: *const Ast, enc
}
for (0..tree.nodes.len) |i| {
- const node: Ast.Node.Index = @enumFromInt(i);
+ const node: Ast.Node.Index = @fromBackingInt(@intCast(i));
switch (tree.nodeTag(node)) {
.root => continue,
diff --git a/src/features/semantic_tokens.zig b/src/features/semantic_tokens.zig
index b371a3319f..c4dcf617e5 100644
--- a/src/features/semantic_tokens.zig
+++ b/src/features/semantic_tokens.zig
@@ -201,7 +201,7 @@ const Builder = struct {
delta.line,
delta.character,
@intCast(length),
- @intFromEnum(token_type),
+ @backingInt(token_type),
@as(u16, @bitCast(token_modifiers)),
});
self.previous_source_index = loc.start;
diff --git a/src/features/workspace_symbols.zig b/src/features/workspace_symbols.zig
index c3bed53b6a..327e7a48ce 100644
--- a/src/features/workspace_symbols.zig
+++ b/src/features/workspace_symbols.zig
@@ -36,7 +36,7 @@ pub fn handler(server: *Server, arena: std.mem.Allocator, request: types.workspa
const SortContext = struct {
names: []const std.zig.Ast.TokenIndex,
fn lessThan(ctx: @This(), lhs: TrigramStore.Declaration.Index, rhs: TrigramStore.Declaration.Index) bool {
- return ctx.names[@intFromEnum(lhs)] < ctx.names[@intFromEnum(rhs)];
+ return ctx.names[@backingInt(lhs)] < ctx.names[@backingInt(rhs)];
}
};
@@ -56,8 +56,8 @@ pub fn handler(server: *Server, arena: std.mem.Allocator, request: types.workspa
try symbols.ensureUnusedCapacity(arena, declaration_buffer.items.len);
for (declaration_buffer.items) |declaration| {
- const name_token = names[@intFromEnum(declaration)];
- const kind = kinds[@intFromEnum(declaration)];
+ const name_token = names[@backingInt(declaration)];
+ const kind = kinds[@backingInt(declaration)];
const loc = offsets.tokenToLoc(&handle.tree, name_token);
const name = @import("document_symbol.zig").tokenNameMaybeQuotes(&handle.tree, name_token);
diff --git a/src/main.zig b/src/main.zig
index 27582cb63b..67ee9cea8f 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -75,7 +75,7 @@ fn logFn(
};
}
- if (@intFromEnum(level) > @intFromEnum(log_level)) return;
+ if (@backingInt(level) > @backingInt(log_level)) return;
if (!log_stderr and log_file == null) return;
const level_txt: []const u8 = switch (level) {
diff --git a/src/testing.zig b/src/testing.zig
index d5bf58677c..04f7c357e1 100644
--- a/src/testing.zig
+++ b/src/testing.zig
@@ -35,7 +35,7 @@ pub fn printDocumentScope(doc_scope: DocumentScope) void {
if (builtin.mode != .Debug) @compileError("this function should only be used in debug mode!");
for (0..doc_scope.scopes.len) |index| {
- const scope_index: DocumentScope.Scope.Index = @enumFromInt(index);
+ const scope_index: DocumentScope.Scope.Index = @fromBackingInt(@intCast(index));
const scope = doc_scope.scopes.get(index);
if (index != 0) std.debug.print("\n\n", .{});
std.debug.print(
@@ -57,8 +57,8 @@ pub fn printDocumentScope(doc_scope: DocumentScope) void {
for (doc_scope.getScopeDeclarationsConst(scope_index)) |decl| {
std.debug.print(" - {s:<8} {}\n", .{
- doc_scope.declaration_lookup_map.keys()[@intFromEnum(decl)].name,
- doc_scope.declarations.get(@intFromEnum(decl)),
+ doc_scope.declaration_lookup_map.keys()[@backingInt(decl)].name,
+ doc_scope.declarations.get(@backingInt(decl)),
});
}
}
diff --git a/tests/analysis/switch.zig b/tests/analysis/switch.zig
index c718f2ed72..89c72fe52b 100644
--- a/tests/analysis/switch.zig
+++ b/tests/analysis/switch.zig
@@ -29,7 +29,7 @@ const NonExhaustiveEnum = enum(u8) {
const some_u8: u8 = 'e';
const some_enum: Enum = .baz;
const some_tagged_union: TaggedUnion = .{ .baz = 3.14 };
-const some_non_exhaustive_enum: NonExhaustiveEnum = @enumFromInt(42);
+const some_non_exhaustive_enum: NonExhaustiveEnum = @fromBackingInt(42);
const switch_u8 = switch (some_u8) {
'x' => |a| a,
diff --git a/tests/lsp_features/semantic_tokens.zig b/tests/lsp_features/semantic_tokens.zig
index d936440f7e..d8abdd3515 100644
--- a/tests/lsp_features/semantic_tokens.zig
+++ b/tests/lsp_features/semantic_tokens.zig
@@ -2135,7 +2135,7 @@ const TokenIterator = struct {
const delta_line = token_data[0];
const delta_start = token_data[1];
const length = token_data[2];
- const token_type: zls.semantic_tokens.TokenType = @enumFromInt(token_data[3]);
+ const token_type: zls.semantic_tokens.TokenType = @fromBackingInt(token_data[3]);
const token_modifiers: zls.semantic_tokens.TokenModifiers = @bitCast(@as(u16, @intCast(token_data[4])));
self.position.line += delta_line;
From 1980ecb23bbe65d1bb54f0bb8ee203dc52ff9c3f Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 16 Jul 2026 07:46:12 +0200
Subject: [PATCH 11/14] do not force lld as linker when using -Duse-llvm
---
build.zig | 4 ----
1 file changed, 4 deletions(-)
diff --git a/build.zig b/build.zig
index 07c75fb379..0ee3760562 100644
--- a/build.zig
+++ b/build.zig
@@ -175,7 +175,6 @@ pub fn build(b: *Build) !void {
.root_module = exe_module,
.max_rss = if (optimize == .debug and target_query.os_tag == .wasi) 2_600_000_000 else 2_000_000_000,
.use_llvm = use_llvm,
- .use_lld = use_llvm,
});
}
@@ -217,7 +216,6 @@ pub fn build(b: *Build) !void {
.name = "zls",
.root_module = exe_module,
.use_llvm = use_llvm,
- .use_lld = use_llvm,
});
b.installArtifact(exe);
}
@@ -246,7 +244,6 @@ pub fn build(b: *Build) !void {
}),
.filters = test_filters,
.use_llvm = use_llvm,
- .use_lld = use_llvm,
});
const src_tests = b.addTest(.{
@@ -254,7 +251,6 @@ pub fn build(b: *Build) !void {
.root_module = zls_module,
.filters = test_filters,
.use_llvm = use_llvm,
- .use_lld = use_llvm,
});
blk: { // zig build test, zig build test-analysis
From 23e4a748f64e77f5dd638921dc5533ce5cdb2800 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Wed, 15 Jul 2026 07:59:46 +0200
Subject: [PATCH 12/14] do not run git describe to resolve version when being a
dependency
---
build.zig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/build.zig b/build.zig
index 0ee3760562..fa37a2977c 100644
--- a/build.zig
+++ b/build.zig
@@ -321,6 +321,14 @@ fn getVersion(b: *Build) std.SemanticVersion {
}
if (zls_version.pre == null) return zls_version;
+ if (!b.isRoot()) {
+ return .{
+ .major = zls_version.major,
+ .minor = zls_version.minor,
+ .patch = zls_version.patch,
+ .pre = "dev",
+ };
+ }
// Ensure git version changes get picked up.
git: {
From bd98108fb2ca1e231af16d19dc1dee028f4e40c0 Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Thu, 3 Sep 2026 23:13:44 +0200
Subject: [PATCH 13/14] CI: disable release artifact deployment
---
.github/workflows/artifacts.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml
index af2773a082..030f738371 100644
--- a/.github/workflows/artifacts.yml
+++ b/.github/workflows/artifacts.yml
@@ -11,7 +11,9 @@ on:
jobs:
deploy:
- if: github.repository_owner == 'zigtools'
+ # https://github.com/zigtools/zls/issues/3208
+ if: false
+ # if: github.repository_owner == 'zigtools'
runs-on: ubuntu-22.04
timeout-minutes: 30
steps:
From 89b3bbdfde1072217f7bc0412105d9f6cd99c7fd Mon Sep 17 00:00:00 2001
From: Techatrix
Date: Fri, 4 Sep 2026 16:46:01 +0200
Subject: [PATCH 14/14] add warning about missing build system integration to
README
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index 9dd2c5a86f..13b83c2356 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,10 @@ The complete installation guide is available on the [Zigtools website](https://z
### Build From Source
+> [!WARNING]
+> ZLS currently lacks critical build system integration with Zig nightly/master. See [#3208](https://github.com/zigtools/zls/issues/3208) for more information.
+> It is recommended to use Zig and ZLS 0.16.0 in the meantime.
+
The default branch of ZLS targets [Zig master](https://ziglang.org/download/).
```bash
|