菜单

诊断测试

相关源文件

诊断测试是 Kotlin 编译器测试基础设施的关键组成部分,旨在验证编译器是否能正确识别和报告错误、警告和其他诊断消息。本文档介绍了诊断测试框架、诊断测试的组织和执行方式,以及它们如何帮助确保编译器的前端组件正常工作。

有关验证编译器后端正确性的代码生成测试,请参阅代码生成测试

目的和概述

诊断测试可验证编译器在分析 Kotlin 代码时检测错误并生成适当诊断消息的能力。这些测试确保:

  1. 语法错误得到正确识别
  2. 类型检查执行正确
  3. 语义规则得到恰当执行
  4. 错误消息清晰准确
  5. 语言功能实现按预期工作

诊断测试框架同时支持经典的 Kotlin 编译器前端 (K1) 和新的前端 IR (FIR) 实现,以及各种解析器实现 (PSI 和 Light Tree)。

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java18-25 compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsWithLatestLanguageVersionTestGenerated.java18-25 compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/PhasedJvmDiagnosticPsiTestGenerated.java18-25

诊断测试架构

诊断测试系统由几个关键组件组成:

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsWithLatestLanguageVersionTestGenerated.java compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/PhasedJvmDiagnosticPsiTestGenerated.java analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLDiagnosticsFirTestGenerated.java compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeWithLatestLanguageVersionTestGenerated.java

测试生成

诊断测试由测试生成器自动生成,该生成器:

  1. 扫描测试数据目录中的测试文件
  2. 为每个测试文件生成包含相应测试方法的测试运行器类
  3. 根据文件指令设置适当的测试配置

生成的测试类继承自提供核心测试功能的抽象基类。

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java18-19

测试执行流程

当诊断测试执行时,它会经历以下阶段:

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java24-28

诊断测试类型

Kotlin 编译器测试基础设施包含几种不同实现细节的诊断测试:

基于前端实现

  1. K1(遗留)诊断测试:使用原始 Kotlin 编译器前端
  2. FIR(前端 IR)诊断测试:使用新的 FIR 实现

基于解析树表示

  1. 基于 PSI 的测试:使用 IntelliJ 的程序结构接口 (PSI) 表示
  2. 基于 Light Tree 的测试:使用轻量级解析树表示以提高性能

基于测试配置

  1. 标准诊断测试:具有默认配置的基础测试
  2. 分阶段诊断测试:执行编译器特定阶段的测试
  3. 语言版本特定测试:使用特定语言版本运行的测试
  4. 特殊功能测试:针对特定语言功能(例如,多平台)的测试

这种测试类型矩阵会产生特定的测试运行器,例如:

测试类型目的
DiagnosticTest使用 K1 前端的基础诊断测试
FirLightTreeDiagnosticsTest使用 FIR 和 Light Tree 表示的测试
PhasedJvmDiagnosticPsiTest使用 PSI 的分阶段诊断
LLDiagnosticsFirTest使用 FIR 的低级 API 诊断
FirOldFrontendMPPDiagnosticsWithLightTree使用 FIR 和 Light Tree 的多平台项目测试

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsWithLatestLanguageVersionTestGenerated.java analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLDiagnosticsFe10TestGenerated.java

诊断测试文件

诊断测试文件是带有注释中特殊指令的 Kotlin 源代码文件。这些文件位于 /compiler/testData/diagnostics/tests/ 目录及其子目录中。

测试文件结构

典型的诊断测试文件结构如下:

通用测试指令

指令目的
RUN_PIPELINE_TILL指定要运行的编译器阶段(例如,FRONTEND)
DIAGNOSTICS启用 (+) 或禁用 (-) 特定诊断检查
LANGUAGE启用 (+) 或禁用 (-) 特定语言功能
WITH_EXTRA_CHECKERS启用额外的诊断检查器
FILE在多文件测试中定义一个文件
TARGET_BACKEND为代码生成测试指定目标后端

标记预期诊断

预期诊断使用 <!DIAGNOSTIC_TYPE!> 语法在测试文件中进行内联标记。

对于需要检查同一代码片段的多个诊断的测试,它们可以用逗号分隔。

来源:compiler/testData/diagnostics/tests/inline/exposingPrivateTypeInInternal.fir.kt1-14 compiler/testData/diagnostics/tests/inline/exposingPrivateTypeInPublic.fir.kt1-14

测试组织

诊断测试按功能领域和语言结构组织在 /compiler/testData/diagnostics/tests/ 目录中。这种结构有助于查找相关测试并确保全面覆盖。

主要测试类别

  • 基本语法和语义测试
  • 类型检查和类型推断测试
  • 特定功能测试(例如,内联函数、多平台)
  • 特定语言版本测试
  • 特殊情况测试(例如,错误恢复)

测试文件和命名约定

测试文件遵循指示其用途的命名约定:

  • 基本功能测试:根据语言功能命名(例如,Casts.ktDiamondFunction.kt
  • 问题特定测试:使用问题编号命名(例如,kt41984.kt
  • 边缘情况:描述性命名(例如,invalidAnnotation.kt

对于特定于 FIR 的变体,测试文件可能具有 .fir.kt 扩展名以指示特定于 FIR 的行为。

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java26-73

运行诊断测试

诊断测试系统与 JUnit 测试框架集成,允许使用标准的测试运行器执行测试。

以编程方式运行测试

从 IDE 运行测试

可以通过以下方式直接从 IDE 运行诊断测试:

  1. 导航到测试类(例如,DiagnosticTestGenerated
  2. 右键单击类或特定的测试方法
  3. 选择“运行”或“调试”

从命令行运行测试

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java26-28

开发新的诊断测试

在开发新语言功能或修复 bug 时,添加新的诊断测试对于验证编译器行为至关重要。

创建新诊断测试的步骤

  1. /compiler/testData/diagnostics/tests/ 的适当子目录中创建新的 .kt 文件。
  2. 在文件顶部添加必要的测试指令。
  3. 编写 Kotlin 代码以演示要测试的行为。
  4. 使用 <!DIAGNOSTIC_TYPE!> 语法标记预期诊断。
  5. 运行测试生成器以创建更新的测试运行器。
  6. 运行测试以验证预期行为。

示例:创建基本诊断测试

最佳实践

  1. 使测试保持最小化并专注于特定行为。
  2. 包含解释预期行为的注释。
  3. 测试正面和负面案例。
  4. 对于复杂功能,彻底测试边缘情况。
  5. 在相关时包含不同语言版本的测试。

来源:compiler/testData/diagnostics/tests/inline/exposingPrivateTypeInInternal.fir.kt compiler/testData/diagnostics/tests/inline/exposingPrivateTypeInPublic.fir.kt

诊断系统集成

诊断测试系统与 Kotlin 编译器的诊断基础设施紧密集成。

这种集成确保诊断测试能够准确地反映编译器的行为,同时在测试和生产环境中使用相同的诊断基础设施。

来源:compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsWithLatestLanguageVersionTestGenerated.java

结论

诊断测试系统是 Kotlin 编译器质量保证基础设施的关键组成部分。它确保:

  1. 编译器能正确识别错误并发出相应的诊断信息。
  2. 对编译器的更改不会无意中破坏现有的错误报告。
  3. 新的语言功能已实现,并带有正确的错误检测。
  4. 错误消息清晰且有帮助。

通过维护一套全面的诊断测试,Kotlin 团队可以自信地发展语言,同时确保编译器保持可靠和用户友好。