Experimentelle Version von PVS-Studio, die C # unterstützt

    PVS-Studio für C / C ++ / C #
    Wir haben eine experimentelle Version des PVS-Studio Analyzers, mit der C # -Projekte analysiert und der Welt gezeigt werden können. Dies ist kein Release und nicht einmal Beta. Dies ist nur der aktuelle Build von PVS-Studio. Wir möchten so früh wie möglich Feedback von unseren Benutzern oder potenziellen Benutzern zum C # -Support erhalten. Daher bieten wir Enthusiasten die Möglichkeit, die neue Version von PVS-Studio in ihren C # -Projekten auszuprobieren und uns über die Ergebnisse, Mängel und Wünsche zu informieren. Oh ja, und natürlich wird der Artikel die Ergebnisse des nächsten Projekts - SharpDevelop - beschreiben.

    PVS-Studio


    Nun lautet eine der wichtigen Fragen: "Warum sollte ein anderes Codeanalyse-Tool für C # erstellt werden?".

    Versuchen wir, sowohl potenziellen Benutzern als auch uns selbst zu antworten, um klar zu verstehen, wohin und warum wir umziehen.

    Wir haben den PVS-Studio Analyzer erfolgreich für die Sprache C / C ++ entwickelt und entwickeln ihn weiter. Dieser Analysator implementiert viele interessante und einzigartige Ideen zur Identifizierung verschiedener Arten von Fehlern. Im Laufe der Zeit wurde deutlich, dass viele der implementierten Diagnosen in keiner Weise mit einer bestimmten Programmiersprache verknüpft sind. Es spielt keine Rolle, welche Sprache Sie verwenden. Es kommt immer zu Tippfehlern, Fehlern aufgrund von Nachlässigkeit oder fehlgeschlagenem Kopieren und Einfügen.

    Und dann beschlossen wir, unsere Erfahrungen auf eine andere Programmiersprache anzuwenden, auf C #. Wie erfolgreich dies sein wird, wird die Zeit zeigen. Wir glauben, dass wir nach und nach ein sehr interessantes Tool entwickeln können, das von einer großen Anzahl von C # -Entwicklern profitiert.

    Unsere Aufgabe ist es nun, so früh wie möglich Feedback von unseren potenziellen Nutzern zu erhalten. Die Vollversion des PVS-Studio Analyzers ist noch nicht fertig. Jetzt hat es wenige Diagnosen (zum Zeitpunkt des Schreibens gab es 36). Sie können diese Version jedoch installieren und ausprobieren. Und wir werden jedem dankbar sein, der dies tut. Es ist wichtig, dass wir sicherstellen, dass wir uns im Allgemeinen in die richtige Richtung bewegen und dass der Analysator als Ganzes betriebsbereit ist. Und wir werden sehr schnell neue und neue Diagnosen hinzufügen.

    Daher schlage ich allen Interessierten vor, die aktuelle Version der experimentellen Version von PVS-Studio von diesem Link herunterzuladen: http://files.viva64.com/beta/PVS-Studio_setup.exe .

    Hinweis Mit der Zeit wird der obige Link ungültig. Wenn Sie diesen Artikel einen Monat oder später nach dem Veröffentlichungsdatum lesen, empfehlen wir Ihnen, die aktuelle Distributionsversion zu installieren: http://www.viva64.com/en/pvs-studio-download/

    Wenn der Leser PVS nicht ausprobiert hat -Studio, ich schlage vor, dass Sie den Artikel " PVS-Studio für Visual C ++ " lesen . Wie Sie sehen, konzentriert es sich auf C ++, aber tatsächlich gibt es keinen Unterschied. Aus Sicht der Benutzeroberfläche gibt es fast keinen Unterschied, ob Sie mit C ++ - oder C # -Projekten arbeiten.

    Um Ihr Feedback und Ihre Vorschläge zu senden, können Sie die Feedback-Seite verwenden .

    Überprüfen des SharpDevelop-Projekts


    Für Programmierer funktioniert gewöhnliche Werbung nicht. Ich weiß jedoch, wie ich die Aufmerksamkeit dieser ernsthaften und sehr beschäftigten Schöpfer auf mich ziehen kann. Wir prüfen verschiedene Open Source Projekte und schreiben Artikel darüber . Es gibt keine bessere Werbung als zu zeigen, was unser Tool kann.

    Ich sehe keinen Grund, das Rad neu zu erfinden. Mit der gleichen Methode werde ich jetzt C # -Programmierer faszinieren. Und hier ist ein weiterer Artikel über das Überprüfen des offenen SharpDevelop-Projekts.

    SharpDevelop ist eine kostenlose Entwicklungsumgebung für C #, Visual Basic .NET, Boo, IronPython, IronRuby, F #, C ++. Wird häufig als Alternative zu Visual Studio .NET verwendet. Bei Mono / GTK + - MonoDevelop gibt es auch eine Gabel.

    Für uns ist es wichtig, dass das Projekt vollständig in C # geschrieben ist. So können wir es mit der experimentellen Version von PVS-Studio überprüfen. Das Projekt hat 8522 Dateien mit der Erweiterung „cs“, deren Gesamtgröße 45 Megabyte beträgt.

    Die verdächtigsten Code-Schnipsel


    Fragment N1
    public override string ToString()
    {
      return String.Format("Thread Name = {1} Suspended = {2}",
                           ID, Name, Suspended);
    }

    Warnung PVS-Studio: V3025 Falsches Format. Beim Aufruf der 'Format'-Funktion wird eine andere Anzahl von tatsächlichen Argumenten erwartet. Erwartet: 2. Vorhanden: 3. Thread.cs 235

    Die ID-Variable wird in keiner Weise verwendet. Vielleicht gibt es hier keinen wirklichen Fehler. Es lohnt sich jedoch, diesen Ort zu besuchen. Vielleicht war geplant, hier eine ganz andere Linie zu bilden.

    Fragment N2
    public override string ToString ()
    {
      return
        String.Format ("[Line {0}:{1,2}-{3,4}:{5}]",
                       File, Row, Column, EndRow, EndColumn, Offset);
    }

    Warnung PVS-Studio: V3025 Falsches Format. Beim Aufruf der 'Format'-Funktion wird eine andere Anzahl von tatsächlichen Argumenten erwartet. Voraussichtlich: 4. Heute: 6. MonoSymbolTable.cs 235

    Ein interessanterer Fall. Was genau der Programmierer wollte, ist mir nicht klar. Vielleicht wollte er eine Nachricht wie diese

    formulieren : [Line file.cs: 10,20-30,40: 7]

    Aber anscheinend vermisste er einige geschweifte Klammern. Daher stellt sich heraus, dass ", 2" und ", 4" die Ausrichtung der Felder angeben und die Werte der Variablen EndRow und EndColumn überhaupt nicht anzeigen.

    Ich wage vorzuschlagen, dass die folgende Formatierungszeile korrekt ist:
    String.Format ("[Line {0}:{1},{2}-{3},{4}:{5}]",
                   File, Row, Column, EndRow, EndColumn, Offset);

    Fragment N3
    static MemberCore GetLaterDefinedMember(MemberSpec a, MemberSpec b)
    {
      var mc_a = a.MemberDefinition as MemberCore;
      var mc_b = b.MemberDefinition as MemberCore;
      ....
      if (mc_a.Location.File != mc_a.Location.File)
        return mc_b;
      return mc_b.Location.Row > mc_a.Location.Row ? mc_b : mc_a;
    }

    PVS-Studio Warnung: V3001 Links und rechts vom Operator '! =' Gibt es identische Unterausdrücke 'mc_a.Location.File'. membercache.cs 1306

    Hier handelt es sich um einen Tippfehler. Ich denke, der folgende Vergleich wäre die richtige Option:
    if (mc_a.Location.File != mc_b.Location.File)

    Fragment N4
    public WhitespaceNode(string whiteSpaceText,
                          TextLocation startLocation)
    {
      this.WhiteSpaceText = WhiteSpaceText;
      this.startLocation = startLocation;
    }

    PVS-Studio Warnung: V3005 Die Variable 'this.WhiteSpaceText' ist sich selbst zugeordnet. WhitespaceNode.cs 65

    Schöner Fehler. Hier zeigte der statische Analysator das Wesentliche. Er ist aufmerksam und wird im Gegensatz zu einem Menschen nicht müde. Also bemerkte er einen Tippfehler. Siehst du sie Stimmen Sie zu, es ist nicht einfach, einen Fehler zu finden.

    Also ein Tippfehler in einem Buchstaben. Es musste "= whiteSpaceText" geschrieben werden. Und es heißt "= WhiteSpaceText". Folglich bleibt der Wert von 'WhiteSpaceText' in der Klasse unverändert.

    Dies ist im Allgemeinen ein gutes Beispiel dafür, wie Variablen nicht benannt werden sollten. Es ist eine schlechte Idee, Namen nur mit einem Klein- / Großbuchstaben zu unterscheiden. Diskussionen über den Codierungsstil gehen jedoch über den Rahmen des Artikels hinaus. Außerdem riecht es nach einem heiligen Diskussionskrieg.

    Fragment N5
    new public bool Enabled {
      get { return base.Enabled; }
      set {
        if (this.InvokeRequired) {
          base.Enabled = this.VScrollBar.Enabled =
          this.hexView.Enabled =this.textView.Enabled =
          this.side.Enabled = this.header.Enabled = value;
        } else {
          base.Enabled = this.VScrollBar.Enabled =
          this.hexView.Enabled = this.textView.Enabled =
          this.side.Enabled = this.header.Enabled = value;
        }
      }
    }

    PVS-Studio Warnung: V3004 Die Anweisung 'then' entspricht der Anweisung 'else'. Editor.cs 225

    Es ist sehr verdächtig, dass unabhängig vom Wert von 'this.InvokeRequired' dieselben Aktionen ausgeführt werden. Ich vermute stark, dass der String "base.Enabled = ....." kopiert wurde. Und dann haben sie vergessen, etwas daran zu ändern.

    Fragment N6, N7, N8, N9
    public override void Run()
    {
      ....
      ISolutionFolderNode solutionFolderNode =
        node as ISolutionFolderNode;
      if (node != null)
      {
        ISolutionFolder newSolutionFolder =
          solutionFolderNode.Folder.CreateFolder(....);
        solutionFolderNode.Solution.Save();
      ....
    }

    PVS-Studio Warnung: V3019 Möglicherweise wird eine falsche Variable nach der Typkonvertierung mit dem Schlüsselwort 'as' mit null verglichen. Überprüfen Sie die Variablen 'node', 'solutionFolderNode'. SolutionNodeCommands.cs 127

    Wir wollten einige Aktionen ausführen, wenn der 'Knoten' von der 'ISolutionFolderNode'-Schnittstelle geerbt wird. Aber sie haben die falsche Variable überprüft. Die richtige Option:
    ISolutionFolderNode solutionFolderNode =
      node as ISolutionFolderNode;
    if (solutionFolderNode != null)
    {

    Übrigens ist dies ein recht häufiges Fehlermuster in C # -Programmen. Im SharpDevelop-Projekt sind beispielsweise drei weitere Fehler aufgetreten:
    • V3019 Möglicherweise wird eine falsche Variable nach der Typkonvertierung mit dem Schlüsselwort 'as' mit null verglichen. Überprüfen Sie die Variablen 'geometry', 'g'. PathHandlerExtension.cs 578
    • V3019 Möglicherweise wird eine falsche Variable nach der Typkonvertierung mit dem Schlüsselwort 'as' mit null verglichen. Überprüfen Sie die Variablen 'oldTransform', 'tg'. ModelTools.cs 420
    • V3019 Möglicherweise wird eine falsche Variable nach der Typkonvertierung mit dem Schlüsselwort 'as' mit null verglichen. Überprüfen Sie die Variablen 'node', 'solutionFolderNode'. SolutionNodeCommands.cs 104

    Fragment N10
    public override void VisitInvocationExpression(....)
    {
      ....
      foundInvocations = (idExpression.Identifier == _varName);
      foundInvocations = true;
      ....
    }

    PVS-Studio Warnung: V3008 Der Variablen 'foundInvocations' werden zweimal nacheinander Werte zugewiesen. Vielleicht ist das ein Fehler. Check lines: 211, 209. RedundantAssignmentIssue.cs 211

    Sehr verdächtige Neuzuweisung. Vielleicht wurde die zweite Aufgabe beim Debuggen des Codes geschrieben und dann vergessen.

    Fehler N11
    public static Snippet CreateAvalonEditSnippet(....)
    {
      ....
      int pos = 0;
      foreach (Match m in pattern.Matches(snippetText)) {
        if (pos < m.Index) {
          snippet.Elements.Add(....);
          pos = m.Index;
        }
        snippet.Elements.Add(....);
        pos = m.Index + m.Length;
      }
      ....
    }

    PVS-Studio Warnung: V3008 Der Variablen 'pos' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Überprüfen Sie die Zeilen: 151, 148. CodeSnippet.cs 151

    Eine weitere verdächtige Neuzuweisung. Hier ist entweder ein Fehler oder die Zuordnung "pos = m.Index;" überflüssig.

    Fragment N12
    ....
    public string Text { get; set; }
    ....
    protected override void OnKeyUp(KeyEventArgs e)
    {
      ....
      editor.Text.Insert(editor.CaretIndex, Environment.NewLine);
      ....
    }

    PVS-Studio Warnung: V3010 Der Rückgabewert der Funktion 'Einfügen' muss verwendet werden. InPlaceEditor.cs 166

    Zeilen in C # sind unveränderlich. Wenn wir also etwas mit einem String machen, muss das Ergebnis irgendwo gespeichert werden. Es ist jedoch leicht zu vergessen, wie es zum Beispiel hier passiert ist. Der Entwickler entschied, durch Aufrufen der Insert () -Methode der Zeile etwas hinzuzufügen. Aber das ist nicht so. Der richtige Code lautet:
    editor.Text =
      editor.Text.Insert(editor.CaretIndex, Environment.NewLine);

    Fragment N13, N14
    public IEnumerable
    GetMappingForTable(SSDL.EntityType.EntityType table)
    {
      var value = GetSpecificMappingForTable(table);
      var baseMapping = BaseMapping;
      if (baseMapping != null)
        value.Union(baseMapping.GetMappingForTable(table));
      return value;
    }

    PVS-Studio Warnung: V3010 Der Rückgabewert der Funktion 'Union' muss verwendet werden. MappingBase.cs 274

    Im Allgemeinen habe ich die Vermutung , dass in C # -Projekten viele Fehler auftreten, die mit der Tatsache zusammenhängen, dass der Programmierer erwartet, dass sich das Objekt ändert, dies geschieht jedoch nicht.

    Mit der Erweiterungsmethode 'Union', die für Auflistungen definiert ist, die die IEnumerable-Schnittstelle implementieren, können Sie die Schnittmenge zweier Mengen abrufen. Der Container 'Wert' ändert sich jedoch nicht. Die richtige Option:
    value = value.Union(baseMapping.GetMappingForTable(table));

    Eine weitere solche Situation finden Sie hier: V3010 Der Rückgabewert der Funktion 'OrderBy' muss verwendet werden. CodeCoverageMethodElement.cs 124

    Snippet N15 Der

    PVS-Studio-Analysator versucht, Situationen zu identifizieren, in denen der Programmierer möglicherweise vergisst, etwas in switch () zu tun. Die Logik, zu entscheiden, ob gewarnt werden soll oder nicht, ist recht komplex. Manchmal werden falsche Positive erhalten, manchmal werden offensichtliche Fehler gefunden. Betrachten Sie eines dieser positiven Dinge.

    Im Code gibt es also eine solche Aufzählung:
    public enum TargetArchitecture {
      I386,
      AMD64,
      IA64,
      ARMv7,
    }

    Stellenweise werden alle Varianten dieser Aufzählung verwendet:
    TargetArchitecture ReadArchitecture ()
    {
      var machine = ReadUInt16 ();
      switch (machine) {
      case 0x014c:
        return TargetArchitecture.I386;
      case 0x8664:
        return TargetArchitecture.AMD64;
      case 0x0200:
        return TargetArchitecture.IA64;
      case 0x01c4:
        return TargetArchitecture.ARMv7;
      }
      throw new NotSupportedException ();
    }

    Es gibt jedoch verdächtige Stellen. Der Analysator hat mich beispielsweise auf folgenden Code aufmerksam gemacht:
    ushort GetMachine ()
    {
      switch (module.Architecture) {
      case TargetArchitecture.I386:
        return 0x014c;
      case TargetArchitecture.AMD64:
        return 0x8664;
      case TargetArchitecture.IA64:
        return 0x0200;
      }
      throw new NotSupportedException ();
    }

    PVS-Studio Warnung: V3002 Die switch-Anweisung deckt nicht alle Werte der 'TargetArchitecture'-Aufzählung ab: ARMv7. ImageWriter.cs 209

    Wie Sie sehen können, ist nicht der Fall in Betracht gezogen, wenn die Architektur ARMv7 ist. Ich weiß nicht, ob das ein Fehler ist oder nicht. Aber es scheint mir, dass dies genau ein Fehler ist. Der Name ARMv7 steht am Ende der Aufzählung, was bedeutet, dass er zuletzt hinzugefügt wurde. Infolgedessen könnte der Programmierer vergessen, die Funktion GetMachine () zu reparieren und diese Architektur zu berücksichtigen.

    Fragment N15
    void DetermineCurrentKind()
    {
      .....
      else if (Brush is LinearGradientBrush) {
        linearGradientBrush = Brush as LinearGradientBrush;
        radialGradientBrush.GradientStops =
          linearGradientBrush.GradientStops;
        CurrentKind = BrushEditorKind.Linear;
      }
      else if (Brush is RadialGradientBrush) {
        radialGradientBrush = Brush as RadialGradientBrush;
        linearGradientBrush.GradientStops =
          linearGradientBrush.GradientStops;
        CurrentKind = BrushEditorKind.Radial;
      }
    }

    PVS-Studio Warnung: V3005 Die Variable 'linearGradientBrush.GradientStops' ist sich selbst zugeordnet. BrushEditor.cs 120

    Ein ziemlich schwerer Code zum Lesen. Und anscheinend wurde deshalb ein Fehler gemacht. Höchstwahrscheinlich wurde der Code mit der Copy-Paste-Methode geschrieben und an einer Stelle falsch geändert.

    Anscheinend statt:
    linearGradientBrush.GradientStops =
      linearGradientBrush.GradientStops;

    Es hätte geschrieben werden sollen:
    linearGradientBrush.GradientStops =
      radialGradientBrush.GradientStops;

    Riecht


    Viele Fragmente, auf die der Analysator zeigt, sind kaum echte Fehler. Andererseits können Nachrichten, die mit einem solchen Code ausgegeben werden, auch nicht als falsch-positiv bezeichnet werden. Normalerweise sagen sie über einen solchen Code, dass es riecht.

    Oben habe ich mir viel Code angesehen, der anscheinend Fehler enthält. Jetzt werde ich einige Beispiele für Gerüche geben. Ich werde nicht alle Situationen betrachten, es ist nicht interessant. Ich beschränke mich auf 3 Beispiele. Entwickler können sich im SharpDevelop-Projekt mit den restlichen Gerüchen vertraut machen.

    Snippet-Code-Snippet N1
    protected override bool CanExecuteCommand(ICommand command)
    {
      ....
      }
      else if (command == DockableContentCommands.ShowAsDocument)
      {
        if (State == DockableContentState.Document)
        {
          return false;
        }
      }
      ....
      else if (command == DockableContentCommands.ShowAsDocument)
      {
        if (State == DockableContentState.Document)
        {
          return false;
        }
      }
      ....
    }

    PVS-Studio-Warnung: V3003 Die Verwendung des Musters 'if (A) {...} else if (A) {...}' wurde erkannt. Es ist wahrscheinlich, dass ein logischer Fehler vorliegt. Check lines: 773, 798. DockableContent.cs 773

    Wie Sie sehen, enthält das Programm zwei identische Blöcke. Die Bedingung des unteren Blocks 'if' wird niemals erfüllt. Meiner Meinung nach ist dies jedoch kein Fehler. Es scheint mir, dass sie den Block nur versehentlich dupliziert haben, und es ist überflüssig. Dies ist jedoch ein sehenswerter Ort.

    Snippet-Code-Snippet N2
    void PropertyExpandButton_Click(object sender, RoutedEventArgs e)
    {
      ....
      ContentPropertyNode clickedNode =
        clickedButton.DataContext as ContentPropertyNode;
      clickedNode = clickedButton.DataContext as ContentPropertyNode;
      if (clickedNode == null)
      ....
    }

    PVS-Studio Warnung: V3008 Der Variablen 'clickedNode' werden nacheinander zweimal Werte zugewiesen. Vielleicht ist das ein Fehler. Zeilen prüfen: 105, 104. PositionedGraphNodeControl.xaml.cs 105

    Der Code ist redundant und kann vereinfacht werden, um:
    ContentPropertyNode clickedNode =
      clickedButton.DataContext as ContentPropertyNode;
    if (clickedNode == null)

    Code-Snippet-Snippet N3
    IEnumerable
    CreateConstructorCompletionData(IType hintType)
    {
      ....
      if (!(hintType.Kind == TypeKind.Interface &&
            hintType.Kind != TypeKind.Array)) {
      ....
    }

    Warnung PVS-Studio: V3023 Betrachten Sie diesen Ausdruck. Der Ausdruck ist zu groß oder enthält einen Druckfehler. CSharpCompletionEngine.cs 2392

    Redundanter Code. Der Ausdruck kann vereinfacht werden:
    if (hintType.Kind != TypeKind.Interface) {

    Ich kann weitermachen, aber das ist genug. Alle anderen "Gerüche" sind ziemlich gleichmäßig und den bereits aufgeführten ähnlich.

    Fazit


    Wie Sie sehen, schützt die C # -Sprache allein nicht vor dummen Fehlern. Deshalb kann ich dieses Bild mit gutem Gewissen hierher bringen.

    PVS-Studio und C #

    Es lebe das Einhorn, das nun gelernt hat, Fehler in C # -Programmen zu finden!

    Aber im Ernst:
    • Bei der Programmierung machen wir alle nicht nur komplexe, sondern auch einfache Fehler. Insgesamt wird viel Zeit darauf verwendet, nach einfachen Fehlern zu suchen. Und manchmal viel .
    • Eine große Anzahl einfacher Fehler kann bereits beim Schreiben von Code erkannt werden, wenn Sie die Tools der statischen Codeanalyse verwenden. Die Verwendung solcher Tools spart viel Zeit, die für die Suche und das Debuggen vieler Fehler aufgewendet werden kann.
    • Das Wichtigste bei der statischen Analyse ist die regelmäßige Verwendung. Es macht keinen Sinn, die statische Analyse einmalig zu überprüfen. Der springende Punkt ist, den Fehler sofort nach dem Erscheinen im Code zu finden. Seltene Kontrollen sind zeitaufwändig und wenig nützlich. Immerhin wurden die Fehler, die schon zu diesem Zeitpunkt schnell und einfach gefunden werden konnten, mit Schweiß und Blut korrigiert.



    Wenn Sie diesen Artikel mit einem englischsprachigen Publikum teilen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov. Experimentelle Version von PVS-Studio mit C # -Unterstützung .

    Haben Sie den Artikel gelesen und eine Frage?
    Oft werden unseren Artikeln die gleichen Fragen gestellt. Wir haben die Antworten hier gesammelt: Antworten auf Fragen von Lesern von Artikeln über PVS-Studio, Version 2015 . Bitte beachten Sie die Liste.

    Jetzt auch beliebt: