diff options
4 files changed, 423 insertions, 131 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index d4d33c1746c..a89758d02c4 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -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 BugReporterVisitor {    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 (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) +          return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, +                                        ParamName, ParamIsReferenceType, +                                        IndirectionLevel); -          if (isRegionOfInterestModifiedInFrame(N)) -            return nullptr; - -          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()); + +    StringRef Sep; +    for (const MemRegion *R : RegionSequence) { + +      // 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 (IndirectReference) -      IndirectionLevel--; // Due to "->" symbol. +    if (Sep.empty()) +      prettyPrintFirstElement(FirstElement, +                              /*MoreItemsExpected=*/false, IndirectionLevel, +                              os); +  } -    if (IsReference) -      IndirectionLevel--; // Due to reference semantics. +  /// Print first item in the chain, return new separator. +  StringRef prettyPrintFirstElement(StringRef FirstElement, +                       bool MoreItemsExpected, +                       int IndirectionLevel, +                       llvm::raw_svector_ostream &os) { +    StringRef Out = "."; -    bool ShouldSurround = IndirectReference && IndirectionLevel > 0; +    if (IndirectionLevel > 0 && MoreItemsExpected) { +      IndirectionLevel--; +      Out = "->"; +    } -    if (ShouldSurround) +    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(const ExplodedNode *N,          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)); diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.c b/clang/test/Analysis/diagnostics/no-store-func-path-notes.c index a444ab60261..2050f6217ca 100644 --- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.c +++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.c @@ -224,3 +224,23 @@ int useindirectingstruct() {    return s.x; // expected-warning{{Undefined or garbage value returned to caller}}                // expected-note@-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@-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@-1{{Returning from 'initializeMaybeInStruct'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-1{{Undefined or garbage value returned to caller}} +} diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp index 17ec96ae5b7..3d7003ead2e 100644 --- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp +++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp @@ -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@-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@-1{{Undefined or garbage value returned to caller}}  } @@ -175,3 +175,161 @@ void rdar40335545() {                            //expected-note@-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@-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@-1{{Returning from constructor for 'HasRef'}} +                              // expected-note@-2{{Calling 'maybeInitialize'}} +                              // expected-note@-3{{Returning from 'maybeInitialize'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-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@-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@-1{{Returning from constructor for 'HasRefToItself'}} +                              // expected-note@-2{{Calling 'maybeInitialize'}} +                              // expected-note@-3{{Returning from 'maybeInitialize'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-1{{Undefined or garbage value returned to caller}} +} + +//// + +void maybeInitialize(const HasRef *pA) { +  if (coin()) // expected-note{{Assuming the condition is false}} +              // expected-note@-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@-1{{Returning from constructor for 'HasRef'}} +  maybeInitialize(&wrapper); // expected-note{{Calling 'maybeInitialize'}} +                             // expected-note@-1{{Returning from 'maybeInitialize'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-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@-1{{Returning from constructor for 'HasRef'}} +}; + +void maybeInitializeWithParent(const HasParentWithRef &pA) { +  if (coin()) // expected-note{{Assuming the condition is false}} +              // expected-note@-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@-1{{Returning from constructor for 'HasParentWithRef'}} +                              // expected-note@-2{{Calling 'maybeInitializeWithParent'}} +                              // expected-note@-3{{Returning from 'maybeInitializeWithParent'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-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@-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@-1{{Returning from constructor for 'HasRef'}} +  maybeInitializeIndirectly(HasIndirectRef(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}} +                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-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@-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@-1{{Returning from constructor for 'HasRef'}} +  maybeInitializeIndirectly(HasIndirectRefByValue(r)); // expected-note{{Calling 'maybeInitializeIndirectly'}} +                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-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@-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@-1{{Returning from constructor for 'HasRef'}} +  maybeInitializeIndirectly(HasIndirectPointerRef(&r)); // expected-note{{Calling 'maybeInitializeIndirectly'}} +                                                // expected-note@-1{{Returning from 'maybeInitializeIndirectly'}} +  return z; // expected-warning{{Undefined or garbage value returned to caller}} +            // expected-note@-1{{Undefined or garbage value returned to caller}} +} diff --git a/clang/test/Analysis/diagnostics/no-store-func-path-notes.m b/clang/test/Analysis/diagnostics/no-store-func-path-notes.m index 51d1515e860..c0aa5144e76 100644 --- a/clang/test/Analysis/diagnostics/no-store-func-path-notes.m +++ b/clang/test/Analysis/diagnostics/no-store-func-path-notes.m @@ -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 * _Nonnull a);      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@-1{{Returning from 'initAMaybe'}} @@ -74,4 +73,33 @@ extern void expectNonNull(NSString * _Nonnull a);                      // expected-note@-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@-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@-1{{Returning from 'initAMaybeWithExplicitSelf'}} +  expectNonNull(a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}} +                    // expected-note@-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@-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@-1{{Returning from 'initPassedAMaybe:'}} +  expectNonNull(paramA->a); // expected-warning{{nil passed to a callee that requires a non-null 1st parameter}} +                            // expected-note@-1{{nil passed to a callee that requires a non-null 1st parameter}} + +} +  @end  | 

