r338667 - [analyzer] Extend NoStoreFuncVisitor to follow fields.
George Karpenkov via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 1 19:02:40 PDT 2018
Author: george.karpenkov
Date: Wed Aug 1 19:02:40 2018
New Revision: 338667
URL: http://llvm.org/viewvc/llvm-project?rev=338667&view=rev
Log:
[analyzer] Extend NoStoreFuncVisitor to follow fields.
rdar://39701823
Differential Revision: https://reviews.llvm.org/D49901
Modified:
cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=338667&r1=338666&r2=338667&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Wed Aug 1 19:02:40 2018
@@ -269,10 +269,14 @@ namespace {
/// pointer dereference outside.
class NoStoreFuncVisitor final : public BugReporterVisitor {
const SubRegion *RegionOfInterest;
+ MemRegionManager &MmrMgr;
const SourceManager &SM;
const PrintingPolicy &PP;
- static constexpr const char *DiagnosticsMsg =
- "Returning without writing to '";
+
+ /// Recursion limit for dereferencing fields when looking for the
+ /// region of interest.
+ /// The limit of two indicates that we will dereference fields only once.
+ static const unsigned DEREFERENCE_LIMIT = 2;
/// Frames writing into \c RegionOfInterest.
/// This visitor generates a note only if a function does not write into
@@ -285,15 +289,17 @@ class NoStoreFuncVisitor final : public
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
+ using RegionVector = SmallVector<const MemRegion *, 5>;
public:
NoStoreFuncVisitor(const SubRegion *R)
- : RegionOfInterest(R),
- SM(R->getMemRegionManager()->getContext().getSourceManager()),
- PP(R->getMemRegionManager()->getContext().getPrintingPolicy()) {}
+ : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
+ SM(MmrMgr.getContext().getSourceManager()),
+ PP(MmrMgr.getContext().getPrintingPolicy()) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
ID.AddPointer(&Tag);
+ ID.AddPointer(RegionOfInterest);
}
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
@@ -307,48 +313,69 @@ public:
auto CallExitLoc = N->getLocationAs<CallExitBegin>();
// No diagnostic if region was modified inside the frame.
- if (!CallExitLoc)
+ if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
return nullptr;
CallEventRef<> Call =
BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
+ if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin()))
+ return nullptr;
+
// Region of interest corresponds to an IVar, exiting a method
// which could have written into that IVar, but did not.
- if (const auto *MC = dyn_cast<ObjCMethodCall>(Call))
- if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest))
- if (potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
- IvarR->getDecl()) &&
- !isRegionOfInterestModifiedInFrame(N))
- return notModifiedMemberDiagnostics(
- Ctx, *CallExitLoc, Call, MC->getReceiverSVal().getAsRegion());
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
+ const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
+ potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
+ IvarR->getDecl()))
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion,
+ "self", /*FirstIsReferenceType=*/false,
+ 1);
+ }
+ }
if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
if (RegionOfInterest->isSubRegionOf(ThisR)
- && !CCall->getDecl()->isImplicit()
- && !isRegionOfInterestModifiedInFrame(N))
- return notModifiedMemberDiagnostics(Ctx, *CallExitLoc, Call, ThisR);
+ && !CCall->getDecl()->isImplicit())
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR,
+ "this",
+ /*FirstIsReferenceType=*/false, 1);
+
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return nullptr;
}
ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
- const ParmVarDecl *PVD = parameters[I];
+ Optional<unsigned> AdjustedIdx = Call->getAdjustedParameterIndex(I);
+ if (!AdjustedIdx)
+ continue;
+ const ParmVarDecl *PVD = parameters[*AdjustedIdx];
SVal S = Call->getArgSVal(I);
- unsigned IndirectionLevel = 1;
+ bool ParamIsReferenceType = PVD->getType()->isReferenceType();
+ std::string ParamName = PVD->getNameAsString();
+
+ int IndirectionLevel = 1;
QualType T = PVD->getType();
while (const MemRegion *R = S.getAsRegion()) {
- if (RegionOfInterest->isSubRegionOf(R)
- && !isPointerToConst(PVD->getType())) {
-
- if (isRegionOfInterestModifiedInFrame(N))
- return nullptr;
+ if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T))
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R,
+ ParamName, ParamIsReferenceType,
+ IndirectionLevel);
- return notModifiedParameterDiagnostics(
- Ctx, *CallExitLoc, Call, PVD, R, IndirectionLevel);
- }
QualType PT = T->getPointeeType();
if (PT.isNull() || PT->isVoidType()) break;
+
+ if (const RecordDecl *RD = PT->getAsRecordDecl())
+ if (auto P = findRegionOfInterestInRecord(RD, State, R))
+ return notModifiedDiagnostics(
+ Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
+
S = State->getSVal(R, PT);
T = PT;
IndirectionLevel++;
@@ -359,20 +386,94 @@ public:
}
private:
+ /// Attempts to find the region of interest in a given CXX decl,
+ /// by either following the base classes or fields.
+ /// Dereferences fields up to a given recursion limit.
+ /// Note that \p Vec is passed by value, leading to quadratic copying cost,
+ /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
+ /// \return A chain fields leading to the region of interest or None.
+ const Optional<RegionVector>
+ findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,
+ const MemRegion *R,
+ RegionVector Vec = {},
+ int depth = 0) {
+
+ if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
+ return None;
+
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
+ if (!RDX->hasDefinition())
+ return None;
+
+ // Recursively examine the base classes.
+ // Note that following base classes does not increase the recursion depth.
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
+ for (const auto II : RDX->bases())
+ if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
+ if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
+ return Out;
+
+ for (const FieldDecl *I : RD->fields()) {
+ QualType FT = I->getType();
+ const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R));
+ const SVal V = State->getSVal(FR);
+ const MemRegion *VR = V.getAsRegion();
+
+ RegionVector VecF = Vec;
+ VecF.push_back(FR);
+
+ if (RegionOfInterest == VR)
+ return VecF;
+
+ if (const RecordDecl *RRD = FT->getAsRecordDecl())
+ if (auto Out =
+ findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1))
+ return Out;
+
+ QualType PT = FT->getPointeeType();
+ if (PT.isNull() || PT->isVoidType() || !VR) continue;
+
+ if (const RecordDecl *RRD = PT->getAsRecordDecl())
+ if (auto Out =
+ findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
+ return Out;
+
+ }
+
+ return None;
+ }
/// \return Whether the method declaration \p Parent
/// syntactically has a binary operation writing into the ivar \p Ivar.
bool potentiallyWritesIntoIvar(const Decl *Parent,
const ObjCIvarDecl *Ivar) {
using namespace ast_matchers;
- if (!Parent || !Parent->getBody())
+ const char * IvarBind = "Ivar";
+ if (!Parent || !Parent->hasBody())
return false;
StatementMatcher WriteIntoIvarM = binaryOperator(
- hasOperatorName("="), hasLHS(ignoringParenImpCasts(objcIvarRefExpr(
- hasDeclaration(equalsNode(Ivar))))));
+ hasOperatorName("="),
+ hasLHS(ignoringParenImpCasts(
+ objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind))));
StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());
- return !Matches.empty();
+ for (BoundNodes &Match : Matches) {
+ auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind);
+ if (IvarRef->isFreeIvar())
+ return true;
+
+ const Expr *Base = IvarRef->getBase();
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))
+ Base = ICE->getSubExpr();
+
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))
+ if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
+ if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf)
+ return true;
+
+ return false;
+ }
+ return false;
}
/// Check and lazily calculate whether the region of interest is
@@ -433,6 +534,8 @@ private:
RuntimeDefinition RD = Call->getRuntimeDefinition();
if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))
return FD->parameters();
+ if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl()))
+ return MD->parameters();
return Call->parameters();
}
@@ -443,123 +546,105 @@ private:
Ty->getPointeeType().getCanonicalType().isConstQualified();
}
- /// \return Diagnostics piece for the member field not modified
- /// in a given function.
- std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics(
- const LocationContext *Ctx,
- CallExitBegin &CallExitLoc,
- CallEventRef<> Call,
- const MemRegion *ArgRegion) {
- const char *TopRegionName = isa<ObjCMethodCall>(Call) ? "self" : "this";
- SmallString<256> sbuf;
- llvm::raw_svector_ostream os(sbuf);
- os << DiagnosticsMsg;
- bool out = prettyPrintRegionName(TopRegionName, "->", /*IsReference=*/true,
- /*IndirectionLevel=*/1, ArgRegion, os, PP);
-
- // Return nothing if we have failed to pretty-print.
- if (!out)
- return nullptr;
-
- os << "'";
- PathDiagnosticLocation L =
- getPathDiagnosticLocation(CallExitLoc.getReturnStmt(), SM, Ctx, Call);
- return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
- }
-
- /// \return Diagnostics piece for the parameter \p PVD not modified
- /// in a given function.
- /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
- /// before we get to the super region of \c RegionOfInterest
+ /// \return Diagnostics piece for region not modified in the current function.
std::shared_ptr<PathDiagnosticPiece>
- notModifiedParameterDiagnostics(const LocationContext *Ctx,
+ notModifiedDiagnostics(const LocationContext *Ctx,
CallExitBegin &CallExitLoc,
CallEventRef<> Call,
- const ParmVarDecl *PVD,
- const MemRegion *ArgRegion,
+ RegionVector FieldChain,
+ const MemRegion *MatchedRegion,
+ StringRef FirstElement,
+ bool FirstIsReferenceType,
unsigned IndirectionLevel) {
- PathDiagnosticLocation L = getPathDiagnosticLocation(
- CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+
+ PathDiagnosticLocation L;
+ if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
+ L = PathDiagnosticLocation::createBegin(RS, SM, Ctx);
+ } else {
+ L = PathDiagnosticLocation(
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(),
+ SM);
+ }
+
SmallString<256> sbuf;
llvm::raw_svector_ostream os(sbuf);
- os << DiagnosticsMsg;
- bool IsReference = PVD->getType()->isReferenceType();
- const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->";
- bool Success = prettyPrintRegionName(
- PVD->getQualifiedNameAsString().c_str(),
- Sep, IsReference, IndirectionLevel, ArgRegion, os, PP);
-
- // Print the parameter name if the pretty-printing has failed.
- if (!Success)
- PVD->printQualifiedName(os);
+ os << "Returning without writing to '";
+ prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion,
+ FieldChain, IndirectionLevel, os);
+
os << "'";
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
- /// \return a path diagnostic location for the optionally
- /// present return statement \p RS.
- PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS,
- const SourceManager &SM,
- const LocationContext *Ctx,
- CallEventRef<> Call) {
- if (RS)
- return PathDiagnosticLocation::createBegin(RS, SM, Ctx);
- return PathDiagnosticLocation(
- Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
- }
-
- /// Pretty-print region \p ArgRegion starting from parent to \p os.
- /// \return whether printing has succeeded
- bool prettyPrintRegionName(StringRef TopRegionName,
- StringRef Sep,
- bool IsReference,
- int IndirectionLevel,
- const MemRegion *ArgRegion,
- llvm::raw_svector_ostream &os,
- const PrintingPolicy &PP) {
- SmallVector<const MemRegion *, 5> Subregions;
+ /// Pretty-print region \p MatchedRegion to \p os.
+ void prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType,
+ const MemRegion *MatchedRegion,
+ RegionVector FieldChain, int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
+
+ if (FirstIsReferenceType)
+ IndirectionLevel--;
+
+ RegionVector RegionSequence;
+
+ // Add the regions in the reverse order, then reverse the resulting array.
+ assert(RegionOfInterest->isSubRegionOf(MatchedRegion));
const MemRegion *R = RegionOfInterest;
- while (R != ArgRegion) {
- if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R) ||
- isa<ObjCIvarRegion>(R)))
- return false; // Pattern-matching failed.
- Subregions.push_back(R);
+ while (R != MatchedRegion) {
+ RegionSequence.push_back(R);
R = cast<SubRegion>(R)->getSuperRegion();
}
- bool IndirectReference = !Subregions.empty();
+ std::reverse(RegionSequence.begin(), RegionSequence.end());
+ RegionSequence.append(FieldChain.begin(), FieldChain.end());
- if (IndirectReference)
- IndirectionLevel--; // Due to "->" symbol.
+ StringRef Sep;
+ for (const MemRegion *R : RegionSequence) {
- if (IsReference)
- IndirectionLevel--; // Due to reference semantics.
+ // Just keep going up to the base region.
+ if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))
+ continue;
+
+ if (Sep.empty())
+ Sep = prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/true,
+ IndirectionLevel, os);
+
+ os << Sep;
+
+ const auto *DR = cast<DeclRegion>(R);
+ Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
+ DR->getDecl()->getDeclName().print(os, PP);
+ }
+
+ if (Sep.empty())
+ prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/false, IndirectionLevel,
+ os);
+ }
- bool ShouldSurround = IndirectReference && IndirectionLevel > 0;
+ /// Print first item in the chain, return new separator.
+ StringRef prettyPrintFirstElement(StringRef FirstElement,
+ bool MoreItemsExpected,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
+ StringRef Out = ".";
- if (ShouldSurround)
+ if (IndirectionLevel > 0 && MoreItemsExpected) {
+ IndirectionLevel--;
+ Out = "->";
+ }
+
+ if (IndirectionLevel > 0 && MoreItemsExpected)
os << "(";
- for (int i = 0; i < IndirectionLevel; i++)
+
+ for (int i=0; i<IndirectionLevel; i++)
os << "*";
- os << TopRegionName;
- if (ShouldSurround)
+ os << FirstElement;
+
+ if (IndirectionLevel > 0 && MoreItemsExpected)
os << ")";
- for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) {
- if (const auto *FR = dyn_cast<FieldRegion>(*I)) {
- os << Sep;
- FR->getDecl()->getDeclName().print(os, PP);
- Sep = ".";
- } else if (const auto *IR = dyn_cast<ObjCIvarRegion>(*I)) {
- os << "->";
- IR->getDecl()->getDeclName().print(os, PP);
- Sep = ".";
- } else if (isa<CXXBaseObjectRegion>(*I)) {
- continue; // Just keep going up to the base region.
- } else {
- llvm_unreachable("Previous check has missed an unexpected region");
- }
- }
- return true;
+ return Out;
}
};
@@ -1595,6 +1680,7 @@ bool bugreporter::trackNullOrUndefValue(
LVNode->getSVal(Inner).getAsRegion();
if (R) {
+ ProgramStateRef S = N->getState();
// Mark both the variable region and its contents as interesting.
SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
Modified: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c?rev=338667&r1=338666&r2=338667&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c (original)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.c Wed Aug 1 19:02:40 2018
@@ -224,3 +224,23 @@ int useindirectingstruct() {
return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
// expected-note at -1{{Undefined or garbage value returned to caller}}
}
+
+typedef struct {
+ int *x;
+} D;
+
+void initializeMaybeInStruct(D* pD) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ *pD->x = 120;
+} // expected-note{{Returning without writing to 'pD->x'}}
+
+int useInitializeMaybeInStruct() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ D d;
+ d.x = &z;
+ initializeMaybeInStruct(&d); // expected-note{{Calling 'initializeMaybeInStruct'}}
+ // expected-note at -1{{Returning from 'initializeMaybeInStruct'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
Modified: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp?rev=338667&r1=338666&r2=338667&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp (original)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.cpp Wed Aug 1 19:02:40 2018
@@ -10,10 +10,10 @@ int initializer1(int &p, int x) {
}
int param_not_initialized_by_func() {
- int p; // expected-note {{'p' declared without an initial value}}
- int out = initializer1(p, 0); // expected-note{{Calling 'initializer1'}}
+ int outP; // expected-note {{'outP' declared without an initial value}}
+ int out = initializer1(outP, 0); // expected-note{{Calling 'initializer1'}}
// expected-note at -1{{Returning from 'initializer1'}}
- return p; // expected-note{{Undefined or garbage value returned to caller}}
+ return outP; // expected-note{{Undefined or garbage value returned to caller}}
// expected-warning at -1{{Undefined or garbage value returned to caller}}
}
@@ -175,3 +175,161 @@ void rdar40335545() {
//expected-note at -1{{}}
(void)useLocal;
}
+
+////////
+
+struct HasRef {
+ int &a;
+ HasRef(int &a) : a(a) {}
+};
+
+
+void maybeInitialize(const HasRef &&pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.a = 120;
+} // expected-note{{Returning without writing to 'pA.a'}}
+
+int useMaybeInitializerWritingIntoField() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ maybeInitialize(HasRef(z)); // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+ // expected-note at -2{{Calling 'maybeInitialize'}}
+ // expected-note at -3{{Returning from 'maybeInitialize'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasRefToItself {
+ HasRefToItself &Ref; // no infinite loop
+ int &z;
+ HasRefToItself(int &z) : Ref(*this), z(z) {}
+};
+
+void maybeInitialize(const HasRefToItself &&pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.z = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.z'}}
+
+int useMaybeInitializerWritingIntoFieldWithRefToItself() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ maybeInitialize(HasRefToItself(z)); // expected-note{{Calling constructor for 'HasRefToItself'}}
+ // expected-note at -1{{Returning from constructor for 'HasRefToItself'}}
+ // expected-note at -2{{Calling 'maybeInitialize'}}
+ // expected-note at -3{{Returning from 'maybeInitialize'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////
+
+void maybeInitialize(const HasRef *pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA->a = 120;
+} // expected-note{{Returning without writing to 'pA->a'}}
+
+int useMaybeInitializerStructByPointer() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ HasRef wrapper(z); // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+ maybeInitialize(&wrapper); // expected-note{{Calling 'maybeInitialize'}}
+ // expected-note at -1{{Returning from 'maybeInitialize'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasParentWithRef : public HasRef {
+ HasParentWithRef(int &a) : HasRef(a) {} // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+};
+
+void maybeInitializeWithParent(const HasParentWithRef &pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.a = 120;
+} // expected-note{{Returning without writing to 'pA.a'}}
+
+int useMaybeInitializerWritingIntoParentField() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ maybeInitializeWithParent(HasParentWithRef(z)); // expected-note{{Calling constructor for 'HasParentWithRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasParentWithRef'}}
+ // expected-note at -2{{Calling 'maybeInitializeWithParent'}}
+ // expected-note at -3{{Returning from 'maybeInitializeWithParent'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectRef {
+ HasRef &Ref;
+ HasIndirectRef(HasRef &Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectRef &pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.Ref.a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.a'}}
+
+int useMaybeInitializeIndirectly() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+ maybeInitializeIndirectly(HasIndirectRef(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+ // expected-note at -1{{Returning from 'maybeInitializeIndirectly'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectRefByValue {
+ HasRef Ref;
+ HasIndirectRefByValue(HasRef Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectRefByValue &pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.Ref.a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref.a'}}
+
+int useMaybeInitializeIndirectlyIndirectRefByValue() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+ maybeInitializeIndirectly(HasIndirectRefByValue(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+ // expected-note at -1{{Returning from 'maybeInitializeIndirectly'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
+
+////////
+
+struct HasIndirectPointerRef {
+ HasRef *Ref;
+ HasIndirectPointerRef(HasRef *Ref) : Ref(Ref) {}
+};
+
+void maybeInitializeIndirectly(const HasIndirectPointerRef &pA) {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ pA.Ref->a = 120;
+} // expected-note{{Returning without writing to 'pA.Ref->a'}}
+
+int useMaybeInitializeIndirectlyWithPointer() {
+ int z; // expected-note{{'z' declared without an initial value}}
+ HasRef r(z); // expected-note{{Calling constructor for 'HasRef'}}
+ // expected-note at -1{{Returning from constructor for 'HasRef'}}
+ maybeInitializeIndirectly(HasIndirectPointerRef(&r)); // expected-note{{Calling 'maybeInitializeIndirectly'}}
+ // expected-note at -1{{Returning from 'maybeInitializeIndirectly'}}
+ return z; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note at -1{{Undefined or garbage value returned to caller}}
+}
Modified: cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m?rev=338667&r1=338666&r2=338667&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m (original)
+++ cfe/trunk/test/Analysis/diagnostics/no-store-func-path-notes.m Wed Aug 1 19:02:40 2018
@@ -52,7 +52,6 @@ int initFromBlock() {
extern void expectNonNull(NSString * _Nonnull a);
@interface A : NSObject
-- (void) func;
- (void) initAMaybe;
@end
@@ -66,7 +65,7 @@ extern void expectNonNull(NSString * _No
a = @"string";
} // expected-note{{Returning without writing to 'self->a'}}
-- (void) func {
+- (void) passNullToNonnull {
a = nil; // expected-note{{nil object reference stored to 'a'}}
[self initAMaybe]; // expected-note{{Calling 'initAMaybe'}}
// expected-note at -1{{Returning from 'initAMaybe'}}
@@ -74,4 +73,33 @@ extern void expectNonNull(NSString * _No
// expected-note at -1{{nil passed to a callee that requires a non-null 1st parameter}}
}
+- (void) initAMaybeWithExplicitSelf {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ self->a = @"string";
+} // expected-note{{Returning without writing to 'self->a'}}
+
+- (void) passNullToNonnullWithExplicitSelf {
+ self->a = nil; // expected-note{{nil object reference stored to 'a'}}
+ [self initAMaybeWithExplicitSelf]; // expected-note{{Calling 'initAMaybeWithExplicitSelf'}}
+ // expected-note at -1{{Returning from 'initAMaybeWithExplicitSelf'}}
+ expectNonNull(a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}}
+ // expected-note at -1{{nil passed to a callee that requires a non-null 1st parameter}}
+}
+
+- (void) initPassedAMaybe:(A *) param {
+ if (coin()) // expected-note{{Assuming the condition is false}}
+ // expected-note at -1{{Taking false branch}}
+ param->a = @"string";
+} // expected-note{{Returning without writing to 'param->a'}}
+
+- (void) useInitPassedAMaybe:(A *) paramA {
+ paramA->a = nil; // expected-note{{nil object reference stored to 'a'}}
+ [self initPassedAMaybe:paramA]; // expected-note{{Calling 'initPassedAMaybe:'}}
+ // expected-note at -1{{Returning from 'initPassedAMaybe:'}}
+ expectNonNull(paramA->a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}}
+ // expected-note at -1{{nil passed to a callee that requires a non-null 1st parameter}}
+
+}
+
@end
More information about the cfe-commits
mailing list