マイクロソフトは最近、プロジェクトのオープンソースコードを投稿しただけでなく、他の企業もこの傾向を追っています。 PVS-Studio開発者にとって、これはアナライザーを再度テストし、検出できる興味深いものを確認し、プロジェクトの作成者に通知するのに最適な方法です。 今日は、Fast Reportsプロジェクトの内部を調べます。
何がチェックされましたか?
FastReportは、 Fast Reportsによって開発されたレポートジェネレーターです。 C#で記述され、.NET Standard 2.0+と互換性があります。 プロジェクトのソースコードは最近、GitHubに投稿され 、さらに分析するためにダウンロードされました。
レポートでは、テキスト、画像、線、図形、図、表、バーコードなどの使用がサポートされます。データに加えて、表紙と裏ページを含むシングルページとマルチページにすることができます。 データソースには、XML、CSV、Json、MS SQL、MySql、Oracle、Postgres、MongoDB、Couchbase、RavenDB、SQLiteを使用できます。
レポートテンプレートを作成するにはさまざまな方法があります。コードから。 xmlファイルとして。 オンラインデザイナーまたはFastReport Designer Community Editionを使用します。
必要に応じて、ライブラリをNuGetパッケージとしてダウンロードできます 。
製品の機能の詳細については、プロジェクトのGitHubページをご覧ください 。
プロジェクトのアセンブリに問題はありませんでした-Visual Studio 2017からアセンブリし、その後PVS-Studioプラグインでチェックしました。
PVS-Studioは、C、C ++、C#、Javaコードのエラーを探す静的アナライザーです。 C#コードを分析する場合、PVS-Studioプラグインを使用してVisual Studio IDEアナライザーを使用するか、コマンドラインからプロジェクトを確認できます。コマンドラインユーティリティにはPVS-Studio_Cmd.exeコマンドラインユーティリティが使用されます。 必要に応じて、ビルドサーバーで分析を構成するか、分析結果をSonarQubeに接続できます。
さて、今回は何が面白かったのか見てみましょう。
このプロジェクトは小規模なので、多くのタイプミスや疑わしい場所に頼るべきではありません。 見つかったものを見て、実際に何かを再現してみましょう。
メガネなど
次のメソッドを満たしました。
public override string ToString() { if (_value == null) return null; return this.String; }
PVS-Studio警告 : V3108 「ToSting()」メソッドから「null」を返すことは推奨されません。 Variant.cs 1519
はい、オーバーライドされたToString()メソッドからnullを返すこと自体はエラーではありませんが、それでもスタイルが悪いです。 これは、とりわけMicrosoftのドキュメントで示されています : ToString()オーバーライドはEmptyまたはnull stringを返すべきではありません 。 ToString()の戻り値としてnullが返されることを期待していない開発者は、以下のコードの実行中にArgumentNullExceptionがスローされると、不愉快な驚きを感じるかもしれません(拡張メソッドがIEnumerable <T>に対して呼び出される場合)。
Variant varObj = new Variant(); varObj.ToString().Contains(character);
例が合成であるという事実に誤りを見つけることができますが、この本質は変わりません。
さらに、このコードは次のようにコメントされています。
/// <summary> /// Returns <see cref="String"/> property unless the value on the right /// is null. If the value on the right is null, returns "". /// </summary> /// <returns></returns>
おっと 代わりに、 ""はnullを返します 。
続けましょう。
ライブラリにはFastStringクラスがあります 。 説明: StringBuilderの高速な代替 。 実際、このクラスにはStringBuilder型のフィールドが含まれています。 FastStringクラスのコンストラクターは、対応するフィールドを初期化するInitメソッドを呼び出します。
コンストラクターの1つのコード:
public FastString() { Init(initCapacity); }
Initメソッドのコード:
private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity); //chars = new char[iniCapacity]; //capacity = iniCapacity; }
必要に応じて、 StringBuilderプロパティを介してsbフィールドにアクセスできます。
public StringBuilder StringBuilder { get { return sb; } }
FastStringには、合計で3つのコンストラクターがあります。
public FastString(); public FastString(int iniCapacity); public FastString(string initValue);
私は、最初のデザイナーの体に、残りの2つの推測を行うことを既に示しましたが、それも難しくはないと思います。 そして今、注意。 次のコードが出力するものを推測します。
FastString fs = new FastString(256); Console.WriteLine(fs.StringBuilder.Capacity);
注意、答え:
意外と? 対応するコンストラクターの本体を見てみましょう。
public FastString(int iniCapacity) { Init(initCapacity); }
私たちの記事の定期的な読者は、ここで問題を見つけることにすでに目を向けているはずです。 アイアナライザー(香り、ロジック、あなたが望むものと呼ぶ)は間違いなく麻痺しており、彼は問題を発見しました: V3117コンストラクターパラメーター 'iniCapacity'は使用されません。 FastString.cs 434
クラスコードに定数フィールドinitCapacityが含まれていることは偶然です。これは、コンストラクタパラメータiniCapacityではなく、 Initメソッドへの引数として渡されます...
private const int initCapacity = 32;
同様の名前を使用する場合は、非常に注意する必要があります。 同様の名前の使用に関連するエラーが発生した場所-C、C ++、C#、Javaのプロジェクト-この種類のタイプミスはどこにでもありました...
ちなみに、タイプミスについて。
次の簡単な例を作成して、その仕組みを見てみましょう。
static void Main(string[] args) { TextObject textObj = new TextObject(); textObj.ParagraphFormat = null; Console.WriteLine("Ok"); }
ご想像のとおり、出力は文字列「Ok」とは異なります:)
どっち? たとえば、次のとおりです。
問題はParagraphFormatプロパティにあり、同様の名前を使用することにあります。
public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } set { ParagraphFormat = value; } }
PVS-Studio警告 : V3110 'ParagraphFormat'プロパティ内で無限再帰が発生する可能性があります。 TextObject.cs 281
ParagraphFormatプロパティは、 paragraphFormatフィールドのラッパーです。 さらに、getプロパティアクセサーのスペルは正しいですが、setプロパティアクセサーには迷惑なタイプミスが含まれています。フィールドの代わりに、同じプロパティでレコードが発生し、再帰が発生します。 繰り返しますが、同様の名前に関連する間違いです。
次のコードスニペットを検討してください。
public override Run Split(float availableWidth, out Run secondPart) { .... if (r.Width > availableWidth) { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } else { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } .... }
PVS-Studio警告 : V3004 「then」ステートメントは「else」ステートメントと同等です。 HtmlTextRenderer.cs 2092
少しコピーアンドペーストし、式r.Width> availableWidthの値に関係なく、同じアクションが実行されます。 ifステートメントを削除するか 、ブランチのいずれかのロジックを変更する必要があります。
public static string GetExpression(FindTextArgs args, bool skipStrings) { while (args.StartIndex < args.Text.Length) { if (!FindMatchingBrackets(args, skipStrings)) break; return args.FoundText; } return ""; }
アナライザーの警告 : V3020ループ内の無条件の「戻り」。 CodeUtils.cs 262
無条件のreturnステートメントにより、上記のループに対して実行される反復は1つだけです。 おそらく、このコードはリファクタリング後に出てきたのかもしれませんし、ループなしで実行できることを行うための異常な方法かもしれません。
private int FindBarItem(string c) { for (int i = 0; i < tabelle_cb.Length; i++) { if (c == tabelle_cb[i].c) return i; } return -1; } internal override string GetPattern() { string result = tabelle_cb[FindBarItem("A")].data + "0"; foreach (char c in text) { int idx = FindBarItem(c.ToString()); result += tabelle_cb[idx].data + "0"; } result += tabelle_cb[FindBarItem("B")].data; return result; }
警告PVS-Studio : V3106負のインデックス値の可能性があります。 「idx」インデックスの値は-1に達する可能性があります。 BarcodeCodabar.cs 70
潜在的に危険なコード。 FindBarItemメソッドは、パラメーターとして渡された要素が見つからない場合、 -1を返すことができます。 呼び出しコード( GetPatternメソッド)では、この値はidx変数に書き込まれ、事前検証なしでtabelle_cb配列のインデックスとして使用されます。 インデックス-1にアクセスすると、タイプIndexOutOfRangeExceptionの例外がスローされます。
続けましょう。
protected override void Finish() { .... if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { if (saveStreams) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(generatedStreams[fileIndex]); } else { .... } .... } .... } .... }
PVS-Studio警告 : V3022式「saveStreams」は常にfalseです。 HTMLExport.cs 849
fileIndex値を取得してDoPageEndメソッドを呼び出す上記のコードは実行されません。これは、コード内の2番目のsaveStreams式の結果が常にfalseになるためです。
最も興味深いのは、おそらくこれだけです( Mono分析の精神で記事を期待していなかったのですか?)。 他にもアナライザーの警告がありましたが、それらを記事に含めるほど興味深いとは思えませんでした(一部は常に舞台裏に残っています)。
設計を知ることは彼らの分析に役立つので、理想的には著者は自分でこれらの警告を見るべきです。 これらは、 V3083 (イベントハンドラへの潜在的に危険な呼び出し)、 V3022 (条件は常にtrue / false(この場合、単一の値を返すメソッドが原因であることが多い))、 V3072 、 V3073 ( IDisposableで動作)などの警告です。
これのいずれかが無関係である場合、次のことができます。
- 検出されたパターンがプロジェクトで正確に正しい場合、 診断をオフにします。
- 警告がそれほど多くない場合は、 警告を誤検知としてマークします 。
- コードにコメントを挿入したくない場合は、抑制データベースに警告を追加します 。 ただし、アナライザを使用するすべての人が抑制ベースにアクセスする必要があります。
おわりに
記事が短いという事実にもかかわらず、私はアナライザーの警告を手で「触って」喜んでいます-アナライザーが誓うものが実際にどのように現れるかを見るために。
プロジェクトの成功者、発見された問題の修正を願っており、オープンソースコミュニティへの一歩を称賛したいと思います!
残りの部分は、コードでアナライザーを試して、何がおもしろいかを確認することをお勧めします。
最高!
この記事を英語圏の聴衆と共有したい場合は、翻訳へのリンクを使用してください:セルゲイヴァシリエフ。 ワイルドウエストで最速のレポート-少数のバグ...