clang 18.0.0git
DynamicTypeChecker.cpp
Go to the documentation of this file.
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
27
28using namespace clang;
29using namespace ento;
30
31namespace {
32class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
33 mutable std::unique_ptr<BugType> BT;
34 void initBugType() const {
35 if (!BT)
36 BT.reset(
37 new BugType(this, "Dynamic and static type mismatch", "Type Error"));
38 }
39
40 class DynamicTypeBugVisitor : public BugReporterVisitor {
41 public:
42 DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
43
44 void Profile(llvm::FoldingSetNodeID &ID) const override {
45 static int X = 0;
46 ID.AddPointer(&X);
47 ID.AddPointer(Reg);
48 }
49
50 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
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
63public:
64 void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
65};
66}
67
68void DynamicTypeChecker::reportTypeError(QualType DynamicType,
69 QualType StaticType,
70 const MemRegion *Reg,
71 const Stmt *ReportedNode,
72 CheckerContext &C) const {
73 initBugType();
75 llvm::raw_svector_ostream OS(Buf);
76 OS << "Object has a dynamic type '";
77 QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
78 llvm::Twine());
79 OS << "' which is incompatible with static type '";
80 QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81 llvm::Twine());
82 OS << "'";
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));
89}
90
91PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
93 ProgramStateRef State = N->getState();
94 ProgramStateRef StatePrev = N->getFirstPred()->getState();
95
96 DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
97 DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
98 if (!TrackedType.isValid())
99 return nullptr;
100
101 if (TrackedTypePrev.isValid() &&
102 TrackedTypePrev.getType() == TrackedType.getType())
103 return nullptr;
104
105 // Retrieve the associated statement.
106 const Stmt *S = N->getStmtForDiagnostics();
107 if (!S)
108 return nullptr;
109
110 const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
111
113 llvm::raw_svector_ostream OS(Buf);
114 OS << "Type '";
115 QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
116 LangOpts, llvm::Twine());
117 OS << "' is inferred from ";
118
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());
123 OS << "' to '";
124 QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
125 LangOpts, llvm::Twine());
126 OS << "')";
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());
131 OS << "' to '";
132 QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
133 LangOpts, llvm::Twine());
134 OS << "')";
135 } else {
136 OS << "this context";
137 }
138
139 // Generate the extra diagnostic.
141 N->getLocationContext());
142 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
143}
144
145static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
146 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
147 if (!Decl)
148 return false;
149
150 return Decl->getDefinition();
151}
152
153// TODO: consider checking explicit casts?
154void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
155 CheckerContext &C) const {
156 // TODO: C++ support.
157 if (CE->getCastKind() != CK_BitCast)
158 return;
159
160 const MemRegion *Region = C.getSVal(CE).getAsRegion();
161 if (!Region)
162 return;
163
164 ProgramStateRef State = C.getState();
165 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
166
167 if (!DynTypeInfo.isValid())
168 return;
169
170 QualType DynType = DynTypeInfo.getType();
171 QualType StaticType = CE->getType();
172
173 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
174 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
175
176 if (!DynObjCType || !StaticObjCType)
177 return;
178
179 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
180 return;
181
182 ASTContext &ASTCtxt = C.getASTContext();
183
184 // Strip kindeofness to correctly detect subtyping relationships.
185 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
186 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
187
188 // Specialized objects are handled by the generics checker.
189 if (StaticObjCType->isSpecialized())
190 return;
191
192 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
193 return;
194
195 if (DynTypeInfo.canBeASubClass() &&
196 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
197 return;
198
199 reportTypeError(DynType, StaticType, Region, CE, C);
200}
201
202void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
203 mgr.registerChecker<DynamicTypeChecker>();
204}
205
206bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
207 return true;
208}
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
#define X(type, name)
Definition: Value.h:142
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
bool canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, const ObjCObjectPointerType *RHSOPT)
canAssignObjCInterfaces - Return true if the two interface types are compatible for assignment from R...
const LangOptions & getLangOpts() const
Definition: ASTContext.h:767
CastKind getCastKind() const
Definition: Expr.h:3561
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:85
QualType getType() const
Definition: Expr.h:142
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3677
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:83
Represents an ObjC class declaration.
Definition: DeclObjC.h:1150
Represents a pointer to an Objective C object.
Definition: Type.h:6430
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:6519
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:856
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Definition: Type.h:6482
A (possibly-)qualified type.
Definition: Type.h:736
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:6781
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
The collection of all-type qualifiers we support.
Definition: Type.h:146
Stmt - This represents one statement.
Definition: Stmt.h:84
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:325
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7558
ASTContext & getASTContext() const
Definition: BugReporter.h:717
const SourceManager & getSourceManager() const
Definition: BugReporter.h:721
BugReporterVisitors are used to add custom diagnostics along a path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
Stores the currently inferred strictest bound on the runtime type of a region in a given state along ...
bool canBeASubClass() const
Returns false if the type information is precise (the type 'DynTy' is the only type in the lattice),...
QualType getType() const
Returns the currently inferred upper bound on the runtime type.
bool isValid() const
Returns true if the dynamic type info is available.
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR)
Get dynamic type information for the region MR.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef