Tools to Find Included Headers Which Are Unused

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.

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.

C++ unused headers

Is there any problems connected with this idea

Yes. A file can compile successfully even if it has missing include files, so this can have false positives, and may remove headers that are actually used.

It is quite a difficult task to analyse which headers should be included, and which are unnecessary, both manually and automatically. Tools have been made to do the checking automatically. Even if "number of false positives is too big", it's still (in my experience) a small fraction of all included headers, so it is far less work to check results of such tool than compare includes of every file to the entire content of those files. Even the script that you suggest can be better than nothing, as long as you don't remove the includes without manual checking.

It helps manual checking to make the files as small as possible. As a side-effect, this also makes incremental compilation much faster (but compilation form scratch slower).

How should I detect unnecessary #include files in a large C++ project?

While it won't reveal unneeded include files, Visual studio has a setting /showIncludes (right click on a .cpp file, Properties->C/C++->Advanced) that will output a tree of all included files at compile time. This can help in identifying files that shouldn't need to be included.

You can also take a look at the pimpl idiom to let you get away with fewer header file dependencies to make it easier to see the cruft that you can remove.

How to use the tool include-what-you-use together with CMake to detect unused headers?

CMake 3.3 introduced the new target property CXX_INCLUDE_WHAT_YOU_USE that can be set to the path of the program include-what-you-use. For instance this CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
add_executable(hello main.cc)

find_program(iwyu_path NAMES include-what-you-use iwyu REQUIRED)

# If using CGAL<3.18, you remove REQUIRED and use
# if(NOT iwyu_path)
# message(FATAL_ERROR "Could not find the program include-what-you-use")
# endif()

set_property(TARGET hello PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path})

is able to build the file main.cc

#include <iostream>
#include <vector>

int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}

and at the same time have include-what-you-use give out a warning that
the included header vector is not needed.

user@ubuntu:/tmp$ ls ~/hello
CMakeLists.txt main.cc
user@ubuntu:/tmp$ mkdir /tmp/build
user@ubuntu:/tmp$ cd /tmp/build
user@ubuntu:/tmp/build$ ~/cmake-3.3.0-rc2-Linux-x86_64/bin/cmake ~/hello
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/build
user@ubuntu:/tmp/build$ make
Scanning dependencies of target hello
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cc.o
Warning: include-what-you-use reported diagnostics:

/home/user/hello/main.cc should add these lines:

/home/user/hello/main.cc should remove these lines:
- #include <vector> // lines 2-2

The full include-list for /home/user/hello/main.cc:
#include <iostream> // for operator<<, basic_ostream, cout, endl, ostream
---

[100%] Linking CXX executable hello
[100%] Built target hello
user@ubuntu:/tmp/build$ ./hello
Hello World!
user@ubuntu:/tmp/build$

If you want to pass custom options to include-what-you-use, like for instance --mapping_file you can do it via

set(iwyu_path_and_options
${iwyu_path}
-Xiwyu
--mapping_file=${my_mapping})

set_property(TARGET hello
PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${iwyu_path_and_options})

Tool to find all header and source file dependencies in C

Klocwork has an On-the-Fly source code analysis product which can figure out redundant and unused header file. Have a look to see if it fulfills your requirements.

Is there any tool / way to detect/remove all unused variables,macros,headers(includes) and functions from c++ code?

From what I know there is currently no tool that does all the things you have mentioned, however there is one that helps in cleaning up the unused include headers: include-what-you-use

"Include what you use" means this: for every symbol (type, function
variable, or macro) that you use in foo.cc, either foo.cc or foo.h
should #include a .h file that exports the declaration of that symbol.
The include-what-you-use tool is a program that can be built with the
clang libraries in order to analyze #includes of source files to find
include-what-you-use violations, and suggest fixes for them.

The main goal of include-what-you-use is to remove superfluous #includes. It does this both by figuring out what #includes are not actually needed for this file (for both .cc and .h files), and
replacing #includes with forward-declares when possible.

One might expect that the Clang static analyzer would do this, but from what I see the availalbe checks do not offer such things.

This might be a good time for someone to suggest a feature request to the analyzer or create a separate tool using LibTooling on a similar par with the tools described at Clang Tools

In the meantime, I'd suggest you enable -Wall and -Wextra compiler flags, which will trigger the following warnings (among others)(see the GCC docs below):

  • -Wunused-function
  • -Wunused-label
  • -Wunused-value
  • -Wunused-variable
  • -Wunused-parameter
  • -Wunused-but-set-parameter

If for some reason you don't want to do that, you could just add -Wunused which will enable only the above -Wunused options combined, without the other flags that -Wall or -Wextra adds.

But in order to get a warning about an unused function parameter, you
must either specify -Wextra -Wunused (note that -Wall implies
-Wunused), or separately specify -Wunused-parameter.

Of course, this means that you have to do the cleanup manually

If you want to be extra pedantic you might as well convert all the warnings into errors by adding the -pedantic-errors flag

For more details read the GCC Warnings Options documentation.



Related Topics



Leave a reply



Submit