Static Code Analyzer - ANSI-C

Overview

Preprocessor

Preinclude Header File (ANSI-C)

Standard Library Header Files

Dealing with Syntax Extensions

Scope of Symbols

Stack Depth for Root Functions

Overview

Development Assistant for C enables you to perform C source file analysis, as well as generating various data based on the results.

Changes in Options > Project, Options > Compiler, Options > Assembler and Options > Static Code Analyzer..., may affect the way the source files are analyzed. These changes will take effect in further source analyses. This is why it is a good idea to make a habit of executing Start > Build Database after such changes.

What C language standards does DA-C comply with?

DA-C symbol analysis is strongly depending on selected C dialect (compiler) because the main goal and advantage of DA-C analyzer is to mimic analysis of your compiler. If ANSI C dialect is selected than DA-C analysis is ANSI-C (C89) compliant. ISO 9899:1999 (C99) features are supported only if selected C dialect supports them through extensions. For example, GNU C dialect and compilers based on GNU C (Green Hills in GNU mode or Keil RealView ARM in GNU mode) supports most of C99 features - variable-length arrays, designated initializers, compound literals, support for variadic macros (macros of variable arity), etc. C++ comments are supported (switch on in a dialog option). To be sure if partial dialect is C99 compliant refer to your compiler documentation.

When generating information about global symbols, DA-C performs tasks similar to those performed by the linker. This allows conflicts between global symbols with the same name to be detected, even if the linker cannot find them. For example, if a public name is declared as int in one module, and long in other, DA-C will consider this a mistake and generate an appropriate error message. In this case, the analysis will generate two symbols with the same name, each with its own list of references. If a symbol is declared with const, unsigned, signed or volatile attribute, and if that attribute is omitted in an another declaration, DA-C will generate a warning, and consider this as two declarations of the same symbol.

If a symbol is defined more than once, an error message will be generated by DA-C, and the superfluous definitions will be considered declarations.

Preprocessor

The preprocessor implemented in DA-C supports macro substitutions, source file inclusion, and conditional compilation. Its functioning is coordinated with the selected C dialect for a particular project by means of its behaving exactly like the preprocessor of the selected compiler. DA-C allows the possibility of using the traditional preprocessor. Using the Options > Static Code Analyzer... command, preprocessing options which determine predefined names as well as availability of C++ comments, can be set.

DA-C supports a set of directives of the selected dialect which affect analysis; certain #pragma directives are also supported. #pragma lib can be used in headers containing function and variable declarations not included in the source code, thus avoiding messages about undefined symbols, if defined by warnings. The header will be treated as a standard library header file. The use of this directive is not necessary in standard headers, since it is assumed for all header files the names of which are given between "<" and ">" characters. Directives for writing/displaying messages (for example #info, #warn...), #assert directives, and conditional inclusion directives in the case of compilers / dialects which support them are also supported.

If the sizeof operator is used in conditional directives (#if), you can manually specify resulting values for each sizeof argument value, in the appropriate section of the project file, for example:

[sizeof]
int=2

The previous example specifies the value of the expression sizeof (int) as 2. In other cases, i.e. in the case of a non-standard expression in a conditional directive, the expression sizeof (int) would equal 1 and generate warnings 314 or 315.

If there are cases in the source where in the #if and #elif directives identifiers which are not macros are used, for example:

enum { ... _PROCESSOR_XYZ_; ... };

#if ( _PROCESSOR_ == _PROCESSOR_XYZ_ )
...
#endif

DA-C will not recognize them and will not assign the correct values to those identifiers, which will lead to errors in the analysis. The solution to this problem is to assign their values in the [PP-MACRO-VALUES] section of the project file. Set values of identifiers in the form:

identifier = value

The value of an identifier can be seen only in the #if and #elif directives, and the names are not case sensitive. Such identifiers that are not found in this section, used in the #if and #elif directives, are treated as zero.

Using “#pragma dac error” directive:

#pragma dac error nnn[+|-|.]

Some DA-C error messages may not be suppressed by this #pragma (e.g. file access, ...).

Using “#pragma dac warning” directive (or “#pragma dac warn”): 

#pragma dac warn[ing] nnn[+|-|.]

Using #pragma warn directive in your source code overrides warning messages options set in the DA-C's Options > Static Code Analyzer... > Warnings, so you can control which warnings are displayed on per-file and per-code-block basis. 

Note that multiple warnings modifiers can be issued in a single line, like in the following example: 

#pragma dac warning 300-320. 333+

Example: If your source code contains the following #pragma directives: 

#pragma dac warning 304+
#pragma dac warning 318-
#pragma dac warning 307.

The warning with the code 304 will be enabled, the warning with the code 318 will be disabled, and the warning with the code 307 will be restored to the value it had when the analysis of the file began (the value set in the DA-C options). 

In addition to DA-C's standard C warnings, the MISRA C warnings can be controlled, too. Use the following syntax for that purpose: 

MISRA C:1998

#pragma dac misra nnn[+|-|.]
#pragma dac m1998 nnn[+|-|.]

Note that multiple MISRA C warnings modifiers can be issued in a single line, like in the following example: 

#pragma dac misra 18-59. 65+
#pragma dac m1998 18-59. 65+

MISRA C:2004

#pragma dac m2004 nnmm[+|-|.]
#pragma dac m2004 nn.mm[+|-|.]

where nnmm is formed by removing '.' (dot) from MISRA C:2004 rule "nn.mm".

#pragma dac m2004 16.10-   // Suppress warning for MISRA C:2004 rule 16.10
#pragma dac m2004 1610.    // Reset rule 16.10 warning state as configured

Note that multiple MISRA C warnings modifiers can be issued in a single line, like in the following example: 

#pragma dac m2004 8.10-8.12. 106+

MISRA C:2012

#pragma dac m2012 nnmm[+|-|.]
#pragma dac m2012 nn.mm[+|-|.]

where nnmm is formed by removing '.' (dot) from MISRA C:2012 rule "nn.mm".

#pragma dac m2012 1610-    // Suppress warning for MISRA C:2012 rule 16.10
#pragma dac m2012 16.10.   // Reset rule 16.10 warning state as configured

Note that multiple MISRA C warnings modifiers can be issued in a single line, like in the following example: 

#pragma dac m2012 8.10-8.12. 106+

Source Code Metrics and Coding Convention Checking

Source Code Metrics and Coding Convention Checking Warnings can not be controlled by "#pragma dac warning" directive.

Preinclude Header File (ANSI-C) 

Standard predefined identifiers __LINE__, __FILE__, __DATE__, __TIME__, are built-in the preprocessor. Also, __DA_C__ is defined for use in potential modifications of the DA-C source code. Should any of these identifiers be used for some other purposes in the project, it can be undefined using the #undef directive. Some projects may require other predefined names, like __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__... They can be defined using the Defines segment of the Options > Static Code Analyzer... dialog box. None of these names will be defined by activating any C dialect, since their values may vary. These definitions can be entered in a file and used through Options > Compiler / Preinclude file. Ready-made preinclude files for the majority of supported compilers are included with DA-C.

Preinclude Header File will be processed before Source Module is processed. It is the possibility for fine tuning of DA-C for your Compiler Dialect.

Standard Library Header Files

Header files with names given between "<" and ">" characters in the #include directive are considered standard library files. By an adequate Warnings setting, all warnings generated in these files can be suppressed. This is especially useful for avoiding undesired messages about unused symbols. Headers containing the #pragma lib directive and all headers contained in directories defined as "Library header directories" in "Directories" tab of "Project Options" are considered standard library files. Compiler headers are always treated as library headers if "Set 'library' attribute for compiler headers" option is checked in "Compiler options".

Dealing with Syntax Extensions

The C language extensions used in C dialects listed in the Options > Compiler dialog box are normally handled by source analysis.

If the analyzed source contains unsupported extensions, they can be handled by defining suitable preprocessor macros. For example, a macro which replaces a storage class specified with nothing can be set in the Defines segment of the Options > Static Code Analyzer... dialog box.

An alternative method would be to customize analyzed source for DA-C by means of the __DA_C__ macro identifier. For example, DA-C analysis does not recognize calling functions indirectly via pointers. As a consequence, the following program fragment:

void func1(void){}
void func2(void){}

void call_via_ptr(void (*ptr_to_func)(void))
{
   ptr_to_func();
}

main()
{
   call_via_ptr(&func1);
   call_via_ptr(&func2);
}

produces an incorrect database.

To get an accurate database, the code fragment from above should be modified by utilizing __DA_C___ macro as follows:

void func1(void){}
void func2(void){}

void call_via_ptr(void (*ptr_to_func)(void))
{
#ifndef __DA_C__

   // this code is processed only by compiler
   ptr_to_func();

#else
  
   // this code is processed only by DA-C analysis
   func1(); // make an explicit call of every possible
   func2(); // function that can be called via ptr_to_func

#endif
}

main()
{
#ifndef __DA_C__
  
   // this code is processed only by compiler
   call_via_ptr(&func1);
   call_via_ptr(&func2);

#else
  
   // this code is processed only by DA-C analysis
   call_via_ptr(0); // do not take addresses of func1 and func2,
   call_via_ptr(0); // because DA-C interprets it as function call

#endif
}

Scope of Symbols

The C language does not provide a mechanism for defining public, file and local types. This is why structures, unions, and enumerations defined out of functions are added to public = global. Type definition names and macros defined in an header file are also added to public = global. Module scope is identical with file scope, local scope is identical with block scope.

Stack Depth for Root Functions

In case of multiple static scope functions with the same name, the one with the greatest stack depth will be chosen for display. Please note that in "With Stack Depth" display mode, Graph configuration option "max recursive depth" is not considered, so graph expansion is unlimited.