BugReporterVisitors.cpp revision 0cd59482abd8aec9ed1eaad11f5fe9c1e42639f6
1// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- 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 file defines a set of BugReporter "visitors" which can be used to 11// enhance the diagnostics reported for a bug. 12// 13//===----------------------------------------------------------------------===// 14#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" 15 16#include "clang/AST/Expr.h" 17#include "clang/AST/ExprObjC.h" 18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 22#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 23 24using namespace clang; 25using namespace ento; 26 27//===----------------------------------------------------------------------===// 28// Utility functions. 29//===----------------------------------------------------------------------===// 30 31const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { 32 // Pattern match for a few useful cases (do something smarter later): 33 // a[0], p->f, *p 34 const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); 35 36 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { 37 if (U->getOpcode() == UO_Deref) 38 return U->getSubExpr()->IgnoreParenCasts(); 39 } 40 else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { 41 return ME->getBase()->IgnoreParenCasts(); 42 } 43 else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { 44 return AE->getBase(); 45 } 46 47 return NULL; 48} 49 50const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { 51 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); 52 if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S)) 53 return BE->getRHS(); 54 return NULL; 55} 56 57const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) { 58 // Callee is checked as a PreVisit to the CallExpr. 59 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); 60 if (const CallExpr *CE = dyn_cast<CallExpr>(S)) 61 return CE->getCallee(); 62 return NULL; 63} 64 65const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { 66 const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); 67 if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) 68 return RS->getRetValue(); 69 return NULL; 70} 71 72//===----------------------------------------------------------------------===// 73// Definitions for bug reporter visitors. 74//===----------------------------------------------------------------------===// 75 76PathDiagnosticPiece* 77BugReporterVisitor::getEndPath(BugReporterContext &BRC, 78 const ExplodedNode *EndPathNode, 79 BugReport &BR) { 80 return 0; 81} 82 83PathDiagnosticPiece* 84BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, 85 const ExplodedNode *EndPathNode, 86 BugReport &BR) { 87 const ProgramPoint &PP = EndPathNode->getLocation(); 88 PathDiagnosticLocation L; 89 90 if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { 91 const CFGBlock *block = BE->getBlock(); 92 if (block->getBlockID() == 0) { 93 L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), 94 BRC.getSourceManager()); 95 } 96 } 97 98 if (!L.isValid()) { 99 const Stmt *S = BR.getStmt(); 100 101 if (!S) 102 return NULL; 103 104 L = PathDiagnosticLocation(S, BRC.getSourceManager(), 105 PP.getLocationContext()); 106 } 107 108 BugReport::ranges_iterator Beg, End; 109 llvm::tie(Beg, End) = BR.getRanges(); 110 111 // Only add the statement itself as a range if we didn't specify any 112 // special ranges for this report. 113 PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, 114 BR.getDescription(), 115 Beg == End); 116 for (; Beg != End; ++Beg) 117 P->addRange(*Beg); 118 119 return P; 120} 121 122 123void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { 124 static int tag = 0; 125 ID.AddPointer(&tag); 126 ID.AddPointer(R); 127 ID.Add(V); 128} 129 130PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, 131 const ExplodedNode *PrevN, 132 BugReporterContext &BRC, 133 BugReport &BR) { 134 135 if (satisfied) 136 return NULL; 137 138 if (!StoreSite) { 139 const ExplodedNode *Node = N, *Last = NULL; 140 141 for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { 142 143 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 144 if (const PostStmt *P = Node->getLocationAs<PostStmt>()) 145 if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) 146 if (DS->getSingleDecl() == VR->getDecl()) { 147 Last = Node; 148 break; 149 } 150 } 151 152 if (Node->getState()->getSVal(R) != V) 153 break; 154 } 155 156 if (!Node || !Last) { 157 satisfied = true; 158 return NULL; 159 } 160 161 StoreSite = Last; 162 } 163 164 if (StoreSite != N) 165 return NULL; 166 167 satisfied = true; 168 llvm::SmallString<256> sbuf; 169 llvm::raw_svector_ostream os(sbuf); 170 171 if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { 172 if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { 173 174 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 175 os << "Variable '" << VR->getDecl() << "' "; 176 } 177 else 178 return NULL; 179 180 if (isa<loc::ConcreteInt>(V)) { 181 bool b = false; 182 if (R->isBoundable()) { 183 if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { 184 if (TR->getValueType()->isObjCObjectPointerType()) { 185 os << "initialized to nil"; 186 b = true; 187 } 188 } 189 } 190 191 if (!b) 192 os << "initialized to a null pointer value"; 193 } 194 else if (isa<nonloc::ConcreteInt>(V)) { 195 os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); 196 } 197 else if (V.isUndef()) { 198 if (isa<VarRegion>(R)) { 199 const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); 200 if (VD->getInit()) 201 os << "initialized to a garbage value"; 202 else 203 os << "declared without an initial value"; 204 } 205 } 206 } 207 } 208 209 if (os.str().empty()) { 210 if (isa<loc::ConcreteInt>(V)) { 211 bool b = false; 212 if (R->isBoundable()) { 213 if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { 214 if (TR->getValueType()->isObjCObjectPointerType()) { 215 os << "nil object reference stored to "; 216 b = true; 217 } 218 } 219 } 220 221 if (!b) 222 os << "Null pointer value stored to "; 223 } 224 else if (V.isUndef()) { 225 os << "Uninitialized value stored to "; 226 } 227 else if (isa<nonloc::ConcreteInt>(V)) { 228 os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() 229 << " is assigned to "; 230 } 231 else 232 return NULL; 233 234 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { 235 os << '\'' << VR->getDecl() << '\''; 236 } 237 else 238 return NULL; 239 } 240 241 // Construct a new PathDiagnosticPiece. 242 ProgramPoint P = N->getLocation(); 243 PathDiagnosticLocation L = PathDiagnosticLocation(P,BRC.getSourceManager()); 244 if (!L.isValid()) 245 return NULL; 246 return new PathDiagnosticEventPiece(L, os.str()); 247} 248 249void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { 250 static int tag = 0; 251 ID.AddPointer(&tag); 252 ID.AddBoolean(Assumption); 253 ID.Add(Constraint); 254} 255 256PathDiagnosticPiece * 257TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, 258 const ExplodedNode *PrevN, 259 BugReporterContext &BRC, 260 BugReport &BR) { 261 if (isSatisfied) 262 return NULL; 263 264 // Check if in the previous state it was feasible for this constraint 265 // to *not* be true. 266 if (PrevN->getState()->assume(Constraint, !Assumption)) { 267 268 isSatisfied = true; 269 270 // As a sanity check, make sure that the negation of the constraint 271 // was infeasible in the current state. If it is feasible, we somehow 272 // missed the transition point. 273 if (N->getState()->assume(Constraint, !Assumption)) 274 return NULL; 275 276 // We found the transition point for the constraint. We now need to 277 // pretty-print the constraint. (work-in-progress) 278 std::string sbuf; 279 llvm::raw_string_ostream os(sbuf); 280 281 if (isa<Loc>(Constraint)) { 282 os << "Assuming pointer value is "; 283 os << (Assumption ? "non-null" : "null"); 284 } 285 286 if (os.str().empty()) 287 return NULL; 288 289 // Construct a new PathDiagnosticPiece. 290 ProgramPoint P = N->getLocation(); 291 PathDiagnosticLocation L = PathDiagnosticLocation(P,BRC.getSourceManager()); 292 if (!L.isValid()) 293 return NULL; 294 return new PathDiagnosticEventPiece(L, os.str()); 295 } 296 297 return NULL; 298} 299 300BugReporterVisitor * 301bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, 302 const Stmt *S) { 303 if (!S || !N) 304 return 0; 305 306 ProgramStateManager &StateMgr = N->getState()->getStateManager(); 307 308 // Walk through nodes until we get one that matches the statement 309 // exactly. 310 while (N) { 311 const ProgramPoint &pp = N->getLocation(); 312 if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { 313 if (ps->getStmt() == S) 314 break; 315 } 316 N = *N->pred_begin(); 317 } 318 319 if (!N) 320 return 0; 321 322 const ProgramState *state = N->getState(); 323 324 // Walk through lvalue-to-rvalue conversions. 325 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { 326 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 327 const VarRegion *R = 328 StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); 329 330 // What did we load? 331 SVal V = state->getSVal(loc::MemRegionVal(R)); 332 333 if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) 334 || V.isUndef()) { 335 return new FindLastStoreBRVisitor(V, R); 336 } 337 } 338 } 339 340 SVal V = state->getSValAsScalarOrLoc(S); 341 342 // Uncomment this to find cases where we aren't properly getting the 343 // base value that was dereferenced. 344 // assert(!V.isUnknownOrUndef()); 345 346 // Is it a symbolic value? 347 if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { 348 const SubRegion *R = cast<SubRegion>(L->getRegion()); 349 while (R && !isa<SymbolicRegion>(R)) { 350 R = dyn_cast<SubRegion>(R->getSuperRegion()); 351 } 352 353 if (R) { 354 assert(isa<SymbolicRegion>(R)); 355 return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); 356 } 357 } 358 359 return 0; 360} 361 362BugReporterVisitor * 363FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, 364 const MemRegion *R) { 365 assert(R && "The memory region is null."); 366 367 const ProgramState *state = N->getState(); 368 SVal V = state->getSVal(R); 369 if (V.isUnknown()) 370 return 0; 371 372 return new FindLastStoreBRVisitor(V, R); 373} 374 375 376PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, 377 const ExplodedNode *PrevN, 378 BugReporterContext &BRC, 379 BugReport &BR) { 380 const PostStmt *P = N->getLocationAs<PostStmt>(); 381 if (!P) 382 return 0; 383 const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); 384 if (!ME) 385 return 0; 386 const Expr *Receiver = ME->getInstanceReceiver(); 387 if (!Receiver) 388 return 0; 389 const ProgramState *state = N->getState(); 390 const SVal &V = state->getSVal(Receiver); 391 const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); 392 if (!DV) 393 return 0; 394 state = state->assume(*DV, true); 395 if (state) 396 return 0; 397 398 // The receiver was nil, and hence the method was skipped. 399 // Register a BugReporterVisitor to issue a message telling us how 400 // the receiver was null. 401 BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver)); 402 // Issue a message saying that the method was skipped. 403 PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), 404 N->getLocationContext()); 405 return new PathDiagnosticEventPiece(L, "No method actually called " 406 "because the receiver is nil"); 407} 408 409// Registers every VarDecl inside a Stmt with a last store visitor. 410void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, 411 const Stmt *S) { 412 const ExplodedNode *N = BR.getErrorNode(); 413 std::deque<const Stmt *> WorkList; 414 WorkList.push_back(S); 415 416 while (!WorkList.empty()) { 417 const Stmt *Head = WorkList.front(); 418 WorkList.pop_front(); 419 420 const ProgramState *state = N->getState(); 421 ProgramStateManager &StateMgr = state->getStateManager(); 422 423 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { 424 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { 425 const VarRegion *R = 426 StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); 427 428 // What did we load? 429 SVal V = state->getSVal(S); 430 431 if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { 432 // Register a new visitor with the BugReport. 433 BR.addVisitor(new FindLastStoreBRVisitor(V, R)); 434 } 435 } 436 } 437 438 for (Stmt::const_child_iterator I = Head->child_begin(); 439 I != Head->child_end(); ++I) 440 WorkList.push_back(*I); 441 } 442} 443 444//===----------------------------------------------------------------------===// 445// Visitor that tries to report interesting diagnostics from conditions. 446//===----------------------------------------------------------------------===// 447PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, 448 const ExplodedNode *Prev, 449 BugReporterContext &BRC, 450 BugReport &BR) { 451 452 const ProgramPoint &progPoint = N->getLocation(); 453 454 const ProgramState *CurrentState = N->getState(); 455 const ProgramState *PrevState = Prev->getState(); 456 457 // Compare the GDMs of the state, because that is where constraints 458 // are managed. Note that ensure that we only look at nodes that 459 // were generated by the analyzer engine proper, not checkers. 460 if (CurrentState->getGDM().getRoot() == 461 PrevState->getGDM().getRoot()) 462 return 0; 463 464 // If an assumption was made on a branch, it should be caught 465 // here by looking at the state transition. 466 if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { 467 const CFGBlock *srcBlk = BE->getSrc(); 468 if (const Stmt *term = srcBlk->getTerminator()) 469 return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC); 470 return 0; 471 } 472 473 if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) { 474 // FIXME: Assuming that BugReporter is a GRBugReporter is a layering 475 // violation. 476 const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = 477 cast<GRBugReporter>(BRC.getBugReporter()). 478 getEngine().getEagerlyAssumeTags(); 479 480 const ProgramPointTag *tag = PS->getTag(); 481 if (tag == tags.first) 482 return VisitTrueTest(cast<Expr>(PS->getStmt()), true, 483 BRC, N->getLocationContext()); 484 if (tag == tags.second) 485 return VisitTrueTest(cast<Expr>(PS->getStmt()), false, 486 BRC, N->getLocationContext()); 487 488 return 0; 489 } 490 491 return 0; 492} 493 494PathDiagnosticPiece * 495ConditionBRVisitor::VisitTerminator(const Stmt *Term, 496 const ExplodedNode *N, 497 const CFGBlock *srcBlk, 498 const CFGBlock *dstBlk, 499 BugReporterContext &BRC) { 500 const Expr *Cond = 0; 501 502 switch (Term->getStmtClass()) { 503 default: 504 return 0; 505 case Stmt::IfStmtClass: 506 Cond = cast<IfStmt>(Term)->getCond(); 507 break; 508 case Stmt::ConditionalOperatorClass: 509 Cond = cast<ConditionalOperator>(Term)->getCond(); 510 break; 511 } 512 513 assert(Cond); 514 assert(srcBlk->succ_size() == 2); 515 const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; 516 return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()), 517 tookTrue, BRC, N->getLocationContext()); 518} 519 520PathDiagnosticPiece * 521ConditionBRVisitor::VisitTrueTest(const Expr *Cond, 522 bool tookTrue, 523 BugReporterContext &BRC, 524 const LocationContext *LC) { 525 526 const Expr *Ex = Cond; 527 528 while (true) { 529 Ex = Ex->IgnoreParens(); 530 switch (Ex->getStmtClass()) { 531 default: 532 return 0; 533 case Stmt::BinaryOperatorClass: 534 return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC); 535 case Stmt::DeclRefExprClass: 536 return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC); 537 case Stmt::UnaryOperatorClass: { 538 const UnaryOperator *UO = cast<UnaryOperator>(Ex); 539 if (UO->getOpcode() == UO_LNot) { 540 tookTrue = !tookTrue; 541 Ex = UO->getSubExpr()->IgnoreParenNoopCasts(BRC.getASTContext()); 542 continue; 543 } 544 return 0; 545 } 546 } 547 } 548} 549 550bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, 551 BugReporterContext &BRC) { 552 const Expr *OriginalExpr = Ex; 553 Ex = Ex->IgnoreParenCasts(); 554 555 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { 556 const bool quotes = isa<VarDecl>(DR->getDecl()); 557 if (quotes) 558 Out << '\''; 559 Out << DR->getDecl()->getDeclName().getAsString(); 560 if (quotes) 561 Out << '\''; 562 return quotes; 563 } 564 565 if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) { 566 QualType OriginalTy = OriginalExpr->getType(); 567 if (OriginalTy->isPointerType()) { 568 if (IL->getValue() == 0) { 569 Out << "null"; 570 return false; 571 } 572 } 573 else if (OriginalTy->isObjCObjectPointerType()) { 574 if (IL->getValue() == 0) { 575 Out << "nil"; 576 return false; 577 } 578 } 579 580 Out << IL->getValue(); 581 return false; 582 } 583 584 return false; 585} 586 587PathDiagnosticPiece * 588ConditionBRVisitor::VisitTrueTest(const Expr *Cond, 589 const BinaryOperator *BExpr, 590 const bool tookTrue, 591 BugReporterContext &BRC, 592 const LocationContext *LC) { 593 594 bool shouldInvert = false; 595 596 llvm::SmallString<128> LhsString, RhsString; 597 { 598 llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); 599 const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC); 600 const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC); 601 602 shouldInvert = !isVarLHS && isVarRHS; 603 } 604 605 if (LhsString.empty() || RhsString.empty()) 606 return 0; 607 608 // Should we invert the strings if the LHS is not a variable name? 609 610 llvm::SmallString<256> buf; 611 llvm::raw_svector_ostream Out(buf); 612 Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; 613 614 // Do we need to invert the opcode? 615 BinaryOperator::Opcode Op = BExpr->getOpcode(); 616 617 if (shouldInvert) 618 switch (Op) { 619 default: break; 620 case BO_LT: Op = BO_GT; break; 621 case BO_GT: Op = BO_LT; break; 622 case BO_LE: Op = BO_GE; break; 623 case BO_GE: Op = BO_LE; break; 624 } 625 626 if (!tookTrue) 627 switch (Op) { 628 case BO_EQ: Op = BO_NE; break; 629 case BO_NE: Op = BO_EQ; break; 630 case BO_LT: Op = BO_GE; break; 631 case BO_GT: Op = BO_LE; break; 632 case BO_LE: Op = BO_GT; break; 633 case BO_GE: Op = BO_LT; break; 634 default: 635 return 0; 636 } 637 638 switch (BExpr->getOpcode()) { 639 case BO_EQ: 640 Out << "equal to "; 641 break; 642 case BO_NE: 643 Out << "not equal to "; 644 break; 645 default: 646 Out << BinaryOperator::getOpcodeStr(Op) << ' '; 647 break; 648 } 649 650 Out << (shouldInvert ? LhsString : RhsString); 651 652 PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); 653 return new PathDiagnosticEventPiece(Loc, Out.str()); 654} 655 656PathDiagnosticPiece * 657ConditionBRVisitor::VisitTrueTest(const Expr *Cond, 658 const DeclRefExpr *DR, 659 const bool tookTrue, 660 BugReporterContext &BRC, 661 const LocationContext *LC) { 662 663 const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); 664 if (!VD) 665 return 0; 666 667 llvm::SmallString<256> Buf; 668 llvm::raw_svector_ostream Out(Buf); 669 670 Out << "Assuming '"; 671 VD->getDeclName().printName(Out); 672 Out << "' is "; 673 674 QualType VDTy = VD->getType(); 675 676 if (VDTy->isPointerType()) 677 Out << (tookTrue ? "non-null" : "null"); 678 else if (VDTy->isObjCObjectPointerType()) 679 Out << (tookTrue ? "non-nil" : "nil"); 680 else if (VDTy->isScalarType()) 681 Out << (tookTrue ? "not equal to 0" : "0"); 682 else 683 return 0; 684 685 PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); 686 return new PathDiagnosticEventPiece(Loc, Out.str()); 687} 688