記事「コードアナライザーの比較の難しさ、または使いやすさを忘れないでください」[ 1 ]は、アナライザーの実際の技術的特性に加えて、使いやすさなどのパラメーターが非常に重要であるため、2つのツールの比較は見かけほど簡単ではないと述べています。
それでも、検出されたエラーを比較することから逃れることはできません。 当然、数を数えるだけでは意味がありません。 そのため、実際のプロジェクトのエラーを検出するための実用的な実験を行うことにしました。
この記事は、Visual Studio 2010に組み込まれた静的コードアナライザーを使用して特定されたエラーを示しています。この調査は、5つのオープンソースプロジェクトで実施されました。 これらの同じプロジェクトは、PVS-Studioを使用して検証されました。 これら2つのツールを比較した結果が表示されます。
Visual Studio 2010 Premiumに組み込まれた静的アナライザーを使用して、最初に5つのランダムなオープンソースプロジェクトをテストしました。 メッセージのリスト全体を調べ、明らかなエラーを選択しました。 その後、PVS-Studioからも来ました。
以下は、調査に関係するプロジェクトのリストです。
さあ、行こう!
eMule Plus
Visual Studio静的アナライザーからは237のメッセージがありますが、そのうち4つは実際のエラーです。
PVS-Studioからのメッセージは合計68件あり、そのうち3件は実際のエラーです。
Visual Studio:警告C6054:文字列 'szwThemeFile'は ゼロ終了。 c:\ emuleplus \ dialogmintraybtn.hpp 445
WCHAR szwThemeFile [MAX_PATH]; WCHAR szwThemeColor [256]; if(m_themeHelper.GetCurrentThemeName(szwThemeFile、 ARRSIZE(szwThemeFile)、szwThemeColor、 ARRSIZE(szwThemeColor)、NULL、0)!= S_OK) NULLを返します。 WCHAR * p; if((p = wcsrchr(szwThemeFile、L '\\'))== NULL)
実際、行が0で終わらない場合があり、潜在的な問題につながります。 ただし、この場合、このエラーはほとんど発生しません。
Visual Studio:警告C6269:操作の順序が間違っている可能性があります: 参照解除は無視されます。 c:\ emuleplus \ customautocomplete.cpp 277
PVS-Studio:V532 '* pointer ++'のステートメントを調べることを検討してください パターン。 おそらく意味:「(*ポインター)++」。 customautocomplete.cpp 277
if(pceltFetched!= NULL) * pceltFetched ++;
ここでは、プログラマは式(* ptr)++を使用する方法を「わからない」。 どうやら、そのようなデザインは十分に無害に見えますが、それでもそのような間違いは非常に一般的です。
Visual Studio:警告C6298:引数 '6':読み取り専用文字列を使用 書き込み可能な文字列引数として。 これは、静的に書き込もうとします 読み取り専用メモリとランダムクラッシュの原因。 c:\ emuleplus \ firewallopener.cpp 183
HRESULT hr = pNSC-> AddPortMapping( riPortRule.m_strRuleName.AllocSysString()、riPortRule.m_byProtocol、 riPortRule.m_nPortNumber、riPortRule.m_nPortNumber、0、L "127.0.0.1"、 ICSTT_IPADDRESS、およびpNSPM);
これは間違いではありませんが、アナライザーのメッセージは公平です。 一般に、これはすべての静的分析ツールの問題です。 それらは完全に正しいメッセージを提供しますが、常にそう遠くないのは本当に間違いです。 これは、ツールや同様のメッセージが役に立たないということですか? いいえ、そうではありません。そのような発言を修正しても一般にコードの品質が向上するからです。
Visual Studio:警告C6314:操作の順序が正しくありません:ビット単位 または、条件式演算子よりも優先順位が高い。 意図を明確にするために括弧を追加します。 c:\ emuleplus \ searchlistctrl.cpp 659
PVS-Studio:V502おそらく '?:'演算子は異なる方法で動作します 予想以上に。 「?:」演算子の優先順位は、 「|」 演算子。 searchlistctrl.cpp 659
menuSearchFile.AppendMenu(MF_STRING | ((iSelectionMark!= -1)&&(dwSelectedCount> 0)&& g_App.m_pServerConnect-> IsConnected()&& ((pCurServer = g_App.m_pServerConnect-> GetCurrentServer())!= NULL)&& (pCurServer-> GetTCPFlags()&SRV_TCPFLG_RELATEDSEARCH))? MF_ENABLED:MF_GRAYED、MP_SEARCHRELATED、 GetResString(IDS_SEARCHRELATED));
ここで(設計が複雑であるため)、誤ったオペレーター優先順位が取得されました。 彼らは何回も世界に繰り返し続けています...まあ、誰が私たちがこれを1行に書くのを防いだのですか? いいえ、プログラマはしばしば「美しく書く」ことを望みます。
PVS-Studio:V519「m_clrSample」オブジェクトに値が2回割り当てられます 続けて。 おそらくこれは間違いです。 fontpreviewcombo.cpp 61
CFontPreviewCombo :: CFontPreviewCombo() { ... m_clrSample = GetSysColor(COLOR_WINDOWTEXT); m_clrSample = RGB(60,0,0); ... }
おそらく彼らは、RGBカラー(60,0,0)がどのように見えるかを試してみて、それを削除するのを忘れていました。
ピクシー
Visual Studio静的アナライザーから18のメッセージがありますが、そのうち0は実際のエラーです。
PVS-Studioから合計65のメッセージがありますが、そのうち5つは実際のエラーです。
PVS-Studio:V519「numRays」オブジェクトに値が2回割り当てられます 続けて。 おそらくこれは間違いです。 bundles.cpp 579 void CGatherBundle :: post(){ numRays = last; numRays = 0; last = 0; 深さ++; }
numRaysが最初に1つの値で初期化され、次に別の値で初期化されることはどれほど疑わしいでしょう。 エラー? かどうか? コードの作者だけが知っています。 しかし、驚くべきことです!
PVS-Studio:V501左に同じサブ式があり、 「|」の右側 演算子:PARAMETER_DPDU | PARAMETER_DPDU quadrics.cpp 880
if(up&(PARAMETER_DPDU | PARAMETER_DPDU)){
どうやらここに何か他のものがあるに違いない。 ところで、静的アナライザーによって検出されたエラーの修正に関する一般的な注意事項。 場合によっては修正が明白であり、誰でも修正できる場合は、コードの作成者のみが自分が書きたいことを理解できる場合があります。 これは、「Wordのような」エラー修正を提供するツールを作成できるかどうかという問題です。
PVS-Studio:V501左に同じサブ式があり、 「|」の右側 演算子:SLC_VECTOR | SLC_VECTOR expression.cpp 2604
ロック(N、getConversion(SLC_VECTOR | SLC_VECTOR、パラメーター[2]));
同様の状況で2回言及されたSLC_VECTORは、もちろんエラーの兆候です。
PVS-Studio:V505「alloca」関数はループ内で使用されます。 これにより、スタックがすぐにオーバーフローすることがあります。 polygons.cpp 1120
インラインvoid triangulatePolygon(...){ ... for(i = 1; i <nloops; i ++){ ... { ... { ... CTriVertex * snVertex =(CTriVertex *) alloca(2 * sizeof(CTriVertex)); ... } while(dVertex!= loops [0]); ... } while(sVertex!= loops [i]); ... } ... }
ネストが大きいため、allocaを呼び出すとスタックオーバーフローが発生する可能性があります。
Virtualdub
Visual Studio静的アナライザーからのメッセージは24個ありますが、そのうち0個は実際のエラーです。
PVS-Studioからのメッセージは合計41件あり、そのうち2件は実際のエラーです。
PVS-Studio:V547式 'c <0'は常にfalseです。 符号なしの型の値が0未満になることはありません。
typedef unsigned short wint_t; wint_t lexgetescape(){ wint_t c = lexgetc(); if(c <0) 致命的(「エスケープシーケンスで検出された改行」); ... }
式は常にfalseであるため、コードは呼び出されません。
PVS-Studio:V557アレイのオーバーランが可能です。 「9」インデックスが指している 配列の限界を超えています。 f_convolute.cpp 73
struct ConvoluteFilterData { long m [9]; 長いバイアス; void * dyna_func; DWORD dyna_size; DWORD dyna_old_protect; BOOL fClip; }; 静的な符号なしlong __fastcall do_conv( 符号なしlong *データ、 const ConvoluteFilterData * cfd、 長い旗、長いピット) { long rt0 = cfd-> m [9]、gt0 = cfd-> m [9]、bt0 = cfd-> m [9]; ... }
アレイの境界を越えた一般的な出口。
ウィンマージ
Visual Studio静的アナライザーからのメッセージの総数は343です。これらのうち、3つは実際のエラーです。
PVS-Studioからのメッセージは合計69個ありますが、そのうち12個は実際のエラーです。
Visual Studio:警告C6313:不正な演算子:ゼロ値フラグ ビット単位のANDではテストできません。 同等性テストを使用して確認します ゼロ値のフラグ。 c:\ winmerge \ src \ bcmenu.cpp 1489
else if(nFlags&MF_STRING){ ASSERT(!(NFlags&MF_OWNERDRAW)); ModifyMenu(pos、nFlags、nID、mdata-> GetString()); }
記録に失敗しました。 もちろん、彼らは何か他のものを記録したかったのですが、すでに「何が起こったのか」。
Visual Studio:警告C6287:冗長コード:左右 部分式は同一です。 c:\ winmerge \ src \ editlib \ ccrystaleditview.cpp 1560
PVS-Studio:V501左に同じサブ式があり、 「||」の右側に 演算子:c == '}' || c == '}' ccrystaleditview.cpp 1560
ブール isclosebrace(TCHAR c) { return c == _T( '}')|| c == _T( '}')|| c == _T( ']') || c == _T( '>'); }
すべてのタイプのブラケットがチェックされるわけではありません。 なんで? よくあることですが、コピーペーストテクノロジはエラーにつながります。
Visual Studio:警告C6287:冗長コード:左右 部分式は同一です。 c:\ winmerge \ src \ mergedoc.cpp 1165 PVS-Studio:V501左に同じサブ式があり、 「||」の右側に 演算子。 mergedoc.cpp 1165
if((m_nBufferType [nBuffer] == BUFFER_UNNAMED)|| (m_nBufferType [nBuffer] == BUFFER_UNNAMED)) nSaveErrorCode = SAVE_NO_FILENAME;
再び悪い状態。 繰り返しますが、それは地雷のせいです。
PVS-Studio:V551この「ケース」ラベルの下のコードは到達不能です。 符号付きchar型の値の範囲:[-128、127]。 ccrystaltextview.cpp 1646
TCHAR ch = strText [i]; スイッチ(ch) { ケース0xB7: ケース0xBB: strHTML + = ch; strHTML + = _T( "<wbr>"); bLastCharSpace = FALSE; nNonbreakChars = 0; 休憩;
そして、これは決して使用されないコードの例です。 事件は書かれているようだが、彼は決して支配権を得られない。 値の範囲が狭すぎるためです。 この場合のTCHARはchar型です。
PVS-Studio:V524「IsValidTextPosX」関数の本体が奇妙です 'IsValidTextPos'関数の本体と完全に同等です (ccrystaltextview.cpp、行3700)。 ccrystaltextview.cpp 3707
bool CCrystalTextView :: IsValidTextPos(定数CPointおよびポイント) { return GetLineCount()> 0 && m_nTopLine> = 0 && m_nOffsetChar> = 0 && point.y> = 0 && point.y <GetLineCount()&& point.x> = 0 && point.x <= GetLineLength(point.y); } bool CCrystalTextView :: IsValidTextPosX(定数CPointおよびポイント) { return GetLineCount()> 0 && m_nTopLine> = 0 && m_nOffsetChar> = 0 && point.y> = 0 && point.y <GetLineCount()&& point.x> = 0 && point.x <= GetLineLength(point.y); } bool CCrystalTextView :: IsValidTextPosY(定数CPointおよびポイント) { return GetLineCount()> 0 && m_nTopLine> = 0 && m_nOffsetChar> = 0 && point.y> = 0 && point.y <GetLineCount(); }
非常に類似した機能...何度も何度もコピーしてパステルを貼り、結果を修正するのを忘れました。 IsValidTextPosX()関数は追加のチェックを行います。
PVS-Studio:V563この「else」ブランチを適用する必要がある可能性があります 前の「if」ステートメント。 bcmenu.cpp 1852
if(IsLunaMenuStyle()) if(!xp_space_accelerators)return; 他に if(!original_space_accelerators)return;
コードを見て、誰が他に言及しているのかを正確に言うのは誰ですか? これはプログラマがやりたかったことでしたか? よろしいですか?
PVS-Studio:V556異なる列挙型の値が比較されます: スイッチ(ENUM_TYPE_A){case ENUM_TYPE_B:...}。 diffwrapper.cpp 956
enum output_style {} ... 列挙型DiffOutputType m_outputStyle; スイッチ(m_options.m_outputStyle) { ケースOUTPUT_CONTEXT:
スイッチの列挙型を少し台無しにしました。 でも怖くない? 怖い?
PVS-Studio:V530関数 'empty'の戻り値は 利用される。 diractions.cpp 1307
void CDirView :: GetItemFileNames(int sel、String&strLeft、 文字列とstrRight)const { UINT_PTR diffpos = GetItemKey(sel); if(diffpos ==(UINT_PTR)SPECIAL_ITEM_POS) { strLeft.empty(); strRight.empty();
空の場合()は空ではありません。 極端に失敗したメソッド名の例。
PVS-Studio:V524「OnUpdateLastdiff」関数の本体が「OnUpdateFirstdiff」関数の本体と完全に同等であることは奇妙です(DirView.cpp、2189行目)。 dirview.cpp 2220
void CDirView :: OnUpdateLastdiff(CCmdUI * pCmdUI) { int firstDiff = GetFirstDifferentItem(); if(firstDiff> -1) pCmdUI->有効(TRUE); 他に pCmdUI->有効(FALSE); } void CDirView :: OnUpdateFirstdiff(CCmdUI * pCmdUI) { int firstDiff = GetFirstDifferentItem(); if(firstDiff> -1) pCmdUI->有効(TRUE); 他に pCmdUI->有効(FALSE); }
他の2つの非常に類似した機能...
PVS-Studio:V501同一のサブ式があります 「pView1-> GetTextBufferEol(line)」の左右 '!='演算子。 mergedoclinediffs.cpp 216
if(pView1-> GetTextBufferEol(line)!= pView1-> GetTextBufferEol(ライン)) {
またはこれ、またはそれ...またはそうではない? おそらくここにpView2があるはずです。
PVS-Studio:V530関数 'empty'の戻り値は 利用される。 varprop.cpp 133
void VariantValue ::クリア() { m_vtype = VT_NULL; m_bvalue = false; m_ivalue = 0; m_fvalue = 0; m_svalue.empty(); m_tvalue = 0; }
繰り返しますが、空()は行をクリアしません。
PVS-Studio:V510「フォーマット」機能の受信は想定されていません 「N」実引数としてのクラス型変数」。PropShel105
String GetSysError(int nerr); ... CString msg; msg.Format( _T(「レジストリキーHKCU /%sを開けませんでした:\ n \ t%d:%s」)、 f_RegDir、retVal、GetSysError(retVal) );
さまざまな緊急事態が発生すると、WinMergeはエラーを報告しようとしますが、場合によっては失敗します。 一見、すべてが正常です。 それは単なる「文字列」のタイプは「std :: wstring」に過ぎません。 したがって、最良の場合はアブラカダブラを出力し、最悪の場合はアクセス違反エラーが発生します。 正しいコードには、c_str()の呼び出しが含まれている必要があります。
PVS-Studio:V534「for」演算子内で間違った変数が比較されている可能性があります。 「i」の確認を検討してください。「BinTrans.cpp 357
//テキストからバイトの翻訳された配列の長さを取得します。 int Text2BinTranslator :: iLengthOfTransToBin( char * src、int srclen) { ... for(k = i; i <srclen; k ++) { if(src [k] == '>') 休憩; } ... }
アナライザーは疑わしいサイクルを見つけました。 このコードは、アクセス違反の傾向があります。 ループは、「>」文字が見つかるか、「srclen」文字の長さの文字列が終了するまで継続する必要があります。 比較のために変数「i」が使用されたのは偶然であり、「k」ではありません。 「>」文字が見つからない場合は、おそらくすべてが終了します。
XUIFramework
Visual Studio静的アナライザーからのメッセージは93個ありますが、そのうち2個は実際のエラーです。
PVS-Studioからのメッセージは合計30件ありますが、そのうち5件は実際のエラーです。
Visual Studio:警告C6269:操作の順序が間違っている可能性があります: 参照解除は無視されます c:\ xui-gui framework \ widgets \ cstatichtml \ ppdrawmanager.cpp 298
PVS-Studio:V532 '* pointer ++'のステートメントを調べることを検討してください パターン。 おそらく意味:「(*ポインター)++」。 ppdrawmanager.cpp 298 for(DWORD pixel = 0; pixel <dwWidth * dwHeight; pixel ++、* pBits ++)
繰り返しますが、誰かが* ptr ++を使用することはできません。 上記のように、よくある間違い。
Visual Studio:警告C6283: 'pBuffer'は配列new []で割り当てられます。 しかし、スカラー削除で削除されました。 c:\ xui-guiフレームワーク\ウィジェット\ cxstatic \ cxstatic.cpp 544
BYTE * pBuffer = new BYTE [nBufferLen]; ... pBufferを削除します。
混乱している人は、削除と削除[]です。 これは問題につながります。 時々問題が現れますが、時々現れません。 しかし、これは必要ありません。
PVS-Studio:V519「m_xSt」オブジェクトには値が2回割り当てられます 続けて。 おそらくこれは間違いです。 resizedlg.cpp 244
m_xSt = CST_RESIZE; m_xSt = CST_RESIZE;
コードから判断すると、m_yStがあるはずです。 しかし、貼り付けを何度も保存するのを止めることはできませんよね?
V531 sizeof()演算子にsizeof()を掛けることは奇妙です。 pphtmldrawer.cpp 258
DWORD dwLen = :: LoadString(hInstDll、dwID、szTemp、 (sizeof(szTemp)* sizeof(TCHAR)));
sizeof(szTemp)/ sizeof(TCHAR)でなければなりません。
PVS-Studio:V556異なる列挙型の値が比較されます: enuHAlign ==中央。 cxstatic.cpp 151
if(enuHAlign == Center)
enuHAlign == Midleでなければなりません。 そして、近くにあるコードにはif(enuVAlign == Middle)がまだありますが、センターがあるはずです。 要するに、混乱した列挙型。
PVS-Studio:V501左に同じサブ式があり、 「||」の右側に 演算子。 resizedlg.cpp 157
HDWP CItemCtrl :: OnSize(...) { ... if(m_styTop == CST_ZOOM || m_styTop == CST_ZOOM || m_styBottom == CST_DELTA_ZOOM || m_styBottom == CST_DELTA_ZOOM) ... }
コードはおそらく次のようになります。
HDWP CItemCtrl :: OnSize(...) { ... if(m_styTop == CST_ZOOM || m_styTop == CST_DELTA_ZOOM || m_styBottom == CST_ZOOM || m_styBottom == CST_DELTA_ZOOM) ... }
比較結果
特定の結論は出しません。 一部のプロジェクトでは、1つのツールが他のツールより優れていることが証明されました。