PVS-Studioと敵対的な環境

敵対的な生息地

別の話は、プログラムが外の世界と対話することがどれほど難しいかです。 一見すると、静的アナライザーに問題はないはずです。 入力ファイル、追加情報を受け取り、レポートを生成する必要があります。 しかし、いつものように、悪魔は詳細にあります。



PVS-Studioは非常に高品質な製品だと思います。 配布キットは、ほぼいつでも作成およびレイアウトできます。 さまざまなレベルとタイプの非常に多くの自動テストを使用しています。 それらの一部の説明は次のとおりです。「 コードアナライザーのテスト方法 」 今、それらの多くがあります。 たとえば、現在では静的解析のために、独自のアナライザーだけでなく、Clangも使用しています。 修正されたバージョンがすべてのテストに合格した場合、ユーザーに安全に発行できます。





残念なことに、内部コードのすべての美しさと信頼性は、敵対的な環境の影響によりばらばらになる場合があります。 その結果、製品の印象全体が悪化します。 私たちは非難するつもりはないようですが、私たちの製品は機能しません。 多数の例を挙げることができます。 頭に浮かぶ最初のもの:

さて、私たちの製品の印象を無邪気にするのがいかに簡単かといった別の話をします。



潜在的なユーザーの1人が、PVS-Studioの奇妙な動作に関する質問を送信しました。



現在、試用版を推進しており、完全版の購入を検討しています。 しかし、分析は結論の妥当性に疑問を投げかけるニュアンスに出会いました。



以下は、エラーを示すスクリーンショットです。



filePathとcachePathは未使用としてマークされています(警告V808 )が、宣言後の次の行で文字通り使用されていることがわかります。



アナライザーの同様の動作を説明できますか?



スクリーンショットでは、次の形式のコードを見ることができます(コードが変更されています):

std::string Foo() { std::string filePath(MAX_PATH + 1, 0); std::string cachePath = "d:\\tmp"; if (!GetTempFileName(cachePath.c_str(), "tmp", 0, &filePath.front())) throw MakeSystemError("...", GetLastError(), __SOURCE__); return std::move(filePath); }
      
      





まあ何と言って。 アナライザーの恥。 結局のところ、それは本当に愚かさを言います。 変数filePathおよびcachePathは明示的に使用されます。 警告の理由はありません。 また、関数の長さは1000行になります。 いいえ、この機能は不名誉のために単純です。



すべて、第一印象は台無しです。 次に、調査の結果について説明します。



PVS-Studioアナライザーは、Visual C ++コンパイラー(CL.exe)またはClangを使用してファイルを前処理します。 Clangの使用方法の詳細については、「 PVS-StudioとClangの相互作用について少し説明します。」で説明しています。



Visual C ++コンパイラプリプロセッサは正常に動作しますが、非常に低速です。 Clangは高速ですが、多くはサポートしていないか、正しく動作していません。 Clang開発者はVisual C ++と非常によく互換性があると主張していますが、これは正しくありません。 Visual C ++でサポートされていないことや、Visual C ++とは異なることを行っている小さなことがたくさんあります。 アナライザーにとって、今回のように、これらの小さなことは致命的です。



デフォルトでは、最初のPVS-StudioアナライザーはClangを使用してファイルの前処理を試みます。 ただし、彼は次のことを知っています。Clangは、Visual C ++ができることを常に前処理できるとはほど遠いです。 前処理エラーが発生すると、CL.exeが起動します。 これはClangを無用に起動するのに少し時間がかかりますが、全体的にこのアプローチは* .iファイルの取得に多くの時間を節約します。



この場合、助けにはなりませんでした。 Clangはファイルを「正常に」前処理しましたが、出力はabracadabraでした。



不適切な動作の理由は、次のようにコードで宣言された__SOURCE__マクロでした。

 #define __SLINE_0__(_line) #_line #define __SLINE__(_line) __SLINE_0__(_line) #define __SOURCE__ __FILE__":"__SLINE__(__LINE__)
      
      





文字列を前処理する場合:

 throw MakeSystemError(_T("GetTempFileName"), GetLastError(), __SOURCE__);
      
      





次のようになります。

 MakeSystemError("GetTempFileName", GetLastError(), "..path.."":""37");
      
      





これは、まさにVisual C ++コンパイラが行うことです。 すべてが素晴らしいです。 アナライザーはこのコードを正しく処理します。



CL.exeを常に使用するように設定でPVS-Studioを明示的に設定すると、誤検知はなくなります。 ただし、Clangを起動すると、アナライザーは誤ったコードを処理します。



Clangはマクロを圧倒することはできず、出力では次のようになります。

 throw MakeSystemError("GetTempFileName", GetLastError(), "..path.."":"__SLINE__(37));
      
      





__SLINE__マクロは非公開のままでした。



結果は、C ++言語の観点からは受け入れられない、誤った設計でした。 それを満たしたPVS-Studioアナライザーは、分析をさらに続行するために、何らかの形で誤ったコードをバイパスしようとします。 ファイルを完全に処理しないよりも、何かをスキップする方が良いでしょう。 多くの場合、このような省略は分析の結果に影響しません。



しかし、今回は、間違った場所を回避するのに苦痛はありませんでした。 ブロック全体がスローされました:

 if (!GetTempFileName(cachePath.c_str(), "tmp", 0, &filePath.front())) throw MakeSystemError("....", GetLastError(), __SOURCE__); return std::move(filePath);
      
      





まさにそうなった...アナライザーは、できる限りのことをしました。



このフラグメントはアナライザーには存在しないため、変数は初期化されず、使用されません。 これにより、誤検知が発生します。



この問題の解決策の1つは、常にVisual C ++のプリプロセッサを使用することです。 しかし、欠点があります-遅い分析。



したがって、この場合、別のパスを選択しました。 連絡先の会社はPVS-Studioの購入を検討しているため、この特定のケースを考慮し、コードの別のバックアップを作成しました。 いですが、実用的です。 ユーザーのプロジェクトで遭遇するいくつかのニュアンスをバイパスする多くの異なる特別な場所が既にあります。 これはサポートの一形態です。



そのため、今回はClangに実装されたプリプロセッサに失望しました。 外部エラーに関する次の同様の記事を書く理由は何でしょうか興味深いです。



そして私たちは生きています。 ご清聴ありがとうございました。



プロジェクトで静的コードアナライザーを試してみ てください 。何か問題が発生した場合は、 当社までご連絡ください 。 多くの場合、否定的な態度を肯定的な態度に変えます。



All Articles