C++ compiler diagnostics improvements in VS “15” Preview 5

This post written by Andrew Marino and Andrew Pardoe

Visual C++ in VS “15” Preview 5 now allows you to see where errors are in a line of code—the column number—as opposed to just showing the line number. As C++ has grown it’s increasingly useful to have a little more help finding your errors.

Consider this somewhat contrived example:

int f(int, int);
 
int main() {
    return f(f(f(1, 2), f(3, 4), 5), f(6, 7));
}

Traditionally Visual C++ would just tell you t.cpp(6): 'f': function does not take 3 arguments. With five different calls to f on one line, it’s hard to figure out where your error is.

Providing column numbers helps greatly: t.cpp(5,32): error C2660: 'f': function does not take 3 arguments. Now at least the compiler has identified which call to f is incorrect.

But you can do more! You can now show the source context with the column information:

t.cpp(4,32): error C2660: 'f': function does not take 3 arguments
     return f(f(f(1, 2), f(3, 4), 5), f(6, 7));
                                   ^

In this mode, the compiler doesn’t just tell you the column, it also points out the error with a caret.

Controlling diagnostics output

You can control the diagnostics format with a new compiler flag, /diagnostics:. There are currently three arguments accepted: classic, column, and caret.

  • classic is the default, showing only the line number. We didn’t change the default because it’s often not a human reading the compiler’s output. Frequently a build system or some other program will be reading the output. In this scenario you want the output to be simple and easily parsed.
  • column provides the next level of information: the column where the error was encountered.
  • caret provides the richest level of information: the context of where the parser found the error and a caret (^) indicating where in the code the error was found.

Customizing output in Visual Studio

Controlling the diagnostic output in Visual Studio is as easy as selecting the level under C/C++ -> General options:

Project properties page

If you select either column or caret, clicking on the error in the error windows will bring the cursor to the column where the error was encountered. For example, the cursor is on the closing parentheses of the call to f that erroneously includes three arguments. Parentheses highlighting (in light grey) shows the scope of the function arguments.

Diagnostics message in VS

At the bottom of this screen you can see the “Col” column. This is off by default in the errors window. Right-clicking on the column headers brings up a dialog where you can select what columns are shown. Selecting the “Col” column allows you to see the column number.

Deleted special member function errors

We haven’t just improved diagnostics formatting—we’re making our diagnostics clearer and more helpful across the board. We’ve made improvements in areas such as static asserts, constexpr, and special member functions.

Here’s an example where code references a deleted special member function. The compiler has generated a default constructor as a special member function. But because the developer supplied an explicit constructor, A(int), the compiler deleted the default constructor. Instantiating a variable with the type of the union that contains A will result in an error to find the default constructor.

struct A
{
    A(int); // non-trivial constructor
};

struct B {}; // trivial constructor

union variant
{
    A a;
    B b;
};

int main()
{
    variant var;
}

Previously the compiler would tell you the nature of the error:

source.cpp(16): error C2280: 'variant::variant(void)': attempting to reference a deleted function
source.cpp(12): note: compiler has generated 'variant::variant' here

Now, it additionally tells you why the function was deleted and points you back at the source of the error:

source.cpp(16,1): error C2280: 'variant::variant(void)': attempting to reference a deleted function
source.cpp(12): note: compiler has generated 'variant::variant' here
source.cpp(12,1): note: 'variant::variant(void)': function was implicitly deleted because a data member 'variant::a' has either no appropriate default constructor or overload resolution was ambiguous
source.cpp(10): note: see declaration of 'variant::a'

Looking ahead

There are many improvements we can make to our compiler diagnostics going forward. The perfect diagnostic would tell you exactly where your error is and how to fix it: we have a long way to go before we get to that point! But we’re committed to making improvements in the accuracy and completeness of our compiler diagnostics.

You may occasionally notice that the column information in a diagnostic is incorrect. This can happen because the code contains multiple-byte characters or because of a bug in the compiler’s parser. Because we’ve never displayed the column number there are a few places where we’ve recorded it incorrectly and haven’t yet found the error. Please let us know if you find a code sample where the diagnostic column information is incorrect!

There area also places where the compiler just won’t emit a column number, even though you’ve asked for columns. There are a couple of examples in the diagnostics above:

source.cpp(12): note: compiler has generated 'variant::variant' here
...
source.cpp(10): note: see declaration of 'variant::a'

This happens because we don’t have column number information in all of our diagnostics. We’re working on expanding the coverage.

We know that we’ve got a long way to go before our diagnostics rival those of other popular C++ compilers. A lot of the 30+ year legacy of the Visual C++ compiler shows in our diagnostics quality. But this is a first step, and we’re committed to continuing the improvements.

Send us your feedback!

As always, we welcome your feedback. For problems, let us know via the Report a Problem option, either from the installer or the Visual Studio IDE itself. For suggestions, let us know through UserVoice. And you can always reach us through e-mail at visualcpp@microsoft.com.