Regeln - Übersicht und Beschreibung

Regeln - Übersicht und Beschreibung 1:

Strikte IEC-Regeln prüfen

Die Prüfungen unterhalb des Knotens "Strikte IEC-Regeln prüfen" ermitteln Funktionalitäten und Datentypen, die in TwinCAT in Erweiterung der IEC61131-3 erlaubt sind.

Regeln - Übersicht und Beschreibung 2:

Gleichzeitigen/Konkurrierenden Zugriff prüfen

Zu diesem Thema existierenden die folgenden Regeln:

SA0006: Schreibzugriff aus mehreren Tasks
Ermittelt Variablen, auf die von mehr als einer Task geschrieben wird.

SA0103: Gleichzeitiger Zugriff auf nicht-atomare Daten
Ermittelt nicht-atomare Variablen (zum Beispiel mit Datentyp STRING, WSTRING, ARRAY, STRUCT, FB-Instanzen, 64-Bit Datentypen), die in mehr als einer Task verwendet werden.

 

Bitte beachten Sie, dass nur direkte Zugriffe erkannt werden können. Indirekte Zugriffe, beispielsweise per Pointer/Referenz, werden nicht aufgelistet.

Bitte beachten Sie außerdem die Dokumentation zum Thema "Multitask-Datenzugriffs-Synchronisation in der SPS", in der einige Hinweise zur Notwendigkeit und den Möglichkeiten einer Datenzugriffs-Synchronisation enthalten sind.

 

Übersicht

- SA0001: Unerreichbarer Code

- SA0002: Leere Objekte

- SA0003: Leere Anweisungen

- SA0004: Mehrfacher Schreibzugriff auf Ausgang

- SA0006: Schreibzugriff aus mehreren Tasks

- SA0007: Adressoperator auf Konstanten

- SA0008: Unterbereichstypen prüfen

- SA0009: Nicht verwendete Rückgabewerte

- SA0010: Arrays mit nur einer Komponente

- SA0011: Nutzlose Deklarationen

- SA0012: Variablen, die als Konstanten deklariert werden könnten

- SA0013: Deklarationen mit gleichem Variablennamen

- SA0014: Zuweisungen auf Instanzen

- SA0015: Zugriff auf globale Daten über FB_init

- SA0016: Lücken in Strukturen

- SA0017: Nicht-reguläre Zuweisungen

- SA0018: Unübliche Bitzugriffe

- SA0020: Möglicherweise Zuweisung eines abgeschnittenen Wertes an REAL-Variable

- SA0021: Weitergabe der Adresse einer temporären Variablen

- SA0022: (Möglicherweise) nicht zurückgewiesene Rückgabewerte

- SA0023: Komplexe Rückgabewerte

- SA0024: Nicht getypte Literale

- SA0025: Unqualifizierte Enumerationskonstanten

- SA0026: Möglicherweise Abschneiden von Strings

- SA0027: Mehrfachverwendung des Namens

- SA0028: Überlappende Speicherbereiche

- SA0029: Notation in Implementierung und Deklaration unterschiedlich

- Nicht verwendete Objekte auflisten

          - SA0031: Nicht verwendete Signaturen

          - SA0032: Nicht verwendete Aufzählungskonstante

          - SA0033: Nicht verwendete Variablen

          - SA0035: Nicht verwendete Eingabevariablen

          - SA0036: Nicht verwendete Ausgabevariablen

- SA0034: Enumerationsvariablen mit falscher Zuweisung

- SA0037: Schreibzugriff auf Eingabevariable

- SA0038: Lesezugriff auf Ausgabevariable

- SA0040: Mögliche Division durch Null

- SA0041: Möglicherweise schleifeninvarianter Code

- SA0042: Verwendung unterschiedlicher Zugriffspfade

- SA0043: Verwendung einer globalen Variablen in nur 1 POU

- SA0044: Deklarationen mit Schnittstellenreferenz

- Konvertierungen

          - SA0019: Implizite Pointer-Konvertierungen

          - SA0130: Implizite erweiternde Konvertierungen

          - SA0131: Implizite einengende Konvertierungen

          - SA0132: Implizite vorzeichenbehaftete/vorzeichenlose Konvertierungen

          - SA0133: Explizite einschränkende Konvertierungen

          - SA0134: Explizite vorzeichenbehaftete/vorzeichenlose Konvertierungen

- Verwendung direkter Adressen

          - SA0005: Ungültige Adressen und Datentypen

          - SA0047: Zugriff auf direkte Adressen

          - SA0048: AT-Deklarationen auf direkte Adressen

- Regeln für Operatoren

          - SA0051: Vergleichsoperatoren auf BOOL-Variablen

          - SA0052: Unübliche Schiebeoperation

          - SA0053: Zu große bitweise Verschiebung

          - SA0054: Vergleich von REAL/LREAL auf Gleichheit/Ungleichheit

          - SA0055: Unnötige Vergleichsoperationen von vorzeichenlosen Operanden

          - SA0056: Konstante außerhalb des gültigen Bereichs

          - SA0057: Möglicher Verlust von Nachkommastellen

          - SA0058: Operation auf Enumerationsvariablen

          - SA0059: Vergleichsoperationen, die immer TRUE oder FALSE liefern

          - SA0060: Null als ungültiger Operand

          - SA0061: Unübliche Operation auf Pointer

          - SA0062: Verwendung von TRUE und FALSE in Ausdrücken

          - SA0063: Möglicherweise nicht 16-bitkompatible Operationen

          - SA0064: Addition eines Pointers

          - SA0065: Pointer-Addition passt nicht zur Basisgröße

          - SA0066: Verwendung von Zwischenergebnissen

- Regeln für Anweisungen

          - FOR-Anweisungen

                    - SA0072: Ungültige Verwendung einer Zählervariablen

                    - SA0073: Verwendung einer nicht-temporären Zählervariablen

                    - SA0080: Schleifenindexvariable für Arrayindex überschreitet Array-Bereich

                    - SA0081: Obergrenze ist kein konstanter Wert

          - CASE-Anweisungen

                    - SA0075: Fehlendes ELSE

                    - SA0076: Fehlende Aufzählungskonstante

                    - SA0077: Datentypdiskrepanz bei CASE-Ausdruck

                    - SA0078: CASE-Anweisung ohne CASE-Zweig

          - SA0090: Return-Anweisung vor Ende der Funktion

- SA0095: Zuweisung in Bedingung

- SA0100: Variablen größer als <n> Bytes

- SA0101: Namen mit unzulässiger Länge

- SA0102: Zugriff von außen auf lokale Variable

- SA0103: Gleichzeitiger Zugriff auf nicht-atomare Daten

- SA0105: Mehrfache Instanzaufrufe

- SA0106: Virtuelle Methodenaufrufe in FB_init

- SA0107: Fehlen von formalen Parametern

- Strikte IEC-Regeln prüfen

          - SA0111: Zeigervariablen

          - SA0112: Referenzvariablen

          - SA0113: Variablen mit Datentyp WSTRING

          - SA0114: Variablen mit Datentyp LTIME

          - SA0115: Deklarationen mit Datentyp UNION

          - SA0117: Variablen mit Datentyp BIT

          - SA0119: Objektorientierte Funktionalität

          - SA0120: Programmaufrufe

          - SA0121: Fehlende VAR_EXTERNAL Deklarationen

          - SA0122: Als Ausdruck definierter Arrayindex

          - SA0123: Verwendung von INI, ADR oder BITADR

          - SA0147: Unübliche Schiebeoperation - strikt

          - SA0148: Unüblicher Bitzugriff - strikt

- Regeln für Initialisierungen

          - SA0118: Initialisierungen nicht mit Konstanten

          - SA0124: Dereferenzierungszugriff in Initialisierungen

          - SA0125: Referenzen in Initialisierungen

- SA0140: Auskommentierte Anweisungen

- Mögliche Verwendung nicht initialisierter Variablen

          - SA0039: Mögliche Null-Pointer-Dereferenzierung

          - SA0046: Mögliche Verwendung nicht initialisierter Schnittstellen

          - SA0145: Mögliche Verwendung nicht initialisierter Referenzen

- SA0150: Verletzung von Unter- oder Obergrenzen von Metriken

- SA0160: Rekursive Aufrufe

- SA0161: Ungepackte Struktur in gepackter Struktur

- SA0162: Fehlende Kommentare

- SA0163: Verschachtelte Kommentare

- SA0164: Mehrzeilige Kommentare

- SA0166: Maximale Anzahl an Eingabe-/Ausgabe-/VAR_IN_OUT Variablen

- SA0167: Temporäre Funktionsbausteininstanzen

- SA0168: Unnötige Zuweisungen

- SA0169: Ignorierte Ausgänge

- SA0170: Adresse einer Ausgangsvariablen sollte nicht verwendet werden

- SA0171: Enumerationen sollten das Attribut 'strict' haben

- SA0175: Verdächtige Operation auf String

 

Detaillierte Beschreibung

SA0001: Unerreichbarer Code

Funktion

Ermittelt Code, der nicht ausgeführt wird, beispielweise wegen einer RETURN oder CONTINUE Anweisung.

Begründung

Unerreichbarer Code sollte in jedem Fall vermieden werden. Häufig weist die Prüfung darauf hin, dass noch Testcode enthalten ist, der wieder entfernt werden sollte.

Wichtigkeit

Hoch

PLCopen-Regel

CP2

Beispiel 1 – RETURN:

PROGRAM MAIN
VAR
    bReturnBeforeEnd : BOOL;
END_VAR
bReturnBeforeEnd := FALSE;
RETURN;
bReturnBeforeEnd := TRUE;        // => SA0001

Beispiel 2 – CONTINUE:

FUNCTION F_ContinueInLoop : BOOL
VAR
    nCounter  : INT;
END_VAR
F_ContinueInLoop := FALSE;
 
FOR nCounter := INT#0 TO INT#5 BY INT#1 DO
    CONTINUE;
    F_ContinueInLoop := FALSE;   // => SA0001
END_FOR

 

SA0002: Leere Objekte

Funktion

Ermittelt POUs, GVLs oder Datentypdeklarationen, die keinen Code enthalten.

Begründung

Leere Objekte sollten vermieden werden. Sie sind oft ein Zeichen dafür, dass ein Objekt nicht vollständig implementiert ist.

Ausnahme: In manchen Fällen wird dem Rumpf eines Funktionsblocks kein Code geben, wenn dieser nur über Schnittstellen verwendet werden soll. In anderen Fällen wird eine Methode nur anlgelegt, weil sie von einer Schnittstelle gefordert wird, ohne dass für die Methode eine sinnvolle Implementierung möglich ist. In jedem Fall sollte eine solche Situation kommentiert werden.

Wichtigkeit

Mittel

 

SA0003: Leere Anweisungen

Funktion

Ermittelt Codezeilen, die ein Semikolon (;), aber keine Anweisung enthalten.

Begründung

Eine leere Anweisung kann ein Anzeichen für fehlenden Code sein.

Ausnahme

Es gibt sinnvolle Verwendungen leerer Anweisungen. Beispielsweise kann es sinnvoll sein, in einer CASE-Anweisung alle Fälle explizit auszuprogrammieren, auch die, in denen nichts zu tun ist. Wenn eine solche leere CASE-Anweisung mit einem Kommentar versehen ist, erzeugt die statische Codeanalyse keine Fehlermeldung.

Wichtigkeit

Niedrig

Beispiele:

;                                // => SA0003
(* comment *);                   // => SA0003
nVar;                            // => SA0003

Das folgende Beispiel erzeugt für den Zustand 2 den Fehler "SA0003: Empty statement".

CASE nVar OF
    1: DoSomething();
    2: ;
    3: DoSomethingElse();
END_CASE

Das folgende Beispiel erzeugt keinen SA0003-Fehler.

CASE nVar OF
    1: DoSomething();
    2: ;                         // nothing to do
    3: DoSomethingElse();
END_CASE

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

 

Regeln - Übersicht und Beschreibung 3:

Diese Regel kann nicht über ein Pragma oder Attribut abgeschaltet werden!
Weitere Informationen zu Attributen finden Sie unter Pragmas und Attribute.

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

 

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

 

Regeln - Übersicht und Beschreibung 4:

Sehen Sie auch die Regel SA0103.

Regeln - Übersicht und Beschreibung 5:

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

 

SA0007: Adressoperator auf Konstanten

Funktion

Ermittelt Stellen, an denen der ADR-Operator bei einer Konstanten angewendet wird.

Begründung

Durch einen Pointer auf eine konstante Variable hebt man die CONSTANT-Eigenschaft der Variable auf. Über den Pointer kann die Variable verändert werden, ohne dass der Compiler dies meldet.

Ausnahme

In seltenen Fällen kann es sinnvoll sein, einen Pointer auf eine Konstante an eine Funktion zu übergeben. Es muss dann allerdings gewährleistet sein, dass diese Funktion den übergebenen Wert nicht ändert. Verwenden Sie in diesem Fall wenn möglich VAR_IN_OUT CONSTANT.

Wichtigkeit

Hoch

 

Regeln - Übersicht und Beschreibung 6:

Wenn die Option Konstanten ersetzen in den Compiler-Optionen der SPS-Projekteigenschaften aktiviert ist, ist der Adressoperator für skalare Konstanten (Integer, BOOL, REAL) nicht erlaubt und ein Übersetzungsfehler wird ausgegeben. (Konstante Strings, Strukturen und Arrays haben immer eine Adresse.)

Beispiel:

PROGRAM MAIN
VAR CONSTANT
    cValue  : INT := INT#15;
END_VAR
VAR
    pValue  : POINTER TO INT;
END_VAR
pValue := ADR(cValue);           // => SA0007

 

SA0008: Unterbereichstypen prüfen

Funktion

Ermittelt Bereichsüberschreitungen von Unterbereichstypen. Zugewiesene Literale werden bereits vom Compiler geprüft. Wenn Konstanten zugeordnet sind, müssen die Werte innerhalb des definierten Bereichs liegen. Wenn Variablen zugeordnet sind, müssen die Datentypen identisch sein.

Begründung

Wenn Unterbereichstypen verwendet werden, dann sollte sichergestellt werden, dass dieser Unterbereich nicht verlassen wird. Der Compiler überprüft solche Unterbereichsverletzungen nur für Zuweisungen von Konstanten.

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 7:

Die Prüfung wird nicht für CFC-Objekte durchgeführt, da die Codestruktur dies nicht zulässt.

Beispiel:

PROGRAM MAIN
VAR
    nSub1  : INT (INT#1..INT#10);
    nSub2  : INT (INT#1..INT#1000);
    nVar   : INT;
END_VAR
nSub1 := nSub2;                  // => SA0008 
nSub1 := nVar;                   // => SA0008

 

SA0009: Nicht verwendete Rückgabewerte

Funktion

Ermittelt Funktions-, Methoden- und Eigenschaftenaufrufe, bei denen der Rückgabewert nicht verwendet wird.

Begründung

Wenn eine Funktion oder eine Methode einen Rückgabewert liefert, dann sollte dieser auch ausgewertet werden. Häufig wird im Rückgabewert mitgeliefert, ob die Funktion erfolgreich ausgeführt werden konnte. Wenn keine Auswertung erfolgt, ist später nicht mehr erkennbar, ob der Rückgabewert übersehen wurde, oder ob er tatsächlich nicht benötigt wird.

Ausnahme

Wenn ein Rückgabewert beim Aufruf nicht von Interesse ist, dann kann dies dokumentiert und die Zuweisung weglassen werden. Fehlerrückgaben sollten nie ignoriert werden!

Wichtigkeit

Mittel

PLCopen-Regel

CP7/CP17

Beispiel:

Funktion F_ReturnBOOL:

FUNCTION F_ReturnBOOL : BOOL
F_ReturnBOOL := TRUE;

Programm MAIN:

PROGRAM MAIN
VAR
    bVar  : BOOL;
END_VAR
F_ReturnBOOL();                  // => SA0009 
bVar := F_ReturnBOOL();

 

SA0010: Arrays mit nur einer Komponente

Funktion

Ermittelt Arrays, die nur eine einzige Komponente enthalten.

Begründung

Ein Array mit einer Komponente kann durch eine Variable vom Basistyp ersetzt werden. Der Zugriff auf diese Variable ist deutlich schneller als der Zugriff mit Index auf eine Variable.

Ausnahme

Häufig wird die Länge eines Arrays über eine Konstante festgelegt und ist ein Parameter für ein Programm. Das Programm kann dann mit Arrays von verschiedener Länge arbeiten und muss nicht geändert werden, wenn die Länge nur 1 beträgt. Eine solche Situation sollte entsprechend dokumentiert werden.

Wichtigkeit

Niedrig

Beispiele:

PROGRAM MAIN 
VAR
    aEmpty1  : ARRAY [0..0] OF INT;                 // => SA0010
    aEmpty2  : ARRAY [15..15] OF REAL;              // => SA0010
END_VAR

 

SA0011: Nutzlose Deklarationen

Funktion

Ermittelt Strukturen, Unions oder Enumerationen mit höchstens einer Komponente.

Begründung

Eine solche Deklaration kann für einen Leser verwirrend sein. Eine Struktur mit nur einem Element kann durch einen Aliastyp ersetzt werden. Eine Enumeration mit einem Element kann durch eine Konstante ersetzt werden.

Wichtigkeit

Niedrig

PLCopen-Regel

CP22/CP24

Beispiel 1 – Struktur:

TYPE ST_SingleStruct :           // => SA0011 
STRUCT
    nPart  : INT;
END_STRUCT
END_TYPE

Beispiel 2 – Union:

TYPE U_SingleUnion :             // => SA0011 
UNION
    fVar  : LREAL;
END_UNION
END_TYPE

Beispiel 3 – Enumeration:

TYPE E_SingleEnum :              // => SA0011 
(
    eOnlyOne := 1
);
END_TYPE

 

SA0012: Variablen, die als Konstanten deklariert werden könnten

Funktion

Ermittelt Variablen, auf die nicht schreibend zugegriffen wird und die deshalb als Konstante deklariert werden könnten.

Begründung

Wenn eine Variable nur an der Deklarationsstelle geschrieben und sonst nur lesend verwendet wird, dann nimmt die statische Analyse an, dass die Variable auch nicht geändert werden soll. Eine Deklaration als Konstante führt dann erstens dazu, dass auch bei Programmänderungen überprüft wird, dass die Variable nicht verändert wird. Zweitens führt die Deklaration als Konstante unter Umständen zu schnellerem Code.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN 
VAR
    nSample  : INT := INT#17;
    nVar     : INT; 
END_VAR
nVar := nVar + nSample;          // => SA0012

 

SA0013: Deklarationen mit gleichem Variablennamen

Funktion

Ermittelt Variablen, die den gleichen Namen haben wie andere Variablen (Beispiel: globale und lokale Variablen mit gleichen Namen), oder wie Funktionen, Aktionen, Methoden oder Eigenschaften (Properties) innerhalb des gleichen Zugriffsbereichs.

Begründung

Gleiche Namen können beim Lesen des Codes verwirrend sein und sie können zu Fehlern führen, wenn unbeabsichtigt auf das falsche Objekt zugegriffen wird. Es wird empfohlen, Namenskonventionen zu verwenden, deren Einhaltung solche Situationen vermeidet.

Wichtigkeit

Mittel

PLCopen-Regel

N5/N9

Beispiele:

Globale Variablenliste GVL_App:

VAR_GLOBAL
    nVar  : INT;
END_VAR

Programm MAIN, welches eine Methode mit dem Namen Sample beinhaltet:

PROGRAM MAIN
VAR
    bVar    : BOOL;
    nVar    : INT;               // => SA0013
    Sample  : DWORD;             // => SA0013
END_VAR
.nVar := 100;                    // Writing global variable "nVar"
nVar  := 500;                    // Writing local variable "nVar"
METHOD Sample
VAR_INPUT
… 

 

SA0014: Zuweisungen auf Instanzen

Funktion

Ermittelt Zuweisungen auf Funktionsbausteininstanzen. Bei Instanzen mit Pointer- oder Referenzvariablen können diese Zuweisungen riskant sein.

Begründung

Dies ist eine Performance-Warnung. Wenn eine Instanz einer anderen Instanz zugewiesen wird, dann werden alle Elemente und Unterelemente von der einen Instanz in die andere kopiert. Pointer auf Daten werden mitkopiert, jedoch nicht deren referenzierte Daten, so dass die Zielinstanz und die Quellinstanz nach der Zuweisung die gleichen Daten enthalten. Je nach Größe der Instanzen kann eine solche Zuweisung sehr lange dauern. Wenn eine Instanz beispielsweise zur Bearbeitung an eine Funktion übergeben werden soll, dann ist es sehr viel performanter, einen Pointer auf die Instanz zu übergeben.

Um selektiv Werte von einer Instanz in eine andere zu kopieren, kann eine Kopiermethode sinnvoll sein:

fb2.CopyFrom(fb1)

Wichtigkeit

Mittel

Beispiel:

PROGRAM MAIN 
VAR
    fb1  : FB_Sample;
    fb2  : FB_Sample;
END_VAR
fb1();
fb2 := fb1;                      // => SA0014

 

SA0015: Zugriff auf globale Daten über FB_init

Funktion

Ermittelt Zugriffe eines Funktionsbausteins auf globale Daten über die FB_init-Methode. Der Wert dieser Variablen hängt von der Reihenfolge der Initialisierungen ab!

Begründung

Je nach Deklarationsstelle der Instanz eines Bausteins kann es sein, dass bei Verletzung der Regel auf eine nicht-initialisierte Variable zugegriffen wird.

Wichtigkeit

Hoch

Beispiel:

Globale Variablenliste GVL_App:

VAR_GLOBAL
    nVar      : INT;
END_VAR

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR
    nLocal    : INT;
END_VAR

Methode FB_Sample.FB_init:

METHOD FB_init : BOOL
VAR_INPUT
    bInitRetains  : BOOL;        // if TRUE, the retain variables are initialized (warm start / cold start)
    bInCopyCode   : BOOL;        // if TRUE, the instance afterwards gets moved into the copy code (online change)
END_VAR
nLocal := 2*nVar;                // => SA0015

Programm MAIN:

PROGRAM MAIN
VAR
    fbSample  : FB_Sample;
END_VAR

 

SA0016: Lücken in Strukturen

Funktion

Ermittelt Lücken in Strukturen oder Funktionsbausteinen, verursacht durch die Alignment-Anforderungen des aktuell eingestellten Zielsystems. Wenn möglich, sollten Sie die Lücken durch Umsortieren der Strukturelemente oder durch Auffüllen mit einem Dummy-Elemententfernen. Wenn dies nicht möglich ist, dann können Sie die Regel für die betroffenen Strukturen durch das Attribut {attribute 'analysis' := '...'} deaktivieren.

Begründung

Durch unterschiedliche Alignment-Anforderungen auf verschiedenen Plattformen, kann es für solche Strukturen zu einem unterschiedlichen Layout im Speicher kommen. Der Code kann sich dann je nach Plattform unterschiedlich verhalten.

Wichtigkeit

Niedrig

Beispiele:

TYPE ST_UnpaddedStructure1 :
STRUCT
    bBOOL  : BOOL;
    nINT   : INT;                // => SA0016
    nBYTE  : BYTE;
    nWORD  : WORD;
END_STRUCT
END_TYPE
TYPE ST_UnpaddedStructure2 :
STRUCT
    bBOOL  : WORD;
    nINT   : INT;
    nBYTE  : BYTE;
    nWORD  : WORD;               // => SA0016
END_STRUCT
END_TYPE

 

SA0017: Nicht-reguläre Zuweisungen

Funktion

Ermittelt Zuweisungen auf Pointer, die keine Adresse (ADR-Operator, Zeigervariablen) oder Konstante 0 sind.

Begründung

Wenn ein Pointer einen Wert enthält, der keine gültige Adresse ist, dann kommt es bei der Dereferenzierung des Pointers zu einer Access Violation Exception.

Wichtigkeit

Hoch

Beispiel:

PROGRAM MAIN 
VAR
    nVar      : INT;
    pInt      : POINTER TO INT;
    nAddress  : XWORD;
END_VAR
nAddress := nAddress + 1;
 
pInt     := ADR(nVar);           // no error
pInt     := 0;                   // no error
pInt     := nAddress;            // => SA0017

 

SA0018: Unübliche Bitzugriffe

Funktion

Ermittelt Bitzugriffe auf vorzeichenbehaftete Variablen. Die Norm IEC 61131-3 erlaubt allerdings nur Bitzugriffe auf Bitfelder. Sehen Sie hierzu auch die strikte Regel SA0148.

Begründung

Vorzeichenbehaftete Datentypen sollten nicht als Bitfelder verwendet werden und umgekehrt. Die Norm IEC 61131-3 sieht solche Zugriffe nicht vor. Diese Regel muss eingehalten werden, wenn der Code protierbar sein soll.

Ausnahme

Ausnahme für Flag-Enumerationen: Wenn eine Enumeration mit Hilfe des Pragmaattributs {attribute 'flags'} als Flag deklariert ist, wird für Bitzugriffe mit den Operationen OR, AND oder NOT der Fehler SA0018 nicht ausgegeben.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN 
VAR
    nINT    : INT;
    nDINT   : DINT;
    nULINT  : ULINT;
    nSINT   : SINT;
    nUSINT  : USINT;
    nBYTE   : BYTE;
END_VAR
nINT.3    := TRUE;               // => SA0018
nDINT.4   := TRUE;               // => SA0018
nULINT.18 := FALSE;              // no error because this is an unsigned data type
nSINT.2   := FALSE;              // => SA0018
nUSINT.3  := TRUE;               // no error because this is an unsigned data type
nBYTE.5   := FALSE;              // no error because BYTE is a bit field

 

SA0020: Möglicherweise Zuweisung eines abgeschnittenen Werts an REAL-Variable

Funktion

Ermittelt Operationen auf Integer-Variablen, bei denen möglicherweise ein abgeschnittener Wert an eine Variable vom Datentyp REAL zugewiesen wird.

Begründung

Die statische Codeanalyse gibt einen Fehler aus, wenn das Ergebnis einer Integerberechnung einer REAL- oder LREAL-Variablen zugewiesen wird. Der Programmierer soll dabei auf eine möglicherweise fehlerhafte Interpretation einer solchen Zuweisung aufmerksam gemacht werden:

fLEAL := nDINT1 * nDINT2.

Da der Wertebereich von LREAL größer ist als der von DINT, könnte angenommen werden, dass das Ergebnis der Rechnung in jedem Fall in LREAL dargestellt wird. Das ist aber nicht der Fall. Der Prozessor berechnet das Ergebnis der Multiplikation als Integer und castet anschließend das Ergebnis nach LREAL. Ein Überlauf in der Integer-Berechnung würde verloren gehen. Um das Problem zu umgehen, muss die Rechnung bereits als REAL-Operation erfolgen:

fLREAL := TO_LREAL(nDINT1) * TO_LREAL(nDINT2)

Wichtigkeit

Hoch

Beispiel:

PROGRAM MAIN 
VAR
    nVar1  : DWORD;
    nVar2  : DWORD;
    fVar   : REAL;
END_VAR
nVar1 := nVar1 + DWORD#1;
nVar2 := nVar2 + DWORD#2;
fVar  := nVar1 * nVar2;          // => SA0020

 

SA0021: Weitergabe der Adresse einer temporären Variablen

Funktion

Ermittelt Zuweisungen von Adressen von temporären Variablen (Variablen auf dem Stack) zu nicht-temporären Variablen.

Begründung

Lokale Variablen einer Funktion oder einer Methode werden auf dem Stack angelegt und existieren nur während der Abarbeitung der Funktion oder Methode. Zeigt ein Pointer nach Abarbeitung der Methode oder Funktion auf eine solche Variable, dann kann über diesen Pointer in undefinierten Speicher gegriffen, oder auf eine falsche Variable in einer anderen Funktion zugegriffen werden. Diese Situation ist in jedem Fall zu vermeiden.

Wichtigkeit

Hoch

Beispiel:

Methode FB_Sample.SampleMethod:

METHOD SampleMethod : XWORD
VAR
    fVar  : LREAL;
END_VAR
SampleMethod := ADR(fVar);

Programm  MAIN:

PROGRAM MAIN
VAR
    nReturn   : XWORD;
    fbSample  : FB_Sample;
END_VAR
nReturn := fbSample.SampleMethod();                 // => SA0021

 

SA0022: (Möglicherweise) nicht zugewiesene Rückgabewerte

Funktion

Ermittelt alle Funktionen und Methoden, die einen Ausführungsstrang ohne Zuweisung auf den Rückgabewert enthalten.

Begründung

Ein nicht zugewiesener Rückgabewert in einer Funktion oder Methode deutet auf fehlenden Code hin. Auch wenn der Rückgabewert in jedem Fall einen Standardwert hat, ist es immer sinnvoll, diesen nochmal explizit zuzuweisen, um Unklarheiten zu vermeiden.

Wichtigkeit

Mittel

Beispiel:

FUNCTION F_Sample : DWORD
VAR_INPUT
    nIn    : UINT;
END_VAR
VAR
    nTemp  : INT;
END_VAR
nIn := nIn + UINT#1;
 
IF (nIn > UINT#10) THEN
    nTemp    := 1;               // => SA0022
ELSE
    F_Sample := DWORD#100;
END_IF

 

SA0023: Komplexe Rückgabewerte

Funktion

Ermittelt komplexe Rückgabewerte, die mit einer einfachen Registerkopie des Prozessors nicht zurückgegeben werden können. Dazu gehören Strukturen und Arrays sowie Rückgabewerte vom Typ STRING (unabhängig von der Größe des belegten Speicherplatzes).

Begründung

Dies ist eine Performance-Warnung. Wenn große Werte als Ergebnis einer Funktion, Methode oder einer Eigenschaft zurückgeliefert werden, dann werden diese vom Prozessor bei der Ausführung des Codes mehrfach umkopiert. Das kann zu Laufzeitproblemen führen und sollte wenn möglich vermieden werden. Eine bessere Performance wird erreicht, wenn ein strukturierter Wert als VAR_IN_OUT an eine Funktion oder Methode übergeben wird und in der Funktion oder Methode gefüllt wird.

Wichtigkeit

Mittel

Beispiel:

Struktur ST_Sample:

TYPE ST_Sample :
STRUCT
    n1  : INT;
    n2  : BYTE;
END_STRUCT
END_TYPE

Beispielfunktionen mit Rückgabewert:

FUNCTION F_MyFunction1 : I_MyInterface              // no error
FUNCTION F_MyFunction2 : ST_Sample                  // => SA0023
FUNCTION F_MyFunction3 : ARRAY[0..1] OF BOOL        // => SA0023

 

SA0024: Nicht getypte Literale

Funktion

Ermittelt nicht getypte Literale/Konstanten (z.B. nCount : INT := 10;).

Begründung

TwinCAT vergibt die Typen für Literale je nach ihrer Verwendung. In einigen Fällen kann dies zu unerwarteten Situationen führen, die besser über ein getyptes Literal geklärt werden. Zum Beispiel:

nDWORD := ROL(DWORD#1, i);

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    nVar  : INT;
    fVar  : LREAL;
END_VAR
nVar := 100;                     // => SA0024
nVar := INT#100;                 // no error
 
fVar := 12.5;                    // => SA0024
fVar := LREAL#12.5;              // no error

 

SA0025: Unqualifizierte Enumerationskonstanten

Funktion

Ermittelt Aufzählungskonstanten, die nicht mit einem qualifizierten Namen verwendet werden, d.h. ohne dass der Name der Enumeration vorangestellt ist.

Begründung

Qualifizierte Zugriffe machen den Code besser lesbar und besser wartbar. Ohne Erzwingen qualifizierter Variablennamen könnte bei Erweiterung des Programms eine weitere Enumeration eingefügt werden, die eine gleichnamige Konstante wie die einer bereits existierenden Enumeration enthält (siehe im Beispiel unten: “eRed”). Dann käme es zu einem nicht-eindeutigen Zugriff in diesem Codestück. Wir empfehlen in jedem Fall nur Enumerationen zu verwenden, die das {attribute ‘qualified-only’} tragen.

Wichtigkeit

Mittel

Beispiel:

Enumeration E_Color:

TYPE E_Color :
(
    eRed,
    eGreen,
    eBlue
);
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    eColor  : E_Color;
END_VAR
eColor := E_Color.eGreen;        // no error
eColor := eGreen;                // => SA0025

 

SA0026: Möglicherweise Abschneiden von Strings

Funktion

Ermittelt String-Zuweisungen und -Initialisierungen, die keine ausreichende String-Länge verwenden.

Begründung

Wenn Strings unterschiedlicher Länge zugewiesen werden, dann wird möglicherweise ein String abgeschnitten. Das Ergebnis ist dann nicht das erwartete.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    sVar1  : STRING[10];
    sVar2  : STRING[6];
    sVar3  : STRING[6] := 'abcdefghi';              // => SA0026
END_VAR
sVar2 := sVar1;                                     // => SA0026

 

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:

  • Der Name einer Enumerationskonstanten ist identisch mit dem Namen in einer anderen Enumeration innerhalb der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name einer Variablen ist identisch mit dem Namen eines anderen Objekts in der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name einer Variablen ist identisch mit dem Namen einer Enumerationskonstanten in einer Enumeration in der Applikation oder in einer eingebundenen Bibliothek.
  • Der Name eines Objekts ist identisch mit dem Namen eines anderen Objekts in der Applikation oder in einer eingebundenen Bibliothek.

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

 

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

 

SA0029: Notation in Implementierung und Deklaration unterschiedlich

Funktion

Ermittelt die Codestellen (in der Implementierung), an denen sich die Notation eines Bezeichners zur Notation in dessen Deklaration unterscheidet.

Begründung

Die Norm IEC 61131-3 definiert Bezeichner als nicht case-sensitiv. Das heißt, eine Variable die als “varx” deklariert wurde, kann im Code auch als “VaRx” verwendet werden. Dies ist jedoch verwirrend und irreführend und sollte daher vermieden werden.

Wichtigkeit

Mittel

Beispiele:

Funktion F_TEST:

FUNCTION F_TEST : BOOL

Programm MAIN:

PROGRAM MAIN
VAR
    nVar     : INT;
    bReturn  : BOOL;
END_VAR
nvar    := nVar + 1;             // => SA0029
bReturn := F_Test();             // => SA0029

 

SA0031: Nicht verwendete Signaturen

Funktion

Ermittelt Programme, Funktionsbausteine, Funktionen, Datentypen, Schnittstellen, Methoden, Eigenschaften, Aktionen etc., die innerhalb des kompilierten Programmcodes nicht aufgerufen werden.

Begründung

Nicht verwendete Objekte vergrößern das Projekt unnötig und können beim Lesen des Codes verwirren.

Wichtigkeit

Niedrig

PLCopen-Regel

CP2

 

SA0032: Nicht verwendete Aufzählungskonstante

Funktion

Ermittelt Enumerationskonstanten, die nicht im kompilierten Programmcode verwendet werden.

Begründung

Nicht verwendete Enumerationskonstanten vergrößern die Enumerationsdefinition unnötig und können beim Lesen des Programms verwirren.

Wichtigkeit

Niedrig

PLCopen-Regel

CP24

Beispiel:

Enumeration E_Sample:

TYPE E_Sample :
(
    eNull,
    eOne,                        // => SA0032
    eTwo
);
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    eSample  : E_Sample;
END_VAR
eSample := E_Sample.eNull;
eSample := E_Sample.eTwo;

 

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

 

SA0035: Nicht verwendete Eingabevariablen

Funktion

Ermittelt Eingangsvariablen, die innerhalb des jeweiligen Funktionsbausteins nicht zugewiesen 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

CP24

Beispiel:

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    bIn1  : BOOL;
    bIn2  : BOOL;                // => SA0035
END_VAR
VAR_OUTPUT
    bOut1 : BOOL;
    bOut2 : BOOL;                // => SA0036
END_VAR
bOut1 := bIn1;

 

SA0036: Nicht verwendete Ausgabevariablen

Funktion

Ermittelt Ausgangsvariablen, die innerhalb des jeweiligen Funktionsbausteins nicht zugewiesen 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

CP24

Beispiel:

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    bIn1  : BOOL;
    bIn2  : BOOL;                // => SA0035
END_VAR
VAR_OUTPUT
    bOut1 : BOOL;
    bOut2 : BOOL;                // => SA0036
END_VAR
bOut1 := bIn1;

 

SA0034: Enumerationsvariablen mit falscher Zuweisung

Funktion

Ermittelt Werte, die einer Enumerationsvariablen zugewiesen sind. Einer Enumerationsvariablen dürfen nur definierte Enumerationskonstanten zugewiesen werden.

Begründung

Eine Variable vom Typ einer Enumeration sollte auch nur die vorgesehenen Werte haben, anderfalls funktioniert Code, der diese Variable verwendet möglicherweise nicht richtig. Wir empfehlen, Enumerationen immer mit dem {attribute 'strict'} zu verwenden. Dann prüft bereits der Compiler die korrekte Verwendung der Enumerationskomponenten.

Wichtigkeit

Hoch

Beispiel:

Enumeration E_Color:

TYPE E_Color :
(
    eRed   := 1,
    eBlue  := 2,
    eGreen := 3
);
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    eColor : E_Color;
END_VAR
eColor := E_Color.eRed;
eColor := eBlue;
eColor := 1;                     // => SA0034

 

SA0037: Schreibzugriff auf Eingabevariable

Funktion

Ermittelt Eingangsvariablen (VAR_INPUT), auf die innerhalb der POU schreibend zugegriffen wird.

Begründung

Nach der Norm IEC 61131-3 darf eine Eingabevariable nicht innerhalb eines Bausteins verändert werden. Ein solcher Zugriff ist außerdem eine Fehlerquelle und macht den Code schlecht wartbar. Es weist daraufhin, dass eine Variable als Eingang und gleichzeitig als Hilfsvariable verwendet wird. Eine solche Doppelverwendung sollte vermieden werden.

Wichtigkeit

Mittel

Beispiel:

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    bIn   : BOOL := TRUE;
    nIn   : INT := 100;
END_VAR
VAR_OUTPUT
    bOut  : BOOL;
END_VAR

Methode FB_Sample.SampleMethod:

IF bIn THEN
    nIn  := 500;                 // => SA0037
    bOut := TRUE;
END_IF

 

SA0038: Lesezugriff auf Ausgabevariable

Funktion

Ermittelt Ausgangsvariablen (VAR_OUTPUT), auf die innerhalb der POU lesend zugegriffen wird.

Begründung

Nach 61131-3 ist es verboten, einen Ausgang innerhalb eines Bausteins zu lesen. Es weist darauf hin, dass der Ausgang nicht nur als Ausgang sondern gleichzeitig als temporäre Variable für Zwischenergebnisse verwendet wird. Eine solche Doppelverwendung sollte vermieden werden.

Wichtigkeit

Niedrig

Beispiel:

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_OUTPUT
    bOut    : BOOL;
    nOut    : INT;
END_VAR
VAR
    bLocal  : BOOL;
    nLocal  : INT;
END_VAR

Methode FB_Sample.SampleMethod:

IF bOut THEN                     // => SA0038
    bLocal := (nOut > 100);      // => SA0038
    nLocal := nOut;              // => SA0038
    nLocal := 2*nOut;            // => SA0038
END_IF

 

SA0040: Mögliche Division durch Null

Funktion

Ermittelt Codestellen, an denen möglicherweise durch Null dividiert wird.

Begründung

Eine Division durch 0 ist nicht erlaubt. Eine Variable durch die dividiert wird, sollte immer vorher auf 0 überprüft werden. Andernfalls kann es zu einer "Divide by Zero"-Exception zur Laufzeit kommen.

Wichtigkeit

Hoch

Beispiel:

PROGRAM MAIN
VAR CONSTANT
    cSample     : INT := 100;
END_VAR
VAR
    nQuotient1  : INT;
    nDividend1  : INT;
    nDivisor1   : INT;
 
    nQuotient2  : INT;
    nDividend2  : INT;
    nDivisor2   : INT;
END_VAR
nDivisor1  := cSample;
nQuotient1 := nDividend1/nDivisor1;                 // no error
 
nQuotient2 := nDividend2/nDivisor2;                 // => SA0040

 

SA0041: Möglicherweise schleifeninvarianter Code

Funktion

Ermittelt möglicherweise schleifeninvarianten Code, d.h. Code innerhalb einer FOR-, WHILE- oder REPEAT-Schleife, der bei jedem Schleifendurchlauf zum gleichen Ergebnis führt, also unnötigerweise immer wieder ausgeführt wird. Dabei werden nur Berechnungen berücksichtigt, keine einfachen Zuweisungen.

Begründung

Dies ist eine Performance-Warnung. Code, der in einer Schleife ausgeführt wird, aber in jedem Schleifendurchlauf das gleiche tut, kann außerhalb der Schleife durchgeführt werden.

Wichtigkeit

Mittel

Beispiel:

Im folgenden Beispiel wird SA0041 als Fehler/Warnung ausgegeben, da die Variablen nTest1 und nTest2 in der Schleife nicht verwendet werden.

PROGRAM MAIN
VAR
    nTest1    : INT := 5;
    nTest2    : INT := nTest1;
    nTest3    : INT;
    nTest4    : INT;
    nTest5    : INT;
    nTest6    : INT;
    nCounter  : INT;
END_VAR
FOR nCounter := 1 TO 100 BY 1 DO
    nTest3 := nTest1 + nTest2;   // => SA0041
    nTest4 := nTest3 + nCounter; // no loop-invariant code, because nTest3 and nCounter are used within loop
    nTest6 := nTest5;            // simple assignments are not regarded
END_FOR

 

SA0042: Verwendung unterschiedlicher Zugriffspfade

Funktion

Ermittelt die Verwendung unterschiedlicher Zugriffspfade für die gleiche Variable.

Begründung

Unterschiedlicher Zugriff auf das gleiche Element reduziert die Lesbarkeit und Wartbarkeit eines Programms. Wir empfehlen die konsequente Verwendung von {attribute 'qualified-only'} für Bibliotheken, globale Variablenlisten und Enumerationen. Dadurch wird der vollqualifizierte Zugriff erzwungen.

Wichtigkeit

Niedrig

Beispiele:

Im folgenden Beispiel wird SA0042 als Fehler/Warnung ausgegeben, da auf die globale Variable nGlobal einmal direkt und einmal über den GVL-Namensraum zugegriffen wird und da auf die Funktion CONCAT einmal direkt und einmal über den Bibliotheksnamensraum zugegriffen wird.

Globale Variablen:

VAR_GLOBAL
    nGlobal   : INT;
END_VAR

Programm MAIN:

PROGRAM MAIN
VAR
    sVar      : STRING;
END_VAR
nGlobal      := INT#2;                              // => SA0042
GVL.nGlobal  := INT#3;                              // => SA0042
 
sVar := CONCAT('ab', 'cd');                         // => SA0042
sVar := Tc2_Standard.CONCAT('ab', 'cd');            // => SA0042

 

SA0043: Verwendung einer globalen Variablen in nur 1 POU

Funktion

Ermittelt globale Variablen, die nur in einer einzigen POU verwendet werden.

Begründung

Eine globale Variable, die nur an einer Stelle verwendet wird, sollte auch an dieser einen Stelle deklariert sein.

Wichtigkeit

Mittel

PLCopen-Regel

CP26

Beispiel:

Die globale Variable nGlobal1 wird nur im Programm MAIN verwendet.

Globale Variablen:

VAR_GLOBAL
    nGlobal1  : INT;             // => SA0043
    nGlobal2  : INT;
END_VAR

Programm SubProgram:

nGlobal2 := 123;

Programm MAIN:

SubProgram();
 
nGlobal1 := nGlobal2; 

 

SA0044: Deklarationen mit Schnittstellenreferenz

Funktion

Ermittelt Deklarationen mit REFERENCE TO <Schnittstelle> und Deklarationen von VAR_IN_OUT-Variablen mit dem Typ einer Schnittstelle (implizit über REFERENCE TO realisiert).

Begründung

Ein Schnittstellentyp ist immer implizit eine Referenz auf eine Instanz eines Funktionsbausteins, der diese Schnittstelle implementiert. Eine Referenz auf eine Schnittstelle ist demnach eine Referenz auf eine Referenz und kann zu sehr unerwünschtem Verhalten führen.

Wichtigkeit

Hoch

Beispiele:

I_Sample ist eine im Projekt definierte Schnittstelle.

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    iInput      : I_Sample;
END_VAR
VAR_OUTPUT
    iOutput     : I_Sample;
END_VAR
VAR_IN_OUT
    iInOut1     : I_Sample;              // => SA0044

    {attribute 'analysis' := '-44'}
    iInOut2     : I_Sample;              // no error SA0044 because rule is deactivated via attribute
END_VAR

Programm MAIN:

PROGRAM MAIN 
VAR
    fbSample    : FB_Sample;
    iSample     : I_Sample;
    refItf      : REFERENCE TO I_Sample; // => SA0044
END_VAR

 

SA0019: Implizite Pointer-Konvertierungen

Funktion

Ermittelt implizit erzeugte Pointer-Datentyp-Konvertierungen.

Begründung

Pointer sind in TwinCAT nicht streng getypt und können einander beliebig zugewiesen werden. Dies wird häufig genutzt und deswegen auch nicht vom Compiler gemeldet. Allerdings kann es dadurch auch ungewollt zu unerwarteten Zugriffen kommen. Wenn ein POINTER TO BYTE einem POINTER TO DWORD zugewiesen wird, ist es möglich, dass über den letzten Pointer ungewollt Speicher überschrieben wird. Lassen Sie diese Regel deshalb in jedem Fall prüfen und unterdrücken Sie die Meldung nur in den Fällen, in denen Sie bewusst anders getypt auf einen Wert zugreifen wollen.

Implizite Datentyp-Konvertierungen werden mit einer anderen Meldung gemeldet.

Ausnahme

BOOL ↔ BIT

Wichtigkeit

Hoch

PLCopen-Regel

CP25

Beispiele:

PROGRAM MAIN 
VAR
    nInt   : INT;
    nByte  : BYTE;
 
    pInt   : POINTER TO INT;
    pByte  : POINTER TO BYTE;
END_VAR
pInt  := ADR(nInt);
pByte := ADR(nByte);
 
pInt  := ADR(nByte);             // => SA0019
pByte := ADR(nInt);              // => SA0019
 
pInt  := pByte;                  // => SA0019
pByte := pInt;                   // => SA0019

 

SA0130: Implizite erweiternde Konvertierungen

Funktion

Ermittelt implizit durchgeführte Konvertierungen von kleineren auf größere Datentypen.

Begründung

Der Compiler erlaubt jegliche Zuweisungen von unterschiedlichen Typen, wenn der Wertebereich des Quelltyps vollständig im Wertebereich des Zieltyps enthalten ist. Allerdings baut der Compiler eine Konvertierung so spät wie möglich in den Code ein. Bei einer Zuweisung der folgenden Art:

nLINT := nDINT * nDINT;

führt der Compiler die implizite Konvertierung erst nach der Multiplikation durch:

nLINT := TO_LINT(nDINT * nDINT);

Ein Überlauf wird daher abgeschnitten. Wenn Sie das verhindern wollen, können Sie die Konvertierung bereits für die Elemente durchführen lassen:

nLINT := TO_LINT(nDINT) * TO_LINT(nDINT);

Es kann daher sinnvoll sein, sich Stellen melden zu lassen, an denen der Compiler implizite Konvertierungen einbaut, um zu prüfen, ob diese genau so gewollt sind. Außerdem können explizite Konvertierungen zu besserer Portierbarkeit auf andere Systeme dienen, wenn diese restriktivere Typprüfungen haben.

Ausnahme

BOOL ↔ BIT

Wichtigkeit

Niedrig

Beispiele:

PROGRAM MAIN 
VAR
    nBYTE    : BYTE;
    nUSINT   : USINT;
    nUINT    : UINT;
    nINT     : INT;
    nUDINT   : UDINT;
    nDINT    : DINT;
    nULINT   : ULINT;
    nLINT    : LINT;
    nLWORD   : LWORD;
    fLREAL   : LREAL;
END_VAR
nLINT   := nINT;                 // => SA0130
nULINT  := nBYTE;                // => SA0130
nLWORD  := nUDINT;               // => SA0130
fLREAL  := nBYTE;                // => SA0130
nDINT   := nUINT;                // => SA0130
 
nBYTE.5 := FALSE;                // no error (BIT-BOOL-conversion)

 

SA0131: Implizite einengende Konvertierungen

Funktion

Ermittelt implizit durchgeführte Konvertierungen von größeren auf kleinere Datentypen.

Ausnahme

BOOL ↔ BIT

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 8:

Diese Meldung ist mittlerweile obsolet, weil sie bereits vom Compiler als Warnung gemeldet wird.

Beispiel:

PROGRAM MAIN
VAR
    fREAL    : REAL;
    fLREAL   : LREAL;
END_VAR
fREAL   := fLREAL;               // => SA0131
 
nBYTE.5 := FALSE;                // no error (BIT-BOOL-conversion)

 

SA0132: Implizite vorzeichenbehaftete/vorzeichenlose Konvertierungen

Funktion

Ermittelt implizit durchgeführte Konvertierungen von vorzeichenbehafteten auf vorzeichenlose Datentypen oder umgekehrt.

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 9:

Diese Meldung ist mittlerweile obsolet, weil sie bereits vom Compiler als Warnung gemeldet wird.

Beispiele:

PROGRAM MAIN
VAR
    nBYTE    : BYTE;
    nUDINT   : UDINT;
    nULINT   : ULINT;
    nWORD    : WORD;
    nLWORD   : LWORD;
    nSINT    : SINT;
    nINT     : INT;
    nDINT    : DINT;
    nLINT    : LINT;
END_VAR
nLINT   := nULINT;               // => SA0132
nUDINT  := nDINT;                // => SA0132
nSINT   := nBYTE;                // => SA0132
nWORD   := nINT;                 // => SA0132
nLWORD  := nSINT;                // => SA0132

 

SA0133: Explizite einschränkende Konvertierungen

Funktion

Ermittelt explizit durchgeführte Konvertierungen von einem größeren auf einen kleineren Datentyp.

Begründung

Eine große Zahl von Typkonvertierungen kann bedeuten, dass falsche Datentypen für Variablen gewählt wurden. Es gibt daher Programmierrichtlinien, die eine explizite Begründung für Datentypkonvertierungen fordern.

Wichtigkeit

Niedrig

Beispiele:

PROGRAM MAIN
VAR
    nSINT    : SINT;
    nDINT    : DINT;
    nLINT    : LINT;
    nBYTE    : BYTE;
    nUINT    : UINT;
    nDWORD   : DWORD;
    nLWORD   : LWORD;
    fREAL    : REAL;
    fLREAL   : LREAL;
END_VAR
nSINT := LINT_TO_SINT(nLINT);    // => SA0133
nBYTE := DINT_TO_BYTE(nDINT);    // => SA0133
nSINT := DWORD_TO_SINT(nDWORD);  // => SA0133
nUINT := LREAL_TO_UINT(fLREAL);  // => SA0133
fREAL := LWORD_TO_REAL(nLWORD);  // => SA0133

 

SA0134: Explizite vorzeichenbehaftete/vorzeichenlose Konvertierungen

Funktion

Ermittelt explizit durchgeführte Konvertierungen von vorzeichenbehafteten auf vorzeichenlose Datentypen oder umgekehrt.

Begründung

Ein übermäßiger Gebrauch von Typkonvertierungen kann bedeuten, dass falsche Datentypen für Variablen gewählt wurden. Es gibt daher Programmierrichtlinien, die eine explizite Begründung für Datentypkonvertierungen fordern.

Wichtigkeit

Niedrig

Beispiele:

PROGRAM MAIN
VAR
    nBYTE    : BYTE;
    nUDINT   : UDINT;
    nULINT   : ULINT;
    nWORD    : WORD;
    nLWORD   : LWORD;
    nSINT    : SINT;
    nINT     : INT;
    nDINT    : DINT;
    nLINT    : LINT;
END_VAR
nLINT  := ULINT_TO_LINT(nULINT); // => SA0134
nUDINT := DINT_TO_UDINT(nDINT);  // => SA0134
nSINT  := BYTE_TO_SINT(nBYTE);   // => SA0134
nWORD  := INT_TO_WORD(nINT);     // => SA0134
nLWORD := SINT_TO_LWORD(nSINT);  // => SA0134

 

SA0005: Ungültige Adressen und Datentypen

Funktion

Ermittelt ungültige Adress- und Datentypspezifikationen.

Für Adressen sind die folgenden Größenpräfixe gültig. Abweichungen davon führen zu einer ungültigen Adressspezifikation.

  • X für BOOL
  • B für 1-Byte-Datentypen
  • W für 2-Byte-Datentypen
  • D für 4-Byte-Datentypen

Begründung

Variablen, die auf direkten Adressen liegen, sollten möglichst mit einer Adresse assoziiert werden, die ihrer Datentypbreite entspricht. Es kann für den Leser des Codes zur Verwirrung führen, wenn beispielsweise ein DWORD auf eine BYTE-Adresse gelegt wird.

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 10:

Mit den empfohlenen Platzhaltern %I* oder %Q* wird eine flexible und optimierte Adressierung von TwinCAT automatisch durchgeführt.

Beispiele:

PROGRAM MAIN 
VAR
    nOK   AT%QW0   : INT;
    bOK   AT%QX5.0 : BOOL;
 
    nNOK  AT%QD10  : INT;        // => SA0005
    bNOK  AT%QB15  : BOOL;       // => SA0005
END_VAR

 

SA0047: Zugriff auf direkte Adressen

Funktion

Ermittelt direkte Adresszugriffe im Implementierungscode.

Begründung

Symbolische Programmierung ist immer zu bevorzugen: Eine Variable hat einen Namen, der auch eine Bedeutung tragen kann. Einer Adresse ist nicht ansehbar, wofür diese verwendet wird.

Wichtigkeit

Hoch

PLCopen-Regel

N1/CP1

Beispiele:

PROGRAM MAIN
VAR
    bBOOL  : BOOL;
    nBYTE  : BYTE;
    nWORD  : WORD;
    nDWORD : DWORD;
END_VAR
bBOOL  := %IX0.0;                // => SA0047
%QX0.0 := bBOOL;                 // => SA0047
%QW2   := nWORD;                 // => SA0047
%QD4   := nDWORD;                // => SA0047
%MX0.1 := bBOOL;                 // => SA0047
%MB1   := nBYTE;                 // => SA0047
%MD4   := nDWORD;                // => SA0047

 

SA0048: AT-Deklarationen auf direkte Adressen

Funktion

Ermittelt AT-Deklarationen auf direkte Adressen.

Begründung

Die Verwendung von direkten Adressen im Code ist eine Fehlerquelle und führt zu schlechterer Lesbarkeit und Wartbarkeit des Codes.

Daher wird die Verwendung der Platzhalter %I* oder %Q* empfohlen, bei denen TwinCAT eine flexible und optimierte Adressierung automatisch durchführt.

Wichtigkeit

Hoch

PLCopen-Regel

N1/CP1

Beispiele:

PROGRAMM MAIN
VAR
    b1    AT%IX0.0 : BOOL;       // => SA0048
    b2    AT%I*    : BOOL;       // no error
END_VAR

 

SA0051: Vergleichsoperationen auf BOOL-Variablen

Funktion

Ermittelt Vergleichsoperationen auf Variablen vom Typ BOOL.

Begründung

TwinCAT erlaubt solche Vergleiche, diese sind aber zumindest sehr unüblich und können verwirrend sein. Die Norm IEC-61131-3 sieht diese Vergleiche nicht vor, daher sollten Sie sie vermeiden.

Wichtigkeit

Mittel

Beispiel:

PROGRAM MAIN
VAR
    b1       : BOOL;
    b2       : BOOL;
    bResult  : BOOL;
END_VAR
bResult := (b1 > b2);            // => SA0051 
bResult := NOT b1 AND b2;
bResult := b1 XOR b2;

 

SA0052: Unübliche Schiebeoperation

Funktion

Ermittelt Schiebeoperationen (Bit-Shift) auf vorzeichenbehaftete Variablen. Die Norm IEC 61131-3 erlaubt allerdings nur Schiebeoperationen auf Bitfelder. Sehen Sie hierzu auch die strikte Regel SA0147.

Begründung

TwinCAT erlaubt Schiebeoperationen auf vorzeichenbehafteten Datentypen. Diese Operationen sind aber unüblich und können verwirrend sein. Die Norm IEC-61131-3 sieht solche Operationen nicht vor, daher sollten Sie sie vermeiden.

Ausnahme

Im Falle von Schiebeoperationen auf Bitfeld-Datentypen (Byte, DWORD, LWORD, WORD) wird kein Fehler SA0052 ausgegeben.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    nINT   : INT;
    nDINT  : DINT;
    nULINT : ULINT;
    nSINT  : SINT;
    nUSINT : USINT;
    nLINT  : LINT;
 
    nDWORD : DWORD;
    nBYTE  : BYTE;
END_VAR
nINT   := SHL(nINT, BYTE#2);     // => SA0052 
nDINT  := SHR(nDINT, BYTE#4);    // => SA0052
nULINT := ROL(nULINT, BYTE#1);   // no error because this is an unsigned data type
nSINT  := ROL(nSINT, BYTE#2);    // => SA0052
nUSINT := ROR(nUSINT, BYTE#3);   // no error because this is an unsigned data type
nLINT  := ROR(nLINT, BYTE#2);    // => SA0052
 
nDWORD := SHL(nDWORD, BYTE#3);   // no error because DWORD is a bit field data type
nBYTE  := SHR(nBYTE, BYTE#1);    // no error because BYTE is a bit field data type 

 

SA0053: Zu große bitweise Verschiebung

Funktion

Ermittelt bei bitweiser Verschiebung (Bitverschiebung/Bit-Shift) von Operanden, ob dessen Datentyp-Breite überschritten wurde.

Begründung

Wenn eine Verschiebeoperation über die Datentypbreite hinausgeht, wird eine Konstante 0 erzeugt. Wenn eine Rotationsverschiebung über die Datentypbreite hinausgeht, dann ist das schwer zu lesen und der Rotationswert sollte deswegen gekürzt werden.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    nBYTE  : BYTE;
    nWORD  : WORD;
    nDWORD : DWORD;
    nLWORD : LWORD;
END_VAR
nBYTE  := SHR(nBYTE, BYTE#8);    // => SA0053 
nWORD  := SHL(nWORD, BYTE#45);   // => SA0053
nDWORD := ROR(nDWORD, BYTE#78);  // => SA0053
nLWORD := ROL(nLWORD, BYTE#111); // => SA0053

nBYTE  := SHR(nBYTE, BYTE#7);    // no error
nWORD  := SHL(nWORD, BYTE#15);   // no error

 

SA0054: Vergleich von REAL/LREAL auf Gleichheit/Ungleichheit

Funktion

Ermittelt, wo die Vergleichsoperatoren = (Gleichzeit) und <> (Ungleichheit) Operanden vom Typ REAL oder LREAL vergleichen.

Begründung

REAL/LREAL-Werte werden als Gleitzahlen nach dem Standard IEEE 754 implementiert. Dieser Standard bringt es mit sich, dass bestimmte scheinbar einfache Dezimalzahlen nicht exakt dargestellt werden können. Das hat zur Folge, dass es für dieselbe Dezimalzahl unterschiedliche Repräsentationen als LREAL geben kann.

Beispiel:

fLREAL_11 := 1.1;
fLREAL_33 := 3.3;
fLREAL_a := fLREAL_11 + fLREAL_11;
fLREAL_b := fLREAL_33 – fLREAL_11;
bTest := fLREAL_a = fLREAL_b;

bTest wird in diesem Fall FALSE liefern, auch wenn die Variablen fLREAL_a und fLREAL_b beide den Monitoring-Wert “2.2” liefern. Das ist kein Fehler des Compilers, sondern eine Eigenschaft der Gleitkomma-Einheiten aller üblichen Prozessoren. Vermeiden können Sie das, indem Sie einen Mindestwert angeben, um den sich die Werte unterscheiden dürfen:

bTest := ABS(fLREAL_a – fLREAL_b) < 0.1;

Ausnahme

Ein Vergleich mit 0.0 wird nicht von dieser Analyse gemeldet. Für die 0 gibt es im Standard IEEE 754 eine exakte Darstellung und daher funktioniert der Vergleich normalerweise wie erwartet. Für eine bessere Performance ist es daher sinnvoll, hier einen direkten Vergleich zuzulassen.

Wichtigkeit

Hoch

PLCopen-Regel

CP54

Beispiele:

PROGRAM MAIN
VAR
    fREAL1  : REAL;
    fREAL2  : REAL;
    fLREAL1 : LREAL;
    fLREAL2 : LREAL;
    bResult : BOOL;
END_VAR
bResult := (fREAL1 = fREAL1);    // => SA0054 
bResult := (fREAL1 = fREAL2);    // => SA0054
bResult := (fREAL1 <> fREAL2);   // => SA0054
bResult := (fLREAL1 = fLREAL1);  // => SA0054
bResult := (fLREAL1 = fLREAL2);  // => SA0054
bResult := (fLREAL2 <> fLREAL2); // => SA0054
 
bResult := (fREAL1 > fREAL2);    // no error
bResult := (fLREAL1 < fLREAL2);  // no error

 

SA0055: Unnötige Vergleichsoperationen von vorzeichenlosen Operanden

Funktion

Ermittelt unnötige Vergleiche mit vorzeichenlosen Operanden. Ein vorzeichenloser Datentyp ist nie kleiner Null.

Begründung

Ein mit dieser Prüfung aufgedeckter Vergleich liefert ein konstantes Ergebnis und das deutet auf einen Fehler im Code hin.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    nBYTE   : BYTE;
    nWORD   : WORD;
    nDWORD  : DWORD;
    nLWORD  : LWORD;
    nUSINT  : USINT;
    nUINT   : UINT;
    nUDINT  : UDINT;
    nULINT  : ULINT;
 
    nSINT   : SINT;
    nINT    : INT;
    nDINT   : DINT;
    nLINT   : LINT;
 
    bResult : BOOL;
END_VAR
bResult := (nBYTE >= BYTE#0);    // => SA0055 
bResult := (nWORD < WORD#0);     // => SA0055
bResult := (nDWORD >= DWORD#0);  // => SA0055
bResult := (nLWORD < LWORD#0);   // => SA0055
bResult := (nUSINT >= USINT#0);  // => SA0055
bResult := (nUINT < UINT#0);     // => SA0055
bResult := (nUDINT >= UDINT#0);  // => SA0055
bResult := (nULINT < ULINT#0);   // => SA0055
 
bResult := (nSINT < SINT#0);     // no error
bResult := (nINT < INT#0);       // no error
bResult := (nDINT < DINT#0);     // no error
bResult := (nLINT < LINT#0);     // no error 

 

SA0056: Konstante außerhalb des gültigen Bereichs

Funktion

Ermittelt Literale (Konstanten) außerhalb des für den Operator gültigen Bereichs.

Begründung

Die Meldung wird für Fälle ausgegeben, in denen eine Variable mit einer Konstanten verglichen wird, die außerhalb des Wertebereichs dieser Variablen liegt. Der Vergleich liefert dann konstant TRUE oder FALSE. Dies deutet auf einen Programmierfehler hin.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    nBYTE   : BYTE;
    nWORD   : WORD;
    nDWORD  : DWORD;
    nUSINT  : USINT;
    nUINT   : UINT;
    nUDINT  : UDINT;
 
    bResult : BOOL;
END_VAR
bResult := nBYTE >= 355;                            // => SA0056
bResult := nWORD > UDINT#70000;                     // => SA0056
bResult := nDWORD >= ULINT#4294967300;              // => SA0056
bResult := nUSINT > UINT#355;                       // => SA0056
bResult := nUINT >= UDINT#70000;                    // => SA0056
bResult := nUDINT > ULINT#4294967300;               // => SA0056

 

SA0057: Möglicher Verlust von Nachkommastellen

Funktion

Ermittelt Anweisungen mit möglichem Verlust von Dezimalstellen.

Begründung

Ein Codestück der folgenden Art:

nDINT := 1;
fREAL := TO_REAL(nDINT / DINT#2);

kann zu einer Fehlinterpretation führen. Diese Codezeile kann zu der Annahme führen, die Division würde als REAL-Operation durchgeführt und das Ergebnis würde in diesem Fall REAL#0.5 sein. Dies ist jedoch nicht der Fall, die Operation wird als Integer-Operation durchgeführt, das Ergebnis wird auf REAL gecastet und fREAL erhält den Wert REAL#0. Um dies zu vermeiden, sollten Sie durch einen Cast dafür sorgen, dass die Operation als REAL-Operation durchgeführt wird:

fREAL := TO_REAL(nDINT) / REAL#2;

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    fREAL : REAL;
    nDINT : DINT;
    nLINT : LINT;
END_VAR
nDINT := nDINT + DINT#11;
fREAL := DINT_TO_REAL(nDINT / DINT#3);              // => SA0057
fREAL := DINT_TO_REAL(nDINT) / 3.0;                 // no error
fREAL := DINT_TO_REAL(nDINT) / REAL#3.0;            // no error
 
nLINT := nLINT + LINT#13;
fREAL := LINT_TO_REAL(nLINT / LINT#7);              // => SA0057
fREAL := LINT_TO_REAL(nLINT) / 7.0;                 // no error
fREAL := LINT_TO_REAL(nLINT) / REAL#7.0;            // no error

 

SA0058: Operation auf Enumerationsvariablen

Funktion

Ermittelt Operationen auf Variablen vom Typ einer Enumeration. Zuweisungen sind erlaubt.

Begründung

Enumerationen sollten nicht als normale Integer-Werte verwendet werden. Alternativ kann ein Alias-Datentyp definiert oder einen Subrange-Typ verwendet werden.

Ausnahme

Wenn eine Enumeration mit dem Attribut {attribute 'strict'} gekennzeichnet ist, dann meldet bereits der Compiler eine solche Operation.

Wenn eine Enumeration mit Hilfe des Pragmaattributs {attribute 'flags'} als Flag deklariert ist, wird für Operationen mit AND, OR, NOT, XOR kein Fehler SA0058 ausgegeben.

Wichtigkeit

Mittel

Beispiel 1:

Enumeration E_Color:

TYPE E_Color :
(
    eRed   := 1,
    eBlue  := 2,
    eGreen := 3
);
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    nVar   : INT;
    eColor : E_Color;
END_VAR
eColor := E_Color.eGreen;                           // no error
eColor := E_Color.eGreen + 1;                       // => SA0058
nVar   := E_Color.eBlue / 2;                        // => SA0058
nVar   := E_Color.eGreen + E_Color.eRed;            // => SA0058

Beispiel 2:

Enumeration E_State mit Attribut 'flags':

{attribute 'flags'}
TYPE E_State :
(
    eUnknown := 16#00000001,
    eStopped := 16#00000002,
    eRunning := 16#00000004
) DWORD;
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    nFlags : DWORD;
    nState : DWORD;
END_VAR
IF (nFlags AND E_State.eUnknown) <> DWORD#0 THEN    // no error
    nState := nState AND E_State.eUnknown;          // no error
 
ELSIF (nFlags OR E_State.eStopped) <> DWORD#0 THEN  // no error
    nState := nState OR E_State.eRunning;           // no error
END_IF

 

SA0059: Vergleichsoperationen, die immer TRUE oder FALSE liefern

Funktion

Ermittelt Vergleiche mit Literalen, die immer das Ergebnis TRUE bzw. FALSE haben und die bereits während der Kompilierung ausgewertet werden können.

Begründung

Eine Operation, die konstant TRUE oder FALSE liefert, ist ein Hinweis auf einen Programmierfehler.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    nBYTE   : BYTE;
    nWORD   : WORD;
    nDWORD  : DWORD;
    nLWORD  : LWORD;
    nUSINT  : USINT;
    nUINT   : UINT;
    nUDINT  : UDINT;
    nULINT  : ULINT;
    nSINT   : SINT;
    nINT    : INT;
    nDINT   : DINT;
    nLINT   : LINT;
    bResult : BOOL;
END_VAR
bResult := nBYTE  <= 255;                           // => SA0059
bResult := nBYTE  <= BYTE#255;                      // => SA0059
bResult := nWORD  <= WORD#65535;                    // => SA0059
bResult := nDWORD <= DWORD#4294967295;              // => SA0059
bResult := nLWORD <= LWORD#18446744073709551615;    // => SA0059
bResult := nUSINT <= USINT#255;                     // => SA0059
bResult := nUINT  <= UINT#65535;                    // => SA0059
bResult := nUDINT <= UDINT#4294967295;              // => SA0059
bResult := nULINT <= ULINT#18446744073709551615;    // => SA0059
bResult := nSINT  >= -128;                          // => SA0059
bResult := nSINT  >= SINT#-128;                     // => SA0059
bResult := nINT   >= INT#-32768;                    // => SA0059
bResult := nDINT  >= DINT#-2147483648;              // => SA0059
bResult := nLINT  >= LINT#-9223372036854775808;     // => SA0059

 

SA0060: Null als ungültiger Operand

Funktion

Ermittelt Operationen, in denen ein Operand mit Wert 0 zu einer ungültigen und nicht sinnvollen Operation führt.

Begründung

Ein solcher Ausdruck kann auf einen Programmierfehler hindeuten. In jedem Fall kostet er unnötig Laufzeit.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    nBYTE   : BYTE;
    nWORD   : WORD;
    nDWORD  : DWORD;
    nLWORD  : LWORD;
END_VAR
nBYTE  := nBYTE  + 0;            // => SA0060
nWORD  := nWORD  - WORD#0;       // => SA0060
nDWORD := nDWORD * DWORD#0;      // => SA0060
nLWORD := nLWORD / 0;            // Compile error: Division by zero

 

SA0061: Unübliche Operation auf Pointer

Funktion

Ermittelt Operationen auf Variablen vom Typ POINTER TO, die nicht = (Gleichheit), <> (Ungleichheit), + (Addition) oder ADR sind.

Begründung

In TwinCAT ist Pointer-Arithmetik grundsätzlich erlaubt und kann auch sinnvoll eingesetzt werden. Als übliche Operation auf Pointer wird daher die Addition eines Pointers mit einem Integerwert eingestuft. Damit ist es möglich, mit Hilfe eines Pointers ein Array mit variabler Länge zu bearbeiten. Alle anderen (unüblichen) Operationen mit Pointer werden mit SA0061 gemeldet.

Wichtigkeit

Hoch

PLCopen-Regel

E2/E3

Beispiele:

PROGRAM MAIN
VAR
    pINT  : POINTER TO INT;
    nVar  : INT;
END_VAR
pINT := ADR(nVar);               // no error
pINT := pINT * DWORD#5;          // => SA0061
pINT := pINT / DWORD#2;          // => SA0061
pINT := pINT MOD DWORD#3;        // => SA0061
pINT := pINT + DWORD#1;          // no error
pINT := pINT - DWORD#1;          // => SA0061

 

SA0062: Verwendung von TRUE oder FALSE in Ausdrücken

Funktion

Ermittelt die Verwendung der Literale TRUE bzw. FALSE in Ausdrücken (z.B. b2 := b1 AND NOT TRUE oder IF (bVar = FALSE) THEN).

Begründung

Ein solcher Ausdruck ist offensichtlich unnötig und kann auf einen Fehler hindeuten. In jedem Fall belastet ein solcher Ausdruck unnötig die Lesbarkeit und ggf. auch die Laufzeit.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    bVar1  : BOOL;
    bVar2  : BOOL;
END_VAR
bVar1 := bVar1 AND NOT TRUE;     // => SA0062
bVar2 := bVar1 OR TRUE;          // => SA0062
bVar2 := bVar1 OR NOT FALSE;     // => SA0062
bVar2 := bVar1 AND FALSE;        // => SA0062
 
IF (bVar = FALSE) THEN           // => SA0062
    ;
END_IF
 
IF NOT bVar THEN                 // => no error
    ;
END_IF

 

SA0063: Möglicherweise nicht 16-bitkompatible Operationen

Funktion

Ermittelt 16-Bit-Operationen mit Zwischenergebnissen. Hintergrund: Auf 16-Bit-Systemen können 32-Bit-Zwischenergebnisse abgeschnitten werden.

Begründung

Diese Meldung soll in dem sehr seltenen Fall vor Problemen schützen, wenn Code geschrieben wird, der sowohl auf einem 16-Bit-Prozessor als auch auf einem 32-Bi- Prozessor laufen soll.

Wichtigkeit

Niedrig

Beispiel:

(nVar+10) kann 16 Bit überschreiten.

PROGRAM MAIN
VAR
    nVar  : INT;
END_VAR
nVar := (nVar + 10) / 2;         // => SA0063

 

SA0064: Addition eines Pointers

Funktion

Ermittelt alle Additionen von Pointern.

Begründung

In TwinCAT ist Pointer-Arithmetik grundsätzlich erlaubt und kann auch sinnvoll eingesetzt werden. Allerdings ist es auch eine Fehlerquelle. Deswegen gibt es Programmiervorschriften, die Pointer-Arithmetik grundsätzlich verbieten. Eine solche Vorschrift kann mit diesem Test überprüft werden.

Wichtigkeit

Mittel

Beispiele:

PROGRAM MAIN
VAR
    aTest : ARRAY[0..10] OF INT;
    pINT  : POINTER TO INT;
    nIdx  : INT;
END_VAR
pINT  := ADR(aTest[0]);
pINT^ := 0;
pINT  := ADR(aTest) + SIZEOF(INT);                  // => SA0064
pINT^ := 1;
pINT  := ADR(aTest) + 6;                            // => SA0064
pINT  := ADR(aTest[10]);
 
FOR nIdx := 0 TO 10 DO
    pINT^ := nIdx;
    pINT  := pINT + 2;                              // => SA0064
END_FOR

 

SA0065: Pointer-Addition passt nicht zur Basisgröße

Funktion

Ermittelt Pointer-Additionen, bei denen der zu addierende Wert nicht zur Basis-Datengröße des Pointers passt. Nur Literale der Basisgröße dürfen addiert werden. Es dürfen auch keine Multiplikationen der Basisgröße addiert werden.

Begründung

In TwinCAT (im Gegensatz zu C und C++) wird bei einer Addition eines Pointers mit einem Integerwert nur dieser Integerwert als Anzahl der Bytes addiert, und nicht der Integerwert mit der Basisgröße multipliziert. Zum Beispiel in ST:

pINT := ADR(array_of_int[0]);
pINT := pINT + 2 ; // in TwinCAT zeigt pINT anschließend auf array_of_int[1]

Dieser Code würde in C anders funktionieren:

short* pShort
pShort = &(array_of_short[0])
pShort = pShort + 2; // in C zeigt pShort anschließend auf array_of_short[2]

Daher sollte in TwinCAT immer ein Vielfaches der Basisgröße des Pointers zu einem Pointer addiert werden. Andernfalls zeigt der Pointer möglicherweise auf einen nicht-alignten Speicher, was (je nach Prozessor) beim Zugriff zu einer "Alignment"-Exception führen kann.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    pUDINT : POINTER TO UDINT;
    nVar   : UDINT;
    pREAL  : POINTER TO REAL;
    fVar   : REAL;
END_VAR
pUDINT := ADR(nVar) + 4;
pUDINT := ADR(nVar) + (2 + 2);
pUDINT := ADR(nVar) + SIZEOF(UDINT);
pUDINT := ADR(nVar) + 3;                            // => SA0065
pUDINT := ADR(nVar) + 2*SIZEOF(UDINT);              // => SA0065
pUDINT := ADR(nVar) + (3 + 2);                      // => SA0065
 
pREAL  := ADR(fVar);
pREAL  := pREAL + 4;
pREAL  := pREAL + (2 + 2);
pREAL  := pREAL + SIZEOF(REAL);
pREAL  := pREAL + 1;                                // => SA0065
pREAL  := pREAL + 2;                                // => SA0065
pREAL  := pREAL + 3;                                // => SA0065
pREAL  := pREAL + (SIZEOF(REAL) - 1);               // => SA0065
pREAL  := pREAL + (1 + 4);                          // => SA0065

 

SA0066: Verwendung von Zwischenergebnissen

Funktion

Ermittelt Verwendungen von Zwischenergebnissen in Anweisungen mit einem Datentyp, der kleiner als die Registergröße ist. Der implizite Cast in diesem Fall führt gegebenenfalls zu unerwünschten Ergebnissen.

Begründung

TwinCAT führt aus Performancegründen Operationen auf der Registerbreite des Prozessors aus. Zwischenergebnisse werden nicht abgeschnitten! Das kann zu Fehlinterpretationen führen, wie im folgenden Fall:

usintTest := 0;
bError := usintTest - 1 <> 255;

In TwinCAT ist bError in diesem Fall TRUE, weil die Operation usintTest - 1 typischerweise als 32-Bit-Operation ausgeführt wird und das Ergebnis nicht auf die Größe von Byte gecastet wird. Im Register steht dann der Wert 16#ffffffff und dieser ist ungleich 255.Um dies zu umgehen müssen Sie das Zwischenergebnis explizit casten:

bError := TO_USINT(usintTest - 1) <> 255;

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 11:

Wenn diese Meldung aktiviert ist, werden sehr viele eher unproblematische Stellen im Code gemeldet werden. Ein Problem kann zwar nur entstehen, wenn die Operation einen Überlauf oder Unterlauf im Datentyp produziert, die statische Analyse kann dies aber für die einzelnen Stellen nicht differenziert erkennen.

Wenn Sie an allen gemeldeten Stellen einen expliziten Typcast einbauen, dann wird der Code deutlich langsamer und unleserlicher!

Beispiel:

PROGRAM MAIN
VAR
    nBYTE   : BYTE;
    nDINT   : DINT;
    nLINT   : LINT;
    bResult : BOOL;
END_VAR
//=====================================================================================================
// type size smaller than register size
// use of temporary result + implicit casting => SA0066
bResult := ((nBYTE - 1) <> 255);                           // => SA0066
 
// correcting this code by explicit cast so that the type size is equal to or bigger than register size
bResult := ((BYTE_TO_LINT(nBYTE) - 1) <> 255);             // no error
bResult := ((BYTE_TO_LINT(nBYTE) - LINT#1) <> LINT#255);   // no error
 
//=====================================================================================================
// result depends on solution platform
bResult := ((nDINT - 1) <> 255);                           // no error on x86 solution platform
                                                           // => SA0066 on x64 solution platform
 
// correcting this code by explicit cast so that the type size is equal to or bigger than register size
bResult := ((DINT_TO_LINT(nDINT) - LINT#1) <> LINT#255);   // no error
 
//=====================================================================================================
// type size equal to or bigger than register size
// use of temporary result and no implicit casting => no error
bResult := ((nLINT - 1) <> 255);                           // no error
 
//====================================================================================================

SA0072: Ungültige Verwendung einer Zählervariablen

Funktion

Ermittelt Schreibzugriffe auf eine Zählervariablen innerhalb einer FOR-Schleife.

Begründung

Eine Manipulation der Zählervariablen in einer FOR-Schleife kann leicht zu einer Endlosschleife führen. Um die Ausführung der Schleife für bestimmte Werte der Zählervariablen zu unterbinden, arbeiten Sie mit CONTINUE oder einfach mit einem IF.

Wichtigkeit

Hoch

PLCopen-Regel

L12

Beispiel:

PROGRAM MAIN
VAR_TEMP
    nIndex  : INT;
END_VAR
VAR
    aSample : ARRAY[1..10] OF INT;
    nLocal  : INT;
END_VAR
FOR nIndex := 1 TO 10 BY 1 DO
    aSample[nIndex] := nIndex;                      // no error
    nLocal          := nIndex;                      // no error
 
    nIndex          := nIndex - 1;                  // => SA0072
    nIndex          := nIndex + 1;                  // => SA0072
    nIndex          := nLocal;                      // => SA0072
END_FOR

 

SA0073: Verwendung einer nicht-temporären Zählervariablen

Funktion

Ermittelt die Verwendung von nicht-temporären Variablen in FOR-Schleifen.

Begründung

Dies ist eine Performance-Warnung. Eine Zählervariable wird in jedem Fall bei jedem Aufruf eines Programmierbausteins initialisiert. Sie können eine solche Variable als temporäre Variable (VAR_TEMP) anlegen, ein Zugriff darauf ist unter Umständen schneller und die Variable belegt keinen dauerhaften Speicherplatz.

Wichtigkeit

Mittel

PLCopen-Regel

CP21/L13

Beispiel:

PROGRAM MAIN
VAR
    nIndex  : INT;
    nSum    : INT;
END_VAR
FOR nIndex := 1 TO 10 BY 1 DO    // => SA0073 
    nSum := nSum + nIndex;
END_FOR

 

SA0080: Schleifenindexvariable für Arrayindex überschreitet Array-Bereich

Funktion

Ermittelt FOR-Anweisungen, bei denen die Indexvariable für den Zugriff auf einen Array-Index verwendet wird und den Bereich des Array-Index überschreitet.

Begründung

Arrays werden typischerweise in FOR-Schleifen bearbeitet. Anfangs- und Endwert der Zählervariable sollte dabei typischerweise mit der Unter- und Obergrenze des Arrays übereinstimmen oder zumindest diese nicht überschreiten. Hier wird eine typische Fehlerursache erkannt, wenn Arraygrenzen geändert werden und nicht sorgfältig mit Konstanten gearbeitet wird, oder wenn aus Versehen in der FOR-Schleife ein anderer Wert verwendet wird als bei der Array-Deklaration.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR CONSTANT
    c1      : INT := 0;
END_VAR
VAR
    nIndex1 : INT;
    nIndex2 : INT;
    nIndex3 : INT;
    a1      : ARRAY[1..100] OF INT;
    a2      : ARRAY[1..9,1..9,1..9] OF INT;
    a3      : ARRAY[0..99] OF INT;
END_VAR
// 1 violation of the rule (lower range is exeeded) => 1 error SA0080
FOR nIndex1 := c1 TO INT#100 BY INT#1 DO
    a1[nIndex1] := nIndex1;                         // => SA0080
END_FOR
 
// 6 violations (lower and upper range is exeeded for each array dimension) => 3 errors SA0080
FOR nIndex2 := INT#0 TO INT#10 BY INT#1 DO
    a2[nIndex2, nIndex2, nIndex2] := nIndex2;       // => SA0080
END_FOR
 
// 1 violation (upper range is exeeded by the end result of the index), expressions on index are not evaluated => no error
FOR nIndex3 := INT#0 TO INT#50 BY INT#1 DO
    a3[nIndex3 * INT#2] := nIndex3;                 // no error
END_FOR

 

SA0081: Obergrenze ist kein konstanter Wert

Funktion

Ermittelt FOR-Anweisungen, bei denen die Obergrenze nicht mit einem konstanten Wert definiert ist.

Begründung

Wenn die Obergrenze einer Schleife ein variabler Wert ist, dann lässt sich nicht mehr erkennen, wie oft eine Schleife ausgeführt wird. Dies kann zur Laufzeit zu gravierenden Problemen führen, im schlimmsten Fall zu einer Endlosschleife.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR CONSTANT
    cMax   : INT := 10;
END_VAR
VAR
    nIndex : INT;
    nVar   : INT;
    nMax1  : INT := 10;
    nMax2  : INT := 10;
END_VAR
FOR nIndex := 0 TO 10 DO         // no error
    nVar := nIndex;
END_FOR
 
FOR nIndex := 0 TO cMax DO       // no error
    nVar := nIndex;
END_FOR

FOR nIndex := 0 TO nMax1 DO      // => SA0081
    nVar := nIndex;
END_FOR

FOR nIndex := 0 TO nMax2 DO      // => SA0081
    nVar := nIndex;
 
    IF nVar = 10 THEN
        nMax2 := 50;
    END_IF
END_FOR

 

SA0075: Fehlendes ELSE

Funktion

Ermittelt CASE-Anweisungen ohne ELSE-Zweig.

Begründung

Defensive Programmierung fordert das Vorhandensein eines ELSE in jeder CASE-Anweisung. Wenn im ELSE-Fall nichts zu tun ist, dann sollten Sie dies durch einen Kommentar kennzeichnen. Dem Leser des Codes ist dann klar, dass der Fall nicht einfach vergessen wurde.

Ausnahme

Ein fehlender ELSE-Zweig wird nicht als fehlend berichtet, wenn in der CASE-Anweisung eine Enumeration verwendet wird, die mit dem Attribut 'strict' deklariert ist, und wenn in dieser CASE-Anweisung alle Enumerationskonstanten aufgeführt sind.

Wichtigkeit

Niedrig

PLCopen-Regel

L17

Beispiel:

{attribute 'qualified_only'}
{attribute 'strict'}
{attribute 'to_string'}
TYPE E_Sample :
(
    eNull,
    eOne,
    eTwo
);
END_TYPE
PROGRAM MAIN
VAR
    eSample : E_Sample;
    nVar    : INT;
END_VAR
CASE eSample OF
    E_Sample.eNull: nVar := 0;
    E_Sample.eOne:  nVar := 1;
    E_Sample.eTwo:  nVar := 2;
END_CASE

CASE eSample OF                  // => SA0075
    E_Sample.eNull: nVar := 0;
    E_Sample.eTwo:  nVar := 2;
END_CASE

 

SA0076: Fehlende Aufzählungskonstante

Funktion

Ermittelt Codepositionen, wo eine Enumerationsvariable als Bedingung verwendet wird und nicht alle Enumerationswerte als CASE-Zweige behandelt sind.

Begründung

Defensive Programmierung erfordert die Bearbeitung aller möglichen Werte einer Enumeration. Wenn für einen bestimmten Enumerationswert keine Aktion nötig ist, dann sollten Sie dies explizit durch einen Kommentar kennzeichnen. Damit wird deutlich, dass der Wert nicht einfach vergessen wurde.

Wichtigkeit

Niedrig

Beispiel:

Im folgenden Beispiel wird der Enumerationswert eYellow nicht als CASE-Zweig behandelt.

Enumeration E_Color:

TYPE E_Color :
(
    eRed,
    eGreen,
    eBlue,
    eYellow
);
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    eColor : E_Color;
    bVar   : BOOL;
END_VAR
eColor := E_Color.eYellow;
 
CASE eColor OF                   // => SA0076
    E_Color.eRed:
         bVar := FALSE;
 
    E_Color.eGreen,
    E_Color.eBlue:
         bVar := TRUE;
 
ELSE
    bVar := NOT bVar;
END_CASE 

 

SA0077: Datentypdiskrepanz bei CASE-Ausdruck

Funktion

Ermittelt Codepositionen, wo der Datentyp einer Bedingung nicht mit dem des CASE-Zweigs übereinstimmt.

Begründung

Wenn die Datentypen zwischen der CASE-Variable und dem CASE-Fall nicht übereinstimmen, dann könnte das auf einen Fehler hindeuten.

Wichtigkeit

Niedrig

Beispiel:

Enumeration E_Sample:

TYPE E_Sample :
(
    eNull,
    eOne,
    eTwo
) DWORD;
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    nDINT  : DINT;
    bVar   : BOOL;
END_VAR
nDINT := nDINT + DINT#1;
 
CASE nDINT OF
    DINT#1:
         bVar := FALSE;
 
    E_Sample.eTwo,               // => SA0077
    DINT#3:
         bVar := TRUE;
 
ELSE
    bVar := NOT bVar;
END_CASE

 

SA0078: CASE-Anweisungen ohne CASE-Zweig

Funktion

Ermittelt CASE-Anweisungen ohne Fälle, d.h. mit nur einer ELSE-Anweisung.

Begründung

Ein CASE-Statement ohne Fälle kostet nur Zeit in der Ausführung und ist schwer zu lesen.

Wichtigkeit

Mittel

Beispiel:

PROGRAM MAIN
VAR
    nVar   : DINT;
    bVar   : BOOL;
END_VAR
nVar := nVar + INT#1;
 
CASE nVar OF                     // => SA0078
ELSE
    bVar := NOT bVar;
END_CASE

 

SA0090: Return-Anweisung vor Ende der Funktion

Funktion

Ermittelt Codepositionen, wo die RETURN-Anweisung nicht die letzte Anweisung in einer Funktion, Methode, Eigenschaft oder in einem Programm ist.

Begründung

Ein RETURN im Code führt zu schlechterer Wartbarkeit, Testbarkeit und Lesbarkeit des Codes. Ein RETURN im Code wird leicht übersehen. Sie müssen Code, der auf alle Fälle beim Austritt einer Funktion ausgeführt werden sollte, vor jedem RETURN einfügen und das wird oft vergessen.

Wichtigkeit

Mittel

PLCopen-Regel

CP14

Beispiel:

FUNCTION F_TestFunction : BOOL
F_TestFunction := FALSE;
RETURN;                          // => SA0090
F_TestFunction := TRUE;

 

SA0095: Zuweisung in Bedingung

Funktion

Ermittelt Zuweisungen in Bedingungen von IF-, CASE-, WHILE- oder REPEAT-Konstrukten.

Begründung

Ein Zuweisung (:=) und ein Vergleich (=) kann leicht verwechselt werden. Eine Zuweisung in einer Bedingung kann daher leicht unabsichtlich erfolgt sein und wird deswegen gemeldet. Auch der Leser des Codes kann dadurch verwirrt werden.

Wichtigkeit

Hoch

Beispiele:

PROGRAM MAIN
VAR
    bTest   : BOOL;
    bResult : BOOL;
    bValue  : BOOL;
 
    b1      : BOOL;
    n1      : INT;
    n2      : INT;
 
    nCond1  : INT  := INT#1;
    nCond2  : INT  := INT#2;
    bCond   : BOOL := FALSE;
    nVar    : INT;
    eSample : E_Sample;
END_VAR
// IF constructs
IF (bTest := TRUE) THEN                                    // => SA0095
    DoSomething();
END_IF
 
IF (bResult := F_Sample(bInput := bValue)) THEN            // => SA0095
    DoSomething();
END_IF
 
b1 := ((n1 := n2) = 99);                                   // => SA0095
 
IF INT_TO_BOOL(nCond1 := nCond2) THEN                      // => SA0095
    DoSomething();
ELSIF (nCond1 := 11) = 11 THEN                             // => SA0095
    DoSomething();
END_IF
 
IF bCond := TRUE THEN                                      // => SA0095
    DoSomething();
END_IF
 
IF (bCond := FALSE) OR (nCond1 := nCond2) = 12 THEN        // => SA0095
    DoSomething();
END_IF
 
IF (nVar := nVar + 1) = 120 THEN                           // => SA0095
    DoSomething();
END_IF
 
// CASE construct
CASE (eSample := E_Sample.eMember0) OF                     // => SA0095
    E_Sample.eMember0:
            DoSomething();
 
    E_Sample.eMember1:
            DoSomething();
END_CASE
 
// WHILE construct
WHILE (bCond = TRUE) OR (nCond1 := nCond2) = 12 DO         // => SA0095
    DoSomething();
END_WHILE
 
// REPEAT construct
REPEAT
    DoSomething();
UNTIL
     (bCond = TRUE) OR ((nCond1 := nCond2) = 12)           // => SA0095
END_REPEAT

 

SA0100: Variablen größer als <n> Bytes

Funktion

Ermittelt Variablen, die mehr als n Bytes verwenden, wobei n durch die aktuelle Konfiguration vorgegeben ist.

Den Parameter, der bei dieser Prüfung berücksichtigt wird, können Sie konfigurieren, indem Sie innerhalb der Regelkonfiguration auf die Zeile von Regel 100 doppelklicken (SPS-Projekteigenschaften > Kategorie "Static Analysis" > Registerkarte "Regeln" > Regel 100). In dem aufgehenden Dialog können Sie folgende Einstellungen vornehmen:

  • Obergrenze in Bytes (Standardwert: 1024)

Begründung

Manche Programmierrichtlinien legen eine maximale Größe für eine einzelne Variable fest. Dies kann hiermit überprüft werden.

Wichtigkeit

Niedrig

Beispiel:

Im folgenden Beispiel ist die Variable aSample größer als 1024 Bytes.

PROGRAM MAIN
VAR
    aSample : ARRAY [0..1024] OF BYTE;              // => SA0100
END_VAR

 

SA0101: Namen mit unzulässiger Länge

Funktion

Ermittelt Namen mit unzulässiger Länge. Der Name von Objekten muss eine definierte Länge haben.

Die Parameter, die bei dieser Prüfung berücksichtigt werden, können Sie konfigurieren, indem Sie innerhalb der Regelkonfiguration auf die Zeile von Regel 101 doppelklicken (SPS-Projekteigenschaften > Kategorie "Static Analysis" > Registerkarte "Regeln" > Regel 101). In dem aufgehenden Dialog können Sie folgende Einstellungen vornehmen:

  • Minimale Anzahl an Zeichen (Standardwert: 5)
  • Maximale Anzahl an Zeichen (Standardwert: 30)
  • Ausnahmen

Begründung

In manchen Programmierrichtlinien wird eine Mindestlänge für Variablennamen festgelegt. Die Einhaltung kann mit dieser Analyse überprüft werden.

Wichtigkeit

Niedrig

PLCopen-Regel

N6

Beispiele:

Regel 101 ist mit folgenden Parametern konfiguriert:

Programm PRG1:

PROGRAM PRG1                     // => SA0101
VAR
END_VAR

Programm MAIN:

PROGRAM MAIN                     // no error due to configured exceptions
VAR
    i     : INT;                 // no error due to configured exceptions
    b     : BOOL;                // => SA0101
    nVar1 : INT;
END_VAR
PRG1();

SA0102: Zugriff von außen auf lokale Variablen

Funktion

Ermittelt Zugriffe von außen auf lokale Variablen von Programmen oder Funktionsbausteinen.

Begründung

TwinCAT ermittelt Schreibzugriffe von außen auf lokale Variablen von Programmen oder Funktionsbausteinen als Kompilierfehler. Da Lesezugriffe auf lokale Variablen nicht vom Compiler abgefangen werden und dies mit dem Grundsatz der Datenkapselung (Verbergen von Daten) bricht und nicht der Norm IEC 61131-3 entspricht, kann diese Regel verwendet werden, um Lesezugriffe auf lokale Variablen zu ermitteln.

Wichtigkeit

Mittel

Beispiele:

Funktionsbaustein FB_Base:

FUNCTION_BLOCK FB_Base
VAR
    nLocal : INT;
END_VAR

Methode FB_Base.SampleMethod:

METHOD SampleMethod : INT
VAR_INPUT
END_VAR
nLocal := nLocal + 1;

Funktionsbaustein FB_Sub:

FUNCTION_BLOCK FB_Sub EXTENDS FB_Base

Methode FB_Sub.SampleMethod:

METHOD SampleMethod : INT
VAR_INPUT
END_VAR
nLocal := nLocal + 5;

Programm PRG_1:

PROGRAM PRG_1
VAR
    bLocal : BOOL;
END_VAR
bLocal := NOT bLocal;

Programm MAIN:

PROGRAM MAIN
VAR
    bRead     : BOOL;
    nReadBase : INT;
    nReadSub  : INT;
    fbBase    : FB_Base;
    fbSub     : FB_Sub;
END_VAR
bRead     := PRG_1.bLocal;       // => SA0102 
nReadBase := fbBase.nLocal;      // => SA0102
nReadSub  := fbSub.nLocal;       // => SA0102

 

SA0103: Gleichzeitiger Zugriff auf nicht-atomare Daten

Funktion

Ermittelt nicht-atomare Variablen (zum Beispiel mit Datentyp STRING, WSTRING, ARRAY, STRUCT, FB-Instanzen, 64-Bit Datentypen), die in mehr als einer Task verwendet werden.

Begründung

Wenn keine Synchronisation beim Zugriff erfolgt, dann kann es bei gleichzeitigem Lesen in einer Task und Schreiben in einer anderen Task dazu kommen, dass inkonsistente Werte gelesen werden.

Ausnahme

In folgenden Fällen greift diese Regel nicht:

  • Wenn das Zielsystem eine FPU (Floating Point Unit) besitzt, wird der Zugriff mehrerer Tasks auf LREAL-Variablen nicht ermittelt und gemeldet.
  • Wenn das Zielsystem ein 64-Bit Prozessor ist bzw. "TwinCAT RT (x64)" als Solution-Plattform ausgewählt ist, greift die Regel nicht für 64-Bit Datentypen.

Wichtigkeit

Mittel

 

Regeln - Übersicht und Beschreibung 12:

Sehen Sie auch die Regel SA0006.

Beispiele:

Struktur ST_Sample:

TYPE ST_Sample :
STRUCT
    bMember : BOOL;
    nTest   : INT;
END_STRUCT
END_TYPE

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    fInput  : LREAL;
END_VAR

GVL:

{attribute 'qualified_only'}
VAR_GLOBAL
    fTest   : LREAL;              // => no error SA0103: Since the target system has a FPU, SA0103 does not apply.
    nTest   : LINT;               // => error reporting depends on the solution platform:
                                     // - SA0103 error if solution platform is set to "TwinCAT RT(x86)"
                                     // - no error SA0103 if solution platform is set to "TwinCAT (x64)"
    sTest   : STRING;             // => SA0103
    wsTest  : WSTRING;            // => SA0103
    aTest   : ARRAY[0..2] OF INT; // => SA0103
    aTest2  : ARRAY[0..2] OF INT; // => SA0103
    fbTest  : FB_Sample;          // => SA0103
    stTest  : ST_Sample;          // => SA0103
END_VAR

Programm MAIN1, aufgerufen von der Task PlcTask1:

PROGRAM MAIN1
VAR
END_VAR
GVL.fTest         := 5.0;
GVL.nTest         := 123;
GVL.sTest         := 'sample text';
GVL.wsTest        := "sample text";
GVL.aTest         := GVL.aTest2;
GVL.fbTest.fInput := 3;
GVL.stTest.nTest  := GVL.stTest.nTest + 1;

Programm MAIN2, aufgerufen von der Task PlcTask2:

PROGRAM MAIN2
VAR
    fLocal  : LREAL;
    nLocal  : LINT;
    sLocal  : STRING;
    wsLocal : WSTRING;
    aLocal  : ARRAY[0..2] OF INT;
    aLocal2 : ARRAY[0..2] OF INT;
    fLocal2 : LREAL;
    nLocal2  : INT;
END_VAR
fLocal  := GVL.fTest + 1.5;
nLocal  := GVL.nTest + 10;
sLocal  := GVL.sTest;
wsLocal := GVL.wsTest;
aLocal  := GVL.aTest;
aLocal2 := GVL.aTest2;
fLocal2 := GVL.fbTest.fInput;
nLocal2 := GVL.stTest.nTest;

 

SA0105: Mehrfache Instanzaufrufe

Funktion

Ermittelt und meldet Instanzen von Funktionsbausteinen, die mehr als einmal aufgerufen werden. Damit eine Fehlermeldung für eine mehrfach aufgerufene Instanz eines Funktionsbausteins generiert wird, muss im Deklarationsteil des Funktionsbausteins das Attribut {attribute 'analysis:report-multiple-instance-calls'} hinzugefügt werden.

Begründung

Einige Funktionsblöcke sind so designt, dass sie nur einmal im Zyklus aufgerufen werden können. Dieser Test prüft, ob ein Aufruf an mehreren Stellen erfolgt.

Wichtigkeit

Niedrig

PLCopen-Regel

CP16/CP20

Beispiel:

Im folgenden Beispiel wird die Statische Analyse einen Fehler für fb2 ausgeben, weil die Instanz mehr als einmal aufgerufen wird und der Funktionsbaustein mit dem benötigten Attribut deklariert ist.

Funktionsbaustein FB_Test1 ohne Attribut:

FUNCTION_BLOCK FB_Test1

Funktionsbaustein FB_Test2 mit Attribut:

{attribute 'analysis:report-multiple-instance-calls'}
FUNCTION_BLOCK FB_Test2

Programm MAIN:

PROGRAM MAIN 
VAR
    fb1  : FB_Test1;
    fb2  : FB_Test2;
END_VAR
fb1();
fb1();
fb2();                           // => SA0105
fb2();                           // => SA0105

 

SA0106: Virtuelle Methodenaufrufe in FB_init

Funktion

Ermittelt Methodenaufrufe in der Methode FB_init eines Basis-Funktionsbausteins, die von einem vom Basis-FB abgeleiteten Funktionsbaustein überschrieben werden.

Begründung

In solchen Fällen kann es sein, dass die Variablen in überschriebenen Methoden im Basis-FB nicht initialisiert sind.

Wichtigkeit

Hoch

Beispiel:

 

Funktionsbaustein FB_Base:

FUNCTION_BLOCK FB_Base
VAR
    nBase        : DINT;
END_VAR

Methode FB_Base.FB_init:

METHOD FB_init : BOOL
VAR_INPUT
    bInitRetains : BOOL;
    bInCopyCode  : BOOL;
END_VAR
VAR
    nLocal       : DINT;
END_VAR
nLocal := MyInit();              // => SA0106

Methode FB_Base.MyInit:

METHOD MyInit : DINT
nBase  := 123;                   // access to member of FB_Base
MyInit := nBase;

Funktionsbaustein FB_Sub:

FUNCTION_BLOCK FB_Sub EXTENDS FB_Base
VAR
    nSub         : DINT;
END_VAR

Methode FB_Sub.MyInit:

METHOD MyInit : DINT
nSub   := 456;                   // access to member of FB_Sub
SUPER^.MyInit();                 // call of base implementation
MyInit := nSub;

Programm MAIN:

PROGRAM MAIN
VAR
    fbBase       : FB_Base;
    fbSub        : FB_Sub;
END_VAR

 

Die Instanz MAIN.fbBase besitzt nach der Initialisierung folgende Variablenwerte:

Die Instanz MAIN.fbSub besitzt nach der Initialisierung folgende Variablenwerte:

Die Variable MAIN.fbSub.nSub ist nach der Initialisierung 0, da während der Initialisierung von fbSub die folgende Aufrufreihenfolge durchgeführt wird:

 

SA0107: Fehlen von formalen Parametern

Funktion

Ermittelt, wo formale Parameter fehlen.

Begründung

Code wird lesbarer, wenn die Formalparameter beim Aufruf angegeben werden.

Wichtigkeit

Niedrig

Beispiel:

Funktion F_Sample:

FUNCTION F_Sample : BOOL
VAR_INPUT
    bIn1 : BOOL;
    bIn2 : BOOL;
END_VAR
F_Sample := bIn1 AND bIn2;

Programm MAIN:

PROGRAM MAIN
VAR
    bReturn : BOOL;
END_VAR
bReturn := F_Sample(TRUE, FALSE);                   // => SA0107 
bReturn := F_Sample(TRUE, bIn2 := FALSE);           // => SA0107
bReturn := F_Sample(bIn1 := TRUE, bIn2 := FALSE);   // no error

 

SA0111: Zeigervariablen

Funktion

Ermittelt Variablen vom Typ POINTER TO.

Begründung

Die Norm IEC 61131-3 erlaubt keine Pointer.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    pINT   : POINTER TO INT;     // => SA0111
END_VAR

 

SA0112: Referenzvariablen

Funktion

Ermittelt Variablen vom Typ REFERENCE TO.

Begründung

Die Norm IEC 61131-3 erlaubt keine Referenzen.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    refInt : REFERENCE TO INT;   // => SA0112
END_VAR

 

SA0113: Variablen mit Datentyp WSTRING

Funktion

Ermittelt Variablen vom Typ WSTRING.

Begründung

Nicht alle Systeme unterstützen WSTRING. Der Code wird leichter portierbar, wenn auf WSTRING verzichtet wird.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    wsVar  : WSTRING;            // => SA0113
END_VAR

 

SA0114: Variablen mit Datentyp LTIME

Funktion

Ermittelt Variablen vom Typ LTIME.

Begründung

Nicht alle Systeme unterstützen LTIME. Der Code wird portierbarer, wenn auf LTIME verzichtet wird.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    tVar   : LTIME;              // => SA0114
END_VAR
// no error SA0114 for the following code line:
tVar := tVar + LTIME#1000D15H23M12S34MS2US44NS;

 

SA0115: Deklarationen mit Datentyp UNION

Funktion

Ermittelt Deklarationen eines UNION-Datentyps und Deklarationen von Variablen vom Typ einer UNION.

Begründung

Die Norm IEC-61131-3 kennt keinen Unions. Der Code wird leichter portierbar, wenn auf Unions verzichtet wird.

Wichtigkeit

Niedrig

Beispiele:

Union U_Sample:

TYPE U_Sample :                  // => SA0115 
UNION
    fVar    : LREAL;
    nVar    : LINT;
END_UNION
END_TYPE

Programm MAIN:

PROGRAM MAIN
VAR
    uSample : U_Sample;          // => SA0115
END_VAR

 

SA0117: Variablen mit Datentyp BIT

Funktion

Ermittelt Deklarationen von Variablen vom Typ BIT (möglich innerhalb von Struktur- und Funktionsbausteindefinitionen).

Begründung

Die IEC-61131-3 kennt keinen Datentyp BIT. Der Code wird leichter portierbar, wenn auf BIT verzichtet wird.

Wichtigkeit

Niedrig

Beispiele:

Struktur ST_Sample:

TYPE ST_Sample :
STRUCT
    bBIT  : BIT;                 // => SA0117
    bBOOL : BOOL;
END_STRUCT
END_TYPE

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR
    bBIT  : BIT;                 // => SA0117
    bBOOL : BOOL;
END_VAR

 

SA0119: Objektorientierte Funktionalität

Funktion

Ermittelt die Verwendung objektorientierter Funktionalitäten, wie beispielsweise:

  • Funktionsbaustein-Deklarationen mit EXTENDS oder IMPLEMENTS
  • Eigenschaften- und Schnittstellendeklarationen
  • Verwendung des THIS- oder SUPER-Zeigers

Begründung

Nicht alle Systeme unterstützen Objektorientierte Programmierung. Der Code wird leichter portierbar, wenn auf Objektorientierung verzichtet wird.

Wichtigkeit

Niedrig

Beispiele:

Schnittstelle I_Sample:

INTERFACE I_Sample                                  // => SA0119

Funktionsbaustein FB_Base:

FUNCTION_BLOCK FB_Base IMPLEMENTS I_Sample          // => SA0119

Funktionsbaustein FB_Sub:

FUNCTION_BLOCK FB_Sub EXTENDS FB_Base               // => SA0119

Methode FB_Sub.SampleMethod:

METHOD SampleMethod : BOOL                          // no error

Get-Funktion der Eigenschaft FB_Sub.SampleProperty:

VAR                                                 // => SA0119 
END_VAR

Set-Funktion der Eigenschaft FB_Sub.SampleProperty:

VAR                                                 // => SA0119 
END_VAR

 

SA0120: Programmaufrufe

Funktion

Ermittelt Programmaufrufe.

Begründung

Nach der Norm IEC 61131-3 können Programme nur in der Taskkonfiguration aufgerufen werden. Der Code wird leichter portierbar, wenn auf Programmaufrufe an anderen Stellen verzichtet wird.

Wichtigkeit

Niedrig

Beispiel:

Programm SubProgram:

PROGRAM SubProgram

Programm MAIN:

PROGRAM MAIN
SubProgram();                    // => SA0120

 

SA0121: Fehlende VAR_EXTERNAL-Deklarationen

Funktion

Ermittelt die Verwendung einer globalen Variablen im Funktionsbaustein, ohne dass sie dort als VAR_EXTERNAL deklariert ist (erforderlich laut Norm).

Begründung

Nach der Norm IEC 61131-3 ist der Zugriff auf globale Variablen nur über einen expliziten Import durch eine VAR_EXTERNAL-Deklaration erlaubt.

Wichtigkeit

Niedrig

PLCopen-Regel

CP18

 

Regeln - Übersicht und Beschreibung 13:

In TwinCAT 3 PLC ist es nicht notwendig, Variablen als extern zu deklarieren. Das Schlüsselwort existiert, um die Kompatibilität zu IEC 61131-3 zu wahren.

Beispiel:

Globale Variablen:

VAR_GLOBAL
    nGlobal : INT;
END_VAR

Programm Prog1:

PROGRAM Prog1
VAR
    nVar    : INT;
END_VAR
nVar := nGlobal;                 // => SA0121

Programm Prog2:

PROGRAM Prog2
VAR
    nVar    : INT;
END_VAR
VAR_EXTERNAL
    nGlobal : INT;
END_VAR
nVar := nGlobal;                 // no error

 

SA0122: Als Ausdruck definierter Arrayindex

Funktion

Ermittelt die Verwendung von Ausdrücken bei der Deklaration von Arraygrenzen.

Begründung

Nicht alle Systeme erlauben Ausdrücke als Arraygrenzen.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR CONSTANT
    cSample  : INT := INT#15;
END_VAR
VAR
    aSample1 : ARRAY[0..10] OF INT;
    aSample2 : ARRAY[0..10+5] OF INT;               // => SA0122
    aSample3 : ARRAY[0..cSample] OF INT;
    aSample4 : ARRAY[0..cSample + 1] OF INT;        // => SA0122
END_VAR

 

SA0123: Verwendung von INI, ADR oder BITADR

Funktion

Ermittelt die Verwendung der (TwinCAT spezifischen) Operatoren INI, ADR, BITADR.

Begründung

TwinCAT-spezifische Operatoren verhindern die Portierbarkeit des Codes.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    nVar : INT;
    pINT : POINTER TO INT;
END_VAR
pINT := ADR(nVar);               // => SA0123

 

SA0147: Unübliche Schiebeoperation - strikt

Funktion

Ermittelt Bitshift-Operationen, die nicht auf Bitfeld-Datentypen (BYTE, WORD, DWORD, LWORD) erfolgen.

Begründung

Die Norm IEC 61131-3 erlaubt nur Bitzugriffe auf Bitfeld-Datentypen. Der TwinCAT 3 Compiler erlaubt jedoch auch Bitshift-Operationen mit nicht vorzeichenbehafteten Datentypen.

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 14:

Sehen Sie auch die nicht strikte Regel SA0052.

Beispiele:

PROGRAM MAIN
VAR
    nBYTE     : BYTE := 16#45;
    nWORD     : WORD := 16#0045;
    nUINT     : UINT;
    nDINT     : DINT;
    nResBYTE  : BYTE;
    nResWORD  : WORD;
    nResUINT  : UINT;
    nResDINT  : DINT;
    nShift    : BYTE := 2;
END_VAR
nResBYTE := SHL(nByte,nShift);   // no error because BYTE is a bit field
nResWORD := SHL(nWORD,nShift);   // no error because WORD is a bit field
nResUINT := SHL(nUINT,nShift);   // => SA0147
nResDINT := SHL(nDINT,nShift);   // => SA0147

 

SA0148: Unüblicher Bitzugriff - strikt

Funktion

Ermittelt Bitzugriffe, die nicht auf Bitfeld-Datentypen (BYTE, WORD, DWORD, LWORD) erfolgen.

Begründung

Die Norm IEC 61131-3 erlaubt nur Bitzugriffe auf Bitfeld-Datentypen. Der TwinCAT 3 Compiler erlaubt jedoch auch Bitzugriffe auf nicht vorzeichenbehaftete Datentypen.

Wichtigkeit

Niedrig

 

Regeln - Übersicht und Beschreibung 15:

Sehen Sie auch die nicht strikte Regel SA0018.

Beispiele:

PROGRAM MAIN
VAR
    nINT      : INT;
    nDINT     : DINT;
    nULINT    : ULINT;
    nSINT     : SINT;
    nUSINT    : USINT;
    nBYTE     : BYTE;
END_VAR
nINT.3    := TRUE;               // => SA0148
nDINT.4   := TRUE;               // => SA0148
nULINT.18 := FALSE;              // => SA0148
nSINT.2   := FALSE;              // => SA0148
nUSINT.3  := TRUE;               // => SA0148
nBYTE.5   := FALSE;              // no error because BYTE is a bitfield

 

SA0118: Initialisierungen nicht mit Konstanten

Funktion

Ermittelt Initialisierungen, die nicht Konstanten zuweisen.

Begründung

Initialisierungen sollten möglichst konstant sein und sich nicht auf andere Variablen beziehen. Insbesondere sollten Sie Funktionsaufrufe in der Initialisierung vermeiden, weil es dadurch zu einem Zugriff auf nicht initialisierte Daten kommen kann.

Wichtigkeit

Mittel

Beispiele:

Funktion F_ReturnDWORD:

FUNCTION F_ReturnDWORD : DWORD

Programm MAIN:

PROGRAM MAIN
VAR CONSTANT
    c1 : DWORD := 100;
END_VAR
VAR
    n1 : DWORD := c1;
    n2 : DWORD := F_ReturnDWORD();                  // => SA0118
    n3 : DWORD := 150;
    n4 : DWORD := n3;                               // => SA0118
END_VAR

 

SA0124: Dereferenzierungszugriff in Initialisierungen

Funktion

Ermittelt alle Codestellen, an denen dereferenzierte Pointer im Deklarationsteil von POUs verwendet werden.

Begründung

Pointer und Referenzen sollten nicht für Initialisierungen verwendet werden, weil es dadurch zur Laufzeit zu "Access Violations" kommen kann, wenn der Pointer nicht initialisiert worden ist.

Wichtigkeit

Mittel

Beispiele:

FUNCTION_BLOCK FB_Test
VAR_INPUT
    pStruct    : POINTER TO ST_Test;
    refStruct  : REFERENCE TO ST_Test;
END_VAR
VAR
    bPointer   : BOOL := pStruct^.bTest;  // => SA0124: Dereference access in initialization
    bRef       : BOOL := refStruct.bTest; // => SA0125: Reference used in initialization
END_VAR
bPointer := pStruct^.bTest;               // => SA0039: Possible null pointer dereference 'pStruct^'
bRef     := refStruct.bTest;              // => SA0145: Possible use of not initialized reference 'refStruct'
 
IF pStruct <> 0 THEN
    bPointer := pStruct^.bTest;           // no error SA0039 as the pointer is checked for unequal 0
END_IF

IF __ISVALIDREF(refStruct) THEN
    bRef     := refStruct.bTest;          // no error SA0145 as the reference is checked via __ISVALIDREF
END_IF

Überblick über die Regeln zum Thema „Dereferenzierung“

Pointer:

  • Dereferenzierung von Pointern im Deklarationsteil => SA0124
  • Mögliche Null-Pointer-Dereferenzierung im Implementierungsteil => SA0039

Referenzen:

  • Verwendung von Referenzen im Deklarationsteil => SA0125
  • Mögliche Verwendung nicht initialisierter Referenzen im Implementierungsteil => SA0145

Schnittstellen:

  • Mögliche Verwendung nicht initialisierter Schnittstellen im Implementierungsteil => SA0046

 

SA0125: Referenzen in Initialisierungen

Funktion

Ermittelt alle Referenzvariablen, die zur Initialisierung im Deklarationsteil von POUs verwendet werden.

Begründung

Pointer und Referenzen sollten nicht für Initialisierungen verwendet werden, weil es dadurch zur Laufzeit zu “Access Violations” kommen kann, wenn der Pointer nicht initialisiert worden ist.

Wichtigkeit

Mittel

Beispiele:

FUNCTION_BLOCK FB_Test
VAR_INPUT
    pStruct    : POINTER TO ST_Test;
    refStruct  : REFERENCE TO ST_Test;
END_VAR
VAR
    bPointer   : BOOL := pStruct^.bTest;  // => SA0124: Dereference access in initialization
    bRef       : BOOL := refStruct.bTest; // => SA0125: Reference used in initialization
END_VAR
bPointer := pStruct^.bTest;               // => SA0039: Possible null pointer dereference 'pStruct^'
bRef     := refStruct.bTest;              // => SA0145: Possible use of not initialized reference 'refStruct'
 
IF pStruct <> 0 THEN
    bPointer := pStruct^.bTest;           // no error SA0039 as the pointer is checked for unequal 0
END_IF

IF __ISVALIDREF(refStruct) THEN
    bRef     := refStruct.bTest;          // no error SA0145 as the reference is checked via __ISVALIDREF
END_IF

Überblick über die Regeln zum Thema „Dereferenzierung“

Pointer:

  • Dereferenzierung von Pointern im Deklarationsteil => SA0124
  • Mögliche Null-Pointer-Dereferenzierung im Implementierungsteil => SA0039

Referenzen:

  • Verwendung von Referenzen im Deklarationsteil => SA0125
  • Mögliche Verwendung nicht initialisierter Referenzen im Implementierungsteil => SA0145

Schnittstellen:

  • Mögliche Verwendung nicht initialisierter Schnittstellen im Implementierungsteil => SA0046

 

SA0039: Mögliche Null-Pointer-Dereferenzierung

Funktion

Ermittelt Codestellen, an denen möglicherweise ein Null-Pointer dereferenziert wird.

Begründung

Ein Pointer sollte vor jeder Dereferenzierung daraufhin geprüft werden, ob er ungleich 0 ist. Anderfalls kann es zu einer “Access Violation” zur Laufzeit kommen.

Wichtigkeit

Hoch

Beispiel 1:

PROGRAM MAIN 
VAR
    pInt1     : POINTER TO INT;
    pInt2     : POINTER TO INT;
    pInt3     : POINTER TO INT;
    nVar1     : INT;
    nCounter  : INT;
END_VAR
nCounter := nCounter + INT#1;
 
pInt1    := ADR(nVar1);
pInt1^   := nCounter;            // no error
 
pInt2^   := nCounter;            // => SA0039
nVar1    := pInt3^;              // => SA0039

Beispiel 2:

FUNCTION_BLOCK FB_Test
VAR_INPUT
    pStruct    : POINTER TO ST_Test;
    refStruct  : REFERENCE TO ST_Test;
END_VAR
VAR
    bPointer   : BOOL := pStruct^.bTest;  // => SA0124: Dereference access in initialization
    bRef       : BOOL := refStruct.bTest; // => SA0125: Reference used in initialization
END_VAR
bPointer := pStruct^.bTest;               // => SA0039: Possible null pointer dereference 'pStruct^'
bRef     := refStruct.bTest;              // => SA0145: Possible use of not initialized reference 'refStruct'
 
IF pStruct <> 0 THEN
    bPointer := pStruct^.bTest;           // no error SA0039 as the pointer is checked for unequal 0
END_IF

IF __ISVALIDREF(refStruct) THEN
    bRef     := refStruct.bTest;          // no error SA0145 as the reference is checked via __ISVALIDREF
END_IF

Überblick über die Regeln zum Thema „Dereferenzierung“

Pointer:

  • Dereferenzierung von Pointern im Deklarationsteil => SA0124
  • Mögliche Null-Pointer-Dereferenzierung im Implementierungsteil => SA0039

Referenzen:

  • Verwendung von Referenzen im Deklarationsteil => SA0125
  • Mögliche Verwendung nicht initialisierter Referenzen im Implementierungsteil => SA0145

Schnittstellen:

  • Mögliche Verwendung nicht initialisierter Schnittstellen im Implementierungsteil => SA0046

 

SA0046: Mögliche Verwendung nicht initialisierter Schnittstellen

Funktion

Ermittelt die Verwendung von Schnittstellen, die vor der Verwendung möglicherweise nicht initialisiert wurden.

Begründung

Eine Interface-Referenz sollte vor ihrer Verwendung auf <> 0 geprüft werden, weil es sonst beim Zugriff zur Laufzeit zu einer "Access Violation" kommen kann.

Wichtigkeit

Hoch

Beispiele:

Schnittstelle I_Sample:

INTERFACE I_Sample
METHOD SampleMethod : BOOL
VAR_INPUT
    nInput  : INT;
END_VAR

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample IMPLEMENTS I_Sample
METHOD SampleMethod : BOOL
VAR_INPUT
    nInput  : INT;
END_VAR

Programm MAIN:

PROGRAM MAIN
VAR
    fbSample      : FB_Sample;
    iSample       : I_Sample;
    iSampleNotSet : I_Sample;
    nParam        : INT;
    bReturn       : BOOL;
END_VAR
iSample := fbSample; 
bReturn := iSample.SampleMethod(nInput := nParam);         // no error
 
bReturn := iSampleNotSet.SampleMethod(nInput := nParam);   // => SA0046

Überblick über die Regeln zum Thema „Dereferenzierung“

Pointer:

  • Dereferenzierung von Pointern im Deklarationsteil => SA0124
  • Mögliche Null-Pointer-Dereferenzierung im Implementierungsteil => SA0039

Referenzen:

  • Verwendung von Referenzen im Deklarationsteil => SA0125
  • Mögliche Verwendung nicht initialisierter Referenzen im Implementierungsteil => SA0145

Schnittstellen:

  • Mögliche Verwendung nicht initialisierter Schnittstellen im Implementierungsteil => SA0046

 

SA0145: Mögliche Verwendung nicht initialisierter Referenzen

Funktion

Ermittelt alle verwendeten Referenzvariablen, die möglicherweise vor der Verwendung nicht initialisiert werden und nicht durch den Operator __ISVALIDREF überprüft wurden. Diese Regel wird im Implementierungsteil von POUs angewendet.

Begründung

Eine Referenz sollte vor dem Zugriff auf Gültigkeit geprüft werden, weil es sonst beim Zugriff zur Laufzeit zu einer "Access Violation" kommen kann.

Wichtigkeit

Hoch

Beispiele:

FUNCTION_BLOCK FB_Test
VAR_INPUT
    pStruct    : POINTER TO ST_Test;
    refStruct  : REFERENCE TO ST_Test;
END_VAR
VAR
    bPointer   : BOOL := pStruct^.bTest;  // => SA0124: Dereference access in initialization
    bRef       : BOOL := refStruct.bTest; // => SA0125: Reference used in initialization
END_VAR
bPointer := pStruct^.bTest;               // => SA0039: Possible null pointer dereference 'pStruct^'
bRef     := refStruct.bTest;              // => SA0145: Possible use of not initialized reference 'refStruct'
 
IF pStruct <> 0 THEN
    bPointer := pStruct^.bTest;           // no error SA0039 as the pointer is checked for unequal 0
END_IF

IF __ISVALIDREF(refStruct) THEN
    bRef     := refStruct.bTest;          // no error SA0145 as the reference is checked via __ISVALIDREF
END_IF

Überblick über die Regeln zum Thema „Dereferenzierung“

Pointer:

  • Dereferenzierung von Pointern im Deklarationsteil => SA0124
  • Mögliche Null-Pointer-Dereferenzierung im Implementierungsteil => SA0039

Referenzen:

  • Verwendung von Referenzen im Deklarationsteil => SA0125
  • Mögliche Verwendung nicht initialisierter Referenzen im Implementierungsteil => SA0145

Schnittstellen:

  • Mögliche Verwendung nicht initialisierter Schnittstellen im Implementierungsteil => SA0046

 

SA0140: Auskommentierte Anweisungen

Funktion

Ermittelt Anweisungen, die auskommentiert sind.

Begründung

Code wird oft zu Debugging-Zwecken auskommentiert. Wenn ein solcher Kommentar freigegeben wird, dann ist zu einem späteren Zeitpunkt nicht mehr klar, ob der Code gelöscht werden sollte oder ob er nur zu Debug-Zwecken auskommentiert wurde und versehentlich nicht mehr einkommentiert wurde.

Wichtigkeit

Hoch

PLCopen-Regel

C4

Beispiel:

//bStart := TRUE;                // => SA0140

 

SA0150: Verletzung von Unter- oder Obergrenzen der Metriken

Funktion

Ermittelt die Bausteine, die die aktivierten Metriken an der Unter- oder Obergrenze verletzen.

Begründung

Code, der bestimmte Metriken einhält, ist leichter lesbar, leichter wartbar und leichter zu testen.

Wichtigkeit

Hoch

PLCopen-Regel

CP9

Beispiel:

Die Metrik "Anzahl Aufrufe" ist in der Metriken-Konfiguration aktiviert und konfiguriert (SPS-Projekteigenschaften > Kategorie "Static Analysis" > Registerkarte "Metriken").

Beim Ausführen der Statischen Analyse wird die Verletzung von SA0150 als Fehler bzw. Warnung im Meldungsfenster ausgegeben.

// => SA0150: Metric violation for 'Prog1'. Result for metric 'Calls' (5) > 3"

 

SA0160: Rekursive Aufrufe

Funktion

Ermittelt rekursive Aufrufe von Programmen, Aktionen, Methoden und Eigenschaften. Ermittelt auch mögliche Rekursionen durch virtuelle Funktionsaufrufe und Schnittstellenaufrufe.

Begründung

Rekursionen führen zu nicht-deterministischem Verhalten und sind daher eine Fehlerquelle.

Wichtigkeit

Mittel

PLCopen-Regel

CP13

Beispiel 1:

Methode FB_Sample.SampleMethod1:

METHOD SampleMethod1
VAR_INPUT
END_VAR
SampleMethod1(); (* => SA0160: Recursive call:
                              'MAIN -> FB_Sample.SampleMethod1 -> FB_Sample.SampleMethod1' *)

Methode FB_Sample.SampleMethod2:

METHOD SampleMethod2 : BOOL
VAR_INPUT
END_VAR
SampleMethod2 := THIS^.SampleMethod2();(* => SA0160: Recursive call:
                                                    'MAIN -> FB_Sample.SampleMethod2 -> FB_Sample.SampleMethod2' *)

Programm MAIN:

PROGRAM MAIN
VAR
    fbSample : FB_Sample;
    bReturn  : BOOL;
END_VAR
fbSample.SampleMethod1();
bReturn := fbSample.SampleMethod2();

Beispiel 2:

Bitte beachten Sie bei Eigenschaften:

Für eine Eigenschaft wird implizit eine lokale Eingangsvariable mit dem Namen der Eigenschaft erzeugt. Die folgende Set-Funktion einer Eigenschaft weist somit den Wert der impliziten lokalen Eingangsvariablen der Eigenschaft einer FB-Variablen zu.

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR
    nParameter : INT;
END_VAR

Set-Funktion der Eigenschaft SampleProperty:

nParameter := SampleProperty;

 

Bei der folgenden Set-Funktion wird somit die implizite Eingangsvariable der Eigenschaft sich selbst zugewiesen. Die Zuweisung einer Variablen auf sich selbst bedeutet keine Rekursion, sodass diese Set-Funktion keinen Fehler SA0160 erzeugt.

Set-Funktion der Eigenschaft SampleProperty:

SampleProperty := SampleProperty;              // no error SA0160

 

Der Zugriff auf eine Eigenschaft über den THIS-Zeiger ist hingegen qualifiziert. Durch Verwendung des THIS-Zeigers wird auf die Instanz und somit auf die Eigenschaft und nicht auf die implizite lokale Eingangsvariable zugegriffen. Das bedeutet, dass die Verschattung von impliziter lokaler Eingangsvariablen und der Eigenschaft selbst aufgehoben wird. Bei der folgenden Set-Funktion wird daher ein erneuter Aufruf der Eigenschaft erzeugt, der zu einer Rekursion und damit zum Fehler SA0160 führt.

Set-Funktion der Eigenschaft SampleProperty:

THIS^.SampleProperty := SampleProperty;        // => SA0160

 

SA0161: Ungepackte Struktur in gepackter Struktur

Funktion

Ermittelt ungepackte Strukturen, die in gepackten Strukturen verwendet werden.

Begründung

Eine ungepackte Struktur legt der Compiler normalerweise auf eine Adresse, die alignten Zugriff auf alle Elemente innerhalb der Struktur erlaubt. Wenn Sie diese Struktur in einer gepackten Struktur anlegen, dann ist ein alignter Zugriff nicht mehr möglich, und ein Zugriff auf ein Element in der ungepackten Struktur kann zur Laufzeit zu einer “Misalignment Exception” führen.

Wichtigkeit

Hoch

Beispiel:

Die Struktur ST_SingleDataRecord ist gepackt, enthält jedoch Instanzen der ungepackten Strukturen ST_4Byte und ST_9Byte. Dies resultiert jeweils in einer Fehlermeldung SA0161.

{attribute 'pack_mode' := '1'}
TYPE ST_SingleDataRecord :
STRUCT
    st9Byte          : ST_9Byte; // => SA0161
    st4Byte          : ST_4Byte; // => SA0161
    n1               : UDINT;
    n2               : UDINT;
    n3               : UDINT;
    n4               : UDINT;
END_STRUCT
END_TYPE

Struktur ST_9Byte:

TYPE ST_9Byte :
STRUCT
    nRotorSlots      : USINT;
    nMaxCurrent      : UINT;
    nVelocity        : USINT;
    nAcceleration    : UINT;
    nDeceleration    : UINT;
    nDirectionChange : USINT;
END_STRUCT
END_TYPE

Struktur ST_4Byte:

TYPE ST_4Byte :
STRUCT
    fDummy           : REAL;
END_STRUCT
END_TYPE

 

SA0162: Fehlende Kommentare

Funktion

Ermittelt Stellen im Programm, die nicht kommentiert sind. Kommentare sind erforderlich bei:

  • der Deklaration von Variablen. Die Kommentare stehen darüber oder rechts davon.
  • der Deklaration von POUs, DUTs, GVLs oder Schnittstellen. Die Kommentare stehen über der Deklaration (in der ersten Zeile).

Begründung

Eine vollständige Kommentierung wird von vielen Programmierrichtlinien gefordert und erhöht die Lesbarkeit und Wartbarkeit des Codes.

Wichtigkeit

Niedrig

PLCopen-Regel

C2

Beispiele:

Das folgende Beispiel erzeugt für die Variable b1 den Fehler: "SA0162: Missing comment for 'b1'".

// Comment for MAIN program
PROGRAM MAIN
VAR
    b1  : BOOL;
    // Comment for variable b2
    b2  : BOOL;
    b3  : BOOL;                  // Comment for variable b3
END_VAR

 

SA0163: Verschachtelte Kommentare

Funktion

Ermittelt Codestellen, an denen verschachtelte Kommentare sind.

Begründung

Verschachtelte Kommentare sind schwer zu lesen und sollten deswegen vermieden werden.

Wichtigkeit

Niedrig

PLCopen-Regel

C3

Beispiele:

Die im folgenden Beispiel entsprechend bezeichneten vier verschachtelten Kommentare führen jeweils zu dem Fehler: "SA0163: Nested comment '<…>'".

(* That is
(* nested comment number 1 *)
*)
PROGRAM MAIN
VAR
    (* That is
    // nested comment
    number 2 *)
    a        : DINT;
    b        : DINT;
 
    (* That is
    (* nested comment number 3 *) *)
    c        : BOOL;
    nCounter : INT;
END_VAR
(* That is // nested comment number 4 *) 
 
nCounter := nCounter + 1;
 
(* This is not a nested comment *)

 

SA0164: Mehrzeilige Kommentare

Funktion

Ermittelt Codestellen, an denen der Mehrzeilenkommentar-Operator (* *) verwendet wird. Erlaubt sind nur die beiden Einzeilenkommentar-Operatoren // für Standardkommentare und /// für Dokumentationskommentare.

Begründung

Einige Programmierrichtlinien verbieten mehrzeilige Kommentare im Code, weil Anfang und Ende eines Kommentars aus dem Blickfeld geraten könnten und die schließende Kommentarklammer durch einen Fehler gelöscht werden könnte.

Wichtigkeit

Niedrig

PLCopen-Regel

C5

 

Regeln - Übersicht und Beschreibung 16:

Sie können diese Prüfung mit dem Pragma {analysis ...} deaktivieren, auch für Kommentare im Deklarationsteil.

Beispiele:

(*
This comment leads to error:
"SA0164 …"
*)
PROGRAM MAIN
VAR
    /// Documentation comment not reported by SA0164
    nCounter1: DINT;
    nCounter2: DINT;             // Standard single-line comment not reported by SA0164
END_VAR
(* This comment leads to error: "SA0164 …" *)
nCounter1 := nCounter1 + 1;
nCounter2 := nCounter2 + 1;

 

SA0166: Maximale Anzahl an Eingabe-/Ausgabe-/VAR_IN_OUT-Variablen

Funktion

Die Prüfung ermittelt, ob eine definierte Anzahl an Eingabevariablen (VAR_INPUT), Ausgabevariablen (VAR_OUTPUT) oder VAR_IN_OUT-Variablen in einem Baustein überschritten wird.

Die Parameter, die bei dieser Prüfung berücksichtigt werden, können Sie konfigurieren, indem Sie innerhalb der Regelkonfiguration auf die Zeile von Regel 166 doppelklicken (SPS-Projekteigenschaften > Kategorie "Static Analysis" > Registerkarte "Regeln" > Regel 166). In dem aufgehenden Dialog können Sie folgende Einstellungen vornehmen:

  • Maximale Anzahl an Eingängen (Standardwert: 10)
  • Maximale Anzahl an Ausgängen (Standardwert: 10)
  • Maximale Anzahl an Ein-/Ausgängen (Standardwert: 10)

Begründung

Es geht um die Überprüfung von individuellen Programmierrichtlinien. Viele Programmierrichtlinien sehen für Bausteine eine maximale Anzahl an Parametern vor. Zu viele Parameter machen den Code unleserlich und die Bausteine schwer zu testen.

Wichtigkeit

Mittel

PLCopen-Regel

CP23

Beispiel:

Regel 166 ist mit folgenden Parametern konfiguriert:

Für den folgenden Funktionsbaustein werden somit zwei Fehler SA0166 gemeldet, da zu viele Eingänge (> 0) und zu viele Ein-/Ausgänge (> 1) deklariert sind.

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample         // => SA0166
VAR_INPUT
    bIn     : BOOL;
END_VAR
VAR_OUTPUT
    bOut    : BOOL;
END_VAR
VAR_IN_OUT
    bInOut1 : BOOL;
    bInOut2 : BOOL;
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

Funktionsbausteine haben einen Zustand, der meist über mehrere SPS-Zyklen hinweg erhalten bleibt. Eine Instanz auf dem Stack existiert nur für die Dauer des Funktionsaufrufs. Es ist daher nur selten sinnvoll, eine Instanz als temporäre Variable anzulegen. Zweitens sind Funktionsbausteininstanzen häufig groß und verbrauchen sehr viel Platz auf dem Stack (der auf Steuerungen meist begrenzt ist). Drittens kann die Initialisierung und häufig auch die Terminierung eines Funktionsbausteins ziemlich viel Zeit in Anspruch nehmen.

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

 

SA0168: Unnötige Zuweisungen

Funktion

Ermittelt Zuweisungen auf Variablen, die keine Auswirkungen im Code haben.

Begründung

Wenn einer Variablen mehrfach Werte zugewiesen werden, ohne dass die Variable zwischen den Zuweisungen ausgewertet wird, wirken sich die ersten Zuweisungen nicht auf das Programm aus.

Wichtigkeit

Niedrig

Beispiel:

PROGRAM MAIN
VAR
    nVar1   : DWORD;
    nVar2   : DWORD;
END_VAR
nVar1 := 1;
 
IF nVar2 > 100 THEN
    nVar2 := 0;
    nVar2 := nVar2 + 1;
END_IF
 
nVar1 := 2;                      // => SA0168

 

SA0169: Ignorierte Ausgänge

Funktion

Ermittelt die Ausgänge von Methoden und Funktionen, die beim Aufruf der Methode oder Funktion nicht angegeben werden.

Begründung

Ignorierte Ausgänge können ein Hinweis auf nicht behandelte Fehler oder sinnlose Funktionsaufrufe sein, da Ergebnisse nicht verwendet werden.

Wichtigkeit

Mittel

Beispiel:

Funktion F_Sample:

FUNCTION F_Sample : BOOL
VAR_INPUT
    bIn     : BOOL;
END_VAR
VAR_OUTPUT
    bOut    : BOOL;
END_VAR

Programm MAIN:

PROGRAM MAIN
VAR
    bReturn : BOOL;
    bFunOut : BOOL;
END_VAR
bReturn := F_Sample(bIn := TRUE , bOut => bFunOut);
bReturn := F_Sample(bIn := TRUE);                          // => SA0169

 

SA0170: Adresse einer Ausgangsvariablen sollte nicht verwendet werden

Funktion

Ermittelt Codestellen, an denen die Adresse einer Ausgangsvariablen (VAR_OUTPUT, VAR_IN_OUT) eines Funktionsbausteins ermittelt wird.

Begründung

Es ist nicht erlaubt, in folgender Weise die Adresse eines Funktionsbausteinausgangs zu verwenden:

  • Via ADR-Operator
  • Via REF=

Ausnahme

Es wird kein Fehler gemeldet, wenn der Ausgang innerhalb desselben Funktionsbausteins verwendet wird.

Wichtigkeit

Mittel

Beispiel:

Funktionsbaustein FB_Sample:

FUNCTION_BLOCK FB_Sample
VAR_INPUT
    nIn          : INT;
END_VAR
VAR_OUTPUT
    nOut         : INT;
END_VAR
VAR
    pFB          : POINTER TO FB_Sample;
    pINT         : POINTER TO INT;
END_VAR
IF pFB <> 0 THEN
    pINT := ADR(pFB^.nOut);                    // => SA0170
END_IF
 
nOut := nIn;
pINT := ADR(THIS^.nOut);                       // no error due to internal usage
pINT := ADR(nOut);                             // no error due to internal usage

Zugriffe innerhalb eines anderen Bausteins, in diesem Fall im Programm MAIN:

PROGRAM MAIN
VAR
    fbSample     : FB_Sample;
    pExternal    : POINTER TO INT;
    refExternal  : REFERENCE TO INT;
END_VAR
pExternal   := ADR(fbSample.nOut);             // => SA0170
refExternal REF= fbSample.nOut;                // => SA0170

 

SA0171: Enumerationen sollten das Attribut 'strict' haben

Funktion

Ermittelt Deklarationen von Enumerationen, die nicht mit dem Attribut {attribute 'strict'} versehen sind.

Begründung

Das Attribut {attribute 'strict'} bewirkt, dass Compilerfehler ausgegeben werden, wenn der Code gegen strikte Programmierregeln für Enumerationen verstößt. Standardmäßig wird beim Anlegen einer neuen Enumeration die Deklaration automatisch mit dem Attribut 'strict' versehen.

Wichtigkeit

Hoch

Für weitere Informationen siehe: PLC > Referenz Programmierung > Pragmas > Attribut-Pragmas > Attribut 'strict'

Beispiel:

{attribute 'qualified_only'}
{attribute 'strict'}
TYPE E_TrafficLight :
(
    eRed := 0,
    eYellow,
    eGreen
);
END_TYPE
{attribute 'qualified_only'}
TYPE E_MachineStates :           // => SA0171
(
    eStopped := 0,
    eRunning,
    eError
);
END_TYPE

 

SA0175: Verdächtige Operation auf String

Funktion

Ermittelt Codestellen, die bei einer UTF-8-Kodierung verdächtig sind.

Erfasste Konstrukte

  1. Indexzugriff auf einen Single-Byte-String
    • Beispiel: sVar[2]
    • Meldung: Verdächtige Operation auf String: Indexzugriff '<expression>'
  2. Adresszugriff auf einen Single-Byte-String
    • Beispiel: ADR(sVar)
    • Meldung: Verdächtige Operation auf String: Möglicher Indexzugriff '<expression>'
  3. Aufruf einer String-Funktion der Tc2_Standard-Bibliothek außer CONCAT und LEN
    • Beispiel: FIND(sVar, 'a');
    • Meldung: Verdächtige Operation auf String: Möglicher Indexzugriff '<expression>'
  4. Einzelnes Byte-Literal, das Nicht-ASCII-Zeichen enthält
    • Beispiele:
      sVar := '99€';
      sVar := 'Ä';
    • Meldung: Verdächtige Operation auf String: Literal '<literal>' enthält Nicht-ASCII-Zeichen

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