如何寫靜態程式碼檢查規則(JAVA)
一.概述
產品線最近在線上出了兩個相似的問題:開發人員在寫迴圈程式碼的時候沒有在迴圈體內使用到迴圈變數而發生了錯誤,比如如下程式碼:
1 |
List<Integer> list = getList(); |
2 |
for(int i = 0;i < 9 ;i++){ |
3 |
System.out.println(list.get(0)); |
4 |
} |
其實我們在程式碼中是想寫list.get(i)的,為了發現諸如此類的問題,我們需要把它固化成靜態程式碼檢查的規則,然後在每次的持續整合中執行。
現在用得比較廣的Java靜態程式碼檢查工具有Findbugs和PMD,由於最近在研究抽象語法樹方面的東西,這次就採用PMD來實現。PMD有兩種實現,對於簡單的規則我們直接藉助已經寫好的Xpath規則就能很輕鬆地達到,對於複雜的規則我們就必須熟悉PMD的api和設計規則了,以下就兩種方式進行介紹。
二.用類Xpath寫規則
首先下載4.2.5版本的PMD,然後採用命令列的方式進入到bin目錄,並執行 designer命令,我們可以看到如下圖:
左上角你需要貼入的程式碼,左下角是生成的抽象語法樹,右上角是設計的xpath規則,右下角是符合xpath查詢條件的內容。對於以下一段程式碼:
1 |
public class a { |
2 |
int hello; |
3 |
int world; |
4 |
5 |
private void run() { |
6 |
int one = 1; |
7 |
int two = 2; |
8 |
} |
9 |
} |
生成的語法樹如下:
如果你需要找到所有的變數定義,那麼使用//VariableDeclaratorId就ok,如果要找到本地變數的定義,那麼使用//LocalVariableDeclaration,這裡的xpath以//開頭表示一個廣泛匹配,表示只要是某個節點就ok,//後面跟的變數是抽象語法樹中的節點。
如果要做這麼一條規則:找出所有if中存在空語句的程式碼,比如如下程式碼:
1 |
public class Foo { |
2 |
void bar(int x) { |
3 |
if (x == 0) { |
4 |
// empty! |
5 |
} |
6 |
} |
7 |
} |
那麼我們的規則可以寫為//IfStatement/Statement[EmptyStatement or Block[count(*) = 0]],表示找出if中的statement中的空宣告或者語句數量為0的程式碼。對於所有寫好的規則我們都必須使用多種原始碼進行反覆測試,然後在幾個真實專案中試用一下發現沒有問題才正式加入靜態程式碼檢查工具中。
三.呼叫API寫規則
對於一個複雜的規則,我們沒法用xpath來表達,那就必須呼叫PMD的API了。對於PMD和Findbugs這種檢查工具都是採用訪問者模式,示意圖如下:
這裡的ObjectStructure就是整個語法樹,Element就是語法樹中的各個節點物件,比如TypeDeclaration(型別宣告)節點、FieldDeclaration(欄位宣告)節點等等,Visitor觀察者即為我們的規則。當我們的規則寫好後,由PMD負責呼叫規則來觀察所有語法樹中的節點,如果發現觀察到的內容與規則不符就會將資訊記錄到PMD的Report裡。
對於我們概述中提到的需求,就是採用呼叫API的方式來實現,我們只需要繼承AbstractJavaRule(這個類就相當於Visitor介面的抽象類實現,裡面有些預設的行為)。
程式碼如下:
1 |
package net.sourceforge.pmd.rules; |
2 |
3 |
import java.util.List; |
4 |
5 |
import net.sourceforge.pmd.AbstractJavaRule; |
6 |
import net.sourceforge.pmd.ast.ASTClassOrInterfaceType; |
7 |
import net.sourceforge.pmd.ast.ASTForInit; |
8 |
import net.sourceforge.pmd.ast.ASTForStatement; |
9 |
import net.sourceforge.pmd.ast.ASTName; |
10 |
import net.sourceforge.pmd.ast.ASTStatement; |
11 |
import net.sourceforge.pmd.ast.ASTVariableDeclaratorId; |
12 |
13 |
public class ForVariableNotUseRule extends AbstractJavaRule { |
14 |
15 |
public Object visit(ASTForStatement node, Object data) { |
16 |
if (!isUsed(node)) { |
17 |
addViolation(data, node, "the varable in for statement is not used"); |
18 |
} |
19 |
return data; |
20 |
} |
21 |
22 |
private boolean isUsed(ASTForStatement node) { |
23 |
Boolean isUsed = false; |
24 |
ASTForInit forInit = node.getFirstChildOfType(ASTForInit.class); |
25 |
if (null == forInit) { |
26 |
isUsed = true; |
27 |
} else { |
28 |
ASTClassOrInterfaceType variableType = forInit.getFirstChildOfType(ASTClassOrInterfaceType.class); |
29 |
if(variableType != null && "Iterator".equals(variableType.getImage())){ |
30 |
return true; |
31 |
} |
32 |
33 |
ASTVariableDeclaratorId variableDeclaratorId = forInit |
34 |
.getFirstChildOfType(ASTVariableDeclaratorId.class); |
35 |
String variableName = variableDeclaratorId.getImage(); |
36 |
37 |
ASTStatement statement = node |
38 |
.getFirstChildOfType(ASTStatement.class); |
39 |
List<ASTName> names = statement.findChildrenOfType(ASTName.class); |
40 |
for (ASTName name : names) { |
41 |
if (variableName.equals(name.getImage())) { |
42 |
isUsed = true; |
43 |
} |
44 |
} |
45 |
} |
46 |
return isUsed; |
47 |
} |
48 |
} |
主要邏輯在isUsed()方法裡面,我們首先找到for迴圈中初始化的語句,然後找到初始化變數的名稱。接著我們分析初始化裡面是不是使用迭代器方式使用,如果是我們認為不會出現之前提到的問題。最後再判斷初始化變數是否在後續的for迴圈程式碼中使用到。
PS:開發中我們如果不用到for中的變數我們應該使用迭代器或者foreach,這是一種更加優雅的方式。
四.加入到靜態程式碼檢查工具
規則寫好後,最後我們要做的工作是把它加入到靜態檢查工具中,我們修改pmd rule資料夾下的pmd.xml檔案,在裡面加上:
1 |
<rule name="ForVariableNotUseRule" since="4.2.5" message="the variable in the for statement but it never used" class="net.sourceforge.pmd.rules.ForVariableNotUseRule" externalInfoUrl="http://pmd.sourceforge.net/rules/basic-jsf.html#DontNestJsfInJstlIteration"> |
2 |
<description>The rule check the variable defined in the "for cycle" and never used in the next code. It is a bad code and lead to a bug, even so you should use foreach. </description> |
3 |
<priority>2</priority> |
4 |
<example> |
5 |
<![CDATA[ |
6 |
List<Integer> list = new ArrayList<Integer>(); |
7 |
for(int i = 0;i < 9 ;i++){ |
8 |
System.out.println(list.get(0)); |
9 |
} |
10 |
]]> |
11 |
</example> |
12 |
</rule> |
五.總結
以上就是對如何增加靜態程式碼檢查規則進行一個簡單的介紹,其實大部分需求PMD或者Findbugs都幫我們做好了,如果確實需要新增一個規則的時候我們需要確認是不是已經在已有規則裡面了,而不必重複造輪子。另外分析Java抽象語法樹其實還可以完成非常多的功能,比如Java原始碼比對、原始碼生成工具等,Eclipse裡面多處用到了這些技術。
相關推薦
如何寫靜態程式碼檢查規則(JAVA)
一.概述 產品線最近在線上出了兩個相似的問題:開發人員在寫迴圈程式碼的時候沒有在迴圈體內使用到迴圈變數而發生了錯誤,比如如下程式碼: 1 List<Integer> list = getList(); 2 for(int i = 0;i <
OC靜態程式碼檢查實戰
此文已由作者楊曉授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 在Mac OS系統上,採用Xcodebuild Analyze命令和OClint工具,對iOS專案進行靜態程式碼檢查,輸出視覺化結果。然後將專案持續整合至CI平臺,並使用PMD外掛進行錯誤統計的展示。對
如何在eclipse中新增程式碼檢查外掛,實時提示檢查問題,eclipse+sonarLint+sonarqube平臺程式碼檢查規則
在eclipse中引入sonarLint外掛 步驟如下: 1、sonarLint安裝 開啟eclipse後,執行 help->Eclipse Marketplace->查詢sonarLint
Android靜態程式碼檢查工具Lint
轉自 https://blog.csdn.net/u012317510/article/details/78221218 前言 Android提供了一個叫做Lint的靜態程式碼檢查工具。Lint工具可以幫助你檢查可能影響應用質量和效能的程式碼問題。該工具會報告檢測到的每個問
eclipse+sonarLint+sonarqube平臺程式碼檢查規則 在編碼過程中實時提示程式碼中壞味道
效率達標後,質量就該提上日程。以前編碼中的壞習慣該收斂收斂了。 專案開發完成後,構建測試環境時,sonarqube查出了一堆的壞味道,懷著又愛又恨的心情,改著這些壞味道,開始了還債的過程。好煎熬。 是不是可以在開發過程中就檢查呢,二次返工不好受。 一下午不編碼了,和so
靜態程式碼檢查工具簡介
靜態程式碼檢查工具簡介 在 Java 軟體開發過程中,開發團隊往往要花費大量的時間和精力發現並修改程式碼缺陷。傳統的程式碼複審、同行評審,通過人工方式來檢查缺陷仍然是一件耗時耗力的事情。Java 靜態程式碼分析(static code analysis)工具能夠在程式碼構建過程中幫助開發人員快速
C++效能系列之靜態程式碼檢查工具介紹(一)
FxCop Integrator允許將獨立的FxCop(1.36或10.0)和Code Metrics PowerTool 10.0整合到VS2010中。 最新版本(2.0.0 RTW)包含以下新功能: 支援使用程式碼度量PowerTool的計算程式碼
JS 的靜態程式碼檢查工具 Flow
無意中看到了VUE的原始碼,沒看幾行就被看蒙了。 Vue: Class<Component> const vm: Component = this 想了一下,查了查相關資料,是不是ES 6的新語法。 確實不是。 是Facebook開源的JS靜態檢查工具用法。
C#靜態程式碼檢查工具StyleCode -- 初探
最近我們Advent Data Service (ADS) 在專案上需要按照程式碼規範進行程式碼的編寫工作,以方便將來程式碼的閱讀與維護。 但是人工檢查起來容易遺漏或者格式不統一, ReSharper又是收費的,而且費用不菲。 於是美國的同事推薦了我們一款開源工
靜態程式碼檢查工具 cppcheck 的使用
CppCheck是一個C/C++程式碼缺陷靜態檢查工具。不同於C/C++編譯器及其它分析工具,CppCheck只檢查編譯器檢查不出來的bug,不檢查語法錯誤。所謂靜態程式碼檢查就是使用一個工具檢查我們寫的程式碼是否安全和健壯,是否有隱藏的問題。 比如無意間寫了這
ant + findbugs 安裝及實現靜態程式碼檢查,並生成HTML檢查報告
1、ant + findbugs安裝 通過Eclipse或者MyEclipse繼承ant、findbugs外掛。外掛可以到網上去下。 注:findbugs最好是下載1.3.9版本,如果是其他版本,可能在執行的時候會提示版本衝突錯誤! 2、通過findbugs做靜態程式碼檢查
靜態程式碼檢查-CheckStyle
Important Note: If you did not use the update site to install the plug-in please make sure to restart Eclipse with the -clean option at least once. This is
靜態程式碼檢查cppcheck整體瞭解
cppcheck是一個C++開源的靜態程式碼檢查工具。基本上編譯器不檢查的問題他都檢查,效果還是不錯的。工作中用到cppcheck作為程式碼檢查,網上現在能搜到的關於cppcheck相關資訊也不多,自己也在這裡記錄一下。其實引入cppcheck確實能為程式碼提供一些基本風險檢
利用Scanner與SonarJS進行js靜態程式碼檢查
安裝SonarQube 6.7 LTS版本 檢視sonar-scanner是否安裝成功,執行一下命令 ./sonar-scanner -v 執行程式碼分析命令在專案跟目錄 sonar-sc
webstorm引用ESLint進行靜態程式碼檢查
規範程式碼 JavaScript 的程式碼檢查工具有:JSLint,JSHint, JSCS, ESLint,本文著重介紹 ESLint。 ESLint 在一系列的程式碼質量檢查工具中,是最年輕的一個,當然也是最現代化的。配置多樣,支援 JavaScript, JSON 以
5個靜態程式碼檢查工具
FlexeLint 支援windows,linux http://www.gimpel.com/html/flex.htm pclint 只支援 windows http://www.gimpel.com/html/pcl.htm splint 只支援c程式碼的檔案 c
JAVA 靜態程式碼分析--規範檢查-checkstyle
簡介 外掛的用途就不多說了,主要用於JAVA程式碼規範檢測,預設用的sun的一套檢查標準,也可以自己定義。這裡講的版本是5.6 在eclipse中安裝checkstyle help--> eclipse marketplace 搜尋 checkstyle,安裝即可。
Java程式碼塊(構造塊、靜態程式碼塊、普通程式碼塊)
在Java中根據程式碼塊出現的位置以及關鍵字,分為下列四中程式碼塊: 1.普通程式碼塊 定義在方法中的程式碼塊 2.構造塊 定義在類中的程式碼塊,不加任何修飾符 { } 在物件產生時,優先於構造方法執行,有幾個物件產生,就呼叫幾次構造塊。 用於在構造方法執行前完成一些屬性的初始化操作 3
【Java基礎】程式碼塊,構造程式碼塊,靜態程式碼塊
程式碼塊 public class Demo { public static void main(String[] args){ { int a = 1; System.out.println(a); }
java機制:類的載入詳解(靜態類,靜態變數,靜態方法,靜態程式碼塊,構造程式碼塊,成員變數,成員方法,父類...)
“程式碼編譯的結果從本地機器碼轉變為位元組碼,是儲存格式發展的一小步,卻是變成語言發展的一大步”,這句話出自《深入理解JAVA虛擬機器》 一.原始碼編譯 &n