Static Analysis Light
Mit der „Statischen Codeanalyse“ prüft TwinCAT 3 PLC vor dem Laden auf das Zielsystem, ob der Quellcode eines Projekts festgelegten Kodierrichtlinien folgt.
Die lizenzfreie Variante der statischen Codeanalyse ist das „Static Analysis Light“. Die dort konfigurierten Prüfungen werden automatisch im Anschluss an jede erfolgreiche Codeerzeugung durchgeführt. Sie definieren den gewünschten Satz an Regeln in den SPS-Projekteigenschaften in der Kategorie Static Analysis.
Abweichungen von den Regeln werden als Fehlermeldungen in der Fehlerliste ausgegeben. Jede Regel besitzt eine eindeutige Nummer. Wenn während der Statischen Analyse die Verletzung einer Regel festgestellt wird, wird die Nummer zusammen mit einer Fehlerbeschreibung gemäß folgender Syntax in der Fehlerliste ausgegeben. Die Abkürzung „SA“ weist dabei auf „Static Analysis“ hin.
Syntax: "SA<Regelnummer>: <Regelbeschreibung>"
Beispiel für Regelnummer 33 (Nicht verwendete Variablen): "SA0033: Nicht verwendet: Variable 'bSample'"
TwinCAT analysiert nur den Programmcode des aktuellen Projekts, Bibliotheken bleiben unbeachtet! |
Beachten Sie, dass das Static Analysis Light automatisch im Anschluss an den erfolgreichen Übersetzungsprozess ausgeführt wird. Falls die Codegenerierung hingegen nicht erfolgreich war, d.h. wenn der Compiler Kompilierfehler festgestellt hat, wird das Static Analysis Light nicht ausgeführt. |
Mithilfe von Pragmas können Sie Prüfungen für bestimmte Codeteile ausschalten (siehe unten). |
Konfiguration des Regelsatzes
Die Kategorie Static Analysis der SPS-Projekteigenschaften definiert die Prüfungen, die die Light-Version der statischen Codeanalyse bei jeder Codeerzeugung durchführt.
Geltungsbereich der Static-Analysis-Konfiguration Die Einstellungen, die Sie in der Kategorie Static Analysis der SPS-Projekteigenschaften vornehmen, sind sogenannte Solution options und wirken sich daher nicht nur auf das SPS-Projekt aus, dessen Eigenschaften Sie aktuell bearbeiten. Der konfigurierte Regelsatz wird für alle SPS-Projekte übernommen, die sich in der Entwicklungsumgebung befinden. |
Verfügbare Regeln innerhalb des Static Analysis Light:
- SA0033: Nicht verwendete Variablen
- SA0028: Überlappende Speicherbereiche
- SA0006: Schreibzugriff aus mehreren Tasks
- SA0004: Mehrfacher Schreibzugriff auf Ausgang
- SA0027: Mehrfachverwendung des Namens
- SA0167: Temporäre Funktionsbausteininstanzen
- SA0175: Verdächtige Operation auf String
SA0033: Nicht verwendete Variablen
Funktion | Ermittelt Variablen, die deklariert sind, aber innerhalb des kompilierten Programmcodes nicht verwendet werden. |
Begründung | Nicht verwendete Variablen machen ein Programm weniger gut lesbar und wartbar. Nicht verwendete Variablen belegen unnötig Speicher und kosten bei der Initialisierung unnötig Laufzeit. |
Wichtigkeit | Mittel |
PLCopen-Regel | CP22/CP24 |
SA0028: Überlappende Speicherbereiche
Funktion | Ermittelt die Stellen, durch die zwei oder mehr Variablen denselben Speicherplatz belegen. |
Begründung | Wenn zwei Variablen auf dem gleichen Speicherplatz liegen, dann kann sich der Code sehr unerwartet verhalten. Dies ist in jedem Fall zu vermeiden. Wenn es unumgänglich ist, einen Wert in verschiedenen Interpretationen zu verwenden, zum Beispiel einmal als DINT und einmal als REAL, dann sollten Sie eine UNION definieren. Auch über einen Pointer können Sie auf einen Wert anders getypt zugreifen, ohne dass der Wert umgewandelt wird. |
Wichtigkeit | Hoch |
Beispiel:
In dem folgenden Beispiel verwenden beide Variablen Byte 21, d.h. die Speicherbereiche der Variablen überlappen.
PROGRAM MAIN
VAR
nVar1 AT%QB21 : INT; // => SA0028
nVar2 AT%QD5 : DWORD; // => SA0028
END_VAR
SA0006: Schreibzugriff aus mehreren Tasks
Funktion | Ermittelt Variablen, auf die von mehr als einer Task geschrieben wird. |
Begründung | Eine Variable, die in mehreren Tasks geschrieben wird, kann unter Umständen ihren Wert unerwartet ändern. Das kann zu verwirrenden Situationen führen. Stringvariablen und auf einigen 32-Bit-Systemen auch 64-Bit-Integer-Variablen können sogar einen inkonsistenten Zustand bekommen, wenn die Variable gleichzeitig in zwei Tasks geschrieben wird. |
Ausnahme | In bestimmten Fällen kann es nötig sein, dass mehrere Tasks eine Variable schreiben. Stellen Sie dann sicher, beispielsweise durch die Verwendung von Semaphoren, dass der Zugriff nicht zu einem inkonsistenten Zustand führt. |
Wichtigkeit | Hoch |
PLCopen-Regel | CP10 |
Sehen Sie auch die Regel SA0103. |
Aufruf entspricht Schreibzugriff Bitte beachten Sie, dass Aufrufe als Schreibzugriff interpretiert werden. Beispielsweise wird der Aufruf einer Methode für eine Funktionsbausteininstanz als Schreibzugriff auf die Funktionsbausteininstanz angesehen. Eine genauere Analyse der Zugriffe und Aufrufe ist z.B. aufgrund von virtuellen Aufrufen (Zeiger, Interface) nicht möglich. Wenn Sie die Regel SA0006 für eine Variable (z.B. für eine Funktionsbausteininstanz) deaktivieren möchten, können Sie das folgende Attribut oberhalb der Variablendeklaration einfügen: {attribute 'analysis' := '-6'} |
Beispiele:
Die beiden globalen Variablen nVar und bVar werden von zwei Tasks geschrieben.
Globale Variablenliste:
VAR_GLOBAL
nVar : INT;
bVar : BOOL;
END_VAR
Programm MAIN_Fast, aufgerufen von der Task PlcTaskFast:
nVar := nVar + 1; // => SA0006
bVar := (nVar > 10); // => SA0006
Programm MAIN_Slow, aufgerufen von der Task PlcTaskSlow:
nVar := nVar + 2; // => SA0006
bVar := (nVar < -50); // => SA0006
SA0004: Mehrfacher Schreibzugriff auf Ausgang
Funktion | Ermittelt Ausgänge, die an mehr als einer Position geschrieben werden. |
Begründung | Die Wartbarkeit leidet, wenn ein Ausgang an verschiedenen Stellen im Code geschrieben wird. Es ist dann unklar, welcher Schreibzugriff derjenige ist, der tatsächlich Auswirkungen im Prozess hat. Gute Praxis ist es, die Berechnung der Ausgangsvariablen in Hilfsvariablen durchzuführen und an einer Stelle am Ende des Zyklus den berechneten Wert zuzuweisen. |
Ausnahme | Es wird kein Fehler ausgegeben, wenn eine Ausgangsvariable in verschiedenen Zweigen von IF- bzw. CASE-Anweisungen geschrieben wird. |
Wichtigkeit | Hoch |
PLCopen-Regel | CP12 |
Diese Regel kann nicht über ein Pragma oder Attribut abgeschaltet werden! |
Beispiel:
Globale Variablenliste:
VAR_GLOBAL
bVar AT%QX0.0 : BOOL;
nSample AT%QW5 : INT;
END_VAR
Programm MAIN:
PROGRAM MAIN
VAR
nCondition : INT;
END_VAR
IF nCondition < INT#0 THEN
bVar := TRUE; // => SA0004
nSample := INT#12; // => SA0004
END_IF
CASE nCondition OF
INT#1:
bVar := FALSE; // => SA0004
INT#2:
nSample := INT#11; // => SA0004
ELSE
bVar := TRUE; // => SA0004
nSample := INT#9; // => SA0004
END_CASE
SA0027: Mehrfachverwendung des Namens
Funktion | Ermittelt die Mehrfachverwendung eines Namens/Bezeichners einer Variable oder eines Objekts (POU) innerhalb des Gültigkeitsbereichs eines Projekts. Die folgenden Fälle werden abgedeckt:
|
Begründung | Gleiche Namen können beim Lesen des Codes verwirrend sein. Sie können zu Fehlern führen, wenn unbeabsichtigt auf das falsche Objekt zugegriffen wird. Definieren und befolgen Sie deshalb Namenskonventionen zur Vermeidung solcher Situationen. |
Ausnahme | Enumerationen, die mit dem Attribut 'qualified_only' deklariert sind, sind von der SA0027-Prüfung ausgenommen, da auf ihre Elemente nur qualifiziert zugegriffen werden kann. |
Wichtigkeit | Mittel |
Beispiel:
Das folgende Beispiel erzeugt Fehler/Warnung SA0027, da die Bibliothek Tc2_Standard im Projekt eingebunden ist, welche den Funktionsbaustein TON zur Verfügung stellt.
PROGRAM MAIN
VAR
ton : INT; // => SA0027
END_VAR
SA0167: Temporäre Funktionsbausteininstanzen
Funktion | Ermittelt Funktionsbausteininstanzen, die als temporäre Variable deklariert sind. Dies betrifft Instanzen, die in einer Methode oder in einer Funktion oder als VAR_TEMP deklariert sind, und die deshalb in jedem Abarbeitungszyklus bzw. bei jedem Bausteinaufruf neu initialisiert werden. |
Begründung |
|
Wichtigkeit | Mittel |
Beispiele:
Methode FB_Sample.SampleMethod:
METHOD SampleMethod : INT
VAR_INPUT
END_VAR
VAR
fbTrigger : R_TRIG; // => SA0167
END_VAR
Funktion F_Sample:
FUNCTION F_Sample : INT
VAR_INPUT
END_VAR
VAR
fbSample : FB_Sample; // => SA0167
END_VAR
Programm MAIN:
PROGRAM MAIN
VAR_TEMP
fbSample : FB_Sample; // => SA0167
nReturn : INT;
END_VAR
nReturn := F_Sample();
SA0175: Verdächtige Operation auf Zeichenkette
Funktion | Ermittelt Codestellen, die bei einer UTF-8-Kodierung verdächtig sind. |
Erfasste Konstrukte |
|
Wichtigkeit | Mittel |
Beispiele:
VAR
sVar : STRING;
pVar : POINTER TO STRING;
nVar : INT;
END_VAR
// 1) SA0175: Suspicious operation on string: Index access
sVar[2]; // => SA0175
// 2) SA0175: Suspicious operation on string: Possible index access
pVar := ADR(sVar); // => SA0175
// 3) SA0175: Suspicious operation on string: Possible index access
nVar := FIND(sVar, 'a'); // => SA0175
// 4) SA0175: Suspicious operation on string: Literal '<...>' contains Non-ASCII character
sVar := '99€'; // => SA0175
sVar := 'Ä'; // => SA0175
Pragmas und Attribute für Static Analysis Light
Mithilfe eines Pragmas bzw. eines Attributs können Sie Codeteile aus der Prüfung ausklammern. Verwenden Sie das Pragma {analysis …}, um Kodierregeln im Implementierungsteil auszuschalten und das Attribut {attribute 'analysis' := '...'}, um Kodierregeln im Deklarationsteil auszuschalten.
Voraussetzung: Sie haben die Regeln in den Projekteigenschaften aktiviert.
Außerdem können Sie das Attribut {attribute 'no-analysis'} verwenden, um Programmierobjekte von der Statischen Analyse auszuschließen.
Regeln, die in den Projekteigenschaften deaktiviert sind, können Sie auch nicht über Pragma oder Attribut aktivieren. |
Regel SA0004 (Mehrfacher Schreibzugriff auf Ausgang) kann nicht über Pragma deaktiviert werden. |
Pragma {analysis ...}
Das Pragma {analysis -/+<Regelnummer>} können Sie im Implementierungsteil eines Programmierbausteins verwenden, um einzelne Kodierregeln für die nachfolgenden Codezeilen auszuschalten. Sie deaktivieren Codierregeln durch die Angabe der Regelnummern und einem vorangestellten Minuszeichen (“-”). Zur Aktivierung wird ein Pluszeichen (“+”) vorangestellt. Mit Hilfe einer Kommaseparierung können Sie im Pragma beliebig viele Regeln angeben.
Einfügeort:
- Deaktivierung von Regeln: Im Implementierungsteil vor der ersten Codezeile, ab der die Codeanalyse deaktiviert wird, mit {analysis - ...}.
- Aktivierung von Regeln: Nach der letzten Zeile der Deaktivierung mit {analysis + ...}.
- Für die Regel SA0164 kann das Pragma auch im Deklarationsteil vor einem Kommentar eingefügt werden.
Syntax:
- Deaktivierung von Regeln:
- eine Regel: {analysis -<Regelnummer>}
- mehrere Regeln: {analysis -<Regelnummer>, -<weitere Regelnummer>, -<weitere Regelnummer>}
- Aktivierung von Regeln:
- eine Regel: {analysis +<Regelnummer>}
- mehrere Regeln: {analysis +<Regelnummer>, +<weitere Regelnummer>, +<weitere Regelnummer>}
Beispiele:
Sie möchten Regel 24 (nur getypte Literale erlaubt) für eine Zeile deaktivieren (d.h. es ist in diesen Zeilen nicht nötig, "nTest := DINT#99" zu schreiben) und danach wieder aktivieren:
{analysis -24}
nTest := 99;
{analysis +24}
nVar := INT#2;
Angabe mehrerer Regeln:
{analysis -10, -24, -18}
Attribut {attribute 'analysis' := '...'}
Das Attribut {attribute 'analysis' := '-<Regelnummer>'} können Sie verwenden, um bestimmte Regeln für einzelne Deklarationen oder für ein ganzes Programmierobjekt abzuschalten. Sie deaktivieren die Kodierregel durch die Angabe der Regelnnummer(n) und einem vorangestellten Minuszeichen. Sie können im Attribut beliebig viele Regeln angeben.
Einfügeort:
oberhalb der Deklaration eines Programmierobjekts oder in der Zeile oberhalb einer Variablendeklaration
Syntax:
- eine Regel: {attribute 'analysis' := '-<Regelnummer>'}
- mehrere Regeln: {attribute 'analysis' := '-<Regelnummer>, -<weitere Regelnummer>, -<weitere Regelnummer>'}
Beispiele:
Sie möchten Regel 33 (Nicht verwendete Variablen) für alle Variablen der Struktur ausschalten.
{attribute 'analysis' := '-33'}
TYPE ST_Sample :
STRUCT
bMember : BOOL;
nMember : INT;
END_STRUCT
END_TYPE
Sie möchten die Prüfung von Regel 28 (Überlappende Speicherbereiche) und von Regel 33 (Nicht verwendete Variablen) für die Variable nVar1 ausschalten.
PROGRAM MAIN
VAR
{attribute 'analysis' := '-28, -33'}
nVar1 AT%QB21 : INT;
nVar2 AT%QD5 : DWORD;
nVar3 AT%QB41 : INT;
nVar4 AT%QD10 : DWORD;
END_VAR
Sie möchten Regel 6 (Gleichzeitiger Zugriff) für eine globale Variable ausschalten, sodass keine Fehlermeldung generiert wird, wenn die Variable von mehr als einer Task geschrieben wird.
VAR_GLOBAL
{attribute 'analysis' := '-6'}
nVar : INT;
bVar : BOOL;
END_VAR
Attribut {attribute 'no-analysis'}
Das Attribut {attribute 'no-analysis'} können Sie verwenden, um ein gesamtes Programmierobjekt von der Prüfung durch die Statische Analyse auszuschließen. Für dieses Programmierobjekt wird die Prüfung der Kodierregeln, der Namenskonventionen und der unzulässigen Symbole nicht durchgeführt.
Einfügeort:
oberhalb der Deklaration eines Programmierobjekts
Syntax:
{attribute 'no-analysis'}
Beispiele:
{attribute 'qualified_only'}
{attribute 'no-analysis'}
VAR_GLOBAL
…
END_VAR
{attribute 'no-analysis'}
PROGRAM MAIN
VAR
…
END_VAR