Why should I always enable compiler warnings?
stackoverflow.com
stackoverflow.com
Warnings arise from being unable to modify the language semantics. But even if you can modify the semantics, warnings are spawned as a compromise when people cannot agree on what the language semantics should be.
I've tried pretty hard with the D programming language to not have any warnings - that the semantics should be firmly decided upon. Some warnings still have crept into the compiler, but fortunately just a handful.
Not always. For instance, C has a lot of stuff around casting, memory management, etc. that is semantically legal but often good to warn about. Other times, it can warn about a possible stack overflow. C has tons of behavior which is not technically defined but has been agreed upon by most compiler implementers (and doesn't warn), though admittedly, there are also cases where you'll get different results.
That's just what I meant. Can't change C, so issue a warning. For example,
for (i = 0; i < 10; ++i);
sum += i;
My C compiler warns about the ; and the D compiler gives an error for it. A number of common C warnings are just flat out errors in D. For another example, if (a < b < c)
likely will produce a warning in C, but it won't even parse in D (I fixed the grammar so it is not a valid construction).The fact that these pointless and buggy constructs persist in C is evidence that the language cannot be changed, hence a warning is used.
if (x) foo(); // accepted
if (x) ; // rejected
if (x) { } // accepted if (x);
etc. are also rejected. If you actually want an empty statement, { } does the job. Doing it with the grammar is not hackish and it works out quite nicely.In the olden days (1989) people were more willing to accept change in C, such as value-preserving rather than sign-preserving integer promotion rules, and some improvements to the C preprocessor.
That said, style warnings are incredibly hard to detect and enforce (e.g., -Weffc++ is so noisy I've never been in any codebase where I could leave it on)
Today a compiler is not the only tool to parse a language anymore. It sure is convenient to add some print statements where the compilers sees the warnings anyways. A better approach is to provide the frontend as a library to allow plenty of other tools, e.g. a formatter.
[X] for a reference backing this up see the part of Facebook's ACM article on static analysis https://cacm.acm.org/magazines/2019/8/238344-scaling-static-... in the section titled "Human factors"; there they compare "after-the-fact offline analysis" with "in the code-review system", and I think the same reasoning applies to comparing "in the compiler immediately" against "later in the code-review system".
Actually, most people have a (very weak) form a static analysis which happens before the compiler: syntax highlighting. This can be expanded such that type checking and similar fast checks are integrated into the typing feedback loop.
In my ideal world, the next (slightly slower) feedback loop should be the unit test which the developer is currently working on/against.
Only the third-fastest feedback loop is a full compilation where compiler warnings would matter.
A colleague, who was new to C back then, asked about it, so I shared some of my masochistic practices, including the `lint`-necessitated mantra, "Always cast the return value of printf to void."
Years later, after moving to another US state, I'm cutting through the courtyard of a large block, where people are eating lunch outside, and someone shouts, "Oh my god, it's ____! Always cast the return value of printf to void!" She then introduced me to her coworkers at the table, "This is ___. ... He's the reason I code C like an [bad word]." Which I took as a compliment.
Programming in C reminds me of advice I've heard about helicopter piloting: from the moment you start the helicopter, that thing is trying to kill you.
Perhaps it's helpful to think about coding C like a cool-headed, meticulous pilot, who's constantly aware of the danger.
Why?
So the idea was to pass every compiler and linter. Whenever a warning appeared -- such as due to code changes, moving to a new platform, updating some library, etc., -- that was something to look at, not to assume you knew the warning was innocuous and get in the habit of disabling warnings.
The environment is a bit different now, with everyone using a small number of standards-compliant and featureful compilers. When you had situations like, e.g., your cpp on one new platform not even being quite K&R compliant, and potentially mangling your layers of macro expansion subtly, you really needed every hint you could get that something wasn't parsing or behaving like it looked like it would.
It was also perhaps faster just to do all your pedantic practices, than to try and reason about when you needed to do them.
Today, of course, especially if I'm using only one compiler, I might not bother with void on printf. That's where I might redraw the line, like you suggested. (Speaking of personal code for which I have stylistic discretion; for other code, I'd defer to the current conventions of the project and/or discuss with team.)
Though, a side benefit to being conspicuously pedantic is that, when you see a chunk of code that is less-pedantic in some way, it stands out. When we have so many critical defects due to insufficiently perfect C coding, spotting a chunk of code (especially one's own code) that's a little less-pedantic might mean that someone who worked on that was maybe being a little cavalier at the time, and maybe that chunk is a place to focus some attention.
Being warnings&lint-free isn't the most important thing in C, and my bigger concern is that, as a practice, overall, we don't agonize enough over C code correctness and manageability.
Even if you have some mild disagreement with the warning or some critical context that indicates "that's not a problem for us" -- fix it anyways. This goes for both static analysis and compiler warnings (ok, yes -- they're both static analysis). Keeping the codebase green means that you can build with `-Wall -Werror` and prevent new bugs from creeping in. If there's code that absolutely can't be rewritten to avoid the warning, you can mask the warning locally with things like pragmas.
All major compilers come with a way to suppress the particular warning in a particular file if all else fails.
Rust has a ton of lints/warnings in the style of "this looks fishy, did you really mean that?", and users can answer "yes" by adding `#[allow(fishy_situation)]` to a particular scope.
Users also have an option of setting `#[deny(fishy_situation)]` on entire modules or programs to ensure the particular thing never happens.
Because it's per scope per warning, it doesn't have the dilemmas of all-or-nothing `-Werror` and other global warning flags.
https://kristerw.blogspot.com/2017/09/useful-gcc-warning-opt...
-Weverything should be thought of as a tool for discovering new warning flags relevant to your project. Add those flags, but don't enable -Weverything for routine builds, unless you're a masochist.
https://clang.llvm.org/docs/UsersManual.html#diagnostics-ena...
The problem with -Wall is that people use it with -Werror, thus compilers would have to be very conservative about adding new warnings to it. Hence the situation we are in with a few flags "above and beyond". Most of -Wpedantic for example is not something I'd want to break my build on.
The following stack overflow answer has some good example of other warnings you probably don't want, but would be included in -Weverything: https://stackoverflow.com/questions/11714827/how-to-turn-on-...
"People who want to get shit done", sure. Pragmatists, even.
The problem with ignoring warnings is that they tend to accumulate and before long you can't see the ones you really do need to pay attention to. And yes that means you have to take the time to cleanup on new compilers with more warnings.
Like enabling assertions or not, this is (a matter of opinion, and) a difference between "development" and "release" compilations. Ideally we would consistently use something like ./configure --devel for one and ./configure --release for the other. Other languages' build systems have this built in, and building a tarball with autotools used to do something similar, but if you just pull something off GitHub it's not clear what mode you'll end up with.
Makefile has this built in too; you'd just be saying "make development" instead of "make --development".
https://docs.microsoft.com/en-us/windows-hardware/drivers/de...
In case you're not aware, the issue with this is upgrading your compiler almost certainly causes your project to stop compiling altogether, requiring immediate fixes to stuff that otherwise could have waited. What's worse, it also means you can't go back and compile older versions of your code with the newer compiler.
Either you use docker to have a stable environment, or you disable the Werror warning for this specific case.
use foo;
fn my_function() {
use foo;
foo::other_function()
}
This used to not trigger a warning. In rustc 1.35, the existing warning unused_imports became smarter and could now detect the redundant import of foo. This resulted in several crates that had made unused item warnings into errors having the exact problem you said.I think -Werror is a bad idea in general.
Such as giving "warnings" and happily compiling a program that is practically guaranteed to start doing random things at some point.
It's true that there is a great deal of hand-wringing over backwards compatibility when new C++ features are discussed, though.
That way, they act as a kind of "checklist" for potential trouble that, once investigated, are removed from the checklist.
This practice has, on a number of occasions, allowed me to avoid some problems that would have been very hard, if not impossible, to nail down during testing or after release.
"Why should I eat my vegables? Innernet, tell me why!"
> I have posted this Q&A because I'm sick and tired of telling people to enable warnings. Now I can just point them here (or, if I'm in a particularly evil mood, close their question as a dupe). You are welcome to improve this answer or add your own.
What is your real world experience in this regard?
Logging, in a nutshell, is a computer program seeking an attention of a human being. When everything goes smoothly and as expected, no log output needed.
We usually have, though, 4 most often used levels of logging:
- [DEBUG] – human explicitly asking program to tell a lot, for debugging
- [INFO] - program tells "hey, I'm fine here", mostly for assuring worrying human if program even works. As confidence grows, need in this log level falls. (Historically, this log level is often abused for metrics/tracing purposes, but that's another story)
- [WARNING] - program says "hey, human I don't know if this is good or bad, up to you to decide"
- [ERROR] - human attention required, program don't know how to handle situation automatically
Now, from this perspective, [WARNING] level of the compiler is basically compiler/spec giving up on telling human what's right and what's wrong. Given the compilers are written by people who know language spec better than anyone, it would safe to assume that compiler should know better than average developer what's good and what's not.
So when compiler tells develeoper "warning: I don't know if it's ok, deal it yourself", majority of the developers can only say "meh, I don't know either" and "doesn't crash, so we're good, ignore it".
That's why I strongly believe there should be no warnings in compilers at all. INFO and ERROR levels are sufficient for this kind of programs.
This approach (no warning from compiler) is successfully used in Go, for example, and, I believe, the reasoning is similar.