Die schnellsten Berichte im Wilden Westen. Und eine Handvoll Bugs zum booten ...

    Bild 3

    Nicht nur Microsoft hat kürzlich Open-Source-Code für eigene Projekte eingeführt - auch andere Unternehmen folgen diesem Trend. Für uns sind die Entwickler von PVS-Studio eine großartige Möglichkeit, den Analysator erneut zu testen, zu sehen, was für ihn interessant ist, und die Autoren des Projekts darüber zu informieren. Heute schauen wir uns das Fast Reports-Projekt an.

    Was wurde geprüft?


    FastReport ist ein von Fast Reports entwickelter Berichtsgenerator . In C # geschrieben und kompatibel mit .NET Standard 2.0+. Der Quellcode des Projekts wurde kürzlich auf GitHub veröffentlicht , von wo aus es zur weiteren Analyse hochgeladen wurde.

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

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

    Bei Bedarf kann die Bibliothek als NuGet-Pakete heruntergeladen werden .

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

    Bild 8


    Beim Erstellen des Projekts gab es keine Probleme - ich habe es in Visual Studio 2017 gesammelt, wo ich es später mithilfe des PVS-Studio-Plugins überprüft habe.

    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 das Analysegerät mit IDE Visual Studio mithilfe des PVS-Studio-Plugins verwenden, oder Sie können Projekte über die Befehlszeile prüfen , für welches Befehlszeilenhilfsprogramm PVS-Studio_Cmd.exe verwendet wird. Falls gewünscht, können Sie eine Analyse auf dem Assembly-Server einrichten oder die Analyseergebnisse in den SonarQube übernehmen .

    Nun, mal sehen, was diesmal interessant war.

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

    Ochepyatki und nicht nur


    Ich habe folgende Methode getroffen:

    publicoverridestringToString() {
      if (_value == null) returnnull;
      returnthis.String;
    }

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

    Ja, kehrt null aus überschriebene Methode ToString () für sich allein keinen Fehler ist, aber noch ist es - schlechter Stil. Dies wird angezeigt, einschließlich, in der Dokumentation des Microsoft : Sie die ToString () die Überschreibung nicht - Anweisung zurückgeben sollte die Leere oder eine Null - : string . Entwickler, die nicht erwarten, als Rückgabewert von ToString () null zurückzugeben, werden möglicherweise unangenehm überrascht, wenn während der Ausführung des folgenden Codes eine Ausnahme generiert wird.ArgumentNullException (vorausgesetzt, dass die Erweiterungsmethode für IEnumerable <T> aufgerufen wird ).

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

    Man kann einen Fehler darin finden, dass das Beispiel synthetisch ist, aber das Wesen ä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 Gibt null statt "" zurück .

    Wir machen weiter.

    Die Bibliothek hat eine Klasse FastString . Beschreibung: Schnelle Alternative zu StringBuilder . Tatsächlich enthält diese Klasse ein Feld des Typs StringBuilder . Die Konstruktoren der FastString- Klasse rufen die Init- Methode auf , die das entsprechende Feld initialisiert.

    Code eines der Designer:

    publicFastString()
    {
      Init(initCapacity);
    }

    Und der Code der Init- Methode :

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

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

    public StringBuilder StringBuilder
    {
      get { return sb;  }
    }

    Insgesamt hat FastString 3 Konstruktoren:

    publicFastString();
    publicFastString(int iniCapacity);
    publicFastString(string initValue);

    Ich habe bereits die Leiche des ersten Designers gezeigt. Was die anderen beiden zu schätzen meinen, denke ich, ist auch einfach. Und jetzt Aufmerksamkeit. Ratet mal, was den folgenden Code ausgegeben hat:

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

    Achtung Antwort:

    Bild 2


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

    publicFastString(int iniCapacity)
    {
      Init(initCapacity);
    }

    Regelmäßige Leser unserer Artikel sollten hier bereits ein Problem haben. Am Augenanalysator (Duft, Logik, nennen Sie es was Sie wollen) genau gepflanzt und er hat das Problem gefunden: V3117 Konstruktorparameter 'iniCapacity' wird nicht verwendet. FastString.cs 453

    Welcher Zufall, dass der Klassencode das konstante Feld initCapacity enthält , das anstelle des Konstruktorparameters iniCapacity als Argument an die Methode Init übergeben wird ...

    privateconstint initCapacity = 32;

    Wenn Sie ähnliche Namen verwenden, müssen Sie sehr, sehr aufmerksam sein. Wo immer Fehler mit der Verwendung ähnlicher Namen verbunden sind - Projekte in C, C ++, C #, Java -, gab es überall Tippfehler dieser Art ...

    Übrigens, Tippfehler.

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

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

    Wie Sie sich schon gedacht haben, unterscheidet sich die Ausgabe von der Zeile "Ok" :)

    Wie? 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

    Property paragraph ist ein Wrapper über das Feld paragraph . Darüber hinaus wird der Get-Eigenschaftszugriffscode korrekt geschrieben, der Set-Eigenschaftszugriffscode enthält jedoch einen ärgerlichen Tippfehler: Anstelle eines Felds wird ein Eintrag in derselben Eigenschaft angezeigt, was zu einer Rekursion führt. Wieder ein Fehler mit ähnlichen Namen verbunden.

    Betrachten Sie den folgenden Codeausschnitt.

    publicoverride 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. Jetzt werden dieselben Aktionen ausgeführt, unabhängig vom Wert des Ausdrucks r.Width> availableWidth . Sie müssen entweder die if -Anweisung entfernen oder die Logik in einem der Zweige ändern.

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

    Analysewarnung : V3020 Ein unbedingter Rücksprung innerhalb einer Schleife. CodeUtils.cs 262

    Aufgrund des bedingungslosen Return- Operators für die obige Schleife wird nicht mehr als eine Iteration ausgeführt. Vielleicht wurde dieser Code nach einem Refactoring abgerufen oder es ist nur eine ungewöhnliche Methode, etwas zu tun, das ohne Schleife möglich ist.

    privateintFindBarItem(string c)
    {
      for (int i = 0; i < tabelle_cb.Length; i++)
      {
        if (c == tabelle_cb[i].c)
          return i;
      }
      return-1;
    }
    internaloverridestringGetPattern()
    {
      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;
    }

    PVS-Studio Warnung : V3106 Möglicher negativer Indexwert. Der Wert von 'idx' kann -1 erreichen. BarcodeCodabar.cs 70

    Möglicherweise gefährlicher Code. Die FindBarItem- Methode kann -1 zurückgeben, wenn das als Parameter übergebene Element nicht gefunden wird. Im aufrufenden Code (Methode GetPattern ) wird dieser Wert in die Variable idx geschrieben und ohne vorherige Prüfung als Index des Arrays tabelle_cb verwendet . Beim Zugriff über den Index -1 wird eine Ausnahme vom Typ IndexOutOfRangeException generiert .

    Wir machen weiter.

    protectedoverridevoidFinish()
    {
      ....
      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 false ist .

    Das interessanteste ist vielleicht alles (Sie haben nicht auf den Artikel im Sinne der Analyse von Mono gewartet ?). Es gab andere Warnungen des Analysators, aber sie schienen nicht interessant genug, um sie in den Artikel aufzunehmen (einige bleiben immer hinter den Kulissen).

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

    Wenn irgendetwas davon irrelevant ist, können Sie:


    Fazit


    Bild 4


    Trotz der Tatsache, dass der Artikel klein erschien, war es eine Freude, die Warnungen des Analysators mit meinen Händen zu „berühren“ - um zu sehen, wie sich der Analysator in der Praxis äußert.

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

    Ich empfehle den Rest, den Analysator mit Ihrem Code zu testen und herauszufinden, welche interessanten Dinge Sie finden können.

    Alles Gute!



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

    Jetzt auch beliebt: