Header File Inclusion Static Analysis Tools

Header file inclusion static analysis tools?

The output of gcc -w -H <file> might be useful (If you parse it and put some counts in) the -w is there to suppress all warnings, which might be awkward to deal with.

From the gcc docs:

-H

Print the name of each header file used, in addition to other normal activities. Each name is indented to show how deep in the
#include stack it is. Precompiled header files are also printed,
even if they are found to be invalid; an invalid precompiled header
file is printed with ...x and a valid one with ...!.

The output looks like this:

. /usr/include/unistd.h
.. /usr/include/features.h
... /usr/include/bits/predefs.h
... /usr/include/sys/cdefs.h
.... /usr/include/bits/wordsize.h
... /usr/include/gnu/stubs.h
.... /usr/include/bits/wordsize.h
.... /usr/include/gnu/stubs-64.h
.. /usr/include/bits/posix_opt.h
.. /usr/include/bits/environments.h
... /usr/include/bits/wordsize.h
.. /usr/include/bits/types.h
... /usr/include/bits/wordsize.h
... /usr/include/bits/typesizes.h
.. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.. /usr/include/bits/confname.h
.. /usr/include/getopt.h
. /usr/include/stdio.h
.. /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.. /usr/include/libio.h
... /usr/include/_G_config.h
.... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h
.... /usr/include/wchar.h
... /usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stdarg.h
.. /usr/include/bits/stdio_lim.h
.. /usr/include/bits/sys_errlist.h
Multiple include guards may be useful for:
/usr/include/bits/confname.h
/usr/include/bits/environments.h
/usr/include/bits/predefs.h
/usr/include/bits/stdio_lim.h
/usr/include/bits/sys_errlist.h
/usr/include/bits/typesizes.h
/usr/include/gnu/stubs-64.h
/usr/include/gnu/stubs.h
/usr/include/wchar.h

Static analysis of header inclusion in C++

I have used checkheaders with some success. Development seems to have slowed down some in the last year, but it is still usable. Probably it's best to use the trunk version.

Basic question about header file inclusion

Multiple inclusion of the same header file is not a problem (and not a noticeable compile-time increase) if each header is appropriately protected with an "#ifndef" as you did.

Note that the name you used in that "#ifndef" must be unique (different in each header file), so the name "HEADER_FILE" is not very good - it would have been better to call it with a unique name, e.g., "INCLUDED_MAIN_H" (and other header files will use other names). Alternatively, all modern compilers support the "#pragma once" command which is better than ifndef in two ways:

  1. You don't need to invent a unique name (and risk that it's not unique)
  2. The compiler doesn't need to read the header file until the end just to look for the "#endif" - once it sees the "#pragma once" and knows this is the second time reading the same file, it immediately stops reading it.

But even though including the same header file is not a problem, including too many headers in a file that doesn't need it is a problem - it increases compilation time, and also increases incremental compilation time: If you change a header file, and a hundred other files include it, the build system (e.g., make) will need to re-compile all of these hundred other files. So usually I would recommend that each source file (.c) should include only the minimal set of header files that it really needs - rather than include some big "main.h" that includes everything.

Detecting superfluous #includes in C/C++?

It's not automatic, but doxygen will produce dependency diagrams for #included files. You will have to go through them visually, but they can be very useful for getting a picture of what is using what.

Header inclusion order confusion

It is best to include all relevant headers for your class in the header file for that class. Choosing not to do so then requires that you remember to include the necessary headers in every other file that uses your class, and as you add new functionality to your class, and hence the need for additional headers, you then have to visit every place where your class header has been included and add the proper headers to each file.

Tools to find included headers which are unused?

DISCLAIMER: My day job is working for a company that develops static analysis tools.

I would be surprised if most (if not all) static analysis tools did not have some form of header usage check. You could use this wikipedia page to get a list of available tools and then email the companies to ask them.

Some points you might consider when you're evaluating a tool:

For function overloads, you want all headers containing overloads to be visible, not just the header that contains the function that was selected by overload resolution:

// f1.h
void foo (char);

// f2.h
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
foo (0); // Calls 'foo(int)' but all functions were in overload set
}

If you take the brute force approach, first remove all headers and then re-add them until it compiles, if 'f1.h' is added first then the code will compile but the semantics of the program have been changed.

A similar rule applies when you have partial and specializations. It doesn't matter if the specialization is selected or not, you need to make sure that all specializations are visible:

// f1.h
template <typename T>
void foo (T);

// f2.h
template <>
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
foo (0); // Calls specialization 'foo<int>(int)'
}

As for the overload example, the brute force approach may result in a program which still compiles but has different behaviour.

Another related type of analysis that you can look out for is checking if types can be forward declared. Consider the following:

// A.h
class A { };

// foo.h
#include "A.h"
void foo (A const &);

// bar.cc
#include "foo.h"

void bar (A const & a)
{
foo (a);
}

In the above example, the definition of 'A' is not required, and so the header file 'foo.h' can be changed so that it has a forward declaration only for 'A':

// foo.h
class A;
void foo (A const &);

This kind of check also reduces header dependencies.

Solving and fixing vulnerability pointed by the static analysis tool SPLINT

For eliminating the warning for scanf try what the tool suggests:

    (void)scanf("%s", ans);

For eliminating the warning for tolower(ans[0]) == 'y' try this:

    if (tolower(ans[0]) == (int)'y')

But latter shouldn't be a warning because in C the type of a char literal (such as 'y') is int, so IMO the warning is bogous anyway.



Related Topics



Leave a reply



Submit