Static Analysis Fixes, Improvements, and Updates in Visual Studio 2019 16.9

Jordan Maples

The C++ static analysis team’s goal is to make your C++ coding experience as safe as possible. We are adding richer code safety checks and addressing high impact customer feedback bugs posted on the C++ Developer Community page. Thank you for engaging with us and giving us great feedback on the past releases and early previews leading to this point. Going forward, the C++ team will provide a list of code analysis bug and crash fixes with every GA release of Visual Studio. Below is the compilation of improvements and bug fixes that were made from VS 2019 16.8 to 16.9 for code analysis and Cpp Core Check.

Analysis crash fixes:

  • Using an index operator on the address of a non-address and non-array object.
void function() {
    int buf{};
    ((unsigned char*)&buf)[3] = 1;
  • Functions with more than 255 arguments would cause a crash during analysis.
  • Array member field addresses were incorrectly converted in dynamic initializer function.
  • Fixed internal compiler error for aggregate initialization in /analyze.
char c[]{'1', {}};
  • Fixed a crash caused during analysis of bitfields and enums.
struct TestStruct {
    enum TestEnum : char { Dummy };
    TestEnum    m1 : 1;
    TestEnum    m2 : 1;
    short       m3;

TestStruct Test() {
    return{ TestStruct::Dummy, TestStruct::Dummy, {} };
  • Specifying an array of three elements but only providing two elements in the initializer list.
#include <array>
#include <string>
using namespace std;
void function() {
    array<string, 3> arr {"one", "two"};
  • Fixed crash on empty KMDF projects.

Bug fixes:

  • Addressed noisy warnings in an object’s destructor when a function that would have initialized or updated the object fails.
  • Support for the GSL functions gsl::as_bytes and gsl::as_writable_bytes was added to prevent C26710 warnings from being issued against otherwise valid buffer accesses.
#include <gsl/span>
void fn1(gsl::span<int, 5> view, byte val) {
    auto bview = as_writable_bytes(view);
    bview[19] = val;  // OK
    bview[20] = val;  // C26710 & C26000
  • Fixed ruleset loading failures that occurred when a relative path of a ruleset was used in combination with the exact path of a ruleset directory. E.g: /analyze:rulesetdirectory f:\customRuleset /analyze:ruleset myrules.ruleset
  • Fixed false positives of C6237 and C6285 on if constexpr expressions.
constexpr bool get_condition_a() { return false; }
constexpr bool some_other_check() { return true; }
constexpr void f1() {
    constexpr bool some_condition = get_condition_a();
    if constexpr (some_condition && some_other_check()) {  //Previously issued C6237

constexpr void f2() {
    constexpr int print_debug = false;
    constexpr int headers_debug = false;
    if constexpr (print_debug == true || headers_debug == true) { //Previously issued C6285
  • Fixed false positive of C26444 when returning upon construction.
struct Test {
    int i{};

Test foo() {
    return Test(); //Previously issued C26444
  • Fixed issue where casts with the same source and destination types were being misidentified as reinterpret cast, which would produce C26490 instead of C26473.
struct S{};
void foo(S* s) {
    S* s2 = static_cast<S*>(s); //Previously C26490, now C26473
  • Fixed an incorrect C26465 warning when attempting to cast away const. C26492 will now be issued instead.
struct S{};
void foo(const S& s) {
    const S* pS = &s;
    S* s2 = const_cast<S*>(pS); //Previously C26465, now C26492
  • Fixed false positive for C26814 that would be issued on const member variables.
  • Fixed corner case where PREFast entered an infinite loop while examining buffer extents.
  • Fixed false positive of C26815 that fired when assigning a value to a std::optional that is passed by reference into a function.
  • Fixed false positive C26816 when returning a pointer from a vector of pointers.
  • Fixed false positive of C26485 which appeared when calls to printf used string literals chosen by a ternary operator.

Additional changes:

  • Updated support for SARIF format to conform to the version 2.1 specification.
  • Added SARIF support for additional rule action levels for ruleset files. The rule actions can now be specified as “None”, “Default”, “Info”, “Warning”, and “Error”.
  • Removed C26443 – The enforcement for C.128 has changed making C26443 obsolete.
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Example For Warning Levels" Description="" ToolsVersion="16.0">
  <IncludeAll Action="Info" />
  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis"
    <Rule Id="C6001" Action="Error" />
    <Rule Id="C6011" Action="Warning" />
    <Rule Id="C6101" Action="Info" />
    <Rule Id="C6201" Action="Default" />
    <Rule Id="C6386" Action="None" />
  • Using a C-style void cast to ignore return values decorated with [[nodiscard]] previously issued C26493 urging developers to not use C-style casts. The new rule C26457 will be issued in its place, guiding the developer to assign the return value to std::ignore if they intend to discard the return value.
#include <tuple>

struct S{};
[[nodiscard]] S fn1();

void function() {
    (void)fn1(); //Previously C26493, now C26457
    std::ignore = fn1();
  • The text for C26496 was updated from “The variable '%variable%' is assigned only once, mark it as const (con.4)” to “The variable '%variable%' does not change after construction, mark it as const (con.4)”.

As mentioned earlier, the work that we do is heavily influenced by feedback we receive on the Developer Community so thank you again for your participation. Please continue to file feedback and let us know if there is a checker or rule that you would like to see added to C++ Core Check.

Stay tuned for more C++ static analysis blogs as we work towards 16.10. Coming soon are posts on improvements to C++ Core Check rules, improved diagnostics, and an update on the Microsoft/GSL GitHub project. In the meanwhile, do not hesitate to reach out to us. We can be reached via the comments below or @VisualC on Twitter.