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 
28 using namespace clang;
29 using namespace ento;
30 
31 namespace {
32 class 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,
46  BugReporterContext &BRC,
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 
58 public:
59  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
60 };
61 }
62 
63 void DynamicTypeChecker::reportTypeError(QualType DynamicType,
64  QualType StaticType,
65  const MemRegion *Reg,
66  const Stmt *ReportedNode,
67  CheckerContext &C) const {
68  SmallString<192> Buf;
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 
85 PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
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 
106  SmallString<256> Buf;
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 
139 static 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?
148 void 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 
196 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
197  mgr.registerChecker<DynamicTypeChecker>();
198 }
199 
200 bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
201  return true;
202 }
static char ID
Definition: Arena.cpp:183
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
#define X(type, name)
Definition: Value.h:143
LineState State
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
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:778
CastKind getCastKind() const
Definition: Expr.h:3579
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
QualType getType() const
Definition: Expr.h:142
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3707
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:482
Represents an ObjC class declaration.
Definition: DeclObjC.h:1153
Represents a pointer to an Objective C object.
Definition: Type.h:7020
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:7109
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:910
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
Definition: Type.h:7072
A (possibly-)qualified type.
Definition: Type.h:940
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:7371
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:318
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:8160
const SourceManager & getSourceManager() const
Definition: BugReporter.h:737
ASTContext & getASTContext() const
Definition: BugReporter.h:733
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.
ExplodedNode * getFirstPred()
const ProgramStateRef & getState() const
const LocationContext * getLocationContext() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
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.