clang  3.9.0
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
42  : public BugReporterVisitorImpl<DynamicTypeBugVisitor> {
43  public:
44  DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
45 
46  void Profile(llvm::FoldingSetNodeID &ID) const override {
47  static int X = 0;
48  ID.AddPointer(&X);
49  ID.AddPointer(Reg);
50  }
51 
52  PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
53  const ExplodedNode *PrevN,
54  BugReporterContext &BRC,
55  BugReport &BR) override;
56 
57  private:
58  // The tracked region.
59  const MemRegion *Reg;
60  };
61 
62  void reportTypeError(QualType DynamicType, QualType StaticType,
63  const MemRegion *Reg, const Stmt *ReportedNode,
64  CheckerContext &C) const;
65 
66 public:
67  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
68 };
69 }
70 
71 void DynamicTypeChecker::reportTypeError(QualType DynamicType,
72  QualType StaticType,
73  const MemRegion *Reg,
74  const Stmt *ReportedNode,
75  CheckerContext &C) const {
76  initBugType();
77  SmallString<192> Buf;
78  llvm::raw_svector_ostream OS(Buf);
79  OS << "Object has a dynamic type '";
80  QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81  llvm::Twine());
82  OS << "' which is incompatible with static type '";
83  QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
84  llvm::Twine());
85  OS << "'";
86  std::unique_ptr<BugReport> R(
87  new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
88  R->markInteresting(Reg);
89  R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
90  R->addRange(ReportedNode->getSourceRange());
91  C.emitReport(std::move(R));
92 }
93 
94 PathDiagnosticPiece *DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
95  const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
96  BugReport &BR) {
98  ProgramStateRef StatePrev = PrevN->getState();
99 
100  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
101  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
102  if (!TrackedType.isValid())
103  return nullptr;
104 
105  if (TrackedTypePrev.isValid() &&
106  TrackedTypePrev.getType() == TrackedType.getType())
107  return nullptr;
108 
109  // Retrieve the associated statement.
110  const Stmt *S = nullptr;
111  ProgramPoint ProgLoc = N->getLocation();
112  if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) {
113  S = SP->getStmt();
114  }
115 
116  if (!S)
117  return nullptr;
118 
119  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
120 
121  SmallString<256> Buf;
122  llvm::raw_svector_ostream OS(Buf);
123  OS << "Type '";
124  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
125  LangOpts, llvm::Twine());
126  OS << "' is inferred from ";
127 
128  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
129  OS << "explicit cast (from '";
130  QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
131  Qualifiers(), OS, LangOpts, llvm::Twine());
132  OS << "' to '";
133  QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
134  LangOpts, llvm::Twine());
135  OS << "')";
136  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
137  OS << "implicit cast (from '";
138  QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
139  Qualifiers(), OS, LangOpts, llvm::Twine());
140  OS << "' to '";
141  QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
142  LangOpts, llvm::Twine());
143  OS << "')";
144  } else {
145  OS << "this context";
146  }
147 
148  // Generate the extra diagnostic.
150  N->getLocationContext());
151  return new PathDiagnosticEventPiece(Pos, OS.str(), true, nullptr);
152 }
153 
154 static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
155  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
156  if (!Decl)
157  return false;
158 
159  return Decl->getDefinition();
160 }
161 
162 // TODO: consider checking explicit casts?
163 void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
164  CheckerContext &C) const {
165  // TODO: C++ support.
166  if (CE->getCastKind() != CK_BitCast)
167  return;
168 
169  const MemRegion *Region = C.getSVal(CE).getAsRegion();
170  if (!Region)
171  return;
172 
173  ProgramStateRef State = C.getState();
174  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
175 
176  if (!DynTypeInfo.isValid())
177  return;
178 
179  QualType DynType = DynTypeInfo.getType();
180  QualType StaticType = CE->getType();
181 
182  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
183  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
184 
185  if (!DynObjCType || !StaticObjCType)
186  return;
187 
188  if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
189  return;
190 
191  ASTContext &ASTCtxt = C.getASTContext();
192 
193  // Strip kindeofness to correctly detect subtyping relationships.
194  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
195  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
196 
197  // Specialized objects are handled by the generics checker.
198  if (StaticObjCType->isSpecialized())
199  return;
200 
201  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
202  return;
203 
204  if (DynTypeInfo.canBeASubClass() &&
205  ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
206  return;
207 
208  reportTypeError(DynType, StaticType, Region, CE, C);
209 }
210 
211 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
212  mgr.registerChecker<DynamicTypeChecker>();
213 }
CastKind getCastKind() const
Definition: Expr.h:2680
A (possibly-)qualified type.
Definition: Type.h:598
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:79
bool canBeASubClass() const
Returns false if the type information is precise (the type T is the only type in the lattice)...
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:5083
The collection of all-type qualifiers we support.
Definition: Type.h:117
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:92
LineState State
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:48
const LangOptions & getLangOpts() const
Definition: ASTContext.h:604
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Definition: Type.h:934
Represents an ObjC class declaration.
Definition: DeclObjC.h:1091
const LocationContext * getLocationContext() const
const ProgramStateRef & getState() const
const ProgramStateRef & getState() const
Stores the currently inferred strictest bound on the runtime type of a region in a given state along ...
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:644
QualType getType() const
Returns the currently inferred upper bound on the runtime type.
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
CHECKER * registerChecker()
Used to register checkers.
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:5259
const std::string ID
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx)
Definition: Type.h:4262
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:2734
QualType getType() const
Definition: Expr.h:126
bool isValid() const
Return false if no dynamic type info is available.
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:1454
const MemRegion * getAsRegion() const
Definition: SVals.cpp:135
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
Definition: ProgramPoint.h:150
Represents a pointer to an Objective C object.
Definition: Type.h:4991
const LangOptions & getLangOpts() const
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:5818
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
Definition: Type.h:5046
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:12171
This class provides an interface through which checkers can create individual bug reports...
Definition: BugReporter.h:55
SourceManager & getSourceManager()
Definition: BugReporter.h:550
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.