mirror of https://github.com/microsoft/clang.git
[Static Analyzer] Lambda support.
Differential Revision: http://reviews.llvm.org/D12652 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247426 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
162a673e5f
commit
c0c55d2615
|
@ -256,6 +256,9 @@ private:
|
||||||
/// \sa getMaxNodesPerTopLevelFunction
|
/// \sa getMaxNodesPerTopLevelFunction
|
||||||
Optional<unsigned> MaxNodesPerTopLevelFunction;
|
Optional<unsigned> MaxNodesPerTopLevelFunction;
|
||||||
|
|
||||||
|
/// \sa shouldInlineLambdas
|
||||||
|
Optional<bool> InlineLambdas;
|
||||||
|
|
||||||
/// A helper function that retrieves option for a given full-qualified
|
/// A helper function that retrieves option for a given full-qualified
|
||||||
/// checker name.
|
/// checker name.
|
||||||
/// Options for checkers can be specified via 'analyzer-config' command-line
|
/// 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.
|
/// This is controlled by the 'max-nodes' config option.
|
||||||
unsigned getMaxNodesPerTopLevelFunction();
|
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:
|
public:
|
||||||
AnalyzerOptions() :
|
AnalyzerOptions() :
|
||||||
AnalysisStoreOpt(RegionStoreModel),
|
AnalysisStoreOpt(RegionStoreModel),
|
||||||
|
|
|
@ -341,6 +341,10 @@ public:
|
||||||
void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst);
|
ExplodedNodeSet &Dst);
|
||||||
|
|
||||||
|
/// VisitLambdaExpr - Transfer function logic for LambdaExprs.
|
||||||
|
void VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
|
||||||
|
ExplodedNodeSet &Dst);
|
||||||
|
|
||||||
/// VisitBinaryOperator - Transfer function logic for binary operators.
|
/// VisitBinaryOperator - Transfer function logic for binary operators.
|
||||||
void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred,
|
void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst);
|
ExplodedNodeSet &Dst);
|
||||||
|
|
|
@ -325,3 +325,7 @@ bool AnalyzerOptions::shouldPrunePaths() {
|
||||||
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
|
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
|
||||||
return getBooleanOption("cfg-conditional-static-initializers", true);
|
return getBooleanOption("cfg-conditional-static-initializers", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnalyzerOptions::shouldInlineLambdas() {
|
||||||
|
return getBooleanOption("inline-lambdas", /*Default=*/true);
|
||||||
|
}
|
||||||
|
|
|
@ -769,7 +769,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
case Stmt::SEHTryStmtClass:
|
case Stmt::SEHTryStmtClass:
|
||||||
case Stmt::SEHExceptStmtClass:
|
case Stmt::SEHExceptStmtClass:
|
||||||
case Stmt::SEHLeaveStmtClass:
|
case Stmt::SEHLeaveStmtClass:
|
||||||
case Stmt::LambdaExprClass:
|
|
||||||
case Stmt::SEHFinallyStmtClass: {
|
case Stmt::SEHFinallyStmtClass: {
|
||||||
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
|
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
|
||||||
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
|
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
|
||||||
|
@ -1013,6 +1012,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||||
Bldr.addNodes(Dst);
|
Bldr.addNodes(Dst);
|
||||||
break;
|
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: {
|
case Stmt::BinaryOperatorClass: {
|
||||||
const BinaryOperator* B = cast<BinaryOperator>(S);
|
const BinaryOperator* B = cast<BinaryOperator>(S);
|
||||||
if (B->isLogicalOp()) {
|
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,
|
// 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
|
// you can even do things with it. We simply pretend
|
||||||
assert(Ex->isGLValue() || VD->getType()->isVoidType());
|
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
|
// For references, the 'lvalue' is the pointer address stored in the
|
||||||
// reference region.
|
// reference region.
|
||||||
if (VD->getType()->isReferenceType()) {
|
if (VD->getType()->isReferenceType() || CaptureByReference) {
|
||||||
if (const MemRegion *R = V.getAsRegion())
|
if (const MemRegion *R = V.getAsRegion())
|
||||||
V = state->getSVal(R);
|
V = state->getSVal(R);
|
||||||
else
|
else
|
||||||
|
|
|
@ -513,3 +513,41 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
|
||||||
SVal V = state->getSVal(loc::MemRegionVal(R));
|
SVal V = state->getSVal(loc::MemRegionVal(R));
|
||||||
Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V));
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -1013,10 +1013,21 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
|
||||||
const CXXThisRegion*
|
const CXXThisRegion*
|
||||||
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
|
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
|
||||||
const LocationContext *LC) {
|
const LocationContext *LC) {
|
||||||
const StackFrameContext *STC = LC->getCurrentStackFrame();
|
|
||||||
assert(STC);
|
|
||||||
const PointerType *PT = thisPointerTy->getAs<PointerType>();
|
const PointerType *PT = thisPointerTy->getAs<PointerType>();
|
||||||
assert(PT);
|
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));
|
return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,3 +174,17 @@ int radar_13213575() {
|
||||||
return radar13213575_testit<true>(5) + radar13213575_testit<false>(3);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
// RUN: FileCheck --input-file=%t %s
|
||||||
|
|
||||||
|
void clang_analyzer_warnIfReached();
|
||||||
|
void clang_analyzer_eval(int);
|
||||||
|
|
||||||
struct X { X(const X&); };
|
struct X { X(const X&); };
|
||||||
void f(X x) { (void) [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: [B2 (ENTRY)]
|
||||||
// CHECK: Succs (1): B1
|
// CHECK: Succs (1): B1
|
||||||
// CHECK: [B1]
|
// CHECK: [B1]
|
||||||
|
|
|
@ -299,13 +299,7 @@ namespace destructors {
|
||||||
void testRecursiveFramesStart() { testRecursiveFrames(false); }
|
void testRecursiveFramesStart() { testRecursiveFrames(false); }
|
||||||
|
|
||||||
void testLambdas() {
|
void testLambdas() {
|
||||||
// This is the test we would like to write:
|
[]() { check(NoReturnDtor()); } != nullptr || check(Dtor());
|
||||||
// []() { 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testGnuExpressionStatements(int v) {
|
void testGnuExpressionStatements(int v) {
|
||||||
|
|
Loading…
Reference in New Issue