ワイルドウエストで最速のレポート。 さらに、いくつかのバグ...





写真3






マイクロソフトは最近、プロジェクトのオープンソースコードを投稿しただけでなく、他の企業もこの傾向を追っています。 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ページをご覧ください



写真8







プロジェクトのアセンブリに問題はありませんでした-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);
      
      





注意、答え:



写真2







意外と? 対応するコンストラクターの本体を見てみましょう。



 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」とは異なります:)



どっち? たとえば、次のとおりです。



写真1







問題は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-StudioV3106負のインデックス値の可能性があります。 「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(この場合、単一の値を返すメソッドが原因であることが多い))、 V3072V3073IDisposableで動作)などの警告です。



これのいずれかが無関係である場合、次のことができます。





おわりに



写真4







記事が短いという事実にもかかわらず、私はアナライザーの警告を手で「触って」喜んでいます-アナライザーが誓うものが実際にどのように現れるかを見るために。



プロジェクトの成功者、発見された問題の修正を願っており、オープンソースコミュニティへの一歩を称賛したいと思います!



残りの部分は、コードでアナライザーを試して、何がおもしろいかを確認することをお勧めします。



最高!











この記事を英語圏の聴衆と共有したい場合は、翻訳へのリンクを使用してください:セルゲイヴァシリエフ。 ワイルドウエストで最速のレポート-少数のバグ...



All Articles