MonoDevelopのエラーを探す


重要なイベントがPVS-Studioアナライザーのライフサイクルで発生しました-最新バージョンでは、C#で記述されたコードをチェックする機能が追加されました。 このアナライザーの開発者の一人として、私はプロジェクトをチェックせずに通り過ぎることができませんでした。 小さくて未知のプロジェクトをチェックすることに興味を持つ人はほとんどいないので、有名なものを選択する必要があり、その選択はMonoDevelopに委ねられました。

プロジェクトについて少し


MonoDevelopは、C#、Java、Boo、Nemerle、Visual Basic .NET、Vala、CIL、C、およびC ++アプリケーションを作成するために設計された無料の開発環境です。 Embarcadero TechnologiesのOxygeneのサポートも計画されています。



当初、これはMono / GTK +のSharpDevelopポートでしたが、それ以来、プロジェクトは初期状態からは程遠い状態になっています。

MonoDevelopはMonoプロジェクトの一部です。 スクリプトを作成する手段としてUnity3D配布キットに組み込まれていますが、古いバージョン(4.0.1)です。

この開発環境の機能の中で、構文の強調表示、コードの折りたたみ、コード補完、クラスブラウザー、プラグインのサポート、統合デバッガー、ビジュアルフォームデザイナー、ユニットテストが強調表示されます。

プロジェクトのソースコードはGitHub対応するリポジトリで入手でき、 アセンブリの手順はプロジェクトの公式Webサイトで説明されています。

何がチェックされましたか?


前述したように、プロジェクトはCVSコードを分析する機能が追加されたPVS-Studio静的コードアナライザーの最新バージョンを使用して分析されました。 これはC#アナライザーの最初のリリースであり、現在40以上の診断ルールを実装しています。 このバージョンはまだC ++アナライザーと同じくらい開発されていないことは明らかですが、このツールを使用すると、すでに非常に興味深いエラーを見つけることができます(その一部はこの記事で説明します)。 C#アナライザーは個別の製品ではなく、同じPVS-Studioの一部であり、別のプログラミング言語で記述されたコードを分析する方法を単純に認識しています。

このリンクからアナライザーの最新バージョンをダウンロードしてください

分析結果に関するいくつかの言葉


分析の結果、95件のプロジェクトで8,457個のファイルが検証されました。

アナライザーは、最初の118個の警告、2番目の128個の警告、3番目のレベルの475個の警告を生成しました。

一部の人は、それほど多くのファイルにはそれほど多くないと言うかもしれません。 ただし、ここでは、現時点で実装されている診断がC ++アナライザーよりも少ないという事実を考慮する価値があります。 次に、アナライザーは1回限りのチェックでは無効です。 これは数回繰り返されていますが、もう一度言及する価値があります-静的解析ツールの使用から十分に利益を得るために、それらは一度ではなく定期的に使用されるべきです。 これにより、トラブルシューティングの時間が節約され、その結果、プロジェクト開発が安価で簡単になります。

分析結果


この記事では、見つかったエラーのうち最も興味深いもののいくつかを検討します。見つかったすべてのエラーを検討すると、この記事のボリュームが不適切な次元に増えるためです。 この記事は、プロジェクトのコード例とともに特定の種類のエラーの説明を含むサブセクションに分かれています。 したがって、すぐに最も興味深いエラーを表示することができます。

演算子の左右に同じ式


このセクションには、 'A ||という形式のエラーの説明が含まれています。 A '。 多くの場合、このようなエラーは、タイプミスまたはコピーペーストの失敗とプログラマーの不注意に起因します。 多くの場合、このようなエラーは、特に変数の名前が十分に長く、1文字だけ異なる場合、大量のコードで見つけるのが難しい場合があります。 原則として、別の変数の使用が暗示されますが、そのようなチェックは単に冗長なコードである場合があります。 以下のすべての詳細。
protected override SourceCodeLocation GetSourceCodeLocation (string fixtureTypeNamespace, string fixtureTypeName, string methodName) { if (string.IsNullOrEmpty (fixtureTypeName) || string.IsNullOrEmpty (fixtureTypeName)) return null; .... } 

アナライザーの警告: V3001 「||」の左側と右側には、同一のサブ式「string.IsNullOrEmpty(fixtureTypeName)」があります。 演算子。 MonoDevelop.NUnit NUnitProjectTestSuite.cs 84

エラーは肉眼で見える-条件では、同じ文字列変数が 'null'の等価性または 'String.Empty'の等価性について二重チェックされます。 以下のコードでは(知覚を複雑にしないために、本文全体はここに記載されていないため、単語をとる)変数 'fixtureTypeNamespace'に対して同様のチェックが実行されるため、この条件の2番目のチェックは変数 'methodNameをメソッドへの引数としてとる'またはまったく。

同様のエラーの別の例:
 bool TryAddDocument (string fileName, out OpenRazorDocument currentDocument) { .... var guiDoc = IdeApp.Workbench.GetDocument (fileName); if (guiDoc != null && guiDoc.Editor != null) .... guiDoc.Closed += (sender, args) => { var doc = sender as MonoDevelop.Ide.Gui.Document; if (doc.Editor != null && doc.Editor != null) .... } .... } 

アナライザーの警告: V3001 「&&」演算子の左側と右側に同一の副次式「doc.Editor!= Null」があります。 MonoDevelop.AspNet RazorCSharpParser.cs 180

再び、同じ式内で2つの同一のチェック。 理論的には、演算子「as」を使用して変数「sender」を変数「doc」にキャストした後、値「null」を書き込むことができます。 その結果、「doc.Editor!= Null」をチェックすると、「NullReferenceException」タイプの例外がスローされます。 コードの修正バージョンは次のようになります。
 if (doc != null && doc.Editor != null) 

別のエラーコード:
 static MemberCore GetLaterDefinedMember (MemberSpec a, MemberSpec b) { var mc_a = a.MemberDefinition as MemberCore; var mc_b = b.MemberDefinition as MemberCore; if (mc_a == null) return mc_b; if (mc_b == null) return mc_a; if (a.DeclaringType.MemberDefinition != b.DeclaringType.MemberDefinition) return mc_b; if (mc_a.Location.File != mc_a.Location.File) return mc_b; return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a; } 

アナライザーの警告: V3001 「!=」演算子の左側と右側に同じサブ式「mc_a.Location.File」があります。 ICSharpCode.NRefactory.CSharp membercache.cs 1319

そのような間違いは目を引くことはないかもしれませんが、分析器は人ではなく、そのようなことを見逃しません。 コードから、「mc_a」オブジェクトの「File」プロパティがそれ自体と比較されることがわかりますが、「mc_b」オブジェクトの対応するプロパティと比較する必要があることは明らかです。

正しいコードは次のとおりです。
 if (mc_a.Location.File != mc_b.Location.File) 

冗長コード:
 public override AppResult Property (string propertyName, object value) { if (resultIter != null && resultIter.HasValue) { var objectToCompare = TModel.GetValue (resultIter.Value, Column); return MatchProperty (propertyName, objectToCompare, value); } return MatchProperty (propertyName, ParentWidget, value); } TreeIter? resultIter; 

アナライザーの警告: V3001 「&&」演算子の左側と右側には、同一のサブ式「resultIter!= Null」があります。 MonoDevelop.Ide GtkTreeModelResult.cs 125

変数「resultIter」はnull許容型であるため、「resultIter!= Null」および「resultIter.HasValue」という形式のチェックは同一であり、いずれか1つに制限することができます。

まったく同じコードがもう1回出会った。 対応するアナライザー警告:

V3001 「&&」演算子の左側と右側には、同一のサブ式「resultIter!= Null」があります。 MonoDevelop.Ide GtkTreeModelResult.cs 135

次のコードスニペットを検討してください。
 Accessibility DeclaredAccessibility { get; } bool IsStatic { get; } private bool MembersMatch(ISymbol member1, ISymbol member2) { if (member1.Kind != member2.Kind) { return false; } if (member1.DeclaredAccessibility != member1.DeclaredAccessibility || member1.IsStatic != member1.IsStatic) { return false; } if (member1.ExplicitInterfaceImplementations().Any() || member2.ExplicitInterfaceImplementations().Any()) { return false; } return SignatureComparer .HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors( member1, member2, this.IsCaseSensitive); } 

アナライザーの警告:
別のタイプミス。 そして1つではなく、一度に2つ。 繰り返しますが、同じオブジェクト( 'member1')のプロパティ間で比較が行われます。 プロパティはプリミティブであり、追加のロジックがないため、このようなチェックも意味を失います。 はい。コードは、オブジェクト「member1」と「member2」のプロパティを比較する必要があることを示しています。 コードの正しいバージョン:
 if (member1.DeclaredAccessibility != member2.DeclaredAccessibility || member1.IsStatic != member2.IsStatic) 


変数をそれ自体に割り当てる


前のような一般的な種類のエラーではありませんが、それほど興味深いものではありません。 多くの場合、誤った状況は、メソッド内のクラスのメンバーに、渡された引数の1つの値を割り当てる必要がある場合です。これらの名前は、最初の文字の場合のみ異なることがよくあります。 間違いを犯しやすいです。 また、変数をそれ自体に割り当てる単純なケースもあり、これらがプロパティである場合、コンパイラは警告を出しません。 複雑なロジックがゲッター/セッタープロパティにかかっている場合、そのようなアクションは理解できますが、ない場合、割り当ては少なくとも奇妙に見えます。 しかし、私たちは根拠がないわけではないので、そのようなエラーの例を見てみるほうがよいでしょう。
 public ViMacro (char macroCharacter) { MacroCharacter = MacroCharacter; } public char MacroCharacter {get; set;} 

アナライザーの警告: V3005 「MacroCharacter」変数はそれ自体に割り当てられます。 Mono.TextEditor ViMacro.cs 57

上記の説明-プロパティの名前とコンストラクター引数は最初の文字の場合にのみ異なるため、プロパティの値は引数として渡された値に書き込むのではなく、それ自体に書き込まれます。 プロパティの定義を見ると、追加のロジックが含まれていないことがわかります。
 public ViMark (char markCharacter) { MarkCharacter = MarkCharacter; } public char MarkCharacter {get; set;} 

アナライザーの警告: V3005 「MarkCharacter」変数はそれ自体に割り当てられます。 Mono.TextEditor ViMark.cs 45

エラーは前のものとまったく同じです。 繰り返しますが、変数名の最初の文字は混乱しています。そのため、コンストラクターが期待どおりに機能しません。
 public WhitespaceNode(string whiteSpaceText, TextLocation startLocation) { this.WhiteSpaceText = WhiteSpaceText; this.startLocation = startLocation; } public string WhiteSpaceText { get; set; } 

アナライザーの警告: V3005 「this.WhiteSpaceText」変数はそれ自体に割り当てられます。 ICSharpCode.NRefactory.CSharp WhitespaceNode.cs 65

このエラーも以前のエラーと似ていますが、今回は2つの割り当ての1つでプログラマーが封印されなかったため、コードはより興味深いものになりました。 クイックタイピングでは、特に自動コード置換の手段を使用する場合、このようなエラーを見逃しやすいです。 ただし、静的アナライザーを使用して新しいコードを定期的にチェックすることにより、これを回避できました。 たとえば、PVS-Studioでは、コンパイル後に新しいコードを自動的にチェックできますインクリメンタル分析モードを参照)。
 void OptionsChanged (object sender, EventArgs e) { gutterMargin.IsVisible = Options.ShowLineNumberMargin; iconMargin.IsVisible = iconMargin.IsVisible; .... } public bool IsVisible { get; set; } 

アナライザーの警告: V3005 「iconMargin.IsVisible」変数はそれ自体に割り当てられます。 MonoDevelop.HexEditor HexEditor.cs 241

これは、サブセクションの冒頭で説明した2番目のタイプのエラーです。 プロパティ値は独自に割り当てられますが、このプロパティに類似した名前のローカル変数はありません。 さらに、このプロパティは追加のロジックに関連付けられていません。 確かに言うことはもはやできませんが、おそらく正しいコードは次のようになっているはずです。
 iconMargin.IsVisible = gutterMargin.IsVisible; 


選択の錯覚


面白い副題ですね。 ただし、たとえば、診断メッセージV3004またはV3012を使用して検出されたエラーなど、いくつかのタイプのエラーを最も正確に説明しています。 このタイプのエラーの本質は、チェック対象の条件(「if」演算子のV3004および3項のV3012)がtrueまたはfalseであるかどうかに関係なく、同じアクションが常に実行されるか、同じ結果が返されることです。 残念ながら、V3004警告によって診断されたエラーはプロジェクトで見つかりませんでしたが、V3012警告がいくつかありました。これについては以下で説明します。
 public enum WindowCommands { NextDocument, PrevDocument, OpenDocumentList, OpenWindowList, SplitWindowVertically, SplitWindowHorizontally, UnsplitWindow, SwitchSplitWindow, SwitchNextDocument, SwitchPreviousDocument } protected static void Switch (bool next) { if (!IdeApp.Preferences.EnableDocumentSwitchDialog) { IdeApp.CommandService.DispatchCommand ( next ? WindowCommands.NextDocument : WindowCommands.NextDocument); return; } var toplevel = Window.ListToplevels () .FirstOrDefault (w => w.HasToplevelFocus) ?? IdeApp.Workbench.RootWindow; var sw = new DocumentSwitcher (toplevel, next); sw.Present (); } 

アナライザーの警告: V3012 「?:」演算子は、その条件式に関係なく、常に1つの同じ値WindowCommands.NextDocumentを返します。 MonoDevelop.Ide WindowCommands.cs 254

三項演算子は常に同じ列挙要素( 'WindowCommands.NextDocument')を返します。 「next」変数の値が「false」の場合、要素「WindowCommands.PrevDocument」が返されるはずであると想定します。

繰り返しますが、このようなエラーは自動コード置換の使用が原因であると疑われています。 迅速な作業では、コードの作成に役立つツールがエラーの作成に役立つことにまったく気付かない場合があります。 ただし、これらはこの記事の範囲外のこのトピックに関する仮定と議論にすぎません。

私は別の同様の例に会った
 private void StartTestElement(ITestResult result) { ITest test = result.Test; TestSuite suite = test as TestSuite; if (suite != null) { xmlWriter.WriteStartElement("test-suite"); xmlWriter.WriteAttributeString("type", suite.TestType); xmlWriter.WriteAttributeString("name", suite.TestType == "Assembly" ? result.Test.FullName : result.Test.FullName); } .... } 

アナライザーの警告: V3012 「?:」演算子は、その条件式に関係なく、常に1つの同じ値result.Test.FullNameを返します。 GuiUnit_NET_4_5 NUnit2XmlOutputWriter.cs 207

コードフラグメントからわかるように、式 'suite.TestType ==“ Assembly”'はtrueまたはfalseになり、三項演算子の結果は 'FullName'プロパティの値になります。

演算子「as」によるキャスト後に「null」の等価性について間違った変数をチェック


そして、これはC#固有の状況です。 さらに、テスト済みのプロジェクトから判断すると、これは特定のエラーパターンであり、個別のケースではありません。 ご存知のように、「as」演算子を使用してキャストできなかった場合、結果はnullになります(「InvalidCastException」型の例外がスローされた場合、構文「(type_name)arg」を使用した明示的なキャストとは対照的です) ) 多くの場合、このようなキャストの後、チェックが実行されて成功したことを確認します。 ただし、キャスト結果ではなく、還元可能な変数を誤ってチェックすることにより、多くの場合ミスを犯します。 そのような場合のいくつかを以下で説明します。
 public override bool Equals (object o) { SolutionItemReference sr = o as SolutionItemReference; if (o == null) return false; return (path == sr.path) && (id == sr.id); } 

アナライザーの警告: V3019 「as」キーワードを使用した型変換後に、誤った変数がnullと比較される可能性があります。 変数「o」、「sr」を確認します。 MonoDevelop.Core SolutionItemReference.cs 81

このコードでは、タイプ「オブジェクト」の変数「o」がタイプ「SolutionItemReference」に変換されます。 そのようなキャストが失敗すると、値「null」が変数「sr」に書き込まれます。 その結果、「o == null」のチェックは成功し(当然、「o」が「null」でない場合)、「path == sr.path」をチェックすると、「NullReferenceException」タイプの例外がスローされます。 これらはすべて、適切な場所で正しい変数をチェックすることで回避できます。
  if (sr == null) return false; 

このコードの別の例:
 void OnTokenSelectionChanged (object sender, EventArgs args) { TreeSelection selection = sender as TreeSelection; if (sender != null) { TreeIter iter; TreeModel model = (TreeModel)tokensStore; if (selection.GetSelected (out model, out iter)) { entryToken.Text = (string)tokensStore.GetValue (iter, 0); comboPriority.Active = (int)tokensStore.GetValue (iter, 1); } else { entryToken.Text = String.Empty; comboPriority.Active = (int)TaskPriority.Normal; } } } 

アナライザーの警告: V3019 「as」キーワードを使用した型変換後に、誤った変数がnullと比較される可能性があります。 変数「送信者」、「選択」を確認してください。 MonoDevelop.Ide TasksOptionsPanel.cs 123

状況は前の状況とまったく同じです。 「送信者」を「TreeSelection」に「null」にキャストした後、間違った変数がチェックされます。そのため、「NullReferenceException」を取得する危険があります。

同じエラーパターンを持つ同様のコード例がさらに2回検出されました。

同様の条件の繰り返しチェック


これらの式で使用される変数はそれらの間で変わらないが、同じ条件が二重チェックされる場合があります。 この間違いは、一見思われるよりもはるかに深刻な結果を招く可能性があります。 どのもの-実際の例を見る方が良いです。
 public override void VisitIndexerExpression( IndexerExpression indexerExpression) { .... var localResolveResult = context.Resolve(indexerExpression.Target) as LocalResolveResult; if (localResolveResult == null) return; var resolveResult = context.Resolve(indexerExpression); if (localResolveResult == null) return; .... } 

アナライザーの警告: V3021同一の条件式を持つ2つの「if」ステートメントがあります。 最初の「if」ステートメントにはメソッドの戻り値が含まれます。 これは、2番目の「if」ステートメントが無意味であることを意味しますICSharpCode.NRefactory.CSharp.Refactoring ParameterCanBeDeclaredWithBaseTypeIssue.cs 356

このコードスニペットから、「resolveResult == null」を確認する代わりに、「localResolveResult == null」を確認することが2回実行されることが明確にわかります。 これは、カットコードスニペットから明確に見ることができます。 このフラグメントに加えてメソッドのメインロジック(ここでは例を挙げないため、ここでは説明しません)に加えて含まれるコードを見て、このエラーを簡単に見つけることができるかどうかは大きな問題です。 いずれの場合でも、「resolveResult」が「null」の場合にメソッドを終了する代わりに、引き続き正常に動作します。つまり、「resolveResult」を使用する後続のすべてのロジックがtarratarasに移行します。

同様の監視の別の例を次に示します。
 bool TryRemoveTransparentIdentifier(....) { .... string nae1Name = ExtractExpressionName(ref nae1); if (nae1Name == null) return false; .... string nae2Name = ExtractExpressionName(ref nae2); if (nae1Name == null) return false; .... } 

アナライザーの警告: V3021同一の条件式を持つ2つの「if」ステートメントがあります。 最初の「if」ステートメントにはメソッドの戻り値が含まれます。 これは、2番目の「if」ステートメントが無意味であることを意味しますICSharpCode.NRefactory.CSharp CombineQueryExpressions.cs 114

繰り返しますが、検証のために変数を混同したという事実により、ループは終了せず、正しい値が返され、メソッドのさらなるロジックに違反します。

そして、同じエラーを含む、より興味深い例があります:
 public static SW.FontWeight ToWpfFontWeight (this FontWeight value) { if (value == FontWeight.Thin) return SW.FontWeights.Thin; if (value == FontWeight.Ultralight) return SW.FontWeights.UltraLight; if (value == FontWeight.Light) return SW.FontWeights.Light; if (value == FontWeight.Semilight) return SW.FontWeights.Light; if (value == FontWeight.Book) return SW.FontWeights.Normal; if (value == FontWeight.Medium) return SW.FontWeights.Medium; if (value == FontWeight.Semibold) return SW.FontWeights.SemiBold; if (value == FontWeight.Bold) return SW.FontWeights.Bold; if (value == FontWeight.Ultrabold) return SW.FontWeights.UltraBold; if (value == FontWeight.Heavy) return SW.FontWeights.Black; if (value == FontWeight.Ultraheavy) return SW.FontWeights.UltraBlack; return SW.FontWeights.Normal; } 

さて、あなたは見つけましたか? 冗談です、空で指を突くだけです。 しかし、アナライザーには問題はなく、彼は落ち着いてタスクに対処しました。

アナライザーの警告: V3021同一の条件式を持つ2つの「if」ステートメントがあります。 最初の「if」ステートメントにはメソッドの戻り値が含まれます。 これは、2番目の「if」ステートメントが無意味であることを意味しますXwt.WPF DataConverter.cs 217

問題が何であるかをよりよく理解するには、FontWeight列挙を調べる必要があります。
 public enum FontWeight { /// The thin weight (100) Thin = 100, /// The ultra light weight (200) Ultralight = 200, /// The light weight (300) Light = 300, /// The semi light weight (350) Semilight = 350, /// The book weight (380) Book = 350, .... } 

定数「Semilight」と「Book」の値は同じですが、コメントでは、定数「Book」の値は380でなければならないことが明確に示されています。

さらに興味深いことに、値「value」が380に等しい場合、このメソッドは引き続き正常に機能します。 この場合、リストされた条件はどれも満たされないため、条件 'value == FontWeight.Book'が満たされた場合に返される値のみが返されます。 「バグではなく、機能」

さて、このサブセクションの終わりに:
 public override object GetData (TransferDataType type) { if (type == TransferDataType.Text) return clipboard.WaitForText (); if (type == TransferDataType.Text) return clipboard.WaitForImage (); .... } 

アナライザーの警告: V3021同一の条件式を持つ2つの「if」ステートメントがあります。 最初の「if」ステートメントにはメソッドの戻り値が含まれます。 これは、2番目の「if」ステートメントが無意味であることを意味しますXwt.Gtk ClipboardBackend.cs 86

このコードスニペットでは、タイプミスを簡単に見つけることができます。 「type == TransferDataType.Text」のチェックを繰り返す代わりに、「type == TransferDataType.Image」をチェックする必要がありました。

競合する条件を確認する


1つの式の制限内で、同じ変数が任意の値と等しい/等しくないかどうかがチェックされるコードがあります。 このようなチェックは少なくとも冗長であり、誤った変数の値が2回目にチェックされるという事実に関連するエラーを含む可能性もあります。 このようなエラーはプロジェクトでも見つかりました。
 IEnumerable<ICompletionData> CreateConstructorCompletionData(IType hintType) { .... if (!(hintType.Kind == TypeKind.Interface && hintType.Kind != TypeKind.Array)) .... } 

アナライザーの警告: V3023この式の検査を検討してください。 表現が過剰であるか、誤植が含まれています。 ICSharpCode.NRefactory.CSharp CSharpCompletionEngine.cs 2397

コードの環境から判断すると、彼らはここで式をチェックするだけで複雑になりました。 この条件はすべて次の形式のコードに単純化できるため、この複雑さが必要な理由は明らかではありません。
 if (hintType.Kind != TypeKind.Interface) 

同様のケース:
 void OnUpdateClicked (object s, StatusBarIconClickedEventArgs args) { if (args.Button != Xwt.PointerButton.Right && args.Button == Xwt.PointerButton.Left) { HideAlert (); AddinManagerWindow.Run (IdeApp.Workbench.RootWindow); } } 

アナライザーの警告: V3023この式の検査を検討してください。 表現が過剰であるか、誤植が含まれています。 MonoDevelop.Ide AddinsUpdateHandler.cs 97

このコードスニペットから、比較のための他の変数の使用がここで暗示されていないことは明らかですが、それにもかかわらず、過剰な比較が行われます。 Buttonプロパティに追加のロジックが掛けられていないため、読み取り時に「落とし穴」はありません。 繰り返しますが、コードは簡単に単純化されます。
 if (args.Button == Xwt.PointerButton.Left) 


誤ったフォーマット文字列


多くの場合、文字列の書式設定にエラーを含むコードがあります。 通常、エラーには2つのタイプがあります。
このプロジェクトでは、最初のタイプのエラーのみが発生しました。 それらの1つの例:
 ConditionExpression ParseReferenceExpression (string prefix) { StringBuilder sb = new StringBuilder (); string ref_type = prefix [0] == '$' ? "a property" : "an item list"; int token_pos = tokenizer.Token.Position; IsAtToken (TokenType.LeftParen, String.Format ( "Expected {0} at position {1} in condition \"{2}\". Missing opening parantheses after the '{3}'.", ref_type, token_pos, conditionStr, prefix)); .... IsAtToken (TokenType.RightParen, String.Format ( "Expected {0} at position {1} in condition \"{2}\". Missing closing parantheses'.", ref_type, token_pos, conditionStr, prefix)); .... } 

アナライザーの警告: V3025の形式が正しくありません 。 「フォーマット」関数を呼び出すときに、異なる数のフォーマット項目が予想されます。 予想:3.現在:4. MonoDevelop.Core ConditionParser.cs 254

ほとんどの場合、このエラーはコピーと貼り付けが失敗した結果でした。IsAtTokenメソッドの2番目の呼び出しは最初の呼び出しと似ているため、唯一の違いは閉じ括弧に関することです。 ただし、「prefix」の引数は使用されません。 重要ではありませんが、彼には意味がありません。

同様の警告:

ヌル参照の潜在的な逆参照


多くの場合、変数が「null」に等しいかどうかを確認する必要があります。特に、この変数がメソッドの引数である場合、その作業の結果は「as」演算子を使用して取得されます。それを使用する前に、変数に値「null」が含まれていないことを確認する必要があります。そうでない場合、たとえば、オブジェクトのメンバーの1つを呼び出そうとすると、「NullReferenceException」型の例外がスローされます。

しかし、プログラマーは、不注意により、リンクを間接参照した後にそのようなチェックを実行する場合があります。ここでそのようなケースが発生しました。
 void Replace (RedBlackTreeNode oldNode, RedBlackTreeNode newNode) { .... if (oldNode.parent.left == oldNode || oldNode == null && oldNode.parent.left == null) .... } 

アナライザーの警告:V3027変数 'oldNode'は、同じ論理式でヌルと検証される前に、論理式で使用されました。 MonoDevelop.HexEditor RedBlackTree.cs 167

コードからわかるように、最初にオブジェクト「oldNode.parent.left」のいくつかのフィールドがオブジェクト「oldNode」自体と比較され、次にこのオブジェクトとフィールドが「null」の等しいかどうかがチェックされます。ただし、「oldNode」がまだnullの場合、最初のチェックで「NullReferenceException」タイプの例外が既にスローされます。正しい決定は、最初にオブジェクトが 'null'であるかどうかをチェックすることです。

おわりに


個人的には、いくつかの興味深いエラーを見つけることができたため、検証の結果に満足しました。これらのすべてがこの記事で検討されたわけではありません。ほとんどすぐに記事の資料が十分すぎることが明らかになったため、多くの診断メッセージが表面的に検査されました。

このボリュームのプロジェクトにはそれほど多くの間違いはないと言う人がいるかもしれません。ただし、テスト段階で多くのエラーが検出され、静的アナライザーを使用して開発段階で検出および修正できるため、コードの記述とデバッグのプロセスが容易になり、最終製品の総コストが削減されることに注意してください。

その他の実績のあるC#プロジェクト


他のオープンソースC#プロジェクトのチェックアウトについて読むことに興味があるかもしれません。ただし、これらのプロジェクトの一部はアナライザーの開発中にチェックされたため、チェックすると、最良の結果が表示される可能性があります。


この記事を英語圏の聴衆と共有したい場合は、翻訳へのリンクを使用してください:Sergey Vasiliev。MonoDevelopのバグを探しています。

記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここで回答を集めました: PVS-Studioバージョン2015に関する記事の読者からの質問への回答 。 リストをご覧ください。

Source: https://habr.com/ru/post/J274321/


All Articles