clang  9.0.0svn
DeadStoresChecker.cpp
Go to the documentation of this file.
1 //==- DeadStoresChecker.cpp - Check for stores to dead variables -*- 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 file defines a DeadStores, a flow-sensitive checker that looks for
10 // stores to variables that are no longer live.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/Attr.h"
17 #include "clang/AST/ParentMap.h"
23 #include "llvm/ADT/BitVector.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/Support/SaveAndRestore.h"
26 
27 using namespace clang;
28 using namespace ento;
29 
30 namespace {
31 
32 /// A simple visitor to record what VarDecls occur in EH-handling code.
33 class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
34 public:
35  bool inEH;
37 
38  bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
39  SaveAndRestore<bool> inFinally(inEH, true);
40  return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
41  }
42 
43  bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
44  SaveAndRestore<bool> inCatch(inEH, true);
45  return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
46  }
47 
48  bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
49  SaveAndRestore<bool> inCatch(inEH, true);
50  return TraverseStmt(S->getHandlerBlock());
51  }
52 
53  bool VisitDeclRefExpr(DeclRefExpr *DR) {
54  if (inEH)
55  if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
56  S.insert(D);
57  return true;
58  }
59 
60  EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
61  inEH(false), S(S) {}
62 };
63 
64 // FIXME: Eventually migrate into its own file, and have it managed by
65 // AnalysisManager.
66 class ReachableCode {
67  const CFG &cfg;
68  llvm::BitVector reachable;
69 public:
70  ReachableCode(const CFG &cfg)
71  : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
72 
73  void computeReachableBlocks();
74 
75  bool isReachable(const CFGBlock *block) const {
76  return reachable[block->getBlockID()];
77  }
78 };
79 }
80 
81 void ReachableCode::computeReachableBlocks() {
82  if (!cfg.getNumBlockIDs())
83  return;
84 
86  worklist.push_back(&cfg.getEntry());
87 
88  while (!worklist.empty()) {
89  const CFGBlock *block = worklist.pop_back_val();
90  llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
91  if (isReachable)
92  continue;
93  isReachable = true;
95  e = block->succ_end(); i != e; ++i)
96  if (const CFGBlock *succ = *i)
97  worklist.push_back(succ);
98  }
99 }
100 
101 static const Expr *
103  while (Ex) {
104  const BinaryOperator *BO =
105  dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
106  if (!BO)
107  break;
108  if (BO->getOpcode() == BO_Assign) {
109  Ex = BO->getRHS();
110  continue;
111  }
112  if (BO->getOpcode() == BO_Comma) {
113  Ex = BO->getRHS();
114  continue;
115  }
116  break;
117  }
118  return Ex;
119 }
120 
121 namespace {
122 class DeadStoreObs : public LiveVariables::Observer {
123  const CFG &cfg;
124  ASTContext &Ctx;
125  BugReporter& BR;
126  const CheckerBase *Checker;
128  ParentMap& Parents;
129  llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
130  std::unique_ptr<ReachableCode> reachableCode;
131  const CFGBlock *currentBlock;
132  std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
133 
134  enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
135 
136 public:
137  DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
138  const CheckerBase *checker, AnalysisDeclContext *ac,
139  ParentMap &parents,
140  llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
141  : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
142  Escaped(escaped), currentBlock(nullptr) {}
143 
144  ~DeadStoreObs() override {}
145 
146  bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
147  if (Live.isLive(D))
148  return true;
149  // Lazily construct the set that records which VarDecls are in
150  // EH code.
151  if (!InEH.get()) {
152  InEH.reset(new llvm::DenseSet<const VarDecl *>());
153  EHCodeVisitor V(*InEH.get());
154  V.TraverseStmt(AC->getBody());
155  }
156  // Treat all VarDecls that occur in EH code as being "always live"
157  // when considering to suppress dead stores. Frequently stores
158  // are followed by reads in EH code, but we don't have the ability
159  // to analyze that yet.
160  return InEH->count(D);
161  }
162 
163  bool isSuppressed(SourceRange R) {
164  SourceManager &SMgr = Ctx.getSourceManager();
165  SourceLocation Loc = R.getBegin();
166  if (!Loc.isValid())
167  return false;
168 
169  FileID FID = SMgr.getFileID(Loc);
170  bool Invalid = false;
171  StringRef Data = SMgr.getBufferData(FID, &Invalid);
172  if (Invalid)
173  return false;
174 
175  // Files autogenerated by DriverKit IIG contain some dead stores that
176  // we don't want to report.
177  if (Data.startswith("/* iig"))
178  return true;
179 
180  return false;
181  }
182 
183  void Report(const VarDecl *V, DeadStoreKind dsk,
184  PathDiagnosticLocation L, SourceRange R) {
185  if (Escaped.count(V))
186  return;
187 
188  // Compute reachable blocks within the CFG for trivial cases
189  // where a bogus dead store can be reported because itself is unreachable.
190  if (!reachableCode.get()) {
191  reachableCode.reset(new ReachableCode(cfg));
192  reachableCode->computeReachableBlocks();
193  }
194 
195  if (!reachableCode->isReachable(currentBlock))
196  return;
197 
198  if (isSuppressed(R))
199  return;
200 
201  SmallString<64> buf;
202  llvm::raw_svector_ostream os(buf);
203  const char *BugType = nullptr;
204 
205  switch (dsk) {
206  case DeadInit:
207  BugType = "Dead initialization";
208  os << "Value stored to '" << *V
209  << "' during its initialization is never read";
210  break;
211 
212  case DeadIncrement:
213  BugType = "Dead increment";
214  LLVM_FALLTHROUGH;
215  case Standard:
216  if (!BugType) BugType = "Dead assignment";
217  os << "Value stored to '" << *V << "' is never read";
218  break;
219 
220  case Enclosing:
221  // Don't report issues in this case, e.g.: "if (x = foo())",
222  // where 'x' is unused later. We have yet to see a case where
223  // this is a real bug.
224  return;
225  }
226 
227  BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
228  L, R);
229  }
230 
231  void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
232  DeadStoreKind dsk,
233  const LiveVariables::LivenessValues &Live) {
234 
235  if (!VD->hasLocalStorage())
236  return;
237  // Reference types confuse the dead stores checker. Skip them
238  // for now.
239  if (VD->getType()->getAs<ReferenceType>())
240  return;
241 
242  if (!isLive(Live, VD) &&
243  !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
244  VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
245 
246  PathDiagnosticLocation ExLoc =
247  PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
248  Report(VD, dsk, ExLoc, Val->getSourceRange());
249  }
250  }
251 
252  void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
253  const LiveVariables::LivenessValues& Live) {
254  if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
255  CheckVarDecl(VD, DR, Val, dsk, Live);
256  }
257 
258  bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
259  if (B->isCompoundAssignmentOp())
260  return true;
261 
262  const Expr *RHS = B->getRHS()->IgnoreParenCasts();
263  const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
264 
265  if (!BRHS)
266  return false;
267 
268  const DeclRefExpr *DR;
269 
270  if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
271  if (DR->getDecl() == VD)
272  return true;
273 
274  if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
275  if (DR->getDecl() == VD)
276  return true;
277 
278  return false;
279  }
280 
281  void observeStmt(const Stmt *S, const CFGBlock *block,
282  const LiveVariables::LivenessValues &Live) override {
283 
284  currentBlock = block;
285 
286  // Skip statements in macros.
287  if (S->getBeginLoc().isMacroID())
288  return;
289 
290  // Only cover dead stores from regular assignments. ++/-- dead stores
291  // have never flagged a real bug.
292  if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
293  if (!B->isAssignmentOp()) return; // Skip non-assignments.
294 
295  if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
296  if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
297  // Special case: check for assigning null to a pointer.
298  // This is a common form of defensive programming.
299  const Expr *RHS =
301  RHS = RHS->IgnoreParenCasts();
302 
303  QualType T = VD->getType();
304  if (T.isVolatileQualified())
305  return;
306  if (T->isPointerType() || T->isObjCObjectPointerType()) {
307  if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
308  return;
309  }
310 
311  // Special case: self-assignments. These are often used to shut up
312  // "unused variable" compiler warnings.
313  if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
314  if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
315  return;
316 
317  // Otherwise, issue a warning.
318  DeadStoreKind dsk = Parents.isConsumedExpr(B)
319  ? Enclosing
320  : (isIncrement(VD,B) ? DeadIncrement : Standard);
321 
322  CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
323  }
324  }
325  else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
326  if (!U->isIncrementOp() || U->isPrefix())
327  return;
328 
329  const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
330  if (!parent || !isa<ReturnStmt>(parent))
331  return;
332 
333  const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
334 
335  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
336  CheckDeclRef(DR, U, DeadIncrement, Live);
337  }
338  else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
339  // Iterate through the decls. Warn if any initializers are complex
340  // expressions that are not live (never used).
341  for (const auto *DI : DS->decls()) {
342  const auto *V = dyn_cast<VarDecl>(DI);
343 
344  if (!V)
345  continue;
346 
347  if (V->hasLocalStorage()) {
348  // Reference types confuse the dead stores checker. Skip them
349  // for now.
350  if (V->getType()->getAs<ReferenceType>())
351  return;
352 
353  if (const Expr *E = V->getInit()) {
354  while (const FullExpr *FE = dyn_cast<FullExpr>(E))
355  E = FE->getSubExpr();
356 
357  // Look through transitive assignments, e.g.:
358  // int x = y = 0;
360 
361  // Don't warn on C++ objects (yet) until we can show that their
362  // constructors/destructors don't have side effects.
363  if (isa<CXXConstructExpr>(E))
364  return;
365 
366  // A dead initialization is a variable that is dead after it
367  // is initialized. We don't flag warnings for those variables
368  // marked 'unused' or 'objc_precise_lifetime'.
369  if (!isLive(Live, V) &&
370  !V->hasAttr<UnusedAttr>() &&
371  !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
372  // Special case: check for initializations with constants.
373  //
374  // e.g. : int x = 0;
375  //
376  // If x is EVER assigned a new value later, don't issue
377  // a warning. This is because such initialization can be
378  // due to defensive programming.
379  if (E->isEvaluatable(Ctx))
380  return;
381 
382  if (const DeclRefExpr *DRE =
383  dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
384  if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
385  // Special case: check for initialization from constant
386  // variables.
387  //
388  // e.g. extern const int MyConstant;
389  // int x = MyConstant;
390  //
391  if (VD->hasGlobalStorage() &&
392  VD->getType().isConstQualified())
393  return;
394  // Special case: check for initialization from scalar
395  // parameters. This is often a form of defensive
396  // programming. Non-scalars are still an error since
397  // because it more likely represents an actual algorithmic
398  // bug.
399  if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
400  return;
401  }
402 
403  PathDiagnosticLocation Loc =
404  PathDiagnosticLocation::create(V, BR.getSourceManager());
405  Report(V, DeadInit, Loc, E->getSourceRange());
406  }
407  }
408  }
409  }
410  }
411 };
412 
413 } // end anonymous namespace
414 
415 //===----------------------------------------------------------------------===//
416 // Driver function to invoke the Dead-Stores checker on a CFG.
417 //===----------------------------------------------------------------------===//
418 
419 namespace {
420 class FindEscaped {
421 public:
422  llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
423 
424  void operator()(const Stmt *S) {
425  // Check for '&'. Any VarDecl whose address has been taken we treat as
426  // escaped.
427  // FIXME: What about references?
428  if (auto *LE = dyn_cast<LambdaExpr>(S)) {
429  findLambdaReferenceCaptures(LE);
430  return;
431  }
432 
433  const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
434  if (!U)
435  return;
436  if (U->getOpcode() != UO_AddrOf)
437  return;
438 
439  const Expr *E = U->getSubExpr()->IgnoreParenCasts();
440  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
441  if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
442  Escaped.insert(VD);
443  }
444 
445  // Treat local variables captured by reference in C++ lambdas as escaped.
446  void findLambdaReferenceCaptures(const LambdaExpr *LE) {
447  const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
448  llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
449  FieldDecl *ThisCaptureField;
450  LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
451 
452  for (const LambdaCapture &C : LE->captures()) {
453  if (!C.capturesVariable())
454  continue;
455 
456  VarDecl *VD = C.getCapturedVar();
457  const FieldDecl *FD = CaptureFields[VD];
458  if (!FD)
459  continue;
460 
461  // If the capture field is a reference type, it is capture-by-reference.
462  if (FD->getType()->isReferenceType())
463  Escaped.insert(VD);
464  }
465  }
466 };
467 } // end anonymous namespace
468 
469 
470 //===----------------------------------------------------------------------===//
471 // DeadStoresChecker
472 //===----------------------------------------------------------------------===//
473 
474 namespace {
475 class DeadStoresChecker : public Checker<check::ASTCodeBody> {
476 public:
477  void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
478  BugReporter &BR) const {
479 
480  // Don't do anything for template instantiations.
481  // Proving that code in a template instantiation is "dead"
482  // means proving that it is dead in all instantiations.
483  // This same problem exists with -Wunreachable-code.
484  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
485  if (FD->isTemplateInstantiation())
486  return;
487 
488  if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
489  CFG &cfg = *mgr.getCFG(D);
490  AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
491  ParentMap &pmap = mgr.getParentMap(D);
492  FindEscaped FS;
493  cfg.VisitBlockStmts(FS);
494  DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
495  L->runOnAllBlocks(A);
496  }
497  }
498 };
499 }
500 
501 void ento::registerDeadStoresChecker(CheckerManager &mgr) {
502  mgr.registerChecker<DeadStoresChecker>();
503 }
504 
505 bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {
506  return true;
507 }
Defines the clang::ASTContext interface.
Represents a function declaration or definition.
Definition: Decl.h:1748
A (possibly-)qualified type.
Definition: Type.h:643
Stmt * getBody() const
Get the body of the Declaration.
succ_iterator succ_begin()
Definition: CFG.h:769
Stmt - This represents one statement.
Definition: Stmt.h:66
unsigned getBlockID() const
Definition: CFG.h:876
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:88
Stmt * getHandlerBlock() const
Definition: StmtCXX.h:51
Opcode getOpcode() const
Definition: Expr.h:3440
Describes the capture of a variable or of this, or of a C++1y init-capture.
Definition: LambdaCapture.h:25
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
bool isConsumedExpr(Expr *E) const
Definition: ParentMap.cpp:159
Represents a variable declaration or definition.
Definition: Decl.h:812
const T * getAs() const
Member-template getAs<specific type>&#39;.
Definition: Type.h:6818
static bool isAssignmentOp(Opcode Opc)
Definition: Expr.h:3531
long i
Definition: xmmintrin.h:1456
CXXRecordDecl * getLambdaClass() const
Retrieve the class that corresponds to the lambda.
Definition: ExprCXX.cpp:1199
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:154
FullExpr - Represents a "full-expression" node.
Definition: Expr.h:920
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:263
Represents a member of a struct/union/class.
Definition: Decl.h:2605
AnalysisDeclContext contains the context data for the function or method under analysis.
bool isReferenceType() const
Definition: Type.h:6363
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
Represents Objective-C&#39;s @catch statement.
Definition: StmtObjC.h:77
AdjacentBlocks::const_iterator const_succ_iterator
Definition: CFG.h:745
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3405
bool isVolatileQualified() const
Determine whether this type is volatile-qualified.
Definition: Type.h:6197
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition: Expr.cpp:2946
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Definition: ExprCXX.h:1722
bool isScalarType() const
Definition: Type.h:6699
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool hasAttr() const
Definition: DeclBase.h:542
Represents a single basic block in a source-level CFG.
Definition: CFG.h:570
This represents one expression.
Definition: Expr.h:108
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt...
Definition: CFG.h:1027
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition: Decl.h:1035
#define V(N, I)
Definition: ASTContext.h:2907
Specifies that a value-dependent expression of integral or dependent type should be considered a null...
Definition: Expr.h:733
UnaryOperator - This represents the unary-expression&#39;s (except sizeof and alignof), the postinc/postdec operators from postfix-expression, and various extensions.
Definition: Expr.h:2016
ValueDecl * getDecl()
Definition: Expr.h:1217
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition: Type.h:6186
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define false
Definition: stdbool.h:17
Encodes a location in the source.
Expr * getSubExpr() const
Definition: Expr.h:2046
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
Definition: Stmt.h:1203
bool hasGlobalStorage() const
Returns true for all variables that do not have local storage.
Definition: Decl.h:1077
const Decl * getDecl() const
bool isObjCObjectPointerType() const
Definition: Type.h:6455
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:1992
succ_iterator succ_end()
Definition: CFG.h:770
Expr * getLHS() const
Definition: Expr.h:3445
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
void VisitBlockStmts(CALLBACK &O) const
Definition: CFG.h:1180
bool isValid() const
Return true if this is a valid SourceLocation object.
static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex)
bool isLive(const Stmt *S) const
const Expr * getInit() const
Definition: Decl.h:1219
bool isMacroID() const
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
Opcode getOpcode() const
Definition: Expr.h:2041
Base for LValueReferenceType and RValueReferenceType.
Definition: Type.h:2690
Represents Objective-C&#39;s @finally statement.
Definition: StmtObjC.h:127
void getCaptureFields(llvm::DenseMap< const VarDecl *, FieldDecl *> &Captures, FieldDecl *&ThisCapture) const
For a closure type, retrieve the mapping from captured variables and this to the non-static data memb...
Definition: DeclCXX.cpp:1416
SourceManager & getSourceManager()
Definition: ASTContext.h:665
Represents a C++ struct/union/class.
Definition: DeclCXX.h:300
static bool isCompoundAssignmentOp(Opcode Opc)
Definition: Expr.h:3536
CXXCatchStmt - This represents a C++ catch block.
Definition: StmtCXX.h:28
capture_range captures() const
Retrieve this lambda&#39;s captures.
Definition: ExprCXX.cpp:1169
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:251
Stmt * getParentIgnoreParenCasts(Stmt *) const
Definition: ParentMap.cpp:133
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1141
Expr * getRHS() const
Definition: Expr.h:3447
bool isPointerType() const
Definition: Type.h:6351
QualType getType() const
Definition: Decl.h:647
A trivial tuple used to represent a source range.
SourceLocation getBegin() const
This class handles loading and caching of source files into memory.