diff options
| author | Gabor Horvath <xazax.hun@gmail.com> | 2015-09-11 16:55:01 +0000 |
|---|---|---|
| committer | Gabor Horvath <xazax.hun@gmail.com> | 2015-09-11 16:55:01 +0000 |
| commit | 15843343b67a37c8859bd7f84d2f34b0f7eead85 (patch) | |
| tree | d78eac474e240b69e50692102685ac4a4dd54113 /clang | |
| parent | 62921286982916bf03158f31fb053882faee7bf1 (diff) | |
| download | bcm5719-llvm-15843343b67a37c8859bd7f84d2f34b0f7eead85.tar.gz bcm5719-llvm-15843343b67a37c8859bd7f84d2f34b0f7eead85.zip | |
[Static Analyzer] Lambda support.
Differential Revision: http://reviews.llvm.org/D12652
llvm-svn: 247426
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h | 7 | ||||
| -rw-r--r-- | clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 40 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 38 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/MemRegion.cpp | 15 | ||||
| -rw-r--r-- | clang/test/Analysis/dead-stores.cpp | 14 | ||||
| -rw-r--r-- | clang/test/Analysis/lambda-notes.cpp | 204 | ||||
| -rw-r--r-- | clang/test/Analysis/lambdas.cpp | 174 | ||||
| -rw-r--r-- | clang/test/Analysis/temporaries.cpp | 8 |
10 files changed, 495 insertions, 13 deletions
diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index f02e48a4418..be212336a69 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -256,6 +256,9 @@ private: /// \sa getMaxNodesPerTopLevelFunction Optional<unsigned> MaxNodesPerTopLevelFunction; + /// \sa shouldInlineLambdas + Optional<bool> InlineLambdas; + /// A helper function that retrieves option for a given full-qualified /// checker name. /// Options for checkers can be specified via 'analyzer-config' command-line @@ -509,6 +512,10 @@ public: /// This is controlled by the 'max-nodes' config option. unsigned getMaxNodesPerTopLevelFunction(); + /// Returns true if lambdas should be inlined. Otherwise a sink node will be + /// generated each time a LambdaExpr is visited. + bool shouldInlineLambdas(); + public: AnalyzerOptions() : AnalysisStoreOpt(RegionStoreModel), diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index d8f1c34fefd..8337f770d4b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -341,6 +341,10 @@ public: void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst); + /// VisitLambdaExpr - Transfer function logic for LambdaExprs. + void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// VisitBinaryOperator - Transfer function logic for binary operators. void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst); diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 7d7fc9d0fb5..93133ef4c4b 100644 --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -325,3 +325,7 @@ bool AnalyzerOptions::shouldPrunePaths() { bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { return getBooleanOption("cfg-conditional-static-initializers", true); } + +bool AnalyzerOptions::shouldInlineLambdas() { + return getBooleanOption("inline-lambdas", /*Default=*/true); +} diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index d4f2e40605f..00d9c7a9616 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -769,7 +769,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: - case Stmt::LambdaExprClass: case Stmt::SEHFinallyStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); @@ -1013,6 +1012,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::LambdaExprClass: + if (AMgr.options.shouldInlineLambdas()) { + Bldr.takeNodes(Pred); + VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + } else { + const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + } + break; + case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { @@ -1853,11 +1863,35 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, // C permits "extern void v", and if you cast the address to a valid type, // you can even do things with it. We simply pretend assert(Ex->isGLValue() || VD->getType()->isVoidType()); - SVal V = state->getLValue(VD, Pred->getLocationContext()); + const LocationContext *LocCtxt = Pred->getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr; + const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); + SVal V; + bool CaptureByReference = false; + if (AMgr.options.shouldInlineLambdas() && DeclRefEx && + DeclRefEx->refersToEnclosingVariableOrCapture() && MD && + MD->getParent()->isLambda()) { + // Lookup the field of the lambda. + const CXXRecordDecl *CXXRec = MD->getParent(); + llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); + const FieldDecl *FD = LambdaCaptureFields[VD]; + Loc CXXThis = + svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame()); + SVal CXXThisVal = state->getSVal(CXXThis); + V = state->getLValue(FD, CXXThisVal); + if (FD->getType()->isReferenceType() && + !VD->getType()->isReferenceType()) + CaptureByReference = true; + } else { + V = state->getLValue(VD, LocCtxt); + } // For references, the 'lvalue' is the pointer address stored in the // reference region. - if (VD->getType()->isReferenceType()) { + if (VD->getType()->isReferenceType() || CaptureByReference) { if (const MemRegion *R = V.getAsRegion()) V = state->getSVal(R); else diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 849ca960ade..1958d7bc4b1 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -513,3 +513,41 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, SVal V = state->getSVal(loc::MemRegionVal(R)); Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } + +void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LocCtxt = Pred->getLocationContext(); + + // Get the region of the lambda itself. + const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( + LE, LocCtxt); + SVal V = loc::MemRegionVal(R); + + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the lambda, we should explicitly bind + // the captures. + CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); + for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), + e = LE->capture_init_end(); + i != e; ++i, ++CurField) { + SVal Field = State->getLValue(*CurField, V); + SVal InitExpr = State->getSVal(*i, LocCtxt); + State = State->bindLoc(Field, InitExpr); + } + + // Decay the Loc into an RValue, because there might be a + // MaterializeTemporaryExpr node above this one which expects the bound value + // to be an RValue. + SVal LambdaRVal = State->getSVal(R); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + // FIXME: is this the right program point kind? + Bldr.generateNode(LE, Pred, + State->BindExpr(LE, LocCtxt, LambdaRVal), + nullptr, ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); +} diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index 78c5f4ad7af..d41fed0619e 100644 --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1013,10 +1013,21 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const CXXThisRegion* MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); const PointerType *PT = thisPointerTy->getAs<PointerType>(); assert(PT); + // Inside the body of the operator() of a lambda a this expr might refer to an + // object in one of the parent location contexts. + const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + // FIXME: when operator() of lambda is analyzed as a top level function and + // 'this' refers to a this to the enclosing scope, there is no right region to + // return. + while (!LC->inTopFrame() && + PT != D->getThisType(getContext())->getAs<PointerType>()) { + LC = LC->getParent(); + D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + } + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); } diff --git a/clang/test/Analysis/dead-stores.cpp b/clang/test/Analysis/dead-stores.cpp index d442c621d87..2027ee6be1f 100644 --- a/clang/test/Analysis/dead-stores.cpp +++ b/clang/test/Analysis/dead-stores.cpp @@ -174,3 +174,17 @@ int radar_13213575() { return radar13213575_testit<true>(5) + radar13213575_testit<false>(3); } +//===----------------------------------------------------------------------===// +// Dead store checking involving lambdas. +//===----------------------------------------------------------------------===// + +int basicLambda(int i, int j) { + i = 5; // no warning + j = 6; // no warning + [i] { (void)i; }(); + [&j] { (void)j; }(); + i = 2; + j = 3; + return i + j; +} + diff --git a/clang/test/Analysis/lambda-notes.cpp b/clang/test/Analysis/lambda-notes.cpp new file mode 100644 index 00000000000..8b548f3dc6f --- /dev/null +++ b/clang/test/Analysis/lambda-notes.cpp @@ -0,0 +1,204 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core -analyzer-config inline-lambdas=true -analyzer-output plist -verify %s -o %t +// RUN: FileCheck --input-file=%t %s + + +// Diagnostic inside a lambda + +void diagnosticFromLambda() { + int i = 0; + [=] { + int p = 5/i; // expected-warning{{Division by zero}} + (void)p; + }(); +} + +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>path</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>kind</key><string>control</string> +// CHECK: <key>edges</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>start</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>8</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>8</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: <key>end</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>event</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <key>ranges</key> +// CHECK: <array> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>12</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </array> +// CHECK: <key>depth</key><integer>0</integer> +// CHECK: <key>extended_message</key> +// CHECK: <string>The value 0 is assigned to field ''</string> +// CHECK: <key>message</key> +// CHECK: <string>The value 0 is assigned to field ''</string> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>event</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <key>ranges</key> +// CHECK: <array> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>12</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </array> +// CHECK: <key>depth</key><integer>0</integer> +// CHECK: <key>extended_message</key> +// CHECK: <string>Calling 'operator()'</string> +// CHECK: <key>message</key> +// CHECK: <string>Calling 'operator()'</string> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>event</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <key>depth</key><integer>1</integer> +// CHECK: <key>extended_message</key> +// CHECK: <string>Entered call from 'diagnosticFromLambda'</string> +// CHECK: <key>message</key> +// CHECK: <string>Entered call from 'diagnosticFromLambda'</string> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>control</string> +// CHECK: <key>edges</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>start</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>9</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: <key>end</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>14</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>14</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>event</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>14</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <key>ranges</key> +// CHECK: <array> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>13</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>15</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </array> +// CHECK: <key>depth</key><integer>1</integer> +// CHECK: <key>extended_message</key> +// CHECK: <string>Division by zero</string> +// CHECK: <key>message</key> +// CHECK: <string>Division by zero</string> +// CHECK: </dict> +// CHECK: </array> +// CHECK: <key>description</key><string>Division by zero</string> +// CHECK: <key>category</key><string>Logic error</string> +// CHECK: <key>type</key><string>Division by zero</string> +// CHECK: <key>check_name</key><string>core.DivideZero</string> +// CHECK: <key>issue_context_kind</key><string>C++ method</string> +// CHECK: <key>issue_context</key><string>operator()</string> +// CHECK: <key>issue_hash</key><string>1</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>10</integer> +// CHECK: <key>col</key><integer>14</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </dict> +// CHECK: </array> + diff --git a/clang/test/Analysis/lambdas.cpp b/clang/test/Analysis/lambdas.cpp index 33e216b57ec..15100be32d6 100644 --- a/clang/test/Analysis/lambdas.cpp +++ b/clang/test/Analysis/lambdas.cpp @@ -1,9 +1,181 @@ -// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=debug.DumpCFG %s > %t 2>&1 +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1 // RUN: FileCheck --input-file=%t %s +void clang_analyzer_warnIfReached(); +void clang_analyzer_eval(int); + struct X { X(const X&); }; void f(X x) { (void) [x]{}; } + +// Lambda semantics tests. + +void basicCapture() { + int i = 5; + [i]() mutable { + // clang_analyzer_eval does nothing in inlined functions. + if (i != 5) + clang_analyzer_warnIfReached(); + ++i; + }(); + [&i] { + if (i != 5) + clang_analyzer_warnIfReached(); + }(); + [&i] { + if (i != 5) + clang_analyzer_warnIfReached(); + i++; + }(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} +} + +void deferredLambdaCall() { + int i = 5; + auto l1 = [i]() mutable { + if (i != 5) + clang_analyzer_warnIfReached(); + ++i; + }; + auto l2 = [&i] { + if (i != 5) + clang_analyzer_warnIfReached(); + }; + auto l3 = [&i] { + if (i != 5) + clang_analyzer_warnIfReached(); + i++; + }; + l1(); + l2(); + l3(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} +} + +void multipleCaptures() { + int i = 5, j = 5; + [i, &j]() mutable { + if (i != 5 && j != 5) + clang_analyzer_warnIfReached(); + ++i; + ++j; + }(); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(j == 6); // expected-warning{{TRUE}} + [=]() mutable { + if (i != 5 && j != 6) + clang_analyzer_warnIfReached(); + ++i; + ++j; + }(); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(j == 6); // expected-warning{{TRUE}} + [&]() mutable { + if (i != 5 && j != 6) + clang_analyzer_warnIfReached(); + ++i; + ++j; + }(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + clang_analyzer_eval(j == 7); // expected-warning{{TRUE}} +} + +void testReturnValue() { + int i = 5; + auto l = [i] (int a) { + return i + a; + }; + int b = l(3); + clang_analyzer_eval(b == 8); // expected-warning{{TRUE}} +} + +// Nested lambdas. + +void testNestedLambdas() { + int i = 5; + auto l = [i]() mutable { + [&i]() { + ++i; + }(); + if (i != 6) + clang_analyzer_warnIfReached(); + }; + l(); + clang_analyzer_eval(i == 5); // expected-warning{{TRUE}} +} + +// Captured this. + +class RandomClass { + int i; + + void captureFields() { + i = 5; + [this]() { + // clang_analyzer_eval does nothing in inlined functions. + if (i != 5) + clang_analyzer_warnIfReached(); + ++i; + }(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} + } +}; + + +// Nested this capture. + +class RandomClass2 { + int i; + + void captureFields() { + i = 5; + [this]() { + // clang_analyzer_eval does nothing in inlined functions. + if (i != 5) + clang_analyzer_warnIfReached(); + ++i; + [this]() { + // clang_analyzer_eval does nothing in inlined functions. + if (i != 6) + clang_analyzer_warnIfReached(); + ++i; + }(); + }(); + clang_analyzer_eval(i == 7); // expected-warning{{TRUE}} + } +}; + + +// Captured function pointers. + +void inc(int &x) { + ++x; +} + +void testFunctionPointerCapture() { + void (*func)(int &) = inc; + int i = 5; + [&i, func] { + func(i); + }(); + clang_analyzer_eval(i == 6); // expected-warning{{TRUE}} +} + + +// Test inline defensive checks +int getNum(); + +void inlineDefensiveChecks() { + int i = getNum(); + [=]() { + if (i == 0) + ; + }(); + int p = 5/i; + (void)p; +} + // CHECK: [B2 (ENTRY)] // CHECK: Succs (1): B1 // CHECK: [B1] diff --git a/clang/test/Analysis/temporaries.cpp b/clang/test/Analysis/temporaries.cpp index 6e476339cb7..e96e9b0e28d 100644 --- a/clang/test/Analysis/temporaries.cpp +++ b/clang/test/Analysis/temporaries.cpp @@ -299,13 +299,7 @@ namespace destructors { void testRecursiveFramesStart() { testRecursiveFrames(false); } void testLambdas() { - // This is the test we would like to write: - // []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); - // But currently the analyzer stops when it encounters a lambda: - [] {}; - // The CFG for this now looks correct, but we still do not reach the line - // below. - clang_analyzer_warnIfReached(); // FIXME: Should warn. + []() { check(NoReturnDtor()); } != nullptr || check(Dtor()); } void testGnuExpressionStatements(int v) { |

