> For the complete documentation index, see [llms.txt](https://code-visualization.shawnxie.top/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://code-visualization.shawnxie.top/di-san-pian-cheng-xu-fen-xi-yu-dai-ma-tu-pu/static-analysis.md).

# 静态分析：不运行代码时能知道什么

静态分析是在不运行程序的情况下，从源码、配置、依赖和构建信息中提取事实。它是代码可视化的数据基础之一，也是 IDE、质量平台、安全扫描、架构治理和影响面分析的核心能力。

静态分析的优势是覆盖面广。只要代码在仓库里，就可以被扫描、解析和建模。它不依赖线上流量，也不依赖测试是否执行到某条路径。但它的局限也很明显：它看到的是“代码可能如何运行”，而不一定是“代码实际如何运行”。

本章重点回答：不运行代码时，我们能知道什么，不能知道什么。

## 静态分析的输入

静态分析的输入不只是源码文件。一个真实项目通常还需要以下信息：

* 源码：类、函数、变量、注解、装饰器、配置读取。
* 构建文件：Maven、Gradle、npm、Go module、Cargo 等依赖声明。
* 配置文件：路由、数据库、消息队列、功能开关、框架配置。
* 生成代码：IDL、ORM、API client、protobuf、OpenAPI 生成物。
* 测试代码：测试类、测试用例、测试夹具。
* 静态规则：架构分层、禁止依赖、命名约定、安全规则。

如果只分析源码而忽略配置，很多关系会丢失。比如 Spring 的 Bean 绑定、路由配置、数据库连接、消息 topic，经常不只存在于普通方法调用里。

## 文件、模块与依赖关系

最基础的静态事实是文件和模块结构。工具可以从目录、包名、构建配置和导入语句中提取依赖关系。

依赖图可以回答：

* 模块之间如何依赖。
* 是否存在循环依赖。
* 上层模块是否直接依赖底层实现。
* 哪些模块被大量模块引用。
* 哪些模块处于边界位置。

依赖图通常比调用图粒度更粗。它适合做架构治理、模块拆分和遗留系统理解。比如一个后台报表模块直接依赖核心交易模块，可能说明分层不清；多个业务模块共同依赖一个工具包，可能说明工具包处于高风险公共路径。

## 符号、引用与调用关系

在更细粒度上，静态分析可以提取符号定义、引用和调用关系。

符号定义告诉我们类、方法、字段在哪里声明。引用关系告诉我们这些符号在哪里被使用。调用关系进一步描述一个函数或方法调用了哪些目标方法。

调用图可以回答：

* 谁调用了目标方法。
* 目标方法会调用哪些下游方法。
* 某个业务入口可能经过哪些代码。
* 修改底层方法可能影响哪些上游入口。

调用图是影响面分析的核心输入。但调用图很少是完全准确的。多态、反射、动态代理、依赖注入和运行时配置都会让静态调用解析变得困难。因此，调用图中的边最好带上来源和置信度：确定解析、候选解析、框架规则推断、运行时确认。

## 继承、实现与类型层次

面向对象项目中，类型层次本身就是一种重要结构。继承图和接口实现图可以帮助理解扩展点、插件机制和多态分派。

类型关系可以回答：

* 一个接口有哪些实现类。
* 一个抽象类有哪些子类。
* 某个方法重写了哪个父类或接口方法。
* 修改接口签名会影响哪些实现。

这类关系在 AI 时代尤其关键。Agent 修改一个接口方法时，不能只看接口文件，还要检查实现类、调用方和测试。如果缺少类型层次，AI 很容易做出局部修改，却遗漏完整语义链路。

## 复杂度和代码气味

静态分析还可以生成质量指标，例如：

* 圈复杂度。
* 函数长度。
* 嵌套深度。
* 重复代码。
* 参数数量。
* 依赖数量。
* 不合理异常处理。
* 潜在空指针或资源泄漏。

这些指标不应该被机械理解。高复杂度不一定立刻有问题，低复杂度也不代表没有风险。它们更适合作为排序和提醒信号。

当复杂度与变更频率、测试覆盖、线上错误结合时，价值会更高。一个高复杂度、频繁变更、缺少测试、又在核心链路上的文件，才是真正应该优先关注的风险点。

## 架构规则检查

架构治理可以被表达为一组静态规则：

* 表现层不能直接访问数据库。
* 业务域之间只能通过公开接口通信。
* 基础设施模块不能依赖业务模块。
* 某些包不能跨层调用。
* 核心模块不允许依赖实验性组件。

静态分析可以持续检查这些规则，并把违规依赖转化为报告或图形。相比人工 Review，规则化检查更稳定，也更适合进入 CI。

在 AI 生成代码后，架构规则检查会更重要。Agent 可能为了完成任务快速引入一个依赖，但它不一定知道这个依赖违反长期架构约束。静态分析可以作为边界守卫。

## 安全规则和数据流入口

很多安全扫描也从静态分析开始。工具会识别危险 API、敏感函数、输入入口、权限校验和数据流路径。

例如：

* 用户输入是否进入 SQL 拼接。
* 文件路径是否来自未校验参数。
* 敏感字段是否被记录到日志。
* 权限判断是否覆盖关键操作。
* 不安全反序列化是否可达。

完整安全分析通常需要数据流支持，但静态层至少可以识别 Source、Sink 和候选路径，为后续 DFG 或污点分析提供基础。

## 静态分析的误差来源

静态分析并不等于真相。常见误差包括：

* 反射和动态加载。
* 依赖注入和运行时绑定。
* AOP、代理和拦截器。
* 动态语言特性。
* 生成代码缺失。
* 构建 Profile 或配置环境不同。
* 测试代码和生产代码路径差异。

因此，静态分析结果应该表达不确定性。不要把候选调用说成确定调用，不要把语法匹配说成真实漏洞，不要把模块依赖误解为业务依赖。

## 和动态分析的边界

静态分析适合全量覆盖，动态分析适合确认真实执行。二者应该互补：

* 静态分析发现所有可能路径。
* 动态分析确认真实路径和频率。
* 静态分析发现候选风险。
* 动态分析提供运行时证据。
* 静态分析支持修改前评估。
* 动态分析支持修改后验证。

后续代码图谱应该同时容纳两类事实。比如一条调用边可以来自静态解析，也可以被 Trace 证实；一个方法可以静态上可达，但运行时很少被访问；一个接口可以有多个实现，但生产环境只启用了其中一个。

## 小结

静态分析让我们在不运行代码的情况下获得大量软件事实：结构、依赖、调用、类型、质量、安全和架构规则。它是代码可视化的基础层，也是 AI Agent 查询代码库的重要入口。

但静态分析必须承认自己的边界。它描述的是可能性，不总是现实。下一章会进入动态分析，讨论运行起来之后才能看到的事实。
