DynamicTypeChecker.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This checker looks for cases where the dynamic type of an object is unrelated
  10. // to its static type. The type information utilized by this check is collected
  11. // by the DynamicTypePropagation checker. This check does not report any type
  12. // error for ObjC Generic types, in order to avoid duplicate erros from the
  13. // ObjC Generics checker. This checker is not supposed to modify the program
  14. // state, it is just the observer of the type information provided by other
  15. // checkers.
  16. //
  17. //===----------------------------------------------------------------------===//
  18. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  23. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
  24. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  25. #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
  26. using namespace clang;
  27. using namespace ento;
  28. namespace {
  29. class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
  30. mutable std::unique_ptr<BugType> BT;
  31. void initBugType() const {
  32. if (!BT)
  33. BT.reset(
  34. new BugType(this, "Dynamic and static type mismatch", "Type Error"));
  35. }
  36. class DynamicTypeBugVisitor : public BugReporterVisitor {
  37. public:
  38. DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
  39. void Profile(llvm::FoldingSetNodeID &ID) const override {
  40. static int X = 0;
  41. ID.AddPointer(&X);
  42. ID.AddPointer(Reg);
  43. }
  44. PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
  45. BugReporterContext &BRC,
  46. PathSensitiveBugReport &BR) override;
  47. private:
  48. // The tracked region.
  49. const MemRegion *Reg;
  50. };
  51. void reportTypeError(QualType DynamicType, QualType StaticType,
  52. const MemRegion *Reg, const Stmt *ReportedNode,
  53. CheckerContext &C) const;
  54. public:
  55. void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
  56. };
  57. }
  58. void DynamicTypeChecker::reportTypeError(QualType DynamicType,
  59. QualType StaticType,
  60. const MemRegion *Reg,
  61. const Stmt *ReportedNode,
  62. CheckerContext &C) const {
  63. initBugType();
  64. SmallString<192> Buf;
  65. llvm::raw_svector_ostream OS(Buf);
  66. OS << "Object has a dynamic type '";
  67. QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
  68. llvm::Twine());
  69. OS << "' which is incompatible with static type '";
  70. QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
  71. llvm::Twine());
  72. OS << "'";
  73. auto R = std::make_unique<PathSensitiveBugReport>(
  74. *BT, OS.str(), C.generateNonFatalErrorNode());
  75. R->markInteresting(Reg);
  76. R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
  77. R->addRange(ReportedNode->getSourceRange());
  78. C.emitReport(std::move(R));
  79. }
  80. PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
  81. const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
  82. ProgramStateRef State = N->getState();
  83. ProgramStateRef StatePrev = N->getFirstPred()->getState();
  84. DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
  85. DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
  86. if (!TrackedType.isValid())
  87. return nullptr;
  88. if (TrackedTypePrev.isValid() &&
  89. TrackedTypePrev.getType() == TrackedType.getType())
  90. return nullptr;
  91. // Retrieve the associated statement.
  92. const Stmt *S = N->getStmtForDiagnostics();
  93. if (!S)
  94. return nullptr;
  95. const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
  96. SmallString<256> Buf;
  97. llvm::raw_svector_ostream OS(Buf);
  98. OS << "Type '";
  99. QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
  100. LangOpts, llvm::Twine());
  101. OS << "' is inferred from ";
  102. if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
  103. OS << "explicit cast (from '";
  104. QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
  105. Qualifiers(), OS, LangOpts, llvm::Twine());
  106. OS << "' to '";
  107. QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
  108. LangOpts, llvm::Twine());
  109. OS << "')";
  110. } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
  111. OS << "implicit cast (from '";
  112. QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
  113. Qualifiers(), OS, LangOpts, llvm::Twine());
  114. OS << "' to '";
  115. QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
  116. LangOpts, llvm::Twine());
  117. OS << "')";
  118. } else {
  119. OS << "this context";
  120. }
  121. // Generate the extra diagnostic.
  122. PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
  123. N->getLocationContext());
  124. return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
  125. }
  126. static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
  127. const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
  128. if (!Decl)
  129. return false;
  130. return Decl->getDefinition();
  131. }
  132. // TODO: consider checking explicit casts?
  133. void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
  134. CheckerContext &C) const {
  135. // TODO: C++ support.
  136. if (CE->getCastKind() != CK_BitCast)
  137. return;
  138. const MemRegion *Region = C.getSVal(CE).getAsRegion();
  139. if (!Region)
  140. return;
  141. ProgramStateRef State = C.getState();
  142. DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
  143. if (!DynTypeInfo.isValid())
  144. return;
  145. QualType DynType = DynTypeInfo.getType();
  146. QualType StaticType = CE->getType();
  147. const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
  148. const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
  149. if (!DynObjCType || !StaticObjCType)
  150. return;
  151. if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
  152. return;
  153. ASTContext &ASTCtxt = C.getASTContext();
  154. // Strip kindeofness to correctly detect subtyping relationships.
  155. DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
  156. StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
  157. // Specialized objects are handled by the generics checker.
  158. if (StaticObjCType->isSpecialized())
  159. return;
  160. if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
  161. return;
  162. if (DynTypeInfo.canBeASubClass() &&
  163. ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
  164. return;
  165. reportTypeError(DynType, StaticType, Region, CE, C);
  166. }
  167. void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
  168. mgr.registerChecker<DynamicTypeChecker>();
  169. }
  170. bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) {
  171. return true;
  172. }