clang 23.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(), N->getStackFrame());
133 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
134}
135
136static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
137 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
138 if (!Decl)
139 return false;
140
141 return Decl->getDefinition();
142}
143
144// TODO: consider checking explicit casts?
145void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
146 CheckerContext &C) const {
147 // TODO: C++ support.
148 if (CE->getCastKind() != CK_BitCast)
149 return;
150
151 const MemRegion *Region = C.getSVal(CE).getAsRegion();
152 if (!Region)
153 return;
154
155 ProgramStateRef State = C.getState();
156 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
157
158 if (!DynTypeInfo.isValid())
159 return;
160
161 QualType DynType = DynTypeInfo.getType();
162 QualType StaticType = CE->getType();
163
164 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
165 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
166
167 if (!DynObjCType || !StaticObjCType)
168 return;
169
170 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
171 return;
172
173 ASTContext &ASTCtxt = C.getASTContext();
174
175 // Strip kindeofness to correctly detect subtyping relationships.
176 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
177 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
178
179 // Specialized objects are handled by the generics checker.
180 if (StaticObjCType->isSpecialized())
181 return;
182
183 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
184 return;
185
186 if (DynTypeInfo.canBeASubClass() &&
187 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
188 return;
189
190 reportTypeError(DynType, StaticType, Region, CE, C);
191}
192
193void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
194 mgr.registerChecker<DynamicTypeChecker>();
195}
196
197bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
198 return true;
199}
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:959
CastKind getCastKind() const
Definition Expr.h:3723
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:8063
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition TypeBase.h:8152
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition Type.cpp:996
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Definition TypeBase.h:8115
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8445
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Stmt - This represents one statement.
Definition Stmt.h:86
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9275
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:565
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.
ExplodedNode * getFirstPred()
const StackFrame * getStackFrame() const
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:97
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.