[analyzer] Do not cache out on some shared implicit AST nodes

Some AST nodes which 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.
Since these nodes do not really require any processing from the analyzer
we just omit them from the CFG.

Differential Revision: https://reviews.llvm.org/D71371
This commit is contained in:
Gabor Horvath 2019-12-11 11:34:44 -08:00
parent 83e1bd36be
commit 9fdcae7c81
8 changed files with 64 additions and 18 deletions

View File

@ -1251,6 +1251,7 @@ public:
bool AddRichCXXConstructors = false;
bool MarkElidedCXXConstructors = false;
bool AddVirtualBaseBranches = false;
bool OmitImplicitValueInitializers = false;
BuildOptions() = default;

View File

@ -2135,6 +2135,11 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
default:
return VisitStmt(S, asc);
case Stmt::ImplicitValueInitExprClass:
if (BuildOpts.OmitImplicitValueInitializers)
return Block;
return VisitStmt(S, asc);
case Stmt::AddrLabelExprClass:
return VisitAddrLabelExpr(cast<AddrLabelExpr>(S), asc);

View File

@ -43,6 +43,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
options(Options) {
AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
AnaCtxMgr.getCFGBuildOptions().OmitImplicitValueInitializers = true;
}
AnalysisManager::~AnalysisManager() {

View File

@ -1321,6 +1321,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::WhileStmtClass:
case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
case Stmt::ImplicitValueInitExprClass:
// These nodes are shared in the CFG and would case caching out.
// Moreover, no additional evaluation required for them, the
// analyzer can reconstruct these values from the AST.
llvm_unreachable("Should be pruned from CFG");
case Stmt::ObjCSubscriptRefExprClass:
case Stmt::ObjCPropertyRefExprClass:
@ -1391,7 +1396,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::IntegerLiteralClass:
case Stmt::FixedPointLiteralClass:
case Stmt::CharacterLiteralClass:
case Stmt::ImplicitValueInitExprClass:
case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:

View File

@ -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}}
}

View File

@ -31,11 +31,10 @@ void test() {
// CHECK: 11: struct LUQ var = {getUQ(), .uq.q.a = 100};
// CHECK: 12: 1
// CHECK: 13: 2
// CHECK: 14: /*implicit*/(int)0
// CHECK: 15: {[B1.12], [B1.13]}
// CHECK: 14: {[B1.12], [B1.13]}
// CHECK: 17: /*no init*/
// CHECK: 18: /*no init*/
// CHECK: 19: /*no init*/
// CHECK: 20: 3
// CHECK: 21: {[B1.18], [B1.19], [B1.20]}
// CHECK: 22: {/*base*/[B1.17], /*updater*/[B1.21]}
// CHECK: 24: struct Q s[] = {[0] = (struct Q){1, 2}, [0].c = 3};
// CHECK: 19: 3
// CHECK: 20: {[B1.17], [B1.18], [B1.19]}
// CHECK: 21: {/*base*/[B1.16], /*updater*/[B1.20]}
// CHECK: 23: struct Q s[] = {[0] = (struct Q){1, 2}, [0].c = 3};

View File

@ -126,14 +126,13 @@ public:
// WARNINGS-NEXT: 5: (CXXConstructExpr, class A)
// ANALYZER-NEXT: 5: (CXXConstructExpr, A() (Base initializer), class A)
// CHECK-NEXT: 6: A([B1.5]) (Base initializer)
// CHECK-NEXT: 7: /*implicit*/(int)0
// CHECK-NEXT: 8: i([B1.7]) (Member initializer)
// CHECK-NEXT: 9: this
// CHECK-NEXT: 10: [B1.9]->i
// CHECK-NEXT: 11: r([B1.10]) (Member initializer)
// WARNINGS-NEXT: 12: (CXXConstructExpr, class A)
// ANALYZER-NEXT: 12: (CXXConstructExpr, [B1.13], class A)
// CHECK-NEXT: 13: A a;
// CHECK-NEXT: 7: i(/*implicit*/(int)0) (Member initializer)
// CHECK-NEXT: 8: this
// CHECK-NEXT: 9: [B1.8]->i
// CHECK-NEXT: 10: r([B1.9]) (Member initializer)
// WARNINGS-NEXT: 11: (CXXConstructExpr, class A)
// ANALYZER-NEXT: 11: (CXXConstructExpr, [B1.12], class A)
// CHECK-NEXT: 12: A a;
// CHECK-NEXT: Preds (2): B2 B3
// CHECK-NEXT: Succs (1): B0
// CHECK: [B2]

View File

@ -1224,8 +1224,7 @@ const C &bar3(bool coin) {
// CHECK: 16: a([B1.15]) (Member initializer)
// CHECK: 17: ~B() (Temporary object destructor)
// CHECK: 18: ~A() (Temporary object destructor)
// CHECK: 19: /*implicit*/(int)0
// CHECK: 20: b([B1.19]) (Member initializer)
// CHECK: 19: b(/*implicit*/(int)0) (Member initializer)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]