1 //== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
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
7 //===----------------------------------------------------------------------===//
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
17 //===----------------------------------------------------------------------===//
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"
28 using namespace clang
;
32 class DynamicTypeChecker
: public Checker
<check::PostStmt
<ImplicitCastExpr
>> {
33 mutable std::unique_ptr
<BugType
> BT
;
34 void initBugType() const {
37 new BugType(this, "Dynamic and static type mismatch", "Type Error"));
40 class DynamicTypeBugVisitor
: public BugReporterVisitor
{
42 DynamicTypeBugVisitor(const MemRegion
*Reg
) : Reg(Reg
) {}
44 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
50 PathDiagnosticPieceRef
VisitNode(const ExplodedNode
*N
,
51 BugReporterContext
&BRC
,
52 PathSensitiveBugReport
&BR
) override
;
55 // The tracked region.
59 void reportTypeError(QualType DynamicType
, QualType StaticType
,
60 const MemRegion
*Reg
, const Stmt
*ReportedNode
,
61 CheckerContext
&C
) const;
64 void checkPostStmt(const ImplicitCastExpr
*CE
, CheckerContext
&C
) const;
68 void DynamicTypeChecker::reportTypeError(QualType DynamicType
,
71 const Stmt
*ReportedNode
,
72 CheckerContext
&C
) const {
75 llvm::raw_svector_ostream
OS(Buf
);
76 OS
<< "Object has a dynamic type '";
77 QualType::print(DynamicType
.getTypePtr(), Qualifiers(), OS
, C
.getLangOpts(),
79 OS
<< "' which is incompatible with static type '";
80 QualType::print(StaticType
.getTypePtr(), Qualifiers(), OS
, C
.getLangOpts(),
83 auto R
= std::make_unique
<PathSensitiveBugReport
>(
84 *BT
, OS
.str(), C
.generateNonFatalErrorNode());
85 R
->markInteresting(Reg
);
86 R
->addVisitor(std::make_unique
<DynamicTypeBugVisitor
>(Reg
));
87 R
->addRange(ReportedNode
->getSourceRange());
88 C
.emitReport(std::move(R
));
91 PathDiagnosticPieceRef
DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
92 const ExplodedNode
*N
, BugReporterContext
&BRC
, PathSensitiveBugReport
&) {
93 ProgramStateRef State
= N
->getState();
94 ProgramStateRef StatePrev
= N
->getFirstPred()->getState();
96 DynamicTypeInfo TrackedType
= getDynamicTypeInfo(State
, Reg
);
97 DynamicTypeInfo TrackedTypePrev
= getDynamicTypeInfo(StatePrev
, Reg
);
98 if (!TrackedType
.isValid())
101 if (TrackedTypePrev
.isValid() &&
102 TrackedTypePrev
.getType() == TrackedType
.getType())
105 // Retrieve the associated statement.
106 const Stmt
*S
= N
->getStmtForDiagnostics();
110 const LangOptions
&LangOpts
= BRC
.getASTContext().getLangOpts();
112 SmallString
<256> Buf
;
113 llvm::raw_svector_ostream
OS(Buf
);
115 QualType::print(TrackedType
.getType().getTypePtr(), Qualifiers(), OS
,
116 LangOpts
, llvm::Twine());
117 OS
<< "' is inferred from ";
119 if (const auto *ExplicitCast
= dyn_cast
<ExplicitCastExpr
>(S
)) {
120 OS
<< "explicit cast (from '";
121 QualType::print(ExplicitCast
->getSubExpr()->getType().getTypePtr(),
122 Qualifiers(), OS
, LangOpts
, llvm::Twine());
124 QualType::print(ExplicitCast
->getType().getTypePtr(), Qualifiers(), OS
,
125 LangOpts
, llvm::Twine());
127 } else if (const auto *ImplicitCast
= dyn_cast
<ImplicitCastExpr
>(S
)) {
128 OS
<< "implicit cast (from '";
129 QualType::print(ImplicitCast
->getSubExpr()->getType().getTypePtr(),
130 Qualifiers(), OS
, LangOpts
, llvm::Twine());
132 QualType::print(ImplicitCast
->getType().getTypePtr(), Qualifiers(), OS
,
133 LangOpts
, llvm::Twine());
136 OS
<< "this context";
139 // Generate the extra diagnostic.
140 PathDiagnosticLocation
Pos(S
, BRC
.getSourceManager(),
141 N
->getLocationContext());
142 return std::make_shared
<PathDiagnosticEventPiece
>(Pos
, OS
.str(), true);
145 static bool hasDefinition(const ObjCObjectPointerType
*ObjPtr
) {
146 const ObjCInterfaceDecl
*Decl
= ObjPtr
->getInterfaceDecl();
150 return Decl
->getDefinition();
153 // TODO: consider checking explicit casts?
154 void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr
*CE
,
155 CheckerContext
&C
) const {
156 // TODO: C++ support.
157 if (CE
->getCastKind() != CK_BitCast
)
160 const MemRegion
*Region
= C
.getSVal(CE
).getAsRegion();
164 ProgramStateRef State
= C
.getState();
165 DynamicTypeInfo DynTypeInfo
= getDynamicTypeInfo(State
, Region
);
167 if (!DynTypeInfo
.isValid())
170 QualType DynType
= DynTypeInfo
.getType();
171 QualType StaticType
= CE
->getType();
173 const auto *DynObjCType
= DynType
->getAs
<ObjCObjectPointerType
>();
174 const auto *StaticObjCType
= StaticType
->getAs
<ObjCObjectPointerType
>();
176 if (!DynObjCType
|| !StaticObjCType
)
179 if (!hasDefinition(DynObjCType
) || !hasDefinition(StaticObjCType
))
182 ASTContext
&ASTCtxt
= C
.getASTContext();
184 // Strip kindeofness to correctly detect subtyping relationships.
185 DynObjCType
= DynObjCType
->stripObjCKindOfTypeAndQuals(ASTCtxt
);
186 StaticObjCType
= StaticObjCType
->stripObjCKindOfTypeAndQuals(ASTCtxt
);
188 // Specialized objects are handled by the generics checker.
189 if (StaticObjCType
->isSpecialized())
192 if (ASTCtxt
.canAssignObjCInterfaces(StaticObjCType
, DynObjCType
))
195 if (DynTypeInfo
.canBeASubClass() &&
196 ASTCtxt
.canAssignObjCInterfaces(DynObjCType
, StaticObjCType
))
199 reportTypeError(DynType
, StaticType
, Region
, CE
, C
);
202 void ento::registerDynamicTypeChecker(CheckerManager
&mgr
) {
203 mgr
.registerChecker
<DynamicTypeChecker
>();
206 bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager
&mgr
) {