<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Should be fixed in trunk<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Aug 3, 2018, at 1:30 PM, George Karpenkov via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" class="">cfe-commits@lists.llvm.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=us-ascii" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Yeah, saw that as well, fix coming.<br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class="">On Aug 3, 2018, at 10:32 AM, Alexander Kornienko <<a href="mailto:alexfh@google.com" class="">alexfh@google.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><br class=""><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Thu, Aug 2, 2018 at 4:03 AM George Karpenkov via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" class="">cfe-commits@lists.llvm.org</a>> wrote:<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: george.karpenkov<br class="">
Date: Wed Aug 1 19:02:40 2018<br class="">
New Revision: 338667<br class="">
<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=338667&view=rev" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project?rev=338667&view=rev</a><br class="">
Log:<br class="">
[analyzer] Extend NoStoreFuncVisitor to follow fields.<br class="">
<br class="">
<a href="rdar://39701823" class="">rdar://39701823</a><br class="">
<br class="">
Differential Revision: <a href="https://reviews.llvm.org/D49901" rel="noreferrer" target="_blank" class="">https://reviews.llvm.org/D49901</a><br class="">
<br class="">
Modified:<br class="">
cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp<br class="">
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c<br class="">
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp<br class="">
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m<br class="">
<br class="">
Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp<br class="">
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=338667&r1=338666&r2=338667&view=diff" rel="noreferrer" target="_blank" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=338667&r1=338666&r2=338667&view=diff</a><br class="">
==============================================================================<br class="">
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original)<br class="">
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Wed Aug 1 19:02:40 2018<br class="">
@@ -269,10 +269,14 @@ namespace {<br class="">
/// pointer dereference outside.<br class="">
class NoStoreFuncVisitor final : public BugReporterVisitor {<br class="">
const SubRegion *RegionOfInterest;<br class="">
+ MemRegionManager &MmrMgr;<br class="">
const SourceManager &SM;<br class="">
const PrintingPolicy &PP;<br class="">
- static constexpr const char *DiagnosticsMsg =<br class="">
- "Returning without writing to '";<br class="">
+<br class="">
+ /// Recursion limit for dereferencing fields when looking for the<br class="">
+ /// region of interest.<br class="">
+ /// The limit of two indicates that we will dereference fields only once.<br class="">
+ static const unsigned DEREFERENCE_LIMIT = 2;<br class="">
<br class="">
/// Frames writing into \c RegionOfInterest.<br class="">
/// This visitor generates a note only if a function does not write into<br class="">
@@ -285,15 +289,17 @@ class NoStoreFuncVisitor final : public<br class="">
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;<br class="">
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;<br class="">
<br class="">
+ using RegionVector = SmallVector<const MemRegion *, 5>;<br class="">
public:<br class="">
NoStoreFuncVisitor(const SubRegion *R)<br class="">
- : RegionOfInterest(R),<br class="">
- SM(R->getMemRegionManager()->getContext().getSourceManager()),<br class="">
- PP(R->getMemRegionManager()->getContext().getPrintingPolicy()) {}<br class="">
+ : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),<br class="">
+ SM(MmrMgr.getContext().getSourceManager()),<br class="">
+ PP(MmrMgr.getContext().getPrintingPolicy()) {}<br class="">
<br class="">
void Profile(llvm::FoldingSetNodeID &ID) const override {<br class="">
static int Tag = 0;<br class="">
ID.AddPointer(&Tag);<br class="">
+ ID.AddPointer(RegionOfInterest);<br class="">
}<br class="">
<br class="">
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,<br class="">
@@ -307,48 +313,69 @@ public:<br class="">
auto CallExitLoc = N->getLocationAs<CallExitBegin>();<br class="">
<br class="">
// No diagnostic if region was modified inside the frame.<br class="">
- if (!CallExitLoc)<br class="">
+ if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))<br class="">
return nullptr;<br class="">
<br class="">
CallEventRef<> Call =<br class="">
BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);<br class="">
<br class="">
+ if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin()))<br class="">
+ return nullptr;<br class="">
+<br class="">
// Region of interest corresponds to an IVar, exiting a method<br class="">
// which could have written into that IVar, but did not.<br class="">
- if (const auto *MC = dyn_cast<ObjCMethodCall>(Call))<br class="">
- if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest))<br class="">
- if (potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),<br class="">
- IvarR->getDecl()) &&<br class="">
- !isRegionOfInterestModifiedInFrame(N))<br class="">
- return notModifiedMemberDiagnostics(<br class="">
- Ctx, *CallExitLoc, Call, MC->getReceiverSVal().getAsRegion());<br class="">
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {<br class="">
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {<br class="">
+ const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion();<br class="">
+ if (RegionOfInterest->isSubRegionOf(SelfRegion) &&<br class="">
+ potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),<br class="">
+ IvarR->getDecl()))<br class="">
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion,<br class="">
+ "self", /*FirstIsReferenceType=*/false,<br class="">
+ 1);<br class="">
+ }<br class="">
+ }<br class="">
<br class="">
if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {<br class="">
const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();<br class="">
if (RegionOfInterest->isSubRegionOf(ThisR)<br class="">
- && !CCall->getDecl()->isImplicit()<br class="">
- && !isRegionOfInterestModifiedInFrame(N))<br class="">
- return notModifiedMemberDiagnostics(Ctx, *CallExitLoc, Call, ThisR);<br class="">
+ && !CCall->getDecl()->isImplicit())<br class="">
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR,<br class="">
+ "this",<br class="">
+ /*FirstIsReferenceType=*/false, 1);<br class="">
+<br class="">
+ // Do not generate diagnostics for not modified parameters in<br class="">
+ // constructors.<br class="">
+ return nullptr;<br class="">
}<br class="">
<br class="">
ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);<br class="">
for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {<br class="">
- const ParmVarDecl *PVD = parameters[I];<br class="">
+ Optional<unsigned> AdjustedIdx = Call->getAdjustedParameterIndex(I);<br class="">
+ if (!AdjustedIdx)<br class="">
+ continue;<br class="">
+ const ParmVarDecl *PVD = parameters[*AdjustedIdx];<br class="">
SVal S = Call->getArgSVal(I);<br class="">
- unsigned IndirectionLevel = 1;<br class="">
+ bool ParamIsReferenceType = PVD->getType()->isReferenceType();<br class="">
+ std::string ParamName = PVD->getNameAsString();<br class="">
+<br class="">
+ int IndirectionLevel = 1;<br class="">
QualType T = PVD->getType();<br class="">
while (const MemRegion *R = S.getAsRegion()) {<br class="">
- if (RegionOfInterest->isSubRegionOf(R)<br class="">
- && !isPointerToConst(PVD->getType())) {<br class="">
-<br class="">
- if (isRegionOfInterestModifiedInFrame(N))<br class="">
- return nullptr;<br class="">
+ if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T))<br class="">
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R,<br class="">
+ ParamName, ParamIsReferenceType,<br class="">
+ IndirectionLevel);<br class="">
<br class="">
- return notModifiedParameterDiagnostics(<br class="">
- Ctx, *CallExitLoc, Call, PVD, R, IndirectionLevel);<br class="">
- }<br class="">
QualType PT = T->getPointeeType();<br class="">
if (PT.isNull() || PT->isVoidType()) break;<br class="">
+<br class="">
+ if (const RecordDecl *RD = PT->getAsRecordDecl())<br class="">
+ if (auto P = findRegionOfInterestInRecord(RD, State, R))<br class="">
+ return notModifiedDiagnostics(<br class="">
+ Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName,<br class="">
+ ParamIsReferenceType, IndirectionLevel);<br class="">
+<br class="">
S = State->getSVal(R, PT);<br class="">
T = PT;<br class="">
IndirectionLevel++;<br class="">
@@ -359,20 +386,94 @@ public:<br class="">
}<br class="">
<br class="">
private:<br class="">
+ /// Attempts to find the region of interest in a given CXX decl,<br class="">
+ /// by either following the base classes or fields.<br class="">
+ /// Dereferences fields up to a given recursion limit.<br class="">
+ /// Note that \p Vec is passed by value, leading to quadratic copying cost,<br class="">
+ /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.<br class="">
+ /// \return A chain fields leading to the region of interest or None.<br class="">
+ const Optional<RegionVector><br class="">
+ findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,<br class="">
+ const MemRegion *R,<br class="">
+ RegionVector Vec = {},<br class="">
+ int depth = 0) {<br class="">
+<br class="">
+ if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.<br class="">
+ return None;<br class="">
+<br class="">
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))<br class="">
+ if (!RDX->hasDefinition())<br class="">
+ return None;<br class="">
+<br class="">
+ // Recursively examine the base classes.<br class="">
+ // Note that following base classes does not increase the recursion depth.<br class="">
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))<br class="">
+ for (const auto II : RDX->bases())<br class="">
+ if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())<br class="">
+ if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth))<br class="">
+ return Out;<br class="">
+<br class="">
+ for (const FieldDecl *I : RD->fields()) {<br class="">
+ QualType FT = I->getType();<br class="">
+ const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R));<br class="">
+ const SVal V = State->getSVal(FR);<br class="">
+ const MemRegion *VR = V.getAsRegion();<br class="">
+<br class="">
+ RegionVector VecF = Vec;<br class="">
+ VecF.push_back(FR);<br class="">
+<br class="">
+ if (RegionOfInterest == VR)<br class="">
+ return VecF;<br class="">
+<br class="">
+ if (const RecordDecl *RRD = FT->getAsRecordDecl())<br class="">
+ if (auto Out =<br class="">
+ findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1))<br class="">
+ return Out;<br class="">
+<br class="">
+ QualType PT = FT->getPointeeType();<br class="">
+ if (PT.isNull() || PT->isVoidType() || !VR) continue;<br class="">
+<br class="">
+ if (const RecordDecl *RRD = PT->getAsRecordDecl())<br class="">
+ if (auto Out =<br class="">
+ findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))<br class="">
+ return Out;<br class="">
+<br class="">
+ }<br class="">
+<br class="">
+ return None;<br class="">
+ }<br class="">
<br class="">
/// \return Whether the method declaration \p Parent<br class="">
/// syntactically has a binary operation writing into the ivar \p Ivar.<br class="">
bool potentiallyWritesIntoIvar(const Decl *Parent,<br class="">
const ObjCIvarDecl *Ivar) {<br class="">
using namespace ast_matchers;<br class="">
- if (!Parent || !Parent->getBody())<br class="">
+ const char * IvarBind = "Ivar";<br class="">
+ if (!Parent || !Parent->hasBody())<br class="">
return false;<br class="">
StatementMatcher WriteIntoIvarM = binaryOperator(<br class="">
- hasOperatorName("="), hasLHS(ignoringParenImpCasts(objcIvarRefExpr(<br class="">
- hasDeclaration(equalsNode(Ivar))))));<br class="">
+ hasOperatorName("="),<br class="">
+ hasLHS(ignoringParenImpCasts(<br class="">
+ objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind))));<br class="">
StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));<br class="">
auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());<br class="">
- return !Matches.empty();<br class="">
+ for (BoundNodes &Match : Matches) {<br class="">
+ auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind);<br class="">
+ if (IvarRef->isFreeIvar())<br class="">
+ return true;<br class="">
+<br class="">
+ const Expr *Base = IvarRef->getBase();<br class="">
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))<br class="">
+ Base = ICE->getSubExpr();<br class="">
+<br class="">
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))<br class="">
+ if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))<br class="">
+ if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf)<br class="">
+ return true;<br class="">
+<br class="">
+ return false;<br class="">
+ }<br class="">
+ return false;<br class="">
}<br class="">
<br class="">
/// Check and lazily calculate whether the region of interest is<br class="">
@@ -433,6 +534,8 @@ private:<br class="">
RuntimeDefinition RD = Call->getRuntimeDefinition();<br class="">
if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))<br class="">
return FD->parameters();<br class="">
+ if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl()))<br class="">
+ return MD->parameters();<br class="">
<br class="">
return Call->parameters();<br class="">
}<br class="">
@@ -443,123 +546,105 @@ private:<br class="">
Ty->getPointeeType().getCanonicalType().isConstQualified();<br class="">
}<br class="">
<br class="">
- /// \return Diagnostics piece for the member field not modified<br class="">
- /// in a given function.<br class="">
- std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics(<br class="">
- const LocationContext *Ctx,<br class="">
- CallExitBegin &CallExitLoc,<br class="">
- CallEventRef<> Call,<br class="">
- const MemRegion *ArgRegion) {<br class="">
- const char *TopRegionName = isa<ObjCMethodCall>(Call) ? "self" : "this";<br class="">
- SmallString<256> sbuf;<br class="">
- llvm::raw_svector_ostream os(sbuf);<br class="">
- os << DiagnosticsMsg;<br class="">
- bool out = prettyPrintRegionName(TopRegionName, "->", /*IsReference=*/true,<br class="">
- /*IndirectionLevel=*/1, ArgRegion, os, PP);<br class="">
-<br class="">
- // Return nothing if we have failed to pretty-print.<br class="">
- if (!out)<br class="">
- return nullptr;<br class="">
-<br class="">
- os << "'";<br class="">
- PathDiagnosticLocation L =<br class="">
- getPathDiagnosticLocation(CallExitLoc.getReturnStmt(), SM, Ctx, Call);<br class="">
- return std::make_shared<PathDiagnosticEventPiece>(L, os.str());<br class="">
- }<br class="">
-<br class="">
- /// \return Diagnostics piece for the parameter \p PVD not modified<br class="">
- /// in a given function.<br class="">
- /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced<br class="">
- /// before we get to the super region of \c RegionOfInterest<br class="">
+ /// \return Diagnostics piece for region not modified in the current function.<br class="">
std::shared_ptr<PathDiagnosticPiece><br class="">
- notModifiedParameterDiagnostics(const LocationContext *Ctx,<br class="">
+ notModifiedDiagnostics(const LocationContext *Ctx,<br class="">
CallExitBegin &CallExitLoc,<br class="">
CallEventRef<> Call,<br class="">
- const ParmVarDecl *PVD,<br class="">
- const MemRegion *ArgRegion,<br class="">
+ RegionVector FieldChain,<br class="">
+ const MemRegion *MatchedRegion,<br class="">
+ StringRef FirstElement,<br class="">
+ bool FirstIsReferenceType,<br class="">
unsigned IndirectionLevel) {<br class="">
- PathDiagnosticLocation L = getPathDiagnosticLocation(<br class="">
- CallExitLoc.getReturnStmt(), SM, Ctx, Call);<br class="">
+<br class="">
+ PathDiagnosticLocation L;<br class="">
+ if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {<br class="">
+ L = PathDiagnosticLocation::createBegin(RS, SM, Ctx);<br class="">
+ } else {<br class="">
+ L = PathDiagnosticLocation(<br class="">
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(),<br class="">
+ SM);<br class="">
+ }<br class="">
+<br class="">
SmallString<256> sbuf;<br class="">
llvm::raw_svector_ostream os(sbuf);<br class="">
- os << DiagnosticsMsg;<br class="">
- bool IsReference = PVD->getType()->isReferenceType();<br class="">
- const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->";<br class="">
- bool Success = prettyPrintRegionName(<br class="">
- PVD->getQualifiedNameAsString().c_str(),<br class="">
- Sep, IsReference, IndirectionLevel, ArgRegion, os, PP);<br class="">
-<br class="">
- // Print the parameter name if the pretty-printing has failed.<br class="">
- if (!Success)<br class="">
- PVD->printQualifiedName(os);<br class="">
+ os << "Returning without writing to '";<br class="">
+ prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion,<br class="">
+ FieldChain, IndirectionLevel, os);<br class="">
+<br class="">
os << "'";<br class="">
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());<br class="">
}<br class="">
<br class="">
- /// \return a path diagnostic location for the optionally<br class="">
- /// present return statement \p RS.<br class="">
- PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS,<br class="">
- const SourceManager &SM,<br class="">
- const LocationContext *Ctx,<br class="">
- CallEventRef<> Call) {<br class="">
- if (RS)<br class="">
- return PathDiagnosticLocation::createBegin(RS, SM, Ctx);<br class="">
- return PathDiagnosticLocation(<br class="">
- Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);<br class="">
- }<br class="">
-<br class="">
- /// Pretty-print region \p ArgRegion starting from parent to \p os.<br class="">
- /// \return whether printing has succeeded<br class="">
- bool prettyPrintRegionName(StringRef TopRegionName,<br class="">
- StringRef Sep,<br class="">
- bool IsReference,<br class="">
- int IndirectionLevel,<br class="">
- const MemRegion *ArgRegion,<br class="">
- llvm::raw_svector_ostream &os,<br class="">
- const PrintingPolicy &PP) {<br class="">
- SmallVector<const MemRegion *, 5> Subregions;<br class="">
+ /// Pretty-print region \p MatchedRegion to \p os.<br class="">
+ void prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType,<br class="">
+ const MemRegion *MatchedRegion,<br class="">
+ RegionVector FieldChain, int IndirectionLevel,<br class="">
+ llvm::raw_svector_ostream &os) {<br class="">
+<br class="">
+ if (FirstIsReferenceType)<br class="">
+ IndirectionLevel--;<br class="">
+<br class="">
+ RegionVector RegionSequence;<br class="">
+<br class="">
+ // Add the regions in the reverse order, then reverse the resulting array.<br class="">
+ assert(RegionOfInterest->isSubRegionOf(MatchedRegion));<br class="">
const MemRegion *R = RegionOfInterest;<br class="">
- while (R != ArgRegion) {<br class="">
- if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R) ||<br class="">
- isa<ObjCIvarRegion>(R)))<br class="">
- return false; // Pattern-matching failed.<br class="">
- Subregions.push_back(R);<br class="">
+ while (R != MatchedRegion) {<br class="">
+ RegionSequence.push_back(R);<br class="">
R = cast<SubRegion>(R)->getSuperRegion();<br class="">
}<br class="">
- bool IndirectReference = !Subregions.empty();<br class="">
+ std::reverse(RegionSequence.begin(), RegionSequence.end());<br class="">
+ RegionSequence.append(FieldChain.begin(), FieldChain.end());<br class="">
<br class="">
- if (IndirectReference)<br class="">
- IndirectionLevel--; // Due to "->" symbol.<br class="">
+ StringRef Sep;<br class="">
+ for (const MemRegion *R : RegionSequence) {<br class="">
<br class="">
- if (IsReference)<br class="">
- IndirectionLevel--; // Due to reference semantics.<br class="">
+ // Just keep going up to the base region.<br class="">
+ if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))<br class="">
+ continue;<br class="">
+<br class="">
+ if (Sep.empty())<br class="">
+ Sep = prettyPrintFirstElement(FirstElement,<br class="">
+ /*MoreItemsExpected=*/true,<br class="">
+ IndirectionLevel, os);<br class="">
+<br class="">
+ os << Sep;<br class="">
+<br class="">
+ const auto *DR = cast<DeclRegion>(R);<br class="">
+ Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";<br class="">
+ DR->getDecl()->getDeclName().print(os, PP);<br class=""></blockquote><div class=""><br class=""></div><div class="">We have crashes on this line no test case yet.</div></div></div>
</div></blockquote></div><br class=""></div>
_______________________________________________<br class="">cfe-commits mailing list<br class=""><a href="mailto:cfe-commits@lists.llvm.org" class="">cfe-commits@lists.llvm.org</a><br class="">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits<br class=""></div></blockquote></div><br class=""></body></html>