Static Analysis Light
Before a project is loaded onto the target system, TwinCAT 3 PLC uses "Static Code Analysis" to check whether the source code follows specified coding guidelines.
The license-free version of the static code analysis is called "Static Analysis Light". The checks configured there are automatically performed following each successful code generation. The required set of rules is defined in the PLC project properties under Static Analysis.
Deviations from the rules are issued as error messages in the Error List. Each rule has a unique number. If a violation of a rule is detected during the static analysis, the rule number is output in the error list together with an error description based on the following syntax. The abbreviation "SA" stands for "Static Analysis".
Syntax: "SA<rule number>: <rule description>"
Sample for rule number 33 (Unused variables): "SA0033: Not used: Variable 'bSample'"
TwinCAT only analyses the program code of the current project, but not libraries. |
Note that the Static Analysis Light is automatically executed following the successful compilation process. If, on the other hand, the code generation was not successful, i.e. if the compiler detected compilation errors, the Static Analysis Light is not executed. |
You can use pragmas to disable checks for certain code parts (see below). |
Configuring the rule set
The Static Analysis category of the PLC project properties defines the checks carried out by the Light version of the static code analysis whenever code is generated.
Scope of the Static Analysis configuration The parameters you set in the category Static Analysis of the PLC project properties are referred to as Solution options and therefore affect not only the PLC project whose properties you currently edit. The configured rule set is used for all PLC projects in the development environment. |
Available rules within the Static Analysis Light:
- SA0033: Unused variables
- SA0028: Overlapping memory areas
- SA0006: Write access from several tasks
- SA0004: Multiple writes access on output
- SA0027: Multiple usage of name
- SA0167: Report temporary FunctionBlock instances
- SA0175: Suspicious operation on string
SA0033: Unused variables
Function | Determines variables that are declared but not used within the compiled program code. |
Reason | Unused variables make a program less easy to read and maintain. Unused variables occupy unnecessary memory space and take up unnecessary runtime during the initialization. |
Importance | medium |
PLCopen rule | CP22/CP24 |
SA0028: Overlapping memory areas
Function | Determines the points due to which two or more variables occupy the same storage space. |
Reason | If two variables occupy the same storage space, the code may behave very unexpectedly. This must be avoided in all cases. If the use of a value in different interpretations is unavoidable, for example once as DINT and once as REAL, you should define a UNION. Also, via a pointer you can access a value typed otherwise without converting the value. |
Importance | High |
Sample:
In the following sample both variables use byte 21, i.e. the memory areas of the variables overlap.
PROGRAM MAIN
VAR
nVar1 AT%QB21 : INT; // => SA0028
nVar2 AT%QD5 : DWORD; // => SA0028
END_VAR
SA0006: Write access from several tasks
Function | Determines variables with write access from more than one task. |
Reason | A variable that is written in several tasks may change its value unexpectedly under certain circumstances. This can lead to confusing situations. String variables and, on some 32-bit systems, 64-bit integer variables also may even assume an inconsistent state if the variable is written in two tasks at the same time. |
Exception | In certain cases it may be necessary for several tasks to write a variable. Make sure, for example through the use of semaphores, that the access does not lead to an inconsistent state. |
Importance | High |
PLCopen rule | CP10 |
See also rule SA0103. |
Call corresponds to write access Please note that calls are interpreted as write access. For example, calling a method for a function block instance is regarded as a write access to the function block instance. A more detailed analysis of accesses and calls is not possible, e.g. due to virtual calls (pointers, interface). To deactivate rule SA0006 for a variable (e.g. for a function block instance), the following attribute can be inserted above the variable declaration: {attribute 'analysis' := '-6'} |
Examples:
The two global variables nVar and bVar are written by two tasks.
Global variable list:
VAR_GLOBAL
nVar : INT;
bVar : BOOL;
END_VAR
Program MAIN_Fast, called from the task PlcTaskFast:
nVar := nVar + 1; // => SA0006
bVar := (nVar > 10); // => SA0006
Program MAIN_Slow, called from the task PlcTaskSlow:
nVar := nVar + 2; // => SA0006
bVar := (nVar < -50); // => SA0006
SA0004: Multiple write access on output
Function | Determines outputs that are written at more than one position. |
Reason | The maintainability suffers if an output is written in various places in the code. It is then unclear which write access is actually affecting the process. It is good practice to perform the calculation of the output variables in auxiliary variables and to assign the calculated value to a point at the end of the cycle. |
Exception | No error is issued if an output variable is written in different branches of IF or CASE statements. |
Importance | High |
PLCopen rule | CP12 |
This rule cannot be disabled via a pragma or attribute! |
Sample:
Global variable list:
VAR_GLOBAL
bVar AT%QX0.0 : BOOL;
nSample AT%QW5 : INT;
END_VAR
MAIN program:
PROGRAM MAIN
VAR
nCondition : INT;
END_VAR
IF nCondition < INT#0 THEN
bVar := TRUE; // => SA0004
nSample := INT#12; // => SA0004
END_IF
CASE nCondition OF
INT#1:
bVar := FALSE; // => SA0004
INT#2:
nSample := INT#11; // => SA0004
ELSE
bVar := TRUE; // => SA0004
nSample := INT#9; // => SA0004
END_CASE
SA0027: Multiple usage of name
Function | Determines multiple use of a variable name/identifier or object name (POU) within the scope of a project. The following cases are covered:
|
Reason | Identical names can be confusing when reading the code. They can lead to errors if the wrong object is accessed inadvertently. Therefore, define and follow naming conventions in order to avoid such situations. |
Exception | Enumerations declared with the 'qualified_only' attribute are exempt from SA0027 checking because their elements can only be accessed in a qualified manner. |
Importance | Medium |
Sample:
The following sample generates error/warning SA0027, since the library Tc2_Standard is referenced in the project, which provides the function block TON.
PROGRAM MAIN
VAR
ton : INT; // => SA0027
END_VAR
SA0167: Report temporary FunctionBlock instances
Function | Determines function block instances that are declared as temporary variables. This applies to instances that are declared in a method, in a function or as VAR_TEMP, and which are reinitialized in each processing cycle or each function block call. |
Reason |
|
Importance | Medium |
Examples:
Method FB_Sample.SampleMethod:
METHOD SampleMethod : INT
VAR_INPUT
END_VAR
VAR
fbTrigger : R_TRIG; // => SA0167
END_VAR
Function F_Sample:
FUNCTION F_Sample : INT
VAR_INPUT
END_VAR
VAR
fbSample : FB_Sample; // => SA0167
END_VAR
MAIN program:
PROGRAM MAIN
VAR_TEMP
fbSample : FB_Sample; // => SA0167
nReturn : INT;
END_VAR
nReturn := F_Sample();
SA0175: Suspicious operation on string
Function | Determines code positions that are suspicious for UTF-8 encoding. |
Captured constructs |
|
Importance | Medium |
Examples:
VAR
sVar : STRING;
pVar : POINTER TO STRING;
nVar : INT;
END_VAR
// 1) SA0175: Suspicious operation on string: Index access
sVar[2]; // => SA0175
// 2) SA0175: Suspicious operation on string: Possible index access
pVar := ADR(sVar); // => SA0175
// 3) SA0175: Suspicious operation on string: Possible index access
nVar := FIND(sVar, 'a'); // => SA0175
// 4) SA0175: Suspicious operation on string: Literal '<...>' contains Non-ASCII character
sVar := '99€'; // => SA0175
sVar := 'Ä'; // => SA0175
Pragmas and attributes for Static Analysis Light
A pragma or an attribute can be used to exclude certain parts of the code from the check. Use the pragma {analysis ...}, to turn off coding rules in the implementation part and the attribute {attribute 'analysis' := '...'}, to turn off coding rules in the declaration part.
Requirement: You have activated the rules in the project properties.
In addition, you can use the attribute {attribute 'no-analysis'} to exclude programming objects from the static analysis.
Rules that are disabled in the project properties cannot be activated by a pragma or attribute. |
Rule SA0004 (Multiple writes access on output) cannot be disabled by a pragma. |
Pragma {analysis ...}
You can use the pragma {analysis -/+<rule number>} in the implementation part of a programming block in order to disregard individual coding rules for the following code lines. Coding rules are deactivated by specifying the rule numbers preceded by a minus sign ("-"). For activation they are preceded by a plus sign ("+"). You can specify any number of rules in the pragma with the help of comma separation.
Insertion location:
- Deactivation of rules: In the implementation part of the first code line from which the code analysis is disabled with {analysis - ...}.
- Activation of rules: After the last line of the deactivation with {analysis + ...}.
- For rule SA0164, the pragma can also be inserted in the declaration part before a comment.
Syntax:
- Deactivation of rules:
- one rule: {analysis -<rule number>}
- several rules: {analysis -<rule number>, -<further rule number>, -<further rule number>}
- Activation of rules:
- one rule: {analysis +<rule number>}
- several rules: {analysis +<rule number>, +<further rule number>, +<further rule number>}
Samples:
Rule 24 (only typed literals permitted) is to be disabled for one line (i.e. in these lines it is not necessary to write "nTest := DINT#99") and then enabled again:
{analysis -24}
nTest := 99;
{analysis +24}
nVar := INT#2;
Specification of several rules:
{analysis -10, -24, -18}
Attribute {attribute 'analysis' := '...'}
You can use the attribute {attribute 'analysis' := '-<rule number>'} to switch off certain rules for individual declarations or for a complete programming object. The code rule is deactivated by specifying the rule number(s) with a minus sign in front. You can specify any number of rules in the attribute.
Insertion location:
above the declaration of a programming object or in the line above a variable declaration
Syntax:
- one rule: {attribute 'analysis' := '-<rule number>'}
- several rules: {attribute 'analysis' := '-<rule number>, -<further rule number>, -<further rule number>'}
Samples:
Rule 33 (unused variables) is to be disabled for all variables of the structure.
{attribute 'analysis' := '-33'}
TYPE ST_Sample :
STRUCT
bMember : BOOL;
nMember : INT;
END_STRUCT
END_TYPE
Checking of rules 28 (overlapping memory areas) and 33 (unused variables) is to be disabled for variable nVar1.
PROGRAM MAIN
VAR
{attribute 'analysis' := '-28, -33'}
nVar1 AT%QB21 : INT;
nVar2 AT%QD5 : DWORD;
nVar3 AT%QB41 : INT;
nVar4 AT%QD10 : DWORD;
END_VAR
Rule 6 (concurrent access) is to be disabled for a global variable, so that no error message is generated if write access to the variable occurs from more than one task.
VAR_GLOBAL
{attribute 'analysis' := '-6'}
nVar : INT;
bVar : BOOL;
END_VAR
Attribute {attribute 'no-analysis'}
You can use the {attribute 'no-analysis'} attribute to exclude an entire programming object from the static analysis check. For this programming object no checks are carried out for the coding rules, naming conventions and forbidden symbols.
Insertion location:
above the declaration of a programming object
Syntax:
{attribute 'no-analysis'}
Samples:
{attribute 'qualified_only'}
{attribute 'no-analysis'}
VAR_GLOBAL
…
END_VAR
{attribute 'no-analysis'}
PROGRAM MAIN
VAR
…
END_VAR