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 }
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
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.
Finally, because function argument refactoring works at a higher level thann simple identifiers, the following limitations hold.