Customized Warning Levels and Code Analysis for External Headers

Will

If you have tried to keep your code clean by selecting strict compiler warning levels or code analysis rulesets, you likely know how frustrating it can be to see these warnings for headers that are not part of your project. To alleviate this, we’ve made it easy to mark headers as external to your project in the latest preview Visual Studio 2019. This is something we have been working on for a while with help and feedback from the community. Some of you may remember our previous post, Broken Warnings Theory, when we added experimental external header support to the Microsoft C++ Compiler. I’m happy to announce that, thanks to this feedback, external headers are now officially supported by the compiler, code analysis, and integrated into the IDE.

These external headers can have their own compiler warning level, code analysis, and template diagnostics settings. This leaves you free to choose stricter settings for your project’s code to enforce code quality without getting bogged down with warnings from headers that are beyond your control.

Adding External Headers to Your Projects

You can find a new “External Include Directories” property for your projects under “VC++ Directories” which can be used to designate any include directory as containing external headers. This external include directory will be added to the include search path as normal, but every header that in that directory or a subdirectory of it will be treated as external. From a compilation standpoint, you won’t notice any differences, but you can choose a different set of warning levels and other analysis settings for headers in these directories.

Specify “External Include Directories” in the project properties “VC++ Directories” tab.

By default, all headers from the toolset and the Windows SDK are included as external headers. However, you can add any other include directories (such as 3rd party libraries) to this new property, separated by a semicolon as well.

Keep in mind that this new property will be ignored by earlier versions of the toolset. If you want to ensure that the project builds properly with earlier versions, you will need to make sure that any “External Include Directories” are also listed in the “Include Directories” property or they will not be found by the compiler. In Visual Studio 2019, we will continue to also include the toolset and Windows SDK headers in the existing “Include Directories” property for backwards compatibility, but in the next major release of Visual Studio this will be removed.

Customizing Warning Levels External Headers

You can customize the way external headers are treated in the project properties “C/C++ > External Includes” tab.

Set the warning level, code analysis, and other settings for external header in the project properties “C/C++ > External Includes” tab.

Customizing Code Analysis Settings for External Headers

Being able to mark certain headers as external to the project will make Code Analysis much easier to use. The examples below show some of the ways you can leverage this new feature to get the most out of Code Analysis.

Migration from undocumented CAExcludePath to /external:* and /analyze:external* options

To support one of the most common requirements of suppressing code analysis warnings for defects in library headers over which developers have no control, we created a temporary solution to use a special environment variable (“CAExcludePath”), that can be used to specify directories for which code analysis will not report any warnings.

We now have a better solution to control code analysis behavior for external files. While we decided to leave “CAExcludePath” option as is, we strongly recommend to switch to the /external:* and /analyze:external* options. With these options, code analysis of external headers can be turned off, or use a separate ruleset from the rest of the codebase. These come with even better usability and maintainability as they are all available through the VS IDE.

Using /analyze:external- with /external:*

Consider the following header and source files, with no meaningful functionality but intentionally injected bugs:

Header File (externallib.h)

#pragma once
 
#include <Windows.h>
 
#pragma warning (disable:26440 26497)
namespace ExternalLib
{
#pragma warning(disable:4700)
    int GetValue()
    {
        int arr[2];
        return arr[2];
    }
 
    template <typename T>
    T GetValue(T, _In_range_(0, sizeof(T) - 1) int)
    {
        T arr[4];
        return arr[sizeof(T)];
    };
}

Source File (MyApp1.cpp)

#include <externallib.h>
 
using namespace ExternalLib;
 
void foo()
{
    auto sum = GetValue(3ll, 4);
    sum += GetValue(3, 3);
    sum += GetValue(L'a', 2);
    sum += GetValue('a', 1);
}

When analyzed with default options we get code analysis warnings for functions from both the header file and source file:

Errors without /External

Now, if we add the directory for the externallib.h to the “External Include Directories” as follows:

Add Include to External

And then set the “Disable Code Analysis for External Headers” to “Yes (/analyze:external-)” as follows:

Disable Code Analysis for External

Executing “Run Code Analysis” will no longer report any Code Analysis warnings from the external header file:

Errors from Source File Only

Some templates may have bugs depending on the template arguments. If you want to analyze templates even if they are in the external files, you can set the “Template Diagnostics in External Headers” option to “Yes (/external:templates-)”. Now, executing “Run Code Analysis” will report Code Analysis warnings for the template functions even if they are in the external header file:

Errors from External Templates

Using /analyze:external:ruleset with /external:*

Instead of turning off code analysis for external files, it is now possible to analyze external files with a different ruleset from the rest of the codebase by specifying a different ruleset file with the “Analysis Ruleset for External Headers” option.

For this example, I have created a custom ruleset “ExternalHeaderRules” that enables only two rules, C6021 and C6385, as warnings. Then I selected it for the “Analysis Ruleset for External Headers” option:

Custom Ruleset for External Headers

Please note that the “Disable Code Analysis for External Headers” option is set to “No”, enabling code analysis on the external headers. With this change, code analysis will now analyze functions from external files using the custom “ExternalHeaderRules” ruleset, and report following warnings:

Errors from Custom Ruleset

Currently, the “Template Diagnostics in External Headers” option is ignored if “Analysis Ruleset for External Headers” is used. We plan to change the behavior to honor the option and treat templates as non-external and apply the general ruleset instead of the ruleset for external headers.

Bonus: Even Better Code Analysis Performance

While we were working on this feature, we realized some of the built-in checkers were not really skipping functions from the files that are excluded through “CAExcludePath” environment variable. Instead, they were analyzed just like others and the resultant warnings from those functions were simply filtered out. As we add support for the /external:* option, we updated them to honor “CAExcludePath” as well as /external:* and /analyze:external- options, and to skip analysis of functions from excluded or external files. This resulted in observed performance improvements in our production codebase, ranging from 25% to 30%. Actual performance improvements will vary depending on how much of the codebase is from excluded or external header files, and how much of the project uses PCH, etc.

External Headers and the Microsoft C++ Compiler

Several new flags have been added to the Microsoft C++ Compiler to specify external include directories and their warning and code analysis settings. You can learn more about the behavior of these flags on our compiler documentation pages.

Send Us Feedback

Please grab a copy of the latest Visual Studio 2019 preview and try it out. We would love to hear your feedback about Visual Studio. If you have feedback, suggestions, or any other comments please submit them to Developer Community.

Posted in C++

7 comments

Comments are closed. Login to edit/delete your existing comments

    • Jonathan EmmettMicrosoft employee

      Hi Martin,

      Yes this is a bug that is on its way to a 16.10 servicing release and should be available soon. This bug only impacts projects where the “Include Directories” property is modified from the default, the “External Include Directories” does not include these modified paths, and there are headers that share the same name that can be found from both sets of paths. The header search order will not be altered if the same custom paths are placed in both the include and external include properties, as suggested in the post for compatibility with older toolsets.

  • Hristo Hristov

    How is that going to work with CMake? What about creating some sort of config file that can be placed in the root of the project and maybe add this format to other compilers like clang?

    What about the opposite option /internal or /root and treat anything else which is not inside of that root directory as external and ignore it?

    Is this an issue with modules and JMC?

    • Arnav Yash Chandra

      It works with Clang using SYSTEM keyword in target_include_libraries. I guess CMake would need to add the mentioned flags when specifying SYSTEM keyword in target_include_libraries, just like it includes -isystem flag for the same in GCC and Clang.

  • Phil Deets

    Are headers from vcpkg dependencies automatically considered to be external? If not, it would be great if you would make it so they are.

  • David Lowndes

    Setting the /analyze:external- option (Disable Code Analysis for External Headers) doesn’t appear to have any effect on headers from vcpkg usage. It’d be nice if it did.
    Do you want a bug report on this, or is it work in progress already?

    • Hwi-sung ImMicrosoft employee

      Thanks a lot for sharing your experience and suggestion. I recommend opening a feedback on this for the vcpkg so that vcpkg and add support for this. I guess /external:* in general does not work for vcpkg out of the box – i.e., compiler warnings also get generated for headers from vcpkg. So, please note all the related issues in the feedback. That way, we will have a tracking feedback for this regardless whether it is already in progress (or in plan) or not.