Conversation
Add helper to read Maven settings.xml for Sonatype credentials and configure Nexus publishing with new Central Portal endpoints. Also ensure kotlinSourcesJar depends on ANTLR tasks.
Replace direct key/password configuration with GPG agent for more secure signing. Fallback to traditional method if GPG agent is not configured.
…larity This commit addresses the semantic mixing issue where `PackageName` had different meanings across languages (package/namespace/module/config table). Changes: - Add `ContainerKind` enum to distinguish container types: - SOURCE_FILE, PACKAGE, NAMESPACE, MODULE, CRATE, CONFIG, BUILD_SCRIPT, IDL - Add new structured fields to `CodeContainer`: - `Language`: the programming language (java, typescript, rust, etc.) - `Kind`: semantic container type - `DeclaredPackage`: explicitly declared package/namespace in source - `ResolvedModulePath`: path-derived module name (for Rust, Python, TS) - `NamespacePath`: namespace hierarchy as list (for C#, C++, TS) - Update all language parsers to fill the new fields: - Java/Kotlin/Go/Scala: DeclaredPackage from package declaration - Rust/Python/TypeScript: ResolvedModulePath from file path - C#/C++/TypeScript: NamespacePath for namespace hierarchies - Toml: Kind=CONFIG - CMake: Kind=BUILD_SCRIPT - Protobuf/Thrift: Kind=IDL Backward compatibility: - Legacy `PackageName` field is preserved and still populated - New fields have sensible defaults (empty strings, UNKNOWN kind) Closes #41
|
Caution Review failedThe pull request is closed. Note Other AI code review bot(s) detectedCodeRabbit 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. 📝 WalkthroughWalkthroughThis PR extends the core domain model with structured container, import, and export metadata, updates many language listeners to populate those fields, and revises build/publishing to use OSSRH Central Portal endpoints with Maven settings credential parsing, GPG-agent signing support, and longer Nexus timeouts. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR introduces structured container semantics to improve cross-language code analysis by disambiguating the overloaded PackageName field. It adds a ContainerKind enum and five new fields (Language, Kind, DeclaredPackage, ResolvedModulePath, NamespacePath) to the CodeContainer class, enabling clearer semantic distinction between packages, namespaces, and modules across 14 languages.
Changes:
- Added
ContainerKindenum with 9 semantic container types (SOURCE_FILE, PACKAGE, NAMESPACE, MODULE, CRATE, CONFIG, BUILD_SCRIPT, IDL, UNKNOWN) - Extended
CodeContainerwith structured fields for language-specific semantics while maintaining backward compatibility - Updated all 14 language parsers to populate the new fields appropriately
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| chapi-domain/src/main/kotlin/chapi/domain/core/CodeContainer.kt | Added ContainerKind enum and new structured fields with comprehensive documentation |
| chapi-domain/src/test/kotlin/chapi/domain/core/CodeContainerTest.kt | Added unit tests demonstrating usage of new fields across different languages |
| chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt | Updated to set Language="java", Kind=SOURCE_FILE, and DeclaredPackage |
| chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaBasicIdentListener.kt | Updated to set Language="java", Kind=SOURCE_FILE, and DeclaredPackage |
| chapi-ast-kotlin/src/main/kotlin/chapi/ast/kotlinast/KotlinBasicIdentListener.kt | Updated to set Language="kotlin", Kind=SOURCE_FILE, and DeclaredPackage |
| chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt | Updated to set Language="go", Kind=SOURCE_FILE, and DeclaredPackage |
| chapi-ast-scala/src/main/kotlin/chapi/ast/scalaast/ScalaFullIdentListener.kt | Updated to set Language="scala", Kind=SOURCE_FILE, and DeclaredPackage |
| chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptFullIdentListener.kt | Updated to set Language="typescript", Kind=MODULE, ResolvedModulePath, and NamespacePath with proper enter/exit handling |
| chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustAstBaseListener.kt | Updated to set Language="rust", Kind=MODULE, and ResolvedModulePath |
| chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonFullIdentListener.kt | Updated to set Language="python", Kind=MODULE, and ResolvedModulePath |
| chapi-ast-csharp/src/main/kotlin/chapi/ast/csharpast/CSharpAstListener.kt | Updated to set Language="csharp", Kind=NAMESPACE, DeclaredPackage, and NamespacePath with proper namespace stack management |
| chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt | Updated to set Language="cpp", Kind=SOURCE_FILE, DeclaredPackage, and NamespacePath |
| chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt | Updated to set Language="c", Kind=SOURCE_FILE |
| chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/ProtobufFullIdentListener.kt | Updated to set Language="protobuf", Kind=IDL, and DeclaredPackage |
| chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/Protobuf2FullIdentListener.kt | Updated to set Language="protobuf", Kind=IDL, and DeclaredPackage |
| chapi-ast-thrift/src/main/kotlin/chapi/ast/thrift/ThriftFullIdentListener.kt | Updated to set Language="thrift", Kind=IDL, and DeclaredPackage |
| chapi-parser-toml/src/main/kotlin/chapi/parser/toml/TomlListener.kt | Updated to set Language="toml", Kind=CONFIG |
| chapi-parser-cmake/src/main/kotlin/chapi/parser/cmake/CMakeBasicListener.kt | Updated to set Language="cmake", Kind=BUILD_SCRIPT |
| build.gradle.kts | Unrelated infrastructure changes for Sonatype Central Portal migration and Maven credentials handling |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| FullName = fileName, | ||
| Language = "python", | ||
| Kind = ContainerKind.MODULE, | ||
| ResolvedModulePath = fileName.substringBeforeLast('.').replace('/', '.').replace('\\', '.') |
There was a problem hiding this comment.
The Python ResolvedModulePath calculation uses fileName with path separators replaced by dots, but doesn't account for potential edge cases such as files with multiple dots in the name (e.g., "test.utils.py" would become "test.utils" which may not be the intended module path). Consider using only the directory path for module resolution, not the full filename.
| codeContainer.NamespacePath = codeContainer.NamespacePath + nsName | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
The C++ namespace handling only appends to NamespacePath when entering a namespace but never removes from it when exiting. This will cause incorrect namespace paths for nested namespaces or multiple namespaces in the same file. Consider implementing an exit handler similar to the C# and TypeScript implementations.
| override fun exitNamespaceDefinition(ctx: CPP14Parser.NamespaceDefinitionContext?) { | |
| if (codeContainer.NamespacePath.isNotEmpty()) { | |
| codeContainer.NamespacePath = codeContainer.NamespacePath.dropLast(1) | |
| } | |
| } |
| @Test | ||
| fun `should support structured container semantics for Java`() { | ||
| val javaContainer = CodeContainer( | ||
| FullName = "UserService.java", | ||
| PackageName = "com.example.service", | ||
| Language = "java", | ||
| Kind = ContainerKind.SOURCE_FILE, | ||
| DeclaredPackage = "com.example.service" | ||
| ) | ||
|
|
||
| assertEquals("java", javaContainer.Language) | ||
| assertEquals(ContainerKind.SOURCE_FILE, javaContainer.Kind) | ||
| assertEquals("com.example.service", javaContainer.DeclaredPackage) | ||
| // Legacy field should still work | ||
| assertEquals("com.example.service", javaContainer.PackageName) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should support structured container semantics for Rust`() { | ||
| val rustContainer = CodeContainer( | ||
| FullName = "parser.rs", | ||
| PackageName = "crate::infrastructure::parser", | ||
| Language = "rust", | ||
| Kind = ContainerKind.MODULE, | ||
| ResolvedModulePath = "crate::infrastructure::parser" | ||
| ) | ||
|
|
||
| assertEquals("rust", rustContainer.Language) | ||
| assertEquals(ContainerKind.MODULE, rustContainer.Kind) | ||
| assertEquals("crate::infrastructure::parser", rustContainer.ResolvedModulePath) | ||
| // DeclaredPackage should be empty for Rust (no package declaration) | ||
| assertEquals("", rustContainer.DeclaredPackage) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should support namespace path for CSharp`() { | ||
| val csharpContainer = CodeContainer( | ||
| FullName = "UserService.cs", | ||
| PackageName = "MyApp.Services.Users", | ||
| Language = "csharp", | ||
| Kind = ContainerKind.NAMESPACE, | ||
| DeclaredPackage = "MyApp.Services.Users", | ||
| NamespacePath = listOf("MyApp", "Services", "Users") | ||
| ) | ||
|
|
||
| assertEquals("csharp", csharpContainer.Language) | ||
| assertEquals(ContainerKind.NAMESPACE, csharpContainer.Kind) | ||
| assertEquals(listOf("MyApp", "Services", "Users"), csharpContainer.NamespacePath) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should support config container kind for Toml`() { | ||
| val tomlContainer = CodeContainer( | ||
| FullName = "Cargo.toml", | ||
| Language = "toml", | ||
| Kind = ContainerKind.CONFIG | ||
| ) | ||
|
|
||
| assertEquals("toml", tomlContainer.Language) | ||
| assertEquals(ContainerKind.CONFIG, tomlContainer.Kind) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should support build script container kind for CMake`() { | ||
| val cmakeContainer = CodeContainer( | ||
| FullName = "CMakeLists.txt", | ||
| Language = "cmake", | ||
| Kind = ContainerKind.BUILD_SCRIPT | ||
| ) | ||
|
|
||
| assertEquals("cmake", cmakeContainer.Language) | ||
| assertEquals(ContainerKind.BUILD_SCRIPT, cmakeContainer.Kind) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should support IDL container kind for Protobuf`() { | ||
| val protobufContainer = CodeContainer( | ||
| FullName = "user.proto", | ||
| PackageName = "com.example.api", | ||
| Language = "protobuf", | ||
| Kind = ContainerKind.IDL, | ||
| DeclaredPackage = "com.example.api" | ||
| ) | ||
|
|
||
| assertEquals("protobuf", protobufContainer.Language) | ||
| assertEquals(ContainerKind.IDL, protobufContainer.Kind) | ||
| assertEquals("com.example.api", protobufContainer.DeclaredPackage) | ||
| } | ||
|
|
||
| @Test | ||
| fun `should maintain backward compatibility with legacy PackageName field`() { | ||
| // Old code that only uses PackageName should still work | ||
| val legacyContainer = CodeContainer( | ||
| FullName = "Test.java", | ||
| PackageName = "com.example" | ||
| ) | ||
|
|
||
| assertEquals("com.example", legacyContainer.PackageName) | ||
| // New fields should have sensible defaults | ||
| assertEquals("", legacyContainer.Language) | ||
| assertEquals(ContainerKind.UNKNOWN, legacyContainer.Kind) | ||
| assertEquals("", legacyContainer.DeclaredPackage) | ||
| assertEquals("", legacyContainer.ResolvedModulePath) | ||
| assertEquals(emptyList<String>(), legacyContainer.NamespacePath) | ||
| } |
There was a problem hiding this comment.
While unit tests have been added for the new fields in CodeContainerTest, there are no integration tests verifying that the parsers actually populate these fields correctly when parsing real code. Consider adding parser-level tests that verify Language, Kind, DeclaredPackage, ResolvedModulePath, and NamespacePath are correctly set for representative code samples in each supported language.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@build.gradle.kts`:
- Around line 204-231: The getMavenCredentials() helper currently returns
credentials from the first <server> entry unconditionally; update it to filter
by server id before returning credentials by reading
server.getElementsByTagName("id").item(0)?.textContent and comparing it against
an allowed set (e.g., "ossrh", "central", "sonatype-nexus-staging" or a
configurable list). In the loop over servers (variable servers, server), only
return Pair(username, password) when the id matches the allowed IDs; otherwise
continue searching and keep existing try/catch and logger.warn behavior if none
match.
- Around line 100-103: The snapshotsRepoUrl constant is pointing to the wrong
OSSRH endpoint; update the value of snapshotsRepoUrl to
"https://central.sonatype.com/repository/maven-snapshots/" so that the
conditional assignment to url (the existing url = if
(version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
uses the correct snapshots repository; leave releasesRepoUrl and the url
selection logic unchanged.
In `@chapi-ast-cpp/src/main/kotlin/chapi/ast/cppast/CPPBasicIdentListener.kt`:
- Around line 35-41: The enterNamespaceDefinition adds the namespace to
codeContainer.NamespacePath but there is no exitNamespaceDefinition to pop it,
causing NamespacePath to grow; add an override fun exitNamespaceDefinition(ctx:
CPP14Parser.NamespaceDefinitionContext?) that checks if
codeContainer.NamespacePath.isNotEmpty() then sets codeContainer.NamespacePath =
codeContainer.NamespacePath.dropLast(1); also consider clearing or resetting
codeContainer.PackageName and/or DeclaredPackage on exit for top-level namespace
resets if your semantics require it (mirror the pattern used in
TypeScriptFullIdentListener.kt).
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #42 +/- ##
============================================
+ Coverage 73.20% 73.58% +0.37%
- Complexity 1226 1249 +23
============================================
Files 69 69
Lines 4800 5106 +306
Branches 941 975 +34
============================================
+ Hits 3514 3757 +243
- Misses 714 769 +55
- Partials 572 580 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This commit addresses Issue #41 problem 3: CodeImport/CodeExport being too "string-based" to accurately represent different language import styles. Changes to CodeImport: - Add `ImportKind` enum: UNKNOWN, DEFAULT, NAMED, NAMESPACE, SIDE_EFFECT, STATIC, WILDCARD, RELATIVE, TYPE_ONLY, DOT - Add `ImportSpecifier` data class with OriginalName, LocalName, IsTypeOnly - New fields: Kind, Specifiers, DefaultName, NamespaceName, IsTypeOnly, PathSegments Changes to CodeExport: - Add `ExportKind` enum: UNKNOWN, DEFAULT, NAMED, RE_EXPORT_ALL, RE_EXPORT_NAMED, TYPE_ONLY - Add `ExportSpecifier` data class with LocalName, ExportedName, IsTypeOnly - New fields: Kind, Specifiers, FromSource, IsTypeOnly Parser updates: - TypeScript: full support for default/named/namespace/side-effect imports, default/named/re-export exports with specifiers - Rust: PathSegments for use paths, WILDCARD for glob imports - Python: NAMED/RELATIVE/WILDCARD imports with specifiers - Java: STATIC/WILDCARD/NAMED imports with specifiers - Scala: NAMED/WILDCARD imports with specifiers - Go: DOT/SIDE_EFFECT/NAMED imports with specifiers Backward compatibility: - All legacy fields (Source, AsName, UsageName, Name, SourceFile) preserved - New fields have sensible defaults Partially addresses #41 (problem 3 of 5)
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
chapi-ast-scala/src/main/kotlin/chapi/ast/scalaast/ScalaFullIdentListener.kt (1)
37-56: Unusedspecifiersaccumulator list.The
specifierslist at line 37 is populated (line 44) but never used. EachCodeImportat line 50 creates its own inlinelistOf(ImportSpecifier(...))instead of using the accumulated list. Either remove the unusedspecifierslist or use it to create a singleCodeImportwith all specifiers for grouped imports likeimport pkg.{A, B, C}.Option 1: Remove unused list
if (importSelectors != null) { // Handle import with selectors: import org.apache.spark.sql.{DataFrame, Row, functions} - val specifiers = mutableListOf<ImportSpecifier>() - importSelectors.importSelector().forEach { selector -> val selectorName = selector.Id(0)?.text ?: "_" val asName = if (selector.Id().size > 1) selector.Id(1).text else selectorName if (selectorName != "_") { - specifiers += ImportSpecifier(OriginalName = selectorName, LocalName = asName) - val codeImport = CodeImport(
🤖 Fix all issues with AI agents
In `@chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaBasicIdentListener.kt`:
- Around line 21-51: enterImportDeclaration currently treats static imports
before checking for wildcard, and named imports don't set UsageName; update the
logic to first handle the static+wildcard case (if isStatic && isWildcard) so
that for "import static a.b.C.*;" you set codeImport.Kind = ImportKind.WILDCARD,
codeImport.UsageName = listOf("*"), codeImport.Source =
fullSource.substringBeforeLast(".*").removeSuffix(".*") or simply
fullSource.substringBeforeLast(".*").ifEmpty{fullSource.substringBeforeLast('.')
/*fallback*/} (resulting in "a.b.C"), and Specifiers = empty or null; then
handle static named imports (isStatic && !isWildcard) by setting UsageName to
the last segment and Source to dropLast(1) as you already do; finally ensure
non-static named imports (else branch) also populate codeImport.UsageName =
listOf(className) in addition to Specifiers and Kind = NAMED so
JavaFullIdentListener's imp.UsageName.contains(...) works; keep PathSegments
assignment as-is.
In `@chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt`:
- Around line 77-84: Named imports currently don't set codeImport.UsageName
which breaks checks like warpTargetFullType()'s
imp.UsageName.contains(pureTargetType); in the else branch that sets
codeImport.Kind = ImportKind.NAMED and builds className and
codeImport.Specifiers, also populate codeImport.UsageName (e.g., set it to a
list containing className or the appropriate usage string) so named imports
mirror static/wildcard imports; update the block that constructs ImportSpecifier
(and/or codeImport.UsageName) to include the className in UsageName.
- Around line 64-76: Handle the static+wildcard import case before the exclusive
branches: if both isStatic and isWildcard are true, set codeImport.Source =
fullSource, codeImport.UsageName = listOf("*"), set codeImport.Scope = "static"
and set codeImport.Kind and Specifiers to reflect a static wildcard import (e.g.
Kind = ImportKind.WILDCARD or a combined value used in your model and Specifiers
= listOf(ImportSpecifier(OriginalName="*", LocalName="*"))), then return/skip
the other branches; update the logic around the isStatic/isWildcard checks in
JavaFullIdentListener (where fullSource, isStatic, isWildcard, codeImport,
ImportSpecifier and ImportKind are referenced) so static wildcard imports like
import static java.util.Arrays.* are recorded with Source = "java.util.Arrays"
and UsageName = ["*"] and Scope = "static".
In `@chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustAstBaseListener.kt`:
- Around line 72-97: Move the glob and relative detection into the per-path loop
in enterUseDeclaration so each produced path from buildToPath is classified
correctly; for each path (in the imports.forEach), compute isGlob for that path
by checking if the path contains "*" or endsWith "*" (e.g. path.any { it == "*"
} or path.last() == "*"), and compute relative by checking the path's first
segment against the set {"crate","self","super"} instead of only "crate". Then
use these per-path booleans when constructing CodeImport (fields: Kind =
ImportKind.WILDCARD/RELATIVE/NAMED, Scope = "crate" when relative else "cargo",
and ensure Specifiers/AsName remain derived from the path). Reference
functions/types: enterUseDeclaration, buildToPath, imports, CodeImport,
ImportKind, ImportSpecifier.
In
`@chapi-ast-scala/src/main/kotlin/chapi/ast/scalaast/ScalaFullIdentListener.kt`:
- Around line 94-99: The code sets ImportSpecifier.OriginalName to asName for
non-wildcard imports, but OriginalName must be the exported/source identifier
(the name from the import path) while LocalName should remain the local/asName;
in ScalaFullIdentListener locate the non-wildcard branch that assigns
codeImport.Specifiers and change ImportSpecifier.OriginalName to the actual
source identifier parsed from the import path (the parsed import element, not
asName) and keep ImportSpecifier.LocalName = asName; ensure this uses the
variable that holds the source/imported name (instead of asName) when creating
the ImportSpecifier.
♻️ Duplicate comments (1)
chapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonFullIdentListener.kt (1)
8-13: Container initialization with module semantics for Python.The
CodeContainercorrectly usesContainerKind.MODULEfor Python files (as Python files are modules) and derivesResolvedModulePathfrom the filename. Note that the path derivation may not handle edge cases like filenames with multiple dots (e.g.,test.utils.py→test.utils), as noted in a previous review.
🧹 Nitpick comments (5)
chapi-ast-typescript/src/main/kotlin/chapi/ast/typescriptast/TypeScriptFullIdentListener.kt (1)
1337-1343: Potential duplicate exports when adding to both nodes.Adding the same export to both
currentNode.ExportsanddefaultNode.Exportsmay cause duplicates in the final output. IfcurrentNoderefers to an active class/interface anddefaultNodeis also added tocodeContainer.DataStructures(ingetNodeInfo), the export will appear twice.Consider adding only to
defaultNodefor top-level default exports, or conditionally adding based on whether we're inside a class context:♻️ Suggested fix
override fun enterExportDefaultDeclaration(ctx: TypeScriptParser.ExportDefaultDeclarationContext?) { // Get the exported expression/name val name = ctx?.singleExpression()?.text if (name != null) { val export = CodeExport( Name = name, Kind = ExportKind.DEFAULT ) - currentNode.Exports += export defaultNode.Exports += export } }chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt (2)
51-55: Consider defensive null handling forqualifiedName().While
ctxis typically non-null in ANTLR listener enter methods, usingctx!!.qualifiedName()!!.textcould cause aNullPointerExceptionif the grammar encounters a malformed package declaration wherequalifiedName()is null. The previous implementation may have had similar handling, but consider a defensive approach.♻️ Suggested defensive handling
override fun enterPackageDeclaration(ctx: JavaParser.PackageDeclarationContext?) { - val packageName = ctx!!.qualifiedName()!!.text - codeContainer.PackageName = packageName - codeContainer.DeclaredPackage = packageName + val packageName = ctx?.qualifiedName()?.text ?: return + codeContainer.PackageName = packageName + codeContainer.DeclaredPackage = packageName }
57-62: Consider defensive null handling forqualifiedName().Similar to
enterPackageDeclaration, usingctx!!.qualifiedName()!!.textcould throw aNullPointerExceptionfor malformed import statements.♻️ Suggested defensive handling
override fun enterImportDeclaration(ctx: JavaParser.ImportDeclarationContext?) { - val fullSource = ctx!!.qualifiedName()!!.text + val fullSource = ctx?.qualifiedName()?.text ?: return val isStatic = ctx.STATIC() != null val isWildcard = ctx.MUL() != nullchapi-ast-python/src/main/kotlin/chapi/ast/pythonast/PythonFullIdentListener.kt (1)
95-97:AsNamereflects only the last alias in multi-import statements.In
from x import a, b as c, onlycis stored inAsName. This is consistent with the legacy single-value field design, but the full alias information is now correctly captured inSpecifiers. Consider documenting this behavior or deprecatingAsNamein favor ofSpecifiersin future versions.chapi-ast-rust/src/main/kotlin/chapi/ast/rustast/RustAstBaseListener.kt (1)
17-23: Consider emittingCRATEfor crate roots.Right now
Kindis alwaysMODULE, solib.rs/main.rsfiles never surface asCRATE. If downstream consumers rely on that distinction, they’ll miss it.♻️ Proposed adjustment
- Kind = ContainerKind.MODULE, + Kind = if (fileName.endsWith(LIB_RS) || fileName.endsWith(MAIN_RS)) { + ContainerKind.CRATE + } else { + ContainerKind.MODULE + },
chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaBasicIdentListener.kt
Show resolved
Hide resolved
| if (isStatic) { | ||
| val sourceSplit = fullSource.split(".") | ||
| codeImport.UsageName = listOf(sourceSplit.last()) | ||
|
|
||
| val split = sourceSplit.dropLast(1) | ||
| codeImport.Source = split.joinToString(".") | ||
| codeImport.Source = sourceSplit.dropLast(1).joinToString(".") | ||
| codeImport.Kind = ImportKind.STATIC | ||
| codeImport.Scope = "static" | ||
| codeImport.Specifiers = listOf(ImportSpecifier( | ||
| OriginalName = sourceSplit.last(), | ||
| LocalName = sourceSplit.last() | ||
| )) | ||
| } else if (isWildcard) { | ||
| codeImport.Kind = ImportKind.WILDCARD | ||
| codeImport.UsageName = listOf("*") |
There was a problem hiding this comment.
Static wildcard imports handled incorrectly.
For imports like import static java.util.Arrays.*;, both isStatic and isWildcard are true. The current if-elif structure means the static branch executes exclusively, incorrectly treating it as a single static member import:
fullSource="java.util.Arrays"(fromqualifiedName())- Current logic sets
Source="java.util"andUsageName=["Arrays"] - Should be:
Source="java.util.Arrays",UsageName=["*"],Kindshould reflect both static and wildcard
🐛 Proposed fix to handle static wildcard imports
if (isStatic) {
- val sourceSplit = fullSource.split(".")
- codeImport.UsageName = listOf(sourceSplit.last())
- codeImport.Source = sourceSplit.dropLast(1).joinToString(".")
- codeImport.Kind = ImportKind.STATIC
- codeImport.Scope = "static"
- codeImport.Specifiers = listOf(ImportSpecifier(
- OriginalName = sourceSplit.last(),
- LocalName = sourceSplit.last()
- ))
- } else if (isWildcard) {
+ if (isWildcard) {
+ // Static wildcard import: import static pkg.Class.*
+ codeImport.UsageName = listOf("*")
+ codeImport.Source = fullSource
+ codeImport.Kind = ImportKind.STATIC // or a new STATIC_WILDCARD kind
+ codeImport.Scope = "static"
+ } else {
+ // Single static member import: import static pkg.Class.member
+ val sourceSplit = fullSource.split(".")
+ codeImport.UsageName = listOf(sourceSplit.last())
+ codeImport.Source = sourceSplit.dropLast(1).joinToString(".")
+ codeImport.Kind = ImportKind.STATIC
+ codeImport.Scope = "static"
+ codeImport.Specifiers = listOf(ImportSpecifier(
+ OriginalName = sourceSplit.last(),
+ LocalName = sourceSplit.last()
+ ))
+ }
+ } else if (isWildcard) {
codeImport.Kind = ImportKind.WILDCARD
codeImport.UsageName = listOf("*")🤖 Prompt for AI Agents
In `@chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt`
around lines 64 - 76, Handle the static+wildcard import case before the
exclusive branches: if both isStatic and isWildcard are true, set
codeImport.Source = fullSource, codeImport.UsageName = listOf("*"), set
codeImport.Scope = "static" and set codeImport.Kind and Specifiers to reflect a
static wildcard import (e.g. Kind = ImportKind.WILDCARD or a combined value used
in your model and Specifiers = listOf(ImportSpecifier(OriginalName="*",
LocalName="*"))), then return/skip the other branches; update the logic around
the isStatic/isWildcard checks in JavaFullIdentListener (where fullSource,
isStatic, isWildcard, codeImport, ImportSpecifier and ImportKind are referenced)
so static wildcard imports like import static java.util.Arrays.* are recorded
with Source = "java.util.Arrays" and UsageName = ["*"] and Scope = "static".
chapi-ast-java/src/main/kotlin/chapi/ast/javaast/JavaFullIdentListener.kt
Show resolved
Hide resolved
chapi-ast-scala/src/main/kotlin/chapi/ast/scalaast/ScalaFullIdentListener.kt
Show resolved
Hide resolved
- Improve import classification for Java static imports, Rust relative imports, and Scala import specifiers - Fix Python module path resolution to handle file extensions correctly - Add namespace tracking for C++ AST - Update Sonatype snapshot repository URL and server ID filtering
Summary
This PR addresses Issue #41 - solving Problem 1 and Problem 3:
Problem 1: Container Semantics
The
PackageNamefield inCodeContainerhad different meanings across languages:packagedeclarationnamespaceSolution
Add structured fields to
CodeContainerwhile maintaining backward compatibility:ContainerKindenum - distinguishes semantic container types:SOURCE_FILE,PACKAGE,NAMESPACE,MODULE,CRATE,CONFIG,BUILD_SCRIPT,IDL,UNKNOWNNew fields:
Language: String- the programming languageKind: ContainerKind- semantic container typeDeclaredPackage: String- explicitly declared package/namespaceResolvedModulePath: String- path-derived module nameNamespacePath: List<String>- namespace hierarchy as listProblem 3: Import/Export Semantics
The original
CodeImportwithUsageName: List<String>+AsName: Stringcouldn't accurately express:use a::{b, c as d}Solution
CodeImport new fields:
ImportKindenum:UNKNOWN,DEFAULT,NAMED,NAMESPACE,SIDE_EFFECT,STATIC,WILDCARD,RELATIVE,TYPE_ONLY,DOTImportSpecifierdata class:OriginalName,LocalName,IsTypeOnlyKind,Specifiers,DefaultName,NamespaceName,IsTypeOnly,PathSegmentsCodeExport new fields:
ExportKindenum:UNKNOWN,DEFAULT,NAMED,RE_EXPORT_ALL,RE_EXPORT_NAMED,TYPE_ONLYExportSpecifierdata class:LocalName,ExportedName,IsTypeOnlyKind,Specifiers,FromSource,IsTypeOnlyExamples
Backward Compatibility
CodeContainer.PackageNameCodeImport.Source,CodeImport.AsName,CodeImport.UsageNameCodeExport.Name,CodeExport.SourceFileTest plan
CodeImportcovering all language patternsCodeContainercovering all container kindsPartially addresses #41 (problems 1 and 3 of 5)
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.