基础篇

F30|代码不是普通文档:语法树、调用关系与版本

基础篇约 2.7 千字1 组分步图解

从具体对象与小例子开始,理解计算过程与适用条件。

本章目录
动态图解 · F30

代码检索不能只靠“语义相近”

grep、AST、LSP、调用图各自看到什么?

同一个函数名可能出现在注释、字符串、不同模块的同名函数中。文本搜索先定位线索,语法分析区分结构,语言服务帮助解析具体符号,运行和测试才告诉你这条路径是否真的触发。它们像不同视角的检查,不是越多越好地全部跑一遍。

AST按语言语法组织的代码结构树。
LSP让编辑器查询符号、引用和诊断等信息的协议。
调用图以函数为节点、可能的调用为边的关系图。

输入与适用条件:以foo为教学符号,不指向用户仓库。调用图可能受动态调用、条件编译与运行配置影响。

1 / 6 · 文本搜索

第1步,共6步

窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。

代码检索不能只靠“语义相近”:文本搜索文本搜索找到字符串 foo语法结构AST区分函数定义与注释符号解析LSP确定引用实际绑定哪个定义调用关系查谁调用它、受什么条件控制版本与环境核对当前源码和依赖配置测试与运行用执行证据验证候选原因教学流程;高亮表示当前步骤,不表示真实耗时比例。

搜索命中不代表命中的是实际执行的函数。

本步状态由图形与说明共同给出。

默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。

为什么需要

用代码自身结构减少无关阅读与误定位。

怎样实现

从最便宜且准确的路径/符号检索开始,证据不足再扩大。

可能的代价与误解

一上来全库语义扫描可能漏精确符号,同时浪费上下文。

如何判断是否做对

对已知缺陷记录实际入口、定义、调用者、版本和失败测试是否一致。

先从一行代码看不同观察层

total = price * count是一串文本,也有结构:左边是赋值目标,右边是乘法表达式,price和count是变量。文本搜索能找到字符,语法分析能识别关系,两者提供的信息不同。

AST(抽象语法树)把代码组织成语法节点,例如赋值节点下面有乘法节点。符号symbol是函数、变量、类型等语言实体;同名字符串可能指向不同符号,所以全文匹配不总能替代语言理解。

LSP不是自动懂业务的模型

LSP(语言服务器协议)让编辑器向语言服务请求“定义在哪、有哪些引用、是否存在类型错误”等能力。它是通信协议,实际分析由对应语言服务完成。

“找到函数定义”与“知道生产环境传进了什么数据”仍不同。类型系统可以排除部分错误,但某个配置导致的运行问题可能需要日志、测试或真实输入。

图、节点和边怎样读

图graph由节点和边组成。函数是节点,调用关系是从调用方到被调用方的有向边。调用图帮助理解影响范围。

DAG(有向无环图)是没有沿箭头绕回自己的依赖图。例如A完成后才能B,A完成后也可C,最后D依赖B和C。它适合安排任务先后,而不是神经网络独有概念。

“依赖闭包”就是沿必要边追到足以解释当前问题的关联;“静态调用图”由源码推断,“动态调用关系”来自实际执行。动态分派、反射和插件会让静态图不完全等同真实路径。

Git的几种状态为什么都重要

commit(提交)固定一份历史快照;分支branch是指向某条提交演进的引用;工作树working tree是当前检出的文件及未提交修改;编辑器里未保存的文本又可能还没进入磁盘。

Agent读取提交A的索引,但修改的是带本地更改的工作树,就可能依据过期内容生成补丁。相同路径不代表相同版本。

diff或patch描述修改前后哪些内容改变。补丁能应用,只说明某种文本或结构匹配成功,不证明业务语义正确。

一次Bug定位的具体顺序

先用失败输入或报错建立可重复现象;定位相关入口与函数;阅读调用者如何传参数;检查配置、版本和边界条件;提出候选原因;用最小实验区分;修改后对当前版本验证。

例如“目录exclude不生效”,只读字符串比较函数可能看不出问题。需要知道路径是否已规范化、目录规则是否应匹配分隔符边界、Windows和Unix分隔是否统一。这些是业务与环境前提,不是模型多读几个随机文件就能自动补足。

“懂业务”需要什么资料

业务对象、核心不变量、术语、外部依赖、异常处理和验收例子,比大量历史闲聊更直接。不变量invariant是在允许操作过程中应始终成立的条件,例如“不能修改公共签名”或“已提交记录必须可追溯”。

这些规则可以存于项目文档和测试中,通过按需检索提供。把全部业务知识塞进一份无限增长的Prompt,会带来检索和维护问题。

编译、测试和运行证据怎样配合

编译检查部分语法与类型约束;单元测试检查特定局部行为;集成测试检查组件配合;生产日志反映实际环境事件。每种证据都有范围,不能用其中一个覆盖全部。

所以代码检索的最终目标不是“找到最多文件”,而是为当前修改获得最小充分、版本正确、可验证的依据。

运行示例 · F30

同一个函数名,文本命中为何不等于真实调用

输入与条件。文件a.py定义 build();b.py有注释“build可能失败”;c.py写 from a import build; build()。

步骤与对象内部动作中间结果 / 新状态
1文本搜索搜索build三个文件都可命中,注释也算。
2语法分析区分函数定义、注释与调用节点b.py没有执行build的调用。
3绑定分析解析c.py的导入关系该调用指向a.py中的build,不是任意同名函数。
4核对运行入口确认c.py是否在本次任务执行路径上静态有调用也不意味着本次真的运行。
5查看运行证据测试或日志记录调用发生与版本与静态结构合起来确定问题位置。

把过程连起来。grep找字面,AST找结构,语言服务找部分语义绑定,运行证据说明某次实际发生了什么。

边界与反例。这是小型静态Python示例;动态导入、反射、生成代码和不完整项目会增加不确定性。

依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。

课后自检

自检 1

AST能直接告诉你真实生产数据库返回了什么吗?

查看参考答案收起参考答案
通常不能。AST表达代码结构,真实运行输入需要日志、测试或其他环境证据。
自检 2

索引基于commit A,工作树已有未提交修改,读到内容一定一致吗?

查看参考答案收起参考答案
不一定。需绑定版本并补充工作树或编辑器差异,防止基于旧证据覆盖新修改。

机制依据:[TREESITTER][LSP][SWEBENCH]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。

记录保存在本机浏览器。

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。