namespace {
+/// Put a diagnostic on return statement of all inlined functions
+/// for which the region of interest \p RegionOfInterest was passed into,
+/// but not written inside, and it has caused an undefined read or a null
+/// pointer dereference outside.
+class NoStoreFuncVisitor final
+ : public BugReporterVisitorImpl<NoStoreFuncVisitor> {
+
+ const SubRegion *RegionOfInterest;
+ static constexpr const char *DiagnosticsMsg =
+ "Returning without writing to '";
+ bool Initialized = false;
+
+ /// Frames writing into \c RegionOfInterest.
+ /// This visitor generates a note only if a function does not write into
+ /// a region of interest. This information is not immediately available
+ /// by looking at the node associated with the exit from the function
+ /// (usually the return statement). To avoid recomputing the same information
+ /// many times (going up the path for each node and checking whether the
+ /// region was written into) we instead pre-compute and store all
+ /// stack frames along the path which write into the region of interest
+ /// on the first \c VisitNode invocation.
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
+
+public:
+ NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ if (!Initialized) {
+ findModifyingFrames(N);
+ Initialized = true;
+ }
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getCurrentStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || FramesModifyingRegion.count(SCtx))
+ return nullptr;
+
+ CallEventRef<> Call =
+ BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
+ const SourceManager &SM = BRC.getSourceManager();
+ if (auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisRegion) &&
+ !CCall->getDecl()->isImplicit())
+ return notModifiedInConstructorDiagnostics(Ctx, SM, PP, *CallExitLoc,
+ CCall, ThisRegion);
+ }
+
+ ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
+ for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
+ const ParmVarDecl *PVD = parameters[I];
+ SVal S = Call->getArgSVal(I);
+ unsigned IndirectionLevel = 1;
+ QualType T = PVD->getType();
+ while (const MemRegion *R = S.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(R) &&
+ !isPointerToConst(PVD->getType()))
+ return notModifiedDiagnostics(
+ Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
+ QualType PT = T->getPointeeType();
+ if (PT.isNull() || PT->isVoidType()) break;
+ S = State->getSVal(R, PT);
+ T = PT;
+ IndirectionLevel++;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ /// Write to \c FramesModifyingRegion all stack frames along
+ /// the path which modify \c RegionOfInterest.
+ void findModifyingFrames(const ExplodedNode *N) {
+ ProgramStateRef LastReturnState;
+ do {
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (CallExitLoc) {
+ LastReturnState = State;
+ }
+ if (LastReturnState &&
+ wasRegionOfInterestModifiedAt(N, LastReturnState)) {
+ const StackFrameContext *SCtx =
+ N->getLocationContext()->getCurrentStackFrame();
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifyingRegion.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+ SCtx = SCtx->getParent()->getCurrentStackFrame();
+ }
+ }
+
+ N = N->getFirstPred();
+ } while (N);
+ }
+
+ /// \return Whether \c RegionOfInterest was modified at \p N,
+ /// where \p ReturnState is a state associated with the return
+ /// from the current frame.
+ bool wasRegionOfInterestModifiedAt(const ExplodedNode *N,
+ ProgramStateRef ReturnState) {
+ SVal ValueAtReturn = ReturnState->getSVal(RegionOfInterest);
+
+ // Writing into region of interest.
+ if (auto PS = N->getLocationAs<PostStmt>())
+ if (auto *BO = PS->getStmtAs<BinaryOperator>())
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
+ N->getSVal(BO->getLHS()).getAsRegion()))
+ return true;
+
+ // SVal after the state is possibly different.
+ SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
+ if (!ReturnState->areEqual(ValueAtN, ValueAtReturn).isConstrainedTrue() &&
+ (!ValueAtN.isUndef() || !ValueAtReturn.isUndef()))
+ return true;
+
+ return false;
+ }
+
+ /// Get parameters associated with runtime definition in order
+ /// to get the correct parameter name.
+ ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
+ if (isa<FunctionDecl>(Call->getDecl()))
+ return dyn_cast<FunctionDecl>(Call->getRuntimeDefinition().getDecl())
+ ->parameters();
+ return Call->parameters();
+ }
+
+ /// \return whether \p Ty points to a const type, or is a const reference.
+ bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> notModifiedInConstructorDiagnostics(
+ const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ const CXXConstructorCall *Call,
+ const MemRegion *ArgRegion) {
+
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << DiagnosticsMsg;
+ bool out = prettyPrintRegionName(
+ "this", "->", /*IsReference=*/true,
+ /*IndirectionLevel=*/1, ArgRegion, os, PP);
+
+ // Return nothing if we have failed to pretty-print.
+ if (!out)
+ return nullptr;
+
+ os << "'";
+ PathDiagnosticLocation L =
+ getPathDiagnosticLocation(nullptr, SM, Ctx, Call);
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
+ /// before we get to the super region of \c RegionOfInterest
+ std::shared_ptr<PathDiagnosticPiece>
+ notModifiedDiagnostics(const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ CallEventRef<> Call,
+ const ParmVarDecl *PVD,
+ const MemRegion *ArgRegion,
+ unsigned IndirectionLevel) {
+
+ PathDiagnosticLocation L = getPathDiagnosticLocation(
+ CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+ 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 << "'";
+ 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(const char *TopRegionName,
+ const char *Sep,
+ bool IsReference,
+ int IndirectionLevel,
+ const MemRegion *ArgRegion,
+ llvm::raw_svector_ostream &os,
+ const PrintingPolicy &PP) {
+ SmallVector<const MemRegion *, 5> Subregions;
+ const MemRegion *R = RegionOfInterest;
+ while (R != ArgRegion) {
+ if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R)))
+ return false; // Pattern-matching failed.
+ Subregions.push_back(R);
+ R = dyn_cast<SubRegion>(R)->getSuperRegion();
+ }
+ bool IndirectReference = !Subregions.empty();
+
+ if (IndirectReference)
+ IndirectionLevel--; // Due to "->" symbol.
+
+ if (IsReference)
+ IndirectionLevel--; // Due to reference semantics.
+
+ bool ShouldSurround = IndirectReference && IndirectionLevel > 0;
+
+ if (ShouldSurround)
+ os << "(";
+ for (int i=0; i<IndirectionLevel; i++)
+ os << "*";
+ os << TopRegionName;
+ if (ShouldSurround)
+ os << ")";
+
+ for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) {
+ if (auto *FR = dyn_cast<FieldRegion>(*I)) {
+ os << Sep;
+ FR->getDecl()->getDeclName().print(os, PP);
+ Sep = ".";
+ } else if (auto *CXXR = dyn_cast<CXXBaseObjectRegion>(*I)) {
+ continue; // Just keep going up to the base region.
+ } else {
+ llvm_unreachable("Previous check has missed an unexpected region");
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace
+
+namespace {
+
class MacroNullReturnSuppressionVisitor final
: public BugReporterVisitorImpl<MacroNullReturnSuppressionVisitor> {
if (R) {
// Mark both the variable region and its contents as interesting.
SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
+ report.addVisitor(
+ llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R)));
MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
N, R, EnableNullFPSuppression, report, V);
--- /dev/null
+// RUN: %clang_analyze_cc1 -x c -analyzer-checker=core -analyzer-output=text -verify %s
+
+typedef __typeof(sizeof(int)) size_t;
+void *memset(void *__s, int __c, size_t __n);
+
+int initializer1(int *p, int x) {
+ if (x) { // expected-note{{Taking false branch}}
+ *p = 1;
+ return 0;
+ } else {
+ return 1; // expected-note {{Returning without writing to '*p'}}
+ }
+}
+
+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'}}
+ // expected-note@-1{{Returning from 'initializer1'}}
+ return p; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+int param_initialized_properly() {
+ int p;
+ int out = initializer1(&p, 1);
+ return p; //no-warning
+}
+
+static int global;
+
+int initializer2(int **p, int x) {
+ if (x) { // expected-note{{Taking false branch}}
+ *p = &global;
+ return 0;
+ } else {
+ return 1; // expected-note {{Returning without writing to '*p'}}
+ }
+}
+
+int param_not_written_into_by_func() {
+ int *p = 0; // expected-note{{'p' initialized to a null pointer value}}
+ int out = initializer2(&p, 0); // expected-note{{Calling 'initializer2'}}
+ // expected-note@-1{{Returning from 'initializer2'}}
+ return *p; // expected-warning{{Dereference of null pointer (loaded from variable 'p')}}
+ // expected-note@-1{{Dereference of null pointer (loaded from variable 'p')}}
+}
+
+void initializer3(int *p, int param) {
+ if (param) // expected-note{{Taking false branch}}
+ *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int param_written_into_by_void_func() {
+ int p; // expected-note{{'p' declared without an initial value}}
+ initializer3(&p, 0); // expected-note{{Calling 'initializer3'}}
+ // expected-note@-1{{Returning from 'initializer3'}}
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+void initializer4(int *p, int param) {
+ if (param) // expected-note{{Taking false branch}}
+ *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+void initializer5(int *p, int param) {
+ if (!param) // expected-note{{Taking false branch}}
+ *p = 0;
+} // expected-note{{Returning without writing to '*p'}}
+
+int multi_init_tries_func() {
+ int p; // expected-note{{'p' declared without an initial value}}
+ initializer4(&p, 0); // expected-note{{Calling 'initializer4'}}
+ // expected-note@-1{{Returning from 'initializer4'}}
+ initializer5(&p, 1); // expected-note{{Calling 'initializer5'}}
+ // expected-note@-1{{Returning from 'initializer5'}}
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+int initializer6(const int *p) {
+ return 0;
+}
+
+int no_msg_on_const() {
+ int p; // expected-note{{'p' declared without an initial value}}
+ initializer6(&p);
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+typedef struct {
+ int x;
+} S;
+
+int initializer7(S *s, int param) {
+ if (param) { // expected-note{{Taking false branch}}
+ s->x = 0;
+ return 0;
+ }
+ return 1; // expected-note{{Returning without writing to 's->x'}}
+}
+
+int initialize_struct_field() {
+ S local;
+ initializer7(&local, 0); // expected-note{{Calling 'initializer7'}}
+ // expected-note@-1{{Returning from 'initializer7'}}
+ return local.x; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+void nullwriter(int **p) {
+ *p = 0; // expected-note{{Null pointer value stored to 'p'}}
+} // no extra note
+
+int usage() {
+ int x = 0;
+ int *p = &x;
+ nullwriter(&p); // expected-note{{Calling 'nullwriter'}}
+ // expected-note@-1{{Returning from 'nullwriter'}}
+ return *p; // expected-warning{{Dereference of null pointer (loaded from variable 'p')}}
+ // expected-note@-1{{Dereference of null pointer (loaded from variable 'p')}}
+}
+
+typedef struct {
+ int x;
+ int y;
+} A;
+
+void partial_initializer(A *a) {
+ a->x = 0;
+} // expected-note{{Returning without writing to 'a->y'}}
+
+int use_partial_initializer() {
+ A a;
+ partial_initializer(&a); // expected-note{{Calling 'partial_initializer'}}
+ // expected-note@-1{{Returning from 'partial_initializer'}}
+ return a.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+typedef struct {
+ int x;
+ int y;
+} B;
+
+typedef struct {
+ B b;
+} C;
+
+void partial_nested_initializer(C *c) {
+ c->b.x = 0;
+} // expected-note{{Returning without writing to 'c->b.y'}}
+
+int use_partial_nested_initializer() {
+ B localB;
+ C localC;
+ localC.b = localB;
+ partial_nested_initializer(&localC); // expected-note{{Calling 'partial_nested_initializer'}}
+ // expected-note@-1{{Returning from 'partial_nested_initializer'}}
+ return localC.b.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+void test_subregion_assignment(C* c) {
+ B b;
+ c->b = b;
+}
+
+int use_subregion_assignment() {
+ C c;
+ test_subregion_assignment(&c); // expected-note{{Calling 'test_subregion_assignment'}}
+ // expected-note@-1{{Returning from 'test_subregion_assignment'}}
+ return c.b.x; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+int confusing_signature(int *);
+int confusing_signature(int *p) {
+ return 0; // expected-note{{Returning without writing to '*p'}}
+}
+
+int use_confusing_signature() {
+ int a; // expected-note {{'a' declared without an initial value}}
+ confusing_signature(&a); // expected-note{{Calling 'confusing_signature'}}
+ // expected-note@-1{{Returning from 'confusing_signature'}}
+ return a; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+int coin();
+
+int multiindirection(int **p) {
+ if (coin()) // expected-note{{Assuming the condition is true}}
+ // expected-note@-1{{Taking true branch}}
+ return 1; // expected-note{{Returning without writing to '**p'}}
+ *(*p) = 0;
+ return 0;
+}
+
+int usemultiindirection() {
+ int a; // expected-note {{'a' declared without an initial value}}
+ int *b = &a;
+ multiindirection(&b); // expected-note{{Calling 'multiindirection'}}
+ // expected-note@-1{{Returning from 'multiindirection'}}
+ return a; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+int indirectingstruct(S** s) {
+ if (coin()) // expected-note{{Assuming the condition is true}}
+ // expected-note@-1{{Taking true branch}}
+ return 1; // expected-note{{Returning without writing to '(*s)->x'}}
+
+ (*s)->x = 0;
+ return 0;
+}
+
+int useindirectingstruct() {
+ S s;
+ S* p = &s;
+ indirectingstruct(&p); //expected-note{{Calling 'indirectingstruct'}}
+ //expected-note@-1{{Returning from 'indirectingstruct'}}
+ return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
--- /dev/null
+// RUN: %clang_analyze_cc1 -x c++ -analyzer-checker=core -analyzer-output=text -verify %s
+
+int initializer1(int &p, int x) {
+ if (x) { // expected-note{{Taking false branch}}
+ p = 1;
+ return 0;
+ } else {
+ return 1; // expected-note {{Returning without writing to 'p'}}
+ }
+}
+
+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'}}
+ // expected-note@-1{{Returning from 'initializer1'}}
+ return p; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct S {
+ int initialize(int *p, int param) {
+ if (param) { //expected-note{{Taking false branch}}
+ *p = 1;
+ return 1;
+ }
+ return 0; // expected-note{{Returning without writing to '*p'}}
+ }
+};
+
+int use(S *s) {
+ int p; //expected-note{{'p' declared without an initial value}}
+ s->initialize(&p, 0); //expected-note{{Calling 'S::initialize'}}
+ //expected-note@-1{{Returning from 'S::initialize'}}
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+int initializer2(const int &p) {
+ return 0;
+}
+
+int no_msg_const_ref() {
+ int p; //expected-note{{'p' declared without an initial value}}
+ initializer2(p);
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+void nested() {}
+void init_in_nested_func(int **x) {
+ *x = 0; // expected-note{{Null pointer value stored to 'y'}}
+ nested();
+} // no-note
+
+int call_init_nested() {
+ int x = 0;
+ int *y = &x;
+ init_in_nested_func(&y); // expected-note{{Calling 'init_in_nested_func'}}
+ // expected-note@-1{{Returning from 'init_in_nested_func'}}
+ return *y; //expected-warning{{Dereference of null pointer (loaded from variable 'y')}}
+ //expected-note@-1{{Dereference of null pointer (loaded from variable 'y')}}
+}
+
+struct A {
+ int x;
+ int y;
+};
+
+void partial_init_by_reference(A &a) {
+ a.x = 0;
+} // expected-note {{Returning without writing to 'a.y'}}
+
+int use_partial_init_by_reference() {
+ A a;
+ partial_init_by_reference(a); // expected-note{{Calling 'partial_init_by_reference'}}
+ // expected-note@-1{{Returning from 'partial_init_by_reference'}}
+ return a.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct B : A {
+};
+
+void partially_init_inherited_struct(B *b) {
+ b->x = 0;
+} // expected-note{{Returning without writing to 'b->y'}}
+
+int use_partially_init_inherited_struct() {
+ B b;
+ partially_init_inherited_struct(&b); // expected-note{{Calling 'partially_init_inherited_struct'}}
+ // expected-note@-1{{Returning from 'partially_init_inherited_struct'}}
+ return b.y; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct C {
+ int x;
+ int y;
+ C(int pX, int pY) : x(pX) {} // expected-note{{Returning without writing to 'this->y'}}
+};
+
+int use_constructor() {
+ C c(0, 0); // expected-note{{Calling constructor for 'C'}}
+ // expected-note@-1{{Returning from constructor for 'C'}}
+ return c.y; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+struct D {
+ void initialize(int *);
+};
+
+void D::initialize(int *p) {
+
+} // expected-note{{Returning without writing to '*p'}}
+
+int use_d_initializer(D* d) {
+ int p; // expected-note {{'p' declared without an initial value}}
+ d->initialize(&p); // expected-note{{Calling 'D::initialize'}}
+ // expected-note@-1{{Returning from 'D::initialize'}}
+ return p; // expected-note{{Undefined or garbage value returned to caller}}
+ // expected-warning@-1{{Undefined or garbage value returned to caller}}
+}
+
+int coin();
+
+struct S2 {
+ int x;
+};
+
+int pointerreference(S2* &s) {
+ if (coin()) // expected-note{{Assuming the condition is true}}
+ // expected-note@-1{{Taking true branch}}
+ return 1; // expected-note{{Returning without writing to 's->x'}}
+
+ s->x = 0;
+ return 0;
+}
+
+int usepointerreference() {
+ S2 s;
+ S2* p = &s;
+ pointerreference(p); //expected-note{{Calling 'pointerreference'}}
+ //expected-note@-1{{Returning from 'pointerreference'}}
+ return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+ // expected-note@-1{{Undefined or garbage value returned to caller}}
+}
--- /dev/null
+// RUN: %clang_analyze_cc1 -x objective-c -analyzer-checker=core -analyzer-output=text -Wno-objc-root-class -fblocks -verify %s
+
+@interface I
+- (int)initVar:(int *)var param:(int)param;
+@end
+
+@implementation I
+- (int)initVar:(int *)var param:(int)param {
+ if (param) { // expected-note{{Taking false branch}}
+ *var = 1;
+ return 0;
+ }
+ return 1; // expected-note{{Returning without writing to '*var'}}
+}
+@end
+
+int foo(I *i) {
+ int x; //expected-note{{'x' declared without an initial value}}
+ int out = [i initVar:&x param:0]; //expected-note{{Calling 'initVar:param:'}}
+ //expected-note@-1{{Returning from 'initVar:param:'}}
+ if (out) // expected-note{{Taking true branch}}
+ return x; //expected-warning{{Undefined or garbage value returned to caller}}
+ //expected-note@-1{{Undefined or garbage value returned to caller}}
+ return 0;
+}
+
+int initializer1(int *p, int x) {
+ if (x) { // expected-note{{Taking false branch}}
+ *p = 1;
+ return 0;
+ } else {
+ return 1; // expected-note {{Returning without writing to '*p'}}
+ }
+}
+
+int initFromBlock() {
+ __block int z;
+ ^{ // expected-note {{Calling anonymous block}}
+ int p; // expected-note{{'p' declared without an initial value}}
+ initializer1(&p, 0); // expected-note{{Calling 'initializer1'}}
+ // expected-note@-1{{Returning from 'initializer1'}}
+ z = p; // expected-warning{{Assigned value is garbage or undefined}}
+ // expected-note@-1{{Assigned value is garbage or undefined}}
+ }();
+ return z;
+}
if (c)
//expected-note@-1{{Assuming 'c' is not equal to 0}}
//expected-note@-2{{Taking true branch}}
- return;
+ return; // expected-note{{Returning without writing to '*x'}}
*x = 5;
}
if (x <= 0) //expected-note {{Taking true branch}}
//expected-note@-1 {{Assuming 'x' is <= 0}}
- return;
+ return; //expected-note{{Returning without writing to 'X->f1'}}
X->f1 = getValidPtr();
}
double testPassingParentRegionStruct(int x) {
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>15</integer>
-// CHECK-NEXT: <key>col</key><integer>9</integer>
+// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>15</integer>
-// CHECK-NEXT: <key>col</key><integer>14</integer>
+// CHECK-NEXT: <key>col</key><integer>17</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>15</integer>
+// CHECK-NEXT: <key>col</key><integer>12</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Returning without writing to '*x'</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Returning without writing to '*x'</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>22</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>54</integer>
+// CHECK-NEXT: <key>col</key><integer>5</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Returning without writing to 'X->f1'</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Returning without writing to 'X->f1'</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>60</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
//expected-note@-1{{Assuming 'err' is not equal to 0}}
//expected-note@-2{{Taking true branch}}
CFRelease(ref);
- return;
+ return; // expected-note{{Returning without writing to '*storeRef'}}
}
*storeRef = ref;
}
// CHECK-NEXT: </array>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>control</string>
+// CHECK-NEXT: <key>edges</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>start</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>71</integer>
+// CHECK-NEXT: <key>col</key><integer>5</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>71</integer>
+// CHECK-NEXT: <key>col</key><integer>13</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: <key>end</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>72</integer>
+// CHECK-NEXT: <key>col</key><integer>5</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>72</integer>
+// CHECK-NEXT: <key>col</key><integer>10</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>72</integer>
+// CHECK-NEXT: <key>col</key><integer>5</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Returning without writing to '*storeRef'</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Returning without writing to '*storeRef'</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>