現在、C ++言語用の静的解析ツールには、Coverity / Klocwork / Parasoft / Clang / CppCheck / PVS-Studioなど多くの選択肢があります。 そして最近、ReSharper C ++がリリースされました。
それでも、オンザフライでの分析タスクは依然として関連しています。 この機能を備えたソリューションはほとんどありません。 同じReSharperは、Visual Studioと統合され、オンラインで多くの問題(たとえば、未使用のヘッダーファイル)を検出しますが、従来の静的アナライザーよりもリファクタリングツールとして見られています。 R#には、セマンティックエラー、コピーアンドペーストエラー、タイプミス、およびランタイムエラー(メモリリーク、バッファオーバーフロー、nullポインタの逆参照など)の診断がほとんどありません。
Coverity / Klocwork / Parasoft-これらのツールはそれぞれ市場リーダーと呼ばれていますが、個々の開発者がこれらのアナライザーの試用版を入手することは非常に困難です。これらの製品のターゲット指向は異なり、大企業を対象としているためです したがって、それらを考慮することは意味がありません。
Clangの主な目的は、ソースプログラムの脆弱性を見つけることです。 利用可能なチェックはここにあります。
clangが実際にタイプミスやセマンティックエラーを診断しないことは残念ですが、Xcode以外でclangを使用するのはやや不便です。 もちろん、アナライザーはコンソールから起動できますが、そのようなスキームでは手動構成(ファイルのリスト、ヘッダーファイルのディレクトリへのパス、定義済みマクロなど)が必要です。 そのため、IDEの外部で外部ユーティリティを定期的に起動することは、便利で便利とは言えません。
PVS-Studioはもう少し進んでビルドシステムに統合されました。 これで、プロジェクトの各ビルド後に分析が自動的に実行されます。 しかし、ここでも疑問が生じます。エンコード中に見つかったすべてのエラーを強調表示して、その場で分析を行わないのはなぜですか? これはエンドユーザーにとってはるかに論理的で便利です。
これはまさに、Veroneアナライザーが解決する問題です。 インストール後、Visual Studioを開くとアナライザーが自動的に起動します。 バックグラウンドスレッドで、Veroneはプログラマが作業中に行った変更を追跡し、分析し、見つかった問題の結果を表示します。 現在のファイルのすべての診断は、エディターの右側に特別なインデントで示されます。
いわゆるグリフの色は、診断の種類(注意、警告、エラー)によって異なります。 見つかったすべての問題を確認するには、ツールバーのアナライザーアイコンをクリックします。その後、検出されたすべての問題を含むウィンドウが表示されます。
現時点では、アナライザーにはセマンティックエラーとタイプミスの検出を目的とした約20の診断機能があります。 もちろん、これは既存のツールと競合するには非常に小さいですが、これは最初のリリースにすぎず、多くの開発が含まれていないことを理解する価値があります。 今後の計画は記事の最後になります。 それでも、実際のプロジェクトの欠陥を特定するにはこれで十分です。
たとえば、LLVMソースコードで次の問題が見つかりました。
else if (ArgTy->isStructureOrClassType()) return record_type_class; else if (ArgTy->isUnionType()) //<--- return union_type_class; else if (ArgTy->isArrayType()) return array_type_class; else if (ArgTy->isUnionType()) //<---- return union_type_class;
ここに条件があります
ArgTy->isUnionType()
平凡な不注意のために二度繰り返された。 そして、このフラグメントが深刻なバグとほとんど考えられない場合、このコードはすでにより多くの疑いを引き起こしています:
if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY) //<---- outs() << "undefined [lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY) //<--- outs() << "undefined [private lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY) outs() << "undefined [private]) "; else outs() << "undefined) ";
2番目のブロックの状態は最初のブロックの状態と完全に同一であるため、2番目のブロックは制御を受け取りません。 ソースコードを調べた後、2番目のケースでは、次の形式の条件があるはずです。
(NDesc & MachO::REFERENCE_TYPE) == MachO:: REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY
同様の問題がlibsassプロジェクトによって発見されました。
if (lhs_tail->combinator() != rhs_tail->combinator()) return false; if (lhs_tail->head() && !rhs_tail->head()) return false; if (!lhs_tail->head() && rhs_tail->head()) return false; if (lhs_tail->head() && lhs_tail->head()) { //<--- if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; }
このフラグメントでは、最後の条件に誤りがあります。
lhs_tail->head() && lhs_tail->head()
繰り返しになりますが、コードはエラーが感じられないように書かれていますが、これは修正する必要がないという意味ではありません。修正は最後のコミットのいずれかで作者によって行われました。
Torque 3Dグラフィックエンジンで多くの不審なスポットが見つかりました。
// Update accumulation texture if it changed. // Note: accumulation texture can be NULL, and must be updated. if ( passRI->accuTex != lastAccuTex ) { sgData.accuTex = passRI->accuTex; lastAccuTex = lastAccuTex; dirty = true; }
ロジックに基づいて、lastAccuTexポインター(GFXTextureObject *)は最後に処理されたpassRI-> accuTexオブジェクトを参照しますが、変数の不注意により、独自の値が割り当てられます。
そして、このフラグメントでは:
class ConnectionProtocol { public: ConnectionProtocol(); virtual void processRawPacket(BitStream *bstream); virtual Net::Error sendPacket(BitStream *bstream) = 0; virtual void keepAlive() = 0; virtual void handleConnectionEstablished() = 0; virtual void handleNotify(bool recvd) = 0; virtual void handlePacket(BitStream *bstream) = 0; ... };
クラスはポリモーフィックですが、デフォルトのデストラクター(仮想ではありません)があります。 したがって、削除中に基本クラスへのポインターが使用されると、子クラスでリソースリークが発生する可能性があります。
以下の例のように、小数部分を破棄する整数除算の疑わしい場所があります。
bm->avgfloat=PACKETBLOBS/2
変数bm-> avgfloatには浮動小数点型がありますが、整数除算は代入演算子の右側にあるため、小数部分は破棄されます。 これは明らかな間違いであるとは言いませんが、注意が必要です。
結論として、静的アナライザーの開発は、多くのリソースと時間を必要とする非常に大きなタスクであると言う価値があります。 このリリースには含まれていない多くの開発があります。 したがって、今後6か月または1年でVeroneが開発するいくつかの領域について説明します。
- モックファイルの生成に基づく、その場でのより高度な分析アルゴリズム(アイデアについてSasha Kokovinに感謝)
現在、このアルゴリズムは非常に簡単に機能します。 実際、すべてが<file name>-<date of last analysis>のペアでファイルに結び付けられています。 そして、アナライザーコアは特定の周波数で開始します。 作業中に少数のファイルが変更されるため、このスキームにより、大規模なプロジェクトに対応できます。 - エディターでまだ保存されていない変更を分析する可能性
残念ながら、上記のスキームはファイルに関連付けられており、エディターで既に行われたがまだ保存されていない変更を考慮していません。 このタスクは非常に膨大であり、徹底的なテストが必要なため、最初のリリースでは実装されていませんでした。
- ランタイムエラー分析
主な指示の1つは、現在のバージョンにはないランタイムエラーの分析です。 実行時エラー分析とオンザフライ分析を組み合わせることは非常に有望に見えます。なぜなら、私の意見では、たとえば、コーディングの時点でNULLポインターを逆参照するのはとてもクールだからです。 もちろん、データフロー分析アルゴリズムは非常にリソースを消費するため、いくつかの単純化を行う必要があります。 しかし、たとえば、ローカル分析でも具体的なメリットがあり、開発者に問題の可能性を警告します。
より遠いタスクからも強調する価値があります:
- GUIの改善
現在、診断を非表示にし、それらを検索してファイルに保存する機能はありません。これは日常の作業に役立ちます。
- 他のIDEおよびオペレーティングシステムをマスターする
- スタンドアロンアプリ
スタンドアロンのグラフィカルアプリケーションが便利な理由は 開発環境にリベットされていません。 すでにアナライザーのコンソールバージョンを安全に使用できますが、グラフィカルインターフェイスを備えたバージョンは、この目的のためのより便利なソリューションのようです。
アナライザーはここからダウンロードできます 。 現時点では、アナライザーはWindowsでのみ使用可能で、Visual Studio 2015との統合用のバージョンが使用可能です。VisualStudio 2012およびVisual Studio 2013のバージョンは1週間または2週間で表示されます。
ご質問にお答えします。 フィードバックや提案をお待ちしております!