关于代码规范:p3c-插件是怎么检查出你那屎山的代码

46次阅读

共计 6864 个字符,预计需要花费 18 分钟才能阅读完成。

作者:小傅哥
博客:https://bugstack.cn
原文:https://mp.weixin.qq.com/s/RwzprbY2AhdgslY8tbVL-A

一、前言

你会对你用到都技术,好奇吗?

尽管咱们都被称为码农,也都是写着代码,但因为所处场景需要的不同,所以各类码农也都做着不一样都事件。

有些人对立标准、有些人开发组件、有些人编写业务、有些人倒腾验证,但越是工作内容简略如 CRUD 一样的码农,用到他人提供好的货色却是越多。一会装置个插件、一会引入个 Jar 包、一会调他人个接口,而本人的工作就像是装配工,东拼拼西凑凑,就把产品需要写完了。

坏了,这么干可能几年下来,也不会有什么技术上都冲破。因为你对那些应用都技术不好奇,不想晓得它们是怎么实现的。就像阿里的 P3C 插件,是怎么查看代码剖析进去我写的拉胯的呢?

二、P3C 插件是什么

P3C 是阿里开源代码库的插件工程名称,它以阿里巴巴 Java 开发手册为规范,用于监测代码品质的 IDEA/Eclipse 插件。

  • 源码:https://github.com/alibaba/p3c

插件装置实现后,就能够依照编程规约,动态剖析代码中呈现的代码:命名格调、常量定义、汇合解决、并发解决、OOP、管制语句、正文、异样等各项潜在危险,同时会给出一些优化操作和实例。

  • 在恪守开发手册规范并依照插件查看都状况下,还是能够十分好的对立编码标准和格调都,也能剔除掉一些潜在都危险。
  • 如果你是老手编程用户或者想写出规范都代码,那么十分倡议你依照这样都插件来辅助本人做代码开发。当然如果你所在的公司也有相应都规范手册和插件,也能够依照后恪守它都约定的。

三、P3C 插件源码

在最开始应用这类代码查看都插件的时候,就十分好奇它是怎么发现我的屎山代码的,用了什么样都技术原理呢,如果我能剖析下是不是也能够把这样都技术手段用到其余中央。

在剖析这样一个代码查看插件前,先思考要从 IDEA 插件都源码查起,看看它是什么个逻辑,之后剖析具体是如何应用都。其实这与一些其余的框架性源码学习都是相似的,拿到官网都文档、GitHub 对应的源码,依照步骤进行构建、部署、测试、调试、剖析,进而找到外围原理。

P3C 以 IDEA 插件开发为例,次要波及到插件局部和规约局部,因为是把规约查看的能力与插件技术联合,所以会波及到一些 IDEA 开发的技术。另外 P3C 插件波及到都技术语言不只是 Java 还有一部分 kotlin 它是一种在 Java 虚拟机上运行的动态类型编程语言。

  • 插件源码:https://github.com/alibaba/p3c/blob/master/idea-plugin
  • 规约源码:https://github.com/alibaba/p3c/tree/master/p3c-pmd

1. 插件配置 p3c.xml

<action class="com.alibaba.p3c.idea.action.AliInspectionAction" id="AliP3CInspectionAction"
        popup="true" text="编码规约扫描" icon="P3cIcons.ANALYSIS_ACTION">
    <keyboard-shortcut keymap="$default"
                       first-keystroke="shift ctrl alt J"/>
    <add-to-group group-id="MainToolBar" anchor="last"/>
    <add-to-group group-id="ProjectViewPopupMenu" anchor="last"/>
    <add-to-group group-id="ChangesViewPopupMenu" anchor="last"/>
    <add-to-group group-id="EditorPopupMenu" anchor="last"/>
</action>
  • 翻看源码最重要是要找到入口,这个入口通常也是你在应用插件、程序、接口等时候,最间接进入都那局部。
  • 那么咱们在应用 P3C 插件的时候,最显著的就是 编码规约扫描 通过源码中找到这个关键字,看它都波及了哪个类都配置。
  • action 是 IDEA 插件中用于配置窗体事件入口都中央,以及把这个操作配置到哪个按钮下和对应都快捷键。

2. 编码规约扫描(AliInspectionAction)

class AliInspectionAction : AnAction() {override fun actionPerformed(e: AnActionEvent) {
        val project = e.project ?: return
        val analysisUIOptions = ServiceManager.getService(project, AnalysisUIOptions::class.java)!!
        analysisUIOptions.GROUP_BY_SEVERITY = true

        val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx
        val toolWrappers = Inspections.aliInspections(project) {it.tool is AliBaseInspection}
        val psiElement = e.getData<PsiElement>(CommonDataKeys.PSI_ELEMENT)
        val psiFile = e.getData<PsiFile>(CommonDataKeys.PSI_FILE)
        val virtualFile = e.getData<VirtualFile>(CommonDataKeys.VIRTUAL_FILE)
        
        ...
        
        createContext(
        toolWrappers, managerEx, element,
        projectDir, analysisScope
        ).doInspections(analysisScope)
}        
  • 这是一个基于 kotlin 语言开发的插件代码逻辑,它通过 actionPerformed 办法获取到工程信息、类信息等,接下来就能够执行代码查看了 doInspections

3. 规约 p3c-pmd

当咱们再往下翻看浏览的时候,就看到了一个对于 pmd 的货色。PMD 是一款采纳 BSD 协定公布的 Java 程序动态代码查看工具,当应用 PMD 规定剖析 Java 源码时,PMD 首先利用 JavaCC 和 EBNF 文法产生了一个语法分析器,用来剖析一般文本模式的 Java 代码,产生合乎特定语法结构的语法,同时又在 JavaCC 的根底上增加了语义的概念即 JJTree,通过 JJTree 的一次转换,这样就将 Java 代码转换成了一个 AST,AST 是 Java 符号流之上的语义层,PMD 把 AST 解决成一个符号表。而后编写 PMD 规定,一个 PMD 规定能够看成是一个 Visitor,通过遍历 AST 找出多个对象之间的一种特定模式,即代码所存在的问题。该软件功能强大,扫描效率高,是 Java 程序员 debug 的好帮手。

那么 p3c-pmd 是什么呢?

ViolationUtils.addViolationWithPrecisePosition(this, node, data,
    I18nResources.getMessage("java.naming.ClassNamingShouldBeCamelRule.violation.msg",
        node.getImage()));
  • p3c-pmd 插件是基于 PMD 实现的,更具体的来说是基于 pmd-java 的,因为 PMD 不仅反对 Java 代码剖析,还反对其余多种语言。
  • 具体自定义规定的形式,通过自定义 Java 类和 XPATH 规定实现。

四、规约监测案例

讲道理,说一千道一万,还得是拿出代码跑一下,才晓得 PMD 具体是什么个样子。

1. 测试工程

guide-pmd
└── src
    ├── main
    │   ├── java
    │   │   └── cn.itedus.guide.pmd.rule
    │   │       ├── naming
    │   │       │   ├── ClassNamingShouldBeCamelRule.java
    │   │       │   ├── ConstantFieldShouldBeUpperCaseRule.java
    │   │       │   └── LowerCamelCaseVariableNamingRule.java
    │   │       ├── utils
    │   │       │   ├── StringAndCharConstants.java
    │   │       │   └── ViolationUtils.java    
    │   │       └── I18nResources
    │   └── resources
    │       ├── rule 
    │       │   └── ali-naming.xml  
    │       ├── messages.xml   
    │       └── namelist.properties  
    └── test
        └── java
            └── cn.itedus.demo.test
                ├── ApiTest.java
                └── TErrDto.java
  • 源码:https://github.com/fuzhengwei/guide-pmd

这是一个相似 p3c-pmd 的测试工程,通过自行扩大重写代码监测规约的形式,来解决本人对于代码的审核规范解决。

  • naming 下的类是用于解决一些和名称相干的规定,类名、属性名、办法名等
  • resources 下 ali-naming.xml 是规约的配置文件

2. 驼峰命名规约

public class ClassNamingShouldBeCamelRule extends AbstractJavaRule {

    private static final Pattern PATTERN
            = Pattern.compile("^I?([A-Z][a-z0-9]+)+(([A-Z])|(DO|DTO|VO|DAO|BO|DAOImpl|YunOS|AO|PO))?$");

    @Override
    public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {if (PATTERN.matcher(node.getImage()).matches()) {return super.visit(node, data);
        }
        
        ViolationUtils.addViolationWithPrecisePosition(this, node, data,
                I18nResources.getMessage("java.naming.ClassNamingShouldBeCamelRule.violation.msg",
                        node.getImage()));

        return super.visit(node, data);
    }
}
  • 通过继承 PMD 提供的 AbstractJavaRule 抽象类,重写 visit 办法,应用正则的形式进行验证。
  • visit 办法都入参类型十分多,别离用于解决类、接口、办法、代码等各项内容的监测解决,只有重写须要的办法,在外面进行本人都解决就能够。
  • ClassNamingShouldBeCamelRule、ConstantFieldShouldBeUpperCaseRule、LowerCamelCaseVariableNamingRule 三个类都性能相似,这里就不一一展现了,能够间接参考源码。

3. ali-naming.xml 配置

<rule name="ClassNamingShouldBeCamelRule"
      language="java"
      since="1.6"
      message="java.naming.ClassNamingShouldBeCamelRule.rule.msg"
      class="cn.itedus.guide.pmd.rule.naming.ClassNamingShouldBeCamelRule">
    <priority>3</priority>
</rule>
  • 在 ali-naming.xml 用于配置规约解决类、priority 级别、message 揭示文字。
  • 同时还能够配置代码示例,应用 <example> 标签,在外面写好规范代码即可。

4. 测试验证规约

问题类示例

public class TErrDto {

    public static final Long max = 50000L;

    public void QueryUserInfo(){
        boolean baz = true;
        while (baz)
            baz = false;
    }

}

单元测试

@Test
public void test_naming(){String[] str = {
            "-d",
            "E:\\itstack\\git\\github.com\\guide-pmd\\src\\test\\java\\cn\\itedus\\demo\\test\\TErrDto.java",
            "-f",
            "text",
            "-R",
            "E:\\itstack\\git\\github.com\\guide-pmd\\src\\main\\resources\\rule\\ali-naming.xml"
            // "category/java/codestyle.xml"
    };
    PMD.main(str);
}
  • 规约的测试验证能够间接应用 PMD.main 办法,在办法中提供字符串数组入参,这里的代码监测地址和规约配置须要是绝对路径。

测试后果

TErrDto.java:3:【TErrDto】不合乎 UpperCamelCase 命名格调
TErrDto.java:5:    常量【max】命名应全副大写并以下划线分隔
TErrDto.java:7:    办法名【QueryUserInfo】不合乎 lowerCamelCase 命名格调

Process finished with exit code 4
  • 从测试后果能够看到,咱们写的三个代码规约别离监测出了代码的命名格调、常量大写、办法名不合乎驼峰标识。
  • 同时你还能够测试 category/java/codestyle.xml 这个是 PMD 本身提供好的规约监测。

五、扩大理解 Sonar

其实有了 PMD 动态代码查看规约,能做都事件就很多,不是只对正在写的代码进行查看,还能够对不同阶段的代码进行剖析和危险揭示,比方:筹备提测阶段、曾经上线实现,都能够做相应的监测解决。

而 Sonar 就是一个这样都工具,它是一个 Web 零碎,能够展示动态代码扫描的后果,后果是能够自定义的,反对多种语言的原理是它的扩展性。https://www.sonarqube.org/

  • 不遵循代码规范:sonar 能够通过 PMD,CheckStyle,Findbugs 等等代码规定检测工具标准代码编写。
  • 潜在的缺点:sonar 能够通过 PMD,CheckStyle,Findbugs 等等代码规定检测工具检 测出潜在的缺点。
  • 蹩脚的复杂度散布:文件、类、办法等,如果简单度过高将难以扭转,这会使得开发人员 难以了解它们, 且如果没有自动化的单元测试,对于程序中的任何组件的扭转都将可能导致须要全面的回归测试。
  • 反复:显然程序中蕴含大量复制粘贴的代码是品质低下的,sonar 能够展现 源码中反复重大的中央。
  • 正文有余或者过多:没有正文将使代码可读性变差,特地是当不可避免地呈现人员变动 时,程序的可读性将大幅降落 而过多的正文又会使得开发人员将精力过多地破费在浏览正文上,亦违反初衷。
  • 不足单元测试:sonar 能够很不便地统计并展现单元测试覆盖率。
  • 蹩脚的设计:通过 sonar 能够找出循环,展现包与包、类与类之间的相互依赖关系,能够检测自定义的架构规定 通过 sonar 能够治理第三方的 jar 包,能够利用 LCOM4 检测单个工作规定的利用状况,检测耦合。
  • 进步代码品质:理解本人在编码过程中犯过的谬误,让本人的代码更具备可读性和维护性。

六、总结

  • PMD 是一款采纳 BSD 协定的代码查看工具,你能够扩大实现为本人的规范和标准以及欠缺共性的揭示和修复操作。
  • 另外基于 IDEA 插件实现的代码查看或者有审计要求的解决,也能够基于 IDEA 插件做更多的扩大,比方揭示修复、提供修复操作、本身业务逻辑的查看。例如 momo 开源库下的一款 IDEA 动态代码平安审计及破绽一键修复插件 https://github.com/momosecurity/momo-code-sec-inspector-java
  • 这里补充一点,kotlin 语言能够在 IDEA 中转换为 Java 语言,这样你在浏览相似这样的代码时候,如果不难看懂也能够转换一下在浏览。此外 IDEA 插件开发须要基于 Gradle 或者自身提供都模版进行创立,如果感兴趣也能够浏览我写的 IDEA 插件开发文章。

七、系列举荐

  • 基于 IDEA 插件开发和字节码插桩技术,实现研发交付品质主动剖析
  • 技术调研,IDEA 插件怎么开发「脚手架、低代码可视化编排、接口生成测试」?
  • 《SpringBoot 中间件设计和开发》
  • 笔记整顿:技术架构涵盖内容和演变过程总结
  • 给学习加点实际,开发一个分布式 IM(即时通信)零碎!%E7%B3%BB%E7%BB%9F.html)

正文完
 0