Two musings on the design of compiler warnings (2020)
An essay on two aspects of compiler warning design: machine-applicable fixits and how code can suppress a warning, using GCC and Clang -Wparentheses as the example.
Extract
Today I finally write down some musings on two peripheral aspects of compiler diagnostic design that I think are very important for certain kinds of warnings. My first musing relates to fixits, and my second to suppression mechanisms. What is a fixit? Most C++ compilers these days don’t just tell you that a problem occurred (like ed’s famous ? prompt), and what the problem is (“expected identifier,” for example). They’ll try to tell you how to fix the problem. Compare GCC 4.1’s error messages with GCC 4.6’s. (Godbolt.) For this input: struct S {} struct T {}; GCC 4.1 complains merely that you have put multiple types in one declaration. GCC 4.6 more helpfully complains that it expected ';' after struct definition, and points to the exact position where it believes a semicolon should be inserted. GCC 7 goes even further, and suggests a fixit — a machine-readable “spellcheck suggestion” that some IDEs are able to apply with a single click. test.cpp:1:12: error: expected ';' after struct definition struct S {} ^ ; In medical terms, the compiler has gone from simply detecting the symptom (“this declaration seems to contain two types, which isn’t valid C++”) to producing a diagnosis of the disease (“I bet you forgot a semicolon somewhere”); and a compiler that supports fixits is actually prescribing a treatment plan. Each of these steps is a quantum leap beyond the previous one in terms of both user-friendliness and implementation complexity. What is a suppression mechanism? Suppression mechanisms relate to compiler warnings, not compiler errors. Consider the following snippet (Godbolt): if (argc = 1) puts("hello"); Every mainstream compiler warns on this suspicious-looking code (GCC and Clang with -Wall; MSVC with -W4). Here’s GCC’s diagnostic: warning: suggest parentheses around assignment used as truth value [-Wparentheses] 4 | if (argc = 1) | ~~~~~^~~ Notice that GCC doesn’t even bother to explain really what its diagnosis is; it emits a passive-aggressive warning message that does nothing more than suggest how to shut itself up! The diagnosis, of course, is that frequently people mean to write if (argc == 1) but their finger slips and they write if (argc = 1) instead, which unfortunately is also valid C++ code. Every mainstream compiler has evolved the same way to deal with this: if you really mean =, then you write an extra pair of parentheses around the expression, like so: if ((argc = 1)) The extra pair of parentheses is what I’m calling a …