diff --git a/README.md b/README.md index 54db2f3ce..a13d1d151 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ When upgrading Zig, make sure to update ZLS too keep them in sync. ## Features -ZLS supports most language features, including simple type function support, using namespace, payload capture type resolution, custom packages, cImport and others. Support for comptime and semantic analysis is Work-in-Progress. +ZLS supports most language features, including simple type function support, payload capture type resolution, custom packages, and others. Support for comptime and semantic analysis is Work-in-Progress. The following LSP features are supported: diff --git a/build.zig b/build.zig index cfec30536..fec594a95 100644 --- a/build.zig +++ b/build.zig @@ -6,10 +6,10 @@ 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: -/// Release 0.16.0 +/// delete `@cImport` from the language /// /// 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.16.0"; +const minimum_runtime_zig_version = "0.17.0-dev.28+67a5b6e5e"; const release_targets = [_]std.Target.Query{ .{ .cpu_arch = .aarch64, .os_tag = .linux }, diff --git a/build.zig.zon b/build.zig.zon index 67ea58df4..e4cefd63b 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.16.0", + .minimum_zig_version = "0.17.0-dev.39+d092c752f", // 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 = .{ diff --git a/flake.lock b/flake.lock index 16c79fb1b..b062a46d5 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1776067740, - "narHash": "sha256-B35lpsqnSZwn1Lmz06BpwF7atPgFmUgw1l8KAV3zpVQ=", + "lastModified": 1776434932, + "narHash": "sha256-gyqXNMgk3sh+ogY5svd2eNLJ6oEwzbAeaoBrrxD0lKk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7e495b747b51f95ae15e74377c5ce1fe69c1765f", + "rev": "c7f47036d3df2add644c46d712d14262b7d86c0c", "type": "github" }, "original": { @@ -29,11 +29,11 @@ ] }, "locked": { - "lastModified": 1776183664, - "narHash": "sha256-lmTMKC0Rc0L1GChNEAkn+hV/iaFMU4B2C9NkQqydumo=", + "lastModified": 1776620537, + "narHash": "sha256-ZHAzWbLwhw7lvDLhtmnQk+hIUWOUcWO2r2tLvCQzhVc=", "owner": "silversquirl", "repo": "zig-flake", - "rev": "2c9eec04c4a27ca54addd9e6c28a5a64906cba0a", + "rev": "34923b3e8c670a5819efc737f8d0fc9fefbfc0fd", "type": "github" }, "original": { diff --git a/src/DiagnosticsCollection.zig b/src/DiagnosticsCollection.zig index 2dbbe165c..a6184b0c5 100644 --- a/src/DiagnosticsCollection.zig +++ b/src/DiagnosticsCollection.zig @@ -33,8 +33,6 @@ pub const Tag = enum(u32) { /// - ast-check /// - warn_style parse, - /// errors from `@cImport` - cimport, /// - Build On Save /// - Build Runner _, diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index e40a9881a..5f59a07d8 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -11,7 +11,6 @@ const Ast = std.zig.Ast; const BuildAssociatedConfig = @import("BuildAssociatedConfig.zig"); pub const BuildConfig = @import("build_runner/shared.zig").BuildConfig; const tracy = @import("tracy"); -const translate_c = @import("translate_c.zig"); const DocumentScope = @import("DocumentScope.zig"); const DiagnosticsCollection = @import("DiagnosticsCollection.zig"); const TrigramStore = @import("TrigramStore.zig"); @@ -26,7 +25,6 @@ mutex: std.Io.Mutex = .init, wait_group: if (supports_build_system) std.Io.Group else void = if (supports_build_system) .init else {}, handles: Uri.ArrayHashMap(*Handle.Future) = .empty, build_files: if (supports_build_system) Uri.ArrayHashMap(*BuildFile) else void = if (supports_build_system) .empty else {}, -cimports: if (supports_build_system) std.array_hash_map.Auto(CImportHash, translate_c.Result) else void = if (supports_build_system) .empty else {}, diagnostics_collection: *DiagnosticsCollection, builds_in_progress: std.atomic.Value(i32) = .init(0), transport: ?*lsp.Transport = null, @@ -165,8 +163,6 @@ pub const Handle = struct { tree: Ast, /// List of every file that has been `@Import`ed. Does not include imported modules. file_imports: []const Uri, - /// Contains one entry for every `@cImport` in the document - cimports: std.MultiArrayList(CImportHandle), /// `true` if the document has been directly opened by the client i.e. with `textDocument/didOpen` /// `false` indicates the document only exists because it is a dependency of another document /// or has been closed with `textDocument/didClose`. @@ -421,20 +417,11 @@ pub const Handle = struct { var new_file_imports: std.ArrayList(Uri) = .empty; errdefer new_file_imports.deinit(allocator); - var new_cimports: std.MultiArrayList(CImportHandle) = .empty; - errdefer { - for (new_cimports.items(.source)) |source| { - allocator.free(source); - } - new_cimports.deinit(allocator); - } - try collectImports( allocator, handle.uri, &new_tree, &new_file_imports, - &new_cimports, ); const file_imports = try new_file_imports.toOwnedSlice(allocator); @@ -446,12 +433,10 @@ pub const Handle = struct { old_handle.tree = handle.tree; old_handle.impl.has_tree_and_source = true; } - old_handle.cimports = handle.cimports; handle.tree = new_tree; old_handle.file_imports = handle.file_imports; handle.file_imports = file_imports; - handle.cimports = new_cimports; handle.impl.has_tree_and_source = true; old_handle.document_scope = handle.document_scope; @@ -484,7 +469,6 @@ pub const Handle = struct { uri: Uri, tree: *const Ast, file_imports: *std.ArrayList(Uri), - cimports: *std.MultiArrayList(CImportHandle), ) error{OutOfMemory}!void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -522,25 +506,6 @@ pub const Handle = struct { file_imports.appendAssumeCapacity(import_uri); continue; } - - if (std.mem.eql(u8, name, "@cImport")) { - try cimports.ensureUnusedCapacity(allocator, 1); - - const c_source = translate_c.convertCInclude(allocator, tree, node) catch |err| switch (err) { - error.Unsupported => continue, - error.OutOfMemory => return error.OutOfMemory, - }; - - var hasher: CImportHasher = .init(&@splat(0)); - hasher.update(c_source); - - cimports.appendAssumeCapacity(.{ - .node = node, - .hash = hasher.finalResult(), - .source = c_source, - }); - continue; - } } } @@ -549,7 +514,6 @@ pub const Handle = struct { .uri = undefined, .tree = undefined, .file_imports = &.{}, - .cimports = .empty, .lsp_synced = undefined, .impl = .{ .store = undefined, @@ -572,9 +536,6 @@ pub const Handle = struct { for (self.file_imports) |uri| uri.deinit(allocator); allocator.free(self.file_imports); - for (self.cimports.items(.source)) |source| allocator.free(source); - self.cimports.deinit(allocator); - self.impl.associated_build_file.deinit(allocator); self.impl.associated_compilation_units.deinit(allocator); @@ -712,11 +673,6 @@ pub fn deinit(self: *DocumentStore) void { self.allocator.destroy(build_file); } self.build_files.deinit(self.allocator); - - for (self.cimports.values()) |*result| { - result.deinit(self.allocator); - } - self.cimports.deinit(self.allocator); } self.* = undefined; @@ -1635,7 +1591,6 @@ fn createAndStoreDocument( .uri = gop.key_ptr.*, .tree = undefined, .file_imports = &.{}, - .cimports = .empty, .lsp_synced = options.lsp_synced, .impl = .{ .store = store, @@ -1672,288 +1627,6 @@ fn createAndStoreDocument( return &handle_future.handle; } -pub const CImportHasher = std.crypto.auth.siphash.SipHash128(1, 3); -pub const CImportHash = [CImportHasher.mac_length]u8; - -pub const CImportHandle = struct { - /// the `@cImport` node - node: Ast.Node.Index, - /// hash of c source file - hash: CImportHash, - /// c source file - source: []const u8, -}; - -/// returns `true` if all include paths could be collected -/// may return `false` because include paths from a build.zig may not have been resolved already -/// **Thread safe** takes a shared lock -pub fn collectIncludeDirs( - store: *DocumentStore, - allocator: std.mem.Allocator, - handle: *Handle, - include_dirs: *std.ArrayList([]const u8), -) error{ Canceled, OutOfMemory }!bool { - comptime std.debug.assert(supports_build_system); - - const tracy_zone = tracy.trace(@src()); - defer tracy_zone.end(); - - var arena_allocator: std.heap.ArenaAllocator = .init(allocator); - defer arena_allocator.deinit(); - - const target_info: std.Target = .{ - .cpu = .{ - .arch = builtin.cpu.arch, - .model = undefined, - .features = undefined, - }, - .os = builtin.target.os, - .abi = .none, - .ofmt = comptime std.Target.ObjectFormat.default(builtin.os.tag, builtin.cpu.arch), - .dynamic_linker = std.Target.DynamicLinker.none, - }; - const arena_allocator_allocator = arena_allocator.allocator(); - const native_paths: std.zig.system.NativePaths = try .detect(arena_allocator_allocator, store.io, &target_info, store.config.environ_map); - - try include_dirs.ensureUnusedCapacity(allocator, native_paths.include_dirs.items.len); - for (native_paths.include_dirs.items) |native_include_dir| { - include_dirs.appendAssumeCapacity(try allocator.dupe(u8, native_include_dir)); - } - - const collected_all = switch (try handle.getAssociatedBuildFile(store)) { - .none => true, - .unresolved => false, - .resolved => |resolved| collected_all: { - const build_config = resolved.build_file.tryLockConfig(store.io) orelse break :collected_all false; - defer resolved.build_file.unlockConfig(store.io); - - const module = build_config.modules.map.get(resolved.root_source_file) orelse break :collected_all true; - - try include_dirs.ensureUnusedCapacity(allocator, module.include_dirs.len); - for (module.include_dirs) |include_path| { - const absolute_path = if (std.Io.Dir.path.isAbsolute(include_path)) - try allocator.dupe(u8, include_path) - else blk: { - const build_file_path = resolved.build_file.uri.toFsPath(allocator) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.UnsupportedScheme => continue, - }; - const build_file_dirname = std.Io.Dir.path.dirname(build_file_path) orelse continue; - break :blk try std.Io.Dir.path.join(allocator, &.{ build_file_dirname, include_path }); - }; - - include_dirs.appendAssumeCapacity(absolute_path); - } - break :collected_all true; - }, - }; - - return collected_all; -} - -/// returns `true` if all c macro definitions could be collected -/// may return `false` because macros from a build.zig may not have been resolved already -/// **Thread safe** takes a shared lock -pub fn collectCMacros( - store: *DocumentStore, - allocator: std.mem.Allocator, - handle: *Handle, - c_macros: *std.ArrayList([]const u8), -) error{ Canceled, OutOfMemory }!bool { - comptime std.debug.assert(supports_build_system); - - const collected_all = switch (try handle.getAssociatedBuildFile(store)) { - .none => true, - .unresolved => false, - .resolved => |resolved| collected_all: { - const build_config = resolved.build_file.tryLockConfig(store.io) orelse break :collected_all false; - defer resolved.build_file.unlockConfig(store.io); - - const module = build_config.modules.map.get(resolved.root_source_file) orelse break :collected_all true; - - try c_macros.ensureUnusedCapacity(allocator, module.c_macros.len); - for (module.c_macros) |c_macro| { - c_macros.appendAssumeCapacity(try allocator.dupe(u8, c_macro)); - } - break :collected_all true; - }, - }; - - return collected_all; -} - -/// returns the document behind `@cImport()` where `node` is the `cImport` node -/// if a cImport can't be translated e.g. requires computing a -/// comptime value `resolveCImport` will return null -/// returned memory is owned by DocumentStore -/// **Thread safe** takes an exclusive lock -pub fn resolveCImport(self: *DocumentStore, handle: *Handle, node: Ast.Node.Index) error{ Canceled, OutOfMemory }!?Uri { - comptime std.debug.assert(supports_build_system); - - const tracy_zone = tracy.trace(@src()); - defer tracy_zone.end(); - - if (self.config.zig_exe_path == null) return null; - if (self.config.zig_lib_dir == null) return null; - if (self.config.global_cache_dir == null) return null; - - const index = std.mem.findScalar(Ast.Node.Index, handle.cimports.items(.node), node) orelse return null; - const hash: CImportHash = handle.cimports.items(.hash)[index]; - const source = handle.cimports.items(.source)[index]; - - { - try self.mutex.lock(self.io); - defer self.mutex.unlock(self.io); - if (self.cimports.get(hash)) |result| { - switch (result) { - .success => |uri| return uri, - .failure => return null, - } - } - } - - var include_dirs: std.ArrayList([]const u8) = .empty; - defer { - for (include_dirs.items) |path| { - self.allocator.free(path); - } - include_dirs.deinit(self.allocator); - } - - const collected_all_include_dirs = self.collectIncludeDirs(self.allocator, handle, &include_dirs) catch |err| switch (err) { - error.Canceled => return error.Canceled, - else => { - log.err("failed to resolve include paths: {}", .{err}); - return null; - }, - }; - - var c_macros: std.ArrayList([]const u8) = .empty; - defer { - for (c_macros.items) |c_macro| { - self.allocator.free(c_macro); - } - c_macros.deinit(self.allocator); - } - - const collected_all_c_macros = self.collectCMacros(self.allocator, handle, &c_macros) catch |err| switch (err) { - error.Canceled => return error.Canceled, - else => { - log.err("failed to resolve include paths: {}", .{err}); - return null; - }, - }; - - const maybe_result = translate_c.translate( - self.io, - self.allocator, - self.config, - include_dirs.items, - c_macros.items, - source, - ) catch |err| switch (err) { - error.Canceled, error.OutOfMemory => |e| return e, - else => |e| { - log.err("failed to translate cimport: {}", .{e}); - return null; - }, - }; - var result = maybe_result orelse return null; - - if (result == .failure and (!collected_all_include_dirs or !collected_all_c_macros)) { - result.deinit(self.allocator); - return null; - } - - { - try self.mutex.lock(self.io); - defer self.mutex.unlock(self.io); - const gop = self.cimports.getOrPutValue(self.allocator, hash, result) catch |err| { - result.deinit(self.allocator); - return err; - }; - if (gop.found_existing) { - result.deinit(self.allocator); - result = gop.value_ptr.*; - } - } - - self.publishCimportDiagnostics(handle) catch |err| switch (err) { - error.Canceled => return error.Canceled, - else => { - log.err("failed to publish cImport diagnostics: {}", .{err}); - }, - }; - - switch (result) { - .success => |uri| { - log.debug("Translated cImport into {s}", .{uri.raw}); - return uri; - }, - .failure => return null, - } -} - -fn publishCimportDiagnostics(self: *DocumentStore, handle: *Handle) (std.mem.Allocator.Error || std.Io.File.Writer.Error)!void { - var wip: std.zig.ErrorBundle.Wip = undefined; - try wip.init(self.allocator); - defer wip.deinit(); - - const src_path = try wip.addString(""); - - for (handle.cimports.items(.hash), handle.cimports.items(.node)) |hash, node| { - const result = blk: { - try self.mutex.lock(self.io); - defer self.mutex.unlock(self.io); - break :blk self.cimports.get(hash) orelse continue; - }; - const error_bundle: std.zig.ErrorBundle = switch (result) { - .success => continue, - .failure => |bundle| bundle, - }; - - if (error_bundle.errorMessageCount() == 0) continue; - - const loc = offsets.nodeToLoc(&handle.tree, node); - const source_loc = std.zig.findLineColumn(handle.tree.source, loc.start); - - // assert that the `@intCast` below is safe - comptime std.debug.assert(std.zig.max_src_size <= std.math.maxInt(u32)); - - const src_loc = try wip.addSourceLocation(.{ - .src_path = src_path, - .line = @intCast(source_loc.line), - .column = @intCast(source_loc.column), - .span_start = @intCast(loc.start), - .span_main = @intCast(loc.start), - .span_end = @intCast(loc.end), - .source_line = try wip.addString(source_loc.source_line), - }); - - for (error_bundle.getMessages()) |err_msg_index| { - const err_msg = error_bundle.getErrorMessage(err_msg_index); - const msg = error_bundle.nullTerminatedString(err_msg.msg); - - try wip.addRootErrorMessage(.{ - .msg = try wip.addString(msg), - .src_loc = src_loc, - }); - } - } - - { - var error_bundle = try wip.toOwnedBundle(""); - errdefer error_bundle.deinit(self.allocator); - - try self.diagnostics_collection.pushSingleDocumentDiagnostics( - .cimport, - handle.uri, - .{ .error_bundle = error_bundle }, - ); - } - try self.diagnostics_collection.publishDiagnostics(); -} - pub const UriFromImportStringResult = union(enum) { none, one: Uri, diff --git a/src/Server.zig b/src/Server.zig index b7d48eb2b..94e87e9de 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -1028,13 +1028,6 @@ pub fn resolveConfiguration(server: *Server) error{ Canceled, OutOfMemory }!void server.document_store.invalidateBuildFile(build_file_uri); } } - - if (new_zig_exe_path or new_zig_lib_path) { - for (server.document_store.cimports.values()) |*cimport| { - cimport.deinit(server.document_store.allocator); - } - server.document_store.cimports.clearAndFree(server.document_store.allocator); - } } if (server.status == .initialized and diff --git a/src/analysis.zig b/src/analysis.zig index 1c3d9535d..a9c52128b 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1751,9 +1751,6 @@ fn resolveCallsiteReferences(analyser: *Analyser, decl_handle: DeclWithHandle) E }; const tree = &decl_handle.handle.tree; - const is_cimport = std.mem.eql(u8, std.Io.Dir.path.basename(decl_handle.handle.uri.raw), "cimport.zig"); - - if (is_cimport or !analyser.collect_callsite_references) return null; // protection against recursive callsite resolution const gop_resolved = try analyser.resolved_callsites.getOrPut(analyser.gpa, pay); @@ -2405,17 +2402,6 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error if (try analyser.resolveImportString(analyser.root_handle orelse return null, import_string)) |ty| return ty; return null; }, - .c_import => { - if (!DocumentStore.supports_build_system) return null; - const cimport_uri = (try analyser.store.resolveCImport(handle, node)) orelse return null; - - const new_handle = try analyser.store.getOrLoadHandle(cimport_uri) orelse return null; - - return .{ - .data = .{ .container = .root(new_handle) }, - .is_type_val = true, - }; - }, .FieldType => { if (params.len < 2) return null; @@ -5213,7 +5199,6 @@ pub fn getFieldAccessType( pub const PositionContext = union(enum) { builtin: offsets.Loc, import_string_literal: offsets.Loc, - cinclude_string_literal: offsets.Loc, embedfile_string_literal: offsets.Loc, string_literal: offsets.Loc, field_access: offsets.Loc, @@ -5242,7 +5227,6 @@ pub const PositionContext = union(enum) { return switch (self) { .builtin, .import_string_literal, - .cinclude_string_literal, .embedfile_string_literal, .string_literal, .field_access, @@ -5266,13 +5250,11 @@ pub const PositionContext = union(enum) { /// Asserts that `self` is one of the following: /// - `.import_string_literal` - /// - `.cinclude_string_literal` /// - `.embedfile_string_literal` /// - `.string_literal` pub fn stringLiteralContentLoc(self: PositionContext, source: []const u8) offsets.Loc { var location = switch (self) { .import_string_literal, - .cinclude_string_literal, .embedfile_string_literal, .string_literal, => |l| l, @@ -5500,8 +5482,6 @@ pub fn getPositionContext( const builtin_name = tree.source[loc.start..loc.end]; if (std.mem.eql(u8, builtin_name, "@import")) { new_state = .{ .import_string_literal = tok.loc }; - } else if (std.mem.eql(u8, builtin_name, "@cInclude")) { - new_state = .{ .cinclude_string_literal = tok.loc }; } else if (std.mem.eql(u8, builtin_name, "@embedFile")) { new_state = .{ .embedfile_string_literal = tok.loc }; } diff --git a/src/build_runner/build_runner.zig b/src/build_runner/build_runner.zig index d5e28a04f..44d67c787 100644 --- a/src/build_runner/build_runner.zig +++ b/src/build_runner/build_runner.zig @@ -282,8 +282,6 @@ pub fn main(init: process.Init.Minimal) !void { 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-cimport")) { - builder.verbose_cimport = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { builder.verbose_cc = true; } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { @@ -983,7 +981,6 @@ fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void { // const shared = @import("shared.zig"); -const Transport = shared.Transport; const BuildConfig = shared.BuildConfig; fn extractBuildInformation( @@ -999,96 +996,21 @@ fn extractBuildInformation( .generated => |gen| try set.put(allocator, gen.file.step, {}), } } - fn addIncludeDirStepDependencies(allocator: Allocator, set: *std.array_hash_map.Auto(*Step, void), include_dir: std.Build.Module.IncludeDir) !void { - switch (include_dir) { - .path, - .path_system, - .path_after, - .framework_path, - .framework_path_system, - => |lazy_path| try addLazyPathStepDependencies(allocator, set, lazy_path), - .other_step => |other| { - if (other.generated_h) |header| { - try set.put(allocator, header.step, {}); - } - if (other.installed_headers_include_tree) |include_tree| { - try set.put(allocator, include_tree.generated_directory.step, {}); - } - }, - .embed_path => { - // This only affects C source files - }, - .config_header_step => |config_header| try set.put(allocator, &config_header.step, {}), - } - } - /// Only adds the necessary dependencies to resolve the `root_source_file` and `include_dirs`. Does not include dependencies of imported modules. + /// 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); } - - for (module.include_dirs.items) |include_dir| { - try addIncludeDirStepDependencies(allocator, set, include_dir); - } } fn processModule( allocator: Allocator, - modules: *std.array_hash_map.String(shared.BuildConfig.Module), + 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; - var include_dirs: std.array_hash_map.String(void) = .empty; - var c_macros: std.array_hash_map.String(void) = .empty; - - if (compile) |exe| { - try processPkgConfig(allocator, &include_dirs, &c_macros, exe); - } - - try c_macros.ensureUnusedCapacity(allocator, module.c_macros.items.len); - for (module.c_macros.items) |c_macro| { - c_macros.putAssumeCapacity(c_macro, {}); - } - - for (module.include_dirs.items) |include_dir| { - switch (include_dir) { - .path, - .path_system, - .path_after, - .framework_path, - .framework_path_system, - => |include_path| try include_dirs.put(allocator, include_path.getPath(module.owner), {}), - - .other_step => |other| { - if (other.generated_h) |header| { - try include_dirs.put( - allocator, - std.Io.Dir.path.dirname(header.getPath()).?, - {}, - ); - } - if (other.installed_headers_include_tree) |include_tree| { - try include_dirs.put( - allocator, - include_tree.generated_directory.getPath(), - {}, - ); - } - }, - .embed_path => { - // This only affects C source files - }, - .config_header_step => |config_header| { - try include_dirs.put( - allocator, - config_header.generated_dir.getPath(), - {}, - ); - }, - } - } - 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) }); @@ -1096,8 +1018,6 @@ fn extractBuildInformation( // 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 = .{}, - .c_macros = &.{}, - .include_dirs = &.{}, }); for (module.import_table.keys(), module.import_table.values()) |name, import| { @@ -1108,8 +1028,6 @@ fn extractBuildInformation( gop_import.value_ptr.* = try std.Io.Dir.path.resolve(allocator, &.{ cwd, import_root_source_file.getPath2(import.owner, null) }); } } - gop.value_ptr.c_macros = try std.mem.concat(allocator, []const u8, &.{ gop.value_ptr.c_macros, c_macros.keys() }); - gop.value_ptr.include_dirs = try std.mem.concat(allocator, []const u8, &.{ gop.value_ptr.include_dirs, include_dirs.keys() }); } }; const gpa = run.gpa; @@ -1282,185 +1200,6 @@ fn extractBuildInformation( file_writer.interface.writeAll(stringified_build_config) catch return file_writer.err.?; } -fn processPkgConfig( - allocator: Allocator, - include_dirs: *std.array_hash_map.String(void), - c_macros: *std.array_hash_map.String(void), - exe: *Step.Compile, -) !void { - for (exe.root_module.link_objects.items) |link_object| { - if (link_object != .system_lib) continue; - const system_lib = link_object.system_lib; - - if (system_lib.use_pkg_config == .no) continue; - - const args = copied_from_zig.runPkgConfig(exe, system_lib.name) catch |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => switch (system_lib.use_pkg_config) { - .yes => { - // pkg-config failed, so zig will not add any include paths - continue; - }, - .force => { - std.log.warn("pkg-config failed for library {s}", .{system_lib.name}); - continue; - }, - .no => unreachable, - }, - else => |e| return e, - }; - for (args) |arg| { - if (std.mem.startsWith(u8, arg, "-I")) { - const candidate = arg[2..]; - try include_dirs.put(allocator, candidate, {}); - } else if (std.mem.startsWith(u8, arg, "-D")) { - try c_macros.put(allocator, arg, {}); - } - } - } -} - -const copied_from_zig = struct { - /// Run pkg-config for the given library name and parse the output, returning the arguments - /// that should be passed to zig to link the given library. - fn runPkgConfig(self: *Step.Compile, lib_name: []const u8) ![]const []const u8 { - const b = self.step.owner; - const pkg_name = match: { - // First we have to map the library name to pkg config name. Unfortunately, - // there are several examples where this is not straightforward: - // -lSDL2 -> pkg-config sdl2 - // -lgdk-3 -> pkg-config gdk-3.0 - // -latk-1.0 -> pkg-config atk - const pkgs = try getPkgConfigList(b); - - // Exact match means instant winner. - for (pkgs) |pkg| { - if (mem.eql(u8, pkg.name, lib_name)) { - break :match pkg.name; - } - } - - // Next we'll try ignoring case. - for (pkgs) |pkg| { - if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { - break :match pkg.name; - } - } - - // Now try appending ".0". - for (pkgs) |pkg| { - if (std.ascii.findIgnoreCase(pkg.name, lib_name)) |pos| { - if (pos != 0) continue; - if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { - break :match pkg.name; - } - } - } - - // Trimming "-1.0". - if (mem.endsWith(u8, lib_name, "-1.0")) { - const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; - for (pkgs) |pkg| { - if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { - break :match pkg.name; - } - } - } - - return error.PackageNotFound; - }; - - var code: u8 = undefined; - const stdout = if (b.runAllowFail(&.{ - "pkg-config", - pkg_name, - "--cflags", - "--libs", - }, &code, .ignore)) |stdout| stdout else |err| switch (err) { - error.ProcessTerminated => return error.PkgConfigCrashed, - error.ExecNotSupported => return error.PkgConfigFailed, - error.ExitCodeFailure => return error.PkgConfigFailed, - error.FileNotFound => return error.PkgConfigNotInstalled, - else => return err, - }; - - var zig_args = std.array_list.Managed([]const u8).init(b.allocator); - defer zig_args.deinit(); - - var it = mem.tokenizeAny(u8, stdout, " \r\n\t"); - while (it.next()) |tok| { - if (mem.eql(u8, tok, "-I")) { - const dir = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&.{ "-I", dir }); - } else if (mem.startsWith(u8, tok, "-I")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-L")) { - const dir = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&.{ "-L", dir }); - } else if (mem.startsWith(u8, tok, "-L")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-l")) { - const lib = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&.{ "-l", lib }); - } else if (mem.startsWith(u8, tok, "-l")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-D")) { - const macro = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&.{ "-D", macro }); - } else if (mem.startsWith(u8, tok, "-D")) { - try zig_args.append(tok); - } else if (b.debug_pkg_config) { - return self.step.fail("unknown pkg-config flag '{s}'", .{tok}); - } - } - - return zig_args.toOwnedSlice(); - } - - fn execPkgConfigList(self: *std.Build, out_code: *u8) (std.Build.PkgConfigError || std.Build.RunError)![]const std.Build.PkgConfigPkg { - const stdout = try self.runAllowFail(&.{ "pkg-config", "--list-all" }, out_code, .ignore); - var list = std.array_list.Managed(std.Build.PkgConfigPkg).init(self.allocator); - errdefer list.deinit(); - var line_it = mem.tokenizeAny(u8, stdout, "\r\n"); - while (line_it.next()) |line| { - if (mem.trim(u8, line, " \t").len == 0) continue; - var tok_it = mem.tokenizeAny(u8, line, " \t"); - try list.append(.{ - .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, - .desc = tok_it.rest(), - }); - } - return list.toOwnedSlice(); - } - - fn getPkgConfigList(self: *std.Build) ![]const std.Build.PkgConfigPkg { - if (self.pkg_config_pkg_list) |res| { - return res; - } - var code: u8 = undefined; - if (execPkgConfigList(self, &code)) |list| { - self.pkg_config_pkg_list = list; - return list; - } else |err| { - const result = switch (err) { - error.ProcessTerminated => error.PkgConfigCrashed, - error.ExecNotSupported => error.PkgConfigFailed, - error.ExitCodeFailure => error.PkgConfigFailed, - error.FileNotFound => error.PkgConfigNotInstalled, - error.InvalidName => error.PkgConfigNotInstalled, - error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, - else => return err, - }; - self.pkg_config_pkg_list = result; - return result; - } - } -}; - fn serveWatchErrorBundle( io: std.Io, step_id: u32, diff --git a/src/build_runner/shared.zig b/src/build_runner/shared.zig index 7047f5def..12ab19ed8 100644 --- a/src/build_runner/shared.zig +++ b/src/build_runner/shared.zig @@ -17,8 +17,6 @@ pub const BuildConfig = struct { pub const Module = struct { import_table: std.json.ArrayHashMap([]const u8), - c_macros: []const []const u8, - include_dirs: []const []const u8, }; pub const Compile = struct { diff --git a/src/features/completions.zig b/src/features/completions.zig index 09b6a5779..501031dea 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -217,19 +217,6 @@ fn typeToCompletion(builder: *Builder, ty: Analyser.Type) Analyser.Error!void { fn declToCompletion(builder: *Builder, decl_handle: Analyser.DeclWithHandle) Analyser.Error!void { const name = decl_handle.handle.tree.tokenSlice(decl_handle.nameToken()); - const is_cimport = std.mem.eql(u8, std.Io.Dir.path.basename(decl_handle.handle.uri.raw), "cimport.zig"); - if (is_cimport) { - if (std.mem.startsWith(u8, name, "_")) return; - const exclusions: std.StaticStringMap(void) = .initComptime(.{ - .{ "linux", {} }, - .{ "unix", {} }, - .{ "WIN32", {} }, - .{ "WINNT", {} }, - .{ "WIN64", {} }, - }); - if (exclusions.has(name)) return; - } - var doc_comments_buffer: [2][]const u8 = undefined; var doc_comments: std.ArrayList([]const u8) = .initBuffer(&doc_comments_buffer); if (try decl_handle.docComments(builder.arena)) |docs| { @@ -875,7 +862,6 @@ fn collectErrorSetNames( /// Asserts that `pos_context` is one of the following: /// - `.import_string_literal` -/// - `.cinclude_string_literal` /// - `.embedfile_string_literal` /// - `.string_literal` fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.PositionContext) Analyser.Error!void { @@ -986,15 +972,6 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi var search_paths: std.ArrayList([]const u8) = .empty; if (std.Io.Dir.path.isAbsolute(completing) and pos_context != .import_string_literal) { try search_paths.append(builder.arena, completing); - } else if (pos_context == .cinclude_string_literal) { - if (!DocumentStore.supports_build_system) return; - _ = store.collectIncludeDirs(builder.arena, builder.orig_handle, &search_paths) catch |err| switch (err) { - error.Canceled => return error.Canceled, - else => { - log.err("failed to resolve include paths: {}", .{err}); - return; - }, - }; } else blk: { const document_path = builder.orig_handle.uri.toFsPath(builder.arena) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -1018,7 +995,6 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi const entry = opt_entry orelse break; const expected_extension = switch (pos_context) { .import_string_literal => ".zig", - .cinclude_string_literal => ".h", .embedfile_string_literal => null, .string_literal => null, else => unreachable, @@ -1046,7 +1022,6 @@ fn completeFileSystemStringLiteral(builder: *Builder, pos_context: Analyser.Posi try builder.completions.append(builder.arena, .{ .label = label, .kind = if (entry.kind == .file) .File else .Folder, - .detail = if (pos_context == .cinclude_string_literal) path else null, .textEdit = createTextEdit(builder, .{ .newText = insert_text, .insert = insert_range, .replace = replace_range }), .sortText = try generateSortText(builder.arena, score, label), }); @@ -1094,7 +1069,6 @@ pub fn completionAtIndex( .error_access => |loc| try completeError(&builder, loc), .label_access, .label_decl => try completeLabel(&builder), .import_string_literal, - .cinclude_string_literal, .embedfile_string_literal, .string_literal, => try completeFileSystemStringLiteral(&builder, pos_context), diff --git a/src/features/goto.zig b/src/features/goto.zig index 953aa594f..4627415e2 100644 --- a/src/features/goto.zig +++ b/src/features/goto.zig @@ -165,30 +165,7 @@ fn gotoDefinitionBuiltin( const tree = &handle.tree; const name_loc = offsets.tokenIndexToLoc(tree.source, loc.start); const name = offsets.locToSlice(tree.source, name_loc); - if (std.mem.eql(u8, name, "@cImport")) { - if (!DocumentStore.supports_build_system) return null; - - const index = for (handle.cimports.items(.node), 0..) |cimport_node, index| { - const main_token = tree.nodeMainToken(cimport_node); - if (loc.start == tree.tokenStart(main_token)) break index; - } else return null; - const hash = handle.cimports.items(.hash)[index]; - - const result = analyser.store.cimports.get(hash) orelse return null; - const target_range: types.Range = .{ - .start = .{ .line = 0, .character = 0 }, - .end = .{ .line = 0, .character = 0 }, - }; - switch (result) { - .failure => return null, - .success => |uri| return .{ - .originSelectionRange = offsets.locToRange(tree.source, name_loc, offset_encoding), - .targetUri = uri.raw, - .targetRange = target_range, - .targetSelectionRange = target_range, - }, - } - } else if (std.mem.eql(u8, name, "@This")) { + if (std.mem.eql(u8, name, "@This")) { const ty = try analyser.innermostContainer(handle, name_loc.start); const definition = ty.typeDefinitionToken() orelse return null; const token_loc = offsets.tokenToLoc(tree, definition.token); @@ -243,8 +220,6 @@ fn gotoDefinitionString( const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const io = document_store.io; - const loc = pos_context.stringLiteralContentLoc(handle.tree.source); if (loc.start == loc.end) return null; const import_str = offsets.locToSlice(handle.tree.source, loc); @@ -253,25 +228,6 @@ fn gotoDefinitionString( .import_string_literal, .embedfile_string_literal, => try document_store.uriFromImportStr(arena, handle, import_str), - .cinclude_string_literal => blk: { - if (!DocumentStore.supports_build_system) return null; - - if (std.Io.Dir.path.isAbsolute(import_str)) { - break :blk .{ .one = try .fromPath(arena, import_str) }; - } - - var include_dirs: std.ArrayList([]const u8) = .empty; - _ = try document_store.collectIncludeDirs(arena, handle, &include_dirs); - for (include_dirs.items) |dir| { - const path = try std.Io.Dir.path.join(arena, &.{ dir, import_str }); - std.Io.Dir.accessAbsolute(io, path, .{}) catch |err| switch (err) { - error.Canceled => return error.Canceled, - else => {}, - }; - break :blk .{ .one = try .fromPath(arena, path) }; - } - return null; - }, else => unreachable, }; @@ -339,7 +295,6 @@ pub fn gotoHandler( } }, .import_string_literal, - .cinclude_string_literal, .embedfile_string_literal, => { const links = try gotoDefinitionString(&server.document_store, arena, pos_context, handle, server.offset_encoding) orelse return null; diff --git a/src/features/hover.zig b/src/features/hover.zig index 19f512641..7e27beaf1 100644 --- a/src/features/hover.zig +++ b/src/features/hover.zig @@ -213,7 +213,6 @@ fn hoverDefinitionBuiltin( analyser: *Analyser, arena: std.mem.Allocator, handle: *DocumentStore.Handle, - pos_index: usize, name_loc: offsets.Loc, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, @@ -226,33 +225,6 @@ fn hoverDefinitionBuiltin( var contents: std.ArrayList(u8) = .empty; - if (std.mem.eql(u8, name, "@cImport")) blk: { - const index = for (handle.cimports.items(.node), 0..) |cimport_node, index| { - const main_token = handle.tree.nodeMainToken(cimport_node); - const cimport_loc = offsets.tokenToLoc(&handle.tree, main_token); - if (cimport_loc.start <= pos_index and pos_index <= cimport_loc.end) break index; - } else break :blk; - - const source = handle.cimports.items(.source)[index]; - - switch (markup_kind) { - .plaintext, .unknown_value => { - try contents.print(arena, - \\{s} - \\ - , .{source}); - }, - .markdown => { - try contents.print(arena, - \\```c - \\{s} - \\``` - \\ - , .{source}); - }, - } - } - const builtin = data.builtins.get(name) orelse return null; const signature = try Analyser.renderBuiltinFunctionSignature( arena, @@ -523,7 +495,7 @@ pub fn hover( const pos_context = try Analyser.getPositionContext(arena, &handle.tree, source_index, true); const response = switch (pos_context) { - .builtin => |loc| try hoverDefinitionBuiltin(analyser, arena, handle, source_index, loc, markup_kind, offset_encoding), + .builtin => |loc| try hoverDefinitionBuiltin(analyser, arena, handle, loc, markup_kind, offset_encoding), .var_access, .test_doctest_name => try hoverDefinitionGlobal(analyser, arena, handle, source_index, markup_kind, offset_encoding), .field_access => |loc| try hoverDefinitionFieldAccess(analyser, arena, handle, source_index, loc, markup_kind, offset_encoding), .label_access, .label_decl => |loc| try hoverDefinitionLabel(analyser, arena, handle, source_index, loc, markup_kind, offset_encoding), diff --git a/src/features/inlay_hints.zig b/src/features/inlay_hints.zig index 9ffef22bd..02bc4c2fd 100644 --- a/src/features/inlay_hints.zig +++ b/src/features/inlay_hints.zig @@ -38,9 +38,6 @@ const excluded_builtins_set: std.EnumArray(std.zig.BuiltinFn.Tag, bool) = .init( .bit_reverse = true, .offset_of = false, .call = false, - .c_define = true, - .c_import = true, - .c_include = true, .clz = true, .cmpxchg_strong = false, .cmpxchg_weak = false, @@ -48,7 +45,6 @@ const excluded_builtins_set: std.EnumArray(std.zig.BuiltinFn.Tag, bool) = .init( .compile_log = true, // variadic .const_cast = true, .ctz = true, - .c_undef = true, .c_va_arg = false, .c_va_copy = false, .c_va_end = false, diff --git a/src/tools/langref.html.in b/src/tools/langref.html.in index 6f11bc006..2020d248c 100644 --- a/src/tools/langref.html.in +++ b/src/tools/langref.html.in @@ -1032,9 +1032,8 @@ {#header_close#} {#header_open|Local Variables#} -
- Local variables occur inside {#link|Functions#}, {#link|comptime#} blocks, and {#link|@cImport#} blocks. -
+Local variables occur inside {#link|Functions#}, {#link|comptime#} + blocks, and labeled {#link|Blocks#}.
When a local variable is {#syntax#}const{#endsyntax#}, it means that after initialization, the variable's value will not change. If the initialization value of a {#syntax#}const{#endsyntax#} variable is @@ -4573,62 +4572,6 @@ comptime { {#header_close#} - {#header_open|@cDefine#} -
{#syntax#}@cDefine(comptime name: []const u8, value) void{#endsyntax#}
- - This function can only occur inside {#syntax#}@cImport{#endsyntax#}. -
-
- This appends #define $name $value to the {#syntax#}@cImport{#endsyntax#}
- temporary buffer.
-
- To define without a value, like this: -
-#define _GNU_SOURCE
- - Use the void value, like this: -
-{#syntax#}@cDefine("_GNU_SOURCE", {}){#endsyntax#}
- {#see_also|Import from C Header File|@cInclude|@cImport|@cUndef|void#}
- {#header_close#}
- {#header_open|@cImport#}
- {#syntax#}@cImport(expression) type{#endsyntax#}
- - This function parses C code and imports the functions, types, variables, - and compatible macro definitions into a new empty struct type, and then - returns that type. -
-- {#syntax#}expression{#endsyntax#} is interpreted at compile time. The builtin functions - {#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#} work - within this expression, appending to a temporary buffer which is then parsed as C code. -
-- Usually you should only have one {#syntax#}@cImport{#endsyntax#} in your entire application, because it saves the compiler - from invoking clang multiple times, and prevents inline functions from being duplicated. -
-- Reasons for having multiple {#syntax#}@cImport{#endsyntax#} expressions would be: -
-#define CONNECTION_COUNT{#syntax#}@cInclude(comptime path: []const u8) void{#endsyntax#}
- - This function can only occur inside {#syntax#}@cImport{#endsyntax#}. -
-
- This appends #include <$path>\n to the {#syntax#}c_import{#endsyntax#}
- temporary buffer.
-
{#syntax#}@clz(operand: anytype) anytype{#endsyntax#}
{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.
@@ -4758,18 +4701,6 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val {#see_also|@clz|@popCount#} {#header_close#} - {#header_open|@cUndef#} -{#syntax#}@cUndef(comptime name: []const u8) void{#endsyntax#}
- - This function can only occur inside {#syntax#}@cImport{#endsyntax#}. -
-
- This appends #undef $name to the {#syntax#}@cImport{#endsyntax#}
- temporary buffer.
-
{#syntax#}@cVaArg(operand: *std.builtin.VaList, comptime T: type) T{#endsyntax#}
@@ -6784,35 +6715,6 @@ const builtin = @import("builtin");
{#see_also|Primitive Types#} {#header_close#} - {#header_open|Import from C Header File#} -
- The {#syntax#}@cImport{#endsyntax#} builtin function can be used
- to directly import symbols from .h files:
-
- The {#syntax#}@cImport{#endsyntax#} function takes an expression as a parameter.
- This expression is evaluated at compile-time and is used to control
- preprocessor directives and include multiple .h files:
-
@@ -6872,42 +6774,8 @@ $ zig translate-c -cflags -fshort-enums -- varycflags.h|grep -B1 do_something pub const enum_FOO = u8; pub extern fn do_something(foo: enum_FOO) c_int;{#end_shell_samp#} {#header_close#} - {#header_open|@cImport vs translate-c#} -
{#syntax#}@cImport{#endsyntax#} and zig translate-c use the same underlying - C translation functionality, so on a technical level they are equivalent. In practice, - {#syntax#}@cImport{#endsyntax#} is useful as a way to quickly and easily access numeric constants, typedefs, - and record types without needing any extra setup. If you need to pass {#link|cflags|Using -target and -cflags#} - to clang, or if you would like to edit the translated code, it is recommended to use - zig translate-c and save the results to a file. Common reasons for editing - the generated code include: changing {#syntax#}anytype{#endsyntax#} parameters in function-like macros to more - specific types; changing {#syntax#}[*c]T{#endsyntax#} pointers to {#syntax#}[*]T{#endsyntax#} or - {#syntax#}*T{#endsyntax#} pointers for improved type safety; and - {#link|enabling or disabling runtime safety|@setRuntimeSafety#} within specific functions. -
- {#header_close#} - {#see_also|Targets|C Type Primitives|Pointers|C Pointers|Import from C Header File|@cInclude|@cImport|@setRuntimeSafety#} {#header_close#} - {#header_open|C Translation Caching#} -- The C translation feature (whether used via zig translate-c or - {#syntax#}@cImport{#endsyntax#}) integrates with the Zig caching system. Subsequent runs with - the same source file, target, and cflags will use the cache instead of repeatedly translating - the same code. -
-- To see where the cached files are stored when compiling code that uses {#syntax#}@cImport{#endsyntax#}, - use the --verbose-cimport flag: -
- {#code|verbose_cimport_flag.zig#} -
- cimport.h contains the file to translate (constructed from calls to
- {#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#}),
- cimport.h.d is the list of file dependencies, and
- cimport.zig contains the translated output.
-
Some C constructs cannot be translated to Zig - for example, goto, @@ -6933,35 +6801,6 @@ pub extern fn do_something(foo: enum_FOO) c_int;{#end_shell_samp#}
{#see_also|opaque|extern|@compileError#} {#header_close#} - {#header_open|C Macros#} -- C Translation makes a best-effort attempt to translate function-like macros into equivalent - Zig functions. Since C macros operate at the level of lexical tokens, not all C macros - can be translated to Zig. Macros that cannot be translated will be demoted to - {#syntax#}@compileError{#endsyntax#}. Note that C code which uses macros will be - translated without any additional issues (since Zig operates on the pre-processed source - with macros expanded). It is merely the macros themselves which may not be translatable to - Zig. -
-Consider the following example:
- {#syntax_block|c|macro.c#} -#define MAKELOCAL(NAME, INIT) int NAME = INIT -int foo(void) { - MAKELOCAL(a, 1); - MAKELOCAL(b, 2); - return a + b; -} - {#end_syntax_block#} - {#shell_samp#}$ zig translate-c macro.c > macro.zig{#end_shell_samp#} - {#code|macro.zig#} - -Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable - macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since - it cannot be expressed as a Zig function; this simply means that you cannot directly use - {#syntax#}MAKELOCAL{#endsyntax#} from Zig. -
- {#see_also|@compileError#} - {#header_close#} {#header_open|C Pointers#}
diff --git a/src/translate_c.zig b/src/translate_c.zig
deleted file mode 100644
index a4f8fd783..000000000
--- a/src/translate_c.zig
+++ /dev/null
@@ -1,301 +0,0 @@
-//! Implementation of the `translate-c` i.e `@cImport`.
-
-const std = @import("std");
-const zig_builtin = @import("builtin");
-const DocumentStore = @import("DocumentStore.zig");
-const ast = @import("ast.zig");
-const tracy = @import("tracy");
-const Ast = std.zig.Ast;
-const Uri = @import("Uri.zig");
-const log = std.log.scoped(.translate_c);
-
-const OutMessage = std.zig.Client.Message;
-const InMessage = std.zig.Server.Message;
-
-/// converts a `@cInclude` node into an equivalent c header file
-/// which can then be handed over to `zig translate-c`
-/// Caller owns returned memory.
-///
-/// **Example**
-/// ```zig
-/// const glfw = @cImport({
-/// @cDefine("GLFW_INCLUDE_VULKAN", {});
-/// @cInclude("GLFW/glfw3.h");
-/// });
-/// ```
-/// gets converted into:
-/// ```c
-/// #define GLFW_INCLUDE_VULKAN
-/// #include "GLFW/glfw3.h"
-/// ```
-pub fn convertCInclude(allocator: std.mem.Allocator, tree: *const Ast, node: Ast.Node.Index) error{ OutOfMemory, Unsupported }![]const u8 {
- const tracy_zone = tracy.trace(@src());
- defer tracy_zone.end();
-
- std.debug.assert(ast.isBuiltinCall(tree, node));
- std.debug.assert(std.mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(node)), "@cImport"));
-
- var output: std.ArrayList(u8) = .empty;
- errdefer output.deinit(allocator);
-
- var buffer: [2]Ast.Node.Index = undefined;
- for (tree.builtinCallParams(&buffer, node).?) |child| {
- try convertCIncludeInternal(allocator, tree, child, &output);
- }
-
- return output.toOwnedSlice(allocator);
-}
-
-fn convertCIncludeInternal(
- allocator: std.mem.Allocator,
- tree: *const Ast,
- node: Ast.Node.Index,
- output: *std.ArrayList(u8),
-) error{ OutOfMemory, Unsupported }!void {
- var buffer: [2]Ast.Node.Index = undefined;
- if (tree.blockStatements(&buffer, node)) |statements| {
- for (statements) |statement| {
- try convertCIncludeInternal(allocator, tree, statement, output);
- }
- } else if (tree.builtinCallParams(&buffer, node)) |params| {
- if (params.len < 1) return;
-
- const call_name = tree.tokenSlice(tree.nodeMainToken(node));
-
- if (tree.nodeTag(params[0]) != .string_literal) return error.Unsupported;
- const first = extractString(tree.tokenSlice(tree.nodeMainToken(params[0])));
-
- if (std.mem.eql(u8, call_name, "@cInclude")) {
- try output.print(allocator, "#include <{s}>\n", .{first});
- } else if (std.mem.eql(u8, call_name, "@cDefine")) {
- if (params.len < 2) return;
-
- var buffer2: [2]Ast.Node.Index = undefined;
- const is_void = if (tree.blockStatements(&buffer2, params[1])) |block| block.len == 0 else false;
-
- if (is_void) {
- try output.print(allocator, "#define {s}\n", .{first});
- } else {
- if (tree.nodeTag(params[1]) != .string_literal) return error.Unsupported;
- const second = extractString(tree.tokenSlice(tree.nodeMainToken(params[1])));
- try output.print(allocator, "#define {s} {s}\n", .{ first, second });
- }
- } else if (std.mem.eql(u8, call_name, "@cUndef")) {
- try output.print(allocator, "#undef {s}\n", .{first});
- } else {
- return error.Unsupported;
- }
- }
-}
-
-pub const Result = union(enum) {
- // uri to the generated zig file
- success: Uri,
- // zig translate-c failed with the given error messages
- failure: std.zig.ErrorBundle,
-
- pub fn deinit(self: *Result, allocator: std.mem.Allocator) void {
- switch (self.*) {
- .success => |uri| uri.deinit(allocator),
- .failure => |*bundle| bundle.deinit(allocator),
- }
- }
-};
-
-/// takes a c header file and returns the result from calling `zig translate-c`
-/// returns a Uri to the generated zig file on success or the content of stderr on failure
-/// null indicates a failure which is automatically logged
-/// Caller owns returned memory.
-pub fn translate(
- io: std.Io,
- allocator: std.mem.Allocator,
- config: DocumentStore.Config,
- include_dirs: []const []const u8,
- c_macros: []const []const u8,
- source: []const u8,
-) !?Result {
- const tracy_zone = tracy.trace(@src());
- defer tracy_zone.end();
-
- const zig_exe_path = config.zig_exe_path.?;
- const zig_lib_dir = config.zig_lib_dir.?;
- const global_cache_dir = config.global_cache_dir.?;
-
- var random_bytes: [16]u8 = undefined;
- io.random(&random_bytes);
- var sub_path: [std.base64.url_safe.Encoder.calcSize(16)]u8 = undefined;
- _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
-
- var sub_dir = try global_cache_dir.handle.createDirPathOpen(io, &sub_path, .{});
- defer sub_dir.close(io);
-
- sub_dir.writeFile(io, .{
- .sub_path = "cimport.h",
- .data = source,
- }) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- else => {
- log.warn("failed to write to '{s}/{s}/cimport.h': {}", .{ global_cache_dir.path orelse ".", sub_path, err });
- return null;
- },
- };
-
- defer global_cache_dir.handle.deleteTree(io, &sub_path) catch |err| {
- log.warn("failed to delete '{s}/{s}': {}", .{ global_cache_dir.path orelse ".", sub_path, err });
- };
-
- const file_path = try std.Io.Dir.path.join(allocator, &.{ global_cache_dir.path orelse ".", &sub_path, "cimport.h" });
- defer allocator.free(file_path);
-
- const base_args = &[_][]const u8{
- zig_exe_path,
- "translate-c",
- "--zig-lib-dir",
- zig_lib_dir.path orelse ".",
- "--cache-dir",
- global_cache_dir.path orelse ".",
- "--global-cache-dir",
- global_cache_dir.path orelse ".",
- "-lc",
- "--listen=-",
- };
-
- const argc = base_args.len + 2 * include_dirs.len + c_macros.len + 1;
- var argv: std.ArrayList([]const u8) = try .initCapacity(allocator, argc);
- defer argv.deinit(allocator);
-
- argv.appendSliceAssumeCapacity(base_args);
-
- for (include_dirs) |include_dir| {
- argv.appendAssumeCapacity("-I");
- argv.appendAssumeCapacity(include_dir);
- }
-
- argv.appendSliceAssumeCapacity(c_macros);
-
- argv.appendAssumeCapacity(file_path);
-
- var process = std.process.spawn(io, .{
- .argv = argv.items,
- .stdin = .pipe,
- .stdout = .pipe,
- .stderr = .pipe,
- }) catch |err| switch (err) {
- error.Canceled => return error.Canceled,
- else => {
- log.err("failed to spawn zig translate-c process, error: {}", .{err});
- return null;
- },
- };
- defer process.kill(io);
-
- {
- var stdin_writer = process.stdin.?.writer(io, &.{});
- const writer = &stdin_writer.interface;
-
- writer.writeStruct(OutMessage.Header{
- .tag = .update,
- .bytes_len = 0,
- }, .little) catch return @as(std.Io.File.Writer.Error!?Result, stdin_writer.err.?);
-
- writer.writeStruct(OutMessage.Header{
- .tag = .exit,
- .bytes_len = 0,
- }, .little) catch return @as(std.Io.File.Writer.Error!?Result, stdin_writer.err.?);
- }
-
- 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(),
- &.{ process.stdout.?, process.stderr.? },
- );
-
- const stdout = multi_reader.reader(0);
- const stderr = multi_reader.reader(1);
-
- outer: while (true) {
- const timeout: std.Io.Timeout = .{ .duration = .{ .clock = .awake, .raw = .fromSeconds(90) } };
-
- while (stdout.buffered().len < @sizeOf(InMessage.Header)) {
- multi_reader.fill(64, timeout) catch |err| switch (err) {
- error.EndOfStream => break :outer,
- else => |e| return e,
- };
- }
- const header = stdout.takeStruct(InMessage.Header, .little) catch unreachable;
- while (stdout.buffered().len < header.bytes_len) {
- multi_reader.fill(64, timeout) catch |err| switch (err) {
- error.EndOfStream => break :outer,
- else => |e| return e,
- };
- }
- try multi_reader.checkAnyError();
-
- const body = stdout.take(header.bytes_len) catch unreachable;
- var reader: std.Io.Reader = .fixed(body);
-
- // log.debug("received header: {}", .{header});
-
- switch (header.tag) {
- .zig_version => {
- // log.debug("zig-version: {s}", .{body});
- },
- .emit_digest => {
- _ = reader.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch return error.InvalidMessage;
- const bin_result_path = reader.takeArray(16) catch return error.InvalidMessage;
- if (reader.bufferedLen() != 0) return error.InvalidMessage; // ensure that we read the entire body
-
- const hex_result_path = std.Build.Cache.binToHex(bin_result_path.*);
- const result_path = try global_cache_dir.join(allocator, &.{ "o", &hex_result_path, "cimport.zig" });
- defer allocator.free(result_path);
-
- return .{ .success = try .fromPath(allocator, std.mem.sliceTo(result_path, '\n')) };
- },
- .error_bundle => {
- const error_bundle_header = reader.takeStruct(InMessage.ErrorBundle, .little) catch return error.InvalidMessage;
-
- const extra = reader.readSliceEndianAlloc(allocator, u32, error_bundle_header.extra_len, .little) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.EndOfStream => return error.InvalidMessage,
- error.ReadFailed => unreachable,
- };
- errdefer allocator.free(extra);
-
- const string_bytes = reader.readAlloc(allocator, error_bundle_header.string_bytes_len) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.EndOfStream => return error.InvalidMessage,
- error.ReadFailed => unreachable,
- };
- errdefer 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 };
-
- return .{ .failure = error_bundle };
- },
- else => {},
- }
- }
-
- const term = try process.wait(io);
- switch (term) {
- .exited => |code| std.log.err("zig translate-c failed with code {d} and stderr:\n{s}", .{ code, stderr.buffered() }),
- .signal => |sig| std.log.err("zig translate-c failed with signal {t} and stderr:\n{s}", .{ sig, stderr.buffered() }),
- .stopped => |sig| std.log.err("zig translate-c stopped with signal {d} and stderr:\n{s}", .{ sig, stderr.buffered() }),
- .unknown => |code| std.log.err("zig translate-c failed for unknown reason with code {d} and stderr:\n{s}", .{ code, stderr.buffered() }),
- }
- return null;
-}
-
-fn extractString(str: []const u8) []const u8 {
- if (std.mem.startsWith(u8, str, "\"") and std.mem.endsWith(u8, str, "\"")) {
- return str[1 .. str.len - 1];
- } else {
- return str;
- }
-}
diff --git a/src/zls.zig b/src/zls.zig
index 7f244c28d..485a1e714 100644
--- a/src/zls.zig
+++ b/src/zls.zig
@@ -17,7 +17,6 @@ pub const print_ast = @import("print_ast.zig");
pub const Server = @import("Server.zig");
pub const snippets = @import("snippets.zig");
pub const testing = @import("testing.zig");
-pub const translate_c = @import("translate_c.zig");
pub const TrigramStore = @import("TrigramStore.zig");
pub const Uri = @import("Uri.zig");
diff --git a/tests/build_runner_cases/add_module.json b/tests/build_runner_cases/add_module.json
index bbd40dca0..cd2bb4209 100644
--- a/tests/build_runner_cases/add_module.json
+++ b/tests/build_runner_cases/add_module.json
@@ -2,9 +2,7 @@
"dependencies": {},
"modules": {
"root.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
}
},
"compilations": [],
diff --git a/tests/build_runner_cases/define_c_macro.json b/tests/build_runner_cases/define_c_macro.json
deleted file mode 100644
index 080938f0e..000000000
--- a/tests/build_runner_cases/define_c_macro.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "dependencies": {},
- "modules": {
- "root.zig": {
- "import_table": {},
- "c_macros": [
- "-Dkey=value"
- ],
- "include_dirs": []
- }
- },
- "compilations": [],
- "top_level_steps": [
- "install",
- "uninstall"
- ],
- "available_options": {}
-}
\ No newline at end of file
diff --git a/tests/build_runner_cases/define_c_macro.zig b/tests/build_runner_cases/define_c_macro.zig
deleted file mode 100644
index c1cc03fdd..000000000
--- a/tests/build_runner_cases/define_c_macro.zig
+++ /dev/null
@@ -1,8 +0,0 @@
-const std = @import("std");
-
-pub fn build(b: *std.Build) void {
- const foo = b.addModule("foo", .{
- .root_source_file = b.path("root.zig"),
- });
- foo.addCMacro("key", "value");
-}
diff --git a/tests/build_runner_cases/module_root_source_file_collision.json b/tests/build_runner_cases/module_root_source_file_collision.json
index 33e14abbd..529d90533 100644
--- a/tests/build_runner_cases/module_root_source_file_collision.json
+++ b/tests/build_runner_cases/module_root_source_file_collision.json
@@ -6,24 +6,16 @@
"collision": "first.zig",
"first": "first.zig",
"second": "second.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
},
"first.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
},
"second.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
},
"third.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
}
},
"compilations": [
diff --git a/tests/build_runner_cases/module_self_import.json b/tests/build_runner_cases/module_self_import.json
index 13e4c5c8a..08ca05c39 100644
--- a/tests/build_runner_cases/module_self_import.json
+++ b/tests/build_runner_cases/module_self_import.json
@@ -4,9 +4,7 @@
"root.zig": {
"import_table": {
"bar": "root.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
}
},
"compilations": [],
diff --git a/tests/build_runner_cases/multiple_module_import_names.json b/tests/build_runner_cases/multiple_module_import_names.json
index 5072d1e2c..7b3be8ba7 100644
--- a/tests/build_runner_cases/multiple_module_import_names.json
+++ b/tests/build_runner_cases/multiple_module_import_names.json
@@ -4,24 +4,18 @@
"foo.zig": {
"import_table": {
"bar_in_foo": "bar.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
},
"bar.zig": {
"import_table": {
"foo_in_bar": "foo.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
},
"main.zig": {
"import_table": {
"foo_in_main": "foo.zig",
"bar_in_main": "bar.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
}
},
"compilations": [],
diff --git a/tests/build_runner_cases/no_root_source_file.json b/tests/build_runner_cases/no_root_source_file.json
index 74269b1b8..ea65f5e23 100644
--- a/tests/build_runner_cases/no_root_source_file.json
+++ b/tests/build_runner_cases/no_root_source_file.json
@@ -2,16 +2,12 @@
"dependencies": {},
"modules": {
"baz.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
},
"bar.zig": {
"import_table": {
"with-root-source-file": "baz.zig"
- },
- "c_macros": [],
- "include_dirs": []
+ }
}
},
"compilations": [],
diff --git a/tests/build_runner_cases/public_module_with_generated_file.json b/tests/build_runner_cases/public_module_with_generated_file.json
index f170bf379..d89b8f79d 100644
--- a/tests/build_runner_cases/public_module_with_generated_file.json
+++ b/tests/build_runner_cases/public_module_with_generated_file.json
@@ -2,9 +2,7 @@
"dependencies": {},
"modules": {
".zig-local-cache/generated.zig": {
- "import_table": {},
- "c_macros": [],
- "include_dirs": []
+ "import_table": {}
}
},
"compilations": [],
diff --git a/tests/language_features/cimport.zig b/tests/language_features/cimport.zig
deleted file mode 100644
index bd9f0aaec..000000000
--- a/tests/language_features/cimport.zig
+++ /dev/null
@@ -1,135 +0,0 @@
-const std = @import("std");
-const zls = @import("zls");
-
-const Ast = std.zig.Ast;
-
-const Context = @import("../context.zig").Context;
-
-const offsets = zls.offsets;
-const translate_c = zls.translate_c;
-
-const io = std.testing.io;
-const allocator: std.mem.Allocator = std.testing.allocator;
-
-test "zig compile server - translate c" {
- var result1 = try testTranslate(
- \\void foo(int);
- \\void bar(float*);
- );
- defer result1.deinit(allocator);
- try std.testing.expect(result1 == .success);
-
- var result2 = try testTranslate(
- \\#include