1 //== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This checker looks for cases where the dynamic type of an object is unrelated
11 // to its static type. The type information utilized by this check is collected
12 // by the DynamicTypePropagation checker. This check does not report any type
13 // error for ObjC Generic types, in order to avoid duplicate erros from the
14 // ObjC Generics checker. This checker is not supposed to modify the program
15 // state, it is just the observer of the type information provided by other
18 //===----------------------------------------------------------------------===//
20 #include "ClangSACheckers.h"
21 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22 #include "clang/StaticAnalyzer/Core/Checker.h"
23 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
29 using namespace clang;
33 class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
34 mutable std::unique_ptr<BugType> BT;
35 void initBugType() const {
38 new BugType(this, "Dynamic and static type mismatch", "Type Error"));
41 class DynamicTypeBugVisitor : public BugReporterVisitor {
43 DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
45 void Profile(llvm::FoldingSetNodeID &ID) const override {
51 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
52 const ExplodedNode *PrevN,
53 BugReporterContext &BRC,
54 BugReport &BR) override;
57 // The tracked region.
61 void reportTypeError(QualType DynamicType, QualType StaticType,
62 const MemRegion *Reg, const Stmt *ReportedNode,
63 CheckerContext &C) const;
66 void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
70 void DynamicTypeChecker::reportTypeError(QualType DynamicType,
73 const Stmt *ReportedNode,
74 CheckerContext &C) const {
77 llvm::raw_svector_ostream OS(Buf);
78 OS << "Object has a dynamic type '";
79 QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81 OS << "' which is incompatible with static type '";
82 QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
85 std::unique_ptr<BugReport> R(
86 new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
87 R->markInteresting(Reg);
88 R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
89 R->addRange(ReportedNode->getSourceRange());
90 C.emitReport(std::move(R));
93 std::shared_ptr<PathDiagnosticPiece>
94 DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
95 const ExplodedNode *PrevN,
96 BugReporterContext &BRC,
98 ProgramStateRef State = N->getState();
99 ProgramStateRef StatePrev = PrevN->getState();
101 DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
102 DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
103 if (!TrackedType.isValid())
106 if (TrackedTypePrev.isValid() &&
107 TrackedTypePrev.getType() == TrackedType.getType())
110 // Retrieve the associated statement.
111 const Stmt *S = PathDiagnosticLocation::getStmt(N);
115 const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
117 SmallString<256> Buf;
118 llvm::raw_svector_ostream OS(Buf);
120 QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
121 LangOpts, llvm::Twine());
122 OS << "' is inferred from ";
124 if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
125 OS << "explicit cast (from '";
126 QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
127 Qualifiers(), OS, LangOpts, llvm::Twine());
129 QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
130 LangOpts, llvm::Twine());
132 } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
133 OS << "implicit cast (from '";
134 QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
135 Qualifiers(), OS, LangOpts, llvm::Twine());
137 QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
138 LangOpts, llvm::Twine());
141 OS << "this context";
144 // Generate the extra diagnostic.
145 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
146 N->getLocationContext());
147 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
151 static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
152 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
156 return Decl->getDefinition();
159 // TODO: consider checking explicit casts?
160 void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
161 CheckerContext &C) const {
162 // TODO: C++ support.
163 if (CE->getCastKind() != CK_BitCast)
166 const MemRegion *Region = C.getSVal(CE).getAsRegion();
170 ProgramStateRef State = C.getState();
171 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
173 if (!DynTypeInfo.isValid())
176 QualType DynType = DynTypeInfo.getType();
177 QualType StaticType = CE->getType();
179 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
180 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
182 if (!DynObjCType || !StaticObjCType)
185 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
188 ASTContext &ASTCtxt = C.getASTContext();
190 // Strip kindeofness to correctly detect subtyping relationships.
191 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
192 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
194 // Specialized objects are handled by the generics checker.
195 if (StaticObjCType->isSpecialized())
198 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
201 if (DynTypeInfo.canBeASubClass() &&
202 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
205 reportTypeError(DynType, StaticType, Region, CE, C);
208 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
209 mgr.registerChecker<DynamicTypeChecker>();