يعد نشر Microsoft للشفرة المصدر لمشاريعها سببًا جيدًا للتحقق منها. هذه المرة لم يكن استثناء ، واليوم ننظر إلى الأماكن المشبوهة الموجودة في رمز Infer.NET. يسقط التعليق التوضيحي - الوصول إلى النقطة!
قليلا عن المشروع والمحلل
Infer.NET هو نظام تعلم آلي تم تطويره بواسطة متخصصين من Microsoft. أصبح كود المصدر للمشروع متاحًا مؤخرًا على
GitHub ، والذي كان سبب التحقق منه. يمكن العثور على مزيد من التفاصيل حول المشروع ، على سبيل المثال ،
هنا .
تم فحص المشروع باستخدام محلل ثابت الإصدار 6.26 من PVS-Studio. دعني أذكرك بأن PVS-Studio يبحث عن أخطاء في التعليمات البرمجية في C \ C ++ \ C # (وقريبًا في Java) في أنظمة التشغيل Windows و Linux و macOS. كود C # حتى الآن نحن نحلل فقط تحت Windows. يمكن
تنزيل المحلل
وتجربته في مشروعك.
الشيك نفسه كان بسيطًا للغاية وبدون مشاكل. في السابق ، قمت بإلغاء تحميل المشروع من GitHub ، واستعدت الحزم المطلوبة (التبعيات) ، وتأكدت من بناء المشروع بنجاح. هذا مطلوب حتى يتمكن المحلل من الوصول إلى جميع المعلومات اللازمة لتحليل كامل. بعد التجميع ببضع نقرات ، أطلقت تحليل الحل من خلال المكوّن الإضافي PVS-Studio لـ Visual Studio.
بالمناسبة ، هذا ليس المشروع الأول من Microsoft الذي تم اختباره باستخدام PVS-Studio - كان هناك مشاريع أخرى:
Roslyn و
MSBuild و
PowerShell و
CoreFX وغيرها .
ملاحظة إذا كنت أنت أو معارفك مهتمين بتحليل رمز Java ، فيمكنك الكتابة إلينا
بالدعم عن طريق تحديد "أريد محلل لـ Java". لا يوجد إصدار بيتا عام من المحلل ، ولكن يجب أن يكون متاحًا قريبًا. في مكان ما في مختبر سري (من خلال الجدار) ، يعمل الرجال بنشاط عليه.
ولكن يكفي الحديث المجرد - دعونا نلقي نظرة على المشاكل في التعليمات البرمجية.
هل هذا خطأ أم ميزة؟
أقترح محاولة العثور على الخطأ بنفسك - مهمة قابلة للحل تمامًا. لا نكت في روح ما كان في مقال "
أعلى 10 أخطاء في مشاريع C ++ لعام 2017 " ، بصراحة. لذلك لا تتسرع في قراءة تحذير المحلل المقدم بعد مقتطف الشفرة.
private void MergeParallelTransitions() { .... if ( transition1.DestinationStateIndex == transition2.DestinationStateIndex && transition1.Group == transition2.Group) { if (transition1.IsEpsilon && transition2.IsEpsilon) { .... } else if (!transition1.IsEpsilon && !transition2.IsEpsilon) { .... if (double.IsInfinity(transition1.Weight.Value) && double.IsInfinity(transition1.Weight.Value)) { newElementDistribution.SetToSum( 1.0, transition1.ElementDistribution, 1.0, transition2.ElementDistribution); } else { newElementDistribution.SetToSum( transition1.Weight.Value, transition1.ElementDistribution, transition2.Weight.Value, transition2.ElementDistribution); } .... }
تحذير PVS-Studio :
V3001 هناك
عبارات مزدوجة متطابقة "مزدوجة. IsInfinity (انتقال 1. الوزن. القيمة)" إلى اليسار وإلى يمين عامل التشغيل "&&". 479
كما ترى من مقتطف الشفرة ، تعمل الطريقة مع زوج من المتغيرات -
انتقال 1 وانتقال 2 . في بعض الأحيان يكون استخدام أسماء مماثلة له ما يبرره تمامًا ، ولكن تجدر الإشارة إلى أنه في هذه الحالة يزداد احتمال ارتكاب خطأ عن طريق الخطأ في مكان ما مع الاسم.
هذا
ما حدث عند التحقق من الأرقام عن اللانهاية (
double.IsInfinity ). نتيجة لخطأ ، تحققنا من قيمة المتغير نفسه مرتين -
انتقال 1. الوزن . كانت القيمة المحددة في التعبير الفرعي الثاني هي
التحول المتغير
.2 .
رمز مشبوه آخر مماثل.
internal MethodBase ToMethodInternal(IMethodReference imr) { .... bf |= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; .... }
تحذير PVS-Studio :
V3001 توجد
عبارات فرعية متطابقة "BindingFlags.Public" إلى اليسار وإلى يمين "|" عامل. مترجم CodeBuilder.cs 194
عند تشكيل قيمة متغير
bf ، يتم استخدام عنصر التعداد
BindingFlags.Public public مرتين. إما أن يحتوي هذا الرمز على عملية وضع علامة إضافية ، أو بدلاً من الاستخدام الثاني لـ
BindingFlags.Public ، يجب أن تكون هناك قيمة تعداد مختلفة.
بالمناسبة ، في كود المصدر هذا الرمز مكتوب على سطر واحد. يبدو لي أنه إذا تم تنسيقه بنمط جدول (مثل هنا) ، فمن السهل اكتشاف المشكلة.
دعنا ننتقل. أحمل النص الكامل للطريقة وأقترح مرة أخرى أن تجد الخطأ (أو ربما الخطأ) بنفسك.
private void ForEachPrefix(IExpression expr, Action<IExpression> action) {
هل وجدت؟ نحن نتحقق!
تحذيرات PVS-Studio :
- V3003 استخدام نمط 'if (A) {...} آخر إذا تم اكتشاف (A) {...}'. هناك احتمال لوجود خطأ منطقي. خطوط التحقق: 1719 ، 1727. كود المترجم Recognizer.cs 1719
- V3003 استخدام نمط 'if (A) {...} آخر إذا تم اكتشاف (A) {...}'. هناك احتمال لوجود خطأ منطقي. خطوط التحقق: 1721 ، 1729. كود المترجم Recognizer.cs 1721
بسّط الشفرة قليلاً لجعل المشاكل أكثر وضوحًا.
private void ForEachPrefix(IExpression expr, Action<IExpression> action) { if (....) .... else if (expr is IUnaryExpression) ForEachPrefix(((IUnaryExpression)expr).Expression, action); else if (expr is IAddressReferenceExpression) ForEachPrefix(((IAddressReferenceExpression)expr).Expression, action); .... else if (expr is IUnaryExpression) ForEachPrefix(((IUnaryExpression)expr).Expression, action); else if (expr is IAddressReferenceExpression) ForEachPrefix(((IAddressReferenceExpression)expr).Expression, action) .... }
التعبيرات الشرطية
ثم فروع عدة
إذا تم تكرار
العبارات . ربما تمت كتابة هذا الرمز باستخدام طريقة النسخ واللصق ، ولهذا السبب نشأت المشكلة. يتبين الآن أنه لن يتم تنفيذ فروع التكرارات ، حيث:
- إذا كان التعبير الشرطي صحيحًا ، يتم تنفيذ نص أول if إذا كان البيان من الزوج المقابل ؛
- إذا كان التعبير الشرطي خطأ في الحالة الأولى ، فسيكون خطأ في الحالة الثانية.
منذ ذلك الحين
- تحتوي الفروع على نفس الإجراءات ، يبدو الآن وكأنه رمز زائدة محير. من الممكن أن تكون هذه مشكلة مختلفة - فبدلاً من أخذها ، كان ينبغي إجراء عمليات فحص أخرى.
نواصل.
public int Compare(Pair<int, int> x, Pair<int, int> y) { if (x.First < y.First) { if (x.Second >= y.Second) {
تحذيرات PVS-Studio :
- V3004 العبارة "ثم" تعادل العبارة "آخر". وقت التشغيل RegexpTreeBuilder.cs 1080
- V3004 العبارة "ثم" تعادل العبارة "آخر". وقت التشغيل RegexpTreeBuilder.cs 1093
تبدو الشفرة مشبوهة للغاية ، لأنها تحتوي على عبارتين شرطيتين بأجسام متطابقة من الفروع السابقة والأخرى. في كلتا الحالتين ، ربما يستحق الأمر إعادة قيم مختلفة. أو ، إذا كان هذا سلوكًا متصورًا ، فسيكون من المفيد إزالة العبارات الشرطية الزائدة.
كانت هناك دورات مثيرة للاهتمام. المثال أدناه:
private static Set<StochasticityPattern> IntersectPatterns(IEnumerable<StochasticityPattern> patterns) { Set<StochasticityPattern> result = new Set<StochasticityPattern>(); result.AddRange(patterns); bool changed; do { int count = result.Count; AddIntersections(result); changed = (result.Count != count); break; } while (changed); return result; }
تحذير PVS-Studio :
V3020 "كسر" غير مشروط داخل حلقة. الحلقة 474
نظرًا لبيان
الفاصل غير المشروط ، يتم إجراء تكرار واحد للحلقة ، ولا يتم حتى استخدام متغير التحكم. بشكل عام ، يبدو الرمز غريبًا ومريبًا.
تم العثور على نفس الطريقة (نسخة طبق الأصل) في فئة أخرى. تحذير المحلل
المناظر :
V3020 "كسر" غير مشروط داخل حلقة. Visualizers.Windows FactorManagerView.cs 350
بالمناسبة ، اجتمعت طريقة ببيان
متابعة غير مشروط في حلقة (تم العثور عليها من قبل المحلل بنفس التشخيص) ، ولكن كان هناك تعليق فوقها يؤكد أن هذا هو حل مؤقت خاص:
أذكر أنه لم تكن هناك مثل هذه التعليقات بالقرب من بيان
الاستراحة غير المشروط.
دعنا ننتقل.
internal static DependencyInformation GetDependencyInfo(....) { .... IExpression resultIndex = null; .... if (resultIndex != null) { if (parameter.IsDefined( typeof(SkipIfMatchingIndexIsUniformAttribute), false)) { if (resultIndex == null) throw new InferCompilerException( parameter.Name + " has SkipIfMatchingIndexIsUniformAttribute but " + StringUtil.MethodNameToString(method) + " has no resultIndex parameter"); .... } .... } .... }
تحذير PVS-Studio : تعبير V3022 'resultIndex == null' خطأ دائمًا. 382 - المترجم
على الفور ، ألاحظ أنه بين الإعلان والتحقق أعلاه ، يمكن أن تتغير قيمة متغير
النتيجة . ومع ذلك ، بين عمليات التحقق
resultIndex! = Null و
resultIndex == null ، لا يمكن تغيير القيمة بالفعل. لذلك ، ستكون نتيجة التعبير
resultIndex == null دائمًا
خطأ ، مما يعني أنه لن يتم طرح استثناء مطلقًا.
آمل أن يكون لديك اهتمام في العثور على الأخطاء بنفسك ، بدون اقتراحاتي ، للعثور على مشكلة ، ولكن فقط في حالة ، سأقترح القيام بذلك مرة أخرى. رمز الطريقة صغير ، سأعطيه بالكامل.
public static Tuple<int, string> ComputeMovieGenre(int offset, string feature) { string[] genres = feature.Split('|'); if (genres.Length < 1 && genres.Length > 3) { throw new ArgumentException(string.Format( "Movies should have between 1 and 3 genres; given {0}.", genres.Length)); } double value = 1.0 / genres.Length; var result = new StringBuilder( string.Format( "{0}:{1}", offset + MovieGenreBuckets[genres[0]], value)); for (int i = 1; i < genres.Length; ++i) { result.Append( string.Format( "|{0}:{1}", offset + MovieGenreBuckets[genres[i].Trim()], value)); } return new Tuple<int, string>(MovieGenreBucketCount, result.ToString()); }
دعنا نرى ما يحدث هنا. يتم تحليل سلسلة الإدخال بالحرف '|'. إذا لم يكن طول المصفوفة كما هو متوقع ، فيجب طرح استثناء.
انتظر ثانية ...
الأنواع.الطول <1 && الأنواع.الطول> 3 ؟ نظرًا لعدم وجود رقم يقع على الفور في كل من نطاق القيم التي يتطلبها التعبير (
[int.MinValue..1) و
(3..int.MaxValue] ) ، فإن نتيجة التعبير ستكون دائمًا
خاطئة . لذلك ، لا يحمي هذا الفحص أي شيء ، ولن يتم طرح الاستثناء المتوقع.
هذا بالضبط ما يحذر منه المحلل:
أنواع الأنواع V3022 Expression. الطول <1 && الأنواع.الطول> 3 'دائمًا خطأ. ربما "||" يجب استخدام عامل التشغيل هنا. مميزات المقيم. cs 242
التقى عملية الانشطار المشبوهة.
public static void CreateTrueThetaAndPhi(....) { .... double expectedRepeatOfTopicInDoc = averageDocLength / numUniqueTopicsPerDoc; .... int cnt = Poisson.Sample(expectedRepeatOfTopicInDoc); .... }
تحذير PVS-Studio :
V3041 تم التعبير عن التعبير ضمنيًا من النوع "int" إلى النوع "المزدوج". ضع في اعتبارك استخدام قالب صريح لتجنب فقدان جزء كسري. مثال: double A = (double) (X) / Y؛. أدوات LDA المساعدة 74
هذا مشبوه هنا: يتم تنفيذ القسمة الصحيحة (المتغيرات
المتوسطة DocLength و
numUniqueTopicsPerDoc من النوع
int ) ، والنتيجة مكتوبة إلى متغير من النوع
المزدوج . يطرح السؤال: هل تم ذلك بشكل خاص أم أن تقسيم الأعداد الحقيقية لا يزال ضمنيًا؟ إذا كان المتغير
المتوقع RepeatOfTopicInDoc من النوع
int ،
فسيؤدي ذلك إلى توضيح الأسئلة المحتملة.
في أماكن أخرى ، يتم
استخدام طريقة
Poisson.Sample ، التي تكون
وسيطتها المتغير المشبوه
المتوقع RepeatOfTopicInDoc ، على سبيل المثال ، كما هو موضح أدناه.
int numUniqueWordsPerTopic = Poisson.Sample((double)averageWordsPerTopic);
إن متوسط WordsWordsPerTopic من النوع
int ، والذي تم تحويله بالفعل إلى
الضعف في مكان الاستخدام.
وهنا مكان آخر للاستخدام:
double expectedRepeatOfWordInTopic = ((double)numDocs) * averageDocLength / numUniqueWordsPerTopic; .... int cnt = Poisson.Sample(expectedRepeatOfWordInTopic);
يرجى ملاحظة أن المتغيرات لها نفس الأسماء كما في المثال الأصلي ، يتم استخدام تقسيم الأرقام الحقيقية فقط لتهيئة
REPeatOfWordInTopic المتوقعة (بسبب التحويل الصريح لـ
numDocs إلى
الضعف ).
بشكل عام ، يجدر النظر في مكان البدء حيث أصدر المحلل تحذيرًا.
ولكن تأمل ما إذا كان الأمر يستحق التحرير وكيف ، دع مؤلفي الشفرة (يعرفون جيدًا) ، لكن دعنا نذهب أبعد من ذلك. إلى القسم المريب التالي.
public static NonconjugateGaussian BAverageLogarithm(....) { .... double v_opt = 2 / 3 * (Math.Log(mx * mz / Ex2 / 2) - m); if (v_opt != v) { .... } .... }
تحذير PVS-Studio :
V3041 تم التعبير عن التعبير ضمنيًا من النوع "int" إلى النوع "المزدوج". ضع في اعتبارك استخدام قالب صريح لتجنب فقدان جزء كسري. مثال: double A = (double) (X) / Y؛. Runtime ProductExp.cs 137
اكتشف المحلل مرة أخرى العملية المشبوهة لتقسيم الأعداد الصحيحة ، مثل
2 و
3 حرفان رقميان صحيحان ، وستكون نتيجة التعبير 2/3 صفرًا. ونتيجة لذلك ، يأخذ التعبير بأكمله الشكل:
double v_opt = 0 * expr;
موافق غريب قليلا. عدت إلى هذا التحذير عدة مرات ، محاولاً إيجاد نوع من الصيد ، وعدم محاولة إضافته إلى المقالة. الطريقة مليئة بالرياضيات والصيغ المختلفة (والتي بصراحة لم أكن أرغب حقًا في تفكيكها) ، فأنت لا تعرف أبدًا ما يمكن توقعه. بالإضافة إلى ذلك ، أحاول أن أكون متشككًا قدر الإمكان بشأن التحذيرات التي أكتبها في المقالة ، وأصفها فقط بعد دراستها بشكل أفضل.
ولكن بعد ذلك بزغ لي - لماذا أحتاج إلى عامل
0 ، مكتوبًا كـ
2/3 ؟ لذلك هذا المكان يستحق نظرة على أي حال.
public static void WriteAttribute(TextWriter writer, string name, object defaultValue, object value, Func<object, string> converter = null) { if ( defaultValue == null && value == null || value.Equals(defaultValue)) { return; } string stringValue = converter == null ? value.ToString() : converter(value); writer.Write($"{name}=\"{stringValue}\" "); }
تحذير PVS-Studio :
V3080 محتمل
للإشارة الخالية. خذ بعين الاعتبار فحص "القيمة". مترجم WriteHelpers.cs 78
تأكيد عادل تماما للمحلل بناء على الشرط. يمكن أن يحدث إلغاء الإشارة إلى مرجع فارغ في قيمة التعبير.
Equals (defaultValue) if
value == null . بما أن هذا التعبير هو المعامل الصحيح لعامل || ، ولحسابه ، يجب أن يكون المعامل الأيسر
خطأ ، ولهذا يكفي أن يكون واحدًا على الأقل من متغيرات
القيمة الافتراضية \
القيمة ليس
فارغًا . ونتيجة لذلك ، إذا كان
defaultValue! = Null ،
والقيمة == null :
- defaultValue == null -> false ؛
- defaultValue == null && value == null -> false ؛ (لم يتم التحقق من القيمة )
- value.Equals (defaultValue) -> NullReferenceException ، لأن القيمة خالية .
دعونا نلقي نظرة على حالة مماثلة:
public FeatureParameterDistribution( GaussianMatrix traitFeatureWeightDistribution, GaussianArray biasFeatureWeightDistribution) { Debug.Assert( (traitFeatureWeightDistribution == null && biasFeatureWeightDistribution == null) || traitFeatureWeightDistribution.All( w => w != null && w.Count == biasFeatureWeightDistribution.Count), "The provided distributions should be valid and consistent in the number of features."); .... }
تحذير PVS-Studio :
V3080 محتمل
للإشارة الخالية. ضع في اعتبارك فحص "traitFeatureWeightDistribution". ميزة التوصية ParameterDistribution.cs 65
نحن نتخلص من الفائض ، مع ترك المنطق الوحيد لحساب القيمة المنطقية ، بحيث يكون من الأسهل معرفة:
(traitFeatureWeightDistribution == null && biasFeatureWeightDistribution == null) || traitFeatureWeightDistribution.All( w => w != null && w.Count == biasFeatureWeightDistribution.Count)
مرة أخرى ، المُعامل الصحيح لـ || يتم حسابها فقط إذا كانت نتيجة الحساب الأيسر
خاطئة . يمكن أن يكون المعامل الأيسر
خطأ ، بما في ذلك عندما يكون
traitFeatureWeightDistribution == null و
biasFeatureWeightDistribution! = Null . ثم سيتم حساب المعامل الصحيح لعامل التشغيل || ،
وستؤدي استدعاء
traitFeatureWeightDistribution.All إلى
رفع ArgumentNullException .
قطعة أخرى مثيرة للاهتمام من التعليمات البرمجية:
public static double GetQuantile(double probability, double[] quantiles) { .... int n = quantiles.Length; if (quantiles == null) throw new ArgumentNullException(nameof(quantiles)); if (n == 0) throw new ArgumentException("quantiles array is empty", nameof(quantiles)); .... }
تحذير PVS-Studio :
V3095 تم استخدام كائن "
كميات " قبل التحقق من صحته. خطوط التحقق: 91 ، 92. Runtime OuterQuantiles.cs 91
لاحظ أن خاصية
lengthiles.Length يتم
الوصول إليها أولاً ، ومن ثم
يتم التحقق من قيمة
Quantities ( قيمة
فارغة) . ونتيجة لذلك ، إذا كانت
الكم == فارغة ، فإن الطريقة ستستثني استثناء ، خطأ قليلًا ، وقليلاً ليس حيث كانت هناك حاجة إليه. يبدو أنهم أفسدوا الخطوط في الأماكن.
إذا كنت قد تعاملت بنجاح مع اكتشاف الأخطاء السابقة بنفسك ، أقترح عليك تحضير فنجان قهوة ومحاولة تكرار الإنجاز ، والعثور على الخطأ في الطريقة أدناه. لجعلها أكثر إثارة للاهتمام ، أقتبس كود كامل للطريقة.
(
رابط بالحجم الكامل )

حسنًا ، حسنًا ، كانت تلك مزحة (أو هل نجحت؟!). دعنا نبسط المهمة قليلاً:
if (sample.Precision < 0) { precisionIsBetween = true; lowerBound = -1.0 / v; upperBound = -mean.Precision; } else if (sample.Precision < -mean.Precision) { precisionIsBetween = true; lowerBound = 0; upperBound = -mean.Precision; } else {
هل تحسنت؟ أصدر المحلل التحذير التالي لهذا الرمز:
V3008 يتم تعيين المتغير "lowerBound" مرتين على التوالي. ربما هذا خطأ. تحقق من الخطوط: 324 ، 323. Runtime GaussianOp.cs 324
في الواقع ، في فرع
آخر آخر ، يتم تعيين قيمة المتغير
lowerBound مرتين على التوالي. على ما يبدو (وبالحكم من خلال الكود أعلاه) ، يجب أن يكون المتغير
العلوي ملزمًا في إحدى التعيينات.
نتابع المزيد.
private void WriteAucMatrix(....) { .... for (int c = 0; c < classLabelCount; c++) { int labelWidth = labels[c].Length; columnWidths[c + 1] = labelWidth > MaxLabelWidth ? MaxLabelWidth : labelWidth; for (int r = 0; r < classLabelCount; r++) { int countWidth = MaxValueWidth; if (countWidth > columnWidths[c + 1]) { columnWidths[c + 1] = countWidth; } } .... }
تحذير PVS-Studio :
V3081 لا يتم استخدام العداد 'r' داخل حلقة متداخلة. ضع في اعتبارك فحص استخدام عداد "c". الحلقة 459
يرجى ملاحظة أن عداد الحلقة الداخلية -
r - لا يستخدم في جسم هذه الحلقة. وبسبب هذا ، اتضح أنه خلال جميع تكرارات الحلقة الداخلية يتم تنفيذ نفس العمليات على نفس العناصر - بعد كل شيء ، تستخدم المؤشرات أيضًا عداد الحلقة الخارجية (
ج ) ، وليس الداخلي (
ص ).
دعونا نرى ما الذي وجد أنه مثير للاهتمام.
public RegexpFormattingSettings( bool putOptionalInSquareBrackets, bool showAnyElementAsQuestionMark, bool ignoreElementDistributionDetails, int truncationLength, bool escapeCharacters, bool useLazyQuantifier) { this.PutOptionalInSquareBrackets = putOptionalInSquareBrackets; this.ShowAnyElementAsQuestionMark = showAnyElementAsQuestionMark; this.IgnoreElementDistributionDetails = ignoreElementDistributionDetails; this.TruncationLength = truncationLength; this.EscapeCharacters = escapeCharacters; }
تحذير PVS-Studio :
V3117 معلمة المُنشئ 'useLazyQuantifier' غير مستخدمة. وقت التشغيل RegexpFormattingSettings.cs 38
لا يستخدم
المُنشئ معلمة واحدة -
useLazyQuantifier . يبدو هذا مريبًا بشكل خاص على خلفية حقيقة أن خاصية بالاسم والنوع
المطابقين معرَّفة في الفئة -
UseLazyQuantifier . على ما يبدو ، لقد نسوا تهيئته من خلال المعلمة المقابلة.
التقى العديد من معالجات الأحداث الخطرة المحتملة. فيما يلي مثال على أحدها:
public class RecommenderRun { .... public event EventHandler Started; .... public void Execute() {
تحذير PVS-Studio :
V3083 استدعاء غير آمن للحدث "بدأ" ، NullReferenceException ممكن. ضع في اعتبارك تعيين حدث لمتغير محلي قبل استدعاؤه. مقيم التوصية .Run.cs 115
والحقيقة هي أنه بين التحقق من عدم المساواة
الفارغ واستدعاء المعالج ، يمكن إلغاء اشتراك حدث ، وإذا لم يكن هناك مشتركون في الوقت الحالي بين التحقق من عدم وجود واستدعاء معالجات ، فسيتم
طرح NullReferenceException . لتجنب مثل هذه المشاكل ، على سبيل المثال ، يمكنك حفظ الرابط إلى سلسلة التفويض في متغير محلي أو استخدام عامل التشغيل "؟." لاستدعاء معالجات.
بالإضافة إلى مقتطف الرمز أعلاه ، كان هناك 35 مكانًا من هذا القبيل.
بالمناسبة ، تم أيضًا تلبية
785 تحذيرًا من
V3024 . يتم إصدار تحذير
V3024 عند مقارنة الأرقام الحقيقية باستخدام عوامل التشغيل '! =' Or '=='. لن أسهب في الحديث هنا عن سبب عدم صحة هذه المقارنات دائمًا - المزيد عن هذا مكتوب في الوثائق ، وهناك أيضًا رابط إلى
StackOverflow (هذا هو).
بالنظر إلى أن الصيغ والحسابات قد تم الوفاء بها في كثير من الأحيان ، يمكن أن تكون هذه التحذيرات مهمة أيضًا ، على الرغم من أنها تنتقل إلى المستوى 3 (نظرًا لأنها بعيدة عن الصلة في جميع المشاريع).
إذا كنت متأكدًا من أن هذه التحذيرات ليست ذات صلة ، فيمكنك إزالتها
بنقرة واحدة تقريبًا ، مما يقلل العدد الإجمالي لعمليات المحلل.
الخلاصة
بطريقة ما اتضح أنه لفترة طويلة لم أكتب مقالات عن فحص المشاريع ، ولمس هذه العملية مرة أخرى كان لطيفًا للغاية. آمل أن تكون قد تعلمت أيضًا شيئًا جديدًا / مفيدًا من هذه المقالة ، أو على الأقل قرأته باهتمام.
أتمنى للمطورين تصحيحًا مبكرًا لمجالات المشكلة وأذكركم بأن ارتكاب الأخطاء أمر طبيعي ، لكننا أشخاص. لهذا ، هناك حاجة إلى أدوات إضافية مثل التحليلات الثابتة للعثور على ما فاته شخص ، أليس كذلك؟ على أي حال - حظا سعيدا مع المشروع ، وشكرا للعمل!
وتذكر أنه يتم تحقيق أقصى فائدة للمحلل الثابت باستخدامه
المنتظم .
كل التوفيق!

إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بترجمة: Sergey Vasiliev.
ما هي الأخطاء الكامنة في Infer.NET Code؟