Die schnellsten Berichte im wilden Westen. Und noch eine Handvoll Bugs ...

Bild 3

Microsoft hat nicht nur kürzlich Open Source-Code für seine eigenen Projekte veröffentlicht, sondern auch andere Unternehmen folgen diesem Trend. Für uns, die PVS-Studio-Entwickler, ist dies eine großartige Möglichkeit, den Analysator erneut zu testen, zu sehen, welche interessanten Dinge er finden kann, und die Autoren des Projekts darüber zu informieren. Heute schauen wir uns das Fast Reports-Projekt an.

Was wurde überprüft?


FastReport ist ein von Fast Reports entwickelter Berichtsgenerator. Geschrieben in C # und kompatibel mit .NET Standard 2.0+. Der Quellcode des Projekts wurde kürzlich auf GitHub veröffentlicht und zur weiteren Analyse heruntergeladen.

Berichte unterstützen die Verwendung von Text, Bildern, Linien, Formen, Diagrammen, Tabellen, Barcodes usw. Sie können einseitig und mehrseitig sein und enthalten neben Daten auch ein Deckblatt und eine Rückseite. Die Datenquellen können XML, CSV, Json, MS SQL, MySQL, Oracle, Postgres, MongoDB, Couchbase, RavenDB, SQLite sein.

Es gibt verschiedene Möglichkeiten, Berichtsvorlagen zu erstellen: aus Code; als XML-Datei; Verwenden eines Online-Designers oder einer FastReport Designer Community Edition.

Bei Bedarf können Bibliotheken als NuGet-Pakete heruntergeladen werden .

Weitere Informationen zu den Produktfunktionen finden Sie auf der GitHub-Seite des Projekts .

Bild 8


Es gab keine Probleme mit der Montage des Projekts - ich habe es aus Visual Studio 2017 zusammengestellt, von wo aus es anschließend mit dem PVS-Studio-Plugin überprüft wurde.

PVS-Studio ist ein statischer Analysator, der nach Fehlern in C-, C ++ -, C # - und Java-Code sucht. Bei der Analyse von C # -Code können Sie den Visual Studio IDE-Analysator mithilfe des PVS-Studio-Plug-Ins verwenden oder Projekte über die Befehlszeile überprüfen , für die das Befehlszeilenprogramm PVS-Studio_Cmd.exe verwendet wird. Wenn Sie möchten, können Sie die Analyse auf dem Build-Server konfigurieren oder die Analyseergebnisse mit SonarQube verbinden .

Mal sehen, was diesmal interessant war.

Da das Projekt klein ist, sollten Sie nicht mit vielen Tippfehlern und verdächtigen Stellen rechnen. Schauen wir uns an, was gefunden wurde, und versuchen wir sogar, etwas in der Praxis zu reproduzieren.

Gläser und mehr


Erfüllte die folgende Methode:

public override string ToString() { if (_value == null) return null; return this.String; } 

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

Ja, die Rückgabe von null von einer überschriebenen ToString () -Methode ist an sich kein Fehler, aber dennoch ein schlechter Stil. Dies wird unter anderem in der Dokumentation von Microsoft angegeben : Ihre ToString () - Überschreibung sollte nicht leer oder eine Nullzeichenfolge zurückgeben . Entwickler, die keine Nullrückgabe als Rückgabewert von ToString () erwarten, sind möglicherweise unangenehm überrascht, wenn während der Ausführung des folgenden Codes eine ArgumentNullException ausgelöst wird (vorausgesetzt, die Erweiterungsmethode wird für IEnumerable <T> aufgerufen).

 Variant varObj = new Variant(); varObj.ToString().Contains(character); 

Sie können die Tatsache bemängeln, dass das Beispiel synthetisch ist, aber das Wesentliche daran ändert sich nicht.

Darüber hinaus wird dieser Code wie folgt kommentiert:

 /// <summary> /// Returns <see cref="String"/> property unless the value on the right /// is null. If the value on the right is null, returns "". /// </summary> /// <returns></returns> 

Ups Anstelle von "" wird null zurückgegeben .

Fahren wir fort.

In der Bibliothek befindet sich eine FastString- Klasse. Beschreibung: Schnelle Alternative zu StringBuilder . Tatsächlich enthält diese Klasse ein Feld vom Typ StringBuilder . Konstruktoren der FastString- Klasse rufen die Init- Methode auf, die das entsprechende Feld initialisiert.

Der Code eines der Konstruktoren:

 public FastString() { Init(initCapacity); } 

Und der Code der Init- Methode:

 private void Init(int iniCapacity) { sb = new StringBuilder(iniCapacity); //chars = new char[iniCapacity]; //capacity = iniCapacity; } 

Falls gewünscht, können Sie über die StringBuilder- Eigenschaft auf das sb- Feld zugreifen:

 public StringBuilder StringBuilder { get { return sb; } } 

Insgesamt verfügt FastString über 3 Konstruktoren:

 public FastString(); public FastString(int iniCapacity); public FastString(string initValue); 

Ich habe dem Körper des ersten Designers bereits gezeigt, dass die beiden verbleibenden Vermutungen meiner Meinung nach auch nicht schwierig sind. Und jetzt Aufmerksamkeit. Ratet mal, was der folgende Code ausgibt:

 FastString fs = new FastString(256); Console.WriteLine(fs.StringBuilder.Capacity); 

Achtung, die Antwort:

Bild 2


Unerwartet? Schauen wir uns den Körper des entsprechenden Konstruktors an:

 public FastString(int iniCapacity) { Init(initCapacity); } 

Regelmäßige Leser unserer Artikel sollten bereits ein Auge dafür haben, hier ein Problem zu finden. Der Augenanalysator (Geruch, Logik, nennen Sie es wie Sie wollen) ist definitiv taub und er hat ein Problem gefunden: Der V3117- Konstruktorparameter 'iniCapacity' wird nicht verwendet. FastString.cs 434

Was für ein Zufall, dass der Klassencode ein konstantes Feld initCapacity enthält , das anstelle des Konstruktorparameters iniCapacity als Argument an die Init- Methode übergeben wird ...

 private const int initCapacity = 32; 

Wenn Sie ähnliche Namen verwenden, müssen Sie sehr, sehr vorsichtig sein. Überall dort, wo Fehler im Zusammenhang mit der Verwendung ähnlicher Namen gemacht wurden - Projekte in C, C ++, C #, Java - gab es überall Tippfehler dieser Art ...

Übrigens über Tippfehler.

Lassen Sie uns das folgende einfache Beispiel machen und sehen, wie es funktioniert:

 static void Main(string[] args) { TextObject textObj = new TextObject(); textObj.ParagraphFormat = null; Console.WriteLine("Ok"); } 

Wie Sie vielleicht erraten haben, unterscheidet sich die Ausgabe von der Zeichenfolge "Ok" :)

Welches? Also zum Beispiel:

Bild 1


Das Problem liegt in der ParagraphFormat- Eigenschaft und in der Verwendung ähnlicher Namen:

 public ParagraphFormat ParagraphFormat { get { return paragraphFormat; } set { ParagraphFormat = value; } } 

PVS-Studio Warnung : V3110 Mögliche unendliche Rekursion innerhalb der Eigenschaft 'ParagraphFormat'. TextObject.cs 281

Die ParagraphFormat- Eigenschaft ist ein Wrapper über dem AbsatzFormat- Feld. Darüber hinaus ist der get-Eigenschafts-Accessor korrekt geschrieben, aber der set-Eigenschafts-Accessor enthält einen nervigen Tippfehler: Anstelle eines Felds befindet sich der Datensatz in derselben Eigenschaft, was zu einer Rekursion führt. Wieder ein Fehler in Bezug auf ähnliche Namen.

Betrachten Sie das folgende Code-Snippet.

 public override Run Split(float availableWidth, out Run secondPart) { .... if (r.Width > availableWidth) { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } else { List<CharWithIndex> list = new List<CharWithIndex>(); for (int i = point; i < size; i++) list.Add(chars[i]); secondPart = new RunText(renderer, word, style, list, left + r.Width, charIndex); list.Clear(); for (int i = 0; i < point; i++) list.Add(chars[i]); r = new RunText(renderer, word, style, list, left, charIndex); return r; } .... } 

PVS-Studio Warnung : V3004 Die Anweisung 'then' entspricht der Anweisung 'else'. HtmlTextRenderer.cs 2092

Ein wenig Kopieren und Einfügen, und jetzt werden unabhängig vom Wert des Ausdrucks r.Width> availableWidth dieselben Aktionen ausgeführt. Entfernen Sie entweder die if-Anweisung oder ändern Sie die Logik in einem der Zweige.

 public static string GetExpression(FindTextArgs args, bool skipStrings) { while (args.StartIndex < args.Text.Length) { if (!FindMatchingBrackets(args, skipStrings)) break; return args.FoundText; } return ""; } 

Analyzer-Warnung : V3020 Eine bedingungslose Rückgabe innerhalb einer Schleife. CodeUtils.cs 262

Aufgrund der bedingungslosen return-Anweisung wird für die obige Schleife nicht mehr als eine Iteration ausgeführt. Vielleicht hat sich dieser Code nach dem Refactoring herausgestellt, oder vielleicht ist es nur eine ungewöhnliche Art, das zu tun, was ohne eine Schleife möglich wäre.

 private int FindBarItem(string c) { for (int i = 0; i < tabelle_cb.Length; i++) { if (c == tabelle_cb[i].c) return i; } return -1; } internal override string GetPattern() { string result = tabelle_cb[FindBarItem("A")].data + "0"; foreach (char c in text) { int idx = FindBarItem(c.ToString()); result += tabelle_cb[idx].data + "0"; } result += tabelle_cb[FindBarItem("B")].data; return result; } 

Warnung PVS-Studio : V3106 Möglicher negativer Indexwert. Der Wert des 'idx'-Index könnte -1 erreichen. BarcodeCodabar.cs 70

Potenziell gefährlicher Code. Die FindBarItem- Methode kann -1 zurückgeben, wenn das als Parameter übergebene Element nicht gefunden wird. Im aufrufenden Code ( GetPattern- Methode) wird dieser Wert in die Variable idx geschrieben und ohne vorläufige Überprüfung als Index des Arrays tabelle_cb verwendet . Beim Zugriff auf den Index -1 wird eine Ausnahme vom Typ IndexOutOfRangeException ausgelöst .

Fahren wir fort.

 protected override void Finish() { .... if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { if (saveStreams) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(generatedStreams[fileIndex]); } else { .... } .... } .... } .... } 

PVS-Studio Warnung : V3022 Der Ausdruck 'saveStreams' ist immer falsch. HTMLExport.cs 849

Der obige Code mit dem Abrufen des fileIndex- Werts und dem Aufrufen der DoPageEnd- Methode wird niemals ausgeführt, da das Ergebnis des zweiten saveStreams- Ausdrucks im Code immer falsch ist .

Das Interessanteste ist vielleicht alles (Sie haben keinen Artikel im Sinne der Mono-Analyse erwartet?). Es gab andere Warnungen des Analysators, aber sie schienen mir nicht interessant genug zu sein, um sie in den Artikel aufzunehmen (ein Teil bleibt immer hinter den Kulissen).

Die Kenntnis des Designs wäre für ihre Analyse hilfreich. Daher sollten die Autoren diese Warnungen im Idealfall selbst betrachten. Dies sind Warnungen wie V3083 (ein potenziell gefährlicher Aufruf von Ereignishandlern), V3022 (die Bedingung ist immer wahr / falsch (in diesem Fall häufig aufgrund von Methoden, die einen einzelnen Wert zurückgeben)), V3072 , V3073 (Arbeiten mit IDisposable ) und andere.

Wenn dies irrelevant ist, können Sie:


Fazit


Bild 4


Trotz der Tatsache, dass der Artikel kurz war, war ich erfreut, die Warnungen des Analysators mit meinen Händen zu berühren - um zu sehen, wie sich das, was der Analysator schwört, in der Praxis manifestiert.

Ich wünsche den Autoren des Projekts viel Erfolg, die Korrektur der entdeckten Probleme und möchte einen Schritt in Richtung Open-Source-Community loben!

Ich rate dem Rest, den Analysator an Ihrem Code auszuprobieren und zu sehen, was Sie interessant finden können.

Alles Gute!



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Sergey Vasiliev. Die schnellsten Berichte im Wilden Westen - und eine Handvoll Fehler ...

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


All Articles