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 .

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:
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);
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:

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:

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

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