How we made compiler warnings fatal in Firefox
blog.mozilla.org
blog.mozilla.org
"We're going to rush through implementation so that we have lots of time left at the end to fix all the bugs caused by rushing through implementation."
I think that last bit is the most important one. You need to be practical about stuff like this.
Putting a ban-hammer down like a CI-nazi is most likely not going to get you the results you were hoping for.
Edit: Looking at the linked issue [1], this has taken quite some time. First comment on that issue is 15 years old :D
All of the above will come back to bite you hard if you have to release again. Some will come back to bite you right away (the expense of manually testing everything), while others can lurk hidden for years (little vs big endian issues).
I mean, in one sense you're right, but UB can make even trivial maintenance absolute hell even if you stick to the exact same build toolchain.
Not surprisingly, 0 warnings on your dev machine doesn't mean you also have 0 warnings on a production build for another architecture..
There may be cases where the potential false negatives are unacceptable. Be judicious, but in my case the benefits far outweighed the risks.
Of course, it's a great idea to provide upstream patches and issues whenever possible.
CMake also supports this through include_directories(SYSTEM …).
With MSVC you will have to fall back on macros.
#include "warnpush.h"
#include <crappy.h>
#include "warnpop.h"
I always do this for third party headers in my projects. Except for std headers.Example:
https://github.com/ensisoft/newsflash-plus/blob/master/app/c...
This is some good practical advice (and a welcome improvement) from Mozilla.
I'm also reminded of an email I sent out to a technical mailing list some time back, in response to a user's question about suppressing warning messages when running his program (this for a runtime-based interpreter).
Various suggestions came for how to limit, suppress, or remove warnings from the log.
My response, which apparently earned some favourable consideration at the vendor:
"Fix the bugs in your code."
However, that’s not what the grandparent wrote: "For these I add warning specific suppressions”. I read that as disabling single warnings, not a compilation unit at a time.
For example, in Visual Studio, you can do (https://msdn.microsoft.com/en-us/library/2c8f766e.aspx):
#pragma warning(push)
#pragma warning(disable: 1234)
...
#pragma warning(pop)
Intel’s compiler is compatible with this (https://software.intel.com/en-us/cpp-compiler-18.0-developer...), but it would surprise me if it used the same warning numbers.GCC and clang, on the other hand, need (https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html#D..., https://clang.llvm.org/docs/UsersManual.html#controlling-dia...):
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored “-W...”
...
#pragma GCC diagnostic pop
Because you ideally only want to target the exact place that triggers a warning, either is fairly wordy, and it gets worse if you want to cater for all three compilers.It also wouldn’t surprise me if Mozilla supported multiple versions of gcc (for instance because they want to build for an OS with long-term support, or for an obscure OS for which the latest gcc is not (yet) available).
(_If_ you use C99, at least with gcc and clang, you can simplify that by using _Pragma, which has the big advantage that you can use it in preprocessor macros, so that you may be able to simplify what things look like in the source)
On the other hand, its typically some library that is messing up the static analysis tool.
While the #pragama options are good for disabling warnings in typical projects, from personal experience they are broken in NVCC.
Can't you just -isystem those includes?
Years ago when I still programmed in Go I made use of that all the time: if it didn't format on save I knew I had a compiler error somewhere.
And compiler errors are not worth linting given you can just do a syntax check, linting is useful to avoid or require disambiguation of error-prone constructs.
Examples in JS would be `with`, `==`, assignments inside a conditional check, parseInt without a base (mostly in older browser), parseFloat, non-prefixed octal literals (073 rather than the explicit hex-style 0o73), string expression statements (outside of the "use strict" pseudo-pragma), and several scoping errors (which strict mode will mostly only check at runtime). All of these are syntactically perfectly valid, but more often than not they're also mistakes or sources of errors.
I see this in C code often. Why do people seem to like doing this?
while ((c = getchar()) != EOF) {
//
}
Is considered more idiomatic and readable than any alternatives that's not assigning to c within the conditional.
You'll see similar idioms in many other languages, e.g. while((line = reader.readLine()) != null) {
}
But if you're talking about e.g. if (foo = (bar & 0xf)) {
}
It's just bad style ... char c = EOF;
while (c != EOF) {
c = getchar();
...
}
The idiomatic way reads better: "while next character is not EOF".Your (excellent) example of bad style is bad because it reads poorly: "if the result of the assignment of bar & 0xf to foo is true". Not only that, but it is not obvious what the result of an assignment is, if anything. (Yes, I do know it is the value, but personally, I think <nothing> is a more logical result).
char c = getchar();
while (c != EOF) {
c = getchar();
…
}
?Which not only reads badly but requires writing the "producer" twice. Though alternatively you could:
while (1) {
c = getchar();
if (c == EOF) { break; }
...
} char c;
do {
c = getchar();
} while (c != EOF); int c; //a char can't represent EOF
do {
c = getchar();
if (c != EOF) {
//do stuff
}
} while (c != EOF); while ((c = getchar()) != EOF) {
//
}
in a language with iterators would be for (c in chars()) {
//
} while(generator(&c)) {
//
}Two of the most hated thing of C but so convenient...
(firefox-esr:345): GLib-GObject-CRITICAL **: g_object_ref: assertion 'object->ref_count > 0' failedNever having programmed in Gtk, what is the cause of this? Is Gtk broken or are all the programmers using it incorrectly? I've programmed a great deal in WinAPI and stuff like that never happens.
There's a bunch of boilerplate code that just gets carried from project to project.
My favourite was when KDE decided to move to cmake. People realized that most m4/autoconf macros were there for no reason and no one actually knew what they checked for let alone how.
Don't even get me started on the circular dependencies that need to be broken by installing packages with some options disabled. Kind of an awkward bootstrap.
We do our own hardware and kernel development (upstream kernel + .ko's for our stuff) and that's good code. Then we have the daemons in userland providing interfaces to ioctl's and cdevs, with some house keeping and interfaces for DBus and HTTP APIs.
All the userland stuff just vomits GLib-CRITICAL messages. No one bothers to check for them because everything is running as daemons with stderr redirected to /dev/null...
And $WORK is one of the better places I've worked at. I am certain that if those assertions would have been fatal, they would have been fixed a long time ago.
So it's under control, but the Netscape-era assertions haven't all been converted to reliable assertions.
... the downside is that every time someone uses a different version of your normal compiler, or a different compiler, you have an unfortunately large chance of having the build fail because a new bit of code that previously was warning free is now determined to be warning-worthy.
Not a huge problem (and better than cultivating the habit of ignoring warnings), but it is a time cost that has to be paid now and again for committing to halting the build on a static analysis failure.
"Set things up so that fatal warnings are off by default, but enabled on continuous integration (CI). This means the primary coverage is via CI. You don’t want fatal warnings on by default because it causes problems for developers who use non-standard compilers (e.g. pre-release versions with new warning classes). Developers using the same compilers as CI can turn it on locally if they want without problem."
And if you're using a package manager like Cocoapods, it shouldn't be a problem.
Edit: Wait I just re-read your post again and you are using Cocoapods. Can you not just turn on "treat warnings as errors" to YES for your main project, but leave it off for your main project? Or just add `inhibit_all_warnings!` to the top of your Podfile, like on this example file: https://guides.cocoapods.org/syntax/podfile.html
1. Determine the set of warnings you're going to enable for you project, and apply them to your own code and all third-party libraries.
2. For each third party dependency, find the minimal set of warnings that, when suppressed, make them compile cleanly, and disable just those (only for that specific third party project).
3. If any of those suppressions makes you uncomfortable, submit a patch to the third party maintainer if possible :)
As for #3 with open source libraries, I've seen the spectrum of attitudes from "thank you very much for the patch!" to "we don't care about compiler warnings, just turn them off"
EDIT: One thing this doesn't address is third party dependencies where #including their headers from your project results in warnings. If you're a developer who writes header files that produce warnings, please take a moment to hang your head in shame.
A good reason to use a different lib, IMO.
Still, I wouldn't expect so many of these that updating a new compiler would be too onerous.
Conversely, here's one where a warning is produced only without -O2:
orth$ cat z.c
int foo (int a) {
int x;
if (a == 5) { x = 3; return 42; }
return x;
}
orth$ gcc -O2 -Wall -o z.o -c z.c
orth$ gcc -Wall -o z.o -c z.c
z.c: In function ‘foo’:
z.c:4:4: warning: ‘x’ may be used uninitialized in this function [-Wmaybe-uninitialized]
return x;
^
orth$ gcc --version
gcc (Debian 4.9.2-10) 4.9.2Today clang will typically build all of google's software with the new warning on and look at the results. Then they look at each one and decide if it is a real bug or false positive. For the real bugs they look at how serious each bug is (if it is a previously unknown security hole that worse than it could fail in the real world only in unusual situations). For false positives they consider silencing the error changes the code (in some cases the code is very ugly and cleaning the code up fixes the warning, while in others the code was good until the ugly stuff to silence the warning was added). Then the consider the rates of all of the above to decide if the false positives are worth the potential gains. I believe other compilers do similar things, though I don't have insight into their processes (Chandler Carruth gives great talks at C++ conferences on clang which is where I get my information).
Compiler writers are in competition in this area. As a result most things that should be warned about are already warned about, while things that are not worth warning about are not warned about. Thus updates to the compiler are quick because you keep having "how did we ever get by with this mistake" moments, which in turn keeps you motivated to fix the next warning.
It's, in fact, downright stupid and means you're writing in a different programming language with each compiler upgrade and at the mercy of some poorly-considered third-party opinions.
Many compiler warnings are just stylistic. They can even contradict each other. Compiler A says, "suggest parentheses here". Oops, compiler B doesn't like it: "superfluous parentheses in line 42".
As a rule of thumb, warnings that aren't finding bugs aren't worth a damn.
The proper engineering approach is to evaluate newly available warnings and try them. Keep any that are uncovering real problems, or could prevent future ones; enable those and not any others.
Warnings are just tools. You pick the tools and control them, not the other way around.
Every code change carries risk!
> Choose with some care which warnings end up fatal. Don’t be afraid to modify your choices as time goes on.
So, my understanding is that they didn't make all warnings fatal. Only the ones they believe are important.
There are no superfluous parenthesis warnings in GCC and MSVC.
$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:6:5: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
if (a = b) {
^
Parentheses here would be superfluous, but would perhaps confirm you really intended to assign (i.e. used = instead of ==).The warning here couldn't be more justified. In a decent language, this should be a hard error.
fn main() {
if (true) { }
}
results in warning: unnecessary parentheses around `if` condition
--> <anon>:2:8
|
2 | if (true) { }
| ^^^^^^
|
= note: #[warn(unused_parens)] on by default