Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **Go language support** via the `codeanalyzer-go` subprocess backend. New `CLDK.go()` factory
method returns a `GoAnalysis` facade backed by the `GoAnalysisBackend` ABC (parity with
Java/Python/TypeScript), with a `GoCodeanalyzer` local backend that shells out to the
`codeanalyzer-go` binary on `PATH`. Exposes symbol table, call graph, type, and caller/callee
queries over `cldk.models.go` Pydantic models. Supports `analysis_level`, `eager`, and
`target_files`. The binary must be installed and on `PATH`.

## [v1.4.0] - 2026-06-27

### Changed
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ an optional read-only Neo4j backend — selected by the *type* of the `backend=`
| Python | `CLDK.python(...)` | `PyCodeanalyzer` (in-process `codeanalyzer-python`) | `PyNeo4jBackend` | re-exported from `codeanalyzer-python` |
| TypeScript | `CLDK.typescript(...)` | `TSCodeanalyzer` (`codeanalyzer-typescript` binary, subprocess) | `TSNeo4jBackend` | `cldk/models/typescript/` |
| C | `CLDK.c(...)` | libclang (in-process, syntactic only) | — | `cldk/models/c/` |
| Go | `CLDK.go(...)` | `GoCodeanalyzer` (`codeanalyzer-go` binary, subprocess) | — | `cldk/models/go/` |

The legacy `CLDK(language="<lang>").analysis(...)` entry still works as a compat shim. Adding a
language means a new factory method + facade + backend ABC/impl(s) + models + tests — **update this
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

**A unified, multilingual program-analysis SDK for Code LLMs.** CLDK turns raw source code into structured, LLM-ready program facts — symbol tables, call graphs, type hierarchies, and more — behind a single Python API, so you can build analysis-augmented LLM pipelines without wrangling a different static-analysis tool for every language.

Under the hood, CLDK orchestrates mature analysis engines (WALA, Tree-sitter, Jedi, PyCG, ts-morph) and normalizes their output into consistent, typed [Pydantic](https://docs.pydantic.dev/) models. You get the same ergonomic interface whether you are analyzing Java, Python, or TypeScript.
Under the hood, CLDK orchestrates mature analysis engines (WALA, Tree-sitter, Jedi, PyCG, ts-morph, `go/packages` + `go/types`) and normalizes their output into consistent, typed [Pydantic](https://docs.pydantic.dev/) models. You get the same ergonomic interface whether you are analyzing Java, Python, TypeScript, C, or Go.

CLDK is:

Expand Down Expand Up @@ -70,6 +70,7 @@ from cldk import CLDK
analysis = CLDK.java(project_path="/path/to/java/project")
# analysis = CLDK.python(project_path="/path/to/python/project")
# analysis = CLDK.typescript(project_path="/path/to/ts/project")
# analysis = CLDK.go(project_path="/path/to/go/project")
```

Walk the symbol table and pull method bodies:
Expand Down Expand Up @@ -116,7 +117,7 @@ classes = analysis.get_all_classes()

> **`project_path` with the Neo4j backend:** it's **optional** — the graph is read over Bolt, so you can omit it as shown above. CLDK validates `project_path` only when you actually pass one (it must exist and be a directory, on every backend); passing `None` skips that check. Supply a real path only if you also need on-disk source access (e.g. file content/snippets) alongside the graph.

> **Deprecation:** the old `CLDK(language="java").analysis(...)` entry point still works as a thin compatibility shim (it emits a `DeprecationWarning`). Prefer the `CLDK.java()` / `CLDK.python()` / `CLDK.typescript()` factory methods.
> **Deprecation:** the old `CLDK(language="java").analysis(...)` entry point still works as a thin compatibility shim (it emits a `DeprecationWarning`). Prefer the `CLDK.java()` / `CLDK.python()` / `CLDK.typescript()` / `CLDK.c()` / `CLDK.go()` factory methods.

## Supported Languages & Backends

Expand All @@ -127,6 +128,7 @@ Each language is analyzed by a dedicated `codeanalyzer-*` engine; CLDK normalize
| **Java** | [`codeanalyzer-java`](https://github.com/codellm-devkit/codeanalyzer-java) | WALA + JavaParser. Bytecode-level call graphs, type hierarchies, symbol resolution, CRUD-operation and entry-point detection. Optional read-only **Neo4j** graph backend. |
| **Python** | [`codeanalyzer-python`](https://github.com/codellm-devkit/codeanalyzer-python) | Jedi with PyCG-based call graphs. Symbol tables, call graphs, and class/method resolution. Optional read-only **Neo4j** graph backend. |
| **TypeScript / JavaScript** | [`codeanalyzer-typescript`](https://github.com/codellm-devkit/codeanalyzer-typescript) | ts-morph with Jelly-based call graphs. Symbols, call graph, types, decorators, and call sites. Optional read-only **Neo4j** graph backend. |
| **Go** | [`codeanalyzer-go`](https://github.com/codellm-devkit/codeanalyzer-go) | `go/packages` + `go/types`. Symbol table, resolver-based call graph, struct/interface types, pointer/value receivers, multi-return, goroutine call sites, embedded fields. External binary on `PATH`. |

The backend is selected by the **type** of the `backend=` config you pass to a factory: the in-process analyzer (default) or a `Neo4jConnectionConfig` for the read-only graph backend.

Expand All @@ -149,13 +151,15 @@ graph TD
J --> EJ[codeanalyzer-java<br/>WALA · JavaParser]
P --> EP[codeanalyzer-python<br/>Jedi · PyCG]
T --> ET[codeanalyzer-typescript<br/>ts-morph · Jelly]
A --> G[cldk.analysis.go]
G --> EG[codeanalyzer-go<br/>go/packages · go/types]

J -. read-only .-> N[(Neo4j)]
P -. read-only .-> N
T -. read-only .-> N
```

**Data models** — each language has its own set of Pydantic models under `cldk.models` (`cldk.models.java`, `cldk.models.python`, `cldk.models.typescript`). They give you structured, typed, dot-accessible representations of classes, methods, fields, and statements, with JSON serialization and shared conventions across languages.
**Data models** — each language has its own set of Pydantic models under `cldk.models` (`cldk.models.java`, `cldk.models.python`, `cldk.models.typescript`, `cldk.models.c`, `cldk.models.go`). They give you structured, typed, dot-accessible representations of classes, methods, fields, and statements, with JSON serialization and shared conventions across languages.

**Analysis backends** — each language has a backend under `cldk.analysis.<language>` that coordinates its engine (see the table above) and maps the result onto the data models. The read-only Neo4j backends (`cldk.analysis.<language>.neo4j`) reconstruct the *same* models from a Cypher graph, so they are drop-in interchangeable with the in-process analyzers. Backends are orchestrated internally; you only call high-level methods such as `get_symbol_table()`, `get_method_body(...)`, and `get_call_graph(...)`, and CLDK handles tool coordination, parsing, and marshalling under the hood.

Expand Down
12 changes: 11 additions & 1 deletion cldk/analysis/commons/backend_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
# The canonical sub-directory name each language's artifacts live under inside the shared cache
# root. Keyed so that a polyglot repository analyzed under more than one language does not have its
# backends overwrite a single shared ``analysis.json``.
_CACHE_KEYS = {"java": "java", "python": "python", "typescript": "typescript", "c": "c"}
_CACHE_KEYS = {"java": "java", "python": "python", "typescript": "typescript", "c": "c", "go": "go"}


@dataclass
Expand Down Expand Up @@ -113,10 +113,20 @@ class Neo4jConnectionConfig:
application_name: str | None = None


@dataclass
class GoCodeAnalyzerConfig(CodeAnalyzerConfig):
"""Select the in-process codeanalyzer backend for Go.

Inherits :attr:`cache_dir` from :class:`CodeAnalyzerConfig`. Go analysis has no
additional backend-specific knobs at this time.
"""


# Per-language discriminated unions the facades match on.
JavaBackend = Union[CodeAnalyzerConfig, Neo4jConnectionConfig]
PyBackend = Union[PyCodeAnalyzerConfig, Neo4jConnectionConfig]
TSBackend = Union[TSCodeAnalyzerConfig, CodeAnalyzerConfig, Neo4jConnectionConfig]
GoBackend = Union[GoCodeAnalyzerConfig, CodeAnalyzerConfig]


def cache_subdir(cache_dir: Union[str, Path, None], project_dir: Union[str, Path, None], language: str) -> Path | None:
Expand Down
22 changes: 22 additions & 0 deletions cldk/analysis/go/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
################################################################################
# Copyright IBM Corporation 2024
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

"""Go analysis package."""

from cldk.analysis.go.backend import GoAnalysisBackend
from cldk.analysis.go.go_analysis import GoAnalysis

__all__ = ["GoAnalysis", "GoAnalysisBackend"]
77 changes: 77 additions & 0 deletions cldk/analysis/go/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
################################################################################
# Copyright IBM Corporation 2026
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

"""The Go analysis backend contract.

:class:`GoAnalysis` is a thin façade that delegates every query to a *backend*.
Today the only backend is :class:`~cldk.analysis.go.codeanalyzer.GoCodeanalyzer`
(in-process subprocess driver over ``analysis.json``); this ABC formalizes the
surface the façade depends on so an alternative backend (e.g. a Neo4j/Cypher
backend, mirroring :class:`~cldk.analysis.python.backend.PythonAnalysisBackend`)
can be dropped in and selected via the ``backend=`` configuration object without
touching the façade.

The contract is enforced by the type system and at instantiation time.
Backend-specific lifecycle (caches, subprocess management) is intentionally not
part of it.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Dict, List, Optional

from cldk.models.go.models import GoApplication, GoCallable, GoFile, GoType


class GoAnalysisBackend(ABC):
"""Abstract base every Go analysis backend implements.

A backend owns all indexing and query logic for a Go application; the
:class:`~cldk.analysis.go.GoAnalysis` façade is a one-line-delegation shim
over it. Implementations must return the canonical ``cldk.models.go`` Pydantic
objects so backends are behaviorally interchangeable.
"""

# ── application / whole-program ───────────────────────────────────────────

@abstractmethod
def get_application(self) -> GoApplication:
"""The complete application model (symbol table + call graph)."""

@abstractmethod
def get_symbol_table(self) -> Dict[str, GoFile]:
"""The per-file symbol table, keyed by file path relative to the project root."""

@abstractmethod
def get_all_files(self) -> Dict[str, GoFile]:
"""All analyzed files (alias for :meth:`get_symbol_table`)."""

@abstractmethod
def get_file(self, file_path: str) -> Optional[GoFile]:
"""The :class:`~cldk.models.go.GoFile` for a given relative file path, or ``None``."""

# ── types ─────────────────────────────────────────────────────────────────

@abstractmethod
def get_all_types(self) -> Dict[str, GoType]:
"""All named types (structs and interfaces) across all files, keyed by qualified name."""

# ── callables ─────────────────────────────────────────────────────────────

@abstractmethod
def get_all_callables(self) -> Dict[str, GoCallable]:
"""All top-level functions and type methods across all files, keyed by signature."""
21 changes: 21 additions & 0 deletions cldk/analysis/go/codeanalyzer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
################################################################################
# Copyright IBM Corporation 2024
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

"""Go analysis backend package."""

from cldk.analysis.go.codeanalyzer.codeanalyzer import GoCodeanalyzer

__all__ = ["GoCodeanalyzer"]
Loading