clang 22.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
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
31 const BugType BT{this, "Dynamic and static type mismatch", "Type Error"};
32
33 class DynamicTypeBugVisitor : public BugReporterVisitor {
34 public:
35 DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
36
37 void Profile(llvm::FoldingSetNodeID &ID) const override {
38 static int X = 0;
39 ID.AddPointer(&X);
40 ID.AddPointer(Reg);
41 }
42
43 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
44 BugReporterContext &BRC,
45 PathSensitiveBugReport &BR) override;
46
47 private:
48 // The tracked region.
49 const MemRegion *Reg;
50 };
51
52 void reportTypeError(QualType DynamicType, QualType StaticType,
53 const MemRegion *Reg, const Stmt *ReportedNode,
54 CheckerContext &C) const;
55
56public:
57 void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
58};
59}
60
61void DynamicTypeChecker::reportTypeError(QualType DynamicType,
62 QualType StaticType,
63 const MemRegion *Reg,
64 const Stmt *ReportedNode,
65 CheckerContext &C) const {
66 SmallString<192> Buf;
67 llvm::raw_svector_ostream OS(Buf);
68 OS << "Object has a dynamic type '";
69 QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
70 llvm::Twine());
71 OS << "' which is incompatible with static type '";
72 QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
73 llvm::Twine());
74 OS << "'";
75 auto R = std::make_unique<PathSensitiveBugReport>(
76 BT, OS.str(), C.generateNonFatalErrorNode());
77 R->markInteresting(Reg);
78 R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
79 R->addRange(ReportedNode->getSourceRange());
80 C.emitReport(std::move(R));
81}
82
83PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
84 const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
85 ProgramStateRef State = N->getState();
86 ProgramStateRef StatePrev = N->getFirstPred()->getState();
87
88 DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
89 DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
90 if (!TrackedType.isValid())
91 return nullptr;
92
93 if (TrackedTypePrev.isValid() &&
94 TrackedTypePrev.getType() == TrackedType.getType())
95 return nullptr;
96
97 // Retrieve the associated statement.
98 const Stmt *S = N->getStmtForDiagnostics();
99 if (!S)
100 return nullptr;
101
102 const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
103
104 SmallString<256> Buf;
105 llvm::raw_svector_ostream OS(Buf);
106 OS << "Type '";
107 QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
108 LangOpts, llvm::Twine());
109 OS << "' is inferred from ";
110
111 if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
112 OS << "explicit cast (from '";
113 QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
114 Qualifiers(), OS, LangOpts, llvm::Twine());
115 OS << "' to '";
116 QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
117 LangOpts, llvm::Twine());
118 OS << "')";
119 } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
120 OS << "implicit cast (from '";
121 QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
122 Qualifiers(), OS, LangOpts, llvm::Twine());
123 OS << "' to '";
124 QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
125 LangOpts, llvm::Twine());
126 OS << "')";
127 } else {
128 OS << "this context";
129 }
130
131 // Generate the extra diagnostic.
132 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
133 N->getLocationContext());
134 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
135}
136
137static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
138 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
139 if (!Decl)
140 return false;
141
142 return Decl->getDefinition();
143}
144
145// TODO: consider checking explicit casts?
146void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
147 CheckerContext &C) const {
148 // TODO: C++ support.
149 if (CE->getCastKind() != CK_BitCast)
150 return;
151
152 const MemRegion *Region = C.getSVal(CE).getAsRegion();
153 if (!Region)
154 return;
155
156 ProgramStateRef State = C.getState();
157 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
158
159 if (!DynTypeInfo.isValid())
160 return;
161
162 QualType DynType = DynTypeInfo.getType();
163 QualType StaticType = CE->getType();
164
165 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
166 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
167
168 if (!DynObjCType || !StaticObjCType)
169 return;
170
171 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
172 return;
173
174 ASTContext &ASTCtxt = C.getASTContext();
175
176 // Strip kindeofness to correctly detect subtyping relationships.
177 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
178 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
179
180 // Specialized objects are handled by the generics checker.
181 if (StaticObjCType->isSpecialized())
182 return;
183
184 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
185 return;
186
187 if (DynTypeInfo.canBeASubClass() &&
188 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
189 return;
190
191 reportTypeError(DynType, StaticType, Region, CE, C);
192}
193
194void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
195 mgr.registerChecker<DynamicTypeChecker>();
196}
197
198bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
199 return true;
200}
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
#define X(type, name)
Definition Value.h:97
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:891
CastKind getCastKind() const
Definition Expr.h:3656
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
QualType getType() const
Definition Expr.h:144
Represents an ObjC class declaration.
Definition DeclObjC.h:1154
Represents a pointer to an Objective C object.
Definition TypeBase.h:7903
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition TypeBase.h:7992
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition Type.cpp:958
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Definition TypeBase.h:7955
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8285
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Stmt - This represents one statement.
Definition Stmt.h:85
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:334
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9101
ASTContext & getASTContext() const
const SourceManager & getSourceManager() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:553
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:98
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR)
Get dynamic type information for the region MR.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.