跳转到内容

语言支持与 API 保真度

codeknit 提取语法时无需运行编译器、评估宏或加载项目依赖。下表描述的是已测试的构造,而非完整的语言符合性或跨文件名称解析的保证。

每种语言在 internal/plugin/api_fidelity_test.go 中都有一个精确的基本参数/返回签名断言。声明覆盖率也在 internal/plugin/coverage_test.go 和各语言测试包中进行检查。

语言 扩展名 签名基准 声明和容器覆盖率
C .c, .h 定义和原型中的命名参数和返回类型 结构体、联合体、枚举、类型定义、宏;头文件保护内的声明
C++ .cpp, .cc, .cxx, .hpp, .hxx, .h 函数、原型和方法声明中的命名参数和返回类型 类、结构体、方法、命名空间、模板;头文件保护和 extern "C" 包装
C# .cs 命名类型参数和显式返回类型 类、结构体、接口、方法、字段、委托、命名空间
Go .go 分组、未命名、可变、函数类型和泛型参数;命名/多返回值;泛型接收者 带有类型和标签的结构体字段、嵌入字段、接口方法签名、类型参数、包
Java .java 命名类型参数和显式返回类型 类、接口、构造函数、方法、字段、嵌套类、包
JavaScript .js, .jsx, .mjs, .cjs 命名参数;无推断返回类型 函数、箭头函数、类、字段、方法、包含 JSX 的声明
PHP .php 命名类型参数和显式返回类型 类、接口、特征、枚举、方法、属性、命名空间
Python .py, .pyi 注解、默认值、*args、**kwargs、位置/关键字分隔符、返回注解 函数、类、嵌套类、装饰方法;常规绑定接收者被省略
Ruby .rb 命名参数;无推断返回类型 类、模块、实例/单例方法、常量
Rust .rs 类型模式、self/&self/&mut self、可变绑定、显式返回类型 内联和嵌套模块、命名结构体字段、特征方法和实现方法
Scala .scala, .sc 命名类型参数和显式返回类型 类、特征、对象、方法、枚举、包
TypeScript .ts, .tsx, .mts, .cts 命名类型参数和显式返回注解 函数、箭头函数、接口、类、字段、命名空间;TSX 使用其自身的语法

所有处理源代码的 CLI 命令均接受 --header-language c|cpp 参数。该选择仅适用于 .h 文件。其他扩展名保留其通常的解析器。

终端窗口
codeknit parse ./src --header-language cpp
codeknit fingerprint ./src --header-language c
codeknit graph analyze ./src --header-language cpp

默认值为 c,以保留 C 宏、类型定义和联合体的提取。对于命名为 .h 的 C++ 头文件,请使用 cpp;命名为 .hpp 和 .hxx 的文件已默认使用 C++。该选择是显式的,因为两种语法都可以接受对方语言的部分语法,而无需提取其完整结构。

头文件保护和条件分支在不评估其条件的情况下被遍历。因此,结果可能包含来自互斥分支的声明。宏展开、包含解析和构建标志不适用。TUI 对 .h 文件使用默认的 C 选择。

例如,Go:

func Pair(a, b int, rest ...string) (string, error)

生成:

Pair(a: int, b: int, rest: ...string) -> (string, error)

Go 接口方法和 Go/Rust 命名字段在正常和 fingerprint 模式下均显示为符号。方法和字段保留其所属类型。嵌套的 Rust 模块保留限定名称,如 service.storage.read,即使另一个模块声明了同名函数。

Rust 允许字段和方法同名。字段作用域名称在内部带有 #field 后缀,以便其 ID 和包含边保持不同;显示名称和签名保留原始字段名称。

Python 签名保留注解、默认值、可变参数和调用约定分隔符。常规的第一个 self 或 cls 参数在绑定方法中被省略,但在自由函数或静态方法中保留。

多行签名在 JSON 中保留源代码换行符。SKT 将换行符转义为 \n 和 \r,以便每个符号保持在一行物理输出行中。

关系保留接收者限定和词法所有权。相对导入和重命名的 JavaScript/TypeScript 及 Python 导入提供目标上下文。Go 和 Java 包声明可以在同一目录下的文件之间解析。未匹配的导入不会回退到无关声明,竞争候选项仍然保持模糊。

本地接收者绑定优先于命名空间导入,包括当参数遮蔽导入的命名空间时。Python 包导入考虑包的 __init__ 模块中的声明,而非任意后代模块。无法从该模块确定的重新导出仍然未解析。

Java、C++、C#、Scala 和 TypeScript 可调用提取保留声明字节范围,以便重载体可以有不同的调用者和别名作用域,包括同一行上的声明。选择重载目标仍然保守。Java、C++、C#、Scala 和 TypeScript 中的显式覆盖关系查找最近的继承声明;缺少基类、循环或竞争候选项保留不确定性,而非产生自链接。

这是基于语法的分析,而非编译器或运行时调用图。裸 JavaScript 包说明符、包重新导出、构建配置、复杂接收者类型和动态调度可能仍然未解析。存储库中其他位置的唯一名称不足以证明依赖关系。

JSON 保留不确定的边,标记为 resolution: "unresolved" 或 "ambiguous",并在可用时包含候选 ID。这些边没有目标 ShortID。

SKT 对所有关系使用相同的边表示法,并带有可选的解析标记:

S8 --references--> S5
S10 --references[unresolved]--> string, bool
S11 --calls[ambiguous]--> Helper [candidates=S12, S15]

已知端点和候选项使用 ShortID,包括未解析关系的端点。没有符号的裸不确定目标是相对于源文件的名称。看起来像 ShortID 的字面名称和其他复杂标识使用带引号的完整 ID 以避免歧义。具有相同源、类型、解析和候选集的边共享一个目标列表。SKT 阅读器保留解析和候选项,包括对后续输出块中符号的引用。精简输出对关系类型进行编码,同时保持解析标记的可读性。

C/C++ 包含具有显式的 meta/file 和 meta/include 符号。头文件拼写在符号列表中仅出现一次;边使用其 ShortID:

[symbols]
## src/main.c
S1 meta/file L1-L4 main.c
S2 meta/include L1-L1 "config.h" {resolution=unresolved}
S3 meta/include L2-L2 "utils.h" {resolution=unresolved}
S4 meta/include L3-L3 <stdio.h> {resolution=unresolved}
[edges]
S1 --references[unresolved]--> S2, S3, S4

包含符号描述的是书写的指令,而非已解析的头文件声明。同一文件内的重复包含共享一个端点。文件/包含元数据被排除在依赖排名和死代码发现之外;添加 ID 并不意味着通过编译器的包含搜索配置找到了头文件。

依赖度量和 HTML 图链接仅使用已解析的边。报告显示排除的关系计数,HTML 图显示不确定性计数。扇入和扇出计算不同的邻居数量。继承分析考虑所有基类和接口;循环继承没有报告深度。可达性从命名的 main、Main 或 init 可调用开始,并沿依赖边传递。到达成员使其容器相关,而不使每个兄弟成员可达。如果没有识别的入口点,则省略可达性发现。

回调参数和返回的可调用对象是引用,而非调用证明。别名解析保留文件、函数和对象标识;冲突的赋值保持不确定。图发现是审查候选项:外部调用者和动态行为可能改变结果。基于目录的层规则和变更传播权重是启发式的,而非测量概率。

  • 复杂的 C/C++ 指针、引用、数组和函数指针声明器可能仍然具有不完整的签名。宏生成的声明不会被展开。
  • TypeScript/JavaScript 构造函数处理和某些可选、剩余或解构参数形式不完整。
  • 超出测试用例的高级泛型约束和类型形式因语言而异。不会推断返回类型。
  • Rust 宏不会被展开。记录外联模块声明;必须在扫描中包含其文件。元组字段、枚举变体详情和关联项具有部分覆盖率。
  • 语法提取无法以编译器的精度解析动态调度、重载、导入依赖或构建配置语义。
  • 部分语法错误可能仍然会遗漏声明。在应用重构之前,请检查报告的警告并验证源代码。

更完整的提取会改变符号计数和 ShortID。升级时请重新生成现有的 skeleton 输出,而不要与旧输出混合。