Skip to content

Comments

feat(domain): add structured type reference system (CodeTypeRef)#44

Merged
phodal merged 5 commits intomasterfrom
feature/structured-type-ref
Jan 18, 2026
Merged

feat(domain): add structured type reference system (CodeTypeRef)#44
phodal merged 5 commits intomasterfrom
feature/structured-type-ref

Conversation

@phodal
Copy link
Owner

@phodal phodal commented Jan 18, 2026

Summary

Implements Issue #41 Part 4 - structured type system for cross-language type analysis.

Key Features

  • Backward Compatible: All 81 existing tests pass without modification
  • Optional Fields: New TypeRef fields default to null, not breaking existing code

New CodeTypeRef Class

Supports cross-language type constructs:

Feature Languages
Generic/template types Java, TypeScript, Rust, C++, Go
Union/intersection TypeScript
Pointer/reference C, C++, Go, Rust
Slice/map/channel Go
Trait bounds, lifetimes Rust
Function types, tuples TypeScript, Rust

Domain Class Updates

// CodeField - optional TypeRef
CodeField(TypeType = "List<String>", TypeRef = CodeTypeRef.generic("List", ...))

// CodeProperty - optional TypeRef
CodeProperty(TypeType = "int", TypeRef = CodeTypeRef.simple("int"))

// CodeFunction - optional ReturnTypeRef
CodeFunction(ReturnType = "Promise<User>", ReturnTypeRef = CodeTypeRef.generic("Promise", ...))

Test Plan

  • All 81 existing domain tests pass
  • New CodeTypeRefTest with 25 tests covering all type kinds
  • Full project build passes

Next Steps

Individual language parsers can now be updated to populate TypeRef fields:

  • TypeScript parser
  • Rust parser
  • Go parser
  • Java parser
  • Others...

Addresses #41

Summary by CodeRabbit

  • New Features

    • Introduced a structured, cross-language TypeRef model and factory helpers for rich type metadata.
    • Code fields, properties, functions now optionally carry TypeRef/ReturnTypeRef for stronger type analysis.
    • Parsers across many languages (Go, Java, Rust, TypeScript, Kotlin, C++, C#, Python, etc.) now populate TypeRef information.
  • Tests

    • Added comprehensive test suites validating TypeRef construction, language parsing, serialization, and backward compatibility.

✏️ Tip: You can customize this high-level summary in your review settings.

Implements Issue #41 Part 4 - structured type system for cross-language
type analysis. All existing tests pass (backward compatible).

New CodeTypeRef class supports:
- Generic/template types with recursive arguments
- Union/intersection types (TypeScript)
- Pointer/reference types (C/C++/Go/Rust)
- Go-specific: slice, map, channel
- Rust-specific: trait bounds, lifetimes
- Function types, tuple types, nullable types

Updated domain classes with optional TypeRef fields:
- CodeField.TypeRef
- CodeProperty.TypeRef
- CodeFunction.ReturnTypeRef
Copilot AI review requested due to automatic review settings January 18, 2026 07:00
@coderabbitai
Copy link

coderabbitai bot commented Jan 18, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Introduces a new cross-language type model CodeTypeRef and adds optional TypeRef/ReturnTypeRef fields to domain classes. Language-specific TypeRef builders and AST listeners were added/updated to populate these fields across Go, Java, Rust, TypeScript, C/C++, C#, Kotlin, Python; extensive tests and serialization coverage were added.

Changes

Cohort / File(s) Summary
Domain model & constructors
chapi-domain/src/main/kotlin/chapi/domain/core/*
\chapi-domain/src/main/kotlin/chapi/domain/core/CodeTypeRef.kt`, `CodeField.kt`, `CodeFunction.kt`, `CodeProperty.kt``
New CodeTypeRef (enum TypeRefKind + data class) and companion factories; added optional TypeRef: CodeTypeRef? = null to CodeField/CodeProperty and ReturnTypeRef to CodeFunction with @Since("2.3.0"), changing public data shapes and serialization.
Domain tests
chapi-domain/src/test/kotlin/chapi/domain/core/CodeTypeRefTest.kt
New comprehensive tests for CodeTypeRef construction, utilities, JSON (de)serialization, and backward-compatibility with domain classes.
Go: builder & listeners
chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt, \GoAstListener.kt`, `GoFullIdentListener.kt``
New GoTypeRefBuilder to build CodeTypeRef from Go parser contexts/strings; listeners now call builder and set TypeRef on CodeProperty/CodeField.
Java: builder & listener + tests
chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaTypeRefBuilder.kt, \JavaFullIdentListener.kt`, chapi-ast-java/src/test/kotlin/.../JavaTypeRefTest.kt`
New JavaTypeRefBuilder (parser/string) handling generics/arrays/common generics; listener populates TypeRef; new tests validate behavior.
Rust: builder & listeners + tests
chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt, \RustAstBaseListener.kt`, chapi-ast-rust/src/test/kotlin/.../RustTypeRefTest.kt`
New RustTypeRefBuilder handling paths, generics, pointers/references/slices/tuples/lifetimes; listeners set TypeRef for fields/properties/enum tuples; tests added.
TypeScript: builder & listeners + tests
chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt, \TypeScriptAstListener.kt`, `TypeScriptFullIdentListener.kt`, chapi-ast-typescript/src/test/.../TypeScriptTypeRefTest.kt`
New TypeScriptTypeRefBuilder for annotations/type refs (unions/intersections/generics/tuples/functions); listeners populate TypeRef and ReturnTypeRef; tests added.
C/C++: builder & listeners
chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPTypeRefBuilder.kt, \CPPBasicIdentListener.kt``
New CPPTypeRefBuilder parsing C++ type specs/strings (templates, pointers, references, arrays); listener now sets TypeRef on CodeProperty/CodeField.
C#: builder & listeners
chapi-ast-csharp/src/main/kotlin/chapi/ast/csharpast/CSharpTypeRefBuilder.kt, \CSharpAstListener.kt`, `CSharpFullIdentListener.kt``
New CSharpTypeRefBuilder handling nullable/arrays/generics/tuples and string parsing; listeners set TypeRef on fields/parameters.
Kotlin: builder & listener + tests
chapi-ast-kotlin/src/main/kotlin/chapi/ast/kotlinast/KotlinTypeRefBuilder.kt, \KotlinBasicIdentListener.kt`, chapi-ast-kotlin/src/test/.../KotlinTypeRefTest.kt`
New KotlinTypeRefBuilder (nullable/generics/function types); listeners populate TypeRef for properties/parameters; tests added.
Python: builder & listeners
chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt, \PythonAstBaseListener.kt`, `PythonFullIdentListener.kt``
New PythonTypeRefBuilder parsing annotations/strings (Optional/Union/Callable/collections); listeners set TypeRef for parameters and function return ReturnTypeRef.

Sequence Diagram(s)

sequenceDiagram
  participant Parser as "Language Parser"
  participant Listener as "AST Listener"
  participant Builder as "TypeRefBuilder"
  participant Domain as "CodeField/CodeProperty/CodeFunction"
  participant Tests as "Serializer / Tests"

  rect rgba(52,152,219,0.5)
    Parser->>Listener: emit parse context (type / annotation)
    Listener->>Builder: build(typeContext or typeText)
    Builder-->>Listener: CodeTypeRef (structured)
    Listener->>Domain: construct/update object with TypeType, TypeValue, TypeRef / ReturnTypeRef
    Domain->>Tests: serialize/assert (kotlinx.serialization / unit tests)
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I nibble types from far and near,
Raw names to unions, I hold them dear.
Builders hum, listeners pass the thread,
Fields don TypeRef hats upon their head.
Hooray — cross-language carrots for my bed! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.60% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the primary change: introducing a structured type reference system (CodeTypeRef) as a new domain feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive structured type reference system (CodeTypeRef) to enable cross-language type analysis in Chapi. The implementation adds optional type metadata fields to domain classes while maintaining full backward compatibility with existing code.

Changes:

  • Added new CodeTypeRef data class with factory methods for 13+ type constructs (generics, unions, pointers, slices, etc.)
  • Extended CodeField, CodeProperty, and CodeFunction with optional TypeRef/ReturnTypeRef fields
  • Implemented comprehensive test suite with 25 test cases covering all type kinds and backward compatibility

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
chapi-domain/src/main/kotlin/chapi/domain/core/CodeTypeRef.kt New structured type reference system with factory methods and utility functions for cross-language type representation
chapi-domain/src/test/kotlin/chapi/domain/core/CodeTypeRefTest.kt Comprehensive test suite covering all type kinds, helper methods, serialization, and backward compatibility
chapi-domain/src/main/kotlin/chapi/domain/core/CodeField.kt Added optional TypeRef field for structured type analysis
chapi-domain/src/main/kotlin/chapi/domain/core/CodeProperty.kt Added optional TypeRef field for structured type analysis
chapi-domain/src/main/kotlin/chapi/domain/core/CodeFunction.kt Added optional ReturnTypeRef field for structured return type analysis

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +147 to +153
fun pointer(pointeeType: CodeTypeRef, depth: Int = 1) = CodeTypeRef(
raw = "${"*".repeat(depth)}${pointeeType.raw}",
name = pointeeType.name,
kind = TypeRefKind.POINTER,
valueType = pointeeType,
pointerDepth = depth
)
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pointer() factory method doesn't validate that depth parameter is positive. Negative or zero depth would create invalid pointer representations. Consider adding validation to ensure depth >= 1.

Suggested change
fun pointer(pointeeType: CodeTypeRef, depth: Int = 1) = CodeTypeRef(
raw = "${"*".repeat(depth)}${pointeeType.raw}",
name = pointeeType.name,
kind = TypeRefKind.POINTER,
valueType = pointeeType,
pointerDepth = depth
)
fun pointer(pointeeType: CodeTypeRef, depth: Int = 1): CodeTypeRef {
require(depth >= 1) { "Pointer depth must be >= 1, but was $depth" }
return CodeTypeRef(
raw = "${"*".repeat(depth)}${pointeeType.raw}",
name = pointeeType.name,
kind = TypeRefKind.POINTER,
valueType = pointeeType,
pointerDepth = depth
)
}

Copilot uses AI. Check for mistakes.
Comment on lines +215 to +226
fun isPrimitive(): Boolean {
val primitives = setOf(
"int", "long", "short", "byte", "float", "double", "boolean", "char", "void",
"Int", "Long", "Short", "Byte", "Float", "Double", "Boolean", "Char", "Unit",
"number", "string", "boolean", "undefined", "null", "void", "never", "any", "unknown",
"i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize",
"f32", "f64", "bool", "str", "String",
"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "uintptr",
"float32", "float64", "complex64", "complex128", "rune", "error", "size_t"
)
return name.lowercase() in primitives.map { it.lowercase() }
}
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isPrimitive() method creates a new set and performs lowercase mapping on every invocation, which is inefficient. Consider moving the primitives set to a companion object constant for better performance.

Copilot uses AI. Check for mistakes.
Comment on lines +230 to +237
fun isCollection(): Boolean {
val collectionNames = setOf(
"list", "set", "map", "hashmap", "hashset", "treemap", "treeset",
"arraylist", "linkedlist", "vector", "deque", "queue", "stack",
"collection", "iterable", "vec", "slice"
)
return name.lowercase() in collectionNames || isSlice || kind == TypeRefKind.MAP
}
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isCollection() method creates a new set on every invocation, which is inefficient. Consider moving the collectionNames set to a companion object constant for better performance.

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +145
fun array(elementType: CodeTypeRef, dimensions: Int = 1) = CodeTypeRef(
raw = "${elementType.raw}${"[]".repeat(dimensions)}",
name = elementType.name,
kind = TypeRefKind.ARRAY,
valueType = elementType,
arrayDimensions = dimensions
)
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The array() factory method doesn't validate that dimensions parameter is positive. Negative or zero dimensions would create invalid type representations. Consider adding validation to ensure dimensions >= 1.

Suggested change
fun array(elementType: CodeTypeRef, dimensions: Int = 1) = CodeTypeRef(
raw = "${elementType.raw}${"[]".repeat(dimensions)}",
name = elementType.name,
kind = TypeRefKind.ARRAY,
valueType = elementType,
arrayDimensions = dimensions
)
fun array(elementType: CodeTypeRef, dimensions: Int = 1): CodeTypeRef {
require(dimensions >= 1) { "Array dimensions must be >= 1, but was $dimensions" }
return CodeTypeRef(
raw = "${elementType.raw}${"[]".repeat(dimensions)}",
name = elementType.name,
kind = TypeRefKind.ARRAY,
valueType = elementType,
arrayDimensions = dimensions
)
}

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Jan 18, 2026

Codecov Report

❌ Patch coverage is 45.89938% with 785 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.39%. Comparing base (57015cc) to head (ca04144).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
.../main/kotlin/chapi/ast/cppast/CPPTypeRefBuilder.kt 3.01% 176 Missing and 17 partials ⚠️
...kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt 13.33% 142 Missing and 14 partials ⚠️
...kotlin/chapi/ast/csharpast/CSharpTypeRefBuilder.kt 27.07% 117 Missing and 15 partials ⚠️
...rc/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt 49.25% 72 Missing and 30 partials ⚠️
...kotlin/chapi/ast/kotlinast/KotlinTypeRefBuilder.kt 48.78% 65 Missing and 19 partials ⚠️
...hapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt 60.27% 30 Missing and 28 partials ⚠️
...ain/kotlin/chapi/ast/javaast/JavaTypeRefBuilder.kt 76.59% 19 Missing and 14 partials ⚠️
...n/src/main/kotlin/chapi/domain/core/CodeTypeRef.kt 93.95% 3 Missing and 6 partials ⚠️
...n/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt 53.84% 0 Missing and 6 partials ⚠️
...n/chapi/ast/typescriptast/TypeScriptAstListener.kt 71.42% 4 Missing ⚠️
... and 5 more
Additional details and impacted files
@@             Coverage Diff              @@
##             master      #44      +/-   ##
============================================
- Coverage     73.43%   67.39%   -6.05%     
- Complexity     1250     1386     +136     
============================================
  Files            69       77       +8     
  Lines          5120     6548    +1428     
  Branches        978     1248     +270     
============================================
+ Hits           3760     4413     +653     
- Misses          775     1402     +627     
- Partials        585      733     +148     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Add TypeRefBuilder for each language to convert type annotations
to structured CodeTypeRef:

TypeScript:
- Union/intersection types
- Generic types (Promise<T>, Array<T>, etc.)
- Function types, tuples
- Nullable types, mapped types

Go:
- Pointer types (*T, **T)
- Slice types ([]T)
- Map types (map[K]V)
- Channel types (chan T, <-chan T, chan<- T)
- Generic types (T[U])

Rust:
- Reference types (&T, &'a T, &mut T)
- Raw pointer types (*const T, *mut T)
- Option<T> as nullable
- Result<T, E>, Vec<T>, Box<T>
- Tuple types, slice types
- Trait bounds and lifetimes

Java:
- Primitive and class types
- Array types (T[], T[][])
- Generic types (List<T>, Map<K,V>)
- Optional<T> as nullable

All existing tests pass.
Add comprehensive unit tests for TypeRef implementation:

TypeScript tests:
- Simple types, generics (Promise, Array, Map)
- Union types, nullable types
- Array bracket notation, tuples
- Class field types

Go tests:
- Simple types, pointer types
- Slice, map, channel types
- Array types, interface types
- Function return types

Rust tests:
- Simple types, reference types
- Generic types (Vec, Option, Result, HashMap, Box)
- Slice and array types
- TypeRefBuilder string parsing tests

Java tests:
- Primitive and class types
- Generic types (List, Map, Optional)
- Array types (single and multi-dimensional)
- Field types, TypeRefBuilder tests

Also fix Rust TypeRefBuilder to correctly extract typePath from
traitObjectTypeOneBound (simple types were being parsed as dyn types).
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt`:
- Around line 314-325: The generic-argument parser in GoTypeRefBuilder (inside
the branch that creates CodeTypeRef for TypeRefKind.GENERIC) incorrectly uses
argsRaw.split(",") which breaks on nested generics; change it to scan argsRaw
character-by-character tracking bracket depth (same approach used by
findMatchingBracket/map parsing) to split on commas only at depth 0, handle
empty args, trim each slice and call buildFromString for each part, and
construct the args list from those parsed segments instead of using split(",").
- Around line 187-207: The parameter/return parsing in GoTypeRefBuilder (in the
signature handling that calls parameterDecl().mapNotNull { build(param.type_())
}) only adds one entry per parameterDecl, losing arity when a decl has multiple
identifiers (e.g., func(a, b int)); update the logic in the parameter parsing
and the result.parameters() branch to iterate each parameterDecl's identifier
list and emit the type for each identifier (use the parameterDecl's identifiers
count or names and call build(param.type_()) that many times), so both
parameterTypes and the returns list reflect the true arity; keep creating a
single CodeTypeRef of kind TUPLE when multiple returns exist.
- Around line 267-280: The channel parsing in GoTypeRefBuilder incorrectly
requires spaces for directional channels; update the guard to also detect
raw.startsWith("<-chan") (no space) and change the element extraction to use
removePrefix("<-chan"), removePrefix("chan<-"), and removePrefix("chan")
followed by trimStart() so elementRaw is correct regardless of whitespace; keep
the existing mapping of directions ("receive" for "<-chan", "send" for "chan<-",
null for plain "chan") and then pass the trimmed elementRaw into buildFromString
for CodeTypeRef(valueType) while setting channelDirection accordingly.

In `@chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt`:
- Around line 612-624: The fieldDeclaration handling in JavaFullIdentListener
currently fetches the type by navigating declarators.parent.getChild(0) and
defensively casting; instead use the direct ctx.typeType() accessor: replace
that traversal with val typeType = ctx.typeType() and build the TypeRef via
JavaTypeRefBuilder.build(typeType), removing the safe-cast and fallback to
buildFromString; update the CodeField construction to use the resulting TypeRef
and the original type text/value variables so TypeRef structures remain
consistent with other methods (e.g., enterClassDeclaration,
enterFormalParameter).

In
`@chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt`:
- Around line 212-227: The CodeTypeRef returned for Array<T> currently sets kind
= TypeRefKind.ARRAY but leaves arrayDimensions at 0; update the return to set
arrayDimensions = 1 when kind == TypeRefKind.ARRAY (use the computed kind
variable) so that Array<T> and T[] are represented consistently; modify the
CodeTypeRef construction (fields raw, name/typeName, kind, args, valueType =
args.firstOrNull()) to also include arrayDimensions = if (kind ==
TypeRefKind.ARRAY) 1 else 0.
- Around line 256-270: The current function building function type refs ignores
rest parameters because it only maps ctx.parameterList()?.parameter(); update
the logic in TypeScriptTypeRefBuilder (the block that computes paramTypes and
returnType and returns CodeTypeRef) to also check
ctx.parameterList()?.restParameter() for a typeAnnotation, call build(...) on
that typeAnnotation (same helper used for
parameter.requiredParameter()/optionalParameter()), and append that resulting
type to the parameterTypes list so rest parameters (both sole restParameter and
trailing restParameter) are included in the resulting CodeTypeRef; keep
returnType produced by buildFromType(ctx.type_()) unchanged.
🧹 Nitpick comments (4)
chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt (2)

86-168: Consider semantic accuracy for map types.

For hashmap and btreemap (line 136), valueType = args.firstOrNull() assigns the key type, not the value type. This could be semantically confusing for consumers of the API.

If CodeTypeRef supports both keyType and valueType, consider populating them appropriately for map types:

 "vec", "hashmap", "btreemap", "hashset", "btreeset" -> {
     CodeTypeRef(
         raw = raw,
         name = name,
         qualified = qualified,
         kind = TypeRefKind.GENERIC,
         args = args,
-        valueType = args.firstOrNull()
+        keyType = if (name.lowercase() in listOf("hashmap", "btreemap")) args.getOrNull(0) else null,
+        valueType = if (name.lowercase() in listOf("hashmap", "btreemap")) args.getOrNull(1) else args.firstOrNull()
     )
 }

463-495: Missing curly brace tracking may break const generic parsing.

The splitTypeList function tracks <>, (), and [] for depth, but not {}. This could cause incorrect splits for const generics like Array<T, { N + 1 }> or complex type expressions containing blocks.

♻️ Suggested fix
 private fun splitTypeList(str: String): List<String> {
     val result = mutableListOf<String>()
     var depth = 0
     var current = StringBuilder()
     
     for (char in str) {
         when (char) {
-            '<', '(', '[' -> {
+            '<', '(', '[', '{' -> {
                 depth++
                 current.append(char)
             }
-            '>', ')', ']' -> {
+            '>', ')', ']', '}' -> {
                 depth--
                 current.append(char)
             }
chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaTypeRefBuilder.kt (2)

147-156: Wildcards treated as SIMPLE types.

The current approach handles wildcards (e.g., ? extends T, ? super T) by creating a SIMPLE CodeTypeRef with the raw text. This works but loses semantic information about bounded wildcards. Consider adding a dedicated WILDCARD kind with bound information in a future iteration if downstream consumers need wildcard semantics.


161-210: Inconsistent handling of special types between build() and buildFromString().

The build() method (via buildClassOrInterfaceType) applies special handling for Optional, List, Map, etc., setting appropriate kind, nullable, valueType, and keyType fields. However, buildFromString() treats all generic types uniformly without this special handling.

This could lead to inconsistent CodeTypeRef structures depending on the parsing path:

  • Optional<String> via build()kind=NULLABLE, nullable=true, valueType=String
  • Optional<String> via buildFromString()kind=GENERIC, nullable=false, valueType=null

For consistency, consider applying the same special-case logic here, similar to how RustTypeRefBuilder.buildFromString() handles Option as nullable.

♻️ Suggested enhancement for buildFromString
             // Generic type
             raw.contains("<") && raw.endsWith(">") -> {
                 val bracketStart = raw.indexOf('<')
                 val name = raw.substring(0, bracketStart)
                 val argsRaw = raw.substring(bracketStart + 1, raw.length - 1)
                 val args = splitTypeList(argsRaw).map { buildFromString(it.trim()) }
                 
-                CodeTypeRef(
-                    raw = raw,
-                    name = name,
-                    kind = TypeRefKind.GENERIC,
-                    args = args
-                )
+                // Apply special handling for common Java generics
+                when (name.lowercase()) {
+                    "optional" -> CodeTypeRef(
+                        raw = raw,
+                        name = name,
+                        kind = TypeRefKind.NULLABLE,
+                        args = args,
+                        nullable = true,
+                        valueType = args.firstOrNull()
+                    )
+                    "list", "arraylist", "linkedlist", "set", "hashset", "treeset" -> CodeTypeRef(
+                        raw = raw,
+                        name = name,
+                        kind = TypeRefKind.GENERIC,
+                        args = args,
+                        valueType = args.firstOrNull()
+                    )
+                    "map", "hashmap", "treemap", "linkedhashmap" -> CodeTypeRef(
+                        raw = raw,
+                        name = name,
+                        kind = TypeRefKind.GENERIC,
+                        args = args,
+                        keyType = args.getOrNull(0),
+                        valueType = args.getOrNull(1)
+                    )
+                    else -> CodeTypeRef(
+                        raw = raw,
+                        name = name,
+                        kind = TypeRefKind.GENERIC,
+                        args = args
+                    )
+                }
             }

Comment on lines +187 to +207
// Parse parameter types
val paramTypes = signature?.parameters()?.parameterDecl()?.mapNotNull { param ->
build(param.type_())
} ?: listOf()

// Parse return type
val result = signature?.result()
val returnType = when {
result?.parameters() != null -> {
// Multiple return values - create tuple
val returns = result.parameters().parameterDecl().mapNotNull { build(it.type_()) }
if (returns.size == 1) returns.first()
else CodeTypeRef(
raw = result.text,
kind = TypeRefKind.TUPLE,
tupleElements = returns
)
}
result?.type_() != null -> build(result.type_())
else -> null
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "GoTypeRefBuilder.kt" | head -5

Repository: phodal/chapi

Length of output: 122


🏁 Script executed:

find . -type f -name "*.g4" | grep -i go | head -10

Repository: phodal/chapi

Length of output: 138


🏁 Script executed:

git ls-files | grep -i "\.g4$" | head -20

Repository: phodal/chapi

Length of output: 959


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '180,215p'

Repository: phodal/chapi

Length of output: 1541


🏁 Script executed:

cat -n chapi-ast-go/src/main/antlr/GoParser.g4 | grep -A 20 "parameterDecl"

Repository: phodal/chapi

Length of output: 897


🏁 Script executed:

cat -n chapi-ast-go/src/main/antlr/GoParser.g4 | grep -B 5 -A 15 "identifierList"

Repository: phodal/chapi

Length of output: 4212


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | head -50

Repository: phodal/chapi

Length of output: 1861


🏁 Script executed:

grep -r "parameterDecl" chapi-ast-go/src/main/kotlin/chapi/ast/goast/ --include="*.kt" | head -20

Repository: phodal/chapi

Length of output: 1017


🏁 Script executed:

find chapi-ast-go -name "GoParser.kt" -type f

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoAstListener.kt | grep -B 10 -A 10 "parameterDecl"

Repository: phodal/chapi

Length of output: 1475


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt | grep -B 15 -A 15 "parameterDecl"

Repository: phodal/chapi

Length of output: 1986


🏁 Script executed:

# Check if there are generated parser files to understand the parameterDecl API structure
find chapi-ast-go -name "GoParser.java" -o -name "*ParameterDecl*" | head -10

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Search for how identifierList is used in the codebase to understand the API
rg "identifierList\(\)" chapi-ast-go/src/main/kotlin/chapi/ast/goast/ -A 3 -B 1

Repository: phodal/chapi

Length of output: 3654


🏁 Script executed:

# Check how other parts of the code handle multiple identifiers
rg "IDENTIFIER\(\)" chapi-ast-go/src/main/kotlin/chapi/ast/goast/ -B 2 -A 2 | head -40

Repository: phodal/chapi

Length of output: 4059


🏁 Script executed:

# Look for tests that might show function type handling
find chapi-ast-go -name "*Test*.kt" -type f | head -10

Repository: phodal/chapi

Length of output: 387


🏁 Script executed:

# Check if there are Go examples in test resources
find chapi-ast-go -name "*.go" -type f | head -20

Repository: phodal/chapi

Length of output: 1173


🏁 Script executed:

# Check a Go example file with function declarations
cat chapi-ast-go/src/test/resources/examples/function.go

Repository: phodal/chapi

Length of output: 99


🏁 Script executed:

# Check if there are any tests related to function types or type references
grep -r "FunctionType\|parameterDecl\|IDENTIFIER()" chapi-ast-go/src/test/kotlin/ -B 2 -A 5 | head -60

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Look at GoGenericsTest to understand how types are tested
cat -n chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoGenericsTest.kt | head -80

Repository: phodal/chapi

Length of output: 2706


🏁 Script executed:

# Check the full GoFullIdentListener to see how it handles multiple identifiers
cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt | sed -n '255,270p'

Repository: phodal/chapi

Length of output: 1126


🏁 Script executed:

# Check if there are any tests that verify function type handling with multiple parameters
grep -r "func.*,.*int\|flatMap\|mapNotNull" chapi-ast-go/src/test/ -B 2 -A 2

Repository: phodal/chapi

Length of output: 12122


🏁 Script executed:

# Look for any actual test cases with multiple identifiers in parameters
find chapi-ast-go/src/test/resources -name "*.go" -exec grep -l "func.*,.*int\|a.*b.*int" {} \;

Repository: phodal/chapi

Length of output: 555


🏁 Script executed:

cat chapi-ast-go/src/test/resources/examples/params.go

Repository: phodal/chapi

Length of output: 296


🏁 Script executed:

# Now let's check how buildParameters handles this case
cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt | sed -n '144,160p'

Repository: phodal/chapi

Length of output: 841


🏁 Script executed:

# Let's trace through what the current code does with "a, b int32"
# Check processingType to understand how it extracts identifiers
cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt | sed -n '158,175p'

Repository: phodal/chapi

Length of output: 807


🏁 Script executed:

# Let's check if there are any tests that validate parameter counting
grep -r "Parameters.size\|parameters\.size" chapi-ast-go/src/test/kotlin/ -B 5 -A 5

Repository: phodal/chapi

Length of output: 13999


🏁 Script executed:

# Check if there are any tests for GoTypeRefBuilder specifically
find chapi-ast-go -name "*TypeRef*" -o -name "*Type*Test*" | head -10

Repository: phodal/chapi

Length of output: 120


🏁 Script executed:

# Let's look at how GoTypeRefBuilder is tested or used with function types
grep -r "GoTypeRefBuilder\|FunctionType" chapi-ast-go/src/test/ -B 3 -A 3

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Check if identifierList().IDENTIFIER() method exists (for the proposed fix)
grep -r "\.IDENTIFIER()" chapi-ast-go/src/main/kotlin/ | head -10

Repository: phodal/chapi

Length of output: 1407


🏁 Script executed:

# Let's test the actual problem: create a simple Go code with function type and see what happens
cat > /tmp/test_functype.go << 'EOF'
package main

// Function type with combined parameters
type MyFunc func(a, b int) (x, y int)

func main() {}
EOF

# Check params.go to see if it has a function type test
cat chapi-ast-go/src/test/resources/specific/struct_function.go | head -20

Repository: phodal/chapi

Length of output: 338


🏁 Script executed:

# Let's verify the actual impact by checking if IDENTIFIER() method exists on IdentifierListContext
grep -r "identifierList()?.IDENTIFIER()" chapi-ast-go/src/main/kotlin/ -B 2 -A 2

Repository: phodal/chapi

Length of output: 628


🏁 Script executed:

# Now let's trace through exactly what happens with a function type that has combined params
# Look for where GoTypeRefBuilder is called for function types
grep -r "buildFunctionType\|FunctionTypeContext" chapi-ast-go/src/main/kotlin/ -B 3 -A 3

Repository: phodal/chapi

Length of output: 1521


🏁 Script executed:

# Check if there are any tests that create function types with combined parameters
grep -r "func.*\(.*,.*\).*func\|FunctionType.*param\|buildFunctionType" chapi-ast-go/ -B 2 -A 5

Repository: phodal/chapi

Length of output: 3876


🏁 Script executed:

# Let's verify the exact context - look at the buildFunctionType method more carefully
cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '184,220p'

Repository: phodal/chapi

Length of output: 1645


🏁 Script executed:

# Let's create a concrete test to verify the bug exists
cat > /tmp/test_functype_bug.kt << 'EOF'
import chapi.ast.goast.GoAnalyser

// Test function type with combined parameters
val code = """
package main

type MyFunc func(a, b int) (x, y int)
"""

val codeFile = GoAnalyser().analysis(code, "test.go")
val typeRef = codeFile.Types[0].TypeRef
println("Function type: $typeRef")
println("Parameter count: ${typeRef?.parameterTypes?.size}")
println("Return type: ${typeRef?.returnType}")
EOF

# This would need the actual test runner, but let's verify the logic another way

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's verify the parsing by checking what parameterDecl structure looks like
cat -n chapi-ast-go/src/main/antlr/GoParser.g4 | sed -n '388,400p'

Repository: phodal/chapi

Length of output: 578


🏁 Script executed:

# Check if there's test coverage for function type parameter counting
grep -r "parameterTypes\|returnType" chapi-ast-go/src/test/ -B 2 -A 2 | head -40

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's check if buildFunctionType is actually called and tested
grep -r "buildFunctionType\|func.*func.*int.*int\|MyFunc\|Finalsalary" chapi-ast-go/src/test/ -B 3 -A 5

Repository: phodal/chapi

Length of output: 1454


🏁 Script executed:

# Let's look at the struct_function test which has function types
cat chapi-ast-go/src/test/resources/specific/struct_function.go

Repository: phodal/chapi

Length of output: 862


🏁 Script executed:

# Check if there's a test that validates function type parsing
grep -r "FunctionType\|FUNCTION.*kind\|TypeRefKind.FUNCTION" chapi-ast-go/src/test/ -B 3 -A 3

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's verify the actual test code for the struct_function.go file
grep -r "struct_function\|Finalsalary" chapi-ast-go/src/test/ -B 5 -A 10

Repository: phodal/chapi

Length of output: 2010


🏁 Script executed:

# Check where types are processed - to see if function types are parsed through buildFunctionType
grep -r "type.*func\|Type.*func" chapi-ast-go/src/test/kotlin/ -B 2 -A 5

Repository: phodal/chapi

Length of output: 972


🏁 Script executed:

# Look for where TypeRefBuilder.build is called to understand the context
grep -r "GoTypeRefBuilder.build\|buildFunctionType" chapi-ast-go/src/main/kotlin/ -B 3 -A 3

Repository: phodal/chapi

Length of output: 4395


🏁 Script executed:

# Check if there's a test that validates the actual parsing of function types
grep -r "parameterTypes.*size\|returnType.*size" chapi-ast-go/ -B 3 -A 3

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's look at the actual CodeTypeRef class to understand what parameterTypes represents
find chapi-ast-go -name "CodeTypeRef*" -o -name "*TypeRef*" | grep -v test | grep -v antlr

Repository: phodal/chapi

Length of output: 120


🏁 Script executed:

# Check the domain module for CodeTypeRef
find . -name "CodeTypeRef.kt" -o -name "*TypeRef*.kt" | grep -v test | grep -v antlr | head -5

Repository: phodal/chapi

Length of output: 423


🏁 Script executed:

# Check the CodeTypeRef class definition
cat chapi-domain/src/main/kotlin/chapi/domain/core/CodeTypeRef.kt

Repository: phodal/chapi

Length of output: 8265


🏁 Script executed:

# Now let's look for actual test files that test GoTypeRefBuilder or function types
find chapi-ast-go/src/test -name "*.kt" -exec grep -l "parameterTypes\|returnType\|FunctionType" {} \;

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's also check if there are test resources with function type declarations with combined parameters
find chapi-ast-go/src/test/resources -name "*.go" -exec grep -l "func.*,.*)" {} \;

Repository: phodal/chapi

Length of output: 432


🏁 Script executed:

# Let's trace through the actual test to understand if parameterTypes is ever validated
grep -r "parameterTypes" chapi-domain/ -B 2 -A 2

Repository: phodal/chapi

Length of output: 1038


🏁 Script executed:

# Check if buildFunctionType is called anywhere in the codebase
grep -r "buildFunctionType\|\.parameterTypes\|\.returnType" chapi-ast-go/ --include="*.kt" | grep -v test | head -20

Repository: phodal/chapi

Length of output: 352


🏁 Script executed:

# Verify the test file that uses struct_function.go
find chapi-ast-go/src/test -name "*.kt" | xargs grep -l "struct_function"

Repository: phodal/chapi

Length of output: 40


Expand function type parameter/return arity to match identifier count.

For Go signatures like func(a, b int) or return types like (x, y int), each parameterDecl can specify multiple identifiers sharing a single type. The current mapNotNull logic adds only one entry per parameterDecl, undercounting the actual parameter/return arity. This misrepresents function type signatures.

✅ Proposed fix to expand by identifier count
-        val paramTypes = signature?.parameters()?.parameterDecl()?.mapNotNull { param ->
-            build(param.type_())
-        } ?: listOf()
+        val paramTypes = signature?.parameters()?.parameterDecl()?.flatMap { param ->
+            val typeRef = build(param.type_())
+            val count = param.identifierList()?.IDENTIFIER()?.size ?: 1
+            if (typeRef == null) emptyList() else List(count) { typeRef }
+        } ?: emptyList()
...
-                val returns = result.parameters().parameterDecl().mapNotNull { build(it.type_()) }
+                val returns = result.parameters().parameterDecl().flatMap { param ->
+                    val typeRef = build(param.type_())
+                    val count = param.identifierList()?.IDENTIFIER()?.size ?: 1
+                    if (typeRef == null) emptyList() else List(count) { typeRef }
+                }
🤖 Prompt for AI Agents
In `@chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt` around
lines 187 - 207, The parameter/return parsing in GoTypeRefBuilder (in the
signature handling that calls parameterDecl().mapNotNull { build(param.type_())
}) only adds one entry per parameterDecl, losing arity when a decl has multiple
identifiers (e.g., func(a, b int)); update the logic in the parameter parsing
and the result.parameters() branch to iterate each parameterDecl's identifier
list and emit the type for each identifier (use the parameterDecl's identifiers
count or names and call build(param.type_()) that many times), so both
parameterTypes and the returns list reflect the true arity; keep creating a
single CodeTypeRef of kind TUPLE when multiple returns exist.

Comment on lines +267 to +280
raw.startsWith("chan ") || raw.startsWith("chan<-") || raw.startsWith("<-chan ") -> {
val (direction, elementRaw) = when {
raw.startsWith("<-chan ") -> "receive" to raw.drop(7)
raw.startsWith("chan<- ") -> "send" to raw.drop(7)
raw.startsWith("chan ") -> null to raw.drop(5)
else -> null to raw
}
CodeTypeRef(
raw = raw,
name = "chan",
kind = TypeRefKind.CHANNEL,
valueType = buildFromString(elementRaw),
channelDirection = direction
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd chapi-ast-go && find . -name "GoTypeRefBuilder.kt" -type f | head -5

Repository: phodal/chapi

Length of output: 109


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '267,280p'

Repository: phodal/chapi

Length of output: 845


🌐 Web query:

Go channel syntax chan<- chan receive send valid

💡 Result:

Short answer: Yes — but be careful with parsing. Go accepts directional channel types, and you can have channels whose element types are channels. However the parser associates the <- with the nearest chan, so

  • chan<- chan int — valid: a send-only channel whose element is a (bidirectional) chan int. [1][2]
  • <-chan chan int — valid: a receive-only channel whose element is a (bidirectional) chan int. [1][2]

If you want "a channel of receive-only channels" (i.e. the element type is <-chan T) you must avoid the parser ambiguity. Use a named type or parentheses/alias:

  • type Recv <-chan int; c := make(chan Recv) — works. [3]
    (Attempts like make(chan <-chan int) are parsed as chan<- chan int and will not give the intended type; see the discussion and examples.) [3]

References:

  1. Go channel directions overview. [2]
  2. Go101 channels (directional channels). [1]
  3. StackOverflow: "How to create a channel of receive-only channels?" (explains the parsing ambiguity and shows using a type alias). [5]

🏁 Script executed:

cat > /tmp/test_parsing.py << 'EOF'
# Test current parsing logic
def parse_current(raw):
    if raw.startswith("<-chan "):
        return "receive", raw[7:]  # drop 7
    elif raw.startswith("chan<- "):
        return "send", raw[7:]     # drop 7
    elif raw.startswith("chan "):
        return None, raw[5:]       # drop 5
    else:
        return None, raw

def parse_proposed(raw):
    if raw.startswith("<-chan"):
        return "receive", raw.removeprefix("<-chan").lstrip()
    elif raw.startswith("chan<-"):
        return "send", raw.removeprefix("chan<-").lstrip()
    elif raw.startswith("chan "):
        return None, raw.removeprefix("chan").lstrip()
    else:
        return None, raw

test_cases = [
    "chan int",           # Current OK: (None, "int")
    "chan<- int",         # Current OK: (send, "int")
    "chan<-int",          # Current BUG: falls to else -> (None, "chan<-int")
    "<-chan int",         # Current OK: (receive, "int")
    "<-chanint",          # Guard FAILS: doesn't match "<-chan " guard
    "chan[]int",          # Test array element
]

print("CURRENT PARSING:")
for test in test_cases:
    direction, element = parse_current(test)
    print(f"  {test:20} -> direction={direction}, element={element}")

print("\nPROPOSED PARSING:")
for test in test_cases:
    direction, element = parse_proposed(test)
    print(f"  {test:20} -> direction={direction}, element={element}")
EOF
python /tmp/test_parsing.py

Repository: phodal/chapi

Length of output: 142


🏁 Script executed:

find chapi-ast-go -name "*Test*.kt" -o -name "*test*.kt" | grep -i go | head -10

Repository: phodal/chapi

Length of output: 387


🏁 Script executed:

find chapi-ast-go -type f -name "*.kt" | xargs grep -l "GoTypeRefBuilder" | head -5

Repository: phodal/chapi

Length of output: 250


🏁 Script executed:

grep -n "chan" chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoAnalyserTest.kt | head -20

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

grep -n "buildFromString\|TypeRef" chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoAnalyserTest.kt | head -10

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '260,290p'

Repository: phodal/chapi

Length of output: 1672


🏁 Script executed:

cat -n chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '1,50p' | head -30

Repository: phodal/chapi

Length of output: 1118


🏁 Script executed:

cat > /tmp/verify_fix.py << 'EOF'
# Simulating the parsing logic

def current_logic(raw):
    """Current code behavior"""
    print(f"Testing: '{raw}'")
    # Guard check
    if raw.startswith("chan ") or raw.startswith("chan<-") or raw.startswith("<-chan "):
        print(f"  ✓ Guard passed")
        # When pattern matching
        if raw.startswith("<-chan "):
            direction, elem = "receive", raw[7:]
        elif raw.startswith("chan<- "):
            direction, elem = "send", raw[7:]
        elif raw.startswith("chan "):
            direction, elem = None, raw[5:]
        else:
            direction, elem = None, raw  # FALLBACK
        print(f"  → direction={direction}, elementRaw='{elem}'")
        return direction, elem
    else:
        print(f"  ✗ Guard failed - not handled")
        return None, None

def proposed_logic(raw):
    """Proposed fix using removePrefix + trimStart"""
    print(f"Testing: '{raw}'")
    # Guard would need updating too
    if raw.startswith("chan") or raw.startswith("<-chan"):
        print(f"  ✓ Guard passed")
        if raw.startswith("<-chan"):
            direction = "receive"
            elem = raw.removeprefix("<-chan").lstrip()
        elif raw.startswith("chan<-"):
            direction = "send"
            elem = raw.removeprefix("chan<-").lstrip()
        elif raw.startswith("chan "):
            direction = None
            elem = raw.removeprefix("chan").lstrip()
        else:
            direction, elem = None, raw
        print(f"  → direction={direction}, elementRaw='{elem}'")
        return direction, elem
    else:
        print(f"  ✗ Guard failed")
        return None, None

test_cases = [
    "chan int",
    "chan<- int",
    "chan<-int",        # BUG CASE
    "<-chan int",
    "<-chanint",        # NOT HANDLED
]

print("=== CURRENT CODE ===")
for test in test_cases:
    current_logic(test)
    print()

print("\n=== PROPOSED FIX ===")
for test in test_cases:
    proposed_logic(test)
    print()
EOF
python /tmp/verify_fix.py

Repository: phodal/chapi

Length of output: 852


Handle channel types without whitespace (chan<-T, <-chanT).

Go allows channel types without spaces after directional operators, but the current code only matches patterns with required spaces. This causes chan<-int to be mis-parsed as an undirected channel, and <-chanint is not recognized by the guard condition at all.

Update the guard to check for <-chan (without space) and use removePrefix().trimStart() in the pattern matching to correctly extract element types regardless of whitespace:

Proposed fix
-            raw.startsWith("chan ") || raw.startsWith("chan<-") || raw.startsWith("<-chan ") -> {
+            raw.startsWith("chan ") || raw.startsWith("chan<-") || raw.startsWith("<-chan") -> {
                 val (direction, elementRaw) = when {
-                    raw.startsWith("<-chan ") -> "receive" to raw.drop(7)
-                    raw.startsWith("chan<- ") -> "send" to raw.drop(7)
-                    raw.startsWith("chan ") -> null to raw.drop(5)
+                    raw.startsWith("<-chan") -> "receive" to raw.removePrefix("<-chan").trimStart()
+                    raw.startsWith("chan<-") -> "send" to raw.removePrefix("chan<-").trimStart()
+                    raw.startsWith("chan ") -> null to raw.removePrefix("chan").trimStart()
                     else -> null to raw
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
raw.startsWith("chan ") || raw.startsWith("chan<-") || raw.startsWith("<-chan ") -> {
val (direction, elementRaw) = when {
raw.startsWith("<-chan ") -> "receive" to raw.drop(7)
raw.startsWith("chan<- ") -> "send" to raw.drop(7)
raw.startsWith("chan ") -> null to raw.drop(5)
else -> null to raw
}
CodeTypeRef(
raw = raw,
name = "chan",
kind = TypeRefKind.CHANNEL,
valueType = buildFromString(elementRaw),
channelDirection = direction
)
raw.startsWith("chan ") || raw.startsWith("chan<-") || raw.startsWith("<-chan") -> {
val (direction, elementRaw) = when {
raw.startsWith("<-chan") -> "receive" to raw.removePrefix("<-chan").trimStart()
raw.startsWith("chan<-") -> "send" to raw.removePrefix("chan<-").trimStart()
raw.startsWith("chan ") -> null to raw.removePrefix("chan").trimStart()
else -> null to raw
}
CodeTypeRef(
raw = raw,
name = "chan",
kind = TypeRefKind.CHANNEL,
valueType = buildFromString(elementRaw),
channelDirection = direction
)
🤖 Prompt for AI Agents
In `@chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt` around
lines 267 - 280, The channel parsing in GoTypeRefBuilder incorrectly requires
spaces for directional channels; update the guard to also detect
raw.startsWith("<-chan") (no space) and change the element extraction to use
removePrefix("<-chan"), removePrefix("chan<-"), and removePrefix("chan")
followed by trimStart() so elementRaw is correct regardless of whitespace; keep
the existing mapping of directions ("receive" for "<-chan", "send" for "chan<-",
null for plain "chan") and then pass the trimmed elementRaw into buildFromString
for CodeTypeRef(valueType) while setting channelDirection accordingly.

Comment on lines +314 to +325
raw.contains("[") && raw.endsWith("]") -> {
val bracketStart = raw.indexOf('[')
val name = raw.substring(0, bracketStart)
val argsRaw = raw.substring(bracketStart + 1, raw.length - 1)
val args = argsRaw.split(",").map { buildFromString(it.trim()) }
CodeTypeRef(
raw = raw,
name = name,
kind = TypeRefKind.GENERIC,
args = args
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "GoTypeRefBuilder.kt" -type f

Repository: phodal/chapi

Length of output: 122


🏁 Script executed:

cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '310,350p'

Repository: phodal/chapi

Length of output: 1461


🏁 Script executed:

cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | head -n 350 | tail -n +300

Repository: phodal/chapi

Length of output: 1962


🏁 Script executed:

# Search for other split(",") patterns in the file that might have similar issues
rg 'split\s*\(' ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt -n

Repository: phodal/chapi

Length of output: 140


🏁 Script executed:

# Look at the broader context of the buildFromString function
cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | head -n 320 | tail -n +290

Repository: phodal/chapi

Length of output: 1644


🏁 Script executed:

# Get the full buildFromString function to understand context
cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | head -n 331 | tail -n +270

Repository: phodal/chapi

Length of output: 3028


🏁 Script executed:

# Check if there are any tests for this function
find . -name "*GoTypeRefBuilder*Test*" -o -name "*Test*GoTypeRefBuilder*" | head -20

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Search for test files in the go AST directory
find ./chapi-ast-go -name "*Test.kt" -o -name "*test.kt" | head -20

Repository: phodal/chapi

Length of output: 397


🏁 Script executed:

# Check the generics test file
cat -n ./chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoGenericsTest.kt

Repository: phodal/chapi

Length of output: 7727


🏁 Script executed:

# Search for tests that use GoTypeRefBuilder or buildFromString
rg 'GoTypeRefBuilder|buildFromString' ./chapi-ast-go/src/test -A 3 -B 3

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Search for usages of buildFromString in the codebase
rg 'buildFromString' ./chapi-ast-go/src -A 2 -B 2

Repository: phodal/chapi

Length of output: 4325


🏁 Script executed:

# Check the entire file length to understand the "Also applies to: 333-344" reference
wc -l ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 126


🏁 Script executed:

# Search for test cases or usages with nested generics like Foo[Bar[Baz], Qux]
rg '\[\w+\[.*\].*\]' ./chapi-ast-go -A 2 -B 2

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let me verify the actual structure by examining how the function is meant to be used
# Check if there are real-world examples or tests with nested generic types
rg 'Map\[' ./chapi-ast-go/src/test -A 2 -B 2

Repository: phodal/chapi

Length of output: 591


🏁 Script executed:

# Check if there are other similar patterns or if the split issue repeats elsewhere
cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | grep -E 'split|substring' -n

Repository: phodal/chapi

Length of output: 673


🏁 Script executed:

# Let me look more carefully at the map parsing to see if it uses findMatchingBracket
cat -n ./chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt | sed -n '245,265p'

Repository: phodal/chapi

Length of output: 1030


Fix generic argument parsing to handle nested type parameters.

The split(",") approach at line 318 fails for nested generics (e.g., Foo[Bar[Baz], Qux]) because commas inside brackets are not tracked, leading to incorrect type trees. The codebase already uses bracket-depth tracking for map type parsing via findMatchingBracket()—apply the same pattern here.

Proposed fix
-                val args = argsRaw.split(",").map { buildFromString(it.trim()) }
+                val args = splitTypeArgs(argsRaw).map { buildFromString(it.trim()) }
                 CodeTypeRef(
                     raw = raw,
                     name = name,
                     kind = TypeRefKind.GENERIC,
                     args = args
                 )
             }
...
+    private fun splitTypeArgs(argsRaw: String): List<String> {
+        val result = mutableListOf<String>()
+        val buf = StringBuilder()
+        var depth = 0
+        for (ch in argsRaw) {
+            when (ch) {
+                '[' -> { depth++; buf.append(ch) }
+                ']' -> { depth--; buf.append(ch) }
+                ',' -> if (depth == 0) {
+                    result += buf.toString()
+                    buf.setLength(0)
+                } else {
+                    buf.append(ch)
+                }
+                else -> buf.append(ch)
+            }
+        }
+        if (buf.isNotEmpty()) result += buf.toString()
+        return result
+    }
🤖 Prompt for AI Agents
In `@chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoTypeRefBuilder.kt` around
lines 314 - 325, The generic-argument parser in GoTypeRefBuilder (inside the
branch that creates CodeTypeRef for TypeRefKind.GENERIC) incorrectly uses
argsRaw.split(",") which breaks on nested generics; change it to scan argsRaw
character-by-character tracking bracket depth (same approach used by
findMatchingBracket/map parsing) to split on commas only at depth 0, handle
empty args, trim each slice and call buildFromString for each part, and
construct the args list from those parsed segments instead of using split(",").

Comment on lines +612 to 624
// Build TypeRef from the field's typeType context
val fieldTypeCtx = (typeType as? JavaParser.TypeTypeContext)
val typeRef = fieldTypeCtx?.let { JavaTypeRefBuilder.build(it) }
?: JavaTypeRefBuilder.buildFromString(typeTypeText)

val field = CodeField(
typeTypeText,
typeValue,
typeKey,
Modifiers = listOf(),
Annotations = this.currentAnnotations
Annotations = this.currentAnnotations,
TypeRef = typeRef
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for FieldDeclarationContext definition and its methods
rg "class FieldDeclarationContext" -A 30 --type=kotlin -g "**/JavaParser*.kt"

Repository: phodal/chapi

Length of output: 204


🏁 Script executed:

# Search for the grammar file defining fieldDeclaration
find . -name "*.g4" -type f | head -10

Repository: phodal/chapi

Length of output: 514


🏁 Script executed:

# Look at the context of the problematic code to understand the parent hierarchy
rg -B 10 "declarators.parent.getChild" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt | head -30

Repository: phodal/chapi

Length of output: 454


🏁 Script executed:

# Search for the fieldDeclaration rule in the Java grammar
rg -A 10 "fieldDeclaration" chapi-ast-java/src/main/antlr/JavaParser.g4 | head -30

Repository: phodal/chapi

Length of output: 609


🏁 Script executed:

# Look for the parent class structure of FieldDeclarationContext
rg "typeType" chapi-ast-java/src/main/antlr/JavaParser.g4 | head -20

Repository: phodal/chapi

Length of output: 1280


🏁 Script executed:

# Check the exact grammar rule definition
grep -A 5 "fieldDeclaration" chapi-ast-java/src/main/antlr/JavaParser.g4

Repository: phodal/chapi

Length of output: 359


🏁 Script executed:

# Search for FieldDeclarationContext in the generated parser to see what methods it has
rg "class FieldDeclarationContext" -A 50 chapi-ast-java/src/main/antlr/JavaParser.kt | head -80

Repository: phodal/chapi

Length of output: 139


🏁 Script executed:

# Also search for typeType() method definition in FieldDeclarationContext
rg "fun typeType\(\)" chapi-ast-java/src/main/antlr/JavaParser.kt | head -10

Repository: phodal/chapi

Length of output: 139


🏁 Script executed:

# Find all generated parser files in the Java module
find chapi-ast-java -name "*Parser*.kt" -o -name "*Parser*.java" | head -20

Repository: phodal/chapi

Length of output: 120


🏁 Script executed:

# Check the structure of the chapi-ast-java directory
ls -la chapi-ast-java/src/main/

Repository: phodal/chapi

Length of output: 326


🏁 Script executed:

# List files in the antlr directory
ls -la chapi-ast-java/src/main/antlr/

Repository: phodal/chapi

Length of output: 296


🏁 Script executed:

# Check if there's a build directory with generated parser
find chapi-ast-java -name "*.java" -path "*JavaParser*" | head -10

Repository: phodal/chapi

Length of output: 120


🏁 Script executed:

# Search for FieldDeclarationContext in Java files
rg "class FieldDeclarationContext" chapi-ast-java/ -A 50 | head -100

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Search for the actual listener method signature to confirm ctx type
rg "fun enterFieldDeclaration" chapi-ast-java/ -A 5

Repository: phodal/chapi

Length of output: 733


🏁 Script executed:

# Check if there are any usages of ctx.typeType() pattern elsewhere in the listener
rg "\.typeType\(\)" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt | head -20

Repository: phodal/chapi

Length of output: 479


🏁 Script executed:

# Let's verify the exact line where ctx.typeType() is used in the enterFormalParameter method
rg -B 5 -A 5 "fun enterFormalParameter" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt | head -30

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Check if the grammar-generated context classes are actually available at runtime
# by looking for how JavaParser types are imported
rg "import.*JavaParser" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt

Repository: phodal/chapi

Length of output: 89


🏁 Script executed:

# Check what JavaParser import refers to - is it generated or custom?
ls -la chapi-ast-java/src/main/java/chapi/ast/antlr/

Repository: phodal/chapi

Length of output: 235


🏁 Script executed:

# Look for other enterX methods in JavaFullIdentListener to see the pattern
rg "override fun enter" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt | head -20

Repository: phodal/chapi

Length of output: 1255


🏁 Script executed:

# Search for direct ctx.typeType() usages and compare with the getChild(0) approach
rg "ctx\.typeType\(\)" chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt -B 3 -A 3 | head -40

Repository: phodal/chapi

Length of output: 693


Use direct context accessor ctx.typeType() instead of declarators.parent.getChild(0).

The grammar rule fieldDeclaration : typeType variableDeclarators ';' generates a direct typeType() accessor on FieldDeclarationContext. The current code unnecessarily traverses through declarators.parent.getChild(0), which requires defensive casting and a fallback path.

Other methods in the same listener (e.g., enterClassDeclaration, enterFormalParameter) correctly use ctx.typeType() directly. Replace the current approach with:

val typeType = ctx.typeType()
val typeRef = JavaTypeRefBuilder.build(typeType)

This eliminates the safe cast, fallback logic, and potential inconsistency in TypeRef structures.

🤖 Prompt for AI Agents
In `@chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt`
around lines 612 - 624, The fieldDeclaration handling in JavaFullIdentListener
currently fetches the type by navigating declarators.parent.getChild(0) and
defensively casting; instead use the direct ctx.typeType() accessor: replace
that traversal with val typeType = ctx.typeType() and build the TypeRef via
JavaTypeRefBuilder.build(typeType), removing the safe-cast and fallback to
buildFromString; update the CodeField construction to use the resulting TypeRef
and the original type text/value variables so TypeRef structures remain
consistent with other methods (e.g., enterClassDeclaration,
enterFormalParameter).

Comment on lines +212 to +227
// Special handling for common generic types
val kind = when (typeName.lowercase()) {
"array" -> TypeRefKind.ARRAY
"promise", "observable" -> TypeRefKind.GENERIC
"map", "set", "weakmap", "weakset" -> TypeRefKind.GENERIC
"partial", "required", "readonly", "pick", "omit", "record" -> TypeRefKind.MAPPED
else -> TypeRefKind.GENERIC
}

return CodeTypeRef(
raw = raw,
name = typeName,
kind = kind,
args = args,
valueType = args.firstOrNull() // For Array<T>, store T as valueType
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the target file and verify it exists
find . -name "TypeScriptTypeRefBuilder.kt" -type f

Repository: phodal/chapi

Length of output: 146


🏁 Script executed:

#!/bin/bash
# Get line count to understand file size
wc -l chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Read the specific lines mentioned in the review
sed -n '212,227p' chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 753


🏁 Script executed:

#!/bin/bash
# Find CodeTypeRef class definition
find . -name "*.kt" -type f | xargs grep -l "class CodeTypeRef" | head -5

Repository: phodal/chapi

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Search for T[] array handling in TypeScriptTypeRefBuilder
grep -n "T\[\]" chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Search for arrayDimensions usage in the codebase
rg "arrayDimensions" -t kt | head -20

Repository: phodal/chapi

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Read CodeTypeRef class definition
cat -n chapi-domain/src/main/kotlin/chapi/domain/core/CodeTypeRef.kt

Repository: phodal/chapi

Length of output: 10015


🏁 Script executed:

#!/bin/bash
# Search for arrayDimensions usage (without -t flag)
rg "arrayDimensions" --type-list | head -5

Repository: phodal/chapi

Length of output: 445


🏁 Script executed:

#!/bin/bash
# Search for arrayDimensions in kotlin files
rg "arrayDimensions" . -A 2 -B 2 | head -40

Repository: phodal/chapi

Length of output: 3687


🏁 Script executed:

#!/bin/bash
# Search for other TypeRefKind.ARRAY usages
rg "TypeRefKind\.ARRAY" -A 3 -B 3 | head -60

Repository: phodal/chapi

Length of output: 6488


🏁 Script executed:

#!/bin/bash
# View more context around the problematic code in TypeScriptTypeRefBuilder.kt
sed -n '200,240p' chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1660


Set arrayDimensions for Array<T> references.

Array<T> is mapped to TypeRefKind.ARRAY but leaves arrayDimensions at the default value of 0, inconsistent with T[] syntax which explicitly sets it to 1. This can cause downstream consumers to misinterpret single-dimensional arrays. Set arrayDimensions = 1 when kind == TypeRefKind.ARRAY.

Proposed fix
-            return CodeTypeRef(
-                raw = raw,
-                name = typeName,
-                kind = kind,
-                args = args,
-                valueType = args.firstOrNull() // For Array<T>, store T as valueType
-            )
+            val arrayDims = if (kind == TypeRefKind.ARRAY) 1 else 0
+            return CodeTypeRef(
+                raw = raw,
+                name = typeName,
+                kind = kind,
+                args = args,
+                valueType = args.firstOrNull(), // For Array<T>, store T as valueType
+                arrayDimensions = arrayDims
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Special handling for common generic types
val kind = when (typeName.lowercase()) {
"array" -> TypeRefKind.ARRAY
"promise", "observable" -> TypeRefKind.GENERIC
"map", "set", "weakmap", "weakset" -> TypeRefKind.GENERIC
"partial", "required", "readonly", "pick", "omit", "record" -> TypeRefKind.MAPPED
else -> TypeRefKind.GENERIC
}
return CodeTypeRef(
raw = raw,
name = typeName,
kind = kind,
args = args,
valueType = args.firstOrNull() // For Array<T>, store T as valueType
)
// Special handling for common generic types
val kind = when (typeName.lowercase()) {
"array" -> TypeRefKind.ARRAY
"promise", "observable" -> TypeRefKind.GENERIC
"map", "set", "weakmap", "weakset" -> TypeRefKind.GENERIC
"partial", "required", "readonly", "pick", "omit", "record" -> TypeRefKind.MAPPED
else -> TypeRefKind.GENERIC
}
val arrayDims = if (kind == TypeRefKind.ARRAY) 1 else 0
return CodeTypeRef(
raw = raw,
name = typeName,
kind = kind,
args = args,
valueType = args.firstOrNull(), // For Array<T>, store T as valueType
arrayDimensions = arrayDims
)
🤖 Prompt for AI Agents
In
`@chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt`
around lines 212 - 227, The CodeTypeRef returned for Array<T> currently sets
kind = TypeRefKind.ARRAY but leaves arrayDimensions at 0; update the return to
set arrayDimensions = 1 when kind == TypeRefKind.ARRAY (use the computed kind
variable) so that Array<T> and T[] are represented consistently; modify the
CodeTypeRef construction (fields raw, name/typeName, kind, args, valueType =
args.firstOrNull()) to also include arrayDimensions = if (kind ==
TypeRefKind.ARRAY) 1 else 0.

Comment on lines +256 to +270
// Parse parameter types
val paramTypes = ctx.parameterList()?.parameter()?.mapNotNull { param ->
param.requiredParameter()?.typeAnnotation()?.let { build(it) }
?: param.optionalParameter()?.typeAnnotation()?.let { build(it) }
} ?: listOf()

// Parse return type
val returnType = buildFromType(ctx.type_())

return CodeTypeRef(
raw = raw,
kind = TypeRefKind.FUNCTION,
parameterTypes = paramTypes,
returnType = returnType
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file structure and understand the context
find . -name "TypeScriptTypeRefBuilder.kt" -type f | head -5

Repository: phodal/chapi

Length of output: 146


🏁 Script executed:

# Look at the specific lines in question and surrounding context
cat -n chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt | sed -n '240,285p'

Repository: phodal/chapi

Length of output: 1578


🏁 Script executed:

# Check the TypeScript grammar file to understand parameterList structure
find . -name "*.g4" -type f | grep -i typescript

Repository: phodal/chapi

Length of output: 170


🏁 Script executed:

# Let's examine the TypeScript grammar to understand parameterList structure
grep -n "parameterList\|restParameter" chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4 | head -20

Repository: phodal/chapi

Length of output: 524


🏁 Script executed:

# Get more context around the parameterList rule
sed -n '/^parameterList/,/^[a-z]/p' chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4 | head -30

Repository: phodal/chapi

Length of output: 177


🏁 Script executed:

# Search for the parameterList rule definition more thoroughly
rg -A 10 "^\s*parameterList\s*:" chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's look at the restParameter rule definition
rg -A 5 "^restParameter" chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4

Repository: phodal/chapi

Length of output: 219


🏁 Script executed:

# Let's check if there's any documentation or other uses of restParameter in the codebase
rg "restParameter" chapi-ast-typescript/src/main/kotlin/ --type kotlin

Repository: phodal/chapi

Length of output: 969


🏁 Script executed:

# Also check the main parser grammar file more carefully to see how parameter() is defined
rg -A 3 "^parameter\s*:" chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's see the full parameter rule definition
rg -B 2 -A 15 "^parameter\s*:" chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's check what methods are available on the generated parser classes
# by looking at the imports and usage patterns in the existing code
rg "\.parameter\(\)|\.restParameter\(\)" chapi-ast-typescript/src/main/kotlin/ --type kotlin -B 2 -A 2 | head -40

Repository: phodal/chapi

Length of output: 2979


🏁 Script executed:

# Let's see the complete context of how restParameter is used in TypeScriptAstListener
grep -B 3 -A 3 "restParameter()" chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptAstListener.kt | head -30

Repository: phodal/chapi

Length of output: 658


🏁 Script executed:

# Let's examine the full implementation in TypeScriptAstListener to understand the pattern
sed -n '1,100p' chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptAstListener.kt | grep -A 20 "fun buildParameters"

Repository: phodal/chapi

Length of output: 966


🏁 Script executed:

# Get more context on buildRestParameter method to understand what it returns
rg -B 2 -A 10 "fun buildRestParameter" chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptAstListener.kt

Repository: phodal/chapi

Length of output: 541


🏁 Script executed:

# Check what type restParameter returns in the grammar and how typeAnnotation is used elsewhere
rg "typeAnnotation\(\)" chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt | head -10

Repository: phodal/chapi

Length of output: 212


🏁 Script executed:

# Let's verify the exact structure of how parameter() returns in the generated parser
# by checking if there are any integration tests or examples that show function type handling
rg -l "buildFunctionType\|FunctionType" chapi-ast-typescript/src --type kotlin

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's also verify what the generated parser exposes for parameterList
# by examining usage patterns with restParameter in the codebase
rg "parameterList\(\)" chapi-ast-typescript/src/main/kotlin/ --type kotlin -A 2 | head -30

Repository: phodal/chapi

Length of output: 1120


🏁 Script executed:

# Let's verify the complete grammar rules to ensure restParameter is indeed a direct child of parameterList
cat chapi-ast-typescript/src/main/antlr/TypeScriptParser.g4 | sed -n '206,235p'

Repository: phodal/chapi

Length of output: 659


🏁 Script executed:

# Let's check if there are any tests that exercise function types with rest parameters
find . -name "*.kt" -type f -path "*/test/*" | xargs grep -l "restParameter\|function.*\\.\\.\\..*=>" | head -5

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's understand the actual impact by seeing how function types are being used
rg "buildFunctionType\|FunctionTypeContext" chapi-ast-typescript/src/main/kotlin/ --type kotlin -B 3 -A 3 | head -40

Repository: phodal/chapi

Length of output: 38


Include rest parameter types in function type signatures.

The parameterList grammar rule allows restParameter as a direct child, either as the sole parameter or following regular parameters. The current code only processes parameter() elements, which omits rest parameters (...args: T[]). Add handling for ctx.parameterList()?.restParameter()?.typeAnnotation().

Reference: TypeScriptParser.g4 defines parameterList as:

parameterList
    : restParameter
    | parameter (',' parameter)* (',' restParameter)? ','?
🤖 Prompt for AI Agents
In
`@chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefBuilder.kt`
around lines 256 - 270, The current function building function type refs ignores
rest parameters because it only maps ctx.parameterList()?.parameter(); update
the logic in TypeScriptTypeRefBuilder (the block that computes paramTypes and
returnType and returns CodeTypeRef) to also check
ctx.parameterList()?.restParameter() for a typeAnnotation, call build(...) on
that typeAnnotation (same helper used for
parameter.requiredParameter()/optionalParameter()), and append that resulting
type to the parameterTypes list so rest parameters (both sole restParameter and
trailing restParameter) are included in the resulting CodeTypeRef; keep
returnType produced by buildFromType(ctx.type_()) unchanged.

Add TypeRefBuilder for Kotlin and C#:

Kotlin:
- Nullable types (T?)
- Generic types (List<T>, Map<K,V>)
- Function types ((A) -> B)
- Pair/Triple as tuples

C#:
- Nullable reference types (T?)
- Generic types (List<T>, Dictionary<K,V>)
- Array types (T[], T[,])
- Pointer types (T*)
- Tuple types ((T1, T2))

All existing tests pass.
Add TypeRefBuilder for Python and C++:

Python:
- Optional[T], Union[T1, T2] handling
- List[T], Dict[K,V], Set[T], Tuple[T...]
- Callable[[Args], Return] function types
- Python 3.10+ union syntax (T | U)
- Type annotations (PEP 484)

C++:
- Pointer types (T*) and references (T&, T&&)
- Template types (vector<T>, map<K,V>)
- Smart pointers (unique_ptr, shared_ptr)
- std::optional, std::pair, std::tuple
- const qualifiers

All existing tests pass.
@phodal phodal merged commit c5ab35a into master Jan 18, 2026
3 of 6 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt`:
- Around line 81-88: The TypeRef construction in CPPBasicIdentListener (when
building CodeProperty for parameters and fields) only uses the first
typeSpecifier() and loses cv-qualifiers and pointer/reference ops; fix by
passing the full combined type text to the builder: concatenate
declSpecifierSeq().text and declarator().text (or otherwise use the full
declSpecifierSeq plus declarator) and call
CPPTypeRefBuilder.buildFromString(fullTypeString) instead of
CPPTypeRefBuilder.build(typeSpec); alternatively add/use a CPPTypeRefBuilder
overload that accepts both declSpecifierSeq and declarator contexts and
preserves cv-qualifiers and pointer/reference information when constructing the
TypeRef.

In
`@chapi-ast-csharp/src/main/kotlin/chapi/ast/csharpast/CSharpTypeRefBuilder.kt`:
- Around line 38-45: The code miscomputes multi-dimensional array dimensions and
mishandles brackets when parsing types: change the arrayDims calculation (where
arrayRanks, isArray, arrayDims are computed) to derive dimensions from each
rank_specifier by counting commas inside each rank_specifier and adding 1 (e.g.
dims += commas + 1 per rank_specifier) instead of using rank_specifier count;
update buildFromString() to recognize rank_specifier patterns like "[,]" or
"[,,]" (treat any "[...]" with n commas as an array with n+1 dimensions) in
addition to "[]" so multi-dimensional arrays become ARRAY kinds rather than
SIMPLE; and fix splitTypeList() to ignore commas inside square brackets (and
angle brackets) by tracking nesting depth for '<'/'>' and '['/']' so type
arguments like "Dictionary<int[,], string>" are not split on inner commas.
Ensure you reference/modify the functions/variables buildFromString,
splitTypeList, and the arrayRanks/arrayDims logic in CSharpTypeRefBuilder.

In
`@chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt`:
- Around line 78-100: The union parsing branch in PythonTypeRefBuilder (the
when-case that checks raw.contains("|") && !raw.contains("[")) wrongly blocks
generics and parentheses and uses naive raw.split("|"); remove the
!raw.contains("[") guard, add a depth-aware helper (e.g., splitUnionList similar
to splitTypeList) that respects brackets/parentheses and splits only top-level
'|' tokens, then replace raw.split("|").map { buildFromString(...) } with
splitUnionList(raw).map { buildFromString(it) } and keep the existing Optional
detection/CodeTypeRef construction (references: PythonTypeRefBuilder,
splitTypeList, splitUnionList helper, buildFromString, CodeTypeRef).

In `@chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt`:
- Around line 46-57: The traitObjectTypeOneBound branch currently falls back to
buildTypePath when raw doesn't start with "dyn ", causing bare trait objects to
be classified as SIMPLE; change the logic in RustTypeRefBuilder (the
traitObjectTypeOneBound handling) so that whenever ctx.traitObjectTypeOneBound()
is present you treat it as a trait object (INTERFACE) — e.g. always call
buildTraitObjectOneBound(traitObj, raw) (or otherwise ensure
buildTraitObjectOneBound is used even when raw lacks "dyn ") instead of
delegating to buildTypePath; update any related tests to assert that bare trait
objects like Box<MyTrait> produce INTERFACE TypeRefKind.
🧹 Nitpick comments (11)
chapi-ast-java/src/test/kotlin/chapi/ast/javaast/JavaTypeRefTest.kt (1)

259-278: Consider using safe-call operators for consistency.

Lines 265, 275-277 access typeRef.args directly without null-safe operators (?.). While this is acceptable in tests (NPE would simply fail the test), using typeRef.args?.size would be consistent with assertions elsewhere in this file (e.g., lines 77-78, 98) and would provide clearer failure messages if args is unexpectedly null.

♻️ Optional consistency improvement
         `@Test`
         fun `should build generic type from string`() {
             val typeRef = JavaTypeRefBuilder.buildFromString("List<String>")
             
             assertEquals(TypeRefKind.GENERIC, typeRef.kind)
             assertEquals("List", typeRef.name)
-            assertEquals(1, typeRef.args.size)
-            assertEquals("String", typeRef.args.first().name)
+            assertEquals(1, typeRef.args?.size)
+            assertEquals("String", typeRef.args?.first()?.name)
         }

         `@Test`
         fun `should build nested generic type from string`() {
             val typeRef = JavaTypeRefBuilder.buildFromString("Map<String, List<Integer>>")
             
             assertEquals(TypeRefKind.GENERIC, typeRef.kind)
             assertEquals("Map", typeRef.name)
-            assertEquals(2, typeRef.args.size)
-            assertEquals("String", typeRef.args[0].name)
-            assertEquals("List", typeRef.args[1].name)
+            assertEquals(2, typeRef.args?.size)
+            assertEquals("String", typeRef.args?.get(0)?.name)
+            assertEquals("List", typeRef.args?.get(1)?.name)
         }
chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoTypeRefTest.kt (1)

240-254: Strengthen multiple-return assertions.
This test currently checks only count; asserting actual return types would prevent false positives.

♻️ Suggested enhancement
             val container = analyse(code)
             val func = container.DataStructures.flatMap { it.Functions }.first { it.Name == "divide" }
             
             assertEquals(2, func.MultipleReturns.size)
+            val firstReturn = func.MultipleReturns[0]
+            val secondReturn = func.MultipleReturns[1]
+            assertNotNull(firstReturn.TypeRef)
+            assertEquals("int", firstReturn.TypeRef?.name)
+            assertNotNull(secondReturn.TypeRef)
+            assertEquals("error", secondReturn.TypeRef?.name)
chapi-ast-typescript/src/test/kotlin/chapi/ast/typescriptast/TypeScriptTypeRefTest.kt (5)

53-56: Minor inconsistency in field lookup patterns.

Interface fields are found using TypeValue == "name" (line 53), while class fields later use TypeKey == "name" (line 214). If this reflects actual implementation differences between how TypeScript interfaces vs classes store field names, consider adding a brief comment to clarify this for future maintainers.


110-115: Consider adding assertions for Map key/value types.

The test verifies args.size is 2 but doesn't assert the actual key and value types. Based on patterns in other language tests (Java, Rust), consider adding explicit checks:

assertEquals("string", dataField.TypeRef?.args?.get(0)?.name)
assertEquals("number", dataField.TypeRef?.args?.get(1)?.name)

Alternatively, if keyType/valueType fields are populated (as in GoTypeRefTest), those could be asserted instead.


134-136: Assertions work but could be more idiomatic.

The contains checks are functional but verbose. A slight simplification using assertTrue with a clearer message:

assertTrue(unionTypes.orEmpty().containsAll(listOf("string", "number")))

Minor style preference - current approach is acceptable.


156-176: Consider adding multi-dimensional array test.

The current test covers single-dimensional bracket notation. TypeScript supports multi-dimensional arrays (string[][]) which would test arrayDimensions > 1. This would strengthen coverage for edge cases.


180-195: Consider adding heterogeneous tuple test.

The current tuple test uses identical element types ([number, number]), making line 194's assertion (all { it.name == "number" }) valid but not thoroughly testing element differentiation. A test with mixed types (e.g., [string, number, boolean]) would verify correct element-by-element type extraction.

chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt (1)

31-76: Normalize typing.-qualified names for Optional/Union/etc.

Line 31-76: patterns like typing.Optional[int] or typing.Union[...] won’t match the current Optional[/Union[ branches and will fall through to GENERIC/SIMPLE. Consider stripping common prefixes before pattern matching so fully-qualified typing names are handled consistently.

♻️ Suggested normalization pattern
-        val raw = typeStr.trim()
+        val raw = typeStr.trim()
+        val normalized = raw
+            .removePrefix("typing.")
+            .removePrefix("typing_extensions.")
@@
-        return when {
-            raw == "None" -> CodeTypeRef(raw = raw, name = "None", kind = TypeRefKind.SIMPLE)
+        return when {
+            normalized == "None" -> CodeTypeRef(raw = raw, name = "None", kind = TypeRefKind.SIMPLE)
@@
-            raw.startsWith("Optional[") && raw.endsWith("]") -> {
-                val innerRaw = raw.substring(9, raw.length - 1)
+            normalized.startsWith("Optional[") && normalized.endsWith("]") -> {
+                val innerRaw = normalized.substring(9, normalized.length - 1)
                 val inner = buildFromString(innerRaw)
                 CodeTypeRef(
                     raw = raw,

Apply the same normalized variable to Union[...], List[...], Dict[...], etc.

chapi-ast-rust/src/test/kotlin/chapi/ast/rustast/RustTypeRefTest.kt (1)

211-280: Consider a bare trait-object test case.

Line 211-280: the builder tests are solid, but there’s no coverage for Box<Trait> vs Box<dyn Trait>. Adding a test for both would validate how bare trait objects are classified and align with the builder’s traitObjectTypeOneBound handling.

chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPTypeRefBuilder.kt (1)

203-226: && branch is unreachable due to earlier & check.

Line 203-226: raw.endsWith("&") will also match &&, so the rvalue-reference branch never executes. If you want to distinguish later, reorder the checks or remove the dead branch for clarity.

♻️ Suggested reordering
-            // Reference type
-            raw.endsWith("&") -> {
+            // Rvalue reference
+            raw.endsWith("&&") -> {
+                val baseRaw = raw.dropLast(2).trim()
+                val base = buildFromString(baseRaw)
+                CodeTypeRef(
+                    raw = raw,
+                    name = base.name,
+                    kind = TypeRefKind.REFERENCE,
+                    isReference = true,
+                    valueType = base
+                )
+            }
+            // Reference type
+            raw.endsWith("&") -> {
                 val baseRaw = raw.dropLast(1).trim()
                 val base = buildFromString(baseRaw)
                 CodeTypeRef(
                     raw = raw,
                     name = base.name,
                     kind = TypeRefKind.REFERENCE,
                     isReference = true,
                     valueType = base
                 )
             }
-            // Rvalue reference
-            raw.endsWith("&&") -> {
-                val baseRaw = raw.dropLast(2).trim()
-                val base = buildFromString(baseRaw)
-                CodeTypeRef(
-                    raw = raw,
-                    name = base.name,
-                    kind = TypeRefKind.REFERENCE,
-                    isReference = true,
-                    valueType = base
-                )
-            }
chapi-ast-kotlin/src/main/kotlin/chapi/ast/kotlinast/KotlinTypeRefBuilder.kt (1)

221-234: String parsing doesn’t mirror Kotlin collection/tuple mapping.

Line 221-234: buildFromString maps all Name<Args> to GENERIC, while the AST path sets ARRAY/TUPLE/Map key/value metadata for common Kotlin types. This can yield inconsistent TypeRefKind depending on entry point. Consider reusing the same special-case mapping for Array, List, Set, Map, Pair, Triple, etc.

♻️ Example alignment for common Kotlin types
-            raw.contains("<") && raw.endsWith(">") -> {
+            raw.contains("<") && raw.endsWith(">") -> {
                 val bracketStart = raw.indexOf('<')
                 val name = raw.substring(0, bracketStart)
                 val argsRaw = raw.substring(bracketStart + 1, raw.length - 1)
                 val args = splitTypeList(argsRaw).map { buildFromString(it.trim()) }
-                
-                CodeTypeRef(
-                    raw = raw,
-                    name = name,
-                    kind = TypeRefKind.GENERIC,
-                    args = args
-                )
+                when (name.lowercase()) {
+                    "array" -> CodeTypeRef(raw = raw, name = name, kind = TypeRefKind.ARRAY, args = args, valueType = args.firstOrNull())
+                    "map", "mutablemap", "hashmap", "linkedhashmap" ->
+                        CodeTypeRef(raw = raw, name = name, kind = TypeRefKind.GENERIC, args = args, keyType = args.getOrNull(0), valueType = args.getOrNull(1))
+                    "pair", "triple" -> CodeTypeRef(raw = raw, name = name, kind = TypeRefKind.TUPLE, args = args, tupleElements = args)
+                    else -> CodeTypeRef(raw = raw, name = name, kind = TypeRefKind.GENERIC, args = args)
+                }
             }

Comment on lines +81 to +88
val typeSpec = it.declSpecifierSeq().declSpecifier().firstOrNull()?.typeSpecifier()
val type = typeSpec?.text
val name = it.declarator()?.text

CodeProperty(TypeValue = name ?: "", TypeType = type ?: "")
CodeProperty(
TypeValue = name ?: "",
TypeType = type ?: "",
TypeRef = CPPTypeRefBuilder.build(typeSpec)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and read the CPPBasicIdentListener.kt file
fd CPPBasicIdentListener.kt

Repository: phodal/chapi

Length of output: 127


🏁 Script executed:

# Read the file to examine lines 81-88 and 117-128
cat -n chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt | sed -n '75,135p'

Repository: phodal/chapi

Length of output: 2849


🏁 Script executed:

# Now let's find and examine the CPPTypeRefBuilder to understand how it processes typeSpec
fd CPPTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 123


🏁 Script executed:

# Read the CPPTypeRefBuilder to understand how it processes typeSpec
cat -n chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 14403


🏁 Script executed:

# Check if there are other overloads of build() method
rg "fun build" chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 521


🏁 Script executed:

# Search for all usages of CPPTypeRefBuilder to see if there are alternative patterns
rg "CPPTypeRefBuilder" chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/ -A 2

Repository: phodal/chapi

Length of output: 937


🏁 Script executed:

# Let's check what declSpecifierSeq actually contains and how declarator is structured
# Look at the grammar context to understand what information is available
rg "declSpecifierSeq|declarator" chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/ -B 2 -A 2 | head -40

Repository: phodal/chapi

Length of output: 3188


🏁 Script executed:

# Let's check how return type is handled to see the difference
cat -n chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt | sed -n '39,55p'

Repository: phodal/chapi

Length of output: 767


🏁 Script executed:

# Check if there's anywhere that combines declSpecifierSeq + declarator for type construction
cat -n chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt | sed -n '50,70p'

Repository: phodal/chapi

Length of output: 920


🏁 Script executed:

# Final confirmation: check if there's any special handling elsewhere for parameters
rg "buildParameters|memberDeclaratorList" chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/ -A 5 | head -50

Repository: phodal/chapi

Length of output: 2279


TypeRef construction is incomplete for parameters and fields, losing cv-qualifiers and pointer/reference information.

Lines 81-88 (parameters) and 117-128 (fields) extract only the first typeSpecifier() from declSpecifierSeq() and pass it alone to CPPTypeRefBuilder.build(typeSpec). This discards:

  • Other cv-qualifiers and specifiers in declSpecifierSeq()
  • Pointer/reference operators (*, &, &&) from the declarator()

As a result, TypeRef loses const, volatile, and pointer/reference information. For example, const std::string& becomes const or std::string, missing the reference and any qualifiers outside the first type specifier.

The builder has buildFromString() that handles these cases, but it's never called for parameters or fields. Consider either:

  • Passing the full combined type string (declSpecifierSeq().text + declarator().text) to buildFromString(), or
  • Adding an overload to CPPTypeRefBuilder.build() that accepts both declSpecifierSeq and declarator contexts
🤖 Prompt for AI Agents
In `@chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt`
around lines 81 - 88, The TypeRef construction in CPPBasicIdentListener (when
building CodeProperty for parameters and fields) only uses the first
typeSpecifier() and loses cv-qualifiers and pointer/reference ops; fix by
passing the full combined type text to the builder: concatenate
declSpecifierSeq().text and declarator().text (or otherwise use the full
declSpecifierSeq plus declarator) and call
CPPTypeRefBuilder.buildFromString(fullTypeString) instead of
CPPTypeRefBuilder.build(typeSpec); alternatively add/use a CPPTypeRefBuilder
overload that accepts both declSpecifierSeq and declarator contexts and
preserves cv-qualifiers and pointer/reference information when constructing the
TypeRef.

Comment on lines +38 to +45
// Check for array rank specifiers
val arrayRanks = parentCtx.rank_specifier()
val isArray = arrayRanks?.isNotEmpty() == true
val arrayDims = arrayRanks?.size ?: 0

// Check for pointer types (*)
val pointerDepth = parentCtx.STAR()?.size ?: 0

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate the file and check its size
find . -name "CSharpTypeRefBuilder.kt" -type f

Repository: phodal/chapi

Length of output: 134


🏁 Script executed:

#!/bin/bash
# Read the CSharpTypeRefBuilder.kt file to understand the full context
cat -n chapi-ast-csharp/src/main/kotlin/chapi/ast/csharpast/CSharpTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 13494


🏁 Script executed:

#!/bin/bash
# Also check if there's a C# grammar file in the project
find . -name "*.g4" -type f | head -20

Repository: phodal/chapi

Length of output: 999


🏁 Script executed:

#!/bin/bash
# Look for test files related to CSharpTypeRef
find . -path "*/test*" -name "*CSharp*TypeRef*" -o -path "*/test*" -name "*csharp*" -type f | grep -i test | head -20

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Find and examine the rank_specifier in CSharp grammar
rg -n "rank_specifier" chapi-ast-csharp/src/main/antlr/CSharpParser.g4 -A 3

Repository: phodal/chapi

Length of output: 987


🏁 Script executed:

#!/bin/bash
# Look for test files
find . -path "*/test*" -name "*CSharp*" -type f | head -30

Repository: phodal/chapi

Length of output: 221


🏁 Script executed:

#!/bin/bash
# Search for any existing tests related to array types
rg -i "array|rank|\\[" --type kotlin chapi-ast-csharp/src/test -l 2>/dev/null | head -10

Repository: phodal/chapi

Length of output: 217


🏁 Script executed:

#!/bin/bash
# Check if there are examples of multi-dimensional arrays being parsed
rg "\\[,+\\]" . --type kotlin --type java -B 2 -A 2 2>/dev/null | head -50

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Examine the test files
cat -n chapi-ast-csharp/src/test/kotlin/chapi/ast/csharpast/CSharpAstListenerTest.kt | head -100

Repository: phodal/chapi

Length of output: 3300


🏁 Script executed:

#!/bin/bash
# Check for multi-dimensional array test cases
rg "int\[" chapi-ast-csharp/src/test -B 2 -A 2

Repository: phodal/chapi

Length of output: 4481


🏁 Script executed:

#!/bin/bash
# Check if there are any test cases with generics containing arrays
rg "Dictionary|List" chapi-ast-csharp/src/test -B 1 -A 1

Repository: phodal/chapi

Length of output: 14814


🏁 Script executed:

#!/bin/bash
# Check where buildFromString is used in the codebase
rg -n "buildFromString" chapi-ast-csharp/src --type kotlin -B 2 -A 2

Repository: phodal/chapi

Length of output: 3174


🏁 Script executed:

#!/bin/bash
# Verify the exact behavior - see if there are other references to array handling
rg -n "arrayDimensions" chapi-ast-csharp/src --type kotlin -B 2 -A 2

Repository: phodal/chapi

Length of output: 1235


🏁 Script executed:

#!/bin/bash
# Look for usages of the TYPE system to understand the impact
rg -n "CodeTypeRef" chapi-ast-csharp/src/main/kotlin --type kotlin -l | head -10

Repository: phodal/chapi

Length of output: 132


🏁 Script executed:

#!/bin/bash
# Check for any test cases specifically testing array types
rg -n "\[\]|\[," chapi-ast-csharp/src/test/kotlin --type kotlin -B 2 -A 2

Repository: phodal/chapi

Length of output: 2006


🏁 Script executed:

#!/bin/bash
# Look for the specific test cases mentioned in the grammar file
grep -n "int\[\|Dictionary" chapi-ast-csharp/src/test/resources/grammar/AllInOneNoPreprocessor.cs | head -15

Repository: phodal/chapi

Length of output: 1038


🏁 Script executed:

#!/bin/bash
# Verify the exact syntax from grammar examples
grep -n "int\[,\|int\[\[" chapi-ast-csharp/src/test/resources/grammar/AllInOneNoPreprocessor.cs

Repository: phodal/chapi

Length of output: 156


Fix multi-dimensional array and bracket handling in type parsing.

The current implementation has three critical bugs:

  1. Line 41: arrayDims = arrayRanks?.size counts rank_specifier objects, not dimensions. According to the C# grammar (rank_specifier : '[' ','* ']'), int[, ,] has one rank_specifier with 2 commas representing 3 dimensions, but this code would set arrayDims=1.

  2. Lines 242-253: buildFromString() only recognizes [] patterns and ignores multi-dimensional notation like [,] or [,,]. The test file contains int[, ,] cube which would fail to parse as an array type and incorrectly fall through to SIMPLE.

  3. Lines 299-318: splitTypeList() does not track square brackets, so Dictionary<int[,], string> will be split at the comma inside [,], producing incorrect type arguments.

Suggested fixes
-        val arrayDims = arrayRanks?.size ?: 0
+        val arrayDims = arrayRanks?.sumOf { rank ->
+            rank.text.count { it == ',' } + 1
+        } ?: 0
-            raw.endsWith("[]") -> {
-                val dims = raw.count { it == '[' }
-                val baseRaw = raw.replace("[]", "")
-                val base = buildFromString(baseRaw)
-                CodeTypeRef(
-                    raw = raw,
-                    name = base.name,
-                    kind = TypeRefKind.ARRAY,
-                    valueType = base,
-                    arrayDimensions = dims
-                )
-            }
+            Regex("(\\[[,]*\\])+$").containsMatchIn(raw) -> {
+                val suffix = Regex("(\\[[,]*\\])+$").find(raw)!!.value
+                val baseRaw = raw.dropLast(suffix.length)
+                val dims = Regex("\\[[,]*\\]").findAll(suffix)
+                    .sumOf { it.value.count { c -> c == ',' } + 1 }
+                val base = buildFromString(baseRaw)
+                CodeTypeRef(
+                    raw = raw,
+                    name = base.name,
+                    kind = TypeRefKind.ARRAY,
+                    valueType = base,
+                    arrayDimensions = dims,
+                    args = base.args
+                )
+            }
                 '<', '(' -> {
                     depth++
                     current.append(char)
                 }
-                '>', ')' -> {
+                '>', ')', ']' -> {
                     depth--
                     current.append(char)
                 }
+                '[' -> {
+                    depth++
+                    current.append(char)
+                }
🤖 Prompt for AI Agents
In `@chapi-ast-csharp/src/main/kotlin/chapi/ast/csharpast/CSharpTypeRefBuilder.kt`
around lines 38 - 45, The code miscomputes multi-dimensional array dimensions
and mishandles brackets when parsing types: change the arrayDims calculation
(where arrayRanks, isArray, arrayDims are computed) to derive dimensions from
each rank_specifier by counting commas inside each rank_specifier and adding 1
(e.g. dims += commas + 1 per rank_specifier) instead of using rank_specifier
count; update buildFromString() to recognize rank_specifier patterns like "[,]"
or "[,,]" (treat any "[...]" with n commas as an array with n+1 dimensions) in
addition to "[]" so multi-dimensional arrays become ARRAY kinds rather than
SIMPLE; and fix splitTypeList() to ignore commas inside square brackets (and
angle brackets) by tracking nesting depth for '<'/'>' and '['/']' so type
arguments like "Dictionary<int[,], string>" are not split on inner commas.
Ensure you reference/modify the functions/variables buildFromString,
splitTypeList, and the arrayRanks/arrayDims logic in CSharpTypeRefBuilder.

Comment on lines +78 to +100
// T | U - Python 3.10+ union syntax
raw.contains("|") && !raw.contains("[") -> {
val elements = raw.split("|").map { buildFromString(it.trim()) }
val hasNone = elements.any { it.name == "None" }
val nonNoneElements = elements.filter { it.name != "None" }

if (hasNone && nonNoneElements.size == 1) {
CodeTypeRef(
raw = raw,
name = "Optional",
kind = TypeRefKind.NULLABLE,
nullable = true,
valueType = nonNoneElements.first(),
union = elements
)
} else {
CodeTypeRef(
raw = raw,
kind = TypeRefKind.UNION,
union = elements
)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files "chapi-ast-python" | head -20

Repository: phodal/chapi

Length of output: 168


🏁 Script executed:

find . -name "PythonTypeRefBuilder.kt" -type f

Repository: phodal/chapi

Length of output: 168


🏁 Script executed:

cat -n chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt | sed -n '70,110p'

Repository: phodal/chapi

Length of output: 2019


🏁 Script executed:

cat -n chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt | sed -n '1,50p'

Repository: phodal/chapi

Length of output: 2005


🏁 Script executed:

cat -n chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt | sed -n '48,77p'

Repository: phodal/chapi

Length of output: 1522


🏁 Script executed:

wc -l chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 136


🏁 Script executed:

rg -n "split.*\|" chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/ -A 3 -B 1

Repository: phodal/chapi

Length of output: 751


🏁 Script executed:

find . -path "*test*" -name "*PythonTypeRefBuilder*" -o -path "*test*" -name "*python*" -type f | grep -i test | head -10

Repository: phodal/chapi

Length of output: 123


🏁 Script executed:

cat -n chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt | sed -n '200,297p'

Repository: phodal/chapi

Length of output: 3851


🏁 Script executed:

rg -n "fun splitTypeList" chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt -A 15

Repository: phodal/chapi

Length of output: 605


🏁 Script executed:

find . -path "*test*" -name "*TypeRef*" -type f | head -10

Repository: phodal/chapi

Length of output: 491


🏁 Script executed:

find . -path "*test*" -name "*PythonTypeRef*" -type f

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

rg -n "3\.10.*union\||.*\|.*None" chapi-ast-python/ -i

Repository: phodal/chapi

Length of output: 186


🏁 Script executed:

cat -n chapi-ast-python/src/test/kotlin/chapi/ast/pythonast/ 2>/dev/null | head -50

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

find ./chapi-ast-python -path "*test*" -type f -name "*.kt"

Repository: phodal/chapi

Length of output: 218


🏁 Script executed:

rg -n "PythonTypeRefBuilder\|buildFromString" chapi-ast-python/src/test/kotlin/chapi/ast/pythonast/ -A 3 -B 1

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

cat -n chapi-ast-python/src/test/kotlin/chapi/ast/pythonast/PythonAnalyserTest.kt | head -100

Repository: phodal/chapi

Length of output: 4411


🏁 Script executed:

python3 << 'EOF'
# Test case: list[int] | None with naive split
raw = "list[int] | None"
naive_split = raw.split("|")
print(f"Input: {raw}")
print(f"Naive split: {naive_split}")
print(f"Issue: '[' is present, so !raw.contains('[') guard blocks this type annotation")
print()

# What should happen with depth-aware split
def split_union(s):
    result = []
    depth = 0
    current = []
    for char in s:
        if char in '([':
            depth += 1
            current.append(char)
        elif char in ')]':
            depth -= 1
            current.append(char)
        elif char == '|' and depth == 0:
            result.append(''.join(current).strip())
            current = []
        else:
            current.append(char)
    if current:
        result.append(''.join(current).strip())
    return result

print(f"Depth-aware split: {split_union(raw)}")
EOF

Repository: phodal/chapi

Length of output: 142


| union parsing incorrectly blocks generic and parenthesized union types.

Lines 78–100: The guard raw.contains("|") && !raw.contains("[") prevents parsing valid Python 3.10+ union syntax with nested generics. For example, list[int] | None, dict[str, int] | None, and tuple[int, ...] | None are rejected. Additionally, naive raw.split("|") would incorrectly split pipes inside brackets (e.g., dict[str | int, float]).

Replace the guard and implement depth-aware splitting, following the existing splitTypeList() pattern already present in this file (lines 248–280):

Suggested fix
-            // T | U - Python 3.10+ union syntax
-            raw.contains("|") && !raw.contains("[") -> {
-                val elements = raw.split("|").map { buildFromString(it.trim()) }
+            // T | U - Python 3.10+ union syntax
+            raw.contains("|") -> {
+                val elements = splitUnionList(raw).map { buildFromString(it.trim()) }
                 val hasNone = elements.any { it.name == "None" }
                 val nonNoneElements = elements.filter { it.name != "None" }

Add the helper (mirrors splitTypeList):

    private fun splitUnionList(str: String): List<String> {
        val result = mutableListOf<String>()
        var depth = 0
        var current = StringBuilder()

        for (char in str) {
            when (char) {
                '[', '(' -> { depth++; current.append(char) }
                ']', ')' -> { depth--; current.append(char) }
                '|' -> {
                    if (depth == 0) {
                        result.add(current.toString().trim())
                        current = StringBuilder()
                    } else {
                        current.append(char)
                    }
                }
                else -> current.append(char)
            }
        }
        if (current.isNotEmpty()) result.add(current.toString().trim())
        return result
    }
🤖 Prompt for AI Agents
In `@chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonTypeRefBuilder.kt`
around lines 78 - 100, The union parsing branch in PythonTypeRefBuilder (the
when-case that checks raw.contains("|") && !raw.contains("[")) wrongly blocks
generics and parentheses and uses naive raw.split("|"); remove the
!raw.contains("[") guard, add a depth-aware helper (e.g., splitUnionList similar
to splitTypeList) that respects brackets/parentheses and splits only top-level
'|' tokens, then replace raw.split("|").map { buildFromString(...) } with
splitUnionList(raw).map { buildFromString(it) } and keep the existing Optional
detection/CodeTypeRef construction (references: PythonTypeRefBuilder,
splitTypeList, splitUnionList helper, buildFromString, CodeTypeRef).

Comment on lines +46 to +57
// TraitObjectTypeOneBound - this is often how simple types are parsed!
// 'dyn'? traitBound, where traitBound eventually contains typePath
ctx.traitObjectTypeOneBound()?.let { traitObj ->
// If it has explicit 'dyn' keyword, treat as trait object
if (raw.startsWith("dyn ")) {
return buildTraitObjectOneBound(traitObj, raw)
}
// Otherwise, extract the typePath from traitBound
val typePath = traitObj.traitBound()?.typePath()
if (typePath != null) {
return buildTypePath(typePath, raw)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find the file and understand its location
find . -name "RustTypeRefBuilder.kt" -type f

Repository: phodal/chapi

Length of output: 128


🏁 Script executed:

# Let's examine the context around lines 46-57 in the file
cat -n chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt | head -100

Repository: phodal/chapi

Length of output: 4381


🏁 Script executed:

# Search for the buildTypePath and buildTraitObjectOneBound implementations
rg -n "fun buildTypePath|fun buildTraitObjectOneBound" chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 270


🏁 Script executed:

# Let's look at the complete file to understand the full context
wc -l chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 130


🏁 Script executed:

# Let's look at buildTraitObjectOneBound implementation
sed -n '323,360p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1525


🏁 Script executed:

# Let's look at buildTypePath implementation
sed -n '97,150p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 2197


🏁 Script executed:

# Search for tests related to trait objects
find . -path "*test*" -name "*RustTypeRef*" -o -path "*test*" -name "*Rust*Test*" | grep -i rust

Repository: phodal/chapi

Length of output: 276


🏁 Script executed:

# Let's search for test files in the Rust AST module
find . -path "*test*" -type f -name "*.kt" | grep -i rust | head -20

Repository: phodal/chapi

Length of output: 276


🏁 Script executed:

# Let's examine the RustTypeRefTest.kt file for trait object tests
cat -n ./chapi-ast-rust/src/test/kotlin/chapi/ast/rustast/RustTypeRefTest.kt

Repository: phodal/chapi

Length of output: 12210


🏁 Script executed:

# Let's also check the TypeRefKind to understand the classification system
find . -name "TypeRefKind.kt" -o -name "CodeTypeRef.kt" | head -5

Repository: phodal/chapi

Length of output: 119


🏁 Script executed:

# Search for how trait objects are tested
rg -A 5 -B 2 "traitObject|dyn " ./chapi-ast-rust/src/test/kotlin/chapi/ast/rustast/RustTypeRefTest.kt

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's check if there are any trait object related tests or usages in the codebase
rg -i "traitobject|trait\sobject" --type kt

Repository: phodal/chapi

Length of output: 82


🏁 Script executed:

# Let's look at the Rust grammar file to understand how traitObjectTypeOneBound is defined
find . -name "*.g4" | head -5

Repository: phodal/chapi

Length of output: 274


🏁 Script executed:

# Let's search for any existing comments or documentation about bare trait objects
rg -i "bare.*trait|dyn.*trait" --type kt

Repository: phodal/chapi

Length of output: 82


🏁 Script executed:

# Search for trait object related comments or code
rg -i "traitobject|trait object" chapi-ast-rust/

Repository: phodal/chapi

Length of output: 1809


🏁 Script executed:

# Find the Rust grammar file
find . -path "*rust*" -name "*.g4"

Repository: phodal/chapi

Length of output: 146


🏁 Script executed:

# Look at the Rust grammar to understand traitObjectTypeOneBound
find . -path "*rust*" -name "*.g4" -exec cat {} \;

Repository: phodal/chapi

Length of output: 32690


🏁 Script executed:

# Let's search for how bare trait objects are used in tests or examples
rg -B 3 -A 3 "traitObjectTypeOneBound|Trait>" chapi-ast-rust/src/test/

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's check the buildTraitObjectType to compare with buildTraitObjectOneBound
sed -n '316,332p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 596


🏁 Script executed:

# Let's look at the complete file structure to understand type classification better
sed -n '1,95p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt | tail -50

Repository: phodal/chapi

Length of output: 1937


🏁 Script executed:

# Let's look at the buildTraitObjectType to see the difference
sed -n '310,325p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 672


🏁 Script executed:

# Search for any actual usage of bare trait objects in test code or examples
find . -path "*test*" -name "*.rs" -o -path "*example*" -name "*.rs" | head -10

Repository: phodal/chapi

Length of output: 678


🏁 Script executed:

# Let's check if there are any Rust test files showing trait object usage
find . -type f -name "*.rs" | head -20

Repository: phodal/chapi

Length of output: 1088


🏁 Script executed:

# Let's look at the buildTypePath return to see what kind it returns for trait types
sed -n '97,145p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1987


🏁 Script executed:

# Let's check if there are any test cases with bare trait objects (without dyn)
rg -B 2 -A 2 "Box<[A-Z]|trait\s+\w+:" chapi-ast-rust/src/test/resources/grammar/

Repository: phodal/chapi

Length of output: 5627


🏁 Script executed:

# Let's create a simple test to verify the behavior
cat > /tmp/test_trait.rs << 'EOF'
struct Container {
    callback: Box<Trait>,
    handler: dyn Handler,
}

trait Trait {
    fn execute(&self);
}

trait Handler {
    fn handle(&self);
}
EOF

cat /tmp/test_trait.rs

Repository: phodal/chapi

Length of output: 209


🏁 Script executed:

# Search for any discussion of bare trait objects in comments
rg -i "bare.*trait|trait.*object" chapi-ast-rust/src/main/kotlin/

Repository: phodal/chapi

Length of output: 1541


🏁 Script executed:

# Let's search for actual usage patterns with Box and traits in the test resources
rg "Box<" chapi-ast-rust/src/test/resources/grammar/ -A 1 -B 1 | head -30

Repository: phodal/chapi

Length of output: 2280


🏁 Script executed:

# Check the complete buildTypePath to see what all cases return SIMPLE
sed -n '97,180p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt | grep -A 1 "TypeRefKind.SIMPLE"

Repository: phodal/chapi

Length of output: 197


🏁 Script executed:

# Let's verify how buildTypePath handles trait types by checking what it returns
sed -n '97,175p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 2985


🏁 Script executed:

# Search for real examples of bare trait objects in the test resources
rg "Box<[A-Z][a-zA-Z0-9]*" chapi-ast-rust/src/test/resources/grammar/intellijrust_test_allinone.rs

Repository: phodal/chapi

Length of output: 318


🏁 Script executed:

# Let's trace through what happens with Box<A + Copy>
# First check if it matches traitObjectTypeOneBound
# The content inside Box<...> would be parsed as a type_
# Let's look at what the actual flow would be for the bare trait object case

# Let's read the full buildTypePath to see what it returns
sed -n '97,180p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt | tail -40

Repository: phodal/chapi

Length of output: 1379


🏁 Script executed:

# Let's verify the exact behavior by understanding what buildGenericArgs does with trait bounds
sed -n '200,250p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1673


🏁 Script executed:

# Let's check if there's any handling of trait bounds within generic arguments
rg -A 10 "fun buildGenericArgs" chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 518


🏁 Script executed:

# Let's understand the exact type_ parsing flow for Box<A + Copy>
# by checking what happens when buildGenericArgs calls build() on the inner type

# Let's create a test to verify this behavior
# First, let's check if the test file has actual Box<Trait> without dyn examples
cat chapi-ast-rust/src/test/resources/grammar/intellijrust_test_allinone.rs | grep -A 2 "type Sum = Box"

Repository: phodal/chapi

Length of output: 117


🏁 Script executed:

# Let's trace what happens with Box<A + Copy>
# Box is generic, so it calls buildGenericArgs which calls build() on each type argument
# The inner type A + Copy would match traitObjectTypeOneBound

# Let's check the exact line where it extracts typePath
sed -n '46,60p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 716


🏁 Script executed:

# Now let's verify the complete flow - what does buildTypePath return for a simple trait name?
# For "A", buildTypePath would return SIMPLE (since it has no generic args)
sed -n '156,175p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 639


🏁 Script executed:

# Let's verify the exact behavior with a complete trace
# When we have Box<A + Copy>, let's see what the raw parameter value would be

# Let's check buildGenericArgs to see what raw value is passed to build()
sed -n '253,275p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 886


🏁 Script executed:

# Let me verify the exact buildGenericArgs implementation to see what raw value is passed
sed -n '253,280p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1089


🏁 Script executed:

# Let's check if buildGenericArgs passes the original text or reconstructed text
rg -A 15 "fun buildGenericArgs" chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 556


🏁 Script executed:

# Let's verify by checking what happens in the actual grammar
# when Box<Trait> is parsed

# First, let's understand: when buildGenericArgs calls build(typeCtx),
# what is typeCtx.text for the Trait part?

# Let's look at a concrete scenario - if we have Box<MyTrait>:
# 1. buildTypePath gets called for Box
# 2. It finds genericArgs, calls buildGenericArgs
# 3. buildGenericArgs calls build(typeCtx) where typeCtx is the context for MyTrait
# 4. In build(), typeCtx.text would be "MyTrait"
# 5. It matches traitObjectTypeOneBound (since it's a single bound without +)
# 6. raw = "MyTrait", which doesn't start with "dyn "
# 7. Falls through to buildTypePath(typePath, "MyTrait")
# 8. Returns SIMPLE

# Let's verify that Box is indeed handled specially
sed -n '120,145p' chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt

Repository: phodal/chapi

Length of output: 1066


🏁 Script executed:

# Let's check what the expected behavior should be per Rust documentation
# A bare trait object without dyn is deprecated but still valid in older Rust editions
# Let's check if there are any tests that validate this specific scenario

cat chapi-ast-rust/src/test/kotlin/chapi/ast/rustast/RustTypeRefTest.kt | grep -i "trait" | head -20

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's verify by checking if there are any existing tests that would catch this issue
# and understand the intended behavior

# Check for any test involving Box and traits
rg -B 2 -A 5 "Box.*Trait|traitObject" chapi-ast-rust/src/test/kotlin/

Repository: phodal/chapi

Length of output: 38


🏁 Script executed:

# Let's also check if there's any comment or documentation about this behavior
rg -i "bare.*trait|deprecated.*trait|trait.*object" chapi-ast-rust/src/main/kotlin/ -B 2 -A 2

Repository: phodal/chapi

Length of output: 4439


Bare trait objects without the dyn keyword are misclassified as SIMPLE instead of INTERFACE.

Lines 46-57: The code falls through to buildTypePath when raw doesn't start with "dyn ", which returns TypeRefKind.SIMPLE. Since the grammar rule traitObjectTypeOneBound makes dyn optional ('dyn'? traitBound), bare trait objects like Box<MyTrait> will be classified as SIMPLE rather than INTERFACE. This affects deprecated Rust syntax patterns from earlier editions. Consider always treating matched traitObjectTypeOneBound contexts as INTERFACE regardless of the dyn keyword presence, or add a test case to validate the desired behavior for bare trait objects.

🤖 Prompt for AI Agents
In `@chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustTypeRefBuilder.kt`
around lines 46 - 57, The traitObjectTypeOneBound branch currently falls back to
buildTypePath when raw doesn't start with "dyn ", causing bare trait objects to
be classified as SIMPLE; change the logic in RustTypeRefBuilder (the
traitObjectTypeOneBound handling) so that whenever ctx.traitObjectTypeOneBound()
is present you treat it as a trait object (INTERFACE) — e.g. always call
buildTraitObjectOneBound(traitObj, raw) (or otherwise ensure
buildTraitObjectOneBound is used even when raw lacks "dyn ") instead of
delegating to buildTypePath; update any related tests to assert that bare trait
objects like Box<MyTrait> produce INTERFACE TypeRefKind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant