Modern compilers do have insight into the source before it was preprocessed. You'll notice that clang and (modern) gcc will show you where the error is in the unexpanded source line, and then show the expansion of the macros. So when it detects "2^32", it can look back to see if it was a product of macro expansion or if the literal was directly written, and warn accordingly.
Interestingly, msvc can also do this, by virtue of not even having a distinct preprocessor phase at all.
The preprocessor does macro expansion, the compiler compiles the result.
The compiler does not see FLAG_BIT1 ^ FLAG_BIT3, it only sees the result, e.g. 2^32.
Therefore to catch only explicit 2^32 the warning should come from the preprocessor... Nice mess created right there.