レビュー担当者の注意:コード分析の自動化手法

コードレビューは正式な手段ではありませんが、初心者プログラマーがプロジェクトの詳細をすばやく理解するための貴重な機会です。 しかし、この段階では、査読者が見直して考えなければならない資料の量が多いため、眼出血のリスクが伴います。



この記事では、レビュアーからそのようなありふれたものの注意を取り除く方法を説明しますが、たとえば、受け入れられているコード記述スタイルへの準拠のチェック、特定のプロジェクトのみに固有の契約の遵守などの重要なことです。 Androidアプリケーションのコードを例として取り上げますが、この手法はどのJavaコードにも使用できます。







プロジェクトの成長に伴い、個々のモジュールの実装、ライブラリの使用方法、および独自のフレームワークに関する多くの合意が蓄積されています。 そのような契約の例を次に示します。





開いたリソースは、使用後に閉じる必要があります。 そうしないと、リソースと関連する問題のリークが発生します。 多くの静的分析ツールは、既知のリソースに対してこれを実行できます。 頻繁なリソースは、ソケット、データベース接続、メモリ領域です。 ただし、特定のリソースの検証を追加できます。





たとえば、データベースにアクセスするすべてのメソッドでは、データベースにアクセスする前に、必要なパラメーターを使用して特定の監査機能を呼び出す必要があります。





継承クラスのhashCode()とequals()の両方を常に再定義する必要があることを知っています。 プロジェクトに同様の契約がある場合があります。 このカテゴリの別の例は、予想される変数がオーバーライドされたメソッドで使用されていることを確認することです。 これにより、メソッドが意図したとおりに使用されていることを確認できます。



公平を期すため、チェックは別のレベルの管理契約であると付け加えます。 理想的には、アプリケーションアーキテクチャは壊れにくいものでなければなりませんが、これは別の記事のトピックです。



Androidアプリケーションが1つのロガーru.project.Loggerを使用し、android.util.Logクラスも使用していないことを確認します。 これは、リリースアプリケーションでログへのセキュリティ上重要な情報の出力を1か所でオフにできるようにするために必要です。



チェックするより



悪いニュースは、人気のあるFindBugsが 2015年以降開発しいないことです。 良いものはSpotBugsです。「SpotBugsはFindBugsの精神的な後継者であり、コミュニティの支援を受けて中断したところから続けています。」



彼はどのように働いていますか? アナライザーへの入力は、ディレクトリまたは選択されたファイルにバイトコードで渡されます。 バイトコード分析には、 Apache BCELライブラリが使用されます。 チェック自体は、Detectorクラスの子孫に実装されます。 Detectorは、クラス、メソッド、およびメソッドコードを「入力」する訪問者です。 FindBugsコードには、このインターフェイスの子孫が多数あります。



FindBugsプラグインの開発のための環境のセットアップ



1. spotbugsコードを圧縮します。

git clone



2.プロジェクトをまとめる

./gradlew distZip



3.ディストリビューションをディレクトリ/spotbugs-4.0.0-SNAPSHOTに解凍します

プラグインを開発するには、intellij ideaコミュニティエディションを使用します。



4.新しいjava(gradle)アプリケーションプロジェクトを作成します。



5.依存関係を構成します。 spotbugs.jarファイルをプロジェクトの/ libsディレクトリにコピーします。



build.gradle  dependencies {   compile fileTree(dir: 'libs', include: ['*.jar'])   compile 'org.apache.bcel:bcel:6.1' }
      
      





プラグインの構成



Findbugs.xmlファイル-プラグインの説明



 <FindbugsPlugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://findbugs.googlecode.com/svn-history/r13379/trunk/findbugs/etc/findbugsplugin.xsd"              pluginid="ru.sberbank.android.log.plugin"              provider="Find Android.Util.Log class"              defaultenabled="true"              website="https://find-sec-bugs.github.io">  <Detector class="ru.sbt.detector.AndroidLogCallDetector"/>  <BugPattern type="ANDROID_LOG_CALL_DETECTED" abbrev="ANDR_LOG" category="CORRECTNESS"/> </FindbugsPlugin>
      
      





Messages.xmlファイル-見つかったバグに関するメッセージテンプレート



 <MessageCollection>  <Detector class="ru.sbt.detector.AndroidLogCallDetector">      <Details>ANDROID_LOG_CALL_DETECTED alala.</Details>  </Detector>  <BugPattern type="ANDROID_LOG_CALL_DETECTED">      <ShortDescription>android.util.Log usage detected !</ShortDescription>      <LongDescription>android.util.Log usage detected !</LongDescription>      <Details>          <![CDATA[      <p> Please not use android.util.Log ! Use 'ru.sberbank.mobile.core.log.Logger' instead.    ]]>      </Details>  </BugPattern>  <BugCode abbrev="ANDR_LOG">android.util.Log usage detected !</BugCode> </MessageCollection>
      
      





カスタム検出器コード



検出器はバイトコードで動作することを思い出してください。 検出器の動作を理解するために、onCreate()Activityメソッドのバイトコードの一部を示します。Lod.dは( "aaa"、 "bbb")と呼ばれます。



 javap -c LoginActivity.class Compiled from "LoginActivity.java" public class ru.sbrf.testandroidapp.LoginActivity extends android.support.v7.app.AppCompatActivity implements android.app.LoaderManager$LoaderCallbacks<android.database.Cursor> { public ru.sbrf.testandroidapp.LoginActivity(); protected void onCreate(android.os.Bundle);   Code:      0: aload_0      1: aload_1      2: invokespecial #10                 // Method android/support/v7/app/AppCompatActivity.onCreate:(Landroid/os/Bundle;)V      5: ldc           #11                 // String aaa      7: ldc           #12                 // String bbb 9: invokestatic  #13                 // Method    android/util/Log.d:(Ljava/lang/String;Ljava/lang/String;)I     12: pop     13: aload_0     14: ldc           #15                 // int 2130968603     16: invokevirtual #16                 // Method setContentView:(I)V
      
      





9行目。検出器を検索します。 これはandroid.util.Logクラスの静的メソッドの呼び出しです



検出器コード



 public class AndroidLogCallDetector extends BytecodeScanningDetector implements StatelessDetector {  public static final String BUG_TYPE = "ANDROID_LOG_CALL_DETECTED";  public static final String ANDROID_UTIL_LOG = "android/util/Log";  public static final String ALLOWED_LOGER_CLASS = "ru.sberbank.mobile.core.log.Logger";  private final BugReporter bugReporter;  private String clsName;  public AndroidLogCallDetector(BugReporter bugReporter) {      this.bugReporter = bugReporter;  }  @Override  public void visitClassContext(ClassContext classContext) {      JavaClass cls = classContext.getJavaClass();      clsName = cls.getClassName();      //skip allowed logger class      if (!ALLOWED_LOGER_CLASS.equals(clsName)) {          super.visitClassContext(classContext);      }  }  @Override  public void sawOpcode(int seen) {      if (seen == Const.INVOKESTATIC && ANDROID_UTIL_LOG.equals(getClassConstantOperand())) {      BugInstance bug = new BugInstance(this, BUG_TYPE, NORMAL_PRIORITY).addClassAndMethod(this)                  .addSourceLine(this);          bugReporter.reportBug(bug);      }  } }
      
      





プラグインをビルドします。



  ./gradlew jar
      
      





テスト中



Detector-1.0-SNAPSHOT.jarプラグインファイルを/spotbugs-4.0.0-SNAPSHOT/pluginにコピーします。 UIはspotbugs /spotbugs-4.0.0-SNAPSHOT/bin/spotbugsインターフェースを起動します。 検証のために、テストAndroidプロジェクトのバイトコードを含むディレクトリを選択します。 / TestAndroidApp /アプリ/ビルド/中間体/クラス/デバッグ







ご覧のとおり、android.util.Logクラスへのすべての呼び出しが見つかりました。 これで、カスタム検出器をCIシステムで使用できます。 彼は発見されたバグについて開発者に通知します。



コードスタイルチェック



ここで、レビュー担当者がコードスタイルのコンプライアンスについてコードをチェックしないようにします。 このために、 Checkstyleプラグインを使用します 。 ちなみに、すでに実装されているすべてのチェックのリスト添付されており、 こちらあります



プラグインは、 checkstyle.xml構成ファイルを使用して構成されます。 必要なチェックとそのパラメーターを示します。 以下は、次のチェックのサンプルファイルです。







 <i><?</i>xml version="1.0"<i>?></i> <!DOCTYPE module PUBLIC  "Check Configuration"  "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd"<i>></i> <module name="Checker">  <property name="charset" value="UTF-8" />  <property name="severity" value="warning" />  <module name="TreeWalker">      <module name="EqualsHashCode" />      <module name="CovariantEquals" />      <module name="MagicNumber" />      <module name="DeclarationOrder" />      <module name="JavadocMethod">          <property name="scope" value="public" />          <property name="allowMissingParamTags" value="true" />          <property name="allowMissingThrowsTags" value="true" />          <property name="allowMissingReturnTag" value="true" />          <property name="allowThrowsTagsForSubclasses" value="true" />      </module>      <module name="LineLength">          <property name="max" value="160" />          <property name="ignorePattern"              value="^package.*|^import.*|a href|href|http://|https://|ftp://" />      </module>      <module name="EmptyBlock">          <property name="option" value="TEXT" />          <property name="tokens"              value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH" />      </module>      <module name="NeedBraces" />      <module name="MissingSwitchDefault" />      <module name="ModifierOrder" />      <module name="OverloadMethodsDeclarationOrder" />  </module> </module>
      
      





一連の既成のルールとそれらを構成する機能により、ほとんどすべての要件を確認できます。 ただし、独自の特別なルールを作成する必要がある場合は、指示に従って行うこともできます



Checkstyleは、 ANTLRを使用して分析されたプロジェクトのソースコードで動作することに注意してください。 検証を実装するには、ソースコードの構文ツリーを通過するビジターを実装する必要があります。 この記事では、カスタムコードスタイルチェックの作成を検討しません。



Androidアプリケーションにcheckstyleプラグインを追加します



build.gradleに行を追加します



 apply plugin: 'checkstyle' task checkstyle(type: Checkstyle) {  configFile file("$project.rootDir/app/checkstyle.xml")  source 'src/main/java'  include '**/*.java'  exclude '**/gen/**'  exclude '**/R.java'  exclude '**/BuildConfig.java'  classpath = files() } tasks.withType(Checkstyle) {  reports {      xml.enabled false      html.enabled true  } }
      
      





チェックの開始./gradlew checkstyle。

レポートはディレクトリ/build/reports/checkstyle/checkstyle.htmlにあります。 以下にサンプルレポートを示します。







そして、ここにチェックされたコードがあります



 20     static public double  getCount(){ 21       if(true) Log.<i>i</i>("a","b"); 22      return 43.2*15.0; 23    }
      
      





そのため、レビュー担当者を、コードスタイルの確認やプロジェクト契約の確認という通常の作業から解放しました。 これで、開発者は創造性を発揮する時間が増え、コードが改善されます。 この資料がお役に立てば幸いです。コメント欄でご質問にお答えいたします。



All Articles