|
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" |
@@ -401,6 +402,108 @@ static bool isNoexcept(const FunctionDecl *FD) { |
401 | 402 | return false; |
402 | 403 | } |
403 | 404 |
|
| 405 | +/// Checks if the given variable, which is assumed to be a function pointer, is |
| 406 | +/// initialized with a function having 'noreturn' attribute. |
| 407 | +static bool isInitializedWithNoReturn(const VarDecl *VD) { |
| 408 | + if (const Expr *Init = VD->getInit()) { |
| 409 | + if (auto *ListInit = dyn_cast<InitListExpr>(Init); |
| 410 | + ListInit && ListInit->getNumInits() > 0) |
| 411 | + Init = ListInit->getInit(0); |
| 412 | + if (auto *DeclRef = dyn_cast<DeclRefExpr>(Init->IgnoreParenCasts())) |
| 413 | + if (auto *FD = dyn_cast<FunctionDecl>(DeclRef->getDecl())) |
| 414 | + return FD->isNoReturn(); |
| 415 | + } |
| 416 | + return false; |
| 417 | +} |
| 418 | + |
| 419 | +/// Checks if the given expression is a reference to a function with |
| 420 | +/// 'noreturn' attribute. |
| 421 | +static bool isReferenceToNoReturn(const Expr *E) { |
| 422 | + if (auto *DRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) |
| 423 | + if (auto *FD = dyn_cast<FunctionDecl>(DRef->getDecl())) |
| 424 | + return FD->isNoReturn(); |
| 425 | + return false; |
| 426 | +} |
| 427 | + |
| 428 | +namespace { |
| 429 | + |
| 430 | +/// Looks for statements, that can define value of the given variable. |
| 431 | +struct TransferFunctions : public StmtVisitor<TransferFunctions> { |
| 432 | + const VarDecl *Var; |
| 433 | + std::optional<bool> AllValuesAreNoReturn; |
| 434 | + |
| 435 | + TransferFunctions(const VarDecl *VD) : Var(VD) {} |
| 436 | + |
| 437 | + void VisitDeclStmt(DeclStmt *DS) { |
| 438 | + for (auto *DI : DS->decls()) |
| 439 | + if (auto *VD = dyn_cast<VarDecl>(DI)) |
| 440 | + if (VarDecl *Def = VD->getDefinition()) |
| 441 | + if (Def == Var) |
| 442 | + AllValuesAreNoReturn = isInitializedWithNoReturn(Def); |
| 443 | + } |
| 444 | + |
| 445 | + void VisitUnaryOperator(UnaryOperator *UO) { |
| 446 | + if (UO->getOpcode() == UO_AddrOf) { |
| 447 | + if (auto *DRef = |
| 448 | + dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParenCasts())) |
| 449 | + if (DRef->getDecl() == Var) |
| 450 | + AllValuesAreNoReturn = false; |
| 451 | + } |
| 452 | + } |
| 453 | + |
| 454 | + void VisitBinaryOperator(BinaryOperator *BO) { |
| 455 | + if (BO->getOpcode() == BO_Assign) |
| 456 | + if (auto *DRef = dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParenCasts())) |
| 457 | + if (DRef->getDecl() == Var) |
| 458 | + AllValuesAreNoReturn = isReferenceToNoReturn(BO->getRHS()); |
| 459 | + } |
| 460 | + |
| 461 | + void VisitCallExpr(CallExpr *CE) { |
| 462 | + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; |
| 463 | + ++I) { |
| 464 | + const Expr *Arg = *I; |
| 465 | + if (Arg->isGLValue() && !Arg->getType().isConstQualified()) |
| 466 | + if (auto *DRef = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts())) |
| 467 | + if (auto VD = dyn_cast<VarDecl>(DRef->getDecl())) |
| 468 | + if (VD->getDefinition() == Var) |
| 469 | + AllValuesAreNoReturn = false; |
| 470 | + } |
| 471 | + } |
| 472 | +}; |
| 473 | +} // namespace |
| 474 | + |
| 475 | +// Checks if all possible values of the given variable are functions with |
| 476 | +// 'noreturn' attribute. |
| 477 | +static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk, |
| 478 | + AnalysisDeclContext &AC) { |
| 479 | + // The set of possible values of a constant variable is determined by |
| 480 | + // its initializer. |
| 481 | + if (VD->getType().isConstant(AC.getASTContext())) { |
| 482 | + if (const VarDecl *Def = VD->getDefinition()) |
| 483 | + return isInitializedWithNoReturn(Def); |
| 484 | + return false; |
| 485 | + } |
| 486 | + |
| 487 | + // Scan function statements for definitions of the given variable. |
| 488 | + TransferFunctions TF(VD); |
| 489 | + BackwardDataflowWorklist Worklist(*AC.getCFG(), AC); |
| 490 | + Worklist.enqueueBlock(&VarBlk); |
| 491 | + while (const CFGBlock *B = Worklist.dequeue()) { |
| 492 | + for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend(); |
| 493 | + ri != re; ++ri) { |
| 494 | + if (std::optional<CFGStmt> cs = ri->getAs<CFGStmt>()) { |
| 495 | + const Stmt *S = cs->getStmt(); |
| 496 | + TF.Visit(const_cast<Stmt *>(S)); |
| 497 | + if (TF.AllValuesAreNoReturn) |
| 498 | + return *TF.AllValuesAreNoReturn; |
| 499 | + } |
| 500 | + } |
| 501 | + Worklist.enqueuePredecessors(B); |
| 502 | + } |
| 503 | + |
| 504 | + return false; |
| 505 | +} |
| 506 | + |
404 | 507 | //===----------------------------------------------------------------------===// |
405 | 508 | // Check for missing return value. |
406 | 509 | //===----------------------------------------------------------------------===// |
@@ -527,6 +630,17 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { |
527 | 630 | HasAbnormalEdge = true; |
528 | 631 | continue; |
529 | 632 | } |
| 633 | + if (auto *Call = dyn_cast<CallExpr>(S)) { |
| 634 | + const Expr *Callee = Call->getCallee(); |
| 635 | + if (Callee->getType()->isPointerType()) |
| 636 | + if (auto *DeclRef = |
| 637 | + dyn_cast<DeclRefExpr>(Callee->IgnoreParenImpCasts())) |
| 638 | + if (auto *VD = dyn_cast<VarDecl>(DeclRef->getDecl())) |
| 639 | + if (areAllValuesNoReturn(VD, B, AC)) { |
| 640 | + HasAbnormalEdge = true; |
| 641 | + continue; |
| 642 | + } |
| 643 | + } |
530 | 644 |
|
531 | 645 | HasPlainEdge = true; |
532 | 646 | } |
|
0 commit comments