feat(ast): implement Issue #41 P0 parser adaptations#48
Conversation
- TypeScript: populate TopLevel structure alongside legacy default node - TypeScript: add structured call chain parsing (ReceiverExpr, Chain, ChainArguments, IsOptional) - Go: populate TopLevel structure and add ReceiverExpr to CodeCall - Rust: populate TopLevel structure and add ReceiverExpr to CodeCall - Add regression tests for new structured fields
📝 WalkthroughWalkthroughThis PR implements multi-language parser improvements from Issue Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR implements P0 parser adaptations for Issue #41, adding structured call chain parsing and TopLevel scope population across TypeScript, Go, and Rust parsers.
Changes:
- Added TopLevel structure population for package/module-level functions and fields in TypeScript, Go, and Rust
- Implemented structured call chain parsing in TypeScript with ReceiverExpr, Chain, ChainArguments, and IsOptional fields
- Added ReceiverExpr field to CodeCall in Go and Rust parsers
- Added regression tests for TopLevel structure and import/export metadata
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
TypeScriptFullIdentListener.kt |
Adds TopLevel population and structured call chain parsing with ChainCallInfo; maintains backward compatibility with legacy "default" node |
TypeScriptCallTest.kt |
Adds tests for TopLevel structure, Import/Export fields, and Container metadata |
GoFullIdentListener.kt |
Adds TopLevel population for package-level functions and ReceiverExpr field to CodeCall |
GoAnalyserTest.kt |
Updates test to include ReceiverExpr field in expected CodeCall |
RustAstBaseListener.kt |
Adds TopLevel population for free functions and fields |
RustFullIdentListener.kt |
Adds ReceiverExpr field to CodeCall for call and method call expressions |
Comments suppressed due to low confidence (1)
chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoAnalyserTest.kt:42
- The test verifies ReceiverExpr for the function call within main(), but doesn't verify that the package-level function main() is correctly populated in codeContainer.TopLevel. Add assertions to verify that TopLevel is populated and contains the main function, similar to the TypeScript tests for TopLevel structure.
@Test
fun analysis() {
val helloworld = """
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
"""
val codeContainer = GoAnalyser().analysis(helloworld, "")
val value = codeContainer.DataStructures[0]
val expect = CodeDataStruct(
Functions = listOf(
CodeFunction(
Name = "main", Package = "main",
FunctionCalls = listOf(
CodeCall(
NodeName = "fmt",
FunctionName = "Println",
Parameters = listOf(CodeProperty(TypeValue = "hello world", TypeType = "string")),
// New structured field (Issue #41)
ReceiverExpr = "fmt"
)
),
)
),
)
assertEquals(Json.encodeToString(value), Json.encodeToString(expect))
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| is TypeScriptParser.ArgumentsExpressionContext -> { | ||
| val nodeName = wrapTargetType(singleExprCtx) | ||
| val funcName = buildFunctionName(singleExprCtx) | ||
|
|
||
| // Check if this is a chained call (contains ->) | ||
| val callee = singleExprCtx.singleExpression() | ||
| val (receiverExpr, chain, chainArgs, isOptional) = if (callee is TypeScriptParser.MemberDotExpressionContext) { | ||
| val info = buildStructuredCallChain(callee) | ||
| Quadruple(info.receiverExpr, info.chain, info.chainArguments, info.isOptional) | ||
| } else { | ||
| Quadruple(nodeName, listOf<String>(), listOf<List<CodeProperty>>(), false) | ||
| } | ||
|
|
||
| currentFunc.FunctionCalls += CodeCall( | ||
| Parameters = processArgumentList(singleExprCtx.arguments()?.argumentList()), | ||
| FunctionName = buildFunctionName(singleExprCtx), | ||
| NodeName = wrapTargetType(singleExprCtx), | ||
| Position = buildPosition(ctx) | ||
| FunctionName = funcName, | ||
| NodeName = nodeName, | ||
| Position = buildPosition(ctx), | ||
| // New structured fields (Issue #41) | ||
| ReceiverExpr = receiverExpr, | ||
| Chain = chain, | ||
| ChainArguments = chainArgs, | ||
| IsOptional = isOptional | ||
| ) |
There was a problem hiding this comment.
Similar to the bug in argumentsExpressionToCall, the structured call chain fields are incorrectly populated here. For chained calls, the Parameters should come from the innermost method's arguments (from chainInfo), not from the outermost call's arguments. For non-chained calls, ReceiverExpr should be empty for standalone function calls (where there's no receiver object), not set to nodeName which could be the function name itself. Additionally, ChainArguments has the same issue as in argumentsExpressionToCall where it doesn't include the outermost call's arguments.
| /** | ||
| * Issue #41 - Test TopLevel structure is populated for TypeScript | ||
| */ | ||
| @Test | ||
| fun shouldPopulateTopLevelStructure() { | ||
| val code = """ | ||
| const API_URL = "https://api.example.com"; | ||
|
|
||
| export function fetchData() { | ||
| return fetch(API_URL); | ||
| } | ||
|
|
||
| export const handler = async () => { | ||
| return "result"; | ||
| }; | ||
| """ | ||
| val codeFile = TypeScriptAnalyser().analysis(code, "test.ts") | ||
|
|
||
| // New: TopLevel should be populated | ||
| assertNotNull(codeFile.TopLevel, "TopLevel should be populated") | ||
| assertTrue(codeFile.TopLevel!!.Functions.isNotEmpty(), "TopLevel should have functions") | ||
| assertTrue(codeFile.TopLevel!!.Fields.isNotEmpty(), "TopLevel should have fields") | ||
|
|
||
| // Should have fetchData and handler functions | ||
| val functionNames = codeFile.TopLevel!!.Functions.map { it.Name } | ||
| assertTrue(functionNames.contains("fetchData"), "TopLevel should contain fetchData function") | ||
| assertTrue(functionNames.contains("handler"), "TopLevel should contain handler function") | ||
|
|
||
| // Should have API_URL field | ||
| val fieldNames = codeFile.TopLevel!!.Fields.map { it.TypeKey } | ||
| assertTrue(fieldNames.contains("API_URL"), "TopLevel should contain API_URL field") | ||
| } | ||
|
|
||
| /** | ||
| * Issue #41 - Test structured import/export with new fields | ||
| */ | ||
| @Test | ||
| fun shouldPopulateStructuredImportFields() { | ||
| val code = """ | ||
| import { foo, bar as baz } from "module"; | ||
| import * as utils from "./utils"; | ||
| import React from "react"; | ||
| """ | ||
| val codeFile = TypeScriptAnalyser().analysis(code, "test.ts") | ||
|
|
||
| val namedImport = codeFile.Imports.find { it.Source == "module" } | ||
| assertNotNull(namedImport) | ||
| assertEquals(chapi.domain.core.ImportKind.NAMED, namedImport.Kind) | ||
| assertTrue(namedImport.Specifiers.isNotEmpty(), "Named import should have specifiers") | ||
|
|
||
| val namespaceImport = codeFile.Imports.find { it.Source.contains("utils") } | ||
| assertNotNull(namespaceImport) | ||
| assertEquals(chapi.domain.core.ImportKind.NAMESPACE, namespaceImport.Kind) | ||
| assertEquals("utils", namespaceImport.NamespaceName) | ||
|
|
||
| val defaultImport = codeFile.Imports.find { it.Source == "react" } | ||
| assertNotNull(defaultImport) | ||
| assertEquals(chapi.domain.core.ImportKind.DEFAULT, defaultImport.Kind) | ||
| assertEquals("React", defaultImport.DefaultName) | ||
| } | ||
|
|
||
| /** | ||
| * Issue #41 - Test CodeContainer new fields (Language, Kind) | ||
| */ | ||
| @Test | ||
| fun shouldPopulateContainerMetadata() { | ||
| val code = """ | ||
| export const x = 1; | ||
| """ | ||
| val codeFile = TypeScriptAnalyser().analysis(code, "test.ts") | ||
|
|
||
| assertEquals("typescript", codeFile.Language) | ||
| assertEquals(chapi.domain.core.ContainerKind.MODULE, codeFile.Kind) | ||
| } | ||
| } |
There was a problem hiding this comment.
The new tests don't verify the core new fields that were added (ReceiverExpr, Chain, ChainArguments, IsOptional). The tests only check TopLevel, Import/Export fields, and Container metadata, but don't validate that the structured call chain parsing is working correctly. Add assertions to verify ReceiverExpr, Chain, ChainArguments, and IsOptional fields for various call patterns like "axios.get(url).then(handler)".
| /** Simple quadruple data class for destructuring. */ | ||
| private data class Quadruple<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D) |
There was a problem hiding this comment.
The Quadruple data class is a generic utility but is only used for destructuring in one place. Consider using Kotlin's built-in destructuring with data classes or inline this to avoid introducing a reusable generic class that isn't actually reused. Alternatively, create a specific data class with meaningful field names like ChainDestructured with receiverExpr, chain, chainArguments, and isOptional fields for better code clarity.
Summary
Implements P0 parser adaptations for Issue #41 (chapi-domain core model improvements):
TopLevelstructure alongside legacydefaultnode for backward compatibility; add structured call chain parsing withReceiverExpr,Chain,ChainArguments,IsOptionalfieldsTopLevelstructure for package-level functions; addReceiverExprtoCodeCallTopLevelstructure for free functions; addReceiverExprtoCodeCallRelated Issue
Closes #41 (partial - P0 parser adaptations)
Changes
TypeScriptFullIdentListener.ktTopLevelpopulation, structured chain call parsing withChainCallInfoGoFullIdentListener.ktTopLevelpopulation,ReceiverExprfieldRustAstBaseListener.ktTopLevelpopulationRustFullIdentListener.ktReceiverExprfieldTypeScriptCallTest.ktGoAnalyserTest.ktReceiverExprfieldTest Plan
./gradlew test)Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.