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'"

Static Analysis Light 1:

TwinCAT only analyses the program code of the current project, but not libraries.

Static Analysis Light 2:

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.

Static Analysis Light 3:

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.

Static Analysis Light 4:

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

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

Static Analysis Light 5:

See also rule SA0103.

Static Analysis Light 6:

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

Static Analysis Light 7:

This rule cannot be disabled via a pragma or attribute!
For more information on attributes, see Pragmas and attributes.

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:

  • The name of an enumeration constant is identical to the name in another enumeration within the application or in an included library.
  • The name of a variable is identical to the name of another object in the application or in an included library.
  • The name of a variable is identical to the name of an enumeration constant in an enumeration in the application or in an included library.
  • The name of an object is identical to the name of another object in the application or in an included library.

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

  • Function blocks have a state that is usually retained over several PLC cycles. An instance on the stack exists only for the duration of the function call. It is therefore only rarely useful to create an instance as a temporary variable.
  • Secondly, function block instances are frequently large and require a great deal of space on the stack (which is usually limited on controllers).
  • Thirdly, the initialization and often also the scheduling of the function block can take up quite a lot of time.

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

  1. Index access to a single-byte string
    • Example: sVar[2]
    • Message: Suspicious operation on string: index access '<expression>'
  2. Address access to a single-byte string
    • Example: ADR(sVar)
    • Message: Suspicious operation on string: Possible index access '<expression>'
  3. Call of a string function of the Tc2_Standard library except CONCAT and LEN
    • Example: FIND(sVar, 'a');
    • Message: Suspicious operation on string: Possible index access '<expression>'
  4. Single byte literal containing non-ASCII characters
    • Examples:
      sVar := '99€';
      sVar := 'Ä';
    • Message: Suspicious operation on string: literal '<literal>' contains non-ASCII characters

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.

Static Analysis Light 8:

Rules that are disabled in the project properties cannot be activated by a pragma or attribute.

Static Analysis Light 9:

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