Fix a case when the extent can not be retrieved correctly from incomplete array declaration. Use redeclaration to get the array extent.
Details
Diff Detail
Event Timeline
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1663 | I think you supposed to use the getCanonicalDecl() instead. | |
1672 | What does the function return if this branch is not taken? | |
1718 | I would favor not adding another level of indentation xD | |
clang/test/Analysis/initialization.c | ||
101 | Why is this a VLA? I thought it's just an IncompleteArrayType. |
@steakhal
Thank you for your comments.
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1663 | Using getCanonicalDecl does not fix the issue. I've checked. | |
1672 |
In case of a false branch it goes down the code to getBindingForFieldOrElementCommon.
Only ConstantArrayType has getSize() method. And getMostRecentDecl give us an ConstantArrayType even if it is declared as IncompleteArrayType, but getCanonicalDecl does not. | |
1718 | It annoys me as well. I could use goto but I suppose you won't like it even more. :-) | |
clang/test/Analysis/initialization.c | ||
101 | I mixed up in terminilogy. I'll fix it. |
clang/test/Analysis/initialization.c | ||
---|---|---|
102 | It is possible to add a line const int glob_arr3[]; again and then it may not work? |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1663 | Aha ok, can you try iterating over redecls()? Separately, I suspect that this should be performed before the VarRegion is constructed in the first place. Maybe in its constructor we should assert(VD->isThisDeclarationADefinition()) or something like that. |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1663 |
Do you assume that in a list {redecl1, redecl2, redecl3} redecl2 may be our guy but 1 and 3 may not?
I'm not sure I got what you mean, but as I undestood that it is not be a part of this fix, right? | |
clang/test/Analysis/initialization.c | ||
102 | Yes. I tryed. It works. Thanks! But I'll add a test case. |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1661–1663 | ||
1663 | I think this should be part of this fix, but you don't have to do that iteration, there is a better thing for the job: getAnyInitializer. What we need is to find that Decl that has the InitExpr attached. This may not be the canonical nor the most recent redecl. | |
clang/test/Analysis/initialization.c | ||
101–102 | I'd like to see some more elaborate test cases. Notably const int glob_arr3[]; // Incomplete array declaration const int glob_arr3[4] = {1, 2, 3}; // Incomplete Array redeclaration const int glob_arr3[]; // Incomplete array redeclaration here neither the canonical nor the most recent decl have the initexpr. |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1661–1663 | Thnx, I'll try this approach. | |
clang/test/Analysis/initialization.c | ||
101–102 | Exactly. I'll add this particular case, but I should mention that AST surprisingly shows the third redeclaration as ConstantArrayType with the extent. Thus, it works for the current fix. |-VarDecl 0xc0725b0 <line:6:1, col:21> col:11 glob_arr3 'const int []' |-VarDecl 0xc072700 prev 0xc0725b0 <line:7:1, col:34> col:11 glob_arr3 'const int [4]' cinit | `-InitListExpr 0xc072830 <col:26, col:34> 'const int [4]' | |-array_filler: ImplicitValueInitExpr 0xc0728a8 <<invalid sloc>> 'const int' | |-IntegerLiteral 0xc072768 <col:27> 'int' 1 | |-IntegerLiteral 0xc072788 <col:30> 'int' 2 | `-IntegerLiteral 0xc0727a8 <col:33> 'int' 3 `-VarDecl 0xc0728e0 prev 0xc072700 <line:8:1, col:21> col:11 glob_arr3 'const int [4]' |
clang/test/Analysis/initialization.c | ||
---|---|---|
101–102 | So, the redeclaration actually changed the type to ConstantArrayType. If so, you should probably mention this in the comment. |
Updated according to the latest suggestions.
@martong I think now this patch is way more simple and clear than before.
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1632–1636 | Wait. But if VD is null, you get a null-dereference. Oh, but the getAnyInitializer() will overwrite it! That's a surprise. That way, with a properly chosen name you could spare the NOTE comment as well. |
Thanks, indeed! LGTM (with nits about VD)!
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1632–1636 | Yes I agree, probably it is easier to follow the code if you make it explicit that VD is overwritten here. |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1632–1636 |
We have an assertion above.
So was for me :) I'll add a NOTE in the comment. |
Minor nits. Aside from that just land it.
Thanks for the fix.
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1637 | If the return value of getAnyInitializer() is null, then the value of VD will be actually preserved. |
clang/lib/StaticAnalyzer/Core/RegionStore.cpp | ||
---|---|---|
1637 | I'll add the comment before the load. |
Wait. But if VD is null, you get a null-dereference.
But you already dereferenced VD multiple times, so it cannot be null.
Oh, but the getAnyInitializer() will overwrite it! That's a surprise.
TBH I would rather pass a fresh uninitialized pointer if you really need the exact decl which actually provided the initialized expression to make this behavior explicit.
That way, with a properly chosen name you could spare the NOTE comment as well.