调用图分析
CKG(代码知识图谱)的调用图是 wescode 的核心差异化能力之一。它通过静态分析构建函数间的调用关系图,支持调用链追踪、影响分析和可视化交互,帮助 AI 和开发者深入理解代码结构。
调用图生成原理
数据来源
CKG 调用图的生成基于多源数据融合:
| 数据源 | 提供的信息 | 优先级 |
|---|---|---|
| Tree-sitter | 函数调用语法节点 | 基础层 |
| LSP | 类型推断、跳转定义 | 增强层 |
| CKG 增量解析 | 跨文件边解析 | 融合层 |
10-Pass 管线
CKG 索引采用 10-Pass 管线处理,调用图相关的核心阶段:
- Pass 1 - 文件扫描:发现工作区内的源码文件
- Pass 2 - 符号提取:通过 Tree-sitter 提取函数、类型等符号
- Pass 3 - 调用边提取:提取函数调用关系(
caller → callee) - Pass 4 - 边解析:将未解析的调用目标关联到具体符号
- 后续 Pass:FTS5 索引写入、统计计算等
调用边的三级解析
对于一条调用边 foo() → bar(),CKG 按以下优先级尝试解析 bar 的目标:
| 解析级别 | 匹配规则 | 示例 |
|---|---|---|
| 精确匹配 | Type.Method 形式,通过 parent 列匹配 | svc.Create → UserService.Create |
| 包路径匹配 | 文件路径推断包名 | auth.Validate → internal/auth/Validate |
| 裸名回退 | 全局唯一的函数名 | processOrder → 唯一的 processOrder 定义 |
重要:当裸名对应多个候选符号时,CKG 保持
target_id IS NULL(未解析状态),交给查询层分组呈现,不会错误连边。
调用链追踪
向下追踪(Callees)
从一个函数出发,列出它调用的所有函数:
ProcessOrder 函数内部调用了哪些函数?
CKG 返回:
ProcessOrder
├── validateOrder (internal/order/validate.go)
├── checkInventory (internal/inventory/check.go)
├── calculateTotal (internal/pricing/calc.go)
├── createTransaction (internal/payment/tx.go)
│ ├── chargeCard (internal/payment/card.go)
│ └── recordPayment (internal/payment/record.go)
└── sendConfirmation (internal/notify/email.go)
向上追踪(Callers)
反向查找调用某个函数的所有函数:
谁调用了 chargeCard 函数?
CKG 返回:
chargeCard ← 被以下函数调用:
├── createTransaction (internal/payment/tx.go:45)
├── retryPayment (internal/payment/retry.go:23)
└── processRefund (internal/payment/refund.go:67)
多级追踪
CKG 支持多级调用链追踪,用于影响分析:
如果修改 chargeCard,最终会影响哪些 API 端点?
CKG 沿调用图向上追溯,直到到达入口函数:
chargeCard
← createTransaction
← ProcessOrder
← HandleOrderAPI (POST /api/orders)
← HandleBatchImport (POST /api/orders/batch)
← retryPayment
← HandleRetryPayment (POST /api/payments/retry)
← processRefund
← HandleRefundAPI (POST /api/refunds)
影响分析
变更影响范围
CKG 的影响分析帮助评估代码修改的波及范围:
我要修改 database.Connect 函数的签名,影响范围是多大?
AI 使用 CKG 分析:
- 直接调用者:直接调用
database.Connect的函数 - 间接调用者:通过调用链间接依赖的函数
- 影响的包:受波及的 Go 包列表
- 影响的测试:需要重新运行的测试文件
孤立函数检测
CKG 能发现没有被任何函数调用的"孤立函数":
项目中有哪些没被使用的函数?
这些函数可能是:
- 死代码(可以安全删除)
- 仅通过反射调用的函数(需要人工确认)
- 新添加但尚未集成的函数
D3 可视化交互
调用图视图
项目概览面板中的 D3 调用图提供交互式可视化:
节点
每个节点代表一个函数/方法:
- 大小:反映该函数的调用边数(扇入 + 扇出)
- 颜色:反映函数类型或热度
- 标签:函数名(缩放时显示完整限定名)
边
每条边代表一个调用关系:
- 实线:已解析的调用(明确知道目标)
- 虚线:未解析的调用(多候选或动态调用)
- 粗细:调用频率或重要程度
- 箭头:调用方向(从调用者指向被调用者)
交互操作
| 操作 | 效果 |
|---|---|
| 点击节点 | 高亮该函数的调用者和被调用者 |
| 双击节点 | 在编辑器中跳转到函数定义 |
| 右键节点 | 展开/折叠子调用图、查看详情 |
| 框选 | 选中一组节点查看子图 |
| 拖拽节点 | 手动调整布局位置 |
| 搜索 | 在调用图中高亮匹配的函数 |
筛选与聚焦
调用图支持多种筛选方式:
- 按包筛选:只显示特定包内的函数
- 按深度筛选:限制调用链的展示深度
- 按热度筛选:只显示高调用频率的函数
- 隐藏外部调用:隐藏标准库和第三方库的节点
跨包多态分派
INV-CKG-EDGE-01
CKG 调用图遵循 INV-CKG-EDGE-01 不变量——调用边解析不猜测 receiver 大小写。
提取规则
调用提取保留 selector 的原始拼写:
// 提取为 "svc.CreateUser"(保留小写 svc)
svc.CreateUser(ctx, req)
// 提取为 "fmt.Errorf"(保留原始拼写)
fmt.Errorf("error: %w", err)
// 提取为 "UserService.Create"(类型选择器保持原样)
UserService.Create(ctx, req)
不会将 svc.CreateUser 猜测为 Svc.CreateUser——这可能导致错误绑定到一个不相关的类型。
解析规则
对于包含 receiver 的调用(如 svc.CreateUser),解析遵循三级回退:
- 精确 parent 匹配:
symbols.parent = 'Svc'或'UserService' - 包路径匹配:
file_path LIKE '%/service/%' - 裸名单候选:只在全局恰好有一个
CreateUser时绑定
多候选时保持未解析,查询层通过 resolveDisambiguatedCallers 分组呈现(不静默丢弃,也不错误连边)。
接口实现追踪
CKG 支持追踪接口的实现关系:
哪些类型实现了 PaymentProcessor 接口?
CKG 通过分析方法签名匹配,找到所有满足接口契约的具体类型。
多态调用处理
对于接口方法调用,CKG 会展示所有可能的实现:
var processor PaymentProcessor
processor.Process(order) // CKG 会列出所有实现了 Process 的类型
使用示例
示例 1:理解新项目
帮我分析这个项目的核心调用链,从 HTTP handler 到数据库操作
AI 利用 CKG 生成调用链视图,帮助快速理解项目架构。
示例 2:安全审计
找出所有能够执行 SQL 查询的函数,以及它们的调用入口
AI 从底层 SQL 执行函数向上追踪,找出所有可能的入口路径,评估 SQL 注入风险。
示例 3:性能分析准备
找出调用链最深的几个函数,它们可能是性能瓶颈
CKG 计算每个函数的调用深度,返回深度最大的调用路径。
局限与注意事项
- 动态调用:反射、接口断言、高阶函数等动态调用可能无法被静态分析捕获
- 泛型:Go 泛型函数的调用边解析可能不完整
- CGo:C/Go 跨语言调用不在 CKG 的分析范围内
- 生成代码:
go generate生成的代码需要在生成后才能被 CKG 索引 - 索引延迟:新增文件后需要等待增量索引完成,调用图才会更新