Sudah menjadi tradisi bagi pengembang yang baru dipekerjakan di tim PVS-Studio untuk memulai dengan menulis artikel meninjau bug yang ditemukan oleh penganalisa di beberapa proyek open-source. Telerik UI untuk UWP adalah proyek yang dipilih untuk ditinjau hari ini.
Penganalisa kode PVS-Studio
PVS-Studio adalah alat untuk mendeteksi bug dan kerentanan potensial dalam kode sumber program yang ditulis dalam C, C ++, C #, dan Java. Penganalisa berjalan pada Windows, Linux, dan macOS.
PVS-Studio dapat dijalankan dalam beberapa cara:
- sebagai plugin untuk Visual Studio atau IntelliJ IDEA secara lokal di masing-masing komputer pengembang;
- dengan mengintegrasikan dengan SonarQube: platform inspeksi kualitas kode berkelanjutan;
- sebagai aplikasi mandiri untuk berintegrasi ke dalam sistem build;
- dengan berjalan dalam kombinasi dengan utilitas pemantauan kompilasi khusus;
- dengan mengintegrasikan dengan Azure DevOps, Jenkins, TeamCity, Travis CI, dan sistem serupa lainnya;
- dll.
Proyek sedang dianalisis
Telerik UI untuk UWP adalah seperangkat kontrol UI untuk Universal Windows Platform (UWP). Kode sumber proyek
tersedia di Github . Set ini mencakup lebih dari 20 komponen yang memungkinkan pengguna untuk memvisualisasikan data dalam bentuk bagan, membuat daftar dan tabel, dan menggunakan peta untuk menampilkan konten dalam konteks geografis.
Cuplikan kode menarik yang dilaporkan oleh penganalisa
Pesan diagnostik PVS-Studio: V3013 Aneh bahwa tubuh fungsi 'OnMinValuePropertyChanged' sepenuhnya setara dengan tubuh fungsi '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); } } }
Dua metode,
OnMinValuePropertyChanged dan
OnMaxValuePropertyChanged , melakukan tindakan yang sama. Saya sangat curiga ada bug di sini. Perhatikan bahwa kedua metode memanggil metode yang sama,
RaiseMinimumPropertyChangedEvent , sedangkan kelas
RadGaugeAutomationPeer mengimplementasikan metode individual untuk "Minimum" dan "Maksimum":
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); }
Metode
RaiseMinimumPropertyChangedEvent digunakan dua kali, sedangkan metode
RaiseMaximumPropertyChangedEvent tidak digunakan sama sekali. Ini membuat saya ragu metode
OnMaxValuePropertyChanged bekerja dengan baik ... Saya kira itu dimaksudkan untuk terlihat seperti ini:
private static void OnMaxValuePropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args) { .... peer.RaiseMaximumPropertyChangedEvent((double)args.OldValue, (double)args.NewValue); .... }
Tetapi bahkan dengan perbaikan ini, kode tidak terlihat rapi karena banyak elemen duplikat. Sulit untuk dibaca, dan garis-garis yang tampak serupa menumpulkan perhatian Anda, yang membuat tinjauan kode pekerjaan yang sulit. Alat analisis statis, sebaliknya, dapat dengan mudah menanganinya (yang tidak berarti Anda tidak boleh memperbaiki kode Anda dan terutama menghilangkan garis duplikat).
Melihat fragmen ini dan yang berikutnya, saya curiga bahwa penulis proyek menikmati copy-paste setiap saat. Ya, kita semua ... :)
Pesan diagnostik PVS-Studio: V3001 Ada
elemen sub-ekspresi yang identik.RenderSize == emptySize 'ke kiri dan ke kanan' || ' operator. 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); }
Kedua operan dari '||' operator dalam ekspresi kondisional pernyataan
if diwakili oleh subekspresi yang identik. Jelas, subekspresi kedua harus berbeda. Mungkin
RenderSize kedua dimaksudkan untuk
DiinginkanSize atau mungkin subekspresi kedua tidak boleh ada sama sekali. Bagaimanapun, kode ini perlu diperbaiki.
Pesan diagnostik PVS-Studio: V3001 Ada teks sub-ekspresi yang identik '[0] ==' - '' di sebelah kiri dan di sebelah kanan '||' operator. 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; } .... }
Teks yang dimasukkan ke dalam bidang kotak teks dibaca ke dalam variabel. Karakter pertama string kemudian dibandingkan dua kali dengan karakter '-', yang tidak terlihat benar. Jelas, fungsi ini tidak melakukan validasi teks sebagaimana dimaksud.
Pesan diagnostik PVS-Studio: V3001 Ada sub-ekspresi 'newValue.HasValue' yang identik di sebelah kiri dan di sebelah kanan operator '&&'. 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)
Baik ekspresi bersyarat,
newValue.HasValue dan
newValue! = Null , mengembalikan
true jika
newValue memiliki nilai. Penganalisa menunjukkan hal ini, tetapi apakah bug ini harus diperbaiki dengan menghapus salah satu dari subekspresi atau menggantinya dengan yang lain (jika ada sesuatu yang harus diperiksa) adalah sesuatu yang hanya dapat diketahui oleh pembuat kode ini.
Pesan diagnostik PVS-Studio: V3125 Objek 'CurrentAttachedMenu' digunakan setelah diverifikasi terhadap nol. Periksa baris: 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(); } } }
Jika variabel
CurrentAttachedMenu kebetulan sama dengan
nol , mengevaluasi ekspresi
CurrentAttachedMenu.IsOpen akan menghasilkan peningkatan pengecualian. Sepertinya itu hanya kesalahan ketik dan pengembang sebenarnya berarti operasi yang berlawanan, '! =', Daripada cek nol, tetapi jika itu masalahnya, kondisi pernyataan
if akan melempar pengecualian jika variabel
CurrentAttachedMenu adalah sama dengan
nol .
Ada
37 peringatan lainnya dari jenis ini, beberapa di antaranya tampaknya menunjukkan bug asli. Tapi itu peringatan terlalu banyak untuk satu artikel, jadi saya akan melewatkannya.
Pesan diagnostik PVS-Studio: V3019 Kemungkinan variabel yang salah dibandingkan dengan nol setelah konversi jenis menggunakan kata kunci 'sebagai'. Periksa variabel '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; } .... }
Programmer harus bingung satu variabel untuk yang lain. Pemeriksaan nol dilakukan pada referensi sumber,
dragDropElement , daripada referensi yang dihasilkan oleh para pemain,
uiDragDropElement , yang sebenarnya dimaksudkan untuk diperiksa. Asumsi ini didukung oleh fakta bahwa
uiDragDropElement digunakan lebih lanjut tanpa cek nol.
Pesan diagnostik PVS-Studio: V3030 Pemeriksaan berulang. Kondisi '! ShowIndicatorWhenNoData' sudah diverifikasi di baris 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; } .... }
Dua kondisi memeriksa
showIndicatorWhenNoData variabel yang sama. Pemeriksaan kedua mungkin berlebihan, tetapi juga mungkin bahwa salah satu subekspresi duplikat harus sepenuhnya menjadi sesuatu yang lain.
Pesan diagnostik PVS-Studio: V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' Operator dikelilingi oleh ekspresi yang berlawanan. 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); } }
Secara teknis, cuplikan ini benar; analisa hanya menunjukkan redundansi tertentu dalam kondisi tersebut. Tetapi perlu diingat bahwa kode redundan sering merupakan tanda kesalahan pemrograman seperti memeriksa satu variabel lebih dari yang diperlukan daripada beberapa variabel lainnya.
Kondisi ini dapat disederhanakan sedikit dengan menghapus kode yang tidak perlu sebagai berikut:
internal class SelectedItemCollection : ObservableCollection<object> { .... private bool CanInsertItem(object item) { return this.suspendLevel == 0 && this.AllowSelect && (this.AllowMultipleSelect || this.Count == 0); } }
Peringatan serupa lainnya:
- V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' Operator dikelilingi oleh ekspresi yang berlawanan. SelectedItemCollection.cs 93
- V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' Operator dikelilingi oleh ekspresi yang berlawanan. StackVirtualizationStrategy.cs 49
- V3031 Pemeriksaan berlebihan dapat disederhanakan. '||' operator dikelilingi oleh ekspresi berlawanan 'state == null' dan 'state! = null'. LocalFieldDescriptionProviderBase.cs 24
Mari kita pertimbangkan potongan kode lain, yang dikeluarkan oleh penganalisa sebagai berikut:
Pesan diagnostik PVS-Studio:- V3137 Variabel 'leftMargin' ditugaskan tetapi tidak digunakan pada akhir fungsi. DragDrop.cs 87
- V3137 Variabel 'topMargin' diberikan tetapi tidak digunakan pada akhir fungsi. DragDrop.cs 88
internal static class DragDrop { .... double leftMargin = 0d; double topMargin = 0d; if (frameworkElementSource != null) { leftMargin = frameworkElementSource.Margin.Left;
Variabel
leftMargin dan
topMargin ditugaskan beberapa nilai tetapi tidak pernah digunakan setelah itu. Ini belum tentu bug, tetapi kode seperti itu masih terlihat mencurigakan. Ini bisa menjadi tanda kesalahan ketik atau refactoring yang buruk.
Ada peringatan lain dari jenis ini: V3137 Variabel 'currentColumnLength' ditugaskan tetapi tidak digunakan pada akhir fungsi. WrapLayout.cs 824
Pesan diagnostik PVS-Studio: V3061 Parameter 'indeks' selalu ditulis ulang dalam tubuh metode sebelum digunakan. 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,
Parameter
indeks metode
FindGroupAndItemIndex ditimpa sebelum digunakan. Kemungkinan besar, ini menunjukkan kesalahan programmer.
Pesan diagnostik PVS-Studio: V3083 Doa yang tidak aman dari acara 'Selesai', NullReferenceException dimungkinkan. Pertimbangkan menugaskan acara ke variabel lokal sebelum menjalankannya. ActionBase.cs 32
internal abstract class ActionBase { .... protected virtual void OnCompleted() { this.IsCompleted = true; if (this.Completed != null) { this.Completed(this, EventArgs.Empty); } } }
Pengatur kejadian dipanggil dengan cara yang berpotensi tidak aman, dengan risiko meningkatkan
NullReferenceException . Ini akan terjadi jika acara tidak memiliki pelanggan tersisa antara cek nol dan panggilan dari penangan acara.
Laporan itu menunjukkan
49 masalah lagi dari jenis ini. Mereka tidak terlalu menarik untuk dibahas di sini, dan bagaimanapun, penulis proyek dapat dengan mudah menemukan mereka dengan PVS-Studio sendiri, jadi mari kita beralih ke contoh berikut.
Pesan diagnostik PVS-Studio: V3145 dereference tidak aman dari target WeakReference, pertimbangkan untuk memeriksa info. Target. Objek tersebut bisa berupa sampah yang dikumpulkan antara memeriksa 'IsAlive' dan mengakses properti 'Target'. FadeAnimation.cs 84
public class RadFadeAnimation : RadAnimation { .... protected internal override void ApplyAnimationValues(PlayAnimationInfo info) { .... if (info.Target.Opacity != opacity)
NullReferenceException dapat dimunculkan ketika menangani properti
info.Target.Opacity . Untuk lebih memahami apa masalahnya, kita perlu melihat blok tertentu dari kelas
PlayAnimationInfo , khususnya properti
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; } } .... }
Sebenarnya, semakin dalam Anda menggali kode ini, semakin banyak masalah potensial yang Anda gali. Mari kita lihat yang paling menarik - yang memicu peringatan. Masalahnya adalah bahwa bahkan jika eksekusi mengikuti cabang
lain dari pernyataan
if , itu tidak menjamin pengembalian referensi non-nol bahkan jika kita tidak mengambil efek konversi tipe ke akun (objek diinisialisasi oleh konstruktor) .
Bagaimana itu mungkin? Anda lihat, jika objek yang dirujuk oleh
WeakReference adalah pengumpulan sampah antara cek
IsAlive dan panggilan ke
Target ,
this.target.Target akan mengembalikan
nol . Yaitu, pemeriksaan
IsAlive tidak menjamin bahwa objek akan tetap tersedia saat berikutnya Anda memanggil
Target .
Omong-omong,
pengembaliannya nol; masalah terdeteksi oleh diagnostik lain: V3080 Kemungkinan null dereference. Pertimbangkan untuk memeriksa 'info.Target'. FadeAnimation.cs 84
Ada beberapa cacat lagi seperti itu:
- V3145 dereference tidak aman dari target WeakReference, pertimbangkan memeriksa target. Objek tersebut dapat berupa sampah yang dikumpulkan sebelum properti 'Target' diakses. MoveXAnimation.cs 80
- V3145 dereference tidak aman dari target WeakReference, pertimbangkan memeriksa target. Objek tersebut dapat berupa sampah yang dikumpulkan sebelum properti 'Target' diakses. MoveYAnimation.cs 80
- V3145 Dereferensi tidak aman dari target WeakReference, pertimbangkan untuk memeriksa info. Target. Objek tersebut dapat berupa sampah yang dikumpulkan sebelum properti 'Target' diakses. PlaneProjectionAnimation.cs 244
- V3145 Dereferensi tidak aman dari target WeakReference. Objek tersebut bisa berupa sampah yang dikumpulkan antara memeriksa 'IsAlive' dan mengakses properti 'Target'. WeakEventHandler.cs 109
Mari kita beralih ke contoh berikut.
Pesan diagnostik PVS-Studio: V3066 Kemungkinan urutan argumen yang salah diteruskan ke 'NotifyCollectionChangedEventArgs' konstruktor: 'oldItem' dan '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);
Untuk mengetahui arti dari peringatan ini, kita perlu melihat parameter konstruktor
NotifyCollectionChangedEventArgs :
public NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction action, object newItem, object oldItem, int index);
Penganalisa memberitahu kita bahwa variabel
oldItem dan
newItem bertukar dalam ekspresi berikut:
return new NotifyCollectionChangedEventArgs( action, oldItem, newItem, changeIndex);
Namun, implementasi konstruktor memiliki variabel-variabel yang terdaftar dalam urutan yang berlawanan. Anda hanya bisa bertanya-tanya apakah ini dilakukan dengan sengaja atau tidak.
Pesan diagnostik PVS-Studio: V3102 Akses mencurigakan ke elemen objek 'x' dengan indeks konstan di dalam satu loop. 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]))
Elemen
x [0] dan
y [0] dibandingkan pada setiap iterasi loop. Tetapi karena hanya elemen pertama yang dibandingkan, loop tidak masuk akal. Pengembang mungkin bermaksud membandingkan elemen masing-masing array sebagai gantinya. Jika demikian, versi yang benar akan terlihat seperti ini:
for (int i = 0; i < x.Length; i++) { if (!object.Equals(x[i], y[i])) { return false; } }
Pesan diagnostik PVS-Studio: V3123 Mungkin
operator '?:' Bekerja dengan cara yang berbeda dari yang diharapkan. Prioritasnya lebih rendah daripada prioritas operator lain dalam kondisinya. EditRowHostPanel.cs 35
protected override Size MeasureOverride(Size availableSize) { .... bool shouldUpdateRowHeight = editorLine == 0 || displayedElement == null ? false : displayedElement.ContainerType != typeof(DataGridGroupHeader); .... }
Peringatan ini berkaitan dengan penggunaan Operator '?:'. Diutamakan lebih rendah dari
! =, ||, dan
== , yang berarti urutan mengevaluasi ungkapan di atas mungkin berbeda dari yang diharapkan. Kasus khusus ini tampaknya salah positif, dengan kode yang sebenarnya berfungsi sebagaimana dimaksud. Tetapi kode seperti itu sangat sulit dibaca, dan Anda tidak pernah yakin apakah Anda memahaminya dengan benar. Sepertinya ditulis dengan cara yang sengaja sehingga tidak ada yang bisa mengetahuinya :) Cara terbaik untuk membuatnya lebih mudah dibaca adalah dengan menggunakan tanda kurung atau pernyataan
if .
Pesan diagnostik PVS-Studio: V3078 Urutan penyortiran asli akan hilang setelah panggilan berulang ke metode 'OrderBy'. Gunakan metode 'ThenBy' untuk mempertahankan penyortiran asli. 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)); } }
Bug ini ada hubungannya dengan panggilan berulang metode
OrderBy pada kumpulan tipe
IOrderedEnumerable . Koleksi pertama diurutkan berdasarkan kolom dan kemudian oleh baris. Masalahnya adalah bahwa hasil pengurutan pertama - berdasarkan kolom - tidak disimpan di mana pun dan akan hilang saat pengurutan menurut baris dimulai. Jika Anda ingin menyimpan hasil pengurutan kolom dan melakukan beberapa kriteria, gunakan metode
ThenBy :
this.MergeCellSelectionRegions(selectedItemsInView .OrderBy(c => c.Column.ItemInfo.LayoutInfo.Line) .ThenBy(c => c.RowItemInfo.LayoutInfo.Line));
Pesan diagnostik PVS-Studio: V3008 Variabel 'currentColumnLength' diberi nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 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;
Analiser merasa aneh bahwa variabel
currentColumnLength diberi nilai dua kali sementara tidak digunakan di antara dua penugasan ini. Apa pun kondisinya, variabel pada akhirnya akan berakhir sebagai
nol . Kode ini salah atau berlebihan.
Pesan diagnostik PVS-Studio: V3127 Dua fragmen kode serupa ditemukan. Mungkin, ini adalah salah ketik dan variabel 'emptyIconContainer' harus digunakan sebagai ganti 'filledIconContainer' RadRatingItem.cs 240
public class RadRatingItem : RadContentControl { .... protected override void OnApplyTemplate() { .... this.filledIconContainer = this.GetTemplateChild( "FilledIconContainer") as Border; if (this.filledIconContainer == null)
Dua kondisi identik di atas muncul sebagai akibat dari salah ketik. Pengecualian yang diberikan oleh kode ini menunjukkan bahwa kondisi kedua akan terlihat seperti ini:
if (this.emptyIconContainer == null) { throw new MissingTemplatePartException( "EmptyIconContainer", typeof(Border)); }
Pesan diagnostik PVS-Studio: V3020 'Istirahat' tanpa syarat dalam satu lingkaran. 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; } } }
Pernyataan
istirahat bukan bagian dari pernyataan
if . Itu akan mengeksekusi tidak peduli nilai apa yang disimpan dalam
pair.IsFrozen , jadi
foreach loop hanya akan mengulangi sekali saja.
Itu saja untuk ulasan saya tentang bug yang ditemukan di Telerik. Kami siap memberikan pengembang dengan lisensi sementara gratis sehingga mereka dapat melakukan analisis yang lebih menyeluruh dan memperbaiki cacat. Mereka juga dapat menggunakan
opsi lisensi PVS-Studio gratis yang tersedia untuk pengembang open-source.
Kesimpulan
Meskipun penulis Telerik UI untuk UWP telah melakukan pekerjaan besar dalam mengembangkan proyek mereka, mereka masih membiarkan sejumlah kesalahan ketik masuk, seperti yang biasanya terjadi pada kami :). Semua bug itu bisa dengan mudah ditangkap dan diperbaiki menggunakan penganalisa statis, tetapi hal penting yang perlu diingat tentang analisis statis adalah bahwa itu harus digunakan
dengan cara yang benar dan secara teratur .