Kesalahan apa yang disembunyikan dalam kode Infer.NET?


Publikasi kode sumber Microsoft untuk proyek-proyeknya adalah alasan yang bagus untuk memverifikasinya. Kali ini tidak terkecuali, dan hari ini kita melihat tempat mencurigakan yang ditemukan dalam kode Infer.NET. Turun dengan anotasi - langsung ke intinya!

Sedikit tentang proyek dan penganalisa


Infer.NET adalah sistem pembelajaran mesin yang dikembangkan oleh para spesialis dari Microsoft. Kode sumber proyek baru-baru ini tersedia di GitHub , yang merupakan alasan untuk verifikasi. Rincian lebih lanjut tentang proyek ini dapat ditemukan, misalnya, di sini .

Proyek ini diperiksa dengan menggunakan statis static analyzer PVS-Studio versi 6.26. Biarkan saya mengingatkan Anda bahwa PVS-Studio sedang mencari kesalahan dalam kode di C \ C ++ \ C # (dan segera di Jawa) di bawah Windows, Linux, macOS. Kode C # sejauh ini kami hanya menganalisis di bawah Windows. Penganalisa dapat diunduh dan dicoba pada proyek Anda.

Cek itu sendiri sangat sederhana dan tanpa masalah. Sebelumnya, saya membongkar proyek dari GitHub, memulihkan paket yang diperlukan (dependensi), dan memastikan bahwa proyek tersebut berhasil dibangun. Ini diperlukan agar penganalisa memiliki akses ke semua informasi yang diperlukan untuk analisis penuh. Setelah berkumpul dalam beberapa klik, saya meluncurkan analisis solusi melalui plugin PVS-Studio untuk Visual Studio.

Omong-omong, ini bukan proyek pertama dari Microsoft yang diuji menggunakan PVS-Studio - ada yang lain: Roslyn , MSBuild , PowerShell , CoreFX dan lainnya .

Catatan Jika Anda atau kenalan Anda tertarik untuk menganalisis kode Java, Anda dapat menulis dukungan kepada kami dengan memilih "Saya ingin penganalisis untuk Java". Tidak ada versi beta publik dari penganalisis, tetapi harus segera tersedia. Di suatu tempat di laboratorium rahasia (melalui dinding), para pria secara aktif mengerjakannya.

Tapi cukup bicara abstrak - mari kita lihat masalah dalam kode.

Apakah ini bug atau fitur?


Saya sarankan mencoba menemukan kesalahan sendiri - tugas yang sepenuhnya bisa diselesaikan. Tidak ada lelucon dalam semangat apa yang ada dalam artikel " 10 Kesalahan Teratas dalam Proyek C ++ untuk 2017 ", jujur. Jadi, jangan buru-buru membaca penganalisis yang diberikan setelah cuplikan kode.

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); } .... } 

Peringatan PVS-Studio : V3001 Ada sub-ekspresi identik 'ganda.IsInfinity (transition1Weight.Value)' di sebelah kiri dan di sebelah kanan operator '&&'. Runtime Automaton.Simplification.cs 479

Seperti yang Anda lihat dari potongan kode, metode ini bekerja dengan sepasang variabel - transition1 dan transition2 . Penggunaan nama-nama yang mirip kadang-kadang cukup dibenarkan, tetapi perlu diingat bahwa dalam kasus ini kemungkinan tidak sengaja membuat kesalahan di suatu tempat dengan peningkatan nama.

Inilah yang terjadi ketika memeriksa angka untuk infinity ( double.IsInfinity ). Karena kesalahan, kami memeriksa nilai variabel yang sama 2 kali - transition1Weight.Value . Nilai yang dicentang di subekspresi kedua adalah variabel transition2Weight.Value .

Kode mencurigakan serupa lainnya.

 internal MethodBase ToMethodInternal(IMethodReference imr) { .... bf |= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; .... } 

PVS-Studio Warning : V3001 Ada sub-ekspresi yang identik 'BindingFlags.Public' di sebelah kiri dan di sebelah kanan '|' operator. Kompiler CodeBuilder.cs 194

Saat membentuk nilai variabel bf , elemen enumerasi BindingFlags.Public digunakan dua kali. Entah kode ini berisi operasi penandaan tambahan, atau alih-alih penggunaan BindingFlags.Public yang kedua , harus ada nilai enumerasi yang berbeda.

Omong-omong, dalam kode sumber kode ini ditulis pada satu baris. Sepertinya saya bahwa jika diformat dalam gaya tabel (seperti di sini), masalahnya lebih mudah untuk dideteksi.

Mari kita lanjutkan. Saya membawa seluruh tubuh metode dan sekali lagi saya sarankan Anda menemukan kesalahan (atau mungkin kesalahan) sendiri.

 private void ForEachPrefix(IExpression expr, Action<IExpression> action) { // This method must be kept consistent with GetTargets. if (expr is IArrayIndexerExpression) ForEachPrefix(((IArrayIndexerExpression)expr).Target, action); else if (expr is IAddressOutExpression) ForEachPrefix(((IAddressOutExpression)expr).Expression, action); else if (expr is IPropertyReferenceExpression) ForEachPrefix(((IPropertyReferenceExpression)expr).Target, action); else if (expr is IFieldReferenceExpression) { IExpression target = ((IFieldReferenceExpression)expr).Target; if (!(target is IThisReferenceExpression)) ForEachPrefix(target, action); } else if (expr is ICastExpression) ForEachPrefix(((ICastExpression)expr).Expression, action); else if (expr is IPropertyIndexerExpression) ForEachPrefix(((IPropertyIndexerExpression)expr).Target, action); else if (expr is IEventReferenceExpression) ForEachPrefix(((IEventReferenceExpression)expr).Target, action); else if (expr is IUnaryExpression) ForEachPrefix(((IUnaryExpression)expr).Expression, action); else if (expr is IAddressReferenceExpression) ForEachPrefix(((IAddressReferenceExpression)expr).Expression, action); else if (expr is IMethodInvokeExpression) ForEachPrefix(((IMethodInvokeExpression)expr).Method, action); else if (expr is IMethodReferenceExpression) ForEachPrefix(((IMethodReferenceExpression)expr).Target, action); else if (expr is IUnaryExpression) ForEachPrefix(((IUnaryExpression)expr).Expression, action); else if (expr is IAddressReferenceExpression) ForEachPrefix(((IAddressReferenceExpression)expr).Expression, action); else if (expr is IDelegateInvokeExpression) ForEachPrefix(((IDelegateInvokeExpression)expr).Target, action); action(expr); } 

Apakah kamu sudah menemukan? Kami sedang memeriksa!

Peringatan PVS-Studio :
  • V3003 Penggunaan pola 'jika (A) {...} else jika (A) {...}' terdeteksi. Ada kemungkinan kehadiran kesalahan logis. Periksa baris: 1719, 1727. Kompiler CodeRecognizer.cs 1719
  • V3003 Penggunaan pola 'jika (A) {...} else jika (A) {...}' terdeteksi. Ada kemungkinan kehadiran kesalahan logis. Periksa baris: 1721, 1729. Kompiler CodeRecognizer.cs 1721

Sederhanakan kode ini untuk membuat masalahnya lebih jelas.

 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) .... } 

Ekspresi bersyarat dan kemudian cabang beberapa jika pernyataan digandakan. Mungkin kode ini ditulis menggunakan metode salin-rekat, itulah sebabnya masalah muncul. Sekarang ternyata cabang saat itu dari duplikat tidak akan pernah dieksekusi, karena:

  • jika ekspresi kondisional benar, tubuh pernyataan if pertama dari pasangan yang sesuai dijalankan;
  • jika ekspresi kondisional salah dalam kasus pertama, itu akan salah dalam yang kedua.

Karena cabang saat itu berisi tindakan yang sama, sekarang terlihat seperti kode yang berlebihan yang membingungkan. Ada kemungkinan bahwa ini adalah jenis masalah yang berbeda - alih-alih mengambil, pemeriksaan lain seharusnya dilakukan.

Kami melanjutkan.

 public int Compare(Pair<int, int> x, Pair<int, int> y) { if (x.First < y.First) { if (x.Second >= y.Second) { // y strictly contains x return 1; } else { // No containment - order by left bound return 1; } } else if (x.First > y.First) { if (x.Second <= y.Second) { // x strictly contains y return -1; } else { // No containment - order by left bound return -1; } } .... } 

Peringatan PVS-Studio :
  • V3004 Pernyataan 'lalu' setara dengan pernyataan 'lain'. Runtime RegexpTreeBuilder.cs 1080
  • V3004 Pernyataan 'lalu' setara dengan pernyataan 'lain'. Runtime RegexpTreeBuilder.cs 1093

Kode terlihat sangat mencurigakan, karena berisi dua pernyataan bersyarat dengan badan yang identik dari cabang saat itu dan yang lain . Dalam kedua kasus, mungkin layak mengembalikan nilai yang berbeda. Atau, jika ini adalah perilaku yang dikandung, akan berguna untuk menghapus pernyataan bersyarat yang berlebihan.

Ada siklus yang menarik. Contoh di bawah ini:

 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 Warning : V3020 'break' tanpa syarat dalam satu loop. Kompiler DefaultFactorManager.cs 474

Karena pernyataan break tanpa syarat, tepat satu iterasi dari loop dilakukan, dan variabel kontrol berubah bahkan tidak digunakan. Secara umum, kode itu terlihat aneh dan mencurigakan.

Metode yang sama (salinan persis) ditemukan di kelas lain. Peringatan analisa yang sesuai: V3020 'Istirahat' tanpa syarat dalam satu loop. Visualizers.Windows FactorManagerView.cs 350

Ngomong-ngomong, sebuah metode bertemu dengan pernyataan terus tanpa syarat dalam satu lingkaran (penganalisa menemukannya dengan diagnostik yang sama), tetapi ada komentar di atasnya yang mengkonfirmasi bahwa ini adalah solusi sementara khusus:

 // TEMPORARY continue; 

Saya ingat bahwa tidak ada komentar semacam itu di dekat pernyataan break tanpa syarat.

Mari kita lanjutkan.

 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"); .... } .... } .... } 

Peringatan PVS-Studio : Ekspresi V3022 'resultIndex == null' selalu salah. Compiler FactorManager.cs 382

Segera, saya perhatikan bahwa antara deklarasi dan verifikasi di atas, nilai variabel resultIndex dapat berubah. Namun, antara cek resultIndex! = Null dan resultIndex == null, nilainya tidak dapat diubah. Oleh karena itu, hasil dari ekspresi resultIndex == null akan selalu salah , yang berarti bahwa pengecualian tidak akan pernah dilempar.

Saya harap Anda memiliki minat dalam menemukan kesalahan sendiri, tanpa saran saya, untuk menemukan masalah, tetapi untuk berjaga-jaga, saya akan mengusulkan untuk melakukannya lagi. Kode metode kecil, saya akan memberikannya secara keseluruhan.

 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()); } 

Mari kita lihat apa yang terjadi di sini. String input diuraikan oleh karakter '|'. Jika panjang array tidak seperti yang diharapkan, pengecualian harus dibuang. Tunggu sebentar ... genre . Panjang <1 && genre. Panjang> 3 ? Karena tidak ada angka yang langsung jatuh ke dalam rentang nilai yang diperlukan oleh ekspresi ( [int.MinValue..1) dan (3..int.MaxValue] ), hasil dari ekspresi akan selalu salah . Oleh karena itu, pemeriksaan ini tidak melindungi terhadap apa pun, dan pengecualian yang diharapkan tidak akan dibuang.

Inilah yang persisnya diingatkan oleh penganalisa: V3022 Ekspresi 'genre. Panjang <1 && genre. Panjang> 3' selalu salah. Mungkin '||' Operator harus digunakan di sini. Fitur Evaluator.cs 242

Operasi fisi yang mencurigakan bertemu.

 public static void CreateTrueThetaAndPhi(....) { .... double expectedRepeatOfTopicInDoc = averageDocLength / numUniqueTopicsPerDoc; .... int cnt = Poisson.Sample(expectedRepeatOfTopicInDoc); .... } 

Peringatan PVS-Studio : V3041 Ungkapan itu secara implisit dilemparkan dari tipe 'int' ke tipe 'double'. Pertimbangkan untuk menggunakan pemeran tipe eksplisit untuk menghindari hilangnya bagian fraksional. Contoh: double A = (double) (X) / Y; LDA Utilities.cs 74

Ini mencurigakan di sini: pembagian integer dilakukan (variabel averageDocLength dan numUniqueTopicsPerDoc bertipe int ), dan hasilnya ditulis ke variabel bertipe double . Pertanyaannya adalah: apakah ini dilakukan secara khusus, atau apakah pembagian bilangan real masih tersirat? Jika variabel diharapkanRepeatOfTopicInDoc bertipe int , ini akan menjernihkan kemungkinan pertanyaan.

Di tempat lain, metode Poisson.Sample , argumen yang merupakan variabel mencurigakan yang diharapkanRepeatOfTopicInDoc , digunakan, misalnya, seperti dijelaskan di bawah ini.

 int numUniqueWordsPerTopic = Poisson.Sample((double)averageWordsPerTopic); 

averageWordsPerTopic adalah tipe int , yang sudah dikonversi menjadi dua kali lipat di tempat penggunaan.

Dan berikut ini adalah tempat penggunaan lainnya:

 double expectedRepeatOfWordInTopic = ((double)numDocs) * averageDocLength / numUniqueWordsPerTopic; .... int cnt = Poisson.Sample(expectedRepeatOfWordInTopic); 

Harap perhatikan bahwa variabel memiliki nama yang sama seperti dalam contoh asli, hanya pembagian bilangan real yang digunakan untuk menginisialisasi ekspektasiRepeatOfWordInTopic (karena konversi eksplisit numDocs menjadi dua kali lipat ).

Secara umum, ada baiknya melihat tempat awal di mana analis mengeluarkan peringatan.

Tetapi refleksi tentang apakah itu layak diedit dan bagaimana, biarkan penulis kode (mereka tahu lebih baik), tetapi mari kita melangkah lebih jauh. Ke divisi mencurigakan berikutnya.

 public static NonconjugateGaussian BAverageLogarithm(....) { .... double v_opt = 2 / 3 * (Math.Log(mx * mz / Ex2 / 2) - m); if (v_opt != v) { .... } .... } 

Peringatan PVS-Studio : V3041 Ungkapan itu secara implisit dilemparkan dari tipe 'int' ke tipe 'double'. Pertimbangkan untuk menggunakan pemeran tipe eksplisit untuk menghindari hilangnya bagian fraksional. Contoh: double A = (double) (X) / Y; Runtime ProductExp.cs 137

Penganalisa lagi mendeteksi operasi mencurigakan dari divisi integer, seperti 2 dan 3 adalah bilangan literer bilangan bulat, dan hasil dari ekspresi 2/3 adalah 0 . Hasilnya, seluruh ekspresi berbentuk:

 double v_opt = 0 * expr; 

Setuju, sedikit aneh. Beberapa kali saya kembali ke peringatan ini, mencoba menemukan semacam tangkapan, dan tidak mencoba menambahkannya ke artikel. Metode ini dipenuhi dengan matematika dan berbagai formula (yang, sejujurnya, saya tidak benar-benar ingin membongkar), Anda tidak pernah tahu apa yang diharapkan. Selain itu, saya mencoba untuk menjadi skeptis mungkin tentang peringatan yang saya tulis di artikel, dan saya menggambarkannya hanya setelah mempelajarinya dengan lebih baik.

Tapi kemudian saya sadar - mengapa saya perlu faktor 0 , ditulis sebagai 2/3 ? Jadi tempat ini layak untuk dilihat.

 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}\" "); } 

Peringatan PVS-Studio : V3080 Kemungkinan null dereference. Pertimbangkan untuk memeriksa 'nilai'. Kompiler WriteHelpers.cs 78

Penegasan analisis yang cukup adil berdasarkan kondisi. Referensi referensi nol dapat terjadi pada nilai ekspresi. Setara ( nilai default) jika nilai == nol . Karena ungkapan ini adalah operan kanan operator | |, untuk menghitungnya, operan kiri harus salah , dan untuk ini sudah cukup bahwa setidaknya salah satu variabel nilai defaultValue \ nilai tidak nol . Akibatnya, jika defaultValue! = Null , dan nilai == null :

  • defaultValue == null -> false ;
  • defaultValue == null && value == null -> false ; (pemeriksaan nilai tidak terjadi)
  • value.Equals (defaultValue) -> NullReferenceException , karena nilainya nol .

Mari kita lihat kasus serupa:

 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."); .... } 

Peringatan PVS-Studio : V3080 Kemungkinan null dereference. Pertimbangkan untuk memeriksa 'traitFeatureWeightDistribution'. Fitur RekomendasiParameterDistribution.cs 65

Kami membuang kelebihan, hanya menyisakan logika untuk menghitung nilai Boolean, sehingga lebih mudah untuk mencari tahu:

 (traitFeatureWeightDistribution == null && biasFeatureWeightDistribution == null) || traitFeatureWeightDistribution.All( w => w != null && w.Count == biasFeatureWeightDistribution.Count) 

Sekali lagi, operan kanan || Ini dihitung hanya jika hasil perhitungan kiri salah . Operan kiri bisa salah , termasuk ketika traitFeatureWeightDistribution == null dan biasFeatureWeightDistribution! = Null . Maka operan yang tepat dari operator || akan dihitung, dan panggilan ke traitFeatureWeightDistribution. Semua akan menaikkan ArgumentNullException .

Sepotong kode menarik lainnya:

 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 Warning : V3095 Objek 'quantiles' digunakan sebelum diverifikasi dengan null. Periksa baris: 91, 92. Runtime OuterQuantiles.cs 91

Perhatikan bahwa properti quantiles.Length pertama kali diakses , dan kemudian quantiles diperiksa untuk null . Akibatnya, jika kuantil == nol , metode ini akan melempar pengecualian, hanya sedikit salah, dan sedikit tidak sesuai dengan yang dibutuhkan. Rupanya, mereka mengacaukan garis di beberapa tempat.

Jika Anda berhasil menangani deteksi kesalahan sebelumnya sendiri, saya sarankan Anda menyeduh secangkir kopi dan mencoba mengulanginya, menemukan kesalahan dalam metode di bawah ini. Untuk membuatnya sedikit lebih menarik, saya mengutip seluruh kode metode ini.

( Tautan ke ukuran penuh )

Gambar 2



Oke, oke, itu lelucon (atau apakah Anda berhasil?!). Mari sederhanakan tugasnya:

 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 { // in this case, the precision should NOT be in this interval. precisionIsBetween = false; lowerBound = -mean.Precision; lowerBound = -1.0 / v; } 

Apakah semakin baik? Penganalisis mengeluarkan peringatan berikut untuk kode ini: V3008 Variabel 'lowerBound' ditugaskan nilai dua kali berturut-turut. Mungkin ini sebuah kesalahan. Periksa baris: 324, 323. Runtime GaussianOp.cs 324

Memang, di cabang yang lain terakhir, nilai variabel lowerBound ditugaskan dua kali berturut-turut. Rupanya (dan dilihat dari kode di atas), variabel upperBound harus terlibat dalam salah satu tugas.

Kami mengikuti lebih jauh.

 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; } } .... } 

Peringatan PVS-Studio : V3081 Penghitung 'r' tidak digunakan di dalam loop bersarang. Pertimbangkan untuk memeriksa penggunaan penghitung 'c'. CommandLine ClassifierEvaluationModule.cs 459

Harap dicatat bahwa penghitung loop dalam - r - tidak digunakan dalam tubuh loop ini. Karena itu, ternyata selama semua iterasi loop dalam operasi yang sama dilakukan pada elemen yang sama - setelah semua, indeks juga menggunakan counter loop luar ( c ), dan bukan dalam ( r ).

Mari kita lihat apa lagi yang menurut saya menarik.

 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; } 

Peringatan PVS-Studio : Parameter konstruktor V3117 'useLazyQuantifier' tidak digunakan. Runtime RegexpFormattingSettings.cs 38

Konstruktor tidak menggunakan satu parameter - useLazyQuantifier . Ini terlihat sangat mencurigakan dengan latar belakang fakta bahwa properti dengan nama dan tipe yang sesuai didefinisikan di kelas - UseLazyQuantifier . Rupanya, mereka lupa menginisialisasi melalui parameter yang sesuai.

Bertemu dengan beberapa penangan acara yang berpotensi berbahaya. Contoh salah satunya diberikan di bawah ini:

 public class RecommenderRun { .... public event EventHandler Started; .... public void Execute() { // Report that the run has been started if (this.Started != null) { this.Started(this, EventArgs.Empty); } .... } .... } 

Peringatan PVS-Studio : V3083 Doa yang tidak aman dari acara 'Dimulai', NullReferenceException dimungkinkan. Pertimbangkan menugaskan acara ke variabel lokal sebelum menjalankannya. Evaluator RecommenderRun.cs 115

Faktanya adalah bahwa antara cek untuk ketidaksetaraan nol dan panggilan dari pawang, suatu peristiwa dapat berhenti berlangganan, dan jika acara tersebut tidak memiliki pelanggan pada saat antara cek untuk nol dan panggilan dari penangan, NullReferenceException akan dilemparkan . Untuk menghindari masalah seperti itu, misalnya, Anda dapat menyimpan tautan ke rantai delegasi dalam variabel lokal atau menggunakan operator '?.' untuk memanggil penangan.

Selain potongan kode di atas, ada 35 tempat seperti itu.

Omong-omong, 785 peringatan V3024 juga dipenuhi. Peringatan V3024 dikeluarkan saat membandingkan bilangan real menggunakan operator '! =' Atau '=='. Saya tidak akan membahas mengapa perbandingan seperti itu tidak selalu benar - lebih lanjut tentang ini ditulis dalam dokumentasi, ada juga tautan ke StackOverflow (ini dia).

Mempertimbangkan bahwa formula dan perhitungan sering dipenuhi, peringatan ini juga dapat menjadi penting, meskipun mereka dibawa ke level 3 (karena mereka jauh dari relevan di semua proyek).

Jika Anda yakin bahwa peringatan ini tidak relevan, Anda dapat menghapusnya dengan hampir satu klik , mengurangi total operasi penganalisa.



Kesimpulan


Entah bagaimana ternyata untuk waktu yang lama saya belum menulis artikel tentang memeriksa proyek, dan menyentuh proses ini lagi cukup menyenangkan. Saya harap Anda juga mempelajari sesuatu yang baru / bermanfaat dari artikel ini, atau setidaknya membacanya dengan penuh minat.

Saya berharap para pengembang melakukan koreksi awal terhadap area masalah dan saya mengingatkan Anda bahwa membuat kesalahan adalah hal yang normal, namun kami adalah orang-orang. Untuk ini, alat tambahan seperti analisa statis diperlukan untuk menemukan apa yang dilewatkan seseorang, bukan? Pokoknya - semoga sukses dengan proyek ini, dan terima kasih untuk pekerjaannya!

Dan ingatlah bahwa manfaat maksimum dari penganalisa statis dicapai dengan penggunaan regulernya .

Semua yang terbaik!



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Sergey Vasiliev. Apa Kesalahan Mengintai di Kode Infer.NET?

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


All Articles