go: Add support for running sub-tests in table tests without explicit variables for test cases (#46645)

Lev Zakharov and Conrad Irwin created

This PR extends support to run Go table-test subtests (#35657), handling
tests without explicitly declaring variables for test scenarios.

<img width="611" height="318" alt="go-table-tests"
src="https://github.com/user-attachments/assets/e001b661-b512-4183-b6df-6c25f3af4f27"
/>

Release Notes:

- Improved support to run Go table-test subtests, handling tests without
explicitly declaring variables for test scenarios.

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/languages/src/go.rs            | 148 ++++++++++++++++++++++++++++
crates/languages/src/go/runnables.scm |  96 ++++++++++++++++++
2 files changed, 242 insertions(+), 2 deletions(-)

Detailed changes

crates/languages/src/go.rs 🔗

@@ -594,7 +594,10 @@ impl ContextProvider for GoContextProvider {
                     ),
                 ],
                 cwd: package_cwd.clone(),
-                tags: vec!["go-table-test-case".to_owned()],
+                tags: vec![
+                    "go-table-test-case".to_owned(),
+                    "go-table-test-case-without-explicit-variable".to_owned(),
+                ],
                 ..TaskTemplate::default()
             },
             TaskTemplate {
@@ -1117,6 +1120,149 @@ mod tests {
         // );
     }
 
+    #[gpui::test]
+    fn test_go_table_test_slice_without_explicit_variable_detection(cx: &mut TestAppContext) {
+        let language = language("go", tree_sitter_go::LANGUAGE.into());
+
+        let table_test = r#"
+        package main
+
+        import "testing"
+
+        func TestExample(t *testing.T) {
+            for _, tc := range []struct{
+                name string
+                anotherStr string
+            }{
+                {
+                    name: "test case 1",
+                    anotherStr: "foo",
+                },
+                {
+                    name: "test case 2",
+                    anotherStr: "bar",
+                },
+                {
+                    name: "test case 3",
+                    anotherStr: "baz",
+                },
+            } {
+                t.Run(tc.name, func(t *testing.T) {
+                    // test code here
+                })
+            }
+        }
+        "#;
+
+        let buffer =
+            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        cx.executor().run_until_parked();
+
+        let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
+            let snapshot = buffer.snapshot();
+            snapshot.runnable_ranges(0..table_test.len()).collect()
+        });
+
+        let tag_strings: Vec<String> = runnables
+            .iter()
+            .flat_map(|r| &r.runnable.tags)
+            .map(|tag| tag.0.to_string())
+            .collect();
+
+        assert!(
+            tag_strings.contains(&"go-test".to_string()),
+            "Should find go-test tag, found: {:?}",
+            tag_strings
+        );
+        assert!(
+            tag_strings.contains(&"go-table-test-case-without-explicit-variable".to_string()),
+            "Should find go-table-test-case-without-explicit-variable tag, found: {:?}",
+            tag_strings
+        );
+
+        let go_test_count = tag_strings.iter().filter(|&tag| tag == "go-test").count();
+
+        assert!(
+            go_test_count == 1,
+            "Should find exactly 1 go-test, found: {}",
+            go_test_count
+        );
+    }
+
+    #[gpui::test]
+    fn test_go_table_test_map_without_explicit_variable_detection(cx: &mut TestAppContext) {
+        let language = language("go", tree_sitter_go::LANGUAGE.into());
+
+        let table_test = r#"
+        package main
+
+        import "testing"
+
+        func TestExample(t *testing.T) {
+            for name, tc := range map[string]struct {
+          		someStr string
+          		fail    bool
+           	}{
+          		"test failure": {
+         			someStr: "foo",
+         			fail:    true,
+          		},
+          		"test success": {
+         			someStr: "bar",
+         			fail:    false,
+          		},
+           	} {
+                t.Run(name, func(t *testing.T) {
+                    // test code here
+                })
+            }
+        }
+        "#;
+
+        let buffer =
+            cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx));
+        cx.executor().run_until_parked();
+
+        let runnables: Vec<_> = buffer.update(cx, |buffer, _| {
+            let snapshot = buffer.snapshot();
+            snapshot.runnable_ranges(0..table_test.len()).collect()
+        });
+
+        let tag_strings: Vec<String> = runnables
+            .iter()
+            .flat_map(|r| &r.runnable.tags)
+            .map(|tag| tag.0.to_string())
+            .collect();
+
+        assert!(
+            tag_strings.contains(&"go-test".to_string()),
+            "Should find go-test tag, found: {:?}",
+            tag_strings
+        );
+        assert!(
+            tag_strings.contains(&"go-table-test-case-without-explicit-variable".to_string()),
+            "Should find go-table-test-case-without-explicit-variable tag, found: {:?}",
+            tag_strings
+        );
+
+        let go_test_count = tag_strings.iter().filter(|&tag| tag == "go-test").count();
+        let go_table_test_count = tag_strings
+            .iter()
+            .filter(|&tag| tag == "go-table-test-case-without-explicit-variable")
+            .count();
+
+        assert!(
+            go_test_count == 1,
+            "Should find exactly 1 go-test, found: {}",
+            go_test_count
+        );
+        assert!(
+            go_table_test_count == 2,
+            "Should find exactly 2 go-table-test-case-without-explicit-variable, found: {}",
+            go_table_test_count
+        );
+    }
+
     #[gpui::test]
     fn test_go_table_test_slice_ignored(cx: &mut TestAppContext) {
         let language = language("go", tree_sitter_go::LANGUAGE.into());

crates/languages/src/go/runnables.scm 🔗

@@ -107,7 +107,7 @@
   (#set! tag go-main)
 )
 
-; Table test cases - slice and map
+; Table test cases - slice and map with explicit variable
 (
   (short_var_declaration
     left: (expression_list (identifier) @_collection_var)
@@ -206,3 +206,97 @@
   ) @_
   (#set! tag go-table-test-case)
 )
+
+; Table test cases - slice and map declared right inside the loop without
+; explicit variable
+(
+  (for_statement
+    (range_clause
+      left: (expression_list
+        [
+          (
+            (identifier)
+            (identifier) @_loop_var_inner
+          )
+          (identifier) @_loop_var_outer
+        ]
+      )
+      right: (composite_literal
+        type: [
+          (slice_type)
+          (map_type
+            key: (type_identifier) @_key_type
+            (#eq? @_key_type "string")
+          )
+        ]
+        body: (literal_value
+          [
+            (literal_element
+              (literal_value
+                (keyed_element
+                  (literal_element
+                    (identifier) @_field_name
+                  )
+                  (literal_element
+                    [
+                      (interpreted_string_literal) @run @_table_test_case_name
+                      (raw_string_literal) @run @_table_test_case_name
+                    ]
+                  )
+                )
+              )
+            )
+            (keyed_element
+              (literal_element
+                [
+                  (interpreted_string_literal) @run @_table_test_case_name
+                  (raw_string_literal) @run @_table_test_case_name
+                ]
+              )
+            )
+          ]
+        )
+      )
+    )
+    body: (block
+      (expression_statement
+        (call_expression
+          function: (selector_expression
+            operand: (identifier)
+            field: (field_identifier) @_run_method
+            (#eq? @_run_method "Run")
+          )
+          arguments: (argument_list
+            .
+            [
+              (selector_expression
+                operand: (identifier) @_tc_var
+                (#eq? @_tc_var @_loop_var_inner)
+                field: (field_identifier) @_field_check
+                (#eq? @_field_check @_field_name)
+              )
+              (identifier) @_arg_var
+              (#eq? @_arg_var @_loop_var_outer)
+            ]
+            .
+            (func_literal
+              parameters: (parameter_list
+                (parameter_declaration
+                  type: (pointer_type
+                    (qualified_type
+                      package: (package_identifier) @_pkg
+                      name: (type_identifier) @_type
+                      (#eq? @_pkg "testing")
+                      (#eq? @_type "T")
+                    )
+                  )
+                )
+              )
+            )
+          )
+        )
+      )
+    )
+  ) @_
+  (#set! tag go-table-test-case-without-explicit-variable)
+)