PVS-Studio für Java

PVS-Studio für Java

In der siebten Version des statischen Analysators PVS-Studio haben wir Unterstützung für die Java-Sprache hinzugefügt. Es ist Zeit, ein wenig darüber zu sprechen, wie wir angefangen haben, die Java-Sprache zu unterstützen, was wir getan haben und welche Zukunftspläne wir haben. Und natürlich zeigt der Artikel die ersten Tests des Analysators an offenen Projekten.

PVS-Studio


Für Java-Entwickler, die noch nie von dem PVS-Studio-Tool gehört haben, werde ich eine kurze Beschreibung geben.

PVS-Studio ist ein Tool zum Erkennen von Fehlern und potenziellen Schwachstellen im Quellcode von Programmen, die in C, C ++, C # und Java geschrieben wurden. Es läuft unter Windows, Linux und MacOS.

PVS-Studio führt eine statische Code-Analyse durch und generiert einen Bericht, der dem Programmierer hilft, Fehler zu finden und zu beheben. Für diejenigen, die genau wissen möchten, wie PVS-Studio nach Fehlern sucht, empfehle ich den Artikel " Im PVS-Studio Code Analyzer verwendete Technologien zum Auffinden von Fehlern und potenziellen Schwachstellen ".

Starten Sie


Ich könnte mir eine kluge Geschichte einfallen lassen, da wir seit zwei Jahren darüber nachdenken, welche nächste Sprache in PVS-Studio unterstützt werden soll. Die Tatsache, dass Java aufgrund der hohen Beliebtheit dieser Sprache usw. eine vernünftige Wahl ist.

Wie es im Leben passiert, wurde jedoch nicht alles durch eingehende Analyse, sondern durch Experiment entschieden :). Ja, wir haben uns überlegt, in welche Richtung der PVS-Studio-Analysator weiterentwickelt werden soll. Programmiersprachen wie Java, PHP, Python, JavaScript und IBM RPG wurden berücksichtigt. Wir waren zur Java-Sprache geneigt, aber die endgültige Entscheidung wurde noch nicht getroffen. Diejenigen, deren Augen auf ein unbekanntes IBM-Rollenspiel gerichtet sind, verweise ich hier auf diesen Hinweis , aus dem alles klar wird.

Ende 2017 hat Kollege Egor Bredikhin untersucht, welche vorgefertigten Bibliotheken zum Parsen von Code (dh Parser) für neue Richtungen verfügbar sind, die für uns von Interesse sind. Und ich bin auf mehrere Projekte gestoßen, um Java-Code zu analysieren. Basierend auf Spoon gelang es ihm schnell, einen Prototyp-Analysator mit einigen Diagnosen herzustellen. Darüber hinaus wurde klar, dass wir einige Mechanismen des C ++ - Analysators mit Hilfe von SWIG im Java-Analysator verwenden können. Wir haben uns angesehen, was passiert ist, und festgestellt, dass unser nächster Parser für Java sein würde.

Vielen Dank an Egor für sein Engagement und seine aktive Arbeit am Java-Analysator. Wie die Entwicklung weiterging, beschrieb er im Artikel " Entwicklung eines neuen statischen Analysators: PVS-Studio Java ".

Konkurrenten?


Es gibt weltweit viele kostenlose und kommerzielle statische Code-Analysatoren für Java. Es ist nicht sinnvoll, sie alle im Artikel aufzulisten, und ich lasse nur einen Link zu " Liste der Tools für die statische Code-Analyse " (siehe Abschnitt Java und Mehrsprachig).

Ich weiß jedoch, dass wir zunächst nach IntelliJ IDEA, FindBugs und SonarQube (SonarJava) gefragt werden.

IntelliJ IDEE

IntelliJ IDEA verfügt über einen sehr leistungsstarken statischen Code-Analysator. Darüber hinaus entwickelt sich der Analysator weiter und seine Autoren überwachen unsere Aktivitäten genau. Mit IntelliJ IDEA werden wir am schwierigsten sein. Zumindest vorerst werden wir IntelliJ IDEA in Bezug auf Diagnosefunktionen nicht übertreffen können. Deshalb werden wir versuchen, uns auf unsere anderen Vorteile zu konzentrieren.

Die statische Analyse in IntelliJ IDEA ist in erster Linie einer der Chips der Entwicklungsumgebung, die bestimmte Einschränkungen auferlegt. Wir sind frei, was wir mit unserem Analysegerät tun können. Zum Beispiel können wir den Analysator schnell an die spezifischen Bedürfnisse des Kunden anpassen. Schnelle und umfassende Unterstützung ist unser Wettbewerbsvorteil. Unsere Kunden kommunizieren direkt mit Programmierern, die einen bestimmten Teil von PVS-Studio entwickeln.

PVS-Studio bietet viele Möglichkeiten, es in den Entwicklungszyklus großer alter Projekte zu integrieren. Dies ist die Integration mit SonarQube . Dies ist eine massive Unterdrückung von Analysatormeldungen, mit der Sie den Analysator sofort in einem großen Projekt verwenden können, um Fehler nur in neuem oder geändertem Code zu verfolgen. PVS-Studio ist in den kontinuierlichen Integrationsprozess integriert. Ich denke, diese und andere Funktionen werden unserem Analysator helfen, einen Platz unter der Sonne in der Java-Welt zu finden.

Findbugs

Das FindBugs-Projekt wird abgebrochen . Es sollte jedoch daran erinnert werden, dass dies möglicherweise der bekannteste kostenlose statische Analysator für Java-Code ist.

Der Nachfolger von FindBugs ist das SpotBugs- Projekt. Er ist jedoch weniger beliebt, und was mit ihm geschehen wird, ist auch noch nicht ganz klar.

Im Allgemeinen glauben wir, dass FindBugs zwar sehr beliebt war und bleibt und auch ein kostenloser Analysator ist, wir aber nicht darüber nachdenken sollten. Dieses Projekt gehört der Ruhe an.

PS Übrigens kann PVS-Studio jetzt auch kostenlos bei der Arbeit mit offenen Projekten verwendet werden.

SonarQube (SonarJava)

Wir glauben, dass wir nicht mit SonarQube konkurrieren, sondern es ergänzen. PVS-Studio lässt sich in SonarQube integrieren, wodurch Entwickler mehr Fehler und potenzielle Schwachstellen in ihren Projekten finden können. Wie Sie das PVS-Studio-Tool und andere Analysegeräte in SonarQube integrieren, sprechen wir regelmäßig in Meisterkursen, die wir auf verschiedenen Konferenzen abhalten ( Beispiel ).

So starten Sie PVS-Studio für Java


Wir haben den Benutzern die beliebtesten Möglichkeiten zur Integration des Analysators in das Montagesystem zur Verfügung gestellt:

  • Plugin für Maven;
  • Plugin für Gradle;
  • Plugin für IntelliJ IDEA

In der Testphase haben wir viele Benutzer getroffen, die über selbstgeschriebene Montagesysteme verfügen, insbesondere in der mobilen Entwicklung. Sie mochten die Möglichkeit, den Analysator direkt auszuführen und die Quellen und den Klassenpfad aufzulisten.

Detaillierte Informationen zu allen Methoden zum Starten des Analysators finden Sie auf der Dokumentationsseite " So starten Sie PVS-Studio Java ".

Wir konnten die bei Java-Entwicklern so beliebte Plattform zur Qualitätskontrolle von SonarQube- Code nicht ignorieren. Deshalb haben wir unserem SonarQube-Plugin Java-Sprachunterstützung hinzugefügt.

Weitere Pläne


Wir haben viele Ideen, die weiter untersucht werden müssen, aber einige Pläne, die für einen unserer Analysatoren spezifisch sind, sehen folgendermaßen aus:

  • Erstellung neuer Diagnosen und Verfeinerung bestehender Diagnosen;
  • Entwicklung der Datenflussanalyse;
  • Verbesserung der Zuverlässigkeit und Benutzerfreundlichkeit.

Möglicherweise finden wir Zeit, das IntelliJ IDEA-Plugin für CLion anzupassen. Hallo C ++ an Entwickler, die über den Java-Analysator lesen :-)

Beispiele für Fehler in Open Source-Projekten


Ich werde nicht ich sein, wenn ich im Artikel keine Fehler zeige, die mit dem neuen Analysegerät gefunden wurden. Wir könnten ein großes Open-Source-Java-Projekt nehmen und einen klassischen Artikel mit Fehleranalyse schreiben, wie wir es normalerweise tun .

Ich sehe jedoch sofort die Frage voraus, ob wir in Projekten wie IntelliJ IDEA, FindBugs usw. etwas finden können. Deshalb habe ich einfach keinen Ausweg und werde mit diesen Projekten beginnen. Deshalb habe ich mich entschlossen, einige interessante Beispiele für Fehler aus den folgenden Projekten schnell zu überprüfen und aufzuschreiben:

  • IntelliJ IDEA Community Edition . Ich denke, es ist nicht nötig zu erklären, warum dieses Projekt ausgewählt wurde :).
  • SpotBugs Wie ich bereits geschrieben habe, entwickelt sich das FindBugs-Projekt nicht. Schauen Sie sich also das SpotBugs-Projekt an, das der Nachfolger von FindBugs ist. SpotBugs ist ein klassischer statischer Java-Code-Analysator.
  • Einige der Projekte von SonarSource, das Software für die kontinuierliche Kontrolle der Codequalität entwickelt. Schauen Sie sich die Projekte SonarQube und SonarJava an .

Das Schreiben über Fehler in diesen Projekten ist eine schwierige Aufgabe. Tatsache ist, dass diese Projekte von sehr hoher Qualität sind. Eigentlich ist das nicht überraschend. Wie unsere Beobachtungen zeigen, wird der Code von statischen Analysatoren immer gut getestet und mit anderen Tools verifiziert.

Trotz alledem muss ich mit genau diesen Projekten beginnen. Ich werde keine zweite Chance haben, etwas über sie zu schreiben. Ich bin sicher, dass die Entwickler dieser Projekte nach der Veröffentlichung von PVS-Studio für Java PVS-Studio in Betrieb nehmen und damit beginnen werden, es regelmäßig oder zumindest regelmäßig zu überprüfen. Ich weiß zum Beispiel, dass Tagir Valeyev ( lany ), einer der JetBrains-Entwickler, der sich mit dem statischen Codeanalysator IntelliJ IDEA beschäftigt, bereits mit der Beta-Version von PVS-Studio spielt, als ich den Artikel schreibe. Er hat uns bereits 15 Briefe mit Fehlerberichten und Empfehlungen geschrieben. Danke Tagir!

Glücklicherweise muss ich in einem bestimmten Projekt nicht so viele Fehler wie möglich finden. Jetzt ist es meine Aufgabe zu zeigen, dass der PVS-Studio-Analysator für Java nicht umsonst erschien und in der Lage sein wird, die Reihe anderer Tools aufzufüllen, die zur Verbesserung der Codequalität entwickelt wurden. Ich habe nur die Berichte des Analysators durchgesehen und einige Fehler aufgeschrieben, die mir interessant erschienen. Wann immer möglich, habe ich versucht, Fehler verschiedener Art aufzuschreiben. Mal sehen, was passiert ist.

IntelliJ IDEA Integer Division


private static boolean checkSentenceCapitalization(@NotNull String value) { List<String> words = StringUtil.split(value, " "); .... int capitalized = 1; .... return capitalized / words.size() < 0.2; // allow reasonable amount of // capitalized words } 

PVS-Studio Warnung: V6011 [CWE-682] Das Literal '0.2' vom Typ 'double' wird mit einem Wert vom Typ 'int' verglichen. TitleCapitalizationInspection.java 169

Wie beabsichtigt sollte eine Funktion true zurückgeben, wenn weniger als 20% der Wörter mit einem Großbuchstaben beginnen. Tatsächlich funktioniert die Prüfung nicht, da eine Ganzzahldivision auftritt. Durch die Teilung können nur zwei Werte erhalten werden: 0 oder 1.

Die Funktion gibt nur dann einen falschen Wert zurück, wenn alle Wörter mit einem Großbuchstaben beginnen. In allen anderen Fällen erzeugt die Division 0 und die Funktion gibt den wahren Wert zurück.

IntelliJ IDEA Verdächtiger Zyklus


 public int findPreviousIndex(int current) { int count = myPainter.getErrorStripeCount(); int foundIndex = -1; int foundLayer = 0; if (0 <= current && current < count) { current--; for (int index = count - 1; index >= 0; index++) { // <= int layer = getLayer(index); if (layer > foundLayer) { foundIndex = index; foundLayer = layer; } } .... } 

PVS-Studio-Warnung: V6007 [CWE-571] Der Ausdruck 'index> = 0' ist immer wahr. Updater.java 184

Schauen Sie sich zunächst die Bedingung an (0 <= aktuell && aktuell <Anzahl) . Es wird nur ausgeführt, wenn der Wert der Zählvariablen größer als 0 ist.

Schauen Sie sich nun die Schleife an:

 for (int index = count - 1; index >= 0; index++) 

Der Variablenindex wird durch den Ausdruck count - 1 initialisiert. Da die Zählvariable größer als 0 ist, ist der Anfangswert der Indexvariablen immer größer oder gleich 0. Es stellt sich heraus, dass die Schleife ausgeführt wird, bis die Indexvariable überläuft.

Dies ist höchstwahrscheinlich nur ein Tippfehler und das Inkrement darf nicht ausgeführt werden, sondern das Dekrement der Variablen:

 for (int index = count - 1; index >= 0; index--) 

IntelliJ IDEA, Kopieren-Einfügen


 @NonNls public static final String BEFORE_STR_OLD = "before:"; @NonNls public static final String AFTER_STR_OLD = "after:"; private static boolean isBeforeOrAfterKeyword(String str, boolean trimKeyword) { return (trimKeyword ? LoadingOrder.BEFORE_STR.trim() : LoadingOrder.BEFORE_STR).equalsIgnoreCase(str) || (trimKeyword ? LoadingOrder.AFTER_STR.trim() : LoadingOrder.AFTER_STR).equalsIgnoreCase(str) || LoadingOrder.BEFORE_STR_OLD.equalsIgnoreCase(str) || // <= LoadingOrder.BEFORE_STR_OLD.equalsIgnoreCase(str); // <= } 

PVS-Studio Warnung: V6001 [CWE-570] Links und rechts vom '||' befinden sich identische Unterausdrücke 'LoadingOrder.BEFORE_STR_OLD.equalsIgnoreCase (str)'. Betreiber. Überprüfen Sie die Zeilen: 127, 128. ExtensionOrderConverter.java 127

Guter alter Effekt der letzten Zeile . Der Programmierer beeilte sich und vergaß, nachdem er eine Codezeile multipliziert hatte, sie zu reparieren. Infolgedessen wird die doppelte Zeichenfolge str mit BEFORE_STR_OLD verglichen. Wahrscheinlich sollte einer der Vergleiche mit AFTER_STR_OLD erfolgen .

Tippfehler von IntelliJ IDEA


 public synchronized boolean isIdentifier(@NotNull String name, final Project project) { if (!StringUtil.startsWithChar(name,'\'') && !StringUtil.startsWithChar(name,'\"')) { name = "\"" + name; } if (!StringUtil.endsWithChar(name,'"') && !StringUtil.endsWithChar(name,'\"')) { name += "\""; } .... } 

PVS-Studio Warnung: V6001 [CWE-571] Links und rechts vom Operator '&&' befinden sich identische Unterausdrücke '! StringUtil.endsWithChar (Name,' "'). JsonNamesValidator.java 27

Dieser Code überprüft, ob der Name in einfachen oder doppelten Anführungszeichen steht. Ist dies nicht der Fall, werden doppelte Anführungszeichen automatisch hinzugefügt.

Aufgrund eines Tippfehlers wird das Ende des Namens nur auf doppelte Anführungszeichen überprüft. Infolgedessen wird der Name in einfachen Anführungszeichen nicht korrekt verarbeitet.

Vorname

 'Abcd' 

Aufgrund der Hinzufügung zusätzlicher doppelter Anführungszeichen wird daraus Folgendes:

 'Abcd'" 

IntelliJ IDEA, falscher Array-Überlaufschutz


 static Context parse(....) { .... for (int i = offset; i < endOffset; i++) { char c = text.charAt(i); if (c == '<' && i < endOffset && text.charAt(i + 1) == '/' && startTag != null && CharArrayUtil.regionMatches(text, i + 2, endOffset, startTag)) { endTagStartOffset = i; break; } } .... } 

PVS-Studio-Warnung: V6007 [CWE-571] Der Ausdruck 'i <endOffset' ist immer wahr. EnterAfterJavadocTagHandler.java 183

Der Unterausdruck i <endOffset im Zustand der if-Anweisung ist nicht sinnvoll. Die Variable i ist immer kleiner als endOffset , wie aus der Bedingung für die Ausführung der Schleife hervorgeht.

Höchstwahrscheinlich wollte sich der Programmierer beim Aufrufen von Funktionen davor schützen, aus der Reihe zu geraten:

  • text.charAt (i + 1)
  • CharArrayUtil.regionMatches (Text, i + 2, endOffset, startTag)

In diesem Fall sollte der Unterausdruck zum Überprüfen des Index wie folgt lauten : i <endOffset - 2 .

IntelliJ IDEA Repeat Check


 public static String generateWarningMessage(....) { .... if (buffer.length() > 0) { if (buffer.length() > 0) { buffer.append(" ").append( IdeBundle.message("prompt.delete.and")).append(" "); } } .... } 

PVS-Studio-Warnung: V6007 [CWE-571] Der Ausdruck 'buffer.length ()> 0' ist immer wahr. DeleteUtil.java 62

Dies kann entweder ein harmloser redundanter Code oder ein schwerwiegender Fehler sein.

Wenn beispielsweise während des Refactorings zufällig eine doppelte Prüfung aufgetreten ist, ist daran nichts auszusetzen. Die zweite Prüfung kann einfach gelöscht werden.

Ein anderes Szenario ist jedoch möglich. Die zweite Überprüfung sollte völlig anders sein und der Code verhält sich nicht wie beabsichtigt. Dann ist das ein echter Fehler.

Hinweis Übrigens gibt es viele verschiedene redundante Prüfungen. Darüber hinaus wird oft gesehen, dass dies kein Fehler ist. Analysatormeldungen können jedoch auch nicht als falsch positiv bezeichnet werden. Zur Verdeutlichung hier ein Beispiel, das ebenfalls aus IntelliJ IDEA stammt:

 private static boolean isMultiline(PsiElement element) { String text = element.getText(); return text.contains("\n") || text.contains("\r") || text.contains("\r\n"); } 

Der Analysator gibt an, dass die Funktion text.contains ("\ r \ n") immer false zurückgibt. Wenn das Symbol "\ n" und "\ r" nicht gefunden wird, macht es keinen Sinn, nach "\ r \ n" zu suchen. Dies ist kein Fehler, und der Code ist nur deshalb schlecht, weil er etwas langsamer arbeitet und eine bedeutungslose Suche nach einem Teilstring durchführt.

Wie mit einem solchen Code umzugehen ist, liegt in jedem Fall bei den Programmierern. Beim Schreiben von Artikeln achte ich in der Regel einfach nicht auf einen solchen Code.

IntelliJ IDEA, etwas stimmt nicht


 public boolean satisfiedBy(@NotNull PsiElement element) { .... @NonNls final String text = expression.getText().replaceAll("_", ""); if (text == null || text.length() < 2) { return false; } if ("0".equals(text) || "0L".equals(text) || "0l".equals(text)) { return false; } return text.charAt(0) == '0'; } 

PVS-Studio-Warnung: V6007 [CWE-570] Der Ausdruck '"0" .equals (text)' ist immer falsch. ConvertIntegerToDecimalPredicate.java 46

Dieser Code enthält definitiv einen logischen Fehler. Es fällt mir jedoch schwer zu sagen, was der Programmierer überprüfen wollte und wie der Fehler behoben werden kann. Daher werde ich hier nur auf eine bedeutungslose Prüfung hinweisen.

Zu Beginn wird geprüft, ob die Zeichenfolge mindestens zwei Zeichen enthalten muss. Ist dies nicht der Fall, gibt die Funktion false zurück .

Das Folgende ist eine "0" .equals (Text) Prüfung. Dies ist bedeutungslos, da eine Zeichenfolge nicht nur ein Zeichen enthalten kann.

Im Allgemeinen stimmt hier etwas nicht und der Code sollte korrigiert werden.

SpotBugs (Nachfolger von FindBugs), Iterationslimitfehler


 public static String getXMLType(@WillNotClose InputStream in) throws IOException { .... String s; int count = 0; while (count < 4) { s = r.readLine(); if (s == null) { break; } Matcher m = tag.matcher(s); if (m.find()) { return m.group(1); } } throw new IOException("Didn't find xml tag"); .... } 

PVS-Studio-Warnung: V6007 [CWE-571] Der Ausdruck 'count <4' ist immer wahr. Util.java 394

Wie geplant sollte die Suche nach dem XML-Tag nur in den ersten vier Zeilen der Datei durchgeführt werden. Aufgrund der Tatsache, dass sie vergessen haben, die Anzahl der Variablen zu erhöhen , wird die gesamte Datei gelesen.

Erstens kann sich dies als sehr langsamer Vorgang herausstellen, und zweitens kann irgendwo in der Mitte der Datei etwas gefunden werden, das als XML-Tag interpretiert wird, aber nicht.

SpotBugs (Nachfolger von FindBugs), Werte überschreiben


 private void reportBug() { int priority = LOW_PRIORITY; String pattern = "NS_NON_SHORT_CIRCUIT"; if (sawDangerOld) { if (sawNullTestVeryOld) { priority = HIGH_PRIORITY; // <= } if (sawMethodCallOld || sawNumericTestVeryOld && sawArrayDangerOld) { priority = HIGH_PRIORITY; // <= pattern = "NS_DANGEROUS_NON_SHORT_CIRCUIT"; } else { priority = NORMAL_PRIORITY; // <= } } bugAccumulator.accumulateBug( new BugInstance(this, pattern, priority).addClassAndMethod(this), this); } 

PVS-Studio Warnung: V6021 [CWE-563] Der Wert wird der Variablen 'Priorität' zugewiesen, aber nicht verwendet. FindNonShortCircuit.java 197

Der Wert der Prioritätsvariablen wird abhängig vom Wert der Variablen sawNullTestVeryOld festgelegt . Dies spielt jedoch keine Rolle. Ferner wird der Prioritätsvariablen in jedem Fall ein anderer Wert zugewiesen. Ein offensichtlicher Fehler in der Logik der Funktion.

SonarQube, Kopieren-Einfügen


 public class RuleDto { .... private final RuleDefinitionDto definition; private final RuleMetadataDto metadata; .... private void setUpdatedAtFromDefinition(@Nullable Long updatedAt) { if (updatedAt != null && updatedAt > definition.getUpdatedAt()) { setUpdatedAt(updatedAt); } } private void setUpdatedAtFromMetadata(@Nullable Long updatedAt) { if (updatedAt != null && updatedAt > definition.getUpdatedAt()) { setUpdatedAt(updatedAt); } } .... } 

PVS-Studio: V6032 Es ist merkwürdig, dass der Hauptteil der Methode 'setUpdatedAtFromDefinition' dem Hauptteil einer anderen Methode 'setUpdatedAtFromMetadata' vollständig entspricht. Überprüfen Sie die Zeilen: 396, 405. RuleDto.java 396

Die setUpdatedAtFromMetadata- Methode verwendet das Definitionsfeld . Höchstwahrscheinlich sollte das Metadatenfeld verwendet werden. Dies ist den Konsequenzen des fehlgeschlagenen Kopierens und Einfügens sehr ähnlich.

SonarJava, Duplikate bei der Karteninitialisierung


 private final Map<JavaPunctuator, Tree.Kind> assignmentOperators = Maps.newEnumMap(JavaPunctuator.class); public KindMaps() { .... assignmentOperators.put(JavaPunctuator.PLUSEQU, Tree.Kind.PLUS_ASSIGNMENT); .... assignmentOperators.put(JavaPunctuator.PLUSEQU, Tree.Kind.PLUS_ASSIGNMENT); .... } 

PVS-Studio Warnung: V6033 [CWE-462] Ein Element mit demselben Schlüssel 'JavaPunctuator.PLUSEQU' wurde bereits hinzugefügt. Überprüfen Sie die Zeilen: 104, 100. KindMaps.java 104

Das gleiche Schlüssel-Wert-Paar wird zweimal auf die Karte gelegt. Höchstwahrscheinlich stellte sich heraus, dass dies unaufmerksam war, und tatsächlich gibt es keinen wirklichen Fehler. In jedem Fall muss dieser Code jedoch überprüft werden, da Sie möglicherweise vergessen haben, ein anderes Paar hinzuzufügen.

Fazit


Aber welche Schlussfolgerung kann es geben ?! Ich lade alle unverzüglich ein, PVS-Studio herunterzuladen und zu versuchen, Ihre Arbeitsprojekte in Java zu testen! Laden Sie PVS-Studio herunter .

Vielen Dank für Ihre Aufmerksamkeit. Ich hoffe, dass wir die Leser bald mit einer Reihe von Artikeln begeistern werden, die sich mit der Überprüfung verschiedener offener Java-Projekte befassen.



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov. PVS-Studio für Java .

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


All Articles