clang  8.0.0svn
DynamicTypeChecker.cpp
Go to the documentation of this file.
1 //== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This checker looks for cases where the dynamic type of an object is unrelated
11 // to its static type. The type information utilized by this check is collected
12 // by the DynamicTypePropagation checker. This check does not report any type
13 // error for ObjC Generic types, in order to avoid duplicate erros from the
14 // ObjC Generics checker. This checker is not supposed to modify the program
15 // state, it is just the observer of the type information provided by other
16 // checkers.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "ClangSACheckers.h"
28 
29 using namespace clang;
30 using namespace ento;
31 
32 namespace {
33 class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
34  mutable std::unique_ptr<BugType> BT;
35  void initBugType() const {
36  if (!BT)
37  BT.reset(
38  new BugType(this, "Dynamic and static type mismatch", "Type Error"));
39  }
40 
41  class DynamicTypeBugVisitor : public BugReporterVisitor {
42  public:
43  DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
44 
45  void Profile(llvm::FoldingSetNodeID &ID) const override {
46  static int X = 0;
47  ID.AddPointer(&X);
48  ID.AddPointer(Reg);
49  }
50 
51  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
52  const ExplodedNode *PrevN,
53  BugReporterContext &BRC,
54  BugReport &BR) override;
55 
56  private:
57  // The tracked region.
58  const MemRegion *Reg;
59  };
60 
61  void reportTypeError(QualType DynamicType, QualType StaticType,
62  const MemRegion *Reg, const Stmt *ReportedNode,
63  CheckerContext &C) const;
64 
65 public:
66  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
67 };
68 }
69 
70 void DynamicTypeChecker::reportTypeError(QualType DynamicType,
71  QualType StaticType,
72  const MemRegion *Reg,
73  const Stmt *ReportedNode,
74  CheckerContext &C) const {
75  initBugType();
76  SmallString<192> Buf;
77  llvm::raw_svector_ostream OS(Buf);
78  OS << "Object has a dynamic type '";
79  QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
80  llvm::Twine());
81  OS << "' which is incompatible with static type '";
82  QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
83  llvm::Twine());
84  OS << "'";
85  std::unique_ptr<BugReport> R(
86  new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
87  R->markInteresting(Reg);
88  R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
89  R->addRange(ReportedNode->getSourceRange());
90  C.emitReport(std::move(R));
91 }
92 
93 std::shared_ptr<PathDiagnosticPiece>
94 DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
95  const ExplodedNode *PrevN,
96  BugReporterContext &BRC,
97  BugReport &BR) {
98  ProgramStateRef State = N->getState();
99  ProgramStateRef StatePrev = PrevN->getState();
100 
101  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
102  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
103  if (!TrackedType.isValid())
104  return nullptr;
105 
106  if (TrackedTypePrev.isValid() &&
107  TrackedTypePrev.getType() == TrackedType.getType())
108  return nullptr;
109 
110  // Retrieve the associated statement.
112  if (!S)
113  return nullptr;
114 
115  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
116 
117  SmallString<256> Buf;
118  llvm::raw_svector_ostream OS(Buf);
119  OS << "Type '";
120  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
121  LangOpts, llvm::Twine());
122  OS << "' is inferred from ";
123 
124  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
125  OS << "explicit cast (from '";
126  QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
127  Qualifiers(), OS, LangOpts, llvm::Twine());
128  OS << "' to '";
129  QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
130  LangOpts, llvm::Twine());
131  OS << "')";
132  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
133  OS << "implicit cast (from '";
134  QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
135  Qualifiers(), OS, LangOpts, llvm::Twine());
136  OS << "' to '";
137  QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
138  LangOpts, llvm::Twine());
139  OS << "')";
140  } else {
141  OS << "this context";
142  }
143 
144  // Generate the extra diagnostic.
145  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
146  N->getLocationContext());
147  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
148  nullptr);
149 }
150 
151 static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
152  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
153  if (!Decl)
154  return false;
155 
156  return Decl->getDefinition();
157 }
158 
159 // TODO: consider checking explicit casts?
160 void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
161  CheckerContext &C) const {
162  // TODO: C++ support.
163  if (CE->getCastKind() != CK_BitCast)
164  return;
165 
166  const MemRegion *Region = C.getSVal(CE).getAsRegion();
167  if (!Region)
168  return;
169 
170  ProgramStateRef State = C.getState();
171  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
172 
173  if (!DynTypeInfo.isValid())
174  return;
175 
176  QualType DynType = DynTypeInfo.getType();
177  QualType StaticType = CE->getType();
178 
179  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
180  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
181 
182  if (!DynObjCType || !StaticObjCType)
183  return;
184 
185  if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
186  return;
187 
188  ASTContext &ASTCtxt = C.getASTContext();
189 
190  // Strip kindeofness to correctly detect subtyping relationships.
191  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
192  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
193 
194  // Specialized objects are handled by the generics checker.
195  if (StaticObjCType->isSpecialized())
196  return;
197 
198  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
199  return;
200 
201  if (DynTypeInfo.canBeASubClass() &&
202  ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
203  return;
204 
205  reportTypeError(DynType, StaticType, Region, CE, C);
206 }
207 
208 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
209  mgr.registerChecker<DynamicTypeChecker>();
210 }
A (possibly-)qualified type.
Definition: Type.h:642
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Definition: Type.h:985
Stmt - This represents one statement.
Definition: Stmt.h:66
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:87
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const T * getAs() const
Member-template getAs<specific type>&#39;.
Definition: Type.h:6590
The collection of all-type qualifiers we support.
Definition: Type.h:141
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
LineState State
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:5768
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:50
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:709
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:5953
Represents an ObjC class declaration.
Definition: DeclObjC.h:1164
QualType getType() const
Definition: Expr.h:127
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
CastKind getCastKind() const
Definition: Expr.h:2849
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:2924
Dataflow Directional Tag Classes.
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg)
Get dynamic type information for a region.
ObjCInterfaceDecl * getDefinition()
Retrieve the definition of this class, or NULL if this class has been forward-declared (with @class) ...
Definition: DeclObjC.h:1541
Represents a pointer to an Objective C object.
Definition: Type.h:5675
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
Definition: Type.h:5731
bool canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT, const ObjCObjectPointerType *RHSOPT)
canAssignObjCInterfaces - Return true if the two interface types are compatible for assignment from R...
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Definition: SemaDecl.cpp:13824
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:268