تحقق Telerik UI لـ UWP للتعرف على PVS-Studio

الصورة 2

لقد أصبح تقليدًا أن يبدأ المبرمجون الذين يعيدون تزويد فريق PVS-Studio بعملهم من خلال كتابة مقال عن تحليل مشروع مفتوح المصدر. هذه المرة ، سيكون مثل هذا المشروع الذي تم إثباته هو Telerik UI لـ UWP.

PVS-Studio محلل الكود


PVS-Studio هي أداة للكشف عن الأخطاء ونقاط الضعف المحتملة في الكود المصدري للبرامج المكتوبة بلغات C و C ++ و C # و Java. يعمل على أنظمة التشغيل Windows و Linux و macOS.

يوفر المحلل سيناريوهات الاستخدام المختلفة:

  • يمكن استخدامها محليًا على أجهزة التطوير ، حيث يتم دمجها كعنصر إضافي في Visual Studio أو IntelliJ IDEA ؛
  • يمكن أن تتكامل مع منصة ضمان الجودة المستمرة SonarQube ؛
  • يمكن استخدامها بشكل مستقل ، والاندماج في نظام التجميع ؛
  • من الممكن استخدام أداة مراقبة الترجمة ؛
  • التكامل مع Azure DevOps و Jenkins و TeamCity و Travis CI وأنظمة مماثلة أمر ممكن ؛
  • و هكذا.

مشروع مدقق


Telerik UI for UWP عبارة عن مجموعة من مكونات واجهة المستخدم لنظام التشغيل Windows Universal Platform (UWP). يمكن العثور على الكود المصدري للمشروع على جيثب . تحتوي المجموعة على أكثر من 20 مكونًا يسمح لك بتصور البيانات في شكل رسوم بيانية ، وإنشاء قوائم وجداول ، واستخدام خريطة لعرض البيانات المرتبطة بموقع ما.

شظايا جذبت الانتباه عند دراسة تقرير المحلل


تحذير PVS-Studio : V3013 من الغريب أن يكون نص الدالة "OnMinValuePropertyChanged" مساوياً تمامًا لجسم وظيفة "OnMaxValuePropertyChanged". RadGauge.cs 446

private static void OnMinValuePropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args) { double newVal = (double)args.NewValue; ValidateValue(newVal); RadGauge gauge = sender as RadGauge; if (gauge.panel != null) { gauge.panel.UpdateOnMinMaxValueChange(); } if(AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged)) { var peer = FrameworkElementAutomationPeer.FromElement(gauge) as RadGaugeAutomationPeer; if (peer != null) { peer.RaiseMinimumPropertyChangedEvent((double)args.OldValue, (double)args.NewValue); } } } private static void OnMaxValuePropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args) { double newVal = (double)args.NewValue; ValidateValue(newVal); RadGauge gauge = sender as RadGauge; if (gauge.panel != null) { gauge.panel.UpdateOnMinMaxValueChange(); } if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged)) { var peer = FrameworkElementAutomationPeer.FromElement(gauge) as RadGaugeAutomationPeer; if (peer != null) { peer.RaiseMinimumPropertyChangedEvent((double)args.OldValue, (double)args.NewValue); } } } 

اكتشف المحلل طريقتين ، OnMinValuePropertyChanged و OnMaxValuePropertyChanged ، والتي تقوم بتنفيذ نفس الإجراءات. لدي شكوك قوية بأن هناك خطأ قد تسلل إلى هذا الرمز. لاحظ أن الأسلوب OnMinValuePropertyChanged وأسلوب OnMaxValuePropertyChanged يستخدم RaiseMinimumPropertyChangedEvent . في نفس الوقت ، في فئة RadGaugeAutomationPeer ، يمكنك العثور على طريقة "الحد الأدنى" و "الحد الأقصى":

 internal void RaiseMaximumPropertyChangedEvent(double oldValue, double newValue) { this.RaisePropertyChangedEvent( RangeValuePatternIdentifiers.MaximumProperty, oldValue, newValue); } internal void RaiseMinimumPropertyChangedEvent(double oldValue, double newValue) { this.RaisePropertyChangedEvent( RangeValuePatternIdentifiers.MinimumProperty, oldValue, newValue); } 

لا يتم استخدام الأسلوب RaiseMaximumPropertyChangedEvent في التعليمات البرمجية ، ولكن يتم استخدام RaiseMinimumPropertyChangedEvent مرتين. كما تعلمون ، فإن أداء أسلوب OnMaxValuePropertyChanged يثير أسئلة ... أعتقد أنه كان من المفترض أن يكتب ما يلي:

 private static void OnMaxValuePropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args) { .... peer.RaiseMaximumPropertyChangedEvent((double)args.OldValue, (double)args.NewValue); .... } 

لكن على الرغم من ذلك ، لا يبدو الرمز أنيقًا جدًا نظرًا للعدد الكبير من العناصر المكررة. من الصعب أن نفهم ، والخطوط المتكررة تمنع انتباه المبرمج ، ويصبح من الصعب إجراء مراجعة الكود في مثل هذه الظروف. لكن أدوات التحليل الثابتة تقوم بعمل ممتاز في التحقق من مثل هذه الشفرة (ولكن هذا لا يعني أنه يمكنك القيام به دون إعادة التسكين ولا سيما دون تقليل عدد الخطوط المتكررة).

من أعلاه ومقتطف الشفرة التالي ، يمكننا افتراض أن المؤلفين لا يكرهون نسخ اللصق. ومع ذلك ، مثلنا جميعا ... :)

تحذير PVS-Studio : V3001 هناك عنصر تعبيرات فرعية متطابقة. RenderSize == حجم فارغ 'إلى اليسار وإلى يمين' || ' المشغل. TiltInteractionEffect.cs 181

 private static bool IsPointInElementBounds(FrameworkElement element, Point position) { Size emptySize = new Size(0, 0); if (element.RenderSize == emptySize || element.RenderSize == emptySize) { return false; } return new Rect(....).Contains(position); } 

اكتشف المحلل جزءًا من رمز الخطأ يمينًا إلى يمين عامل التشغيل "||" العبارة if تستخدم نفس التعبيرات الفرعية. من الواضح أن التعبير الثاني قد بدا مختلفًا. ربما في مكان RenderSize الثاني يجب أن يكون DesiredSize . أو لا ينبغي أن يكون هناك تعبير ثانٍ على الإطلاق. في أي حال ، تحتاج هذه الشفرة إلى إصلاح.

تحذير PVS-Studio : V3001 هناك نص تعبيرات فرعية متطابقة '[0] ==' - '' إلى اليسار وإلى يسار '||' المشغل. RadNumericBox.cs 1057

 private void ValidateText() { string text = this.textBox.Text; .... if (text.Length == 1 && (text[0] == '-' || text[0] == '-')) { if (this.isNegative) { this.isNegative = false; } else { this.SetText(string.Empty); } return; } .... } 

هنا ، يكتب المطور النص الذي تم إدخاله في حقل مربع النص إلى متغير. ثم تتم مقارنة الحرف الأول من السلسلة مرتين بنفس الحرف "-" ، وهو قرار مشبوه. من الواضح أن التحقق من صحة النص في هذه الوظيفة لا يعمل كما هو مقصود في الأصل.

تحذير PVS-Studio : V3001 هناك تعبيرات فرعية متطابقة 'newValue.HasValue' إلى اليسار وإلى يمين المشغل '&&'. DateTimePicker.cs 576

 private static void OnValueChanged(object sender, DependencyPropertyChangedEventArgs args) { DateTimePicker picker = sender as DateTimePicker; var newValue = (DateTime?)args.NewValue; if (newValue.HasValue && newValue != null) // <= .... } 

تعبير newValue.HasValue بإرجاع true إذا كان newValue يحتوي على أي قيمة ، والتعبير newValue! = Null يفعل نفس الشيء. يولي المحلل الانتباه إلى ذلك ، وما يجب فعله هو إزالة أحد التعبيرات الفرعية أو استبداله بأخرى (إذا كان يجب التحقق من شيء آخر) ، فإن الأمر متروك للمطورين لاتخاذ القرار.

تحذير PVS-Studio : V3125 تم استخدام كائن "CurrentAttachedMenu" بعد أن تم التحقق منه ضد قيمة خالية. خطوط التحقق: 98 ، 96. PopupService.cs 98

 internal static class PopupService { .... private static void Overlay_PointerPressed(....) { if (CurrentAttachedMenu == null || !CurrentAttachedMenu.hitTestService. HitTest(e.GetCurrentPoint(CurrentAttachedMenu).Position).Any()) { CurrentAttachedMenu.IsOpen = false; HideOverlay(); } } } 

إذا تبين أن متغير CurrentAttachedMenu خالي ، فإن تقييم التعبير CurrentAttachedMenu.IsOpen سيثير استثناء. للوهلة الأولى ، يبدو أن هذا خطأ مطبعي بسيط ولا يعني مقارنة مع فارغة ، ولكن العملية العكسية - '! ='. ولكن بعد ذلك سيحدث استثناء في حالة العبارة if إذا كان متغير CurrentAttachedMenu خاليًا .

علاوة على ذلك ، كان هناك 37 تحذيراً من نفس التحذيرات ، يبدو أن بعضها يشير إلى الأخطاء. لكن لوصفها في إطار مقال واحد ما زال كثيرًا ، لذا سأتركها دون مراقبة.

تحذير PVS-Studio : V3019 ربما تتم مقارنة متغير غير صحيح بالقيمة الخالية بعد التحويل باستخدام كلمة "as". تحقق من المتغيرات "dragDropElement" ، و "uiDragDropElement". DragDrop.cs 91

 internal static void StartDrag(....) { var dragDropElement = sender as IDragDropElement; .... UIElement uiDragDropElement = dragDropElement as UIElement; .... if (dragDropElement == null || !dragDropElement.CanStartDrag(trigger, initializeContext)) { return; } .... } 

من المحتمل جدًا أن يكون المؤلف قد خلط بين المتغيرات. لا يتم التحقق من عدم المساواة الفارغة من خلال الرابط الذي تم الحصول عليه كنتيجة للقالب ، ولكن بواسطة الأصلي ( dragDropElement ). على الأرجح ، كان من المفترض أن يتم التحقق من ارتباط uiDragDropElement . يتم تأكيد الحدس أيضًا من خلال حقيقة أن المبرمج استخدم uiDragDropElement أيضًا دون التحقق من وجود قيمة خالية .

تحذير PVS-Studio : فحص متكرر V3030 . تم التحقق من الشرط "! ShowIndicatorWhenNoData" بالفعل في السطر 139. RadDataBoundListBox.PullToRefresh.cs 141

 internal void HandlePullToRefreshItemStateChanged(object item, ItemState state) { .... bool showIndicatorWhenNoData = this.ShowPullToRefreshWhenNoData; if (this.realizedItems.Count == 0 && !showIndicatorWhenNoData) { if (state == ItemState.Recycled && !showIndicatorWhenNoData) { this.StopPullToRefreshLoading(false); this.HidePullToRefreshIndicator(); } return; } .... } 

عثر المحلل على جزء من الكود يتم فيه إعادة التحقق من متغير showIndicatorWhenNoData نفسه ، في ظل شرطين . ربما يكون الشيك بسيطًا ، لكن هناك أيضًا احتمال أن يكون أحد التعبيرات الفرعية المكررة مختلفًا على الإطلاق.

تحذير PVS-Studio : V3031 يمكن تبسيط عملية التحقق المفرطة. '||' يحيط بالمشغل تعبيرات معاكسة. SelectedItemCollection.cs 77

 internal class SelectedItemCollection : ObservableCollection<object> { .... private bool CanInsertItem(object item) { return this.suspendLevel == 0 && this.AllowSelect && ((!this.AllowMultipleSelect && this.Count == 0) || this.AllowMultipleSelect); } } 

هذا الجزء من الكود ليس خاطئًا رسميًا. المحلل تلميحات في بعض التكرار رمز في هذه الحالة. ومع ذلك ، تجدر الإشارة إلى أن الكود الإضافي يكون أحيانًا نتيجة لخطأ ، على سبيل المثال ، عندما يتم فحص متغير آخر ، بدلاً من متغير واحد ، عدة مرات.

يمكنك تقليل هذا الشرط قليلاً وإزالة الرمز الإضافي. على سبيل المثال ، مثل هذا:

 internal class SelectedItemCollection : ObservableCollection<object> { .... private bool CanInsertItem(object item) { return this.suspendLevel == 0 && this.AllowSelect && (this.AllowMultipleSelect || this.Count == 0); } } 

رسائل مماثلة أخرى:

  • V3031 يمكن تبسيط عملية التحقق المفرطة. '||' يحيط بالمشغل تعبيرات معاكسة. SelectedItemCollection.cs 93
  • V3031 يمكن تبسيط عملية التحقق المفرطة. '||' يحيط بالمشغل تعبيرات معاكسة. StackVirtualizationStrategy.cs 49
  • V3031 يمكن تبسيط عملية التحقق المفرطة. '||' عامل التشغيل محاط بعبارات التعبيرات المعاكسة 'state == null' و 'state! = null'. LocalFieldDescriptionsProviderBase.cs 24

النظر في قطعة أخرى من التعليمات البرمجية حيث عاد محلل ما يلي:

تحذيرات PVS-Studio :

  • V3137 يتم تعيين متغير "leftMargin" ولكن لا يتم استخدامه بحلول نهاية الوظيفة. DragDrop.cs 87
  • V3137 يتم تعيين متغير "topMargin" ولكن لا يتم استخدامه بحلول نهاية الوظيفة. DragDrop.cs 88

 internal static class DragDrop { .... double leftMargin = 0d; double topMargin = 0d; if (frameworkElementSource != null) { leftMargin = frameworkElementSource.Margin.Left; // <= topMargin = frameworkElementSource.Margin.Top; // <= } if (dragDropElement == null || !dragDropElement.CanStartDrag(trigger, initializeContext)) { return; } var context = dragDropElement .DragStarting(trigger, initializeContext); if (context == null) { return; } var startDragPosition = e .GetCurrentPoint(context.DragSurface.RootElement).Position; var relativeStartDragPosition = e .GetCurrentPoint(uiDragDropElement).Position; var dragPositionMode = DragDrop .GetDragPositionMode(uiDragDropElement); AddOperation(new DragDropOperation( context, dragDropElement, dragPositionMode, e.Pointer, startDragPosition, relativeStartDragPosition)); } 

يفيد المحلل في أنه تم تعيين متغيرات leftMargin و topMargin ، لكن بعد ذلك لا يتم استخدام هذه المتغيرات حتى نهاية الطريقة. ربما لا يوجد خطأ هنا ، ولكن يبدو أن هذا الرمز مشبوه. قد يكون هذا بسبب الخطأ المطبعي أو إعادة بيع المباني غير الناجحة.

تم العثور على نفس المشكلة في مكان آخر: V3137 يتم تعيين متغير 'currentColumnLength' ولكن لا يتم استخدامه بحلول نهاية الوظيفة. WrapLayout.cs 824

تحذير PVS-Studio : يتم دائمًا إعادة كتابة "فهرس" المعلمة V3061 في نص الطريقة قبل استخدامها. DataEngine.cs 1443

 private static Tuple<Group, int> FindGroupAndItemIndex(.... int index, ....) { if (exhaustiveSearch) { .... } else { var aggregateRowGroup = rowRootGroup; var rowGroupNames = valueProvider.GetRowGroupNames(item); foreach (var groupName in rowGroupNames) { Group group; if (aggregateRowGroup.TryGetGroup(groupName, out group)) { aggregateRowGroup = group; } } index = aggregateRowGroup.IndexOf(item, // <= valueProvider.GetSortComparer()); return Tuple.Create(aggregateRowGroup, index); } } 

يتم الكتابة فوق معلمة الفهرس للأسلوب FindGroupAndItemIndex قبل استخدامها. على الأرجح ، يشير هذا إلى خطأ مبرمج.

تحذير PVS-Studio : الاحتجاج غير الآمن على الحدث "مكتمل" ، NullReferenceException ممكن. النظر في تعيين الحدث إلى متغير محلي قبل استدعاء ذلك. ActionBase.cs 32

 internal abstract class ActionBase { .... protected virtual void OnCompleted() { this.IsCompleted = true; if (this.Completed != null) { this.Completed(this, EventArgs.Empty); } } } 

سمح المبرمج بإجراء مكالمة قد تكون غير آمنة لمعالج الأحداث في هذه الطريقة ، مما قد يؤدي إلى استثناء من النوع NullReferenceException . سيتم طرح استثناء شريطة ألا يكون هذا الحدث بين الاختيار الفارغ واستدعاء معالجات الأحداث.

هناك 49 مشاكل مماثلة أخرى في التعليمات البرمجية. لن يكون من المثير للاهتمام مشاهدتها جميعًا في هذه المقالة ، وسيكون بمقدور المؤلفين العثور عليها بسهولة باستخدام PVS-Studio ، لذلك سننتقل إلى أخطاء أخرى.

PVS-Studio Warning : V3145 dereference غير الآمن لهدف WeakReference ، فكّر في فحص info.Target. يمكن أن يكون الكائن قد تم تجميعه بين التحقق من "IsAlive" والوصول إلى خاصية "Target". FadeAnimation.cs 84

 public class RadFadeAnimation : RadAnimation { .... protected internal override void ApplyAnimationValues(PlayAnimationInfo info) { .... if (info.Target.Opacity != opacity) // <= { info.Target.Opacity = opacity; } .... } .... } 

يحذر المحلل من خطورة استثناء نوع NullReferenceException عند الوصول إلى خاصية info.Target.Opacity . من أجل فهم جوهر المشكلة بشكل أفضل ، تحتاج إلى إلقاء نظرة على أجزاء من فئة PlayAnimationInfo ، وعلى وجه الخصوص ، خاصية Target .

 public class PlayAnimationInfo { .... private WeakReference target; .... public PlayAnimationInfo(Storyboard storyboard, RadAnimation animation, UIElement target) { .... this.target = new WeakReference(target); .... } .... public UIElement Target { get { if (this.target.IsAlive) { return this.target.Target as UIElement; } return null; } } .... } 

بشكل عام ، كلما قمت بحفر هذا الرمز ، زادت المشاكل المحتملة التي يمكنك العثور عليها. دعونا نلقي نظرة على الأكثر إثارة للاهتمام - واحد الذي أصدر المحلل تحذيرا. الحقيقة هي أنه حتى إذا كان التنفيذ يتبع الفرع آنذاك لبيان if ، فإن هذا لا يضمن إعادة مرجع غير صفري. بغض النظر عن المحادثات حول المدلى بها ، هنا نعتبر أن كل شيء مسموح به بسبب تهيئة الكائن في المُنشئ.

كيف هذا ممكن؟ الحقيقة هي أنه إذا كان الاختيار بين IsAlive والدعوة الهدف يتم تنفيذ مجموعة البيانات المهملة ، والتي بموجبها يقع الكائن المشار إليه بواسطة WeakReference ، فإن هذا. target.Target سيعود لاغية . بمعنى أن التحقق من IsAlive لا يضمن أنه في المرة التالية التي يتم فيها الوصول إلى الهدف ، لا يزال الكائن متاحًا.

بالمناسبة ، فإن الوضع هو العودة فارغة. يمسك قاعدة تشخيصية أخرى: V3080 dereference null الممكنة. النظر في تفتيش "info.Target". FadeAnimation.cs 84

حدثت مشكلات مماثلة في التعليمات البرمجية عدة مرات:

  • V3145 التراجع غير الآمن عن هدف WeakReference ، فكّر في فحص الهدف. يمكن أن يكون الكائن قد تم تجميعه قبل الوصول إلى خاصية "الهدف". MoveXAnimation.cs 80
  • V3145 التراجع غير الآمن عن هدف WeakReference ، فكّر في فحص الهدف. يمكن أن يكون الكائن قد تم تجميعه قبل الوصول إلى خاصية "الهدف". MoveYAnimation.cs 80
  • V3145 dereference غير الآمن لهدف WeakReference ، فكّر في فحص info.Target. يمكن أن يكون الكائن قد تم تجميعه قبل الوصول إلى خاصية "الهدف". PlaneProjectionAnimation.cs 244
  • V3145 dreference غير آمن لهدف WeakReference. يمكن أن يكون الكائن قد تم تجميعه بين التحقق من "IsAlive" والوصول إلى خاصية "Target". WeakEventHandler.cs 109

دعنا ننتقل إلى المثال التالي.

تحذير PVS-Studio : V3066 تم تمرير ترتيب غير صحيح للوسيطات إلى مُنشئ 'NotifyCollectionChangedEventArgs': 'oldItem' و 'newItem'. CheckedItemsCollection.cs 470

 public class CheckedItemsCollection<T> : IList<T>, INotifyCollectionChanged { .... private NotifyCollectionChangedEventArgs GenerateArgs(....) { switch (action) { case NotifyCollectionChangedAction.Add: .... case NotifyCollectionChangedAction.Remove: .... case NotifyCollectionChangedAction.Replace: return new NotifyCollectionChangedEventArgs( action, oldItem, newItem, changeIndex); // <= default: return new NotifyCollectionChangedEventArgs(action); } } } 

لفهم معنى هذا التحذير الصادر عن المحلل ، يجدر النظر إلى معلمات مُنشئ NotifyCollectionChangedEventArgs :

  public NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction action, object newItem, object oldItem, int index); 

المحلل يحذر من ذلك في التعبير
  return new NotifyCollectionChangedEventArgs( action, oldItem, newItem, changeIndex); 

تبديل المتغيرات oldItem و newItem . في تعريف المنشئ ، يتم سردها بترتيب مختلف. سواء تم ذلك بوعي أم لا ، يمكن للمرء أن يخمن فقط.

تحذير PVS-Studio : V3102 وصول مشبوه إلى عنصر 'x' بواسطة فهرس ثابت داخل حلقة. DataEngine.cs 1718

 private class ObjectArrayComparer : IEqualityComparer<object[]> { public bool Equals(object[] x, object[] y) { .... for (int i = 0; i < x.Length; i++) { if (!object.Equals(x[0], y[0])) // <= { return false; } } return true; } .... } 

في كل تكرار في الحلقة ، يقارن المبرمج x [0] و y [0]. ومع ذلك ، فإن الحلقة لا معنى لها في هذا الرمز ، حيث تتم مقارنة العناصر الأولى فقط. على الأرجح ، كان هذا يعني مقارنة العناصر المقابلة للصفائف. ثم سيكون الرمز الصحيح مثل هذا:

 for (int i = 0; i < x.Length; i++) { if (!object.Equals(x[i], y[i])) { return false; } } 

تحذير PVS-Studio : V3123 ربما يعمل المشغل '؟: بطريقة مختلفة عما كان متوقعًا. أولويتها أقل من أولوية المشغلين الآخرين في حالتها. EditRowHostPanel.cs 35

 protected override Size MeasureOverride(Size availableSize) { .... bool shouldUpdateRowHeight = editorLine == 0 || displayedElement == null ? false : displayedElement.ContainerType != typeof(DataGridGroupHeader); .... } 

يرتبط التحذير باستخدام المشغل "؟:". لها أولوية أقل من ! = ، || ، ==. لذلك ، قد لا يتم تقييم تعبير كما هو مخطط بواسطة المبرمج. على ما يبدو ، في هذه الحالة ، هذه إشارة إيجابية خاطئة ويعمل الرمز بشكل صحيح. لكن قراءة هذا الرمز صعب للغاية ولا يوجد أي يقين من أنه يتم فهمه بشكل صحيح. يبدو أن المطور كتب بطريقة لا أحد يفهم أي شيء :) أفضل طريقة للقيام بذلك هي أكثر قابلية للقراءة - استخدم الأقواس أو عبارة if .

تحذير PVS-Studio : V3078 سيتم فقد ترتيب الفرز الأصلي بعد استدعاء متكرر لأسلوب "OrderBy". استخدم طريقة "ThenBy" للحفاظ على التصنيف الأصلي. GridModel.Selection.cs 107

 internal partial class GridModel { private void BuildCellSelectionRegions(....) { .... this.MergeCellSelectionRegions(selectedItemsInView .OrderBy(c => c.Column.ItemInfo.LayoutInfo.Line) .OrderBy(c => c.RowItemInfo.LayoutInfo.Line)); } } 

يرتبط الخطأ باستدعاء OrderBy مرة أخرى لمجموعة من النوع IOrderedEnumerable . هنا يتم فرز المجموعة أولاً حسب الأعمدة ، ثم حسب الصفوف. علاوة على ذلك ، في وقت الفرز حسب الصفوف ، لا يتم أخذ الفرز حسب الأعمدة في الاعتبار في أي مكان. لكي لا تفقد الفرز حسب الأعمدة وفرز المجموعة حسب عدة معايير في وقت واحد ، ثم استخدم ThenBy :

  this.MergeCellSelectionRegions(selectedItemsInView .OrderBy(c => c.Column.ItemInfo.LayoutInfo.Line) .ThenBy(c => c.RowItemInfo.LayoutInfo.Line)); 

تحذير PVS-Studio : V3008 يتم تعيين قيم " currentColumnLength " مرتين على التوالي. ربما هذا خطأ. خطوط التحقق: 791 ، 785. WrapLayout.cs 791

 private void OnAvailableLengthChanged(double oldValue, double newValue) { .... if (....) { if (currentColumnLength > 0) { var paddingValue = Math.Max(0, newValue - currentColumnLength); this.paddingRenderInfo.Add(paddingValue); currentColumnLength = 0; // <= slotCount++; } this.ColumnSlotsRenderInfo.Update(i, newValue); this.paddingRenderInfo.Add(0); currentColumnLength = 0; // <= slotCount++; continue; } else { .... } .... } 

بدا من المشكوك فيه للمحلل أنه تم تعيين متغير currentColumnLength قيمة مرتين. لا يتم استخدام المتغير بين المهام. بغض النظر عن الحالة ، فإن هذا المتغير سيكون في النهاية صفرًا. هذا الرمز هو إما غير صحيح أو زائدة عن الحاجة.

PVS-Studio Warning : V3127 تم العثور على شظايا رمز مماثلة. ربما ، هذا خطأ مطبعي ويجب استخدام متغير "blankIconContainer" بدلاً من RadRatingItem.cs 240 "fillIconContainer"

 public class RadRatingItem : RadContentControl { .... protected override void OnApplyTemplate() { .... this.filledIconContainer = this.GetTemplateChild( "FilledIconContainer") as Border; if (this.filledIconContainer == null) // <= { throw new MissingTemplatePartException( "FilledIconContainer", typeof(Border)); } this.emptyIconContainer = this.GetTemplateChild( "EmptyIconContainer") as Border; if (this.filledIconContainer == null) // <= { throw new MissingTemplatePartException( "EmptyIconContainer", typeof(Border)); } this.Initialize(); } .... } 

بسبب خطأ مطبعي ، ظهرت شرطين متطابقة في التعليمات البرمجية. اذا حكمنا من خلال الاستثناء الذي تم إنشاؤه ، يجب أن يكون الشرط الثاني كما يلي:

 if (this.emptyIconContainer == null) { throw new MissingTemplatePartException( "EmptyIconContainer", typeof(Border)); } 

تحذير PVS-Studio : V3020 "انقطاع" غير مشروط داخل حلقة. NodePool.cs 189

 public IEnumerable<KeyValuePair<int, List<T>>> GetUnfrozenDisplayedElements() { foreach (var item in this.generatedContainers) { foreach (var pair in item.Value) { if (!pair.IsFrozen) { yield return item; } break; } } } 

وجد المحلل أن بيان الاستراحة هنا لا ينتمي إلى عبارة if . سيتم تنفيذ Break بغض النظر عن قيمة الزوج. إذا تم تجميده وبسبب هذا في foreach ، سيتم تنفيذ تكرار واحد فقط.

أن أختتم نظرتي في التحذيرات. حتى يتمكن مطورو Telerik من إجراء تحليل أكمل للشفرات وإصلاح العيوب ، نحن على استعداد لتزويدهم بترخيص مؤقت. بالإضافة إلى ذلك ، يمكنهم الاستفادة من الاستخدام المجاني لخيار PVS-Studio المقدم لمؤلفي المشروعات المفتوحة المصدر.

استنتاج


على الرغم من قيام مطوري Telerik UI لـ UWP بعمل رائع ، إلا أنه لم يكن بدون أخطاء مطبعية ، كما يحدث عادةً :). يمكن بسهولة العثور على كل هذه الأخطاء من قبل محلل ثابت وتصحيحها. الشيء الأكثر أهمية هو استخدام محلل بشكل صحيح ومنتظم .



إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Ekaterina Nikiforova. فحص Telerik UI لـ UWP كطريقة للبدء باستخدام PVS-Studio .

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


All Articles