Index: include/clang/Analysis/ProgramPoint.h =================================================================== --- include/clang/Analysis/ProgramPoint.h +++ include/clang/Analysis/ProgramPoint.h @@ -626,6 +626,10 @@ CallExitBegin(const StackFrameContext *L, const ReturnStmt *RS) : ProgramPoint(RS, CallExitBeginKind, L, nullptr) { } + const ReturnStmt *getReturnStmt() const { + return static_cast(getData1()); + } + private: friend class ProgramPoint; CallExitBegin() {} Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -159,6 +159,85 @@ return std::move(P); } +namespace { + +/// Put a diagnostic on return statement of the inlined function \p F +/// iff the region of interest \p R was passed to \p F, +/// but not stored into inside \p F. +class NoStoreFuncVisitor final + : public BugReporterVisitorImpl { + + const MemRegion *RegionOfInterest; + + public: + + NoStoreFuncVisitor(const MemRegion *R) : RegionOfInterest(R) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + } + + std::shared_ptr VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override { + const LocationContext* Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getCurrentStackFrame(); + auto CEB = N->getLocationAs(); + + if (!CEB) + return nullptr; + + CallEventRef<> Call = BRC.getStateManager().getCallEventManager() + .getCaller(SCtx, N->getState()); + + const LocationContext* PCtx = Call->getLocationContext(); + const ReturnStmt* RS = CEB->getReturnStmt(); + for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { + const Expr *Arg = Call->getArgExpr(I); + const MemRegion *R = N->getState()->getSVal(Arg, PCtx).getAsRegion(); + const ParmVarDecl *PVD = Call->parameters()[I]; + + if (R == RegionOfInterest && !isPointerToConst(PVD->getType())) + return generateDiagnostics(Ctx, BRC, RS, Call, PVD); + } + + return nullptr; + } + +private: + std::shared_ptr + generateDiagnostics(const LocationContext *Ctx, BugReporterContext &BRC, + const ReturnStmt *RS, + CallEventRef<> Call, + const ParmVarDecl *PVD) { + + PathDiagnosticLocation L; + if (RS) { + L = PathDiagnosticLocation::createBegin(RS, BRC.getSourceManager(), Ctx); + } else { + L = PathDiagnosticLocation(Call->getDecl()->getSourceRange().getEnd(), + BRC.getSourceManager()); + } + + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Returning without writing to '"; + PVD->printQualifiedName(os); + os << "'"; + return std::make_shared(L, os.str()); + } + + /// \return whether \p Ty points to a const type, or is a const reference. + bool isPointerToConst(QualType Ty) { + return !Ty->getPointeeType().isNull() + && Ty->getPointeeType().isConstQualified(); + } +}; + + +} namespace { /// Emits an extra note at the return statement of an interesting stack frame. @@ -1086,6 +1165,7 @@ if (R) { // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); + report.addVisitor(llvm::make_unique(R)); report.markInteresting(R); report.markInteresting(V); Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -741,6 +741,8 @@ return CEE->getCalleeContext()->getCallSite(); if (Optional PIPP = P.getAs()) return PIPP->getInitializer()->getInit(); + if (Optional CEB = P.getAs()) + return CEB->getReturnStmt(); return nullptr; } Index: test/Analysis/diagnostics/undef-value-param.c =================================================================== --- test/Analysis/diagnostics/undef-value-param.c +++ test/Analysis/diagnostics/undef-value-param.c @@ -12,7 +12,7 @@ if (c) //expected-note@-1{{Assuming 'c' is not equal to 0}} //expected-note@-2{{Taking true branch}} - return; + return; // expected-note{{parameter 'x' not written into}} *x = 5; } Index: test/Analysis/diagnostics/undef-value-param.m =================================================================== --- test/Analysis/diagnostics/undef-value-param.m +++ test/Analysis/diagnostics/undef-value-param.m @@ -69,7 +69,7 @@ //expected-note@-1{{Assuming 'err' is not equal to 0}} //expected-note@-2{{Taking true branch}} CFRelease(ref); - return; + return; // expected-note{{parameter 'storeRef' not written into}} } *storeRef = ref; } Index: test/Analysis/no-store-func-path-notes.mm =================================================================== --- /dev/null +++ test/Analysis/no-store-func-path-notes.mm @@ -0,0 +1,193 @@ +// RUN: %clang_analyze_cc1 -x objective-c++ -analyzer-checker=core -analyzer-output=text -Wno-objc-root-class -fblocks -verify %s + +namespace C_TEST { + +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}} +} + +} // end namespace C_TEST + +namespace CPP_TEST { + +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}} +} + +} // end namespace CPP_TEST + +@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 + +namespace OBJCPP_TEST { + + +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; +} + +} // end namespace OBJCPP_TEST