clang  10.0.0svn
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  mutable std::unique_ptr<BugType> BT;
34  void initBugType() const {
35  if (!BT)
36  BT.reset(
37  new BugType(this, "Dynamic and static type mismatch", "Type Error"));
38  }
39 
40  class DynamicTypeBugVisitor : public BugReporterVisitor {
41  public:
42  DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
43 
44  void Profile(llvm::FoldingSetNodeID &ID) const override {
45  static int X = 0;
46  ID.AddPointer(&X);
47  ID.AddPointer(Reg);
48  }
49 
50  PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
51  BugReporterContext &BRC,
52  PathSensitiveBugReport &BR) override;
53 
54  private:
55  // The tracked region.
56  const MemRegion *Reg;
57  };
58 
59  void reportTypeError(QualType DynamicType, QualType StaticType,
60  const MemRegion *Reg, const Stmt *ReportedNode,
61  CheckerContext &C) const;
62 
63 public:
64  void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
65 };
66 }
67 
68 void DynamicTypeChecker::reportTypeError(QualType DynamicType,
69  QualType StaticType,
70  const MemRegion *Reg,
71  const Stmt *ReportedNode,
72  CheckerContext &C) const {
73  initBugType();
74  SmallString<192> Buf;
75  llvm::raw_svector_ostream OS(Buf);
76  OS << "Object has a dynamic type '";
77  QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
78  llvm::Twine());
79  OS << "' which is incompatible with static type '";
80  QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
81  llvm::Twine());
82  OS << "'";
83  auto R = std::make_unique<PathSensitiveBugReport>(
84  *BT, OS.str(), C.generateNonFatalErrorNode());
85  R->markInteresting(Reg);
86  R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
87  R->addRange(ReportedNode->getSourceRange());
88  C.emitReport(std::move(R));
89 }
90 
91 PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
92  const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
93  ProgramStateRef State = N->getState();
94  ProgramStateRef StatePrev = N->getFirstPred()->getState();
95 
96  DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
97  DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
98  if (!TrackedType.isValid())
99  return nullptr;
100 
101  if (TrackedTypePrev.isValid() &&
102  TrackedTypePrev.getType() == TrackedType.getType())
103  return nullptr;
104 
105  // Retrieve the associated statement.
106  const Stmt *S = N->getStmtForDiagnostics();
107  if (!S)
108  return nullptr;
109 
110  const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
111 
112  SmallString<256> Buf;
113  llvm::raw_svector_ostream OS(Buf);
114  OS << "Type '";
115  QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
116  LangOpts, llvm::Twine());
117  OS << "' is inferred from ";
118 
119  if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
120  OS << "explicit cast (from '";
121  QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
122  Qualifiers(), OS, LangOpts, llvm::Twine());
123  OS << "' to '";
124  QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
125  LangOpts, llvm::Twine());
126  OS << "')";
127  } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
128  OS << "implicit cast (from '";
129  QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
130  Qualifiers(), OS, LangOpts, llvm::Twine());
131  OS << "' to '";
132  QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
133  LangOpts, llvm::Twine());
134  OS << "')";
135  } else {
136  OS << "this context";
137  }
138 
139  // Generate the extra diagnostic.
140  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
141  N->getLocationContext());
142  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
143 }
144 
145 static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
146  const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
147  if (!Decl)
148  return false;
149 
150  return Decl->getDefinition();
151 }
152 
153 // TODO: consider checking explicit casts?
154 void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
155  CheckerContext &C) const {
156  // TODO: C++ support.
157  if (CE->getCastKind() != CK_BitCast)
158  return;
159 
160  const MemRegion *Region = C.getSVal(CE).getAsRegion();
161  if (!Region)
162  return;
163 
164  ProgramStateRef State = C.getState();
165  DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
166 
167  if (!DynTypeInfo.isValid())
168  return;
169 
170  QualType DynType = DynTypeInfo.getType();
171  QualType StaticType = CE->getType();
172 
173  const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
174  const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
175 
176  if (!DynObjCType || !StaticObjCType)
177  return;
178 
179  if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
180  return;
181 
182  ASTContext &ASTCtxt = C.getASTContext();
183 
184  // Strip kindeofness to correctly detect subtyping relationships.
185  DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
186  StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
187 
188  // Specialized objects are handled by the generics checker.
189  if (StaticObjCType->isSpecialized())
190  return;
191 
192  if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
193  return;
194 
195  if (DynTypeInfo.canBeASubClass() &&
196  ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
197  return;
198 
199  reportTypeError(DynType, StaticType, Region, CE, C);
200 }
201 
202 void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
203  mgr.registerChecker<DynamicTypeChecker>();
204 }
205 
206 bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) {
207  return true;
208 }
A (possibly-)qualified type.
Definition: Type.h:643
Stmt - This represents one statement.
Definition: Stmt.h:66
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const T * getAs() const
Member-template getAs<specific type>&#39;.
Definition: Type.h:6857
The collection of all-type qualifiers we support.
Definition: Type.h:137
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
LineState State
bool isSpecialized() const
Whether this type is specialized, meaning that it has type arguments.
Definition: Type.h:5963
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
const ObjCObjectPointerType * stripObjCKindOfTypeAndQuals(const ASTContext &ctx) const
Strip off the Objective-C "kindof" type and (with it) any protocol qualifiers.
Definition: Type.cpp:730
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition: Type.h:6148
Represents an ObjC class declaration.
Definition: DeclObjC.h:1171
QualType getType() const
Definition: Expr.h:137
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr)
CastKind getCastKind() const
Definition: Expr.h:3166
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3244
Dataflow Directional Tag Classes.
ObjCInterfaceDecl * getDefinition()
Retrieve the definition of this class, or NULL if this class has been forward-declared (with @class) ...
Definition: DeclObjC.h:1548
Represents a pointer to an Objective C object.
Definition: Type.h:5870
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
Definition: Type.h:5926
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR)
Get dynamic type information for the region MR.
Definition: DynamicType.cpp:40
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:14579
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:262