Both EOF and the max value of unsigned char is platform dependent. In this
patch we try our best to deduce the value of EOF from the Preprocessor,
if we can't we fall back to -1.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | ||
---|---|---|
540 | Debug message (to be removed)? | |
542 | It would be good to have this function available generally to other checkers, the same functionality is needed in https://reviews.llvm.org/D72705 too. |
Harbormaster failed remote builds in B46321: Diff 244141!
This is actually true, I have a test that crashes! Finally, a case where remote builds are proved to be useful!
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h | ||
---|---|---|
67–71 | While I agree that we shouldn't have to reinvent the Preprocessor every time we need something macro related, I doubt that this will catch anything. I checker my system's standard library, and this is what I found: #ifndef EOF # define EOF (-1) #endif Lets go just one step further and cover the probably majority of the cases where the definition is in parentheses. | |
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | ||
518–523 | Hah, would've never thought of doing this with a lambda. Me likey. | |
577 | Huh, what's happening here exactly? Doesn't Range take 2 RangeInts as ctor arguments? What does {EOFv, EOFv}, {0, UCharMax} mean in this context? | |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
115–118 | This seems a bit clunky even for the Preprocessor -- how about const auto *MacroII = PP.getIdentifierInfo(Macro); if (!MacroII) return; const MacroInfo *MI = PP.getMacroInfo(MacroII); assert(MI); | |
clang/test/Analysis/std-c-library-functions-eof.c | ||
25 | So the test is about checking whether the analyzer correctly assigned -2 to y, right? Then let's check that too. clang_analyzer_eval(y == 2); |
- Move include of Preprocessor.h to CheckerHelpers.cpp
- Try -> try
- Use PP.getIdentifierInfo
- Handle parens in tryExpandAsInteger
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | ||
---|---|---|
577 | Range is a convenience function that creates a IntRangeVector from the two params. This way we can easily create a list of ranges from two ints. Range(-1, 255) results a list of ranges with one list: {{-1, 255}}. | |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
115–118 | Ok, but we cannot assert on MI, because there may be cases when the macro is not defined in a TU. In that case we should just return with None. |
LGTM! Very nice! I think you can commit as you please, granted you add core checkers to the test RUN: lines.
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | ||
---|---|---|
319–327 | I appreciated you sitting down with me with a piece of paper and a pen to explain what happens here, but for the less fortunate this code snippet should be decorated with some ASCII art. I don't insist on you doing that within the scope of this patch, but if you did anyways, that would be great :) | |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
138 | TIL Despite the analyzer having a half-a-decade old checker option that was defaulted to a hexadecimal value, nobody wrote tests for it, and it took 4-5 years to unearth that due to the incorrect parsing of integers (it was set to decimal instead of auto detect) it never ever worked. | |
clang/test/Analysis/std-c-library-functions-eof.c | ||
2–6 | Always enable core checkers for pathsensitive analyses. |
clang/test/Analysis/std-c-library-functions-eof.c | ||
---|---|---|
12 | Maybe you could throw in a test where the definition isn't surrounded by parentheses? |
- Enable core checkers explicitly in test
- Add ASCII art to depict a range list example
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | ||
---|---|---|
319–327 | Ok, I have added that below the existing comment of the enclosing if block. | |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
138 | Well, I hope AutoSenseRadix will just detect the correct base. |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | What exactly happens when the macro is #undef-ined and redefined? We get the last redefinition that's valid at the end of the translation unit, right? Can we check whether there are multiple definitions and guard against that? |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | Ugh, now that you say it that is a valid concern. I had to deal with that back in the day: https://reviews.llvm.org/D52794?id=171962#inline-476352 |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | Solving this does not seem easy in my opinion. To handle #undefs we should build an infrastructure where summaries can reference callable objects. This is necessary, because in evalCall the value of EOF would depend on the souce location of the call expression of the function with the summary. Not impossible to solve, but certainly introduces complexity. Do you think that the redefinition of EOF is so common? I mean maybe it is too much hassle for a very rare edge case (?). |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | The standard library (libc or libc++) should define EOF consitently in stdio.h. #include <stdio.h> #include <stdlib.h> #define EOF -2 // Here be dragons !!! int main(void) { FILE* fp = fopen("test.txt", "r"); int c; while ((c = fgetc(fp)) != EOF) { // BOOM: Infinite loop !!! putchar(c); } fclose(fp); } So, redefinition of EOF (or any standard macro) results in broken code. ) clang eof-inf.c eof-inf.c:3:9: warning: 'EOF' macro redefined [-Wmacro-redefined] #define EOF -2 // Here be dragons !!! ^ /usr/include/x86_64-linux-gnu/bits/libio.h:66:10: note: previous definition is here # define EOF (-1) ^ 1 warning generated. |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | It should be possible to get the list of all macro definitions (of a macro). We can select the first item, or one that is inside a system header (by the SourceLocation). |
clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp | ||
---|---|---|
115–118 | Actually, redefinition of a macro in a standard library is undefined behavior: C++11: 17.6.4.3.1 [macro.names] 1) A translation unit that includes a standard library header shall not #define or #undef names declared in any standard library header. and [reserved.names] 1) The C ++ standard library reserves the following kinds of names: — macros — global names — names with external linkage 2) If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined. Also, this is stated in C99 (7.1). The names of all library types, macros, variables and functions that come from the ISO C standard are reserved unconditionally; your program may not redefine these names. All other library names are reserved if your program explicitly includes the header file that defines or declares them. Thus, I don't think we should come up with solutions to handle already broken code (i.e. programs with undefined behavior). |
We do not need to include Preprocessor.h here (fwd declaration is enough).