aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/CFG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/CFG.cpp')
-rw-r--r--lib/Analysis/CFG.cpp432
1 files changed, 324 insertions, 108 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 0ed1e988a196..a533a8d97b84 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -70,23 +70,47 @@ static SourceLocation GetEndLoc(Decl *D) {
return D->getLocation();
}
+/// Returns true on constant values based around a single IntegerLiteral.
+/// Allow for use of parentheses, integer casts, and negative signs.
+static bool IsIntegerLiteralConstantExpr(const Expr *E) {
+ // Allow parentheses
+ E = E->IgnoreParens();
+
+ // Allow conversions to different integer kind.
+ if (const auto *CE = dyn_cast<CastExpr>(E)) {
+ if (CE->getCastKind() != CK_IntegralCast)
+ return false;
+ E = CE->getSubExpr();
+ }
+
+ // Allow negative numbers.
+ if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
+ if (UO->getOpcode() != UO_Minus)
+ return false;
+ E = UO->getSubExpr();
+ }
+
+ return isa<IntegerLiteral>(E);
+}
+
/// Helper for tryNormalizeBinaryOperator. Attempts to extract an IntegerLiteral
-/// or EnumConstantDecl from the given Expr. If it fails, returns nullptr.
+/// constant expression or EnumConstantDecl from the given Expr. If it fails,
+/// returns nullptr.
static const Expr *tryTransformToIntOrEnumConstant(const Expr *E) {
E = E->IgnoreParens();
- if (isa<IntegerLiteral>(E))
+ if (IsIntegerLiteralConstantExpr(E))
return E;
if (auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
return isa<EnumConstantDecl>(DR->getDecl()) ? DR : nullptr;
return nullptr;
}
-/// Tries to interpret a binary operator into `Decl Op Expr` form, if Expr is
-/// an integer literal or an enum constant.
+/// Tries to interpret a binary operator into `Expr Op NumExpr` form, if
+/// NumExpr is an integer literal or an enum constant.
///
/// If this fails, at least one of the returned DeclRefExpr or Expr will be
/// null.
-static std::tuple<const DeclRefExpr *, BinaryOperatorKind, const Expr *>
+static std::tuple<const Expr *, BinaryOperatorKind, const Expr *>
tryNormalizeBinaryOperator(const BinaryOperator *B) {
BinaryOperatorKind Op = B->getOpcode();
@@ -108,8 +132,7 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) {
Constant = tryTransformToIntOrEnumConstant(B->getLHS());
}
- auto *D = dyn_cast<DeclRefExpr>(MaybeDecl->IgnoreParenImpCasts());
- return std::make_tuple(D, Op, Constant);
+ return std::make_tuple(MaybeDecl, Op, Constant);
}
/// For an expression `x == Foo && x == Bar`, this determines whether the
@@ -121,11 +144,11 @@ tryNormalizeBinaryOperator(const BinaryOperator *B) {
static bool areExprTypesCompatible(const Expr *E1, const Expr *E2) {
// User intent isn't clear if they're mixing int literals with enum
// constants.
- if (isa<IntegerLiteral>(E1) != isa<IntegerLiteral>(E2))
+ if (isa<DeclRefExpr>(E1) != isa<DeclRefExpr>(E2))
return false;
// Integer literal comparisons, regardless of literal type, are acceptable.
- if (isa<IntegerLiteral>(E1))
+ if (!isa<DeclRefExpr>(E1))
return true;
// IntegerLiterals are handled above and only EnumConstantDecls are expected
@@ -525,7 +548,7 @@ private:
CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc);
CFGBlock *VisitCaseStmt(CaseStmt *C);
CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc);
- CFGBlock *VisitCompoundStmt(CompoundStmt *C);
+ CFGBlock *VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestructed);
CFGBlock *VisitConditionalOperator(AbstractConditionalOperator *C,
AddStmtChoice asc);
CFGBlock *VisitContinueStmt(ContinueStmt *C);
@@ -546,7 +569,8 @@ private:
CFGBlock *VisitDeclSubExpr(DeclStmt *DS);
CFGBlock *VisitDefaultStmt(DefaultStmt *D);
CFGBlock *VisitDoStmt(DoStmt *D);
- CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
+ CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E,
+ AddStmtChoice asc, bool ExternallyDestructed);
CFGBlock *VisitForStmt(ForStmt *F);
CFGBlock *VisitGotoStmt(GotoStmt *G);
CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc);
@@ -585,7 +609,8 @@ private:
CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc);
CFGBlock *VisitWhileStmt(WhileStmt *W);
- CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd);
+ CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd,
+ bool ExternallyDestructed = false);
CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc);
CFGBlock *VisitChildren(Stmt *S);
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
@@ -656,15 +681,17 @@ private:
// Visitors to walk an AST and generate destructors of temporaries in
// full expression.
- CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+ CFGBlock *VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
TempDtorContext &Context);
- CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, TempDtorContext &Context);
+ CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
+ TempDtorContext &Context);
CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
+ bool ExternallyDestructed,
TempDtorContext &Context);
CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
+ CXXBindTemporaryExpr *E, bool ExternallyDestructed, TempDtorContext &Context);
CFGBlock *VisitConditionalOperatorForTemporaryDtors(
- AbstractConditionalOperator *E, bool BindToTemporary,
+ AbstractConditionalOperator *E, bool ExternallyDestructed,
TempDtorContext &Context);
void InsertTempDtorDecisionBlock(const TempDtorContext &Context,
CFGBlock *FalseSucc = nullptr);
@@ -1017,34 +1044,34 @@ private:
if (!LHS->isComparisonOp() || !RHS->isComparisonOp())
return {};
- const DeclRefExpr *Decl1;
- const Expr *Expr1;
+ const Expr *DeclExpr1;
+ const Expr *NumExpr1;
BinaryOperatorKind BO1;
- std::tie(Decl1, BO1, Expr1) = tryNormalizeBinaryOperator(LHS);
+ std::tie(DeclExpr1, BO1, NumExpr1) = tryNormalizeBinaryOperator(LHS);
- if (!Decl1 || !Expr1)
+ if (!DeclExpr1 || !NumExpr1)
return {};
- const DeclRefExpr *Decl2;
- const Expr *Expr2;
+ const Expr *DeclExpr2;
+ const Expr *NumExpr2;
BinaryOperatorKind BO2;
- std::tie(Decl2, BO2, Expr2) = tryNormalizeBinaryOperator(RHS);
+ std::tie(DeclExpr2, BO2, NumExpr2) = tryNormalizeBinaryOperator(RHS);
- if (!Decl2 || !Expr2)
+ if (!DeclExpr2 || !NumExpr2)
return {};
// Check that it is the same variable on both sides.
- if (Decl1->getDecl() != Decl2->getDecl())
+ if (!Expr::isSameComparisonOperand(DeclExpr1, DeclExpr2))
return {};
// Make sure the user's intent is clear (e.g. they're comparing against two
// int literals, or two things from the same enum)
- if (!areExprTypesCompatible(Expr1, Expr2))
+ if (!areExprTypesCompatible(NumExpr1, NumExpr2))
return {};
Expr::EvalResult L1Result, L2Result;
- if (!Expr1->EvaluateAsInt(L1Result, *Context) ||
- !Expr2->EvaluateAsInt(L2Result, *Context))
+ if (!NumExpr1->EvaluateAsInt(L1Result, *Context) ||
+ !NumExpr2->EvaluateAsInt(L2Result, *Context))
return {};
llvm::APSInt L1 = L1Result.Val.getInt();
@@ -1077,6 +1104,10 @@ private:
// * Variable x is equal to the largest literal.
// * Variable x is greater than largest literal.
bool AlwaysTrue = true, AlwaysFalse = true;
+ // Track value of both subexpressions. If either side is always
+ // true/false, another warning should have already been emitted.
+ bool LHSAlwaysTrue = true, LHSAlwaysFalse = true;
+ bool RHSAlwaysTrue = true, RHSAlwaysFalse = true;
for (const llvm::APSInt &Value : Values) {
TryResult Res1, Res2;
Res1 = analyzeLogicOperatorCondition(BO1, Value, L1);
@@ -1092,16 +1123,47 @@ private:
AlwaysTrue &= (Res1.isTrue() || Res2.isTrue());
AlwaysFalse &= !(Res1.isTrue() || Res2.isTrue());
}
+
+ LHSAlwaysTrue &= Res1.isTrue();
+ LHSAlwaysFalse &= Res1.isFalse();
+ RHSAlwaysTrue &= Res2.isTrue();
+ RHSAlwaysFalse &= Res2.isFalse();
}
if (AlwaysTrue || AlwaysFalse) {
- if (BuildOpts.Observer)
+ if (!LHSAlwaysTrue && !LHSAlwaysFalse && !RHSAlwaysTrue &&
+ !RHSAlwaysFalse && BuildOpts.Observer)
BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue);
return TryResult(AlwaysTrue);
}
return {};
}
+ /// A bitwise-or with a non-zero constant always evaluates to true.
+ TryResult checkIncorrectBitwiseOrOperator(const BinaryOperator *B) {
+ const Expr *LHSConstant =
+ tryTransformToIntOrEnumConstant(B->getLHS()->IgnoreParenImpCasts());
+ const Expr *RHSConstant =
+ tryTransformToIntOrEnumConstant(B->getRHS()->IgnoreParenImpCasts());
+
+ if ((LHSConstant && RHSConstant) || (!LHSConstant && !RHSConstant))
+ return {};
+
+ const Expr *Constant = LHSConstant ? LHSConstant : RHSConstant;
+
+ Expr::EvalResult Result;
+ if (!Constant->EvaluateAsInt(Result, *Context))
+ return {};
+
+ if (Result.Val.getInt() == 0)
+ return {};
+
+ if (BuildOpts.Observer)
+ BuildOpts.Observer->compareBitwiseOr(B);
+
+ return TryResult(true);
+ }
+
/// Try and evaluate an expression to an integer constant.
bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) {
if (!BuildOpts.PruneTriviallyFalseEdges)
@@ -1119,7 +1181,7 @@ private:
return {};
if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) {
- if (Bop->isLogicalOp()) {
+ if (Bop->isLogicalOp() || Bop->isEqualityOp()) {
// Check the cache first.
CachedBoolEvalsTy::iterator I = CachedBoolEvals.find(S);
if (I != CachedBoolEvals.end())
@@ -1203,6 +1265,10 @@ private:
TryResult BopRes = checkIncorrectRelationalOperator(Bop);
if (BopRes.isKnown())
return BopRes.isTrue();
+ } else if (Bop->getOpcode() == BO_Or) {
+ TryResult BopRes = checkIncorrectBitwiseOrOperator(Bop);
+ if (BopRes.isKnown())
+ return BopRes.isTrue();
}
}
@@ -1575,7 +1641,7 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/false, Context);
}
}
@@ -2051,7 +2117,8 @@ CFGBuilder::prependAutomaticObjScopeEndWithTerminator(
/// Visit - Walk the subtree of a statement and add extra
/// blocks for ternary operators, &&, and ||. We also process "," and
/// DeclStmts (which may contain nested control-flow).
-CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
+CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
+ bool ExternallyDestructed) {
if (!S) {
badCFG = true;
return nullptr;
@@ -2096,7 +2163,7 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
return VisitChooseExpr(cast<ChooseExpr>(S), asc);
case Stmt::CompoundStmtClass:
- return VisitCompoundStmt(cast<CompoundStmt>(S));
+ return VisitCompoundStmt(cast<CompoundStmt>(S), ExternallyDestructed);
case Stmt::ConditionalOperatorClass:
return VisitConditionalOperator(cast<ConditionalOperator>(S), asc);
@@ -2108,7 +2175,8 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
return VisitCXXCatchStmt(cast<CXXCatchStmt>(S));
case Stmt::ExprWithCleanupsClass:
- return VisitExprWithCleanups(cast<ExprWithCleanups>(S), asc);
+ return VisitExprWithCleanups(cast<ExprWithCleanups>(S),
+ asc, ExternallyDestructed);
case Stmt::CXXDefaultArgExprClass:
case Stmt::CXXDefaultInitExprClass:
@@ -2301,6 +2369,9 @@ CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U,
appendStmt(Block, U);
}
+ if (U->getOpcode() == UO_LNot)
+ tryEvaluateBool(U->getSubExpr()->IgnoreParens());
+
return Visit(U->getSubExpr(), AddStmtChoice());
}
@@ -2435,6 +2506,9 @@ CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
appendStmt(Block, B);
}
+ if (B->isEqualityOp() || B->isRelationalOp())
+ tryEvaluateBool(B);
+
CFGBlock *RBlock = Visit(B->getRHS());
CFGBlock *LBlock = Visit(B->getLHS());
// If visiting RHS causes us to finish 'Block', e.g. the RHS is a StmtExpr
@@ -2474,10 +2548,8 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {
static bool CanThrow(Expr *E, ASTContext &Ctx) {
QualType Ty = E->getType();
- if (Ty->isFunctionPointerType())
- Ty = Ty->getAs<PointerType>()->getPointeeType();
- else if (Ty->isBlockPointerType())
- Ty = Ty->getAs<BlockPointerType>()->getPointeeType();
+ if (Ty->isFunctionPointerType() || Ty->isBlockPointerType())
+ Ty = Ty->getPointeeType();
const FunctionType *FT = Ty->getAs<FunctionType>();
if (FT) {
@@ -2603,7 +2675,7 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C,
return addStmt(C->getCond());
}
-CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
+CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C, bool ExternallyDestructed) {
LocalScope::const_iterator scopeBeginPos = ScopePos;
addLocalScopeForStmt(C);
@@ -2619,11 +2691,16 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
I != E; ++I ) {
// If we hit a segment of code just containing ';' (NullStmts), we can
// get a null block back. In such cases, just use the LastBlock
- if (CFGBlock *newBlock = addStmt(*I))
+ CFGBlock *newBlock = Visit(*I, AddStmtChoice::AlwaysAdd,
+ ExternallyDestructed);
+
+ if (newBlock)
LastBlock = newBlock;
if (badCFG)
return nullptr;
+
+ ExternallyDestructed = false;
}
return LastBlock;
@@ -2766,7 +2843,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
// Generate destructors for temporaries in initialization expression.
TempDtorContext Context;
VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/true, Context);
}
}
@@ -2980,9 +3057,17 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) {
if (!Block->hasNoReturnElement())
addSuccessor(Block, &cfg->getExit());
- // Add the return statement to the block. This may create new blocks if R
- // contains control-flow (short-circuit operations).
- return VisitStmt(S, AddStmtChoice::AlwaysAdd);
+ // Add the return statement to the block.
+ appendStmt(Block, S);
+
+ // Visit children
+ if (ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) {
+ if (Expr *O = RS->getRetValue())
+ return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true);
+ return Block;
+ } else { // co_return
+ return VisitChildren(S);
+ }
}
CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
@@ -3014,7 +3099,7 @@ CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
}
CFGBlock *CFGBuilder::VisitSEHFinallyStmt(SEHFinallyStmt *FS) {
- return VisitCompoundStmt(FS->getBlock());
+ return VisitCompoundStmt(FS->getBlock(), /*ExternallyDestructed=*/false);
}
CFGBlock *CFGBuilder::VisitSEHLeaveStmt(SEHLeaveStmt *LS) {
@@ -3898,7 +3983,7 @@ CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) {
autoCreateBlock();
appendStmt(Block, SE);
}
- return VisitCompoundStmt(SE->getSubStmt());
+ return VisitCompoundStmt(SE->getSubStmt(), /*ExternallyDestructed=*/true);
}
CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
@@ -4363,12 +4448,12 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
}
CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
- AddStmtChoice asc) {
+ AddStmtChoice asc, bool ExternallyDestructed) {
if (BuildOpts.AddTemporaryDtors) {
// If adding implicit destructors visit the full expression for adding
// destructors of temporaries.
TempDtorContext Context;
- VisitForTemporaryDtors(E->getSubExpr(), false, Context);
+ VisitForTemporaryDtors(E->getSubExpr(), ExternallyDestructed, Context);
// Full expression has to be added as CFGStmt so it will be sequenced
// before destructors of it's temporaries.
@@ -4477,6 +4562,10 @@ CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E,
autoCreateBlock();
appendStmt(Block, E);
}
+
+ if (E->getCastKind() == CK_IntegralToBoolean)
+ tryEvaluateBool(E->getSubExpr()->IgnoreParens());
+
return Visit(E->getSubExpr(), AddStmtChoice());
}
@@ -4504,7 +4593,7 @@ CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) {
return addStmt(I->getTarget());
}
-CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool ExternallyDestructed,
TempDtorContext &Context) {
assert(BuildOpts.AddImplicitDtors && BuildOpts.AddTemporaryDtors);
@@ -4515,28 +4604,32 @@ tryAgain:
}
switch (E->getStmtClass()) {
default:
- return VisitChildrenForTemporaryDtors(E, Context);
+ return VisitChildrenForTemporaryDtors(E, false, Context);
+
+ case Stmt::InitListExprClass:
+ return VisitChildrenForTemporaryDtors(E, ExternallyDestructed, Context);
case Stmt::BinaryOperatorClass:
return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E),
+ ExternallyDestructed,
Context);
case Stmt::CXXBindTemporaryExprClass:
return VisitCXXBindTemporaryExprForTemporaryDtors(
- cast<CXXBindTemporaryExpr>(E), BindToTemporary, Context);
+ cast<CXXBindTemporaryExpr>(E), ExternallyDestructed, Context);
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass:
return VisitConditionalOperatorForTemporaryDtors(
- cast<AbstractConditionalOperator>(E), BindToTemporary, Context);
+ cast<AbstractConditionalOperator>(E), ExternallyDestructed, Context);
case Stmt::ImplicitCastExprClass:
- // For implicit cast we want BindToTemporary to be passed further.
+ // For implicit cast we want ExternallyDestructed to be passed further.
E = cast<CastExpr>(E)->getSubExpr();
goto tryAgain;
case Stmt::CXXFunctionalCastExprClass:
- // For functional cast we want BindToTemporary to be passed further.
+ // For functional cast we want ExternallyDestructed to be passed further.
E = cast<CXXFunctionalCastExpr>(E)->getSubExpr();
goto tryAgain;
@@ -4550,7 +4643,7 @@ tryAgain:
case Stmt::MaterializeTemporaryExprClass: {
const MaterializeTemporaryExpr* MTE = cast<MaterializeTemporaryExpr>(E);
- BindToTemporary = (MTE->getStorageDuration() != SD_FullExpression);
+ ExternallyDestructed = (MTE->getStorageDuration() != SD_FullExpression);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
// Find the expression whose lifetime needs to be extended.
@@ -4561,7 +4654,7 @@ tryAgain:
// Visit the skipped comma operator left-hand sides for other temporaries.
for (const Expr *CommaLHS : CommaLHSs) {
VisitForTemporaryDtors(const_cast<Expr *>(CommaLHS),
- /*BindToTemporary=*/false, Context);
+ /*ExternallyDestructed=*/false, Context);
}
goto tryAgain;
}
@@ -4579,13 +4672,18 @@ tryAgain:
for (Expr *Init : LE->capture_inits()) {
if (Init) {
if (CFGBlock *R = VisitForTemporaryDtors(
- Init, /*BindToTemporary=*/false, Context))
+ Init, /*ExternallyDestructed=*/true, Context))
B = R;
}
}
return B;
}
+ case Stmt::StmtExprClass:
+ // Don't recurse into statement expressions; any cleanups inside them
+ // will be wrapped in their own ExprWithCleanups.
+ return Block;
+
case Stmt::CXXDefaultArgExprClass:
E = cast<CXXDefaultArgExpr>(E)->getExpr();
goto tryAgain;
@@ -4597,6 +4695,7 @@ tryAgain:
}
CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
+ bool ExternallyDestructed,
TempDtorContext &Context) {
if (isa<LambdaExpr>(E)) {
// Do not visit the children of lambdas; they have their own CFGs.
@@ -4610,14 +4709,22 @@ CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
CFGBlock *B = Block;
for (Stmt *Child : E->children())
if (Child)
- if (CFGBlock *R = VisitForTemporaryDtors(Child, false, Context))
+ if (CFGBlock *R = VisitForTemporaryDtors(Child, ExternallyDestructed, Context))
B = R;
return B;
}
CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
- BinaryOperator *E, TempDtorContext &Context) {
+ BinaryOperator *E, bool ExternallyDestructed, TempDtorContext &Context) {
+ if (E->isCommaOp()) {
+ // For comma operator LHS expression is visited
+ // before RHS expression. For destructors visit them in reverse order.
+ CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), ExternallyDestructed, Context);
+ CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
+ return LHSBlock ? LHSBlock : RHSBlock;
+ }
+
if (E->isLogicalOp()) {
VisitForTemporaryDtors(E->getLHS(), false, Context);
TryResult RHSExecuted = tryEvaluateBool(E->getLHS());
@@ -4652,10 +4759,11 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
}
CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
- CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
+ CXXBindTemporaryExpr *E, bool ExternallyDestructed, TempDtorContext &Context) {
// First add destructors for temporaries in subexpression.
- CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
- if (!BindToTemporary) {
+ // Because VisitCXXBindTemporaryExpr calls setDestructed:
+ CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), true, Context);
+ if (!ExternallyDestructed) {
// If lifetime of temporary is not prolonged (by assigning to constant
// reference) add destructor for it.
@@ -4703,7 +4811,7 @@ void CFGBuilder::InsertTempDtorDecisionBlock(const TempDtorContext &Context,
}
CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
- AbstractConditionalOperator *E, bool BindToTemporary,
+ AbstractConditionalOperator *E, bool ExternallyDestructed,
TempDtorContext &Context) {
VisitForTemporaryDtors(E->getCond(), false, Context);
CFGBlock *ConditionBlock = Block;
@@ -4714,14 +4822,14 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
TempDtorContext TrueContext(
bothKnownTrue(Context.KnownExecuted, ConditionVal));
- VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary, TrueContext);
+ VisitForTemporaryDtors(E->getTrueExpr(), ExternallyDestructed, TrueContext);
CFGBlock *TrueBlock = Block;
Block = ConditionBlock;
Succ = ConditionSucc;
TempDtorContext FalseContext(
bothKnownTrue(Context.KnownExecuted, NegatedVal));
- VisitForTemporaryDtors(E->getFalseExpr(), BindToTemporary, FalseContext);
+ VisitForTemporaryDtors(E->getFalseExpr(), ExternallyDestructed, FalseContext);
if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
InsertTempDtorDecisionBlock(FalseContext, TrueBlock);
@@ -4755,7 +4863,8 @@ CFGBlock *CFGBuilder::VisitOMPExecutableDirective(OMPExecutableDirective *D,
}
// Visit associated structured block if any.
if (!D->isStandaloneDirective())
- if (Stmt *S = D->getStructuredBlock()) {
+ if (CapturedStmt *CS = D->getInnermostCapturedStmt()) {
+ Stmt *S = CS->getCapturedStmt();
if (!isa<CompoundStmt>(S))
addLocalScopeAndDtors(S);
if (CFGBlock *R = addStmt(S))
@@ -4867,9 +4976,13 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
while (const ArrayType *arrayType = astContext.getAsArrayType(ty)) {
ty = arrayType->getElementType();
}
- const RecordType *recordType = ty->getAs<RecordType>();
- const CXXRecordDecl *classDecl =
- cast<CXXRecordDecl>(recordType->getDecl());
+
+ // The situation when the type of the lifetime-extending reference
+ // does not correspond to the type of the object is supposed
+ // to be handled by now. In particular, 'ty' is now the unwrapped
+ // record type.
+ const CXXRecordDecl *classDecl = ty->getAsCXXRecordDecl();
+ assert(classDecl);
return classDecl->getDestructor();
}
case CFGElement::DeleteDtor: {
@@ -4894,12 +5007,6 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
llvm_unreachable("getKind() returned bogus value");
}
-bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const {
- if (const CXXDestructorDecl *DD = getDestructorDecl(astContext))
- return DD->isNoReturn();
- return false;
-}
-
//===----------------------------------------------------------------------===//
// CFGBlock operations.
//===----------------------------------------------------------------------===//
@@ -4965,6 +5072,8 @@ class StmtPrinterHelper : public PrinterHelper {
public:
StmtPrinterHelper(const CFG* cfg, const LangOptions &LO)
: LangOpts(LO) {
+ if (!cfg)
+ return;
for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I ) {
unsigned j = 1;
for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ;
@@ -5287,9 +5396,21 @@ static void print_construction_context(raw_ostream &OS,
}
static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
+ const CFGElement &E);
+
+void CFGElement::dumpToStream(llvm::raw_ostream &OS) const {
+ StmtPrinterHelper Helper(nullptr, {});
+ print_elem(OS, Helper, *this);
+}
+
+static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const CFGElement &E) {
- if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
- const Stmt *S = CS->getStmt();
+ switch (E.getKind()) {
+ case CFGElement::Kind::Statement:
+ case CFGElement::Kind::CXXRecordTypedCall:
+ case CFGElement::Kind::Constructor: {
+ CFGStmt CS = E.castAs<CFGStmt>();
+ const Stmt *S = CS.getStmt();
assert(S != nullptr && "Expecting non-null Stmt");
// special printing for statement-expressions.
@@ -5341,70 +5462,96 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
// Expressions need a newline.
if (isa<Expr>(S))
OS << '\n';
- } else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
- print_initializer(OS, Helper, IE->getInitializer());
+
+ break;
+ }
+
+ case CFGElement::Kind::Initializer:
+ print_initializer(OS, Helper, E.castAs<CFGInitializer>().getInitializer());
OS << '\n';
- } else if (Optional<CFGAutomaticObjDtor> DE =
- E.getAs<CFGAutomaticObjDtor>()) {
- const VarDecl *VD = DE->getVarDecl();
+ break;
+
+ case CFGElement::Kind::AutomaticObjectDtor: {
+ CFGAutomaticObjDtor DE = E.castAs<CFGAutomaticObjDtor>();
+ const VarDecl *VD = DE.getVarDecl();
Helper.handleDecl(VD, OS);
- ASTContext &ACtx = VD->getASTContext();
QualType T = VD->getType();
if (T->isReferenceType())
T = getReferenceInitTemporaryType(VD->getInit(), nullptr);
- if (const ArrayType *AT = ACtx.getAsArrayType(T))
- T = ACtx.getBaseElementType(AT);
- OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
- OS << " (Implicit destructor)\n";
- } else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) {
- const VarDecl *VD = DE->getVarDecl();
- Helper.handleDecl(VD, OS);
+ OS << ".~";
+ T.getUnqualifiedType().print(OS, PrintingPolicy(Helper.getLangOpts()));
+ OS << "() (Implicit destructor)\n";
+ break;
+ }
+ case CFGElement::Kind::LifetimeEnds:
+ Helper.handleDecl(E.castAs<CFGLifetimeEnds>().getVarDecl(), OS);
OS << " (Lifetime ends)\n";
- } else if (Optional<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) {
- const Stmt *LoopStmt = LE->getLoopStmt();
- OS << LoopStmt->getStmtClassName() << " (LoopExit)\n";
- } else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) {
+ break;
+
+ case CFGElement::Kind::LoopExit:
+ OS << E.castAs<CFGLoopExit>().getLoopStmt()->getStmtClassName() << " (LoopExit)\n";
+ break;
+
+ case CFGElement::Kind::ScopeBegin:
OS << "CFGScopeBegin(";
- if (const VarDecl *VD = SB->getVarDecl())
+ if (const VarDecl *VD = E.castAs<CFGScopeBegin>().getVarDecl())
OS << VD->getQualifiedNameAsString();
OS << ")\n";
- } else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) {
+ break;
+
+ case CFGElement::Kind::ScopeEnd:
OS << "CFGScopeEnd(";
- if (const VarDecl *VD = SE->getVarDecl())
+ if (const VarDecl *VD = E.castAs<CFGScopeEnd>().getVarDecl())
OS << VD->getQualifiedNameAsString();
OS << ")\n";
- } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {
+ break;
+
+ case CFGElement::Kind::NewAllocator:
OS << "CFGNewAllocator(";
- if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())
+ if (const CXXNewExpr *AllocExpr = E.castAs<CFGNewAllocator>().getAllocatorExpr())
AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts()));
OS << ")\n";
- } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) {
- const CXXRecordDecl *RD = DE->getCXXRecordDecl();
+ break;
+
+ case CFGElement::Kind::DeleteDtor: {
+ CFGDeleteDtor DE = E.castAs<CFGDeleteDtor>();
+ const CXXRecordDecl *RD = DE.getCXXRecordDecl();
if (!RD)
return;
CXXDeleteExpr *DelExpr =
- const_cast<CXXDeleteExpr*>(DE->getDeleteExpr());
+ const_cast<CXXDeleteExpr*>(DE.getDeleteExpr());
Helper.handledStmt(cast<Stmt>(DelExpr->getArgument()), OS);
OS << "->~" << RD->getName().str() << "()";
OS << " (Implicit destructor)\n";
- } else if (Optional<CFGBaseDtor> BE = E.getAs<CFGBaseDtor>()) {
- const CXXBaseSpecifier *BS = BE->getBaseSpecifier();
+ break;
+ }
+
+ case CFGElement::Kind::BaseDtor: {
+ const CXXBaseSpecifier *BS = E.castAs<CFGBaseDtor>().getBaseSpecifier();
OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()";
OS << " (Base object destructor)\n";
- } else if (Optional<CFGMemberDtor> ME = E.getAs<CFGMemberDtor>()) {
- const FieldDecl *FD = ME->getFieldDecl();
+ break;
+ }
+
+ case CFGElement::Kind::MemberDtor: {
+ const FieldDecl *FD = E.castAs<CFGMemberDtor>().getFieldDecl();
const Type *T = FD->getType()->getBaseElementTypeUnsafe();
OS << "this->" << FD->getName();
OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()";
OS << " (Member object destructor)\n";
- } else if (Optional<CFGTemporaryDtor> TE = E.getAs<CFGTemporaryDtor>()) {
- const CXXBindTemporaryExpr *BT = TE->getBindTemporaryExpr();
+ break;
+ }
+
+ case CFGElement::Kind::TemporaryDtor: {
+ const CXXBindTemporaryExpr *BT = E.castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
OS << "~";
BT->getType().print(OS, PrintingPolicy(Helper.getLangOpts()));
OS << "() (Temporary object destructor)\n";
+ break;
+ }
}
}
@@ -5615,6 +5762,10 @@ void CFG::print(raw_ostream &OS, const LangOptions &LO, bool ShowColors) const {
OS.flush();
}
+size_t CFGBlock::getIndexInCFG() const {
+ return llvm::find(*getParent(), this) - getParent()->begin();
+}
+
/// dump - A simply pretty printer of a CFGBlock that outputs to stderr.
void CFGBlock::dump(const CFG* cfg, const LangOptions &LO,
bool ShowColors) const {
@@ -5652,6 +5803,71 @@ void CFGBlock::printTerminatorJson(raw_ostream &Out, const LangOptions &LO,
Out << JsonFormat(TempOut.str(), AddQuotes);
}
+// Returns true if by simply looking at the block, we can be sure that it
+// results in a sink during analysis. This is useful to know when the analysis
+// was interrupted, and we try to figure out if it would sink eventually.
+// There may be many more reasons why a sink would appear during analysis
+// (eg. checkers may generate sinks arbitrarily), but here we only consider
+// sinks that would be obvious by looking at the CFG.
+static bool isImmediateSinkBlock(const CFGBlock *Blk) {
+ if (Blk->hasNoReturnElement())
+ return true;
+
+ // FIXME: Throw-expressions are currently generating sinks during analysis:
+ // they're not supported yet, and also often used for actually terminating
+ // the program. So we should treat them as sinks in this analysis as well,
+ // at least for now, but once we have better support for exceptions,
+ // we'd need to carefully handle the case when the throw is being
+ // immediately caught.
+ if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) {
+ if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>())
+ if (isa<CXXThrowExpr>(StmtElm->getStmt()))
+ return true;
+ return false;
+ }))
+ return true;
+
+ return false;
+}
+
+bool CFGBlock::isInevitablySinking() const {
+ const CFG &Cfg = *getParent();
+
+ const CFGBlock *StartBlk = this;
+ if (isImmediateSinkBlock(StartBlk))
+ return true;
+
+ llvm::SmallVector<const CFGBlock *, 32> DFSWorkList;
+ llvm::SmallPtrSet<const CFGBlock *, 32> Visited;
+
+ DFSWorkList.push_back(StartBlk);
+ while (!DFSWorkList.empty()) {
+ const CFGBlock *Blk = DFSWorkList.back();
+ DFSWorkList.pop_back();
+ Visited.insert(Blk);
+
+ // If at least one path reaches the CFG exit, it means that control is
+ // returned to the caller. For now, say that we are not sure what
+ // happens next. If necessary, this can be improved to analyze
+ // the parent StackFrameContext's call site in a similar manner.
+ if (Blk == &Cfg.getExit())
+ return false;
+
+ for (const auto &Succ : Blk->succs()) {
+ if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) {
+ if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) {
+ // If the block has reachable child blocks that aren't no-return,
+ // add them to the worklist.
+ DFSWorkList.push_back(SuccBlk);
+ }
+ }
+ }
+ }
+
+ // Nothing reached the exit. It can only mean one thing: there's no return.
+ return true;
+}
+
const Expr *CFGBlock::getLastCondition() const {
// If the terminator is a temporary dtor or a virtual base, etc, we can't
// retrieve a meaningful condition, bail out.