|
37 | 37 | #include "clang/Analysis/AnalysisDeclContext.h"
|
38 | 38 | #include "clang/Analysis/CFG.h"
|
39 | 39 | #include "clang/Analysis/CFGStmtMap.h"
|
| 40 | +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" |
40 | 41 | #include "clang/Basic/Diagnostic.h"
|
41 | 42 | #include "clang/Basic/DiagnosticSema.h"
|
42 | 43 | #include "clang/Basic/SourceLocation.h"
|
|
46 | 47 | #include "clang/Sema/SemaInternal.h"
|
47 | 48 | #include "llvm/ADT/ArrayRef.h"
|
48 | 49 | #include "llvm/ADT/BitVector.h"
|
| 50 | +#include "llvm/ADT/DenseMap.h" |
49 | 51 | #include "llvm/ADT/MapVector.h"
|
50 | 52 | #include "llvm/ADT/STLFunctionalExtras.h"
|
51 | 53 | #include "llvm/ADT/SmallVector.h"
|
@@ -401,6 +403,143 @@ static bool isNoexcept(const FunctionDecl *FD) {
|
401 | 403 | return false;
|
402 | 404 | }
|
403 | 405 |
|
| 406 | +/// Checks if the given expression is a reference to a function with |
| 407 | +/// 'noreturn' attribute. |
| 408 | +static bool isReferenceToNoReturn(const Expr *E) { |
| 409 | + if (auto *DRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) |
| 410 | + if (auto *FD = dyn_cast<FunctionDecl>(DRef->getDecl())) |
| 411 | + return FD->isNoReturn(); |
| 412 | + return false; |
| 413 | +} |
| 414 | + |
| 415 | +/// Checks if the given variable, which is assumed to be a function pointer, is |
| 416 | +/// initialized with a function having 'noreturn' attribute. |
| 417 | +static bool isInitializedWithNoReturn(const VarDecl *VD) { |
| 418 | + if (const Expr *Init = VD->getInit()) { |
| 419 | + if (auto *ListInit = dyn_cast<InitListExpr>(Init); |
| 420 | + ListInit && ListInit->getNumInits() > 0) |
| 421 | + Init = ListInit->getInit(0); |
| 422 | + return isReferenceToNoReturn(Init); |
| 423 | + } |
| 424 | + return false; |
| 425 | +} |
| 426 | + |
| 427 | +namespace { |
| 428 | + |
| 429 | +/// Looks for statements, that can define value of the given variable. |
| 430 | +struct TransferFunctions : public StmtVisitor<TransferFunctions> { |
| 431 | + const VarDecl *Var; |
| 432 | + std::optional<bool> AllValuesAreNoReturn; |
| 433 | + |
| 434 | + TransferFunctions(const VarDecl *VD) : Var(VD) {} |
| 435 | + |
| 436 | + void reset() { AllValuesAreNoReturn = std::nullopt; } |
| 437 | + |
| 438 | + void VisitDeclStmt(DeclStmt *DS) { |
| 439 | + for (auto *DI : DS->decls()) |
| 440 | + if (auto *VD = dyn_cast<VarDecl>(DI)) |
| 441 | + if (VarDecl *Def = VD->getDefinition()) |
| 442 | + if (Def == Var) |
| 443 | + AllValuesAreNoReturn = isInitializedWithNoReturn(Def); |
| 444 | + } |
| 445 | + |
| 446 | + void VisitUnaryOperator(UnaryOperator *UO) { |
| 447 | + if (UO->getOpcode() == UO_AddrOf) { |
| 448 | + if (auto *DRef = |
| 449 | + dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParenCasts())) |
| 450 | + if (DRef->getDecl() == Var) |
| 451 | + AllValuesAreNoReturn = false; |
| 452 | + } |
| 453 | + } |
| 454 | + |
| 455 | + void VisitBinaryOperator(BinaryOperator *BO) { |
| 456 | + if (BO->getOpcode() == BO_Assign) |
| 457 | + if (auto *DRef = dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParenCasts())) |
| 458 | + if (DRef->getDecl() == Var) |
| 459 | + AllValuesAreNoReturn = isReferenceToNoReturn(BO->getRHS()); |
| 460 | + } |
| 461 | + |
| 462 | + void VisitCallExpr(CallExpr *CE) { |
| 463 | + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; |
| 464 | + ++I) { |
| 465 | + const Expr *Arg = *I; |
| 466 | + if (Arg->isGLValue() && !Arg->getType().isConstQualified()) |
| 467 | + if (auto *DRef = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts())) |
| 468 | + if (auto VD = dyn_cast<VarDecl>(DRef->getDecl())) |
| 469 | + if (VD->getDefinition() == Var) |
| 470 | + AllValuesAreNoReturn = false; |
| 471 | + } |
| 472 | + } |
| 473 | +}; |
| 474 | +} // namespace |
| 475 | + |
| 476 | +// Checks if all possible values of the given variable are functions with |
| 477 | +// 'noreturn' attribute. |
| 478 | +static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk, |
| 479 | + AnalysisDeclContext &AC) { |
| 480 | + // The set of possible values of a constant variable is determined by |
| 481 | + // its initializer, unless it is a function parameter. |
| 482 | + if (!isa<ParmVarDecl>(VD) && VD->getType().isConstant(AC.getASTContext())) { |
| 483 | + if (const VarDecl *Def = VD->getDefinition()) |
| 484 | + return isInitializedWithNoReturn(Def); |
| 485 | + return false; |
| 486 | + } |
| 487 | + |
| 488 | + // In multithreaded environment the value of a global variable may be changed |
| 489 | + // asynchronously. |
| 490 | + if (!VD->getDeclContext()->isFunctionOrMethod()) |
| 491 | + return false; |
| 492 | + |
| 493 | + // Check the condition "all values are noreturn". It is satisfied if the |
| 494 | + // variable is set to "noreturn" value in the current block or all its |
| 495 | + // predecessors satisfies the condition. |
| 496 | + using MapTy = llvm::DenseMap<const CFGBlock *, std::optional<bool>>; |
| 497 | + using ValueTy = MapTy::value_type; |
| 498 | + MapTy BlocksToCheck; |
| 499 | + BlocksToCheck[&VarBlk] = std::nullopt; |
| 500 | + const auto BlockSatisfiesCondition = [](ValueTy Item) { |
| 501 | + return Item.getSecond().value_or(false); |
| 502 | + }; |
| 503 | + |
| 504 | + TransferFunctions TF(VD); |
| 505 | + BackwardDataflowWorklist Worklist(*AC.getCFG(), AC); |
| 506 | + Worklist.enqueueBlock(&VarBlk); |
| 507 | + while (const CFGBlock *B = Worklist.dequeue()) { |
| 508 | + // First check the current block. |
| 509 | + for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend(); |
| 510 | + ri != re; ++ri) { |
| 511 | + if (std::optional<CFGStmt> cs = ri->getAs<CFGStmt>()) { |
| 512 | + const Stmt *S = cs->getStmt(); |
| 513 | + TF.reset(); |
| 514 | + TF.Visit(const_cast<Stmt *>(S)); |
| 515 | + if (TF.AllValuesAreNoReturn) { |
| 516 | + if (!TF.AllValuesAreNoReturn.value()) |
| 517 | + return false; |
| 518 | + BlocksToCheck[B] = true; |
| 519 | + break; |
| 520 | + } |
| 521 | + } |
| 522 | + } |
| 523 | + |
| 524 | + // If all checked blocks satisfy the condition, the check is finished. |
| 525 | + if (std::all_of(BlocksToCheck.begin(), BlocksToCheck.end(), |
| 526 | + BlockSatisfiesCondition)) |
| 527 | + return true; |
| 528 | + |
| 529 | + // If this block does not contain the variable definition, check |
| 530 | + // its predecessors. |
| 531 | + if (!BlocksToCheck[B]) { |
| 532 | + Worklist.enqueuePredecessors(B); |
| 533 | + BlocksToCheck.erase(B); |
| 534 | + for (const auto &PredBlk : B->preds()) |
| 535 | + if (!BlocksToCheck.contains(PredBlk)) |
| 536 | + BlocksToCheck[PredBlk] = std::nullopt; |
| 537 | + } |
| 538 | + } |
| 539 | + |
| 540 | + return false; |
| 541 | +} |
| 542 | + |
404 | 543 | //===----------------------------------------------------------------------===//
|
405 | 544 | // Check for missing return value.
|
406 | 545 | //===----------------------------------------------------------------------===//
|
@@ -527,6 +666,17 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
|
527 | 666 | HasAbnormalEdge = true;
|
528 | 667 | continue;
|
529 | 668 | }
|
| 669 | + if (auto *Call = dyn_cast<CallExpr>(S)) { |
| 670 | + const Expr *Callee = Call->getCallee(); |
| 671 | + if (Callee->getType()->isPointerType()) |
| 672 | + if (auto *DeclRef = |
| 673 | + dyn_cast<DeclRefExpr>(Callee->IgnoreParenImpCasts())) |
| 674 | + if (auto *VD = dyn_cast<VarDecl>(DeclRef->getDecl())) |
| 675 | + if (areAllValuesNoReturn(VD, B, AC)) { |
| 676 | + HasAbnormalEdge = true; |
| 677 | + continue; |
| 678 | + } |
| 679 | + } |
530 | 680 |
|
531 | 681 | HasPlainEdge = true;
|
532 | 682 | }
|
|
0 commit comments