メインコンテンツ

CWE Rule 561

デッド コード

R2023a 以降

説明

ルールの説明

The software contains dead code, which can never be executed.

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • デッド コード

  • 呼び出されていない静的関数

  • 到達不能コード

すべて展開する

問題

この問題は、常に true または false になる条件が原因でコードのブロックに到達できない場合に発生します。この欠陥は以下を除外します。

  • Code deactivated by constant false condition: #if 0 または if(0) などのコンパイル時の定数を使用する命令をチェックする。

  • Unreachable code: gotobreakreturn などのコントロール エスケープ後のコードをチェックする。

  • Useless if: ステートメントが常に true であるかどうかをチェックする。

リスク

デッド コードは開発時間、メモリ、および実行サイクルを浪費します。開発者は実行されないコードを保守しなければなりません。実行されない命令でも保存およびキャッシュされる必要があります。

デッド コードは多くの場合、使用されることのないレガシ コードを表します。デッド コードを定期的にクリーン アップすると、将来の保守の負担が減ります。

修正方法

修正方法は欠陥の根本原因によって異なります。たとえば、同じ実行パスで 2 回チェックされるエラー状態が根本原因で、2 回目のチェックが冗長であるため、対応するブロックがデッド コードになっている可能性があります。

多くの場合、結果の詳細 (または Polyspace as You Code のソース コード ツールヒント) には欠陥につながる一連のイベントが表示されます。そのシーケンス内のどのイベントについても修正を実装できます。結果の詳細にイベント履歴が表示されない場合は、ソース コード内で右クリック オプションを使用して、欠陥に関連する変数のこれまでの参照を検索し、関連するイベントを検出できます。Polyspace デスクトップ ユーザー インターフェイスでの Bug Finder の結果の解釈またはPolyspace Access Web インターフェイスでの Bug Finder の結果の解釈 (Polyspace Access)も参照してください。

以下の修正例を参照してください。

isinfisnan などの関数の使用でデッド コードが表示された場合、非有限値を考慮に入れる解析モードを有効にします。詳細は、非有限の浮動小数点を検討 (-allow-non-finite-floats) を参照してください

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 — if ステートメントのデッド コード
#include <stdio.h>

int Return_From_Table(int ch){

    int table[5];

    /* Create a table */
    for(int i=0;i<=4;i++){
        table[i]=i^2+i+1;
    }

    if(table[ch]>100){ //Noncompliant
         return 0;  
    }
    return table[ch];
}

配列 table 内の最大値は 4^2+4+1=21 であるため、テスト式 table[ch]>100 は常に false として評価されます。if ステートメント内の return 0 は実行されません。

修正 — デッド コードを削除

1 つの修正方法として、if 条件をコードから削除することができます。

#include <stdio.h>

int Return_From_Table(int ch){

    int table[5];

    /* Create a table */
    for(int i=0;i<=4;i++){
        table[i]=i^2+i+1;
    }

    return table[ch];
}
例 — 列挙型を含む if のデッド コード
typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void do_something(suit s);

void bridge(void)
{
    suit card = nextcard();
    if ((card < SPADES) || (card > CLUBS))
        card = UNKNOWN_SUIT;

    if (card > 7) {  //Noncompliant
        do_something(card);
    }
}

suit 型は 5 つのオプションで列挙されます。ただし、条件式 card > 7card の最大値が 5 であるため常に false と評価されます。if ステートメント内の内容は実行されません。

修正 — 条件の変更

1 つの修正方法としてコードの if 条件を変更することができます。この修正では、7 は HEART に変更され、card 型に直接関連付けます。

typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void do_something(suit s);

void bridge(void)
{
    suit card = nextcard();
    if ((card < SPADES) || (card > CLUBS))
        card = UNKNOWN_SUIT;

    if (card > HEARTS) {
        do_something(card);
    }
}
問題

この問題は、static 関数が、それが定義されたファイル内で呼び出されなかった場合に発生します。

リスク

呼び出されていない関数は多くの場合、レガシ コードに起因し、不要な保守の原因になります。

修正方法

呼び出されることを意図していない関数は削除します。デバッグ専用の関数の場合は、デバッグ マクロで関数定義をラップします。

以下の修正例を参照してください。

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 — 呼び出されていない関数エラー

次のコードをファイル Initialize_Value.c に保存します。

#include <stdlib.h>
#include <stdio.h>

static int Initialize(void) //Noncompliant
/* Defect: Function not called */
  {
   int input;
   printf("Enter an integer:");
   scanf("%d",&input);
   return(input);
  }
  
 void main()
  {
   int num;

   num=0;

   printf("The value of num is %d",num);
  }

static 関数 Initialize は、ファイル Initialize_Value.c 内で呼び出されていません。

修正 — 関数を少なくとも一度呼び出す

1 つの修正方法として、Initialize をファイル Initialize_Value.c 内で少なくとも一度呼び出すことができます。

#include <stdlib.h>
#include <stdio.h>

static int Initialize(void)
  {
   int input;
   printf("Enter an integer:");
   scanf("%d",&input);
   return(input);
  }

 void main()
  {
   int num;

   /* Fix: Call static function Initialize */
   num=Initialize();
    
   printf("The value of num is %d",num);
  }
問題

この問題は、制御フロー内の break が原因で、その先のコードのセクションに到達できなかった場合に発生します。

breakgotoreturn などのステートメントは、プログラムのフローを別のセクションまたは関数に移します。このフローのエスケープにより、フロー制御コードに続くステートメントは統計的に実行されず、ステートメントは到達不能になります。

このチェックは、while(1) などのトリビアルな無限ループに続くコードも検出します。この種のループでは、プログラムの終了によってのみ、プログラムのフローが解放されます。このタイプの終了は、無限ループの後に来るコードが到達不能になる原因となります。

リスク

到達不能コードは開発時間、メモリ、および実行サイクルを浪費します。開発者は実行されないコードを保守しなければなりません。実行されない命令でも保存およびキャッシュされる必要があります。

修正方法

修正方法は到達不能コードの意図する機能によって異なります。そのコードを実行する場合、コードの配置または制御フローを変えている先行ステートメントの配置をチェックします。たとえば、到達不能コードが return ステートメントの後にある場合、それらの順序を入れ替えるか、その return ステートメント全体を削除しなければならない可能性があります。

問題を修正しない場合は、改めてレビューされないように結果またはコードにコメントを追加します。詳細は、以下を参照してください。

例 — return 後の到達不能コード
typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void guess(suit s);

suit deal(void){
    suit card = nextcard();
    if( (card < SPADES) || (card > CLUBS) ) 
        card = UNKNOWN_SUIT;
        return card;

    if (card < HEARTS) { //Noncompliant
        guess(card);
    }
    return card;
}

この例には、欠落している中かっこと、誤解を招くインデントがあります。1 番目の return ステートメントは、その関数が呼び出された箇所にコードのフローを戻します。この return ステートメントのために、if ブロックと 2 番目の return ステートメントは実行されません。

インデントと中かっこの修正を行うと、エラーが明確になります。

typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void guess(suit s);

suit deal(void){
    suit card = nextcard();
    if( (card < SPADES) || (card > CLUBS) ){ 
        card = UNKNOWN_SUIT;
    }
    return card;

    if (card < HEARTS) {  //Noncompliant
        guess(card);
    }
    return card;
}

修正 — return の削除

1 つの修正方法として、escape ステートメントを削除します。この例では、最初の return ステートメントを削除して、最後の if ステートメントに到達します。

typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void guess(suit s);

suit deal(void){
    suit card = nextcard();
    if( (card < SPADES) || (card > CLUBS) )
    {
        card = UNKNOWN_SUIT;
    }

    if(card < HEARTS)
    {
        guess(card);
    }
    return card;
}
修正 — 到達不能コードの削除

別の修正方法として、必要でなければ到達不能コードを削除します。関数は 2 番目の if ステートメントに到達しないため、これを削除するとコードが単純になり、しかもプログラムの動作は変更されません。

typedef enum _suit {UNKNOWN_SUIT, SPADES, HEARTS, DIAMONDS, CLUBS} suit;
suit nextcard(void);
void guess(suit s);

suit deal(void){
    suit card = nextcard();
    if( (card < SPADES) || (card > CLUBS) )
    {
        card = UNKNOWN_SUIT;
    }
    return card;
}
例 — 無限ループによる到達不能コードの発生
int add_apples(int apple) { 
    int count = 1;
    while(1) {
        if(apple < 99){
            apple++; 
            count++;
        }else{
            count--;
        }
    }
    return count;  //Noncompliant
}

この例では、while(1) ステートメントにより無限ループが形成されています。この無限ループを終了させる方法はプログラムの終了のみであるため、この無限ループの後に来る return count は到達不能になります。

修正 — ループ条件の書き換え

1 つの修正方法として、ループ条件を変更して while ループを有限にします。ここでの修正例では、if の条件 apple < 99 から構成したステートメントがループで使用されます。

int add_apples1(int apple) { 
    int count = 0;
    while(apple < 99) { 
        apple++; 
        count++;
    }
    if(count == 0)
        count = -1;
    return count;
}
修正 — break ステートメントを追加

別の修正方法として、無限ループからのブレークポイントを追加し、無限ループの後に来るコードへの到達を可能にします。この例では、breakelse ブロックに追加され、return count ステートメントが到達可能になっています。

int add_apples(int apple) { 
    int count = 1;
    while(1) {
        if(apple < 99)
        {
            apple++; 
            count++;
        }else{
            count--;
            break;
        }
    }
    return count;
}
修正 — 到達不能コードの削除

別の修正方法として、到達不能コードを削除します。この修正によりコードがクリーンアップされ、確認と保守が容易になります。この例では、return ステートメントを削除し、関数の戻り値の型を void に変更しています。

void add_apples(int apple) { 
    int count = 1;
    while(1) {
        if(apple < 99)
        {
            apple++; 
            count++;
        }else{
            count--;
        }
    }
}

チェック情報

カテゴリ: Bad Coding Practices

バージョン履歴

R2023a で導入