ExprInspectionChecker.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "ClangSACheckers.h"
  10. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  11. #include "clang/StaticAnalyzer/Core/Checker.h"
  12. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  13. #include "llvm/ADT/StringSwitch.h"
  14. using namespace clang;
  15. using namespace ento;
  16. namespace {
  17. class ExprInspectionChecker : public Checker< eval::Call > {
  18. mutable std::unique_ptr<BugType> BT;
  19. void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
  20. void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
  21. void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
  22. void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
  23. typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
  24. CheckerContext &C) const;
  25. public:
  26. bool evalCall(const CallExpr *CE, CheckerContext &C) const;
  27. };
  28. }
  29. bool ExprInspectionChecker::evalCall(const CallExpr *CE,
  30. CheckerContext &C) const {
  31. // These checks should have no effect on the surrounding environment
  32. // (globals should not be invalidated, etc), hence the use of evalCall.
  33. FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
  34. .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
  35. .Case("clang_analyzer_checkInlined",
  36. &ExprInspectionChecker::analyzerCheckInlined)
  37. .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
  38. .Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached)
  39. .Default(nullptr);
  40. if (!Handler)
  41. return false;
  42. (this->*Handler)(CE, C);
  43. return true;
  44. }
  45. static const char *getArgumentValueString(const CallExpr *CE,
  46. CheckerContext &C) {
  47. if (CE->getNumArgs() == 0)
  48. return "Missing assertion argument";
  49. ExplodedNode *N = C.getPredecessor();
  50. const LocationContext *LC = N->getLocationContext();
  51. ProgramStateRef State = N->getState();
  52. const Expr *Assertion = CE->getArg(0);
  53. SVal AssertionVal = State->getSVal(Assertion, LC);
  54. if (AssertionVal.isUndef())
  55. return "UNDEFINED";
  56. ProgramStateRef StTrue, StFalse;
  57. std::tie(StTrue, StFalse) =
  58. State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
  59. if (StTrue) {
  60. if (StFalse)
  61. return "UNKNOWN";
  62. else
  63. return "TRUE";
  64. } else {
  65. if (StFalse)
  66. return "FALSE";
  67. else
  68. llvm_unreachable("Invalid constraint; neither true or false.");
  69. }
  70. }
  71. void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
  72. CheckerContext &C) const {
  73. ExplodedNode *N = C.getPredecessor();
  74. const LocationContext *LC = N->getLocationContext();
  75. // A specific instantiation of an inlined function may have more constrained
  76. // values than can generally be assumed. Skip the check.
  77. if (LC->getCurrentStackFrame()->getParent() != nullptr)
  78. return;
  79. if (!BT)
  80. BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
  81. C.emitReport(
  82. llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
  83. }
  84. void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
  85. CheckerContext &C) const {
  86. ExplodedNode *N = C.getPredecessor();
  87. if (!BT)
  88. BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
  89. C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N));
  90. }
  91. void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
  92. CheckerContext &C) const {
  93. ExplodedNode *N = C.getPredecessor();
  94. const LocationContext *LC = N->getLocationContext();
  95. // An inlined function could conceivably also be analyzed as a top-level
  96. // function. We ignore this case and only emit a message (TRUE or FALSE)
  97. // when we are analyzing it as an inlined function. This means that
  98. // clang_analyzer_checkInlined(true) should always print TRUE, but
  99. // clang_analyzer_checkInlined(false) should never actually print anything.
  100. if (LC->getCurrentStackFrame()->getParent() == nullptr)
  101. return;
  102. if (!BT)
  103. BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
  104. C.emitReport(
  105. llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
  106. }
  107. void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
  108. CheckerContext &C) const {
  109. LLVM_BUILTIN_TRAP;
  110. }
  111. void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
  112. Mgr.registerChecker<ExprInspectionChecker>();
  113. }