静态分析代码质量开发工具【免费下载链接】error-proneCatch common Java mistakes as compile-time errors项目地址https://gitcode.com/gh_mirrors/er/error-prone点击查看免费下载导读UnnecessarySemicolon是 Google Error Prone 静态分析工具内置的一项编译期检查BugChecker用于识别 Java 语言规范允许、但对代码语义毫无作用的多余分号。本文以 docs/bugpattern/UnnecessarySemicolon.md 文档为骨架结合该检查在 core 模块 中的真实实现与 单元测试详细讲解分号多余出现的三类位置、{}与;在空控制流语句中的取舍以及检查器如何做到“该删的删、该留的留”。读完本文你将掌握该检查的判定规则、边界处理逻辑并能在自己的项目中正确使用与配置它。Java 语言中“合法但多余”的分号Java 是一门分号“基本必需”的语言——语句以分号结尾、声明以分号结尾。但语言规范在几处特殊位置允许出现不参与任何语法结构的分号。Error Prone 认为这些分号“没有任何用途且会造成干扰”原文表述为serve no purpose and can be distracting于是提供了UnnecessarySemicolon检查来清理它们。该检查的官方摘要summary定义在源码的BugPattern注解中一句话概括了全部判定逻辑Unnecessary semicolons should be omitted. For empty block statements, prefer {}.—— UnnecessarySemicolon.java即无意义的分号应当删除对空的块语句应优先写成{}。三类“合法但多余”的分号位置原文档依照 Java 语言规范JLS将多余分号归为三类1. 编译单元顶层文件顶层依据 JLS §7.6class Test { };在类的顶层大括号之后、编译单元级别出现的独立;。JLS 对此的解释是编译单元中类/接口声明层面的多余;记号不影响编译单元的语义。这种“游离分号”被语言规范保留纯粹是对 C 程序员习惯的让步——他们习惯在类声明后加;。规范明确建议新写的 Java 代码不要使用它。2. 类声明内部依据 JLS §8.1.7class Test { ; }类体中不关联任何成员声明字段、方法、初始化块等的独立;。3. 作为空语句empty statement依据 JLS §14.6class Test { void f() { ; } }空语句本身就是 Java 语法的一部分;就是一个什么也不做的语句但出现在方法体块中时通常是无意遗留。空控制流语句的写法规范{}优于;当控制流语句如if、while需要一个语句作为其循环体/分支体而该语句为空时文档明确给出了推荐写法优先使用空块{}而不是裸分号;。推荐写法while (true) {}不推荐的写法while (true) ;理由很直观while (true) ;中的分号极易被误读为循环体之外的语句或与相邻代码混淆可读性远差于语义清晰的{}。源码实现一个检查器两条匹配路径该检查的实现位于 core/src/main/java/com/google/errorprone/bugpatterns/UnnecessarySemicolon.java是一个同时实现两个匹配接口的 BugCheckerpublic final class UnnecessarySemicolon extends BugChecker implements EmptyStatementTreeMatcher, ClassTreeMatcher {在 BugChecker.java 中这两个接口分别对应EmptyStatementTreeMatcher.matchEmptyStatement——处理空语句节点即单分号语句ClassTreeMatcher.matchClass——处理类声明节点用于扫描类体内游离的分号。该检查的severity为WARNING源码 L19 与 L43即在默认配置下以警告而非编译错误的形式报告同时提供可一键应用的修复SuggestedFix。Error Prone 会把每条诊断链接到自动生成的文档页docs/bugpattern/UnnecessarySemicolon.md 正是这一机制产出的说明页。路径一空语句的智能区分matchEmptyStatement的逻辑并不简单它需要区分“块内的空语句”与“作为控制流语句体的空语句”if (state.getPath().getParentPath().getLeaf() instanceof StatementTree parent) { switch (parent.getKind()) { case BLOCK, CASE - {} default - { // Dont remove empty statements that arent inside blocks, to avoid rewriting e.g.: // while (true) ; return describeMatch(tree, SuggestedFix.replace(tree, {})); } } } return describeMatch( tree, deleteWithLeadingWhitespace(getStartPosition(tree), state.getEndPosition(tree), state));逐条解读这段代码的三种行为父节点是块BLOCK或case分支空语句只是块里多余的空行占位直接删除。父节点是if、while、for等控制流语句;是这条控制流语句的“空语句体”此时不能简单删掉删掉会破坏语法而是将其替换为{}。源码注释明确给出反例while (true) ;——注释中还写着Dont remove empty statements that arent inside blocks。其他情况直接删除且使用deleteWithLeadingWhitespace连分号前的空白一并清除。路径二类体内游离分号的扫描matchClass负责清理类体以及文件顶层因为文件顶层游离分号也会体现在类的相邻成员之间中不属于任何成员的;。它的做法是遍历类的每个成员取“当前成员结束位置”到“下一个成员开始位置”之间的所有 token凡是SEMI分号类型的 token 就报告并删除ImmutableListErrorProneToken tokens state.getOffsetTokens(from, to); for (ErrorProneToken token : tokens) { if (token.kind() Tokens.TokenKind.SEMI) { state.reportMatch( describeMatch( current, deleteWithLeadingWhitespace(token.pos(), token.endPos(), state))); } }这段扫描逻辑埋了不少边界处理是理解该检查行为的关键多变量声明如int x, y;源码注释说明这种声明在 AST 中会被去糖desugar成两个存在位置重叠的声明节点导致from to因此遇到这种情况直接continue跳过避免误报L95-L99。枚举常量列表末尾的分号enum E { ONE; }中最后一个常量后的;是合法的枚举语法分隔符予以容忍但如果枚举常量列表之后跟着其他声明如enum F { ONE; int x; }这个分号是必需的语法分隔同样不能删除L74-L91。隐式成员无显式源码通过hasExplicitSource过滤掉没有显式源代码的合成成员。修复细节连空白一起删除deleteWithLeadingWhitespace使用 Guava 的CharMatcher.whitespace()从分号位置向前回退把分号连同其前导空白一起替换为空串。源码注释给出了原因google-java-format partial formatting doesnt handle deletion-only changes, so remove leading whitespace as well.即 google-java-format 的局部格式化不处理纯删除型改动因此在删除分号的同时必须把行首缩进也清掉才能保证// refaster与-XepPatchChecks等自动修复流程得到干净的结果L112-L122。测试用例判定规则的完整证据该检查的行为由 UnnecessarySemicolonTest.java 通过BugCheckerRefactoringTestHelper逐条验证覆盖了“正例应修复”“反例应保留”与“边界”三类场景。正例类体与方法体内的游离分号positive测试输入包含类体、方法体内以及文件顶层的多个;期望输出中全部被删除且连带空行一起清理// 输入 class Test { private final Runnable r () - {}; ; void f() { System.err.println(); ; } ; } ; // 期望输出 class Test { private final Runnable r () - {}; void f() { System.err.println(); } }正例switch 分支内的空语句positiveSwitch测试验证case 1: ; break;这种写法中case下的分号会被删除switch (i) { case 1: ; break; case 2: break; default: ; }正例空控制流语句被改写为{}emptyBlock测试同时验证了两个方向for (; ; ) {}中的分号属于 for 循环头部语法;是 for 的组成部分保持不变而while (true) ;这种空语句体则被改写为while (true) {}// 输入 void g() { while (true) ; } // 期望输出 void g() { while (true) {} }注意这与源码matchEmptyStatement中while分支的处理一一对应——Error Prone 只报告“真的多余”的分号for (; ; )里的分号是语法必需的组成部分不在此列。反例枚举相关的分号一律保留negativeEnum测试用expectUnchanged()声明三类枚举写法必须原样不动enum E { ONE, TWO; // 枚举常量列表结束的分号保留 } enum F { ONE, TWO; int x; // 枚举常量与成员声明之间的分号保留必需 } enum G { ONE // 无分号本来就没有无需处理 }这印证了源码中isEnumConstant相关的两处特判逻辑。边界多变量声明不误报multiVariable测试覆盖了源码中from to的跳过逻辑——int x, y;被去糖为两个声明节点检查器必须跳过以免重复/误报同时真正多余的;仍被删除// 输入 int x, y; ; int z; // 期望输出 int x, y; int z;在实际项目中启用与使用Error Prone 是一个在编译期运行的 Java 静态分析工具支持 Bazel、Maven、Ant、Gradle 等主流构建系统见 README.md。UnnecessarySemicolon是内置检查之一随 Error Prone 的 core 模块一起打包因此启用只要在构建中接入 Error Prone 插件-Xep:系列参数或各构建系统的插件配置该检查默认即处于开启状态临时关闭可用命令行参数-Xep:UnnecessarySemicolon:OFF禁用提升级别可用-Xep:UnnecessarySemicolon:ERROR将其从警告提升为编译错误强制团队清理这类分号局部豁免在方法或类上添加SuppressWarnings(UnnecessarySemicolon)即可跳过该位置的检查BugPattern注解中suppressionAnnotations默认即SuppressWarnings参见 BugPattern.java。此外该检查的每次报告都附带自动修复建议SuggestedFix在开启了-XepPatchChecks等自动修复通道后可以在一次编译中直接把这些多余分号及其前导空白全部清理掉。小结UnnecessarySemicolon是 Error Prone 中一个“小而精”的代码风格检查它依据 JLS §7.6、§8.1.7、§14.6 三处规范精准识别文件顶层、类体内与方法体内三类无意义分号同时对空控制流语句给出{}的规范写法。其源码实现通过EmptyStatementTreeMatcher与ClassTreeMatcher双接口协作配合枚举常量、多变量声明、隐式成员等大量边界处理配合测试套件验证做到了“该删的删、该留的留、绝不误伤”。在你的项目中启用它即可在编译期自动清除这类视觉噪音让代码更清爽、更易读。赞分享静态分析代码质量开发工具【免费下载链接】error-proneCatch common Java mistakes as compile-time errors项目地址https://gitcode.com/gh_mirrors/er/error-prone点击查看免费下载相关推荐Error Prone 检查 TruthGetOrDefault消除对 Map.getOrDefault 断言中的语义歧义Error Prone 检查 TruthGetOrDefault消除对 Map.getOrDefault 断言中的语义歧义 TruthGetOrDefault静态分析代码质量开发工具AutoBangumi 3.3 全量解析异步架构重构、aria2 一等公民化与多用户安全体系AutoBangumi 3.3 全量解析异步架构重构、aria2 一等公民化与多用户安全体系 AutoBangumi 3.3 是该项目历史上规模最大的一次发版静态分析代码质量开发工具用 OfficeCLI raw-set 手写 PPTX从零构建一张全自定义设计的幻灯片用 OfficeCLI raw set 手写 PPTX从零构建一张全自定义设计的幻灯片 导读 本文以 examples/ppt/presentation.md静态分析代码质量开发工具上一篇英雄联盟Akari助手智能游戏效率工具终极指南下一篇5个步骤掌握Unitree GO2 Air机器人ROS2开发从零开始构建智能四足机器人应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考