/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
19 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
20 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
21 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
22 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
23 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
24 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
25 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
26 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
27 | | |
28 | | using namespace clang; |
29 | | using namespace ento; |
30 | | |
31 | | namespace { |
32 | | class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { |
33 | | mutable std::unique_ptr<BugType> BT; |
34 | 15 | void initBugType() const { |
35 | 15 | if (!BT) |
36 | 3 | BT.reset( |
37 | 3 | new BugType(this, "Dynamic and static type mismatch", "Type Error")); |
38 | 15 | } |
39 | | |
40 | | class DynamicTypeBugVisitor : public BugReporterVisitor { |
41 | | public: |
42 | 15 | DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {} |
43 | | |
44 | 15 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
45 | 15 | static int X = 0; |
46 | 15 | ID.AddPointer(&X); |
47 | 15 | ID.AddPointer(Reg); |
48 | 15 | } |
49 | | |
50 | | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
51 | | BugReporterContext &BRC, |
52 | | PathSensitiveBugReport &BR) override; |
53 | | |
54 | | private: |
55 | | // The tracked region. |
56 | | const MemRegion *Reg; |
57 | | }; |
58 | | |
59 | | void reportTypeError(QualType DynamicType, QualType StaticType, |
60 | | const MemRegion *Reg, const Stmt *ReportedNode, |
61 | | CheckerContext &C) const; |
62 | | |
63 | | public: |
64 | | void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const; |
65 | | }; |
66 | | } |
67 | | |
68 | | void DynamicTypeChecker::reportTypeError(QualType DynamicType, |
69 | | QualType StaticType, |
70 | | const MemRegion *Reg, |
71 | | const Stmt *ReportedNode, |
72 | 15 | CheckerContext &C) const { |
73 | 15 | initBugType(); |
74 | 15 | SmallString<192> Buf; |
75 | 15 | llvm::raw_svector_ostream OS(Buf); |
76 | 15 | OS << "Object has a dynamic type '"; |
77 | 15 | QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), |
78 | 15 | llvm::Twine()); |
79 | 15 | OS << "' which is incompatible with static type '"; |
80 | 15 | QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), |
81 | 15 | llvm::Twine()); |
82 | 15 | OS << "'"; |
83 | 15 | auto R = std::make_unique<PathSensitiveBugReport>( |
84 | 15 | *BT, OS.str(), C.generateNonFatalErrorNode()); |
85 | 15 | R->markInteresting(Reg); |
86 | 15 | R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg)); |
87 | 15 | R->addRange(ReportedNode->getSourceRange()); |
88 | 15 | C.emitReport(std::move(R)); |
89 | 15 | } |
90 | | |
91 | | PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode( |
92 | 707 | const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { |
93 | 707 | ProgramStateRef State = N->getState(); |
94 | 707 | ProgramStateRef StatePrev = N->getFirstPred()->getState(); |
95 | | |
96 | 707 | DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg); |
97 | 707 | DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg); |
98 | 707 | if (!TrackedType.isValid()) |
99 | 0 | return nullptr; |
100 | | |
101 | 707 | if (TrackedTypePrev.isValid() && |
102 | 707 | TrackedTypePrev.getType() == TrackedType.getType()) |
103 | 695 | return nullptr; |
104 | | |
105 | | // Retrieve the associated statement. |
106 | 12 | const Stmt *S = N->getStmtForDiagnostics(); |
107 | 12 | if (!S) |
108 | 0 | return nullptr; |
109 | | |
110 | 12 | const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); |
111 | | |
112 | 12 | SmallString<256> Buf; |
113 | 12 | llvm::raw_svector_ostream OS(Buf); |
114 | 12 | OS << "Type '"; |
115 | 12 | QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS, |
116 | 12 | LangOpts, llvm::Twine()); |
117 | 12 | OS << "' is inferred from "; |
118 | | |
119 | 12 | if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { |
120 | 0 | OS << "explicit cast (from '"; |
121 | 0 | QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), |
122 | 0 | Qualifiers(), OS, LangOpts, llvm::Twine()); |
123 | 0 | OS << "' to '"; |
124 | 0 | QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, |
125 | 0 | LangOpts, llvm::Twine()); |
126 | 0 | OS << "')"; |
127 | 12 | } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { |
128 | 0 | OS << "implicit cast (from '"; |
129 | 0 | QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), |
130 | 0 | Qualifiers(), OS, LangOpts, llvm::Twine()); |
131 | 0 | OS << "' to '"; |
132 | 0 | QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, |
133 | 0 | LangOpts, llvm::Twine()); |
134 | 0 | OS << "')"; |
135 | 12 | } else { |
136 | 12 | OS << "this context"; |
137 | 12 | } |
138 | | |
139 | | // Generate the extra diagnostic. |
140 | 12 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
141 | 12 | N->getLocationContext()); |
142 | 12 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); |
143 | 12 | } |
144 | | |
145 | 666 | static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) { |
146 | 666 | const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl(); |
147 | 666 | if (!Decl) |
148 | 197 | return false; |
149 | | |
150 | 469 | return Decl->getDefinition(); |
151 | 666 | } |
152 | | |
153 | | // TODO: consider checking explicit casts? |
154 | | void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, |
155 | 8.50k | CheckerContext &C) const { |
156 | | // TODO: C++ support. |
157 | 8.50k | if (CE->getCastKind() != CK_BitCast) |
158 | 7.96k | return; |
159 | | |
160 | 544 | const MemRegion *Region = C.getSVal(CE).getAsRegion(); |
161 | 544 | if (!Region) |
162 | 48 | return; |
163 | | |
164 | 496 | ProgramStateRef State = C.getState(); |
165 | 496 | DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region); |
166 | | |
167 | 496 | if (!DynTypeInfo.isValid()) |
168 | 0 | return; |
169 | | |
170 | 496 | QualType DynType = DynTypeInfo.getType(); |
171 | 496 | QualType StaticType = CE->getType(); |
172 | | |
173 | 496 | const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>(); |
174 | 496 | const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>(); |
175 | | |
176 | 496 | if (!DynObjCType || !StaticObjCType364 ) |
177 | 144 | return; |
178 | | |
179 | 352 | if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType)314 ) |
180 | 198 | return; |
181 | | |
182 | 154 | ASTContext &ASTCtxt = C.getASTContext(); |
183 | | |
184 | | // Strip kindeofness to correctly detect subtyping relationships. |
185 | 154 | DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
186 | 154 | StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); |
187 | | |
188 | | // Specialized objects are handled by the generics checker. |
189 | 154 | if (StaticObjCType->isSpecialized()) |
190 | 94 | return; |
191 | | |
192 | 60 | if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType)) |
193 | 43 | return; |
194 | | |
195 | 17 | if (DynTypeInfo.canBeASubClass() && |
196 | 17 | ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType)) |
197 | 2 | return; |
198 | | |
199 | 15 | reportTypeError(DynType, StaticType, Region, CE, C); |
200 | 15 | } |
201 | | |
202 | 77 | void ento::registerDynamicTypeChecker(CheckerManager &mgr) { |
203 | 77 | mgr.registerChecker<DynamicTypeChecker>(); |
204 | 77 | } |
205 | | |
206 | 154 | bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) { |
207 | 154 | return true; |
208 | 154 | } |