ClDiff: Generating Concise Linked Code Differences
ClDiff: Generating Concise Linked Code Differences
ABSTRACT
分析和理解源代码变更在多种软件维护任务中非常重要。为此,已经提出了许多代码差异分析和代码变更摘要的方法。然而,对于某些任务(例如代码审查和软件合并),这些差异分析方法生成的代码变更表示过于细粒度,而这些摘要方法生成的代码变更表示又过于粗粒度。此外,它们没有考虑代码变更之间的关系。因此,生成的差异或摘要在某些软件维护任务中难以分析和理解。
本文提出了一种代码差异分析方法,称为 ClDiff,用于生成简明的关联代码差异,其粒度介于现有代码差异分析方法和代码变更摘要方法之间。ClDiff 的目标是生成更易于理解的代码差异。ClDiff 以变更前后的源代码文件为输入,包括三个步骤。首先,它通过修剪解析的抽象语法树中的未更改声明来预处理源代码文件。其次,它通过对语句级别及以上的细粒度代码差异进行分组,并描述每组中的高级别变更,生成简明的代码差异。最后,它根据五种预定义的链接方式将相关的简明代码差异链接起来。
通过对 12 个 Java 项目(74,387 次提交)的实验和对 10 名参与者的用户研究表明,ClDiff 在准确性、简洁性、性能和实用性方面表现良好。
INTRODUCTION
Background
分析和理解源代码变更在多种软件维护任务中具有重要意义,例如:
- 代码审查:开发者需要理解代码变更以提升软件质量。
- 软件合并:解决合并冲突时需要了解代码变更的细节。
- 回归测试:代码变更信息可用于高效选择需要重新运行的测试用例。
为此,已提出多种代码差异分析和代码变更摘要方法:
代码差异分析方法
- 文本基础方法:
- 忽略源代码的语法结构,仅生成文本差异。
- 不便于进一步分析和理解代码变更。
- 树基础方法:
- 基于抽象语法树(AST)生成细粒度的语法代码差异。
- 输出为“编辑脚本”(edit script),描述从变更前 AST 到变更后 AST 的转换。
- 局限性:
- 粒度过细、差异过于分散或过长,难以理解(尤其是大型代码变更)。
- 缺少代码变更之间的关系,例如方法签名变更与方法调用变更之间的关联。
代码变更摘要方法
- 生成自然语言摘要:
- 描述代码变更的动机(如提交信息、发布说明)。
- 适用于代码变更的文档化需求。
- 局限性:
- 粒度过于粗略,不适用于深入分析和理解代码变更(如代码审查或软件合并)。
问题总结
现有代码差异分析和代码变更摘要方法在粒度上存在两极化问题:
- 代码差异分析方法:过于细粒度。
- 代码变更摘要方法:过于粗粒度。 无法满足一些任务(如代码审查和软件合并)的需求。
Approach
为了解决现有代码差异分析方法的问题,ClDiff 提出了一个创新的代码差异分析方法,旨在生成简洁、连贯的代码差异表示,主要特点包括:
- 粒度适中:
- 粒度介于现有的代码差异分析方法和代码变更摘要方法之间。
- 既提供简洁的信息,又保留变更之间的关联性。
- 目标:
- 为代码审查和软件合并等任务提供更易理解的代码差异信息。
- 实现过程: ClDiff 的实现包括以下三步:
- 预处理:
- 对输入的变更前后源代码文件进行 AST(抽象语法树)解析。
- 剔除未发生变化的声明,以避免对未改变的 AST 元素进行不必要的分析。
- 生成简洁的代码差异:
- 利用 GumTree 工具生成细粒度的代码差异。
- 在语句级别或以上对相关的细粒度差异进行分组,并为每组描述高层次的变化。
- 将分散但相关的低层次变更聚合成更高层次的表示。
- 关联差异:
- 根据五种预定义的链接类型,将相关的简洁代码差异关联起来。
- 鼓励在某些任务中整体考虑这些关联的代码变化。
- 预处理:
- 优势:
- 生成结果更简洁、信息更集中。
- 保留变更之间的逻辑关系,提升在代码审查和软件合并中的可用性。
Evaluation
- 实验设计:
- 在 12 个开源 Java 项目(共计 74,387 次提交)上进行实验。
- 与 10 名参与者进行了用户研究,评估 ClDiff 在代码变更理解中的实用性。
- 实验结果:
- 高准确性:
- 生成的代码差异准确率为 **99%**。
- 差异链接准确率为 **98%**。
- 高效性:
- 与 GumTree 相比,ClDiff 在 48% 的提交中生成的编辑脚本短了 **80%**。
- 编辑时间减少了 **72%**。
- 更易理解:
- ClDiff 在代码变更理解任务中表现更好。
- 高准确性:
主要贡献
- 提出了一个名为 ClDiff 的代码差异分析方法,可生成简洁且关联的代码差异。
- 实现了适用于 Java 的 ClDiff,并提供了可视化功能。
- 通过实验和用户研究,验证了 ClDiff 在准确性、简洁性、性能和实用性上的优势。
PRELIMINARIES
AST(Abstract Syntax Tree)
- 定义:
- 源代码文件可以解析为一个抽象语法树(AST),它是一个有根、有序的标签树。
- 节点表示代码的结构元素(例如声明、表达式),部分节点还包括代码的实际值(例如变量名)。
- 节点类型层级:
- 根节点:
CompilationUnit
,子节点可以是BodyDeclaration
类型。 - 常见子类型:
TypeDeclaration
(类或接口声明)MethodDeclaration
(方法或构造函数声明)FieldDeclaration
(字段声明)Initializer
(初始化块)
- 声明可嵌套:
- 声明包含语句(如
IfStatement
)。 - 语句包含表达式(如
MethodInvocation
),粒度从声明到表达式逐步递减。
- 声明包含语句(如
- 根节点:
AST差异分析
- 工具:使用 GumTree 工具对比变更前后的两棵 AST。
- GumTree 通过两步生成编辑脚本:
- 节点映射:
- 使用启发式方法生成变更前后 AST 节点的映射集合:⟨节点 b,节点 a⟩。
- 生成编辑脚本:
- 根据节点映射,生成将 ASTb 转换为 ASTa 的编辑动作序列。
- 节点映射:
- GumTree 通过两步生成编辑脚本:
- 编辑动作:
- update:更新节点的值。
- add:新增节点。
- delete:删除叶节点。
- move:移动节点及其子节点。
示例
- AST 差异生成:
- GumTree 对两个 AST 的 8 个节点完成了映射。
- 生成了包含 18 个编辑动作的编辑脚本。
- 例如:
move(n5, n13, 2)
表示将方法调用节点n5
移动为变量声明片段n13
的第二个子节点。
总结
GumTree 通过高精度的 AST 节点映射和编辑脚本生成,为代码差异分析提供了详细且可操作的工具,但输出可能过于细粒度且复杂,不易直接用于高层任务(如代码审查或合并)。
MOTIVATION AND OVERVIEW
3.1 Motivation Example
示例背景
- 代码变更:
- 示例取自 spring-framework 项目的一次提交,其中涉及三个类的代码变更:
- 类①:
- 添加了一个
for
循环结构(第2-4行)。 - 在循环中调用了一个新声明的方法(第5行)。
- 添加了一个
- 类②:
- 重写了类①中新声明的方法(第13-14行)。
- 声明了一个字段(第6行)。
- 提取了一个变量(第7-8行)。
- 在新添加的
if
结构中使用了字段和变量(第9-11行)。
- 类③:
- 重写了类①的新方法(第29-30行)。
- 声明了一个字段(第15行)。
- 新声明的方法使用了该字段(第28行)。
- 新方法被相似的代码调用两次(第16-21行和第22-27行)。
- 类①:
- 示例取自 spring-framework 项目的一次提交,其中涉及三个类的代码变更:
- 部分变更细节:
- 示例特别关注 类②第7-12行 的代码变更,展示变更前后的部分抽象语法树(AST)。
- AST变更:
- 添加节点用绿色高亮。
- 移动节点用黄色高亮。
- 没有删除或更新操作。
存在的问题
- 现有工具的不足:
- 使用 GumTree 工具生成的编辑脚本包含了 17个新增节点 和 1个移动节点。
- 存在的问题:
- 分散性:某些与高层次语法元素(如变量声明语句)相关的编辑动作在脚本中分散,不便于理解。
- 分析困难:开发者和自动化工具更倾向于从高层次角度(如变量声明或语句级别)理解变更,而不是关注细粒度的树操作。
- 示例说明问题:
- 在代码审查中,开发者直观地看到变量声明的插入,而不会关注底层的细粒度编辑动作。
- 在软件合并中,**变量声明被视为一个整体,而不是分散的细粒度变更操作**。
ClDiff改进
- 高层次变更表示:
- ClDiff生成的编辑脚本示例(见Fig. 2(e))包括:
- 添加变量声明语句。
- 添加
if
语句。 - 更新表达式语句(添加简单名称)。
- 移动方法调用使其成为新变量声明的一部分。
- ClDiff生成的编辑脚本示例(见Fig. 2(e))包括:
- 代码变更关系:
- GumTree 未考虑代码变更之间的关系,而这些关系对代码分析非常重要。
- 示例:
- 新声明的方法(第5行):
- 被调用(第3行)。
- 被重写(第13-14行和第29-30行)。
- 类③中,第16-21行与第22-27行的代码几乎相同。
- 新声明的方法(第5行):
- 这些关系能:
- 加速代码审查。
- 提高合并冲突解决的准确性。
总结
ClDiff 通过高层次的编辑动作和建立代码变更关系的链接,解决了细粒度操作分散和关系缺失的问题,有助于:
- 简化代码变更的理解。
- 提高代码审查和合并任务的效率。
3.2 Approach Overview
ClDiff 是一个生成简洁代码差异并建立差异链接的方法,主要分为以下三个步骤:
- 预处理阶段:
- 目标:剔除未变更的代码,减少不必要的差异分析。
- 具体操作:
- 将源代码文件对(变更前和变更后)解析成抽象语法树(AST)。
- 使用哈希技术移除未变更的声明级别元素。
- 粒度:以声明(declaration)为单位,平衡可行性与可扩展性。
- 生成简洁代码差异:
- 目标:将分散的细粒度编辑动作汇总为更高层次的代码差异,便于理解。
- 具体操作:
- 使用 GumTree 获取每对 AST 的映射关系和编辑动作。
- 遍历编辑动作和修剪后的 AST,迭代地将与语句级或以上 AST 元素相关的编辑动作分组。
- 为每组编辑动作生成一个简洁代码差异,捕获其高层次变更。
- 粒度:选择语句级别(statement)作为差异粒度,更贴合开发者对代码变更的直观理解。
- 建立代码差异链接:
- 目标:考虑代码变更之间的因果关系,生成关联的代码差异。
- 具体操作:
- 针对生成的简洁代码差异,检查是否存在预定义的五种代码差异链接(如定义-使用链接 Def-Use link)。
输入与输出
- 输入:源代码文件对(变更前和变更后,如提交、补丁或版本更新)。
- 输出:简洁的代码差异及其链接,可通过 Web 工具进行可视化。
此方法旨在生成简洁、直观、关联性的代码差异表示,便于开发者进行代码审查和合并等任务。
METHODOLOGY
4.1 Pre-Processing
目标:通过修剪抽象语法树(AST)中的未变更声明节点,减少不必要的差异分析。
具体流程:
- 解析文件为 AST:
- 输入:代码变更前后的文件对 ⟨fb, fa⟩。
- 解析为 AST 对 ⟨ASTb, ASTa⟩,分别对应变更前和变更后的 AST。
- 计算哈希值并存储:
- 遍历
ASTb
中的声明节点(包括字段、枚举、方法、内部类和初始化器声明)。 - 为每个声明节点计算两个哈希值:
- 一个基于所属类的规范名称,用于区分内外部类的相同声明。
- 另一个基于声明代码(即节点为根的子树)内容。
- 将声明节点及其哈希值存入映射表,键为这两个哈希值。
- 遍历
- 修剪 AST:
- 遍历
ASTa
中的声明节点,计算同样的两个哈希值。 - 若某节点的两个哈希值在映射表中找到匹配,则从
ASTb
和ASTa
中同时修剪该节点及其所有子节点。
- 遍历
- 输出修剪后的 AST 对:
- 修剪后生成的 AST 对为 ⟨ASTb′, ASTa′⟩,仅保留变更部分。
注意事项:
- 注释和 Javadocs 不作为代码的一部分,在计算前即从 AST 中移除。
结果:生成更精简的 AST,减少分析中对未变更代码的干扰,提高效率。
4.2 Generating Concise Code Differences
ClDiff的目标是将细粒度的代码变更操作聚合成更高层次的简洁代码差异,方便分析和理解。生成过程分为三个阶段:
阶段 1:处理移动操作
- 特点:
move
操作会移动整棵以某节点为根的子树,直接反映了高层次的代码变更。 - 处理:
- 对于每个
move(n,p,i)
操作,将其生成简洁代码差异moveX(n,p,i)
,其中X
是节点n
的标签。 - 从原始编辑操作中移除该
move
操作。
- 对于每个
- 示例:
- 在图 2(d) 中的
move(n5,n13, 2)
,生成的简洁代码差异为moveMethodInvocation(n5,n13, 2)
。
- 在图 2(d) 中的
阶段 2:处理添加和删除操作
- 特点:
- 声明和语句分类:
- C1 类:包含嵌套声明或语句(如
IfStatement
,MethodDeclaration
),分为基础元素和组成元素。 - C2 类:不包含嵌套声明或语句(如
VariableDeclarationStatement
),仅有基础元素。
- C1 类:包含嵌套声明或语句(如
- 一个添加或删除操作通常影响整个或部分声明或语句。
- 声明和语句分类:
- 处理:
- 遍历
add
或delete
操作的目标节点,识别基础元素和组成元素。 - 若基础元素完全新增或删除,生成单个代码差异
addX(n,p,i)
或deleteX(n)
。 - 若部分新增或删除,生成代码差异
addXP(n,p,i)
或deleteXP(n)
。 - 删除对应的操作记录。
- 遍历
- 示例:
- 图 2 中
add(n10,n1, 1)
(变量声明)属于 C2 类,生成部分新增差异。 add(n19,n1, 2)
(if 语句)属于 C1 类,生成完整新增差异。
- 图 2 中
阶段 3:处理剩余的非声明和非语句操作
- 特点:剩余操作仅影响非声明和非语句的节点(如表达式节点),通常分布于同一声明或语句的不同部分。
- 处理:
- 将相关操作归类到其公共祖先节点(声明或语句节点)。
- 生成简洁代码差异
updateX(m) by Y
,其中X
是祖先节点的标签,Y
描述具体操作。
- 示例:
- 图 2(d) 中的
add(n33,n3, 2)
,归类到n30
的最近祖先节点,生成updateExpressionStatement(n2) by addSimpleName(n33,n3, 2)
。
- 图 2(d) 中的
总结
- 目标:通过聚合操作生成基于语句和声明的高层次代码差异,减少分析复杂度。
- 效果:简洁代码差异将细粒度操作整合为更具直观性和可理解性的高层次变更描述。
4.3 Linking Code Differences
ClDiff 的第三步是根据五种预定义链接在生成的简洁代码差异之间建立联系,反映代码变更的因果关系。这些链接设计旨在帮助更好地分析和理解代码变更。
五种代码差异链接
- Def-Use Link(定义-使用链接)
- 定义:如果变量、字段或方法的声明被
d1
改变,其使用被d2
改变,则d1 → d2
是 Def-Use 链接。 - 示例:变量声明的变化和它在同一范围内的使用之间的链接。
- 定义:如果变量、字段或方法的声明被
- Abstract-Method Link(抽象方法链接)
- 定义:如果抽象方法的声明被
d1
改变,其在子类中的实现被d2
改变,则d1 → d2
是 Abstract-Method 链接。 - 示例:抽象类方法声明和子类实现之间的链接。
- 定义:如果抽象方法的声明被
- Override-Method Link(方法覆盖链接)
- 定义:如果类中方法的声明被
d1
改变,其在子类中的覆盖实现可能被d2
改变,则d1 → d2
是 Override-Method 链接。 - 示例:父类方法变化与子类覆盖之间的链接。
- 定义:如果类中方法的声明被
- Implement-Method Link(接口方法实现链接)
- 定义:如果接口中方法的声明被
d1
改变,其在实现该接口的类中的实现必须被d2
改变,则d1 → d2
是 Implement-Method 链接。 - 示例:接口方法变化和实现类方法之间的链接。
- 定义:如果接口中方法的声明被
- Systematic-Change Link(系统化变更链接)
- 定义:如果两个代码差异
d1
和d2
相似,它们可能由系统化变更(如重构或重复修复)引起,则d1 ↔ d2
是 Systematic-Change 链接。 - 示例:类似的代码变更(如重复的变量名修改)之间的链接。
- 定义:如果两个代码差异
链接建立流程
- Def-Use Link:
- 检测涉及变量、字段或方法声明的代码差异
d1
。 - 在相同作用域内查找涉及这些变量、字段或方法使用的代码差异
d2
。 - 建立
d1 → d2
的 Def-Use 链接。
- 检测涉及变量、字段或方法声明的代码差异
- Abstract-Method、Override-Method 和 Implement-Method Links:
- 确定抽象方法、类方法或接口方法的声明变更
d1
。 - 查找与其签名匹配且在相关类或接口中实现的方法变更
d2
。 - 分别建立
d1 → d2
的 Abstract-Method、Override-Method 或 Implement-Method 链接。
- 确定抽象方法、类方法或接口方法的声明变更
- Systematic-Change Link:
- 对
delete
、add
和move
操作,检查节点标签和编辑操作的相似性(如子树的 bi-gram 相似度超过 0.8)。 - 对
update
操作,比较变更前后的代码相似性。 - 建立
d1 ↔ d2
的 Systematic-Change 链接。
- 对
设计特点
- 轻量级:链接建立基于简单的启发式方法,避免依赖重量级程序分析技术。
- 扩展性:当前五种链接已经显示出实际用途,可进一步扩展新类型的链接。
- 准确性:对代码变更案例的实验表明,ClDiff 可以正确建立所有相关链接。
示例
- Def-Use Link:
addVariableDeclarationStatementP
(第 8 行)和addIfStatement
(第 9-11 行)之间建立链接。 - Override-Method Link:
addMethodDeclaration
(第 5 行)和addMethodDeclaration
(第 14 行)之间建立链接。 - Systematic-Change Link:变量声明的重复变更(第 16, 19 行与第 22, 25 行)之间建立链接。
IMPLEMENTATION AND EVALUATION
5.1 Evaluation Setup
为评估 ClDiff 的效果,研究设计了以下实验和用户研究:
实验数据
- 数据来源:12 个高星级开源 Java 项目,来自 GitHub。
- 统计信息:
- 包括项目名称、创建日期、代码行数、Star 数量和提交次数。
- 过滤掉与代码无关(如配置文件变更)或仅涉及测试代码变更的提交,最终使用 74,387 个提交。
- 这些项目规模大、受欢迎,演化历史长,代码变更种类丰富多样。
- 对比工具:使用与 GumTree 原始研究相同的配置对 ClDiff 和 GumTree 进行对比。
用户研究
- 参与者:
- 招募了 10 名研究生,平均 Java 编程经验为 4 年,其中 1 人有 6 年经验。
- 所有参与者均与论文作者无关。
- 任务:
- 随机选择了 10 个提交,每次提交最多涉及 6 个 Java 源文件,控制了代码变更理解的复杂性,确保参与者专注。
研究问题(RQs)
- RQ1:ClDiff 生成的简洁代码差异和链接的准确性如何?(详见 5.2)
- RQ2:ClDiff 生成的简洁代码差异与 GumTree 相比的规模如何?(详见 5.3)
- RQ3:ClDiff 的性能开销与 GumTree 相比如何?(详见 5.4)
- RQ4:ClDiff 在代码变更理解中的实用性与 GumTree 相比如何?(详见 5.5)
5.2 Accuracy Evaluation (RQ1)
为了评估 ClDiff 生成的简明代码差异和关联链接的准确性,研究者从每个项目中随机选择了 10 次提交,手动分析了 ClDiff 的结果。结果显示,共分析了 1,456 个代码差异,准确率达 **99%**;分析了 512 个链接,准确率为 **98%**。
主要发现:
- 错误原因:
- 12 个不准确的代码差异均由 GumTree 映射不准确引起:
- 10 个由于缺失映射(两个应被映射的 AST 节点未被映射),导致生成删除和添加操作,而不是移动操作。
- 2 个由于错误映射(两个不应被映射的 AST 节点被映射),生成了与实际变化不符的代码差异。
- 9 个不准确的链接(均为 Def-Use 链接)由启发式方法导致,例如局部变量和类中字段同名时生成错误链接。
- 12 个不准确的代码差异均由 GumTree 映射不准确引起:
- 链接分布:
- 五种预定义链接中,除 Implement-Method 外,其余均有出现。
- Def-Use 链接占比约 **91%**。
尽管存在少量不准确情况,ClDiff 仍表现出较高的准确性,主要由于代码更改通常集中且分析策略简单。
总结:基于结果,ClDiff 在生成简明代码差异和关联链接上表现出极高的准确性,分别达到 **99% 和 98%**,验证了其高效性和可靠性。
5.3 Conciseness Evaluation (RQ2)
为评估 ClDiff 相较于 GumTree 生成的代码差异是否更简洁(即更短),研究者测量了每次提交的编辑脚本长度(脚本中操作的数量)。为确保公平,ClDiff 的更新操作以细粒度操作的数量进行计数。
主要发现:
- 简洁性结果:
- 对于 90% 的提交,ClDiff 生成的编辑脚本比 GumTree 更短。
- 对于 10% 的提交,ClDiff 与 GumTree 的脚本长度相同,这些情况的细粒度编辑操作无法在语句级或以上进行分组。
- 统计数据:
- 表 3 显示,ClDiff 显著缩短了编辑脚本的长度,其最大和中位长度都明显小于 GumTree。
- 图 6 显示了每次提交中 ClDiff 与 GumTree 脚本长度的比率,各项目的中位比率约为 0.2。
- 具体而言,48% 的提交中,ClDiff 缩短了超过 80% 的编辑脚本长度。这主要得益于高层次的添加和删除操作,能够描述一组细粒度的添加和删除操作。
- 组操作分析:
- 表 4 显示了添加和删除操作的最大和中位组大小:
- 最大组大小通常对应于整个方法声明的添加或删除。
- 添加和删除操作的中位组大小分别为 8 和 6。
- 表 4 显示了添加和删除操作的最大和中位组大小:
总结:根据表 3、表 4 和图 6 的结果,ClDiff 对 48% 的提交生成的编辑脚本比 GumTree 缩短了 80% 以上,显著提高了代码差异的简洁性。
5.4 Performance Evaluation (RQ3)
为了评估性能,表 5 对比了 ClDiff 和 GumTree 在处理每次提交的代码变更文件时的平均时间开销(以毫秒为单位),并详细列出了 ClDiff 各步骤的性能开销。
主要发现:
- 总体性能:
- ClDiff 比 GumTree 用时短 **72%**。
- 原因在于 ClDiff 在应用 GumTree 生成细粒度代码差异之前,会先修剪 AST 中未变更的声明,而 GumTree 直接处理原始 AST。
- 步骤性能:
- ClDiff 第二步是最耗时的步骤,占总时间的 **92%**。
- 第三步是耗时最少的步骤,每次提交仅需 0.42 毫秒,这得益于基于启发式的方法建立链接,同时也保持了高准确性(见 5.2 节讨论)。
- 平均来看,ClDiff 每次提交耗时 188.51 毫秒。
总结:根据表 5 的结果,ClDiff 比 GumTree 的运行时间缩短了 **72%**,验证了其高效的性能表现。
5.5 Usefulness Evaluation (RQ4)
为了评估 ClDiff 的实用性,研究者进行了一项涉及 10 名参与者的人体实验,帮助参与者理解 10 次提交的代码变化(完成 10 个任务)。实验对 ClDiff 和 GumTree 的使用进行了对比,以下是关键结果:
实验设计:
- 参与者被随机分为两组:
- 第一组:前五个任务使用 ClDiff,后五个任务使用 GumTree。
- 第二组:顺序相反。
- 每个参与者需回答每个任务中的问题,写下其对代码变化的总结,并记录完成任务的时间。
- 任务结束后填写包含四个问题的问卷:
- Q1: ClDiff 的表现如何?(选项:好、中立、不好)
- Q2: GumTree 的表现如何?(选项:好、中立、不好)
- Q3: ClDiff 和 GumTree 哪个更有帮助?(选项:ClDiff、GumTree、无差异)
- Q4: ClDiff 的代码差异和链接是否有帮助?(选项:两者都有、仅代码差异、仅链接、两者都没有)
结果分析:
- 评分与时间:
- 评分:总分满分为 4(回答问题和总结各占 2 分)。ClDiff 的总分为 34.0,显著高于 GumTree 的 29.6。
- 时间:完成 10 个任务的平均时间,ClDiff 为 1,539 秒,GumTree 为 1,865 秒。但时间差异无统计显著性。
- 具体表现:
- 4 个任务中 ClDiff 用时更多但评分更高。
- 2 个任务中 ClDiff 用时更少但评分更低。
- 4 个任务中 ClDiff 用时更少且评分更高。
- 问卷结果(表 6):
- Q1:所有参与者都认为 ClDiff 表现良好。
- Q3:大多数参与者认为 ClDiff 比 GumTree 更有帮助。
- Q4:7 名参与者认为 简明代码差异 有帮助,8 名认为 链接 有帮助。
总结:
根据实验结果(图 7 和表 6),可以明确回答 RQ4:
- ClDiff 在理解代码变化方面对所有参与者都更有用。
- 简明代码差异和关联链接对大多数参与者都很有帮助。
5.6 Discussion
1. 威胁因素(Threats):
- 样本规模:实验的准确性分析仅使用了 120 次提交,规模不够大。由于手动分析需要理解映射、编辑脚本、AST 对及实际代码变更,非常耗时,因此参考文献[24]的研究方法,选取了 12 个不同项目的提交作为代表性代码变化。
- 参与者背景:人体实验的参与者是 10 名拥有至少 3 年编程经验的研究生,而非工业领域的开发者。未来需要进一步在人群中评估 ClDiff 的实用性。
2. 限制(Limitations):
- 链接建立的启发式特性:特别是在 Def-Use 链接中(见 5.2 节),可能会影响链接的准确性。计划通过数据流分析来提升链接准确性。
- 有限的链接种类:目前仅支持 5 种链接,计划分析每种链接的实际效用,扩展现有链接能力,并支持更多种类以实现更紧凑且有用的链接集合。
3. 应用(Applications):
- 逻辑耦合检测:通过分析项目的演化历史并将代码差异串联,可以在更细粒度上检测逻辑耦合。
- 提交分类:利用每次提交的代码差异类型统计特征,可通过机器学习技术将提交分类为修复错误、重构或升级。
- 语义变化分析:进一步为生成的代码差异附加语义理解,可用于表征或量化安全补丁或兼容性分析中的语义变化。
- 性能回归分析:结合性能分析技术,ClDiff 可用于分析性能回归并定位其根本原因。
总结:尽管存在一定的威胁和限制,ClDiff 在代码差异分析和理解中展现了广泛的潜在应用价值,特别是在逻辑耦合、提交分类、语义分析和性能回归方面。