Alat analisa statis PVS-Studio dikenal di dunia C, C ++ dan C # sebagai alat untuk mendeteksi kesalahan dan kerentanan potensial. Namun, kami memiliki beberapa klien dari sektor keuangan, karena ternyata Java dan IBM RPG (!) Sekarang diminati di sana. Tetapi kami selalu ingin lebih dekat dengan dunia Enterprise, oleh karena itu, setelah beberapa pemikiran, kami memutuskan untuk mulai membuat penganalisis Java.
Pendahuluan
Tentu saja, ada kekhawatiran. Sangat mudah untuk mengambil pasar penganalisa dalam IBM RPG. Saya sama sekali tidak yakin bahwa ada alat yang layak untuk analisis statis bahasa ini. Di dunia Jawa, segalanya benar-benar berbeda. Sudah ada sederetan alat untuk analisis statis, dan untuk maju, Anda perlu membuat analisa yang sangat kuat dan keren.
Namun demikian, perusahaan kami telah memiliki pengalaman menggunakan beberapa alat untuk analisis statis Java, dan kami yakin bahwa kami dapat melakukan banyak hal dengan lebih baik.
Selain itu, kami memiliki ide bagaimana menggunakan kekuatan penuh dari penganalisa C ++ kami di penganalisis Java. Tetapi hal pertama yang pertama.
Pohon
Pertama-tama, perlu diputuskan bagaimana kita akan mendapatkan pohon sintaks dan model semantik.
Sintaksis pohon adalah elemen dasar di sekitar mana analisa itu dibangun. Saat melakukan pemeriksaan, penganalisis bergerak melalui pohon sintaks dan memeriksa masing-masing node. Tanpa pohon seperti itu, analisis statis yang serius praktis tidak mungkin dilakukan. Misalnya, mencari kesalahan menggunakan ekspresi reguler tidak
menjanjikan .
Perlu dicatat bahwa hanya pohon sintaks saja tidak cukup. Alat analisis juga memerlukan informasi semantik. Sebagai contoh, kita perlu mengetahui tipe semua elemen pohon, dapat pergi ke deklarasi variabel, dll.
Kami memeriksa beberapa opsi untuk mendapatkan pohon sintaks dan model semantik:
Kami segera meninggalkan gagasan untuk menggunakan ANTLR, karena hal ini tidak perlu menyulitkan pengembangan alat analisis (analisis semantik harus dilaksanakan sendiri). Pada akhirnya, kami memutuskan untuk berhenti di perpustakaan Spoon:
- Ini bukan hanya pengurai, tetapi seluruh ekosistem - ia menyediakan tidak hanya pohon pengurai, tetapi juga peluang untuk analisis semantik, misalnya, memungkinkan Anda untuk mendapatkan informasi tentang jenis variabel, pergi ke deklarasi variabel, mendapatkan informasi tentang kelas induk, dan sebagainya.
- Itu didasarkan pada Eclipse JDT dan mampu mengkompilasi kode.
- Ini mendukung versi terbaru Java dan terus diperbarui.
- Dokumentasi yang baik dan API yang jelas.
Berikut adalah contoh dari metamodel yang disediakan oleh Spoon dan yang kami kerjakan saat membuat aturan diagnostik:
Metamodel ini sesuai dengan kode berikut:
class TestClass { void test(int a, int b) { int x = (a + b) * 4; System.out.println(x); } }
Salah satu hal yang menyenangkan tentang Spoon adalah menyederhanakan pohon sintaks (menghapus dan menambahkan node) untuk membuatnya lebih mudah untuk dikerjakan. Pada saat yang sama, kesetaraan semantik dari metamodel yang disederhanakan dari aslinya dijamin.
Bagi kami, ini berarti, misalnya, bahwa kita tidak perlu lagi khawatir melewatkan tanda kurung tambahan saat melintasi pohon. Selain itu, setiap ekspresi ditempatkan di blok, impor diungkap, dan beberapa penyederhanaan serupa lainnya dibuat.
Misalnya, kode seperti ini:
for (int i = ((0)); (i < 10); i++) if (cond) return (((42)));
akan disajikan sebagai berikut:
for (int i = 0; i < 10; i++) { if (cond) { return 42; } }
Berdasarkan pohon sintaks, analisis berbasis pola yang disebut dilakukan. Ini adalah pencarian kesalahan dalam kode sumber program menggunakan pola kode kesalahan yang terkenal. Dalam kasus paling sederhana, penganalisa mencari tempat-tempat yang terlihat seperti kesalahan di pohon sesuai dengan aturan yang dijelaskan dalam diagnostik yang sesuai. Jumlah pola tersebut sangat besar dan kompleksitasnya dapat sangat bervariasi.
Contoh paling sederhana dari kesalahan yang terdeteksi oleh analisis berbasis pola adalah kode berikut dari proyek jMonkeyEngine:
if (p.isConnected()) { log.log(Level.FINE, "Connection closed:{0}.", p); } else { log.log(Level.FINE, "Connection closed:{0}.", p); }
Blok
pernyataan then and
else adalah sama, kemungkinan besar, ada kesalahan logis.
Berikut adalah contoh serupa lainnya dari proyek Hive:
if (obj instanceof Number) {
Kode ini berisi dua kondisi identik dalam urutan bentuk
if (....) else if (....) else if (....) . Sebaiknya periksa bagian kode ini untuk kesalahan logis, atau hapus kode duplikat.
Analisis aliran data
Selain pohon sintaks dan model semantik, penganalisa membutuhkan mekanisme untuk
menganalisis aliran data .
Analisis aliran data memungkinkan Anda menghitung nilai variabel dan ekspresi yang valid di setiap titik dalam program dan, berkat ini, menemukan kesalahan. Kami menyebut nilai-nilai ini sebagai nilai virtual.
Nilai virtual dibuat untuk variabel, bidang kelas, parameter metode, dan hal-hal lain pada penyebutan pertama. Jika ini adalah tugas, mekanisme Aliran Data menghitung nilai virtual dengan menganalisis ekspresi di sebelah kanan, jika tidak seluruh rentang nilai valid untuk jenis variabel ini diambil sebagai nilai virtual. Sebagai contoh:
void func(byte x)
Setiap kali nilai variabel diubah, mesin Data Flow menghitung ulang nilai virtual. Sebagai contoh:
void func() { int x = 5;
Mesin Data Flow juga memproses pernyataan kontrol:
void func(int x)
Dalam contoh ini, ketika memasuki fungsi, tidak ada informasi tentang kisaran nilai variabel
x , oleh karena itu diatur sesuai dengan jenis variabel (dari -2147483648 ke 2147483647). Kemudian blok kondisional pertama memberikan batasan
x > 3, dan rentang digabungkan. Akibatnya, dalam blok
kemudian kisaran nilai untuk
x adalah dari 4 hingga 2147483647, dan di blok
lain dari -2147483648 hingga 3. Kondisi kedua
x <10 diproses dengan cara yang sama.
Selain itu, Anda harus dapat melakukan perhitungan simbolik murni. Contoh paling sederhana:
void f1(int a, int b, int c) { a = c; b = c; if (a == b)
Di sini, variabel
a diberi nilai
c , variabel
b juga diberi nilai
c , setelah itu
a dan
b dibandingkan. Dalam hal ini, untuk menemukan kesalahan, ingatlah sepotong kayu yang sesuai dengan sisi kanan.
Berikut adalah contoh yang sedikit lebih rumit dengan perhitungan karakter:
void f2(int a, int b, int c) { if (a < b) { if (b < c) { if (c < a)
Dalam kasus seperti itu, sudah perlu untuk menyelesaikan sistem ketidaksetaraan dalam bentuk simbolis.
Mekanisme Aliran Data membantu penganalisa menemukan kesalahan yang sangat sulit ditemukan menggunakan analisis berbasis pola.
Kesalahan ini meliputi:
- Meluap;
- Pergi ke luar negeri array;
- Akses dengan referensi nol atau berpotensi nol;
- Kondisi tidak berarti (selalu benar / salah);
- Kebocoran memori dan sumber daya;
- Pembagian dengan 0;
- Dan beberapa lainnya.
Analisis Aliran Data sangat penting saat mencari kerentanan. Misalnya, jika suatu program tertentu menerima input dari pengguna, ada kemungkinan bahwa input tersebut akan digunakan untuk menyebabkan penolakan layanan, atau untuk mendapatkan kendali atas sistem. Contohnya termasuk kesalahan yang menyebabkan buffer overflows untuk beberapa input, atau, misalnya, injeksi SQL. Dalam kedua kasus, agar penganalisa statis untuk mendeteksi kesalahan dan kerentanan seperti itu, perlu untuk memantau aliran data dan kemungkinan nilai variabel.
Saya harus mengatakan bahwa mekanisme analisis aliran data adalah mekanisme yang kompleks dan luas, dan dalam artikel ini saya hanya menyentuh dasar-dasarnya saja.
Mari kita lihat beberapa contoh kesalahan yang dapat dideteksi menggunakan mekanisme Aliran Data.
Proyek Sarang:
public static boolean equal(byte[] arg1, final int start1, final int len1, byte[] arg2, final int start2, final int len2) { if (len1 != len2) {
Kondisi
len1 == len2 selalu puas, karena pemeriksaan sebaliknya telah dilakukan di atas.
Contoh lain dari proyek yang sama:
if (instances != null) {
Di sini, di blok
else , dereferencing dari null pointer dijamin akan terjadi. Catatan: ini
contohnya sama dengan ini.
Contoh dari proyek JMonkeyEngine:
public static int convertNewtKey(short key) { .... if (key >= 0x10000) { return key - 0x10000; } return 0; }
Di sini variabel
kunci dibandingkan dengan angka 65536, bagaimanapun, itu adalah tipe
pendek , dan nilai maksimum yang mungkin untuk
pendek adalah 32767. Dengan demikian, kondisi tersebut tidak pernah terpenuhi.
Contoh dari proyek Jenkins:
public final R getSomeBuildWithWorkspace() { int cnt = 0; for (R b = getLastBuild(); cnt < 5 && b ! = null; b = b.getPreviousBuild()) { FilePath ws = b.getWorkspace(); if (ws != null) return b; } return null; }
Variabel
cnt diperkenalkan dalam kode ini untuk membatasi jumlah lintasan menjadi lima, tetapi lupa menambahkannya, sebagai hasilnya cek tersebut tidak berguna.
Mekanisme penjelasan
Selain itu, penganalisa membutuhkan mekanisme anotasi. Anotasi adalah sistem markup yang memberikan informasi tambahan kepada penganalisa tentang metode dan kelas yang digunakan, selain apa yang dapat diperoleh dengan menganalisis tanda tangan mereka. Markup dilakukan secara manual, ini adalah proses yang panjang dan memakan waktu, karena untuk mencapai hasil terbaik, perlu memberi anotasi sejumlah besar kelas standar dan metode bahasa Jawa. Ini juga masuk akal untuk membubuhi keterangan perpustakaan populer. Secara umum, anotasi dapat dianggap sebagai basis pengetahuan penganalisa tentang kontrak metode dan kelas standar.
Berikut adalah contoh kecil kesalahan yang dapat dideteksi menggunakan anotasi:
int test(int a, int b) { ... return Math.max(a, a); }
Dalam contoh ini, karena kesalahan ketik, variabel yang sama diteruskan sebagai argumen kedua ke metode
Math.max sebagai argumen pertama. Ungkapan seperti itu tidak ada artinya dan mencurigakan.
Mengetahui bahwa argumen metode
Math.max harus selalu berbeda, penganalisa statis akan dapat mengeluarkan peringatan untuk kode tersebut.
Ke depan, saya akan memberikan beberapa contoh markup kelas dan metode bawaan kami (kode C ++):
Class("java.lang.Math") - Function("abs", Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Returns(Arg1, [](const Int &v) { return v.Abs(); }) - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2) .Returns(Arg1, Arg2, [](const Int &v1, const Int &v2) { return v1.Max(v2); }) Class("java.lang.String", TypeClassification::String) - Function("split", Type::Pointer) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotNull(Arg1)) .Returns(Ptr(NotNullPointer)) Class("java.lang.Object") - Function("equals", Type::Pointer) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(This, Arg1)) Class("java.lang.System") - Function("exit", Type::Int32) .Set(FunctionClassification::NoReturn)
Penjelasan:
- Kelas - kelas beranotasi;
- Fungsi - metode kelas beranotasi;
- Murni - anotasi yang menunjukkan bahwa metode ini bersih, mis. deterministik dan tanpa efek samping;
- Set - atur bendera sembarang untuk metode ini.
- FunctionClassification :: NoDiscard - flag yang berarti bahwa nilai balik metode harus digunakan;
- FunctionClassification :: NoReturn - flag yang menunjukkan bahwa metode tidak mengembalikan kontrol;
- Arg1 , Arg2 , ... , ArgN - argumen untuk metode ini;
- Pengembalian - nilai pengembalian metode;
- Membutuhkan - kontrak untuk metode ini.
Perlu dicatat bahwa selain markup manual, ada pendekatan lain untuk penjelasan - output otomatis kontrak berdasarkan kode byte. Jelas bahwa pendekatan ini memungkinkan Anda untuk hanya menampilkan jenis kontrak tertentu, tetapi memungkinkan untuk memperoleh informasi tambahan secara umum dari semua dependensi, dan tidak hanya dari yang dianotasi secara manual.
Omong-omong, sudah ada alat yang dapat menampilkan kontrak seperti
@Nullable ,
NotNull berdasarkan bytecode -
FABA . Seperti yang saya pahami, turunan FABA digunakan dalam IntelliJ IDEA.
Sekarang kami juga mempertimbangkan untuk menambahkan analisis bytecode untuk mendapatkan kontrak untuk semua metode, karena kontrak ini dapat melengkapi anotasi manual kami.
Aturan diagnostik di tempat kerja sering merujuk pada anotasi. Selain diagnostik, anotasi menggunakan mekanisme Aliran Data. Misalnya, menggunakan anotasi metode
java.lang.Math.abs , ia dapat secara akurat menghitung nilai untuk modul angka. Pada saat yang sama, Anda tidak perlu menulis kode tambahan - cukup tandai metode ini dengan benar.
Pertimbangkan contoh kesalahan dari proyek Hibernate yang dapat dideteksi melalui anotasi:
public boolean equals(Object other) { if (other instanceof Id) { Id that = (Id) other; return purchaseSequence.equals(this.purchaseSequence) && that.purchaseNumber == this.purchaseNumber; } else { return false; } }
Dalam kode ini, metode
equals () membandingkan objek
purchaseSequence dengan dirinya sendiri. Tentunya ini adalah kesalahan ketik dan di sebelah kanan harus itu.
Membeli Keadilan , bukan
membeli Keadilan .
Bagaimana Dr. Frankenstein Merakit Alat Analisis dari Bagian
Karena mekanisme Aliran Data dan anotasi itu sendiri tidak terlalu terikat dengan bahasa tertentu, diputuskan untuk menggunakan kembali mekanisme ini dari penganalisa C ++ kami. Ini memungkinkan kami untuk dengan cepat mendapatkan semua kekuatan inti penganalisa C ++ di Java analyzer kami. Selain itu, keputusan ini juga dipengaruhi oleh fakta bahwa mekanisme ini ditulis dalam C ++ modern dengan sekelompok metaprogramming dan templat magic, dan, karenanya, sangat tidak cocok untuk mentransfer ke bahasa lain.
Untuk mengaitkan bagian Java dengan kernel C ++, kami memutuskan untuk menggunakan
SWIG (Simplified Wrapper and Interface Generator) - alat untuk secara otomatis menghasilkan pembungkus dan antarmuka untuk menghubungkan program C dan C ++ dengan program yang ditulis dalam bahasa lain. Untuk Java, SWIG menghasilkan kode
JNI (Java Native Interface) .
SWIG sangat bagus untuk kasus-kasus di mana sudah ada sejumlah besar kode C ++ yang perlu diintegrasikan ke dalam proyek Java.
Saya akan memberikan contoh minimal bekerja dengan SWIG. Misalkan kita memiliki kelas C ++ yang ingin kita gunakan dalam proyek Java:
CoolClass.h
class CoolClass { public: int val; CoolClass(int val); void printMe(); };
CoolClass.cpp
#include <iostream> #include "CoolClass.h" CoolClass::CoolClass(int v) : val(v) {} void CoolClass::printMe() { std::cout << "val: " << val << '\n'; }
Pertama, Anda perlu membuat
file antarmuka SWIG dengan deskripsi semua fungsi dan kelas yang diekspor. Juga dalam file ini, jika perlu, pengaturan tambahan dibuat.
Contoh.i
%module MyModule %{ #include "CoolClass.h" %} %include "CoolClass.h"
Setelah itu, Anda dapat menjalankan SWIG:
$ swig -c++ -java Example.i
Ini akan menghasilkan file-file berikut:
- CoolClass.java - kelas yang dengannya kita akan langsung bekerja dalam proyek Java;
- MyModule.java - kelas modul tempat semua fungsi dan variabel bebas ditempatkan;
- MyModuleJNI.java - pembungkus Java;
- Example_wrap.cxx - pembungkus C ++.
Sekarang Anda hanya perlu menambahkan file .java yang dihasilkan ke proyek Java dan file .cxx ke proyek C ++.
Terakhir, Anda perlu mengkompilasi proyek C ++ sebagai pustaka dinamis dan memuatnya ke proyek Java menggunakan
System.loadLibary () :
App.java
class App { static { System.loadLibary("example"); } public static void main(String[] args) { CoolClass obj = new CoolClass(42); obj.printMe(); } }
Secara skematis, ini dapat direpresentasikan sebagai berikut:
Tentu saja, dalam proyek nyata semuanya tidak begitu sederhana dan Anda harus melakukan sedikit usaha:
- Untuk menggunakan kelas dan metode templat dari C ++, mereka harus dipakai untuk semua parameter templat yang diterima menggunakan arahan templat% ;
- Dalam beberapa kasus, Anda mungkin perlu menangkap pengecualian yang dilemparkan dari bagian C ++ di bagian Java. Secara default, SWIG tidak menangkap pengecualian dari C ++ (terjadi segfault), namun, dimungkinkan untuk melakukan ini menggunakan % exception directive;
- SWIG memungkinkan Anda untuk memperluas kode plus di sisi Java menggunakan direktif % extended. Misalnya, dalam proyek kami, kami menambahkan metode toString () ke nilai virtual sehingga kami dapat melihatnya di Java debugger;
- Untuk meniru perilaku RAII dari C ++, antarmuka AutoClosable diimplementasikan di semua kelas yang diminati;
- Mekanisme direktur memungkinkan penggunaan polimorfisme lintas-bahasa;
- Untuk jenis yang dialokasikan hanya di dalam C ++ (pada kumpulan memori mereka), konstruktor dan finalizer dihapus untuk meningkatkan kinerja. Pengumpul sampah akan mengabaikan jenis ini.
Anda dapat membaca lebih lanjut tentang semua mekanisme ini dalam
dokumentasi SWIG .
Analyzer kami dibuat menggunakan gradle, yang memanggil CMake, yang, pada gilirannya, memanggil SWIG dan mengkompilasi bagian C ++. Untuk programmer, hal ini terjadi hampir tanpa disadari, jadi kami tidak mengalami ketidaknyamanan khusus selama pengembangan.
Inti dari penganalisa C ++ kami dibangun di bawah Windows, Linux, macOS, sehingga penganalisis Java juga berfungsi di OS ini.
Apa itu aturan diagnostik?
Diagnostik itu sendiri dan kode untuk analisis ditulis dalam Java. Ini karena interaksi yang dekat dengan Spoon. Setiap aturan diagnostik adalah pengunjung, yang metodenya kelebihan beban, di mana elemen-elemen yang menarik bagi kami dielakkan:
Misalnya, kerangka kerja diagnostik V6004 terlihat seperti ini:
class V6004 extends PvsStudioRule { .... @Override public void visitCtIf(CtIf ifElement) {
Plugin
Untuk integrasi termudah dari penganalisa statis ke dalam proyek, kami telah mengembangkan plugin untuk sistem perakitan Maven dan Gradle. Pengguna hanya dapat menambahkan plugin kami ke proyek.
Untuk Gradle:
.... apply plugin: com.pvsstudio.PvsStudioGradlePlugin pvsstudio { outputFile = 'path/to/output.json' .... }
Untuk Maven:
.... <plugin> <groupId>com.pvsstudio</groupId> <artifactId>pvsstudio-maven-plugin</artifactId> <version>0.1</version> <configuration> <analyzer> <outputFile>path/to/output.json</outputFile> .... </analyzer> </configuration> </plugin>
Setelah itu, plugin akan secara independen menerima struktur proyek dan memulai analisis.
Selain itu, kami telah mengembangkan plugin prototipe untuk IntelliJ IDEA.
Plugin ini juga berfungsi di Android Studio.
Sebuah plugin untuk Eclipse saat ini sedang dalam pengembangan.
Analisis tambahan
Kami telah menyediakan mode analisis tambahan yang memungkinkan Anda untuk memeriksa hanya file yang dimodifikasi dan dengan demikian secara signifikan mengurangi waktu yang diperlukan untuk analisis kode. Berkat ini, pengembang akan dapat menjalankan analisis sesering yang diperlukan.
Analisis tambahan mencakup beberapa tahapan:
- Caching metamodel sendok;
- Membangun kembali bagian metamodel yang telah diubah;
- Analisis file yang diubah.
Sistem pengujian kami
Untuk menguji penganalisa Java pada proyek nyata, kami menulis alat khusus yang memungkinkan Anda untuk bekerja dengan database proyek terbuka. Itu ditulis dalam ^ W Python + Tkinter dan cross-platform.
Ia bekerja sebagai berikut:
- Proyek uji versi tertentu diunduh dari repositori di GitHub;
- Proyek sedang dirakit;
- Plugin kami ditambahkan ke pom.xml atau build.gradle (menggunakan git apply);
- Penganalisa statis diluncurkan menggunakan plugin;
- Laporan yang dihasilkan dibandingkan dengan tolok ukur untuk proyek ini.
Pendekatan ini memastikan bahwa tanggapan yang baik tidak hilang sebagai akibat dari mengubah kode penganalisa. Di bawah ini adalah antarmuka utilitas pengujian kami.
Proyek-proyek dalam laporan yang memiliki perbedaan dengan standar ditandai dengan warna merah. Tombol Approve memungkinkan Anda untuk menyimpan versi laporan saat ini sebagai referensi.
Contoh Kesalahan
Secara tradisional, saya akan mengutip beberapa kesalahan dari berbagai proyek terbuka yang ditemukan oleh penganalisa Java kami. Di masa depan, direncanakan untuk menulis artikel dengan laporan yang lebih rinci pada setiap proyek.
Proyek hibernasi
PVS-Studio Warning: V6009 Function 'equals' menerima argumen aneh. Periksa argumen: ini, 1. PurchaseRecord.java 57
public boolean equals(Object other) { if (other instanceof Id) { Id that = (Id) other; return purchaseSequence.equals(this.purchaseSequence) && that.purchaseNumber == this.purchaseNumber; } else { return false; } }
Dalam kode ini, metode
equals () membandingkan objek
purchaseSequence dengan dirinya sendiri. Kemungkinan besar, ini adalah kesalahan ketik dan di sebelah kanan seharusnya itu.
Pembelian Keadilan , bukan
pembelian Keadilan .
PVS-Studio Warning: V6009 Function 'equals' menerima argumen aneh. Periksa argumen: ini, 1. ListHashcodeChangeTest.java 232
public void removeBook(String title) { for( Iterator<Book> it = books.iterator(); it.hasNext(); ) { Book book = it.next(); if ( title.equals( title ) ) { it.remove(); } } }
Operasi yang mirip dengan yang sebelumnya - di sebelah kanan harus
book.title , bukan
judul .
Sarang proyek
Peringatan
PVS-Studio: Ekspresi V6007 'colOrScalar1.equals ("Column")' selalu salah. GenVectorCode.java 2768
Peringatan
PVS-Studio: Ekspresi V6007 'colOrScalar1.equals ("Scalar")' selalu salah. GenVectorCode.java 2774
Peringatan
PVS-Studio: Ekspresi V6007 'colOrScalar1.equals ("Column")' selalu salah. GenVectorCode.java 2785
String colOrScalar1 = tdesc[4]; .... if (colOrScalar1.equals("Col") && colOrScalar1.equals("Column")) { .... } else if (colOrScalar1.equals("Col") && colOrScalar1.equals("Scalar")) { .... } else if (colOrScalar1.equals("Scalar") && colOrScalar1.equals("Column")) { .... }
Operator jelas bingung di sini dan bukannya '
||' digunakan '
&&' .
Proyek JavaParser
Peringatan
PVS-Studio: V6001 Ada sub-ekspresi 'tokenRange.getBegin (). GetRange (). IsPresent ()' di sebelah kiri dan di sebelah kanan kanan operator '&&'. Node.java 213
public Node setTokenRange(TokenRange tokenRange) { this.tokenRange = tokenRange; if (tokenRange == null || !(tokenRange.getBegin().getRange().isPresent() && tokenRange.getBegin().getRange().isPresent())) { range = null; } else { range = new Range( tokenRange.getBegin().getRange().get().begin, tokenRange.getEnd().getRange().get().end); } return this; }
Penganalisa menemukan bahwa ekspresi yang sama ada di kiri dan kanan operator
&& (sementara semua metode dalam rantai panggilan bersih). Kemungkinan besar, dalam kasus kedua perlu menggunakan
tokenRange.getEnd () , dan bukan
tokenRange.getBegin () .
Peringatan PVS-Studio: V6016 Akses mencurigakan ke elemen objek 'typeDeclaration.getTypeParameters ()' dengan indeks konstan di dalam satu loop. ResolvedReferenceType.java 265 if (!isRawType()) { for (int i=0; i<typeDeclaration.getTypeParams().size(); i++) { typeParametersMap.add( new Pair<>(typeDeclaration.getTypeParams().get(0), typeParametersValues().get(i))); } }
Penganalisa mendeteksi akses mencurigakan ke item koleksi pada indeks konstan di dalam loop. Mungkin ada kesalahan dalam kode ini.Proyek Jenkins
Peringatan PVS-Studio: Ekspresi V6007 'cnt <5' selalu benar. AbstractProject.java 557 public final R getSomeBuildWithWorkspace() { int cnt = 0; for (R b = getLastBuild(); cnt < 5 && b ! = null; b = b.getPreviousBuild()) { FilePath ws = b.getWorkspace(); if (ws != null) return b; } return null; }
Variabel cnt diperkenalkan dalam kode ini untuk membatasi jumlah lintasan menjadi lima, tetapi lupa menambahkannya, sebagai hasilnya cek tersebut tidak berguna.Proyek Spark
Peringatan PVS-Studio: Ekspresi V6007 'sparkApplications! = Null' selalu benar. SparkFilter.java 127 if (StringUtils.isNotBlank(applications)) { final String[] sparkApplications = applications.split(","); if (sparkApplications != null && sparkApplications.length > 0) { ... } }
Memeriksa null dari hasil yang dikembalikan oleh metode split tidak ada artinya, karena metode ini selalu mengembalikan koleksi dan tidak pernah mengembalikan nol .Proyek Sendok
Peringatan PVS-Studio: V6001 Ada sub-ekspresi yang identik '! M.getSimpleName (). Mulai dengan ("set")' di sebelah kiri dan di sebelah kanan operator '&&'. SpoonTestHelpers.java 108 if (!m.getSimpleName().startsWith("set") && !m.getSimpleName().startsWith("set")) { continue; }
Dalam kode ini, ekspresi yang sama ada di kiri dan kanan operator && (semua metode di rantai panggilan bersih). Kemungkinan besar, kode tersebut mengandung kesalahan logis. PeringatanPVS-Studio: Ekspresi V6007 'idxOfScopeBoundTypeParam> = 0' selalu benar. MethodTypingContext.java 243 private boolean isSameMethodFormalTypeParameter(....) { .... int idxOfScopeBoundTypeParam = getIndexOfTypeParam(....); if (idxOfScopeBoundTypeParam >= 0) {
Di sini mereka disegel dalam kondisi kedua dan bukannya idxOfSuperBoundTypeParam menulis idxOfScopeBoundTypeParam .Proyek Keamanan Musim Semi
Peringatan PVS-Studio: V6001 Ada sub-ekspresi yang identik ke kiri dan ke kanan '||' operator. Periksa baris: 38, 39. AnyRequestMatcher.java 38 @Override @SuppressWarnings("deprecation") public boolean equals(Object obj) { return obj instanceof AnyRequestMatcher || obj instanceof security.web.util.matcher.AnyRequestMatcher; }
Operasi ini mirip dengan yang sebelumnya - di sini nama kelas yang sama ditulis dengan cara yang berbeda.Peringatan PVS-Studio: V6006 Objek telah dibuat tetapi tidak digunakan. Kata kunci 'lempar' bisa hilang. DigestAuthenticationFilter.java 434 if (!expectedNonceSignature.equals(nonceTokens[1])) { new BadCredentialsException( DigestAuthenticationFilter.this.messages .getMessage("DigestAuthenticationFilter.nonceCompromised", new Object[] { nonceAsPlainText }, "Nonce token compromised {0}")); }
Dalam kode ini, mereka lupa menambahkan lemparan sebelum pengecualian. Akibatnya, objek pengecualian BadCredentialsException dilempar , tetapi tidak digunakan dengan cara apa pun, mis. pengecualian tidak dibuang.PVS-Studio Warning: V6030 Metode yang terletak di sebelah kanan '|' operator akan dipanggil terlepas dari nilai operan kiri. Mungkin, lebih baik menggunakan '||'. RedirectUrlBuilder.java 38 public void setScheme(String scheme) { if (!("http".equals(scheme) | "https".equals(scheme))) { throw new IllegalArgumentException("..."); } this.scheme = scheme; }
Dalam kode ini, penggunaan | tidak dapat dibenarkan, karena ketika menggunakannya, sisi kanan akan dihitung bahkan jika sisi kiri sudah benar. Dalam hal ini, ini tidak masuk akal secara praktis, oleh karena itu operator | layak diganti dengan || .
Proyek IDEA IntelliJ
PVS-Studio Warning: V6008 Potensi null dereference dari 'editor'. IntroduceVariableBase.java:609 final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(...);
Analyzer mendeteksi bahwa dalam kode ini, dereferensi editor null pointer dapat terjadi . Layak menambahkan cek tambahan.Peringatan PVS-Studio: Ekspresi V6007 selalu salah. RefResolveServiceImpl.java:814 @Override public boolean contains(@NotNull VirtualFile file) { .... return false & !myProjectFileIndex.isUnderSourceRootOfType(....); }
Sulit bagi saya untuk mengatakan apa yang ada dalam pikiran penulis, tetapi kode seperti itu terlihat sangat mencurigakan. Sekalipun tiba-tiba tidak ada kesalahan di sini, saya pikir ada baiknya menulis ulang tempat ini agar tidak membingungkan penganalisa dan programmer lain. PeringatanPVS-Studio: Ekspresi V6007 'hasil [0]' selalu salah. CopyClassesHandler.java:98 final boolean[] result = new boolean[] {false};
Saya curiga bahwa di sini mereka lupa mengubah nilai hasilnya . Karena itu, penganalisa melaporkan bahwa memeriksa jika (hasil [0]) tidak ada gunanya.Kesimpulan
Arah Java sangat fleksibel - itu adalah desktop, dan android, dan web, dan banyak lagi, jadi kami memiliki banyak ruang untuk beraktifitas. Pertama-tama, tentu saja, kami akan mengembangkan bidang-bidang yang paling diminati.Berikut adalah rencana kami untuk waktu dekat:- Anotasi keluaran berdasarkan bytecode;
- Integrasi ke dalam proyek di Ant (orang lain menggunakannya di 2018?);
- Plugin untuk Eclipse (dalam pengembangan);
- Bahkan lebih banyak diagnosa dan anotasi;
- Meningkatkan mekanisme Aliran Data.
Saya juga menyarankan mereka yang ingin berpartisipasi dalam menguji versi alpha dari Java analyzer kami ketika tersedia. Untuk melakukan ini, kirim surat kepada kami untuk mendukung . Kami akan menambahkan kontak Anda ke daftar dan menulis kepada Anda ketika kami menyiapkan versi alfa pertama.
Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Egor Bredikhin. Pengembangan penganalisa statis baru: