From 4c6e535560dfda34465db8989f752672d72c073a Mon Sep 17 00:00:00 2001 From: Shohruh Ismoilov Date: Sat, 7 Nov 2026 23:01:37 +0500 Subject: [PATCH] fix(go): inherit project build tags in detected launches (#1996) Detected Go run, test and debug configurations ignored the build tags set for the project, so tagged files were left out. They now inherit the project's tags (unless the configuration opts out), including reruns, file-scoped tests and debug launches; maintenance commands keep running without them. Co-authored-by: shohruh-genius <7658451+shohruh-genius@users.noreply.github.com> --- crates/ide-sdk/src/go.rs | 27 ++++++++++++++++++- .../debugger/__tests__/launch.test.ts | 20 ++++++++++++-- src/features/debugger/launch.ts | 4 ++- src/features/run/testing/rerun.test.ts | 8 ++++++ src/features/run/testing/rerun.ts | 2 +- 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/crates/ide-sdk/src/go.rs b/crates/ide-sdk/src/go.rs index e29fce2a3..34bc3d9a7 100644 --- a/crates/ide-sdk/src/go.rs +++ b/crates/ide-sdk/src/go.rs @@ -77,7 +77,11 @@ pub fn apply_to_run(config: &mut ide_run::RunConfig, tags: &GoBuildTags) { } } } - if config.extras.params.get("useTags").is_some_and(|v| v == "true") { + if config.extras.params.get("useTags").is_none_or(|value| value != "false") + && config.args.first().is_some_and(|operation| { + matches!(operation.as_str(), "run" | "build" | "test" | "generate" | "vet" | "list" | "install" | "clean") + }) + { let extra = settings["buildFlags"] .as_array() .into_iter() @@ -205,6 +209,27 @@ mod command_tests { use super::*; use std::collections::BTreeMap; + #[test] + fn detected_go_runs_inherit_tags_only_for_commands_that_accept_build_flags() { + let tags = GoBuildTags { custom: "integration".into(), ..Default::default() }; + for operation in + ["run", "build", "test", "generate", "vet", "list", "install", "clean", "mod", "work", "env", "version"] + { + let mut config: ide_run::RunConfig = serde_json::from_value(json!({ + "id":"go", "name":"Go", "kind":"go", "command":"go", "args":[operation, "."], + "env":{}, "detected":true, "isTest":false + })) + .unwrap(); + let accepts = !["mod", "work", "env", "version"].contains(&operation); + apply_to_run(&mut config, &tags); + assert_eq!(config.args.iter().any(|flag| flag == "-tags=integration"), accepts, "{operation}"); + config.args = vec![operation.into(), "-tags=explicit".into(), ".".into()]; + config.extras.params.insert("useTags".into(), "false".into()); + apply_to_run(&mut config, &tags); + assert_eq!(config.args, [operation, "-tags=explicit", "."]); + } + } + #[test] fn file_tests_debug_the_real_package_and_keep_sibling_test_helpers() { if crate::exec::which("go").is_none() { diff --git a/src/features/debugger/__tests__/launch.test.ts b/src/features/debugger/__tests__/launch.test.ts index 4c60822c3..4658aa663 100644 --- a/src/features/debugger/__tests__/launch.test.ts +++ b/src/features/debugger/__tests__/launch.test.ts @@ -236,7 +236,7 @@ describe("run config → launch config", () => { it("delve: debug mode for run, test mode for test", () => { const run = launchConfigFor(rc({ kind: "go", command: "go", args: ["run", "."], debugAdapter: "delve" }), "/p")!; expect(run.program).toBe("/p"); - expect(extra(run)).toEqual({ mode: "debug" }); + expect(extra(run)).toEqual({ mode: "debug", __goToolArgs: [], __goUseTags: true }); const sub = launchConfigFor( rc({ kind: "go", command: "go", args: ["run", "./cmd/api"], debugAdapter: "delve" }), "/p", @@ -246,7 +246,7 @@ describe("run config → launch config", () => { rc({ kind: "go", command: "go", args: ["test", "./..."], debugAdapter: "delve" }), "/p", )!; - expect(extra(test)).toEqual({ mode: "test" }); + expect(extra(test)).toEqual({ mode: "test", __goToolArgs: [], __goUseTags: true }); expect(test.program).toBe("/p"); // A gutter test / subtest: the package, and `-run` for the test binary. const one = launchConfigFor( @@ -398,3 +398,19 @@ it("netcoredbg launches the evaluated DLL with resolved profile arguments, env a expect(launchConfigFor({ ...c, params: { ...c.params, programArgs: "" } }, "/repo")?.args).toEqual(["--profile"]); expect(launchConfigFor({ ...c, params: {} }, "/repo")).toBeNull(); }); + +it("raw and typed Go debug configurations inherit custom tags unless explicitly disabled", () => { + for (const typed of [false, true]) { + for (const useTags of [undefined, "true", "false"]) { + const params = { + ...(typed ? { goKind: "test", scope: "package", target: "." } : {}), + ...(useTags === undefined ? {} : { useTags }), + }; + const launch = launchConfigFor( + rc({ kind: "go", command: "go", args: ["test", "-tags=explicit", "."], debugAdapter: "delve", params }), + "/p", + )!; + expect(extra(launch).__goUseTags).toBe(useTags !== "false"); + } + } +}); diff --git a/src/features/debugger/launch.ts b/src/features/debugger/launch.ts index e5fb36dae..ac8a2c00d 100644 --- a/src/features/debugger/launch.ts +++ b/src/features/debugger/launch.ts @@ -219,7 +219,7 @@ function delveLaunch(rc: RunConfig, cwd: string, root: string): LaunchConfig { dlvCwd: module, ...(p.scope === "file" ? { __goFiles: target.split("|").map((f) => join(module, f.trim())) } : {}), __goToolArgs: toolArgs, - __goUseTags: p.useTags === "true", + __goUseTags: p.useTags !== "false", ...(p.outputDir ? { output: join(root, `${p.outputDir}/go-debug`) } : {}), }); return out; @@ -276,6 +276,8 @@ function delveLaunch(rc: RunConfig, cwd: string, root: string): LaunchConfig { out.extraJson = JSON.stringify({ mode: test ? "test" : "debug", ...(flags.length ? { buildFlags: formatArgs(flags) } : {}), + __goToolArgs: flags, + __goUseTags: rc.params?.useTags !== "false", }); return out; } diff --git a/src/features/run/testing/rerun.test.ts b/src/features/run/testing/rerun.test.ts index b22ed4ad2..c69229199 100644 --- a/src/features/run/testing/rerun.test.ts +++ b/src/features/run/testing/rerun.test.ts @@ -326,3 +326,11 @@ describe("exact Go test and benchmark selections", () => { expect(config!.args).toEqual(["test", "-race", "-bench", ".", "example.com/p"]); }); }); + +it("keeps inherited Go build tags enabled when deriving a typed package rerun", () => { + const config = { + ...cfg("go", "go", ["test", "./..."]), + params: { goKind: "test", scope: "package", target: "./..." }, + }; + expect(rerunFailedConfig(config, [["example.com/p", "TestChosen"]])!.params?.useTags).toBe("true"); +}); diff --git a/src/features/run/testing/rerun.ts b/src/features/run/testing/rerun.ts index e2a9bec5b..6bc8d6dc6 100644 --- a/src/features/run/testing/rerun.ts +++ b/src/features/run/testing/rerun.ts @@ -225,7 +225,7 @@ export function testsConfig(config: RunConfig, paths: string[][], name: string): toolArgs: formatArgs(dropFlag(goTestParts(args).flags, ["-run", "-test.run"])), programArgs: formatArgs(goTestParts(args).program.slice(1)), } - : { module: config.params.module ?? "", useTags: config.params.useTags ?? "false" }, + : { module: config.params.module ?? "", useTags: config.params.useTags ?? "true" }, } : {}), };