6 Stimmen

Kann Klocwork (oder andere Werkzeuge) Typen, Typedefs und #define-Anweisungen kennen?

Ich habe auf der Suche nach Werkzeuge für Hilfe erkennen Fehler, die verhindern, dass ein Programm ordnungsgemäß als 64-Bit-Code ausgeführt werden kann. In letzter Zeit habe ich mit Klocwork und seiner benutzerdefinierten Prüffunktion herumgespielt, mit der ich den Quellcode als Baum mit XPath navigieren kann. Dies ist als "intelligentere" Alternative zu regulären Ausdrücken nützlich, aber ich war nicht in der Lage, es mit Typen vertraut zu machen.

Nehmen wir zum Beispiel an, ich möchte jede Instanz eines for Schleife, die entweder eine int oder eine long zu zählen. Der folgende Code ist leicht zu finden.

for (int i = 0; i < 10; i++)
    // ...

Die Suche nach diesem Code ist trivial, da sich die Variablendefinition direkt innerhalb der Schleife befindet. Betrachten Sie jedoch das folgende Beispiel.

int i;
// ...
for (i = 0; i < 10; i++)
    // ...

Dies ist schwierig zu finden, da die Variablendefinition von der Schleife getrennt ist und der erforderliche XPath-Ausdruck entweder unhandlich oder fehleranfällig wäre.

Können benutzerdefinierte Klocwork-Regeln also Ausdrücke wie diesen finden, bei denen Typkenntnis erforderlich ist, einschließlich der Auflösung von typedef y #define Aussagen? Gibt es andere Tools, die dies leisten können?

EDIT 1: Betrachten Sie das folgende Beispiel.

typedef int myint;

void Foo() {
    int i;
    for (i = 0; i < 10; i++) {
        Bar();
    }

    myint j;
    for (j = 0; j < 10; j++) {
        Bar();
    }
}

Les Lösung bereitgestellt von ahmeddirie findet die erste Schleife, weil der Typ von i ist ausdrücklich definiert als int . Die zweite Schleife wird jedoch nicht gefunden, da die Typisierung den zugrunde liegenden Typ verschleiert hat. Welche Werkzeuge verfolgen die Typen so, dass die zweite Schleifenvariable identifiziert werden kann? j als tatsächlich ein int ?

2voto

SK-logic Punkte 9388

Sie können Clang verwenden ( http://clang.llvm.org ) oder sogar Elsa ( https://github.com/dsw/oink-stack/ ) für die Generierung eines AST nach einer Typ-Propagierung und Instanziierung von Vorlagen. Beide bieten eine anständige C++-API und einige Möglichkeiten, einen AST in einen lesbaren Text zu überführen. Und beide Optionen sind kostenlos .

1voto

Larry Akers Punkte 19

Das Unternehmen, für das ich arbeite, Semantic Designs Inc., bietet Werkzeuge an, die eine allgemeine Infrastruktur für die Analyse und Transformation von Programmen und spezifische Analysekomponenten für verschiedene Programmiersprachen. Zusammen sind sie als DMS bekannt. Im Fall von C++ umfasst DMS integrierte Lexer, Präprozessoren, Parser, Namen und Typen Auflösungskomponenten für GCC3, GCC4, ISO14882c1998 (ANSI), Visual C++ 6.0 und unverwaltetes Visual Studio 2005 C++. Für verschiedene Dialekte von C gibt es auch eine Kontrollflussanalyse, einen Side Effects Analysator und einen Symbolabhängigkeitsanalysator, mit dem Werkzeuge wie ein Zeigerprüfer, Entferner von deaktivem Code, Funktionsprofiler und Programm Slicer implementiert wurden.

Die Komponenten zur Namens- und Typauflösung liefern eine vollständige Symboltabelle Informationen und Nachschlagemöglichkeiten, so dass Verweise auf Identifikatoren leicht mit ihren Typen und anderen deklarativen deklarativen Informationen. Die Informationen sind wie die erfassten und von einem Compiler verwendet werden, werden aber zusammen mit abstrakten Syntaxbäumen in einer Form aufbewahrt, die eine adaptive Wiederverwendung durch jedes Werkzeug ermöglicht, das die Komponente.

Semantic Designs hat kürzlich ein maßgeschneidertes Tool entwickelt, das speziell auf die Typen von Indexvariablen in Schleifendeklarationen Deklarationen, wie in Ihrem Beispiel. In diesem Fall bestand das Problem darin GCC2-Code zu aktualisieren, der den Compilerschalter -fno-for-scope verwendet, der eine Auflösungsregel für Schleifenvariablen vorsah, die nicht in späteren GCC-Dialekten nicht unterstützt wurde. Das Werkzeug musste die Schleifen transformieren, die Deklarationen ihrer Schleifenvariablen in einen äußeren Kontext verschieben der die -fno-for-scope Scoping-Regel beibehielt. Wo solche Änderungen nicht notwendig waren, wurde keine Änderung vorgenommen.

Das Tool musste also den mit jeder Referenz verbundenen Typ erkennen auf eine Schleifenvariable, wobei im Falle von Maskierungsbereichen zu unterscheiden ist, und den Code so rekonstruieren, dass die GCC3- und GCC4-Namensauflösung Namensauflösung zur gleichen semantischen Interpretation führt wie beim GCC2 mit -fno-for-scope. Dies erforderte die Möglichkeit, auf die Symboltabelle Informationen zugreifen zu können, die mit jeder Variablenreferenz verbunden sind, und in dem Fall wo Code verschoben wurde, die korrekte syntaktische Form der Typdeklaration für jede Variable zu rekonstruieren, deren der Typdeklaration für jede Variable, deren Deklaration verschoben wurde, zu rekonstruieren. Die Symboltabelle und die Bezeichnerreferenztabelle, die von der DMS C++-Namens- und Typauflösungskomponente bereitgestellte Symboltabelle und Bezeichnerreferenztabelle enthielt alle erforderlichen Informationen, und ein Modul zur Rekonstruktion der vorgeschriebenen Typsyntax ermöglichte die Synthese von korrekten neuen Typdeklarationen.

Betrachten Sie zum Beispiel das folgende Beispiel:

// loop variable hides variable in global scope
// will change meaning without -fno-for-scope
// fix: move decl. of cnt before for-loop
//   optionally rename globcnt loop variable

float globcnt = 0.0;

int Foo::foo3() {
    for (int globcnt = 0; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

Die GCC2 -fno-for-scope Semantik zeigt an, dass die Referenzen auf globcnt außerhalb der Schleife auf die Schleifenvariable verweisen, auch wenn GCC3 die Schleifenvariable als "out of scope" betrachtet und die Verweise auf die die globale Variable auflöst. Das Tool wandelte diesen Code in:

float globcnt = 0.0;

int Foo::foo3() {
    int globcnt = 0;
    for (; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

Wäre der Code nicht transformiert worden, hätte der GCC4 immer ein Wert von 1 aus Foo:foo3 zurückgegeben. Transformiert wäre der Wert jedoch durch die Iterationen der Schleife beeinflusst worden, wie sie ursprünglich für GCC2. Das Werkzeug musste erkennen, dass der letzte Verweis auf globcnt auf die lokale Variable vom Typ int war, nicht auf die globale Variable vom Typ float war, was es über die Symboltabelle nachschlagen konnte, und es musste entsprechend handeln.

Andererseits hat das Tool im folgenden Code erkannt, dass dass es keine Verweise auf i außerhalb der Schleife gab, so dass es akzeptabel war (und vorzuziehen), die Deklaration der Schleifenvariablen intakt zu lassen.

int Foo::foo0() {
    for (int i = 0; i < 10; i++) {
        globalInt += i*i;
    }
    return 0;
}

1voto

ahmeddirie Punkte 26

Ich bin mir nicht ganz sicher, ob das das ist, was Sie wollen, aber Sie können die Typen immer ganz einfach mit eingebauten Funktionen auflösen. Zum Beispiel, um Ihre Frage zu beantworten (wenn auch vielleicht nicht Ihre grundlegende Notwendigkeit):

//ForStmt / Init::ExprStmt / Expr::BinaryExpr [ $type := Left.getTypeName() ] [ $type = 'int' | $type.contains('long') ]

Dies findet 'for'-Schleifen, die 'int'- oder 'long int'-Zählertypen verwenden, recht einfach und kann natürlich auf jedes Element einer ausdrucksbasierten Anweisung angewendet werden.

Typdefinitionen sind für diese Art der Manipulation geeignet, unabhängig davon, ob sie vom Programmierer oder von der Sprache definiert wurden. Präprozessor-Definitionen liefern jedoch nur den Typ ihrer Muttersprache (d.h. das Makro selbst ist nicht für die Manipulation durch KAST verfügbar, sondern nur das, wozu es expandiert).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X