clang 19.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 const BugType BT{this, "Dynamic and static type mismatch", "Type Error"};
34
35 class DynamicTypeBugVisitor : public BugReporterVisitor {
36 public:
37 DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
38
39 void Profile(llvm::FoldingSetNodeID &ID) const override {
40 static int X = 0;
41 ID.AddPointer(&X);
42 ID.AddPointer(Reg);
43 }
44
45 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
47 PathSensitiveBugReport &BR) override;
48
49 private:
50 // The tracked region.
51 const MemRegion *Reg;
52 };
53
54 void reportTypeError(QualType DynamicType, QualType StaticType,
55 const MemRegion *Reg, const Stmt *ReportedNode,
56 CheckerContext &C) const;
57
58public:
59 void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
60};
61}
62
63void DynamicTypeChecker::reportTypeError(QualType DynamicType,
64 QualType StaticType,
65 const MemRegion *Reg,
66 const Stmt *ReportedNode,
67 CheckerContext &C) const {
69 llvm::raw_svector_ostream OS(Buf);
70 OS << "Object has a dynamic type '";
71 QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
72 llvm::Twine());
73 OS << "' which is incompatible with static type '";
74 QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
75 llvm::Twine());
76 OS << "'";
77 auto R = std::make_unique<PathSensitiveBugReport>(
78 BT, OS.str(), C.generateNonFatalErrorNode());
79 R->markInteresting(Reg);
80 R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
81 R->addRange(ReportedNode->getSourceRange());
82 C.emitReport(std::move(R));
83}
84
85PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
87 ProgramStateRef State = N->getState();
88 ProgramStateRef StatePrev = N->getFirstPred()->getState();
89
90 DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
91 DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
92 if (!TrackedType.isValid())
93 return nullptr;
94
95 if (TrackedTypePrev.isValid() &&
96 TrackedTypePrev.getType() == TrackedType.getType())
97 return nullptr;
98
99 // Retrieve the associated statement.
100 const Stmt *S = N->getStmtForDiagnostics();
101 if (!S)
102 return nullptr;
103
104 const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
105
107 llvm::raw_svector_ostream OS(Buf);
108 OS << "Type '";
109 QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
110 LangOpts, llvm::Twine());
111 OS << "' is inferred from ";
112
113 if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
114 OS << "explicit cast (from '";
115 QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
116 Qualifiers(), OS, LangOpts, llvm::Twine());
117 OS << "' to '";
118 QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
119 LangOpts, llvm::Twine());
120 OS << "')";
121 } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
122 OS << "implicit cast (from '";
123 QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
124 Qualifiers(), OS, LangOpts, llvm::Twine());
125 OS << "' to '";
126 QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
127 LangOpts, llvm::Twine());
128 OS << "')";
129 } else {
130 OS << "this context";
131 }
132
133 // Generate the extra diagnostic.
135 N->getLocationContext());
136 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
137}
138
139static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
140 const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
141 if (!Decl)
142 return false;
143
144 return Decl->getDefinition();
145}
146
147// TODO: consider checking explicit casts?
148void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
149 CheckerContext &C) const {
150 // TODO: C++ support.
151 if (CE->getCastKind() != CK_BitCast)
152 return;
153
154 const MemRegion *Region = C.getSVal(CE).getAsRegion();
155 if (!Region)
156 return;
157
158 ProgramStateRef State = C.getState();
159 DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
160
161 if (!DynTypeInfo.isValid())
162 return;
163
164 QualType DynType = DynTypeInfo.getType();
165 QualType StaticType = CE->getType();
166
167 const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
168 const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
169
170 if (!DynObjCType || !StaticObjCType)
171 return;
172
173 if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
174 return;
175
176 ASTContext &ASTCtxt = C.getASTContext();
177
178 // Strip kindeofness to correctly detect subtyping relationships.
179 DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
180 StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
181
182 // Specialized objects are handled by the generics checker.
183 if (StaticObjCType->isSpecialized())
184 return;
185
186 if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
187 return;
188
189 if (DynTypeInfo.canBeASubClass() &&
190 ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
191 return;
192
193 reportTypeError(DynType, StaticType, Region, CE, C);
194}
195
196void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
197 mgr.registerChecker<DynamicTypeChecker>();
198}
199
200bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
201 return true;
202}
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
#define X(type, name)
Definition: Value.h:143
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:772
CastKind getCastKind() const
Definition: Expr.h:3534
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:3662
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:449
Represents an ObjC class declaration.
Definition: DeclObjC.h:1150
Represents a pointer to an Objective C object.
Definition: Type.h:6768
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:6857
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:899
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Definition: Type.h:6820
A (possibly-)qualified type.
Definition: Type.h:738
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:7119
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:148
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:326
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7878
ASTContext & getASTContext() const
Definition: BugReporter.h:733
const SourceManager & getSourceManager() const
Definition: BugReporter.h:737
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
The JSON file list parser is used to communicate input to InstallAPI.