xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This defines CastValueChecker which models casts of custom RTTIs.
100b57cec5SDimitry Andric //
11a7dea167SDimitry Andric // TODO list:
12a7dea167SDimitry Andric // - It only allows one succesful cast between two types however in the wild
13a7dea167SDimitry Andric //   the object could be casted to multiple types.
14a7dea167SDimitry Andric // - It needs to check the most likely type information from the dynamic type
15a7dea167SDimitry Andric //   map to increase precision of dynamic casting.
16a7dea167SDimitry Andric //
170b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
180b57cec5SDimitry Andric 
19a7dea167SDimitry Andric #include "clang/AST/DeclTemplate.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
23349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26a7dea167SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
27bdd1243dSDimitry Andric #include <optional>
28a7dea167SDimitry Andric #include <utility>
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric using namespace clang;
310b57cec5SDimitry Andric using namespace ento;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric namespace {
345ffd83dbSDimitry Andric class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
35a7dea167SDimitry Andric   enum class CallKind { Function, Method, InstanceOf };
36a7dea167SDimitry Andric 
370b57cec5SDimitry Andric   using CastCheck =
38a7dea167SDimitry Andric       std::function<void(const CastValueChecker *, const CallEvent &Call,
390b57cec5SDimitry Andric                          DefinedOrUnknownSVal, CheckerContext &)>;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric public:
42a7dea167SDimitry Andric   // We have five cases to evaluate a cast:
43a7dea167SDimitry Andric   // 1) The parameter is non-null, the return value is non-null.
44a7dea167SDimitry Andric   // 2) The parameter is non-null, the return value is null.
45a7dea167SDimitry Andric   // 3) The parameter is null, the return value is null.
460b57cec5SDimitry Andric   // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
47a7dea167SDimitry Andric   //
48a7dea167SDimitry Andric   // 4) castAs: Has no parameter, the return value is non-null.
49a7dea167SDimitry Andric   // 5) getAs:  Has no parameter, the return value is null or non-null.
50a7dea167SDimitry Andric   //
51a7dea167SDimitry Andric   // We have two cases to check the parameter is an instance of the given type.
52a7dea167SDimitry Andric   // 1) isa:             The parameter is non-null, returns boolean.
53a7dea167SDimitry Andric   // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
540b57cec5SDimitry Andric   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
555ffd83dbSDimitry Andric   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric private:
58a7dea167SDimitry Andric   // These are known in the LLVM project. The pairs are in the following form:
59*0fca6ea1SDimitry Andric   // {{match-mode, {namespace, call}, argument-count}, {callback, kind}}
60a7dea167SDimitry Andric   const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
61*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "cast"}, 1},
62a7dea167SDimitry Andric        {&CastValueChecker::evalCast, CallKind::Function}},
63*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "dyn_cast"}, 1},
64a7dea167SDimitry Andric        {&CastValueChecker::evalDynCast, CallKind::Function}},
65*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "cast_or_null"}, 1},
66a7dea167SDimitry Andric        {&CastValueChecker::evalCastOrNull, CallKind::Function}},
67*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "dyn_cast_or_null"}, 1},
68a7dea167SDimitry Andric        {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
69*0fca6ea1SDimitry Andric       {{CDM::CXXMethod, {"clang", "castAs"}, 0},
70a7dea167SDimitry Andric        {&CastValueChecker::evalCastAs, CallKind::Method}},
71*0fca6ea1SDimitry Andric       {{CDM::CXXMethod, {"clang", "getAs"}, 0},
72a7dea167SDimitry Andric        {&CastValueChecker::evalGetAs, CallKind::Method}},
73*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "isa"}, 1},
74a7dea167SDimitry Andric        {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
75*0fca6ea1SDimitry Andric       {{CDM::SimpleFunc, {"llvm", "isa_and_nonnull"}, 1},
76a7dea167SDimitry Andric        {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
770b57cec5SDimitry Andric 
78a7dea167SDimitry Andric   void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
790b57cec5SDimitry Andric                 CheckerContext &C) const;
80a7dea167SDimitry Andric   void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
810b57cec5SDimitry Andric                    CheckerContext &C) const;
82a7dea167SDimitry Andric   void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
830b57cec5SDimitry Andric                       CheckerContext &C) const;
84a7dea167SDimitry Andric   void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
85a7dea167SDimitry Andric                          CheckerContext &C) const;
86a7dea167SDimitry Andric   void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
87a7dea167SDimitry Andric                   CheckerContext &C) const;
88a7dea167SDimitry Andric   void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
89a7dea167SDimitry Andric                  CheckerContext &C) const;
90a7dea167SDimitry Andric   void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
91a7dea167SDimitry Andric                CheckerContext &C) const;
92a7dea167SDimitry Andric   void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
930b57cec5SDimitry Andric                          CheckerContext &C) const;
940b57cec5SDimitry Andric };
950b57cec5SDimitry Andric } // namespace
960b57cec5SDimitry Andric 
isInfeasibleCast(const DynamicCastInfo * CastInfo,bool CastSucceeds)97a7dea167SDimitry Andric static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
98a7dea167SDimitry Andric                              bool CastSucceeds) {
99a7dea167SDimitry Andric   if (!CastInfo)
100a7dea167SDimitry Andric     return false;
101a7dea167SDimitry Andric 
102a7dea167SDimitry Andric   return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric 
getNoteTag(CheckerContext & C,const DynamicCastInfo * CastInfo,QualType CastToTy,const Expr * Object,bool CastSucceeds,bool IsKnownCast)105a7dea167SDimitry Andric static const NoteTag *getNoteTag(CheckerContext &C,
106a7dea167SDimitry Andric                                  const DynamicCastInfo *CastInfo,
107a7dea167SDimitry Andric                                  QualType CastToTy, const Expr *Object,
108a7dea167SDimitry Andric                                  bool CastSucceeds, bool IsKnownCast) {
109a7dea167SDimitry Andric   std::string CastToName =
11016d6b3b3SDimitry Andric       CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
11156f451bbSDimitry Andric                : CastToTy.getAsString();
112a7dea167SDimitry Andric   Object = Object->IgnoreParenImpCasts();
1130b57cec5SDimitry Andric 
114a7dea167SDimitry Andric   return C.getNoteTag(
115a7dea167SDimitry Andric       [=]() -> std::string {
1160b57cec5SDimitry Andric         SmallString<128> Msg;
1170b57cec5SDimitry Andric         llvm::raw_svector_ostream Out(Msg);
1180b57cec5SDimitry Andric 
119a7dea167SDimitry Andric         if (!IsKnownCast)
120a7dea167SDimitry Andric           Out << "Assuming ";
121a7dea167SDimitry Andric 
122a7dea167SDimitry Andric         if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
123e8d8bef9SDimitry Andric           Out << '\'' << DRE->getDecl()->getDeclName() << '\'';
124a7dea167SDimitry Andric         } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
125a7dea167SDimitry Andric           Out << (IsKnownCast ? "Field '" : "field '")
126e8d8bef9SDimitry Andric               << ME->getMemberDecl()->getDeclName() << '\'';
127a7dea167SDimitry Andric         } else {
128a7dea167SDimitry Andric           Out << (IsKnownCast ? "The object" : "the object");
129a7dea167SDimitry Andric         }
130a7dea167SDimitry Andric 
131a7dea167SDimitry Andric         Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
132a7dea167SDimitry Andric             << '\'';
133a7dea167SDimitry Andric 
1345ffd83dbSDimitry Andric         return std::string(Out.str());
1350b57cec5SDimitry Andric       },
1360b57cec5SDimitry Andric       /*IsPrunable=*/true);
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric 
getNoteTag(CheckerContext & C,SmallVector<QualType,4> CastToTyVec,const Expr * Object,bool IsKnownCast)13916d6b3b3SDimitry Andric static const NoteTag *getNoteTag(CheckerContext &C,
14016d6b3b3SDimitry Andric                                  SmallVector<QualType, 4> CastToTyVec,
14116d6b3b3SDimitry Andric                                  const Expr *Object,
14216d6b3b3SDimitry Andric                                  bool IsKnownCast) {
14316d6b3b3SDimitry Andric   Object = Object->IgnoreParenImpCasts();
14416d6b3b3SDimitry Andric 
14516d6b3b3SDimitry Andric   return C.getNoteTag(
14616d6b3b3SDimitry Andric       [=]() -> std::string {
14716d6b3b3SDimitry Andric         SmallString<128> Msg;
14816d6b3b3SDimitry Andric         llvm::raw_svector_ostream Out(Msg);
14916d6b3b3SDimitry Andric 
15016d6b3b3SDimitry Andric         if (!IsKnownCast)
15116d6b3b3SDimitry Andric           Out << "Assuming ";
15216d6b3b3SDimitry Andric 
15316d6b3b3SDimitry Andric         if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
15416d6b3b3SDimitry Andric           Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
15516d6b3b3SDimitry Andric         } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
15616d6b3b3SDimitry Andric           Out << (IsKnownCast ? "Field '" : "field '")
15716d6b3b3SDimitry Andric               << ME->getMemberDecl()->getNameAsString() << '\'';
15816d6b3b3SDimitry Andric         } else {
15916d6b3b3SDimitry Andric           Out << (IsKnownCast ? "The object" : "the object");
16016d6b3b3SDimitry Andric         }
16116d6b3b3SDimitry Andric         Out << " is";
16216d6b3b3SDimitry Andric 
16316d6b3b3SDimitry Andric         bool First = true;
16416d6b3b3SDimitry Andric         for (QualType CastToTy: CastToTyVec) {
16516d6b3b3SDimitry Andric           std::string CastToName =
16656f451bbSDimitry Andric               CastToTy->getAsCXXRecordDecl()
16756f451bbSDimitry Andric                   ? CastToTy->getAsCXXRecordDecl()->getNameAsString()
16856f451bbSDimitry Andric                   : CastToTy.getAsString();
16916d6b3b3SDimitry Andric           Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :
17016d6b3b3SDimitry Andric                          (First ? "neither" : "nor")) << " a '" << CastToName
17116d6b3b3SDimitry Andric               << '\'';
17216d6b3b3SDimitry Andric           First = false;
17316d6b3b3SDimitry Andric         }
17416d6b3b3SDimitry Andric 
17516d6b3b3SDimitry Andric         return std::string(Out.str());
17616d6b3b3SDimitry Andric       },
17716d6b3b3SDimitry Andric       /*IsPrunable=*/true);
17816d6b3b3SDimitry Andric }
17916d6b3b3SDimitry Andric 
180a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
181a7dea167SDimitry Andric // Main logic to evaluate a cast.
182a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
183a7dea167SDimitry Andric 
alignReferenceTypes(QualType toAlign,QualType alignTowards,ASTContext & ACtx)184a7dea167SDimitry Andric static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
185a7dea167SDimitry Andric                                     ASTContext &ACtx) {
186a7dea167SDimitry Andric   if (alignTowards->isLValueReferenceType() &&
187a7dea167SDimitry Andric       alignTowards.isConstQualified()) {
188a7dea167SDimitry Andric     toAlign.addConst();
189a7dea167SDimitry Andric     return ACtx.getLValueReferenceType(toAlign);
190a7dea167SDimitry Andric   } else if (alignTowards->isLValueReferenceType())
191a7dea167SDimitry Andric     return ACtx.getLValueReferenceType(toAlign);
192a7dea167SDimitry Andric   else if (alignTowards->isRValueReferenceType())
193a7dea167SDimitry Andric     return ACtx.getRValueReferenceType(toAlign);
194a7dea167SDimitry Andric 
195a7dea167SDimitry Andric   llvm_unreachable("Must align towards a reference type!");
196a7dea167SDimitry Andric }
197a7dea167SDimitry Andric 
addCastTransition(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsNonNullParam,bool IsNonNullReturn,bool IsCheckedCast=false)198a7dea167SDimitry Andric static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
199a7dea167SDimitry Andric                               CheckerContext &C, bool IsNonNullParam,
200a7dea167SDimitry Andric                               bool IsNonNullReturn,
201a7dea167SDimitry Andric                               bool IsCheckedCast = false) {
202a7dea167SDimitry Andric   ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
2030b57cec5SDimitry Andric   if (!State)
2040b57cec5SDimitry Andric     return;
2050b57cec5SDimitry Andric 
206a7dea167SDimitry Andric   const Expr *Object;
207a7dea167SDimitry Andric   QualType CastFromTy;
208a7dea167SDimitry Andric   QualType CastToTy = Call.getResultType();
2090b57cec5SDimitry Andric 
210a7dea167SDimitry Andric   if (Call.getNumArgs() > 0) {
211a7dea167SDimitry Andric     Object = Call.getArgExpr(0);
212a7dea167SDimitry Andric     CastFromTy = Call.parameters()[0]->getType();
213a7dea167SDimitry Andric   } else {
214a7dea167SDimitry Andric     Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
215a7dea167SDimitry Andric     CastFromTy = Object->getType();
216a7dea167SDimitry Andric     if (CastToTy->isPointerType()) {
217a7dea167SDimitry Andric       if (!CastFromTy->isPointerType())
218a7dea167SDimitry Andric         return;
219a7dea167SDimitry Andric     } else {
220a7dea167SDimitry Andric       if (!CastFromTy->isReferenceType())
2210b57cec5SDimitry Andric         return;
2220b57cec5SDimitry Andric 
223a7dea167SDimitry Andric       CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
224a7dea167SDimitry Andric     }
225a7dea167SDimitry Andric   }
2260b57cec5SDimitry Andric 
227a7dea167SDimitry Andric   const MemRegion *MR = DV.getAsRegion();
228a7dea167SDimitry Andric   const DynamicCastInfo *CastInfo =
229a7dea167SDimitry Andric       getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
230a7dea167SDimitry Andric 
231a7dea167SDimitry Andric   // We assume that every checked cast succeeds.
232a7dea167SDimitry Andric   bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
233a7dea167SDimitry Andric   if (!CastSucceeds) {
234a7dea167SDimitry Andric     if (CastInfo)
235a7dea167SDimitry Andric       CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
236a7dea167SDimitry Andric     else
237a7dea167SDimitry Andric       CastSucceeds = IsNonNullReturn;
238a7dea167SDimitry Andric   }
239a7dea167SDimitry Andric 
240a7dea167SDimitry Andric   // Check for infeasible casts.
241a7dea167SDimitry Andric   if (isInfeasibleCast(CastInfo, CastSucceeds)) {
242a7dea167SDimitry Andric     C.generateSink(State, C.getPredecessor());
243a7dea167SDimitry Andric     return;
244a7dea167SDimitry Andric   }
245a7dea167SDimitry Andric 
246a7dea167SDimitry Andric   // Store the type and the cast information.
247a7dea167SDimitry Andric   bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
248a7dea167SDimitry Andric   if (!IsKnownCast || IsCheckedCast)
249a7dea167SDimitry Andric     State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
250a7dea167SDimitry Andric                                       CastSucceeds);
251a7dea167SDimitry Andric 
252a7dea167SDimitry Andric   SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
25381ad6265SDimitry Andric                         : C.getSValBuilder().makeNullWithType(CastToTy);
254a7dea167SDimitry Andric   C.addTransition(
255a7dea167SDimitry Andric       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
256a7dea167SDimitry Andric       getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
257a7dea167SDimitry Andric }
258a7dea167SDimitry Andric 
addInstanceOfTransition(const CallEvent & Call,DefinedOrUnknownSVal DV,ProgramStateRef State,CheckerContext & C,bool IsInstanceOf)259a7dea167SDimitry Andric static void addInstanceOfTransition(const CallEvent &Call,
260a7dea167SDimitry Andric                                     DefinedOrUnknownSVal DV,
261a7dea167SDimitry Andric                                     ProgramStateRef State, CheckerContext &C,
262a7dea167SDimitry Andric                                     bool IsInstanceOf) {
263a7dea167SDimitry Andric   const FunctionDecl *FD = Call.getDecl()->getAsFunction();
264a7dea167SDimitry Andric   QualType CastFromTy = Call.parameters()[0]->getType();
26516d6b3b3SDimitry Andric   SmallVector<QualType, 4> CastToTyVec;
26616d6b3b3SDimitry Andric   for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1;
26716d6b3b3SDimitry Andric        ++idx) {
26816d6b3b3SDimitry Andric     TemplateArgument CastToTempArg =
26916d6b3b3SDimitry Andric       FD->getTemplateSpecializationArgs()->get(idx);
27016d6b3b3SDimitry Andric     switch (CastToTempArg.getKind()) {
27116d6b3b3SDimitry Andric     default:
27216d6b3b3SDimitry Andric       return;
27316d6b3b3SDimitry Andric     case TemplateArgument::Type:
27416d6b3b3SDimitry Andric       CastToTyVec.push_back(CastToTempArg.getAsType());
27516d6b3b3SDimitry Andric       break;
27616d6b3b3SDimitry Andric     case TemplateArgument::Pack:
27716d6b3b3SDimitry Andric       for (TemplateArgument ArgInPack: CastToTempArg.pack_elements())
27816d6b3b3SDimitry Andric         CastToTyVec.push_back(ArgInPack.getAsType());
27916d6b3b3SDimitry Andric       break;
28016d6b3b3SDimitry Andric     }
28116d6b3b3SDimitry Andric   }
28216d6b3b3SDimitry Andric 
28316d6b3b3SDimitry Andric   const MemRegion *MR = DV.getAsRegion();
28416d6b3b3SDimitry Andric   if (MR && CastFromTy->isReferenceType())
28516d6b3b3SDimitry Andric     MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();
28616d6b3b3SDimitry Andric 
28716d6b3b3SDimitry Andric   bool Success = false;
28816d6b3b3SDimitry Andric   bool IsAnyKnown = false;
28916d6b3b3SDimitry Andric   for (QualType CastToTy: CastToTyVec) {
290a7dea167SDimitry Andric     if (CastFromTy->isPointerType())
291a7dea167SDimitry Andric       CastToTy = C.getASTContext().getPointerType(CastToTy);
292a7dea167SDimitry Andric     else if (CastFromTy->isReferenceType())
293a7dea167SDimitry Andric       CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
294a7dea167SDimitry Andric     else
295a7dea167SDimitry Andric       return;
296a7dea167SDimitry Andric 
297a7dea167SDimitry Andric     const DynamicCastInfo *CastInfo =
298a7dea167SDimitry Andric       getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
299a7dea167SDimitry Andric 
300a7dea167SDimitry Andric     bool CastSucceeds;
301a7dea167SDimitry Andric     if (CastInfo)
302a7dea167SDimitry Andric       CastSucceeds = IsInstanceOf && CastInfo->succeeds();
303a7dea167SDimitry Andric     else
304a7dea167SDimitry Andric       CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
305a7dea167SDimitry Andric 
306a7dea167SDimitry Andric     // Store the type and the cast information.
307a7dea167SDimitry Andric     bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
30816d6b3b3SDimitry Andric     IsAnyKnown = IsAnyKnown || IsKnownCast;
30916d6b3b3SDimitry Andric     ProgramStateRef NewState = State;
310a7dea167SDimitry Andric     if (!IsKnownCast)
31116d6b3b3SDimitry Andric       NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
312a7dea167SDimitry Andric                                            IsInstanceOf);
313a7dea167SDimitry Andric 
31416d6b3b3SDimitry Andric     if (CastSucceeds) {
31516d6b3b3SDimitry Andric       Success = true;
31616d6b3b3SDimitry Andric       C.addTransition(
31716d6b3b3SDimitry Andric           NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
31816d6b3b3SDimitry Andric                              C.getSValBuilder().makeTruthVal(true)),
31916d6b3b3SDimitry Andric           getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,
32016d6b3b3SDimitry Andric                      IsKnownCast));
32116d6b3b3SDimitry Andric       if (IsKnownCast)
32216d6b3b3SDimitry Andric         return;
32316d6b3b3SDimitry Andric     } else if (CastInfo && CastInfo->succeeds()) {
32416d6b3b3SDimitry Andric       C.generateSink(NewState, C.getPredecessor());
32516d6b3b3SDimitry Andric       return;
32616d6b3b3SDimitry Andric     }
32716d6b3b3SDimitry Andric   }
32816d6b3b3SDimitry Andric 
32916d6b3b3SDimitry Andric   if (!Success) {
330a7dea167SDimitry Andric     C.addTransition(
331a7dea167SDimitry Andric         State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
33216d6b3b3SDimitry Andric                         C.getSValBuilder().makeTruthVal(false)),
33316d6b3b3SDimitry Andric         getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));
33416d6b3b3SDimitry Andric   }
335a7dea167SDimitry Andric }
336a7dea167SDimitry Andric 
337a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
338a7dea167SDimitry Andric // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
339a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
340a7dea167SDimitry Andric 
evalNonNullParamNonNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsCheckedCast=false)341a7dea167SDimitry Andric static void evalNonNullParamNonNullReturn(const CallEvent &Call,
342a7dea167SDimitry Andric                                           DefinedOrUnknownSVal DV,
343a7dea167SDimitry Andric                                           CheckerContext &C,
344a7dea167SDimitry Andric                                           bool IsCheckedCast = false) {
345a7dea167SDimitry Andric   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
346a7dea167SDimitry Andric                     /*IsNonNullReturn=*/true, IsCheckedCast);
347a7dea167SDimitry Andric }
348a7dea167SDimitry Andric 
evalNonNullParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)349a7dea167SDimitry Andric static void evalNonNullParamNullReturn(const CallEvent &Call,
350a7dea167SDimitry Andric                                        DefinedOrUnknownSVal DV,
351a7dea167SDimitry Andric                                        CheckerContext &C) {
352a7dea167SDimitry Andric   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
353a7dea167SDimitry Andric                     /*IsNonNullReturn=*/false);
354a7dea167SDimitry Andric }
355a7dea167SDimitry Andric 
evalNullParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)356a7dea167SDimitry Andric static void evalNullParamNullReturn(const CallEvent &Call,
357a7dea167SDimitry Andric                                     DefinedOrUnknownSVal DV,
358a7dea167SDimitry Andric                                     CheckerContext &C) {
359a7dea167SDimitry Andric   if (ProgramStateRef State = C.getState()->assume(DV, false))
360a7dea167SDimitry Andric     C.addTransition(State->BindExpr(Call.getOriginExpr(),
361a7dea167SDimitry Andric                                     C.getLocationContext(),
36281ad6265SDimitry Andric                                     C.getSValBuilder().makeNullWithType(
36381ad6265SDimitry Andric                                         Call.getOriginExpr()->getType()),
36481ad6265SDimitry Andric                                     false),
3650b57cec5SDimitry Andric                     C.getNoteTag("Assuming null pointer is passed into cast",
366a7dea167SDimitry Andric                                  /*IsPrunable=*/true));
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric 
evalCast(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const369a7dea167SDimitry Andric void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
3700b57cec5SDimitry Andric                                 CheckerContext &C) const {
371a7dea167SDimitry Andric   evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
3720b57cec5SDimitry Andric }
3730b57cec5SDimitry Andric 
evalDynCast(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const374a7dea167SDimitry Andric void CastValueChecker::evalDynCast(const CallEvent &Call,
375a7dea167SDimitry Andric                                    DefinedOrUnknownSVal DV,
3760b57cec5SDimitry Andric                                    CheckerContext &C) const {
377a7dea167SDimitry Andric   evalNonNullParamNonNullReturn(Call, DV, C);
378a7dea167SDimitry Andric   evalNonNullParamNullReturn(Call, DV, C);
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric 
evalCastOrNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const381a7dea167SDimitry Andric void CastValueChecker::evalCastOrNull(const CallEvent &Call,
382a7dea167SDimitry Andric                                       DefinedOrUnknownSVal DV,
3830b57cec5SDimitry Andric                                       CheckerContext &C) const {
384a7dea167SDimitry Andric   evalNonNullParamNonNullReturn(Call, DV, C);
385a7dea167SDimitry Andric   evalNullParamNullReturn(Call, DV, C);
3860b57cec5SDimitry Andric }
3870b57cec5SDimitry Andric 
evalDynCastOrNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const388a7dea167SDimitry Andric void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
389a7dea167SDimitry Andric                                          DefinedOrUnknownSVal DV,
3900b57cec5SDimitry Andric                                          CheckerContext &C) const {
391a7dea167SDimitry Andric   evalNonNullParamNonNullReturn(Call, DV, C);
392a7dea167SDimitry Andric   evalNonNullParamNullReturn(Call, DV, C);
393a7dea167SDimitry Andric   evalNullParamNullReturn(Call, DV, C);
3940b57cec5SDimitry Andric }
3950b57cec5SDimitry Andric 
396a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
397a7dea167SDimitry Andric // Evaluating castAs, getAs.
398a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
399a7dea167SDimitry Andric 
evalZeroParamNonNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsCheckedCast=false)400a7dea167SDimitry Andric static void evalZeroParamNonNullReturn(const CallEvent &Call,
401a7dea167SDimitry Andric                                        DefinedOrUnknownSVal DV,
402a7dea167SDimitry Andric                                        CheckerContext &C,
403a7dea167SDimitry Andric                                        bool IsCheckedCast = false) {
404a7dea167SDimitry Andric   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
405a7dea167SDimitry Andric                     /*IsNonNullReturn=*/true, IsCheckedCast);
406a7dea167SDimitry Andric }
407a7dea167SDimitry Andric 
evalZeroParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)408a7dea167SDimitry Andric static void evalZeroParamNullReturn(const CallEvent &Call,
409a7dea167SDimitry Andric                                     DefinedOrUnknownSVal DV,
410a7dea167SDimitry Andric                                     CheckerContext &C) {
411a7dea167SDimitry Andric   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
412a7dea167SDimitry Andric                     /*IsNonNullReturn=*/false);
413a7dea167SDimitry Andric }
414a7dea167SDimitry Andric 
evalCastAs(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const415a7dea167SDimitry Andric void CastValueChecker::evalCastAs(const CallEvent &Call,
416a7dea167SDimitry Andric                                   DefinedOrUnknownSVal DV,
417a7dea167SDimitry Andric                                   CheckerContext &C) const {
418a7dea167SDimitry Andric   evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
419a7dea167SDimitry Andric }
420a7dea167SDimitry Andric 
evalGetAs(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const421a7dea167SDimitry Andric void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
422a7dea167SDimitry Andric                                  CheckerContext &C) const {
423a7dea167SDimitry Andric   evalZeroParamNonNullReturn(Call, DV, C);
424a7dea167SDimitry Andric   evalZeroParamNullReturn(Call, DV, C);
425a7dea167SDimitry Andric }
426a7dea167SDimitry Andric 
427a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
428a7dea167SDimitry Andric // Evaluating isa, isa_and_nonnull.
429a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
430a7dea167SDimitry Andric 
evalIsa(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const431a7dea167SDimitry Andric void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
432a7dea167SDimitry Andric                                CheckerContext &C) const {
433a7dea167SDimitry Andric   ProgramStateRef NonNullState, NullState;
434a7dea167SDimitry Andric   std::tie(NonNullState, NullState) = C.getState()->assume(DV);
435a7dea167SDimitry Andric 
436a7dea167SDimitry Andric   if (NonNullState) {
437a7dea167SDimitry Andric     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
438a7dea167SDimitry Andric     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
439a7dea167SDimitry Andric   }
440a7dea167SDimitry Andric 
441a7dea167SDimitry Andric   if (NullState) {
442a7dea167SDimitry Andric     C.generateSink(NullState, C.getPredecessor());
443a7dea167SDimitry Andric   }
444a7dea167SDimitry Andric }
445a7dea167SDimitry Andric 
evalIsaAndNonNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const446a7dea167SDimitry Andric void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
447a7dea167SDimitry Andric                                          DefinedOrUnknownSVal DV,
448a7dea167SDimitry Andric                                          CheckerContext &C) const {
449a7dea167SDimitry Andric   ProgramStateRef NonNullState, NullState;
450a7dea167SDimitry Andric   std::tie(NonNullState, NullState) = C.getState()->assume(DV);
451a7dea167SDimitry Andric 
452a7dea167SDimitry Andric   if (NonNullState) {
453a7dea167SDimitry Andric     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
454a7dea167SDimitry Andric     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
455a7dea167SDimitry Andric   }
456a7dea167SDimitry Andric 
457a7dea167SDimitry Andric   if (NullState) {
458a7dea167SDimitry Andric     addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
459a7dea167SDimitry Andric   }
460a7dea167SDimitry Andric }
461a7dea167SDimitry Andric 
462a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
463a7dea167SDimitry Andric // Main logic to evaluate a call.
464a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
465a7dea167SDimitry Andric 
evalCall(const CallEvent & Call,CheckerContext & C) const4660b57cec5SDimitry Andric bool CastValueChecker::evalCall(const CallEvent &Call,
4670b57cec5SDimitry Andric                                 CheckerContext &C) const {
468a7dea167SDimitry Andric   const auto *Lookup = CDM.lookup(Call);
469a7dea167SDimitry Andric   if (!Lookup)
4700b57cec5SDimitry Andric     return false;
4710b57cec5SDimitry Andric 
472a7dea167SDimitry Andric   const CastCheck &Check = Lookup->first;
473a7dea167SDimitry Andric   CallKind Kind = Lookup->second;
474a7dea167SDimitry Andric 
475bdd1243dSDimitry Andric   std::optional<DefinedOrUnknownSVal> DV;
476a7dea167SDimitry Andric 
477a7dea167SDimitry Andric   switch (Kind) {
478a7dea167SDimitry Andric   case CallKind::Function: {
479a7dea167SDimitry Andric     // We only model casts from pointers to pointers or from references
480a7dea167SDimitry Andric     // to references. Other casts are most likely specialized and we
481a7dea167SDimitry Andric     // cannot model them.
482a7dea167SDimitry Andric     QualType ParamT = Call.parameters()[0]->getType();
483a7dea167SDimitry Andric     QualType ResultT = Call.getResultType();
484a7dea167SDimitry Andric     if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
48516d6b3b3SDimitry Andric         !(ParamT->isReferenceType() && ResultT->isReferenceType())) {
4860b57cec5SDimitry Andric       return false;
48716d6b3b3SDimitry Andric     }
4880b57cec5SDimitry Andric 
489a7dea167SDimitry Andric     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
490a7dea167SDimitry Andric     break;
491a7dea167SDimitry Andric   }
492a7dea167SDimitry Andric   case CallKind::InstanceOf: {
493a7dea167SDimitry Andric     // We need to obtain the only template argument to determinte the type.
494a7dea167SDimitry Andric     const FunctionDecl *FD = Call.getDecl()->getAsFunction();
495a7dea167SDimitry Andric     if (!FD || !FD->getTemplateSpecializationArgs())
4960b57cec5SDimitry Andric       return false;
4970b57cec5SDimitry Andric 
498a7dea167SDimitry Andric     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
499a7dea167SDimitry Andric     break;
500a7dea167SDimitry Andric   }
501a7dea167SDimitry Andric   case CallKind::Method:
502a7dea167SDimitry Andric     const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
503a7dea167SDimitry Andric     if (!InstanceCall)
5040b57cec5SDimitry Andric       return false;
5050b57cec5SDimitry Andric 
506a7dea167SDimitry Andric     DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
507a7dea167SDimitry Andric     break;
508a7dea167SDimitry Andric   }
509a7dea167SDimitry Andric 
510a7dea167SDimitry Andric   if (!DV)
511a7dea167SDimitry Andric     return false;
512a7dea167SDimitry Andric 
513a7dea167SDimitry Andric   Check(this, Call, *DV, C);
5140b57cec5SDimitry Andric   return true;
5150b57cec5SDimitry Andric }
5160b57cec5SDimitry Andric 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const5175ffd83dbSDimitry Andric void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
5185ffd83dbSDimitry Andric                                         CheckerContext &C) const {
5195ffd83dbSDimitry Andric   C.addTransition(removeDeadCasts(C.getState(), SR));
5205ffd83dbSDimitry Andric }
5215ffd83dbSDimitry Andric 
registerCastValueChecker(CheckerManager & Mgr)5220b57cec5SDimitry Andric void ento::registerCastValueChecker(CheckerManager &Mgr) {
5230b57cec5SDimitry Andric   Mgr.registerChecker<CastValueChecker>();
5240b57cec5SDimitry Andric }
5250b57cec5SDimitry Andric 
shouldRegisterCastValueChecker(const CheckerManager & mgr)5265ffd83dbSDimitry Andric bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
5270b57cec5SDimitry Andric   return true;
5280b57cec5SDimitry Andric }
529