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,
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 {
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(
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
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.
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:145
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
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:894
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
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3789
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:434
Represents an ObjC class declaration.
Definition: DeclObjC.h:1154
Represents a pointer to an Objective C object.
Definition: TypeBase.h:7961
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: TypeBase.h:8050
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:8013
A (possibly-)qualified type.
Definition: TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: TypeBase.h:8343
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: TypeBase.h:331
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:9159
ASTContext & getASTContext() const
Definition: BugReporter.h:740
const SourceManager & getSourceManager() const
Definition: BugReporter.h:744
BugReporterVisitors are used to add custom diagnostics along a path.
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
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:98
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.