PVS-Studio geht in die Cloud: Azure DevOps

Bild 9

Dies ist der zweite Artikel über die Verwendung des statischen Analysators PVS-Studio in Cloud-CI-Systemen. Dieses Mal wird die Azure DevOps-Plattform betrachtet - eine Cloud-CI \ CD-Lösung von Microsoft. Betrachten Sie diesmal ShareX als analysiertes Projekt.

Wir werden drei Komponenten benötigen. Der erste ist der statische Analysator PVS-Studio. Das zweite ist Azure DevOps, in das wir den Analysator integrieren werden. Das dritte Projekt wird überprüft, um die Funktionen von PVS-Studio bei der Arbeit in der Cloud zu demonstrieren. Also fangen wir an.

PVS-Studio ist ein statischer Code-Analysator zur Suche nach Fehlern und Sicherheitsmängeln. Führt eine Code-Analyse in C, C ++, C # und Java durch.

Azure DevOps . Die Azure DevOps-Plattform enthält Tools wie Azure Pipeline, Azure Board, Azure Artifacts und andere, um das Erstellen von Software zu beschleunigen und deren Qualität zu verbessern.

ShareX ist eine kostenlose Anwendung, mit der Sie jeden Teil des Bildschirms erfassen und aufzeichnen können. Das Projekt ist in C # geschrieben und eignet sich hervorragend zur Demonstration der Ausführung des statischen Analysators. Der Quellcode für das Projekt ist auf GitHub verfügbar .

Die Ausgabe des Befehls cloc für das ShareX-Projekt:
Sprache
Dateien
leer
Kommentar
Code
C #
696
20658
24423
102565
MSBuild-Skript
11
1
77
5859
Mit anderen Worten, das Projekt ist klein, aber völlig ausreichend, um die Arbeit von PVS-Studio in Verbindung mit einer Cloud-Plattform zu demonstrieren.

Lass uns einrichten


Um in Azure DevOps zu beginnen, klicken Sie auf den Link und dann auf die Schaltfläche "Kostenlos mit GitHub starten".

Bild 2

Gewähren der Microsoft-Anwendung Zugriff auf die Daten des GitHub-Kontos.

Bild 1

Um die Registrierung abzuschließen, muss ein Microsoft-Konto erstellt werden.

Bild 12

Erstellen Sie nach der Registrierung ein Projekt:

Bild 5

Als nächstes müssen wir zum Abschnitt "Pipelines" - "Builds" gehen und eine neue Build-Pipeline erstellen

Bild 8

Auf die Frage, wo sich unser Code befindet, werden wir antworten - GitHub.

Bild 13

Wir autorisieren die Azure Pipelines-Anwendung und wählen das Repository mit dem Projekt aus, für das wir den Start des statischen Analysators konfigurieren möchten

Bild 7

Wählen Sie im Vorlagenauswahlfenster "Starter-Pipeline".

Bild 17

Wir können eine statische Analyse des Projektcodes auf zwei Arten durchführen: mithilfe von Microsoft-gehosteten oder selbst gehosteten Agenten.

In der ersten Version werden von Microsoft gehostete Agenten verwendet. Solche Agenten sind normale virtuelle Maschinen, die beim Starten unserer Pipeline gestartet und nach dem Ende der Aufgabe gelöscht werden. Durch die Verwendung solcher Agenten können Sie keine Zeit damit verschwenden, sie zu unterstützen und zu aktualisieren. Sie unterliegen jedoch einigen Einschränkungen, z. B. der Unmöglichkeit, zusätzliche Software zu installieren, die zum Erstellen des Projekts verwendet wird.

Ersetzen Sie unsere Standardkonfiguration durch die folgende für die Verwendung von von Microsoft gehosteten Agenten:

#    #      master- trigger: - master #         # ,   Docker-, #      Windows Server 1803 pool: vmImage: 'win1803' container: microsoft/dotnet-framework:4.7.2-sdk-windowsservercore-1803 steps: #    - task: PowerShell@2 inputs: targetType: 'inline' script: 'Invoke-WebRequest -Uri https://files.viva64.com/PVS-Studio_setup.exe -OutFile PVS-Studio_setup.exe' - task: CmdLine@2 inputs: workingDirectory: $(System.DefaultWorkingDirectory) script: | #      nuget restore .\ShareX.sln #  ,        md .\PVSTestResults #   PVS-Studio_setup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /COMPONENTS=Core #        "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials -u $(PVS_USERNAME) -n $(PVS_KEY) #        html. "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" -t .\ShareX.sln -o .\PVSTestResults\ShareX.plog "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" -t html -o .\PVSTestResults\ .\PVSTestResults\ShareX.plog #    - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults 

Hinweis: Gemäß der Dokumentation muss der verwendete Container im Image der virtuellen Maschine zwischengespeichert werden. Zum Zeitpunkt des Schreibens funktioniert dieser Artikel jedoch nicht und der Container wird bei jedem Start der Aufgabe heruntergeladen, was sich negativ auf die Ausführungszeit auswirkt.

Speichern Sie die Pipeline und erstellen Sie die Variablen, die zum Erstellen der Lizenzdatei verwendet werden. Öffnen Sie dazu das Pipeline-Bearbeitungsfenster und klicken Sie in der oberen rechten Ecke auf die Schaltfläche „Variablen“.

Bild 14

Fügen Sie zwei Variablen hinzu - PVS_USERNAME und PVS_KEY , die den Benutzernamen bzw. den Lizenzschlüssel enthalten. Vergessen Sie beim Erstellen der Variablen PVS_KEY nicht, das Element "Diesen Wert geheim halten" zu aktivieren , um den Variablenwert mit einem 2048-Bit-RSA-Schlüssel zu verschlüsseln und die Ausgabe des Variablenwerts an das Taskausführungsprotokoll zu unterdrücken.

Bild 15

Wir speichern die Variablen und starten die Pipeline mit der Schaltfläche "Ausführen".

Die zweite Option zum Ausführen der Analyse ist die Verwendung eines selbst gehosteten Agenten. Selbst gehostete Agenten sind Agenten, die wir selbst konfigurieren und verwalten. Solche Agenten bieten mehr Möglichkeiten für die Installation von Software, die für die Montage und das Testen unseres Softwareprodukts erforderlich ist.

Vor der Verwendung solcher Agenten müssen sie gemäß den Anweisungen konfiguriert und ein statischer Analysator installiert und konfiguriert werden .

Um die Aufgabe auf einem selbst gehosteten Agenten zu starten, ersetzen wir die vorgeschlagene Standardkonfiguration durch die folgende:

 #    #    master- trigger: - master #    self-hosted    'MyPool' pool: 'MyPool' steps: - task: CmdLine@2 inputs: workingDirectory: $(System.DefaultWorkingDirectory) script: | #      nuget restore .\ShareX.sln #  ,        md .\PVSTestResults #        html. "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" -t .\ShareX.sln -o .\PVSTestResults\ShareX.plog "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" -t html -o .\PVSTestResults\ .\PVSTestResults\ShareX.plog #    - task: PublishBuildArtifacts@1 inputs: pathToPublish: PVSTestResults artifactName: PVSTestResults 

Nach Abschluss der Aufgabe kann das Archiv mit den Analyseberichten auf der Registerkarte "Zusammenfassung" heruntergeladen werden, oder wir können die Erweiterung "E-Mail senden" verwenden , mit der Sie das Senden von E-Mails konfigurieren oder auf dem Marktplatz nach einem für uns bequemeren Tool suchen können.

Bild 21

Über Analyseergebnisse


Schauen wir uns nun einige der Fehler an, die im überprüften Projekt gefunden wurden - ShareX.

Redundante Schecks

Beginnen wir zum Aufwärmen mit einfachen Fehlern im Code, nämlich mit redundanten Überprüfungen:

 private void PbThumbnail_MouseMove(object sender, MouseEventArgs e) { .... IDataObject dataObject = new DataObject(DataFormats.FileDrop, new string[] { Task.Info.FilePath }); if (dataObject != null) { Program.MainForm.AllowDrop = false; dragBoxFromMouseDown = Rectangle.Empty; pbThumbnail.DoDragDrop(dataObject, DragDropEffects.Copy | DragDropEffects.Move); Program.MainForm.AllowDrop = true; } .... } 

PVS-Studio- Warnung : V3022 [CWE-571] Der Ausdruck 'dataObject! = Null' ist immer wahr. TaskThumbnailPanel.cs 415

Achten Sie darauf, die Variable dataObject auf null zu überprüfen. Wofür ist sie hier? dataObject kann in diesem Fall einfach nicht null sein , da es mit einem Verweis auf das erstellte Objekt initialisiert wird. Infolgedessen haben wir eine redundante Überprüfung. Ist es kritisch? Nein. Sieht prägnant aus? Nein. Diese Prüfung wird deutlich besser entfernt, um den Code nicht zu überladen.

Werfen wir einen Blick auf einen anderen Code, zu dem Sie ähnliche Kommentare abgeben können:

 private static Image GetDIBImage(MemoryStream ms) { .... try { .... return new Bitmap(bmp); .... } finally { if (gcHandle != IntPtr.Zero) { GCHandle.FromIntPtr(gcHandle).Free(); } } .... } private static Image GetImageAlternative() { .... using (MemoryStream ms = dataObject.GetData(format) as MemoryStream) { if (ms != null) { try { Image img = GetDIBImage(ms); if (img != null) { return img; } } catch (Exception e) { DebugHelper.WriteException(e); } } } .... } 

PVS-Studio Warnung : V3022 [CWE-571] Der Ausdruck 'img! = Null' ist immer wahr. ClipboardHelpers.cs 289

Die GetImageAlternative- Methode überprüft erneut, ob die Variable img unmittelbar nach dem Erstellen einer neuen Instanz der Bitmap- Klasse nicht null ist . Der Unterschied zum vorherigen Beispiel besteht darin, dass zum Initialisieren der Variablen img nicht der Konstruktor, sondern die GetDIBImage- Methode verwendet wird. Der Autor des Codes geht davon aus, dass bei dieser Methode eine Ausnahme auftreten kann, deklariert jedoch nur try und blockiert schließlich , wobei catch weggelassen wird. Wenn eine Ausnahme auftritt, erhält die aufrufende Methode - GetImageAlternative - daher keinen Verweis auf ein Objekt vom Typ Bitmap, sondern muss die Ausnahme in einem eigenen catch-Block behandeln . In diesem Fall wird die Variable img nicht initialisiert und der Ausführungsthread erreicht nicht einmal die Prüfung img! = Null , sondern fällt sofort in den catch-Block . Daher zeigte der Analysator eine redundante Validierung an.

Betrachten Sie das folgende Warnbeispiel mit Code V3022 :

 private void btnCopyLink_Click(object sender, EventArgs e) { .... if (lvClipboardFormats.SelectedItems.Count == 0) { url = lvClipboardFormats.Items[0].SubItems[1].Text; } else if (lvClipboardFormats.SelectedItems.Count > 0) { url = lvClipboardFormats.SelectedItems[0].SubItems[1].Text; } .... } 

PVS-Studio Warnung : V3022 [CWE-571] Der Ausdruck 'lvClipboardFormats.SelectedItems.Count> 0' ist immer wahr. AfterUploadForm.cs 155

Schauen wir uns den zweiten bedingten Ausdruck an. Dort überprüfen wir den Wert der schreibgeschützten Count- Eigenschaft. Diese Eigenschaft zeigt die Anzahl der Elemente in einer Instanz der SelectedItems- Auflistung an. Die Bedingung ist nur erfüllt, wenn die Count- Eigenschaft größer als Null ist. Alles wäre in Ordnung, aber nur in der externen if-Anweisung ist der Count bereits überprüft. Eine Instanz der SelectedItems- Auflistung kann nicht die Anzahl der Elemente kleiner als Null haben. Daher nimmt Count einen Wert an, der entweder gleich Null oder größer als Null ist. Da wir bereits in der ersten if-Anweisung überprüft haben, dass Count Null ist und sich als falsch herausgestellt hat, ist es nicht sinnvoll, eine weitere Prüfung für den else-Zweig zu schreiben, bei dem Count größer als Null ist.

Das letzte Beispiel für die Fehlernummer V3022 ist das folgende Codefragment:

 private void DrawCursorGraphics(Graphics g) { .... int cursorOffsetX = 10, cursorOffsetY = 10, itemGap = 10, itemCount = 0; Size totalSize = Size.Empty; int magnifierPosition = 0; Bitmap magnifier = null; if (Options.ShowMagnifier) { if (itemCount > 0) totalSize.Height += itemGap; .... } .... } 

PVS-Studio Warnung : V3022 Der Ausdruck 'itemCount> 0' ist immer falsch. RegionCaptureForm.cs 1100.

Der Analysator hat festgestellt, dass die Bedingung itemCount> 0 immer falsch ist, da eine etwas höhere Deklaration durchgeführt wird und gleichzeitig die Variable itemCount auf Null gesetzt wird. Bis zur Bedingung wird diese Variable nirgendwo verwendet und ändert sich nicht. Daher hat der Analysator die richtige Schlussfolgerung über den bedingten Ausdruck gezogen, dessen Wert immer falsch ist.

Schauen wir uns jetzt etwas wirklich Interessantes an.

Der beste Weg, um den Fehler zu verstehen, besteht darin, den Fehler zu visualisieren.

Es scheint uns, dass an dieser Stelle ein ziemlich interessanter Fehler gefunden wurde:

 public static void Pixelate(Bitmap bmp, int pixelSize) { .... float r = 0, g = 0, b = 0, a = 0; float weightedCount = 0; for (int y2 = y; y2 < yLimit; y2++) { for (int x2 = x; x2 < xLimit; x2++) { ColorBgra color = unsafeBitmap.GetPixel(x2, y2); float pixelWeight = color.Alpha / 255; r += color.Red * pixelWeight; g += color.Green * pixelWeight; b += color.Blue * pixelWeight; a += color.Alpha * pixelWeight; weightedCount += pixelWeight; } } .... ColorBgra averageColor = new ColorBgra((byte)(b / weightedCount), (byte)(g / weightedCount), (byte)(r / weightedCount), (byte)(a / pixelCount)); .... } 

Ich möchte nicht sofort alle Karten aufdecken und zeigen, was unser Analysator hier gefunden hat. Verschieben wir diesen Moment also für eine kurze Zeit.

Anhand des Namens der Methode lässt sich leicht erraten, was sie bewirkt: Sie senden ihr ein Bild oder ein Fragment des Bildes als Eingabe und es führt seine Pixelierung durch. Der Methodencode ist ziemlich lang, daher geben wir ihn hier nicht vollständig an, sondern versuchen einfach, seinen Algorithmus zu erklären und zu erklären, welche Art von Fehler PVS-Studio hier gefunden hat.

Diese Methode akzeptiert zwei Parameter als Eingabe: ein Objekt vom Typ Bitmap und einen Wert vom Typ int , der die Größe der Pixel angibt. Der Operationsalgorithmus ist recht einfach:

1) Wir zerlegen das Fragment des am Eingang empfangenen Bildes in Quadrate mit einer Seite, die der Größe der Pixelierung entspricht. Wenn wir beispielsweise eine Pixelgröße von 15 haben, erhalten wir ein Quadrat mit 15 x 15 = 225 Pixel.

2) Als nächstes gehen wir um jedes Pixel in diesem Quadrat herum und akkumulieren die Werte der Felder Rot , Grün , Blau und Alpha in Zwischenvariablen und multiplizieren zuvor den Wert der entsprechenden Farbe und den Alphakanalwert mit der Variablen pixelWeight , die durch Teilen des Alpha- Werts durch 255 erhalten wird (die Alpha- Variable hat Typ Byte ). Außerdem durchlaufen wir beim Durchlaufen von Pixeln die in pixelWeight aufgezeichneten Werte zu einer Variablen namens weightedCount .

Das Code-Snippet, das die obigen Schritte ausführt, lautet wie folgt:

 ColorBgra color = unsafeBitmap.GetPixel(x2, y2); float pixelWeight = color.Alpha / 255; r += color.Red * pixelWeight; g += color.Green * pixelWeight; b += color.Blue * pixelWeight; a += color.Alpha * pixelWeight; weightedCount += pixelWeight; 

Beachten Sie übrigens, dass pixelWeight der Variablen weightedCount für dieses Pixel keinen Wert hinzufügt, wenn der Wert der Alpha- Variablen Null ist. Wir werden das in Zukunft brauchen.

3) Nachdem wir alle Pixel im aktuellen Quadrat umgangen haben, können wir die allgemeine „durchschnittliche“ Farbe für dieses Quadrat ermitteln. Der Code, der diese Aktionen ausführt, lautet wie folgt:

 ColorBgra averageColor = new ColorBgra((byte)(b / weightedCount), (byte)(g / weightedCount), (byte)(r / weightedCount), (byte)(a / pixelCount)); 

4) Nachdem wir die endgültige Farbe haben und sie in die Variable durchschnittlicher Farbe schreiben , können wir jedes Pixel im Quadrat erneut umgehen und ihm einen Wert aus der durchschnittlichen Farbe zuweisen .

5) Wir kehren zu Schritt 2 zurück, solange noch rohe Quadrate vorhanden sind.

Wir stellen erneut fest, dass die Variable weightedCount nicht der Anzahl aller quadratischen Pixel entspricht. Wenn beispielsweise ein absolut transparentes Pixel im Bild auftritt (der Wert im Alphakanal ist Null), ist die Variable pixelWeight für dieses Pixel Null ( 0/255 = 0). Daher trägt dieses Pixel nicht zur Bildung des Werts der Variablen weightedCount bei . Dies ist logisch - es macht keinen Sinn, die Farben eines absolut transparenten Pixels zu berücksichtigen.

Alles scheint ziemlich vernünftig - Pixelierung sollte korrekt funktionieren. Und es funktioniert wirklich richtig. Dies gilt nur nicht für PNG-Bilder mit Pixeln mit Werten im Alphakanal von weniger als 255 und ungleich Null. Achten Sie auf das pixelige Bild unten:

Bild 3


Hast du Pixelierung gesehen? Und wir sind nicht. Nun wollen wir diese kleine Intrige enthüllen und erklären, wo genau der Fehler in dieser Methode versteckt ist. Der Fehler hat sich in die Zeile zur Berechnung des Werts der pixelWeight- Variablen eingeschlichen:

 float pixelWeight = color.Alpha / 255; 

Tatsache ist, dass der Autor des Codes, der die Variable pixelWeight als float deklarierte, implizierte, dass beim Teilen des Alpha- Feldes durch 255 zusätzlich zu Null und Eins Bruchzahlen erhalten werden sollten. Hier liegt das Problem, da die Alpha- Variable vom Typ Byte ist und wenn wir sie durch 255 teilen, erhalten wir einen ganzzahligen Wert, und nur dann wird er implizit in float umgewandelt , sodass der Bruchteil verloren geht.

Die Unfähigkeit, PNG-Bilder mit einem gewissen Grad an Transparenz zu pixelisieren, ist leicht zu erklären. Da die Alpha-Kanalwerte für diese Pixel im Bereich 0 <Alpha <255 liegen, erhalten wir beim Teilen der Alpha- Variablen durch 255 immer 0. Daher sind auch die Werte der Variablen pixelWeight , r , g , b , a und weightedCount immer immer wird Null sein. Infolgedessen hat unsere durchschnittliche Farbdurchschnittsfarbe über alle Kanäle hinweg Nullwerte: Rot - 0, Blau - 0, Grün - 0, Alpha - 0. Wenn Sie das Quadrat mit dieser Farbe füllen, ändern wir die ursprüngliche Farbe der Pixel nicht, da die Durchschnittsfarbe absolut transparent ist . Um diesen Fehler zu beheben, müssen Sie nur das Alpha- Feld explizit in den Float- Typ umwandeln . Die korrigierte Codezeile kann folgendermaßen aussehen:

 float pixelWeight = (float)color.Alpha / 255; 

Und es ist Zeit, die Nachricht zu zitieren, die PVS-Studio an den falschen Code ausgegeben hat:

PVS-Studio Warnung : V3041 [CWE-682] Der Ausdruck wurde implizit vom Typ 'int' in den Typ 'float' umgewandelt. Erwägen Sie die Verwendung eines expliziten Typgusses, um den Verlust eines Bruchteils zu vermeiden. Ein Beispiel: double A = (double) (X) / Y; ImageHelpers.cs 1119.

Zum Vergleich geben wir einen Screenshot eines wirklich pixeligen Bildes, das mit einer festen Version der Anwendung erhalten wurde:

Bild 6

Mögliche NullReferenceException

 public static bool AddMetadata(Image img, int id, string text) { .... pi.Value = bytesText; if (pi != null) { img.SetPropertyItem(pi); return true; } .... } 

PVS-Studio Warnung: V3095 [CWE-476] Das ' pi' -Objekt wurde verwendet, bevor es gegen null verifiziert wurde. Überprüfen Sie die Zeilen: 801, 803. ImageHelpers.cs 801

Dieses Codefragment zeigt, dass der Autor erwartet hat, dass die Variable pi null ist, weshalb die Prüfung pi! = Null durchgeführt wird, bevor die SetPropertyItem- Methode aufgerufen wird. Es ist seltsam, dass vor dieser Überprüfung der Eigenschaft pi.Value ein Byte-Array zugewiesen wird, da bei pi null eine Ausnahme vom Typ NullReferenceException ausgelöst wird.

Eine ähnliche Situation wurde anderswo gesehen:

 private static void Task_TaskCompleted(WorkerTask task) { .... task.KeepImage = false; if (task != null) { if (task.RequestSettingUpdate) { Program.MainForm.UpdateCheckStates(); } .... } .... } 

PVS-Studio Warnung: V3095 [CWE-476] Das Objekt 'task' wurde verwendet, bevor es gegen null verifiziert wurde. Überprüfen Sie die Zeilen: 268, 270. TaskManager.cs 268

PVS-Studio hat einen weiteren ähnlichen Fehler gefunden. Die Bedeutung ist immer noch dieselbe, daher besteht keine große Notwendigkeit, ein Codefragment anzugeben. Wir beschränken uns auf die Analysator-Nachricht.

PVS-Studio Warnung: V3095 [CWE-476] Das Objekt 'Config.PhotobucketAccountInfo' wurde verwendet, bevor es gegen Null verifiziert wurde. Überprüfen Sie die Zeilen: 216, 219. UploadersConfigForm.cs 216

Der gleiche Rückgabewert

In der EvalWindows- Methode der WindowsList- Klasse wurde ein verdächtiger Code entdeckt, der unter keinen Umständen true zurückgibt:

 public class WindowsList { public List<IntPtr> IgnoreWindows { get; set; } .... public WindowsList() { IgnoreWindows = new List<IntPtr>(); } public WindowsList(IntPtr ignoreWindow) : this() { IgnoreWindows.Add(ignoreWindow); } .... private bool EvalWindows(IntPtr hWnd, IntPtr lParam) { if (IgnoreWindows.Any(window => hWnd == window)) { return true; // <= } windows.Add(new WindowInfo(hWnd)); return true; // <= } } 

PVS-Studio Warnung: V3009 Es ist seltsam, dass diese Methode immer ein und denselben Wert von 'true' zurückgibt. WindowsList.cs 82

Es erscheint logisch, dass die Methode false zurückgeben sollte, wenn in der Liste ein Zeiger mit demselben Wert wie hWnd mit dem Namen IgnoreWindows gefunden wurde.

Die IgnoreWindows- Liste kann entweder durch Aufrufen des WindowsList- Konstruktors (IntPtr ignoreWindow) oder direkt durch Zugriff auf die Eigenschaft ausgefüllt werden , da sie öffentlich ist. Auf die eine oder andere Weise wird diese Liste laut Visual Studio derzeit im Code in keiner Weise ausgefüllt. Dies ist ein weiterer seltsamer Ort dieser Methode.

Unsicherer Aufruf von Ereignishandlern

 protected void OnNewsLoaded() { if (NewsLoaded != null) { NewsLoaded(this, EventArgs.Empty); } } 

PVS-Studio Warnung: V3083 [CWE-367] Unsicherer Aufruf des Ereignisses 'NewsLoaded', NullReferenceException ist möglich. Überlegen Sie, ob Sie einer lokalen Variablen ein Ereignis zuweisen möchten, bevor Sie sie aufrufen. NewsListControl.cs 111

In diesem Fall kann die folgende unangenehme Situation auftreten: Nach dem Überprüfen der NewsLoaded- Variablen auf Null- Ungleichung kann die Methode, die das Ereignis verarbeitet, beispielsweise in einem anderen Thread abgemeldet werden, und wenn wir in den Hauptteil der bedingten if-Anweisung gelangen , wird die NewsLoaded- Variable gleich null . Wenn Sie versuchen, Abonnenten für ein NewsLoaded- Ereignis anzurufen , das null ist, wird eine NullReferenceException ausgelöst . Es ist viel sicherer, den bedingten Nulloperator zu verwenden und den obigen Code wie folgt neu zu schreiben:

 protected void OnNewsLoaded() { NewsLoaded?.Invoke(this, EventArgs.Empty); } 

Der Analysator zeigte 68 weitere ähnliche Stellen an. Wir werden sie hier nicht beschreiben - das Muster des Ereignisaufrufs in ihnen ist ähnlich.

Geben Sie null von ToString zurück

Vor nicht allzu langer Zeit habe ich aus dem interessanten Artikel eines Kollegen herausgefunden, dass Microsoft nicht empfiehlt, null von einer überschriebenen ToString- Methode zurückzugeben. PVS-Studio ist sich dessen bewusst:

 public override string ToString() { lock (loggerLock) { if (sbMessages != null && sbMessages.Length > 0) { return sbMessages.ToString(); } return null; } } 

PVS-Studio Warnung: V3108 Es wird nicht empfohlen, 'null' von der 'ToSting ()' - Methode zurückzugeben. Logger.cs 167

Warum angemessen, wenn nicht verwendet?

 public SeafileCheckAccInfoResponse GetAccountInfo() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "account/info/?format=json"); .... } 

PVS-Studio Warnung: V3008 Der Variablen 'url' werden zweimal hintereinander Werte zugewiesen. Vielleicht ist das ein Fehler. Kontrollzeilen: 197, 196. Seafile.cs 197

Wie Sie dem Beispiel entnehmen können, wird der Deklaration der URL- Variablen ein Wert zugewiesen, der von der FixPrefix- Methode zurückgegeben wird. In der nächsten Zeile „schleifen“ wir den resultierenden Wert, auch ohne ihn irgendwo zu verwenden. Wir bekommen etwas Ähnliches wie "toter Code" - es macht die Arbeit, es hat keinen Einfluss auf das Endergebnis. Dieser Fehler ist höchstwahrscheinlich das Ergebnis des Kopierens und Einfügens, da solche Codefragmente in 9 weiteren Methoden gefunden werden.
Als Beispiel geben wir zwei Methoden mit einer ähnlichen ersten Zeile an:

 public bool CheckAuthToken() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "auth/ping/?format=json"); .... } .... public bool CheckAPIURL() { string url = URLHelpers.FixPrefix(APIURL); url = URLHelpers.CombineURL(APIURL, "ping/?format=json"); .... } 

Insgesamt


Wie wir sehen können, hängt die Komplexität der Einrichtung der automatischen Überprüfung durch den Analysator nicht vom ausgewählten CI-System ab. In nur 15 Minuten und wenigen Mausklicks richten wir die Überprüfung unseres Projektcodes mit einem statischen Analysator ein.

Abschließend empfehlen wir Ihnen , den Analysator für Ihre Projekte herunterzuladen und zu testen .



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Oleg Andreev, Ilya Gainulin. PVS-Studio in den Clouds: Azure DevOps .

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


All Articles