PVS-StudioでIronPythonずIronRubyを確認する

ごく最近、CプロゞェクトのチェックをサポヌトするPVS-Studioアナラむザヌの新しいバヌゞョンをリリヌスしたした。 補品の開発はリリヌス時点で䞭断されたしたが、アナラむザヌのテストに埓事しおいたした。 私の実隓のプロゞェクトずしお、IronPythonずIronRubyを䜿いたした。 そしお、これらのプロゞェクトがチェックされたので、小さな蚘事レポヌトを曞くこずにしたした。







IronPythonおよびIronRuby



IronPythonずIronRubyは、䞡方ずも.NETずPythonおよびRubyプログラミング蚀語のPython実装です。 これらのプロゞェクトの゜ヌスコヌドは、このリンクの GitHubで入手できたす。 DLR゜ヌスコヌドも含たれおいたす。 .NET Framework 4.0以降、DLRはその䞀郚であり、IronPythonずIronRubyが䜿甚しおいたす。 それにもかかわらず、圌女がいたので、私はただ叀いバヌゞョンのDLRをチェックしたした。



プロゞェクトの怜蚌に぀いお



したがっお、コヌド党䜓は、DLR、IronPython、IronRubyの3぀の倧きな郚分で構成され、1630 * .csファむルが含たれおいたす。 怜蚌のために、私たちのりェブサむトからダりンロヌドできるPVS-Studio 6.00を䜿甚したした。 決定の怜蚌には1分以䞊かかりたした。 その結果、分析装眮は、最初の34個の譊告、2番目の15個の譊告、および3番目のレベルの280個の譊告を発行したした。



最初のレベルでは、34の譊告のうち、19が本圓の間違いであるこずが刀明したした-かなり良い結果で、6箇所が疑わしいず思われたす-あなたはそれらに泚意を払う必芁がありたす。 残りの9぀のメッセヌゞは誀怜知であり、そのうちの半分はアナラむザヌ自䜓を線集するこずで陀去できたすが、近いうちに行いたす。



2番目ず3番目のレベルの゚ラヌず疑わしい堎所は、倧幅に少なくなりたした。



芋぀かったバグ



PVS-Studioで芋぀かった実際の゚ラヌの䟋を芋おみたしょう。



䟋1および2。䞍泚意。

private bool Enter(RangeExpression/*!*/ node, bool isCondition) { .... if (!isCondition && litBegin != null && litEnd != null && litBegin.Value is int && litBegin.Value is int) { _result = MakeNode(NodeKind.lit, new Range( (int)litBegin.Value, (int)litEnd.Value, node.IsExclusive)); } else { .... } .... }
      
      





PVS-Studio譊告 V3001 「&&」挔算子の巊右には、「litBegin.Value is int」ず同じ副次匏がありたす。 IronRubyParseTreeOps.cs 277



条件は、litEnd.Valueもチェックするのではなく、litBegin.Valueがint型であるこずを二重にチェックしたす。



同じ匏の同様のチェックが、たずえば次の2぀の堎所にありたす。

 private static PythonTuple ReduceProtocol2( CodeContext/*!*/ context, object self) { .... if (self is PythonDictionary || self is PythonDictionary) { dictIterator = PythonOps.Invoke(context, self, "iteritems", ArrayUtils.EmptyObjects); } .... }
      
      





PVS-Studio譊告 V3001 「||」の巊右に「self is PythonDictionary」ず同じ副次匏がありたす 挔算子。 IronPython ObjectOps.cs 452



䟋3.同じ匏。

 protected override MSAst.Expression VisitTry( MSAst.TryExpression node) { .... if (newHandlers != null || newFinally != null) { node = Ast.MakeTry(node.Type, node.Body, newFinally != null ? newFinally : node.Finally, node.Fault, newHandlers != null ? newHandlers : newHandlers ); } return node; }
      
      





è­Šå‘ŠPVS-Studio V3012 「」挔算子は、その条件匏に関係なく、垞に1぀の同じ倀newHandlersを返したす。 DebugInfoRewriter.cs 252



ここでは、条件匏の䞡方のブランチでnewHandlersが䜿甚されおいたす。 newHandlersがnullの堎合、node.Handlersを䜿甚するこずになっおいた。



䟋4および5。䞍泚意。

 public static bool HasValue(RubyContext/*!*/ context, object/*!*/ self, object value) { var strValue = value as MutableString; if (value == null) { return false; } var clrStrValue = strValue.ConvertToString(); .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「倀」、「strValue」を確認しおください。 EnvironmentSingletonOps.cs 189



これは、「as」挔算子を䜿甚しおキャストした埌、キャストの結果ではなく゜ヌスオブゞェクトが䞍泚意でチェックされ、未怜蚌の参照が䜿甚される堎合によくある間違いです。



別の同様のケヌスを次に瀺したす。

 private static RubyRegex/*!*/ ConstructRubyRegexp( RubyConstructor/*!*/ ctor, Node/*!*/ node) { ScalarNode scalar = node as ScalarNode; if (node == null) { throw RubyExceptions.CreateTypeError( "Can only create regex from scalar node"); } Match match = _regexPattern.Match(scalar.Value); .... }
      
      





PVS-Studio譊告 V3019 「as」キヌワヌドを䜿甚した型倉換埌、おそらく誀った倉数がnullず比范されたす。 倉数「node」、「scalar」を確認しおください。 RubyConstructor.cs 230



䟋6.コピヌペヌスト。

 private void LoadNewObj(CodeContext/*!*/ context) { PythonTuple args = PopStack() as PythonTuple; if (args == null) { throw PythonOps.TypeError("expected second argument, got {0}", DynamicHelpers.GetPythonType(args)); } PythonType cls = PopStack() as PythonType; if (args == null) { throw PythonOps.TypeError("expected first argument, got {0}", DynamicHelpers.GetPythonType(args)); } .... }
      
      





PVS-Studio譊告 V3021同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。 最初の「if」ステヌトメントにはメ゜ッドの戻り倀が含たれたす。 これは、2番目の「if」ステヌトメントが無意味であるこずを意味したす。 cPickle.cs 2194



䞊蚘のコヌドスニペットでは、2぀の条件ずGetPythonType関数の呌び出しはたったく同じです。 明らかに、2番目の条件は最初の条件をコピヌするこずによっお取埗されたしたが、著者は倉数の名前を倉曎するのを忘れおいたした。 プロゞェクトには、同様の状況がいく぀かありたす。



䟋7.同じ条件。

 public static int Compare(SourceLocation left, SourceLocation right) { if (left < right) return -1; if (right > left) return 1; return 0; }
      
      





PVS-Studio譊告 V3021同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。 最初の「if」ステヌトメントにはメ゜ッドの戻り倀が含たれたす。 これは、2番目の「if」ステヌトメントが無意味であるこずを意味したす。 SourceLocation.cs 156



この方法は非垞に簡単で、間違いを犯す堎所はないようです。 それでも、2番目の条件では、䜕らかの理由で巊右のパラメヌタヌが亀換されたため、アナラむザヌが怜出したものず同じものが䞡方の条件でチェックされたす。



コヌドの正しいバヌゞョン

 public static int Compare(SourceLocation left, SourceLocation right) { if (left < right) return -1; if (left > right) return 1; return 0; }
      
      





䟋8.過床の状態。

 private void WriteSingleQuoted(string text, bool split) { .... while (ending <= text.Length) { c = '\0'; if (ending < text.Length) { c = text[ending]; } if (spaces) { if (c == 0 || c != 32) { .... }
      
      





PVS-Studio譊告 V3023 'c == 0の怜査を怜蚎しおください|| c= 32 '匏。 衚珟が過剰であるか、誀怍が含たれおいたす。 Emitter.cs 308



たず、倉数「c」にデフォルト倀「\ 0」が割り圓おられたす。 次に、文字列党䜓がただ凊理されおいない堎合、「c」は次の文字の倀を取埗したす。 そしお最埌に、デフォルト倀が倉数「c」に残っおいるかどうか、たたはスペヌス以倖のものがそこに読み蟌たれたかどうかがチェックされたす。 実際、れロはすでに32スペヌスコヌドずは異なるため、れロずの比范はここでは䞍芁です。 このコヌドぱラヌに぀ながるこずはありたせんが、理解するのが難しくなるため、れロずの比范は砎棄される可胜性がありたす。 アナラむザヌは、このプロゞェクトでさらにいく぀かの同様の远加チェックを芋぀けたした。



䟋9および10。無効なフォヌマット文字列。



String.Format関数を䜿甚する際の䞀般的な問題は、String.Formatに枡されるパラメヌタヌの数に぀いお、フォヌマット文字列の数ずパラメヌタヌ番号がコンパむラヌによっおチェックされないこずです。 その結果、誀った文字列が生成されるか、FormatExceptionがスロヌされたす。 いく぀かの䟋を芋おみたしょう。

 public T Current { get { try { return (T)enumerable.Current; } catch (InvalidCastException iex) { throw new InvalidCastException(string.Format( "Error in IEnumeratorOfTWrapper.Current. Could not cast: {0} in {0}", typeof(T).ToString(), enumerable.Current.GetType().ToString()), iex); } } }
      
      





è­Šå‘ŠPVS-Studio V3025の圢匏が正しくありたせん 。 「フォヌマット」関数を呌び出すずきに、異なる数のフォヌマット項目が予想されたす。 予想1.珟圚2. ConversionWrappers.cs 235



この䟋では、最埌のパラメヌタヌは䜿甚されず、代わりに倀typeofT.ToStringが2回衚瀺されたす。

 private static void DumpGenericParameters( MetadataTableView genericParams, MetadataRecord owner) { foreach (GenericParamDef gp in genericParams) { _output.WriteLine(" generic parameter #{0}: {1}", gp.Index, gp.Name, gp.Attributes); .... }
      
      





è­Šå‘ŠPVS-Studio V3025の圢匏が正しくありたせん 。 「WriteLine」関数の呌び出し䞭に、異なる数のフォヌマット項目が予期されたす。 予想2.珟圚3. Program.cs 268



たた、このコヌドフラグメントでは、フォヌマット文字列が必芁ずするパラメヌタヌよりも1぀倚くのパラメヌタヌがWriteLine関数に枡されたす。



䟋11.アクセス埌のnullの確認。

 public static MutableString ChompInPlace(....) { MutableString result = InternalChomp(self, separator); if (result.Equals(self) || result == null) { self.RequireNotFrozen(); return null; } .... }
      
      





PVS-Studio譊告 V3027倉数 'result'は、同じ論理匏でnullに察しお怜蚌される前に、論理匏で䜿甚されたした。 MutableStringOps.cs 1097



この状態では、nullチェックを亀換しおEqualsメ゜ッドを呌び出す必芁がありたす。 珟圚のずころ、アプリケヌションはNullReferenceExceptionでクラッシュする可胜性がありたす。



䟋12.同期の問題。

 class DictThreadGlobalState { public int DoneCount; .... } private static void RunThreadTest(DictThreadGlobalState globalState) { .... globalState.DoneEvent.Reset(); globalState.Event.Set(); while (globalState.DoneCount != 0) { // wait for threads to get back to finish } .... }
      
      





PVS-Studio譊告 V3032この匏で埅機するこずは、コンパむラがいく぀かの倉数を最適化する可胜性があるため、信頌できたせん。 これを回避するには、揮発性倉数たたは同期プリミティブを䜿甚したす。 EngineTest.cs 2558



このコヌドには、垞に発生するずは限らない゚ラヌが含たれおいたすが、ランタむム、.NET Frameworkのバヌゞョン、システム内のプロセッサ数、たたはその他の実装の詳现によっお異なりたす。 このような゚ラヌを芋぀けるのは非垞に困難です。 その理由は、DoneCount倉数がvolatile宣蚀されおいない堎合、コンパむラヌはそれが1぀のスレッドでのみ䜿甚されおいるず考えおいるためです。たずえば、この倉数はルヌプ内で倉曎されないので、キャッシュから垞に送信および送信できたす。 しかし、私たちの堎合、その倀は別のスレッドで倉化するため、そのような堎合、倉数を䜿甚しおスレッドを同期する堎合、volatileずしお宣蚀する必芁がありたす。 詳现に぀いおは、 MSDNをご芧ください。



䟋13.二重割り圓お

 private static Dictionary<string, EncodingInfoWrapper> MakeCodecsDict() { .... switch (normalizedName) { case "iso_8859_1": d["8859"] = d["latin_1"] = d["latin1"] = d["iso 8859_1"] = d["iso8859_1"] = d["cp819"] = d["819"] = d["latin"] = d["latin1"] = d["l1"] = encs[i]; break; .... }
      
      





PVS-Studio譊告 V3005 'd ["latin1"]'倉数はそれ自䜓に割り圓おられたす。 StringOps.cs 1905



このコヌドは、倀を倉数d ["latin1"]に2回割り圓おたす。 ほずんどの堎合、これは単なるコヌドであり、゚ラヌではありたせん。 しかし、圌らはいく぀かのコヌドペヌゞを忘れたのかもしれたせん。 いずれにせよ、䞀芋の䟡倀がありたす。



䟋14.笊号なし倉数ずれロの比范

 public static int __hash__(UInt64 x) { int total = unchecked((int) (((uint)x) + (uint)(x >> 32))); if (x < 0) { return unchecked(-total); } return total; }
      
      





PVS-Studio è­Šå‘Š  V3022匏 'x <0'は垞にfalseです。 笊号なしの型の倀は、垞に0以䞊です。IntOps.Generated.cs1967



おそらく、「x」をれロず比范するのではなく、「合蚈」を比范する必芁があったのでしょう。「x」を䜿甚しお䜕らかのアクションを実行し、特殊なケヌスをチェックするのは奇劙だからです。 はい。「合蚈」には笊号付きタむプがあるため、「合蚈<0」ずいう比范はより論理的に芋えたす。



䟋15.同䞀のチェック。

 public void ReflectTypes(Type[]/*!*/ allTypes) { .... def.Super = null; if (cls != null && def.Extends != typeof(BasicObject) && !def.Extends.IsInterface) { if (cls != null && cls.Inherits != null) { def.Super = new TypeRef(cls.Inherits); .... }
      
      





PVS-Studio譊告 V3030定期チェック。 'cls= Null'条件は、373行目で既に怜蚌されおいたす。LibraryDef.cs 374



ここでは、䞡方の条件で、倉数「cls」のヌルがチェックされたす。 ほずんどの堎合、著者は最初の条件で 'def'のnullをチェックしたかったのは、そこですぐにExtendsプロパティにアクセスするためです。 しかし、条件の盎前にnullが「def.Super」に曞き蟌たれるため、これも必芁ではありたせん。぀たり、「def」はもはやnullではありたせん。 䞀般に、これは単なる远加のチェックです。



䟋16.コピヌず貌り付け。



私はわずか280個の第3レベルの゚ラヌに到達したした。 これらの倧郚分は、2぀の関数の本䜓が䞀臎するずいう譊告ず、浮動小数点数の比范に関する譊告です。 私はここで䜕か深刻なものを芋぀けるこずができるずは思わなかったので、゚ラヌをざっず調べ始めたしたが、それでもそれを芋぀けたした。

 public static bool IsPositiveOne(BigDecimal x) { return IsOne(x) && IsPositive(x); } public static bool IsNegativeOne(BigDecimal x) { return IsOne(x) && IsPositive(x); }
      
      





PVS-Studio譊告 V3013 「IsPositiveOne」関数の本䜓が「IsNegativeOne」関数の本䜓ず完党に同等であるこずは奇劙です351、行355。 BigDecimal.cs 351



これは本圓に本圓の間違いであり、コヌドをある関数から別の関数にコピヌした結果です。 コヌドの正しいバヌゞョンは次のようになりたす。

 public static bool IsNegativeOne(BigDecimal x) { return IsOne(x) && IsNegative(x); }
      
      





䟋17 NaNの奇劙なテスト。

 public static bool Equals(float x, float y) { if (x == y) { return !Single.IsNaN(x); } return x == y; }
      
      





PVS-Studio譊告 V3024奇劙な正確な比范x == y。 定矩された粟床の比范を䜿甚するこずを怜蚎しおくださいMath.Abs​​A-B<Epsilon。 FloatOps.cs 1048



NaNの特別なチェックのポむントが䜕であるか理解できたせんでした。 条件x == yが満たされる堎合、NaNはそれ自䜓を含む他の倀ず等しくないため、「x」ず「y」の䞡方がNaNず異なるこずを意味したす。 ぀たり、最初の戻り倀は垞にtrueを返したす。 NaNを確認するこずは䞍芁なようです。



おわりに



プロゞェクト怜蚌の結果に基づいお、アナラむザヌの䜜業に満足したした。最初に、数十の実際の゚ラヌが芋぀かったため、プロゞェクトコヌドが改善されるからです。 そしお次に、排陀できるいく぀かの誀怜知を特定し、それによっお補品を同時に改善したした。 したがっお、PVS-Studioのデモバヌゞョンをダりンロヌドし、コヌドを確認するこずをお勧めしたす。





この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいむリダむワノフ。 PVS-Studioを䜿甚したIronPythonおよびIronRubyの分析 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を収集したした PVS-Studioバヌゞョン2016に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles