From 92706195cb7166fff6f982b481e136ddfe48e615 Mon Sep 17 00:00:00 2001 From: kelmith Date: Sun, 26 Jul 2026 01:52:35 +0530 Subject: [PATCH 1/2] fix: capture PHP backed enums as classes and parent their methods --- ast/src/lang/queries/php.rs | 9 +++++++- ast/src/testing/coverage/php.rs | 6 ++--- ast/src/testing/php/app/Enums/OrderStatus.php | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 ast/src/testing/php/app/Enums/OrderStatus.php diff --git a/ast/src/lang/queries/php.rs b/ast/src/lang/queries/php.rs index 76822c99b..dad017060 100644 --- a/ast/src/lang/queries/php.rs +++ b/ast/src/lang/queries/php.rs @@ -117,6 +117,13 @@ impl Stack for Php { (trait_declaration name: (name) @{CLASS_NAME} ) @{CLASS_DEFINITION} + + (enum_declaration + name: (name) @{CLASS_NAME} + (class_interface_clause + (name) @{INCLUDED_MODULES} + )? + ) @{CLASS_DEFINITION} ]"# ) } @@ -438,7 +445,7 @@ impl Stack for Php { ) -> Result> { let mut parent = node.parent(); while let Some(current) = parent { - if current.kind() == "class_declaration" { + if current.kind() == "class_declaration" || current.kind() == "enum_declaration" { break; } parent = current.parent(); diff --git a/ast/src/testing/coverage/php.rs b/ast/src/testing/coverage/php.rs index 15b3c3596..8fc87cd8d 100644 --- a/ast/src/testing/coverage/php.rs +++ b/ast/src/testing/coverage/php.rs @@ -71,7 +71,7 @@ async fn test_btreemap_graph_structure() -> Result<()> { assert_eq!(endpoints.len(), 41); let functions = graph.find_nodes_by_type(NodeType::Function); - assert_eq!(functions.len(), 22); + assert_eq!(functions.len(), 23); let unit_tests = graph.find_nodes_by_type(NodeType::UnitTest); assert_eq!(unit_tests.len(), 5); @@ -83,7 +83,7 @@ async fn test_btreemap_graph_structure() -> Result<()> { assert_eq!(e2e_tests.len(), 0); let classes = graph.find_nodes_by_type(NodeType::Class); - assert_eq!(classes.len(), 9); + assert_eq!(classes.len(), 10); let data_models = graph.find_nodes_by_type(NodeType::DataModel); assert_eq!(data_models.len(), 0); @@ -111,7 +111,7 @@ async fn test_btreemap_test_to_function_edges() -> Result<()> { assert_eq!(calls_edges, 22); let contains_edges = graph.count_edges_of_type(EdgeType::Contains); - assert_eq!(contains_edges, 106); + assert_eq!(contains_edges, 110); let handler_edges = graph.count_edges_of_type(EdgeType::Handler); assert_eq!(handler_edges, 13); diff --git a/ast/src/testing/php/app/Enums/OrderStatus.php b/ast/src/testing/php/app/Enums/OrderStatus.php new file mode 100644 index 000000000..b4bd0a5f1 --- /dev/null +++ b/ast/src/testing/php/app/Enums/OrderStatus.php @@ -0,0 +1,22 @@ + Function "label" "OrderStatus.php" +// @ast node: Function "label" + +namespace App\Enums; + +enum OrderStatus: string +{ + case Pending = 'pending'; + case Shipped = 'shipped'; + case Delivered = 'delivered'; + + public function label(): string + { + return match ($this) { + self::Pending => 'Pending', + self::Shipped => 'Shipped', + self::Delivered => 'Delivered', + }; + } +} From 27178d9303da8c7d0aebc1dacd88beae8654ab82 Mon Sep 17 00:00:00 2001 From: kelmith Date: Sun, 26 Jul 2026 01:52:41 +0530 Subject: [PATCH 2/2] fix: resolve C# primary constructor parameters as implicit class fields --- ast/src/lang/registry/cs_resolver.rs | 33 +++++++++++++++++++ ast/src/testing/coverage/csharp.rs | 12 +++---- .../csharp/Controllers/StatusController.cs | 23 +++++++++++++ cli/tests/cli/csharp.rs | 6 ++-- cli/tests/cli/php.rs | 4 +-- 5 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 ast/src/testing/csharp/Controllers/StatusController.cs diff --git a/ast/src/lang/registry/cs_resolver.rs b/ast/src/lang/registry/cs_resolver.rs index bc72494bb..8ad0f96e9 100644 --- a/ast/src/lang/registry/cs_resolver.rs +++ b/ast/src/lang/registry/cs_resolver.rs @@ -87,6 +87,7 @@ fn walk_for_class_fields(node: Node, source: &[u8], out: &mut HashMap) { + let Some(params) = (0..node.named_child_count()) + .filter_map(|i| node.named_child(i)) + .find(|n| n.kind() == "parameter_list") + else { + return; + }; + for i in 0..params.named_child_count() { + let Some(param) = params.named_child(i) else { + continue; + }; + if param.kind() != "parameter" { + continue; + } + let Some(name_node) = param.child_by_field_name("name") else { + continue; + }; + let Ok(name) = name_node.utf8_text(source) else { + continue; + }; + let Some(type_node) = param.child_by_field_name("type") else { + continue; + }; + if let Some(t) = strip_cs_type(type_node, source) { + fields.insert(name.to_string(), t); + } + } +} + fn extract_fields_from_body(body: Node, source: &[u8], fields: &mut HashMap) { for i in 0..body.named_child_count() { let Some(child) = body.named_child(i) else { diff --git a/ast/src/testing/coverage/csharp.rs b/ast/src/testing/coverage/csharp.rs index 53c2e550d..23c1eb621 100644 --- a/ast/src/testing/coverage/csharp.rs +++ b/ast/src/testing/coverage/csharp.rs @@ -68,10 +68,10 @@ async fn test_btreemap_graph_structure() -> Result<()> { let graph = repo.build_graph_inner::().await?; let endpoints = graph.find_nodes_by_type(NodeType::Endpoint); - assert_eq!(endpoints.len(), 81); + assert_eq!(endpoints.len(), 82); let functions = graph.find_nodes_by_type(NodeType::Function); - assert_eq!(functions.len(), 362); + assert_eq!(functions.len(), 363); let unit_tests = graph.find_nodes_by_type(NodeType::UnitTest); assert_eq!(unit_tests.len(), 31); @@ -83,7 +83,7 @@ async fn test_btreemap_graph_structure() -> Result<()> { assert_eq!(e2e_tests.len(), 0); let classes = graph.find_nodes_by_type(NodeType::Class); - assert_eq!(classes.len(), 164); + assert_eq!(classes.len(), 165); let data_models = graph.find_nodes_by_type(NodeType::DataModel); assert_eq!(data_models.len(), 19); @@ -108,13 +108,13 @@ async fn test_btreemap_test_to_function_edges() -> Result<()> { let graph = repo.build_graph_inner::().await?; let calls_edges = graph.count_edges_of_type(EdgeType::Calls); - assert_eq!(calls_edges, 300); + assert_eq!(calls_edges, 326); let contains_edges = graph.count_edges_of_type(EdgeType::Contains); - assert_eq!(contains_edges, 1523); + assert_eq!(contains_edges, 1527); let handler_edges = graph.count_edges_of_type(EdgeType::Handler); - assert_eq!(handler_edges, 81); + assert_eq!(handler_edges, 82); let implements_edges = graph.count_edges_of_type(EdgeType::Implements); assert_eq!(implements_edges, 0); diff --git a/ast/src/testing/csharp/Controllers/StatusController.cs b/ast/src/testing/csharp/Controllers/StatusController.cs new file mode 100644 index 000000000..450f9c15c --- /dev/null +++ b/ast/src/testing/csharp/Controllers/StatusController.cs @@ -0,0 +1,23 @@ +// @ast node: Class "StatusController" +// @ast node: Function "GetStatus" +// @ast edge: Calls -> Function "GetDashboardAsync" "CommonServices.cs" +// @ast node: Endpoint "status" +// @ast edge: Handler -> Function "GetStatus" "StatusController.cs" +// @ast node: Import "import-imports-srctestingcsharpcontrollersstatuscontrollercs-7" + +using Microsoft.AspNetCore.Mvc; +using CSharpTestServer.Services; + +namespace CSharpTestServer.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class StatusController(IAdminService adminService) : ControllerBase +{ + [HttpGet("status")] + public async Task> GetStatus() + { + var dashboard = await adminService.GetDashboardAsync(); + return Ok(dashboard); + } +} diff --git a/cli/tests/cli/csharp.rs b/cli/tests/cli/csharp.rs index ff04353bc..3506cfc87 100644 --- a/cli/tests/cli/csharp.rs +++ b/cli/tests/cli/csharp.rs @@ -47,9 +47,9 @@ fn parse_stats_csharp_dir() { let out = run_stakgraph(&["--stats", &dir]); assert_eq!(out.exit_code, 0, "stderr: {}", out.stderr); - assert!(out.stdout.contains("Endpoint 81"), "stdout: {}", out.stdout); - assert!(out.stdout.contains("Class 164"), "stdout: {}", out.stdout); - assert!(out.stdout.contains("Function 362"), "stdout: {}", out.stdout); + assert!(out.stdout.contains("Endpoint 82"), "stdout: {}", out.stdout); + assert!(out.stdout.contains("Class 165"), "stdout: {}", out.stdout); + assert!(out.stdout.contains("Function 363"), "stdout: {}", out.stdout); } // ── search ──────────────────────────────────────────────────────────────────── diff --git a/cli/tests/cli/php.rs b/cli/tests/cli/php.rs index be15b41c6..0bb3ebe59 100644 --- a/cli/tests/cli/php.rs +++ b/cli/tests/cli/php.rs @@ -51,8 +51,8 @@ fn parse_stats_php_dir() { assert_eq!(out.exit_code, 0, "stderr: {}", out.stderr); assert!(out.stdout.contains("Endpoint 41"), "stdout: {}", out.stdout); - assert!(out.stdout.contains("Class 9"), "stdout: {}", out.stdout); - assert!(out.stdout.contains("Function 22"), "stdout: {}", out.stdout); + assert!(out.stdout.contains("Class 10"), "stdout: {}", out.stdout); + assert!(out.stdout.contains("Function 23"), "stdout: {}", out.stdout); } // ── search ────────────────────────────────────────────────────────────────────