xazax.hun created this revision. xazax.hun added reviewers: NoQ, dcoughlin, Szelethus, baloghadamsoftware, haowei. xazax.hun added a project: clang. Herald added subscribers: Charusso, gamesh411, dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet.
Some AST nodes that stands for implicit initialization is shared. The analyzer will do the same evaluation on the same nodes resulting in the same state. The analyzer will "cache out", i.e. it thinks that it visited an already existing node in the exploded graph. This is not true in this case and we lose coverage. This patch introduces a trick that prevents caching out. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D71371 Files: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp clang/test/Analysis/designated-initializer-values.c Index: clang/test/Analysis/designated-initializer-values.c =================================================================== --- /dev/null +++ clang/test/Analysis/designated-initializer-values.c @@ -0,0 +1,38 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c99 -verify %s + +void clang_analyzer_eval(int); + +void array_init() { + int a[5] = {[4] = 29, [2] = 15, [0] = 4}; + clang_analyzer_eval(a[0] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(a[1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a[2] == 15); // expected-warning{{TRUE}} + clang_analyzer_eval(a[3] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a[4] == 29); // expected-warning{{TRUE}} + int b[5] = {[0 ... 2] = 1, [4] = 5}; + clang_analyzer_eval(b[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[1] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[2] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[3] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(b[4] == 5); // expected-warning{{TRUE}} +} + +struct point { + int x, y; +}; + +void struct_init() { + struct point p = {.y = 5, .x = 3}; + clang_analyzer_eval(p.x == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(p.y == 5); // expected-warning{{TRUE}} +} + +void array_of_struct() { + struct point ptarray[3] = { [2].y = 1, [2].x = 2, [0].x = 3 }; + clang_analyzer_eval(ptarray[0].x == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[0].y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[1].x == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[1].y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[2].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[2].y == 1); // expected-warning{{TRUE}} +} Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -193,6 +193,11 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, ObjectsUnderConstructionMap) +// The AST sometimes ends up sharing nodes for compile time constants. +// We might end up caching out on those notes, this trait intended to +// prevent that explicitly. +REGISTER_MAP_WITH_PROGRAMSTATE(NoCachingOutForConsts, const Stmt *, unsigned long) + //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -1403,6 +1408,13 @@ case Stmt::OMPArraySectionExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); + if (isa<ImplicitValueInitExpr>(S)) { + ProgramStateRef State = Pred->getState(); + const unsigned long *Count = State->get<NoCachingOutForConsts>(0); + State = State->set<NoCachingOutForConsts>(0, Count? *Count + 1 : 0); + Pred = Bldr.generateNode(S, Pred, State); + assert(Pred && "We should never cache out on constants"); + } ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this);
Index: clang/test/Analysis/designated-initializer-values.c =================================================================== --- /dev/null +++ clang/test/Analysis/designated-initializer-values.c @@ -0,0 +1,38 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c99 -verify %s + +void clang_analyzer_eval(int); + +void array_init() { + int a[5] = {[4] = 29, [2] = 15, [0] = 4}; + clang_analyzer_eval(a[0] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(a[1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a[2] == 15); // expected-warning{{TRUE}} + clang_analyzer_eval(a[3] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a[4] == 29); // expected-warning{{TRUE}} + int b[5] = {[0 ... 2] = 1, [4] = 5}; + clang_analyzer_eval(b[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[1] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[2] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b[3] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(b[4] == 5); // expected-warning{{TRUE}} +} + +struct point { + int x, y; +}; + +void struct_init() { + struct point p = {.y = 5, .x = 3}; + clang_analyzer_eval(p.x == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(p.y == 5); // expected-warning{{TRUE}} +} + +void array_of_struct() { + struct point ptarray[3] = { [2].y = 1, [2].x = 2, [0].x = 3 }; + clang_analyzer_eval(ptarray[0].x == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[0].y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[1].x == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[1].y == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[2].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(ptarray[2].y == 1); // expected-warning{{TRUE}} +} Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -193,6 +193,11 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, ObjectsUnderConstructionMap) +// The AST sometimes ends up sharing nodes for compile time constants. +// We might end up caching out on those notes, this trait intended to +// prevent that explicitly. +REGISTER_MAP_WITH_PROGRAMSTATE(NoCachingOutForConsts, const Stmt *, unsigned long) + //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -1403,6 +1408,13 @@ case Stmt::OMPArraySectionExprClass: case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); + if (isa<ImplicitValueInitExpr>(S)) { + ProgramStateRef State = Pred->getState(); + const unsigned long *Count = State->get<NoCachingOutForConsts>(0); + State = State->set<NoCachingOutForConsts>(0, Count? *Count + 1 : 0); + Pred = Bldr.generateNode(S, Pred, State); + assert(Pred && "We should never cache out on constants"); + } ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits