# Shortcomings

The nature of the C language and its preprocessor can result in pathological cases that can confuse the CScout analysis and substitution engine. In all cases the confusion only results in erroneous analysis or substitutions of the particular identifiers and will not affect other parts of the code. In some cases you can even slightly modify your workspace definition or code to ensure CScout works as you intend. The following cases are the most important in recognising and substituting identifiers:

1. Conditional compilation

   Some programs have parts of them compiled under conditional preprocessor directives. Consider the following example:

   #ifdef unix
   #include <unistd.h>
   #define erase_file(x) unlink(x)
   #endif
   #ifdef WIN32
   #include <windows.h>
   #define erase_file(x) DeleteFile(x)
   #endif
   main(int argc, char *argv[])
   {
   erase_file(argv[1]);
   }
   As humans we can understand that `erase_file` occurs three times within the file. However, because CScout preprocesses the file following the C preprocessor semantics, it will typically match only two instances. In some cases you can get around this problem by defining macros that will ensure that all code inside conditional directives gets processed. In other cases this will result in errors (e.g. a duplicate macro definition in the above example). In such cases you can include in your workspace the same project multiple times, each time with a different set of defined macros.

       workspace example {
       	project idtest {
       		define DEBUG 1
       		define TEST 1
       		file idtest.c util.c
       	}
       	project idtest2 {
       		define NDEBUG 1
       		define PRODUCTION
       		file idtest.c util.c
       }

2. Partial coverage of macro use

   Consider the following example:

   struct s1 {
   int id;
   } a;
   struct s2 {
   char id;
   } b;
   struct s3 {
   double id;
   } c;
   #define getid(x) ((x)->id)
   main()
   {
   printf("%d %c", getid(a), getid(b));
   }
   In the above example, changing an `id` instance should also change the other three instances. However, CScout will not associate the member of `s3` with the identifier appearing in the `getid` macro or the `s1` or `s2` structures, because there is no `getid` macro invocation to link them together. If e.g. `id` is replaced with `val` the program will compile and function correctly, but when one tries to access the `c` struture's member in the future using `getid` an error will result.
   struct s1 {
   int val;
   } a;
   struct s2 {
   char val;
   } b;
   struct s3 {
   double id;
   } c;
   #define getid(x) ((x)->val)
   main()
   {
   printf("%d %c", getid(a), getid(b)); /* OK */
   printf(" %g", getid(c)); /* New statement: error */
   }
   To avoid this (rare) problem you can introduce dummy macro invocations of the form:
   #ifdef CSCOUT
   (void)getid(d)
   #endif
3. Undefined macros

   We employ a heuristic classifying all instances of an undefined macro as being the same identifier. Thus in the following sequence `foo` will match all three macro instances:

   #undef foo
   #ifdef foo
   #endif
   #ifdef foo
   #endif
   #define foo 1
   In most cases this is what you want, but there may be cases where the macro appears in different files and with a different meaning. In such cases the undefined instances of the macro will erroneously match the defined instance.

In addition, the analysis of functions can be confused by the following situations.

1. Functions getting called through function pointers will not appear in the call graphs. This is a common limitation of static call analysis.
2. Function-like macros called from inside function bodies that were generated by macro expansion will not be registered as calls.
3. Non-function like macros that expand into function calls will not appear in the call graph; the corresponding functions will appear to be called by the function containing the macro.

Finally, because function argument refactoring works at a higher level thann simple identifiers, the following limitations hold.

1. When a function call's arguments macro-expand into unballanced brackets or into multiple function arguments the replacement can misbehave.
2. When there is not a one-to-one correspondence between a function's name and its associated identifier (i.e. when the function's name is generated through macro-token concatenation) the function argument refactoring is not offered as an option.

Contents | « Previous Next (Error Messages) »