فحص Telerik UI لـ UWP كطريقة للبدء باستخدام PVS-Studio

الصورة 2

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

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


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

يمكن تشغيل PVS-Studio بعدة طرق:

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

المشروع قيد التحليل


Telerik UI for UWP عبارة عن مجموعة من عناصر تحكم UI لنظام التشغيل 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 ، تنفيذ نفس الإجراءات. أظن بشدة أن هناك خطأ هنا. لاحظ أن كلا الطريقتين تستدعي نفس الأسلوب ، 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); } 

يتم استخدام الأسلوب RaiseMinimumPropertyChangedEvent مرتين ، بينما لا يتم استخدام الأسلوب RaiseMaximumPropertyChangedEvent على الإطلاق. هذا يجعلني أشك في أن أسلوب 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); } 

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

رسالة تشخيص 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 و newValue! = Null ، تُرجع true إذا كان newValue له قيمة. يشير المحلل إلى ذلك ، ولكن ما إذا كان ينبغي إصلاح هذا الخطأ عن طريق إزالة أحد التعبيرات الفرعية أو عن طريق استبداله بأخرى (في حالة وجود شيء آخر لفحصه) ، فهذا شيء يمكن لمؤلفي هذا الرمز فقط اكتشافه.

رسالة تشخيص 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 يمكن تبسيط عملية التحقق المفرطة. '||' عامل التشغيل محاط بعبارات التعبيرات المعاكسة "الحالة == فارغة" و "الحالة! = فارغة". 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: V3083 الاحتجاج غير الآمن للحدث "مكتمل" ، 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: V3145 إلغاء الأمان غير الآمن لهدف 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 ، فإنه لا يضمن إرجاع مرجع غير فارغ حتى لو لم نأخذ تأثيرات تحويل النوع في الاعتبار (يتم تهيئة الكائن بواسطة المنشئ) .

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

بالمناسبة ، عودة فارغة. تم الكشف عن المشكلة عن طريق تشخيص آخر: V3080 dereference خالية ممكن. النظر في تفتيش "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); 

يخبرنا المحلل أن المتغيرات oldItem و newItem يتم تبديلهما في التعبير التالي:

  return new NotifyCollectionChangedEventArgs( action, oldItem, newItem, changeIndex); 

ومع ذلك ، فإن تطبيق المنشئ له تلك المتغيرات المدرجة في الترتيب المعاكس. يمكنك فقط أن تتساءل عما إذا كان هذا قد تم عن قصد أم لا.

رسالة تشخيص 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: 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 . سيتم تنفيذه بغض النظر عن القيمة المخزّنة في زوج . إذا تم تجميده ، فستكرر حلقة foreach مرة واحدة فقط.

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

استنتاج


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

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


All Articles