マイクロソフトは最近、プロジェクトのオープンソースコードを投稿しただけでなく、他の企業もこの傾向を追っています。 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);
例が合成であるという事実に誤りを見つけることができますが、この本質は変わりません。
さらに、このコードは次のようにコメントされています。
おっと 代わりに、 ""は
nullを返し
ます 。
続けましょう。
ライブラリには
FastStringクラスがあり
ます 。 説明:
StringBuilderの高速な代替 。 実際、このクラスには
StringBuilder型のフィールドが含まれています。
FastStringクラスのコンストラクターは、対応するフィールドを初期化する
Initメソッドを呼び出します。
コンストラクターの1つのコード:
public FastString() { Init(initCapacity); }
Initメソッドのコード:
private void Init(int iniCapacity) { sb = new StringBuilder(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で動作)などの
警告です。
これのいずれかが無関係である場合、次のことができます。
おわりに

記事が短いという事実にもかかわらず、私はアナライザーの警告を手で「触って」喜んでいます-アナライザーが誓うものが実際にどのように現れるかを見るために。
プロジェクトの成功者、発見された問題の修正を願っており、オープンソースコミュニティへの一歩を称賛したいと思います!
残りの部分は、コードでアナライザーを試して、何がおもしろいかを確認することをお勧めします。
最高!

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