I'd prefer not to go too much in specifics due to NDA's and all that; but one simple example that I've encountered:
I've seen simple cases where a function returned a signed integer value modulo x. Which of course means that this function can only return a value within 0..x-1.
This function was called in another function which then did some allocations based on it and the static analyzer complained about integer overflows which were obviously not possible in this case.
Dependency chains in preprocessors is a big pain in general too.
#ifdef A
#define B
#endif
#ifdef C
#ifdef B
blabla
#endif
#endif
This will yield a dependency chain for B of (A,C) -> B resulting in blabla being compiled. With complex code bases this already becomes quickly unmanageable. As far as I know no code analyzer is able to handle this.
And I haven't even started talking about supporting #if constructs (ex: #if X > 2). You would need to implement SAT solvers for that too.
These are just other reasons why seperating the preprocessor from the rest of the language was such a bad idea; you lose all the static code analysis stuff you can do on C/C++ code because of it and preprocessor directives control a lot of things in modern and big code bases.
Yes, the preprocessor is a disaster for static analysis. I don't know anyone who attempts to analyze un-preprocessed code (we certainly don't).
Along with the problem you describe, one occasionally sees stuff like this:
#if A
void foo() {
#else
void foo(int x) {
#endif
Here there are two versions of the beginning of a function definition. There's not even any way to represent that in an ordinary AST, even if you could parse it, which you can't with any ordinary kind of parser. And it's not hard to come up with even nastier examples.
Yeah, even something standalone which attempts to implement a C-preprocessor such that my editor can hook into it and properly resolve the used code-blocks might be already useful. But it's going to be a massive pain, if not impossible, to actually implement it.
I remember the day I actually realized what a clusterfuck the whole preprocessor was when trying to figure out all this precompiled header craziness; it's only there to solve all the horrible shortcomings of the POSIX/C/C++ eco-system as designed and imposed on it by how the preprocessor works. Even Strousup at several occasions admitted that he would like to have the preprocessor removed from C++. That's not going to happen anymore and it's obviously Captain Hindsight speaking here.
> I've seen simple cases where a function returned a signed integer value modulo x. Which of course means that this function can only return a value within 0..x-1.
Actually, signed modulo can return negative numbers... See, for example, this StackOverflow question:
That may be, but I want to have all this information in my editor too so my editor can warn me about it. Take something like a developer just defining a macro like __USE_GNU to get rid of a warning about an undefined function; you just have no idea what box of Pandora you just opened up with all this preprocessor magic going on behind the scenes. Who knows? They might even be re-defining MAX_PATH or something. With code making assumptions on the value of this macro you could open up new bugs. I want all this information right there for the developer in his editor.
Also there's just no guarantees on how the software is built; in a lot of cases that's even outside a developer's reach.
But, yeah, since it's next to impossible to actually get to that state then, yes, sadly the only real option left is to analyze your finite set of builds.
(Full disclosure: I work for HP Fortify.)