[Analyzer] [NFC] Parameter Regions
authorAdam Balogh <adam.balogh@ericsson.com>
Mon, 11 May 2020 13:00:42 +0000 (15:00 +0200)
committerAdam Balogh <adam.balogh@ericsson.com>
Tue, 9 Jun 2020 10:08:56 +0000 (12:08 +0200)
Currently, parameters of functions without their definition present cannot
be represented as regions because it would be difficult to ensure that the
same declaration is used in every case. To overcome this, we split
`VarRegion` to two subclasses: `NonParamVarRegion` and `ParamVarRegion`.
The latter does not store the `Decl` of the parameter variable. Instead it
stores the index of the parameter which enables retrieving the actual
`Decl` every time using the function declaration of the stack frame. To
achieve this we also removed storing of `Decl` from `DeclRegion` and made
`getDecl()` pure virtual. The individual `Decl`s are stored in the
appropriate subclasses, such as `FieldRegion`, `ObjCIvarRegion` and the
newly introduced `NonParamVarRegion`.

Differential Revision: https://reviews.llvm.org/D80522

16 files changed:
clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/lib/StaticAnalyzer/Core/MemRegion.cpp
clang/lib/StaticAnalyzer/Core/Store.cpp
clang/test/Analysis/explain-svals.c
clang/test/Analysis/explain-svals.cpp
clang/test/Analysis/explain-svals.m
clang/unittests/StaticAnalyzer/CMakeLists.txt
clang/unittests/StaticAnalyzer/ParamRegionTest.cpp [new file with mode: 0644]

index f7bd5b5..0f33909 100644 (file)
@@ -18,6 +18,7 @@
 #include "clang/AST/Attr.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+#include "llvm/ADT/StringExtras.h"
 
 namespace clang {
 
@@ -179,7 +180,7 @@ public:
     return OS.str();
   }
 
-  std::string VisitVarRegion(const VarRegion *R) {
+  std::string VisitNonParamVarRegion(const NonParamVarRegion *R) {
     const VarDecl *VD = R->getDecl();
     std::string Name = VD->getQualifiedNameAsString();
     if (isa<ParmVarDecl>(VD))
@@ -216,6 +217,39 @@ public:
            "' inside " + Visit(R->getSuperRegion());
   }
 
+  std::string VisitParamVarRegion(const ParamVarRegion *R) {
+    std::string Str;
+    llvm::raw_string_ostream OS(Str);
+
+    const ParmVarDecl *PVD = R->getDecl();
+    std::string Name = PVD->getQualifiedNameAsString();
+    if (!Name.empty()) {
+      OS << "parameter '" << Name << "'";
+      return std::string(OS.str());
+    }
+
+    unsigned Index = R->getIndex() + 1;
+    OS << Index << llvm::getOrdinalSuffix(Index) << " parameter of ";
+    const Decl *Parent = R->getStackFrame()->getDecl();
+    if (const auto *FD = dyn_cast<FunctionDecl>(Parent))
+      OS << "function '" << FD->getQualifiedNameAsString() << "()'";
+    else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Parent))
+      OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'";
+    else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Parent)) {
+      if (MD->isClassMethod())
+        OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'";
+      else
+        OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'";
+    } else if (isa<BlockDecl>(Parent)) {
+      if (cast<BlockDecl>(Parent)->isConversionFromLambda())
+        OS << "lambda";
+      else
+        OS << "block";
+    }
+
+    return std::string(OS.str());
+  }
+
   std::string VisitSVal(SVal V) {
     std::string Str;
     llvm::raw_string_ostream OS(Str);
index 2dd75fa..a71469e 100644 (file)
@@ -402,9 +402,10 @@ public:
   const StackFrameContext *getCalleeStackFrame(unsigned BlockCount) const;
 
   /// Returns memory location for a parameter variable within the callee stack
-  /// frame. May fail; returns null on failure.
-  const VarRegion *getParameterLocation(unsigned Index,
-                                        unsigned BlockCount) const;
+  /// frame. The behavior is undefined if the block count is different from the
+  /// one that is there when call happens. May fail; returns null on failure.
+  const ParamVarRegion *getParameterLocation(unsigned Index,
+                                             unsigned BlockCount) const;
 
   /// Returns true if on the current path, the argument was constructed by
   /// calling a C++ constructor over it. This is an internal detail of the
index f2ba900..b0391bd 100644 (file)
@@ -890,20 +890,12 @@ public:
 
 class DeclRegion : public TypedValueRegion {
 protected:
-  const ValueDecl *D;
-
-  DeclRegion(const ValueDecl *d, const MemRegion *sReg, Kind k)
-      : TypedValueRegion(sReg, k), D(d) {
+  DeclRegion(const MemRegion *sReg, Kind k) : TypedValueRegion(sReg, k) {
     assert(classof(this));
-    assert(d && d->isCanonicalDecl());
   }
 
-  static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
-                      const MemRegion* superRegion, Kind k);
-
 public:
-  const ValueDecl *getDecl() const { return D; }
-  void Profile(llvm::FoldingSetNodeID& ID) const override;
+  virtual const ValueDecl *getDecl() const = 0;
 
   static bool classof(const MemRegion* R) {
     unsigned k = R->getKind();
@@ -914,9 +906,9 @@ public:
 class VarRegion : public DeclRegion {
   friend class MemRegionManager;
 
-  // Constructors and private methods.
-  VarRegion(const VarDecl *vd, const MemRegion *sReg)
-      : DeclRegion(vd, sReg, VarRegionKind) {
+protected:
+  // Constructors and protected methods.
+  VarRegion(const MemRegion *sReg, Kind k) : DeclRegion(sReg, k) {
     // VarRegion appears in unknown space when it's a block variable as seen
     // from a block using it, when this block is analyzed at top-level.
     // Other block variables appear within block data regions,
@@ -925,17 +917,45 @@ class VarRegion : public DeclRegion {
            isa<BlockDataRegion>(sReg) || isa<UnknownSpaceRegion>(sReg));
   }
 
-  static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl *VD,
-                            const MemRegion *superRegion) {
-    DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind);
+public:
+  const VarDecl *getDecl() const override = 0;
+
+  const StackFrameContext *getStackFrame() const;
+
+  QualType getValueType() const override {
+    // FIXME: We can cache this if needed.
+    return getDecl()->getType();
   }
 
-public:
-  void Profile(llvm::FoldingSetNodeID& ID) const override;
+  static bool classof(const MemRegion *R) {
+    unsigned k = R->getKind();
+    return k >= BEGIN_VAR_REGIONS && k <= END_VAR_REGIONS;
+  }
+};
 
-  const VarDecl *getDecl() const { return cast<VarDecl>(D); }
+class NonParamVarRegion : public VarRegion {
+  friend class MemRegionManager;
 
-  const StackFrameContext *getStackFrame() const;
+  const VarDecl *VD;
+
+  // Constructors and private methods.
+  NonParamVarRegion(const VarDecl *vd, const MemRegion *sReg)
+      : VarRegion(sReg, NonParamVarRegionKind), VD(vd) {
+    // VarRegion appears in unknown space when it's a block variable as seen
+    // from a block using it, when this block is analyzed at top-level.
+    // Other block variables appear within block data regions,
+    // which, unlike everything else on this list, are not memory spaces.
+    assert(isa<GlobalsSpaceRegion>(sReg) || isa<StackSpaceRegion>(sReg) ||
+           isa<BlockDataRegion>(sReg) || isa<UnknownSpaceRegion>(sReg));
+  }
+
+  static void ProfileRegion(llvm::FoldingSetNodeID &ID, const VarDecl *VD,
+                            const MemRegion *superRegion);
+
+public:
+  void Profile(llvm::FoldingSetNodeID &ID) const override;
+
+  const VarDecl *getDecl() const override { return VD; }
 
   QualType getValueType() const override {
     // FIXME: We can cache this if needed.
@@ -949,7 +969,50 @@ public:
   void printPrettyAsExpr(raw_ostream &os) const override;
 
   static bool classof(const MemRegion* R) {
-    return R->getKind() == VarRegionKind;
+    return R->getKind() == NonParamVarRegionKind;
+  }
+};
+
+/// ParamVarRegion - Represents a region for paremters. Only parameters of the
+/// function in the current stack frame are represented as `ParamVarRegion`s.
+/// Parameters of top-level analyzed functions as well as captured paremeters
+/// by lambdas and blocks are repesented as `VarRegion`s.
+
+// FIXME: `ParamVarRegion` only supports parameters of functions, C++
+// constructors, blocks and Objective-C methods with existing `Decl`. Upon
+// implementing stack frame creations for functions without decl (functions
+// passed by unknown function pointer) methods of `ParamVarRegion` must be
+// updated.
+class ParamVarRegion : public VarRegion {
+  friend class MemRegionManager;
+
+  const Expr *OriginExpr;
+  unsigned Index;
+
+  ParamVarRegion(const Expr *OE, unsigned Idx, const MemRegion *SReg)
+      : VarRegion(SReg, ParamVarRegionKind), OriginExpr(OE), Index(Idx) {
+    assert(!cast<StackSpaceRegion>(SReg)->getStackFrame()->inTopFrame());
+  }
+
+  static void ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+                            unsigned Idx, const MemRegion *SReg);
+
+public:
+  const Expr *getOriginExpr() const { return OriginExpr; }
+  unsigned getIndex() const { return Index; }
+
+  void Profile(llvm::FoldingSetNodeID& ID) const override;
+
+  void dumpToStream(raw_ostream &os) const override;
+
+  QualType getValueType() const override;
+  const ParmVarDecl *getDecl() const override;
+
+  bool canPrintPrettyAsExpr() const override;
+  void printPrettyAsExpr(raw_ostream &os) const override;
+
+  static bool classof(const MemRegion *R) {
+    return R->getKind() == ParamVarRegionKind;
   }
 };
 
@@ -991,16 +1054,22 @@ private:
 class FieldRegion : public DeclRegion {
   friend class MemRegionManager;
 
-  FieldRegion(const FieldDecl *fd, const SubRegion* sReg)
-      : DeclRegion(fd, sReg, FieldRegionKind) {}
+  const FieldDecl *FD;
 
-  static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl *FD,
+  FieldRegion(const FieldDecl *fd, const SubRegion *sReg)
+      : DeclRegion(sReg, FieldRegionKind), FD(fd) {}
+
+  static void ProfileRegion(llvm::FoldingSetNodeID &ID, const FieldDecl *FD,
                             const MemRegion* superRegion) {
-    DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind);
+    ID.AddInteger(static_cast<unsigned>(FieldRegionKind));
+    ID.AddPointer(FD);
+    ID.AddPointer(superRegion);
   }
 
 public:
-  const FieldDecl *getDecl() const { return cast<FieldDecl>(D); }
+  const FieldDecl *getDecl() const override { return FD; }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const override;
 
   QualType getValueType() const override {
     // FIXME: We can cache this if needed.
@@ -1022,6 +1091,8 @@ public:
 class ObjCIvarRegion : public DeclRegion {
   friend class MemRegionManager;
 
+  const ObjCIvarDecl *IVD;
+
   ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg);
 
   static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl *ivd,
@@ -1029,6 +1100,9 @@ class ObjCIvarRegion : public DeclRegion {
 
 public:
   const ObjCIvarDecl *getDecl() const;
+
+  void Profile(llvm::FoldingSetNodeID& ID) const override;
+
   QualType getValueType() const override;
 
   bool canPrintPrettyAsExpr() const override;
@@ -1312,11 +1386,18 @@ public:
 
   /// getVarRegion - Retrieve or create the memory region associated with
   ///  a specified VarDecl and LocationContext.
-  const VarRegion* getVarRegion(const VarDecl *D, const LocationContext *LC);
+  const VarRegion *getVarRegion(const VarDecl *VD, const LocationContext *LC);
 
   /// getVarRegion - Retrieve or create the memory region associated with
-  ///  a specified VarDecl and super region.
-  const VarRegion *getVarRegion(const VarDecl *D, const MemRegion *superR);
+  ///  a specified VarDecl and LocationContext.
+  const NonParamVarRegion *getNonParamVarRegion(const VarDecl *VD,
+                                                const MemRegion *superR);
+
+  /// getParamVarRegion - Retrieve or create the memory region
+  /// associated with a specified CallExpr, Index and LocationContext.
+  const ParamVarRegion *getParamVarRegion(const Expr *OriginExpr,
+                                          unsigned Index,
+                                          const LocationContext *LC);
 
   /// getElementRegion - Retrieve the memory region associated with the
   ///  associated element type, index, and super region.
index a0d7db6..9a34639 100644 (file)
@@ -308,6 +308,10 @@ public:
   Loc getLValue(const CXXRecordDecl *BaseClass, const SubRegion *Super,
                 bool IsVirtual) const;
 
+  /// Get the lvalue for a parameter.
+  Loc getLValue(const Expr *Call, unsigned Index,
+                const LocationContext *LC) const;
+
   /// Get the lvalue for a variable reference.
   Loc getLValue(const VarDecl *D, const LocationContext *LC) const;
 
index 3c52c2b..44ab31f 100644 (file)
@@ -73,9 +73,13 @@ ABSTRACT_REGION(SubRegion, MemRegion)
       ABSTRACT_REGION(DeclRegion, TypedValueRegion)
         REGION(FieldRegion, DeclRegion)
         REGION(ObjCIvarRegion, DeclRegion)
-        REGION(VarRegion, DeclRegion)
-        REGION_RANGE(DECL_REGIONS, FieldRegionKind,
-                                   VarRegionKind)
+        ABSTRACT_REGION(VarRegion, DeclRegion)
+          REGION(NonParamVarRegion, VarRegion)
+          REGION(ParamVarRegion, VarRegion)
+        REGION_RANGE(VAR_REGIONS, NonParamVarRegionKind,
+                                  ParamVarRegionKind)
+      REGION_RANGE(DECL_REGIONS, FieldRegionKind,
+                                 ParamVarRegionKind)
       REGION(ElementRegion, TypedValueRegion)
       REGION(ObjCStringRegion, TypedValueRegion)
       REGION(StringRegion, TypedValueRegion)
index 40467b3..30fd62f 100644 (file)
@@ -709,7 +709,8 @@ ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
 
   case MemRegion::SymbolicRegionKind:
   case MemRegion::AllocaRegionKind:
-  case MemRegion::VarRegionKind:
+  case MemRegion::NonParamVarRegionKind:
+  case MemRegion::ParamVarRegionKind:
   case MemRegion::FieldRegionKind:
   case MemRegion::ObjCIvarRegionKind:
     // These are the types we can currently track string lengths for.
@@ -814,7 +815,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
   }
   case MemRegion::SymbolicRegionKind:
   case MemRegion::AllocaRegionKind:
-  case MemRegion::VarRegionKind:
+  case MemRegion::NonParamVarRegionKind:
+  case MemRegion::ParamVarRegionKind:
   case MemRegion::FieldRegionKind:
   case MemRegion::ObjCIvarRegionKind:
     return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
@@ -1009,10 +1011,14 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
     os << "a C++ temp object of type "
        << cast<TypedValueRegion>(MR)->getValueType().getAsString();
     return true;
-  case MemRegion::VarRegionKind:
+  case MemRegion::NonParamVarRegionKind:
     os << "a variable of type"
        << cast<TypedValueRegion>(MR)->getValueType().getAsString();
     return true;
+  case MemRegion::ParamVarRegionKind:
+    os << "a parameter of type"
+       << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+    return true;
   case MemRegion::FieldRegionKind:
     os << "a field of type "
        << cast<TypedValueRegion>(MR)->getValueType().getAsString();
index b133bde..dd6f78e 100644 (file)
@@ -222,39 +222,17 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
   return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
 }
 
-const VarRegion *CallEvent::getParameterLocation(unsigned Index,
-                                                 unsigned BlockCount) const {
+const ParamVarRegion
+*CallEvent::getParameterLocation(unsigned Index, unsigned BlockCount) const {
   const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
   // We cannot construct a VarRegion without a stack frame.
   if (!SFC)
     return nullptr;
 
-  // Retrieve parameters of the definition, which are different from
-  // CallEvent's parameters() because getDecl() isn't necessarily
-  // the definition. SFC contains the definition that would be used
-  // during analysis.
-  const Decl *D = SFC->getDecl();
-
-  // TODO: Refactor into a virtual method of CallEvent, like parameters().
-  const ParmVarDecl *PVD = nullptr;
-  if (const auto *FD = dyn_cast<FunctionDecl>(D))
-    PVD = FD->parameters()[Index];
-  else if (const auto *BD = dyn_cast<BlockDecl>(D))
-    PVD = BD->parameters()[Index];
-  else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
-    PVD = MD->parameters()[Index];
-  else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
-    PVD = CD->parameters()[Index];
-  assert(PVD && "Unexpected Decl kind!");
-
-  const VarRegion *VR =
-      State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
-
-  // This sanity check would fail if our parameter declaration doesn't
-  // correspond to the stack frame's function declaration.
-  assert(VR->getStackFrame() == SFC);
-
-  return VR;
+  const ParamVarRegion *PVR =
+    State->getStateManager().getRegionManager().getParamVarRegion(
+        getOriginExpr(), Index, SFC);
+  return PVR;
 }
 
 /// Returns true if a type is a pointer-to-const or reference-to-const
@@ -325,8 +303,9 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
     if (getKind() != CE_CXXAllocator)
       if (isArgumentConstructedDirectly(Idx))
         if (auto AdjIdx = getAdjustedParameterIndex(Idx))
-          if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
-            ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
+          if (const TypedValueRegion *TVR =
+                  getParameterLocation(*AdjIdx, BlockCount))
+            ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
   }
 
   // Invalidate designated regions using the batch invalidation API.
@@ -527,7 +506,8 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
     // which makes getArgSVal() fail and return UnknownVal.
     SVal ArgVal = Call.getArgSVal(Idx);
     if (!ArgVal.isUnknown()) {
-      Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+      Loc ParamLoc = SVB.makeLoc(
+          MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
       Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
     }
   }
index 2fea21b..c5e38cc 100644 (file)
@@ -218,7 +218,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
     auto CE = BD->capture_end();
     for (; I != E; ++I) {
       const VarRegion *capturedR = I.getCapturedRegion();
-      const VarRegion *originalR = I.getOriginalRegion();
+      const TypedValueRegion *originalR = I.getOriginalRegion();
 
       // If the capture had a copy expression, use the result of evaluating
       // that expression, otherwise use the original value.
index e14725e..ba2875a 100644 (file)
@@ -303,12 +303,12 @@ SVal ExprEngine::computeObjectUnderConstruction(
         // Operator arguments do not correspond to operator parameters
         // because this-argument is implemented as a normal argument in
         // operator call expressions but not in operator declarations.
-        const VarRegion *VR = Caller->getParameterLocation(
+        const TypedValueRegion *TVR = Caller->getParameterLocation(
             *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
-        if (!VR)
+        if (!TVR)
           return None;
 
-        return loc::MemRegionVal(VR);
+        return loc::MemRegionVal(TVR);
       };
 
       if (const auto *CE = dyn_cast<CallExpr>(E)) {
index 77944de..455adf5 100644 (file)
@@ -160,11 +160,9 @@ const StackFrameContext *VarRegion::getStackFrame() const {
 }
 
 ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
-    : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+    : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {}
 
-const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
-  return cast<ObjCIvarDecl>(D);
-}
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; }
 
 QualType ObjCIvarRegion::getValueType() const {
   return getDecl()->getType();
@@ -178,6 +176,33 @@ QualType CXXDerivedObjectRegion::getValueType() const {
   return QualType(getDecl()->getTypeForDecl(), 0);
 }
 
+QualType ParamVarRegion::getValueType() const {
+  assert(getDecl() &&
+         "`ParamVarRegion` support functions without `Decl` not implemented"
+         " yet.");
+  return getDecl()->getType();
+}
+
+const ParmVarDecl *ParamVarRegion::getDecl() const {
+  const Decl *D = getStackFrame()->getDecl();
+
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    assert(Index < FD->param_size());
+    return FD->parameters()[Index];
+  } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+    assert(Index < BD->param_size());
+    return BD->parameters()[Index];
+  } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+    assert(Index < MD->param_size());
+    return MD->parameters()[Index];
+  } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+    assert(Index < CD->param_size());
+    return CD->parameters()[Index];
+  } else {
+    llvm_unreachable("Unexpected Decl kind!");
+  }
+}
+
 //===----------------------------------------------------------------------===//
 // FoldingSet profiling.
 //===----------------------------------------------------------------------===//
@@ -249,25 +274,44 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
   CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
 }
 
+void FieldRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+  ProfileRegion(ID, getDecl(), superRegion);
+}
+
 void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
                                    const ObjCIvarDecl *ivd,
                                    const MemRegion* superRegion) {
-  DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+  ID.AddInteger(static_cast<unsigned>(ObjCIvarRegionKind));
+  ID.AddPointer(ivd);
+  ID.AddPointer(superRegion);
+}
+
+void ObjCIvarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+  ProfileRegion(ID, getDecl(), superRegion);
 }
 
-void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
-                               const MemRegion* superRegion, Kind k) {
-  ID.AddInteger(static_cast<unsigned>(k));
-  ID.AddPointer(D);
+void NonParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+                                      const VarDecl *VD,
+                                      const MemRegion *superRegion) {
+  ID.AddInteger(static_cast<unsigned>(NonParamVarRegionKind));
+  ID.AddPointer(VD);
   ID.AddPointer(superRegion);
 }
 
-void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const {
-  DeclRegion::ProfileRegion(ID, D, superRegion, getKind());
+void NonParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+  ProfileRegion(ID, getDecl(), superRegion);
 }
 
-void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
-  VarRegion::ProfileRegion(ID, getDecl(), superRegion);
+void ParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+                                   unsigned Idx, const MemRegion *SReg) {
+  ID.AddInteger(static_cast<unsigned>(ParamVarRegionKind));
+  ID.AddPointer(OE);
+  ID.AddInteger(Idx);
+  ID.AddPointer(SReg);
+}
+
+void ParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+  ProfileRegion(ID, getOriginExpr(), getIndex(), superRegion);
 }
 
 void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym,
@@ -479,12 +523,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const {
   os << "SymRegion{" << sym << '}';
 }
 
-void VarRegion::dumpToStream(raw_ostream &os) const {
-  const auto *VD = cast<VarDecl>(D);
+void NonParamVarRegion::dumpToStream(raw_ostream &os) const {
   if (const IdentifierInfo *ID = VD->getIdentifier())
     os << ID->getName();
   else
-    os << "VarRegion{D" << VD->getID() << '}';
+    os << "NonParamVarRegion{D" << VD->getID() << '}';
 }
 
 LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
@@ -531,6 +574,18 @@ void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
   os << "StackLocalsSpaceRegion";
 }
 
+void ParamVarRegion::dumpToStream(raw_ostream &os) const {
+  const ParmVarDecl *PVD = getDecl();
+  assert(PVD &&
+         "`ParamVarRegion` support functions without `Decl` not implemented"
+         " yet.");
+  if (const IdentifierInfo *ID = PVD->getIdentifier()) {
+    os << ID->getName();
+  } else {
+    os << "ParamVarRegion{P" << PVD->getID() << '}';
+  }
+}
+
 bool MemRegion::canPrintPretty() const {
   return canPrintPrettyAsExpr();
 }
@@ -550,11 +605,18 @@ void MemRegion::printPrettyAsExpr(raw_ostream &) const {
   llvm_unreachable("This region cannot be printed pretty.");
 }
 
-bool VarRegion::canPrintPrettyAsExpr() const {
-  return true;
+bool NonParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void NonParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+  os << getDecl()->getName();
 }
 
-void VarRegion::printPrettyAsExpr(raw_ostream &os) const {
+bool ParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void ParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+  assert(getDecl() &&
+         "`ParamVarRegion` support functions without `Decl` not implemented"
+         " yet.");
   os << getDecl()->getName();
 }
 
@@ -693,7 +755,8 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
   case MemRegion::CXXTempObjectRegionKind:
   case MemRegion::CXXThisRegionKind:
   case MemRegion::ObjCIvarRegionKind:
-  case MemRegion::VarRegionKind:
+  case MemRegion::NonParamVarRegionKind:
+  case MemRegion::ParamVarRegionKind:
   case MemRegion::ElementRegionKind:
   case MemRegion::ObjCStringRegionKind: {
     QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
@@ -847,9 +910,11 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
       for (BlockDataRegion::referenced_vars_iterator
            I = BR->referenced_vars_begin(),
            E = BR->referenced_vars_end(); I != E; ++I) {
-        const VarRegion *VR = I.getOriginalRegion();
-        if (VR->getDecl() == VD)
-          return cast<VarRegion>(I.getCapturedRegion());
+        const TypedValueRegion *OrigR = I.getOriginalRegion();
+        if (const auto *VR = dyn_cast<VarRegion>(OrigR)) {
+          if (VR->getDecl() == VD)
+            return cast<VarRegion>(I.getCapturedRegion());
+        }
       }
     }
 
@@ -858,8 +923,30 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
   return (const StackFrameContext *)nullptr;
 }
 
-const VarRegionMemRegionManager::getVarRegion(const VarDecl *D,
+const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
                                                 const LocationContext *LC) {
+  const auto *PVD = dyn_cast<ParmVarDecl>(D);
+  if (PVD) {
+    unsigned Index = PVD->getFunctionScopeIndex();
+    const StackFrameContext *SFC = LC->getStackFrame();
+    const Stmt *CallSite = SFC->getCallSite();
+    if (CallSite) {
+      const Decl *D = SFC->getDecl();
+      if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+        if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
+          return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+                                              getStackArgumentsRegion(SFC));
+      } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+        if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
+          return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+                                              getStackArgumentsRegion(SFC));
+      } else {
+        return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+                                            getStackArgumentsRegion(SFC));
+      }
+    }
+  }
+
   D = D->getCanonicalDecl();
   const MemRegion *sReg = nullptr;
 
@@ -942,13 +1029,23 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
     }
   }
 
-  return getSubRegion<VarRegion>(D, sReg);
+  return getSubRegion<NonParamVarRegion>(D, sReg);
 }
 
-const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
-                                                const MemRegion *superR) {
+const NonParamVarRegion *
+MemRegionManager::getNonParamVarRegion(const VarDecl *D,
+                                       const MemRegion *superR) {
   D = D->getCanonicalDecl();
-  return getSubRegion<VarRegion>(D, superR);
+  return getSubRegion<NonParamVarRegion>(D, superR);
+}
+
+const ParamVarRegion *
+MemRegionManager::getParamVarRegion(const Expr *OriginExpr, unsigned Index,
+                                    const LocationContext *LC) {
+  const StackFrameContext *SFC = LC->getStackFrame();
+  assert(SFC);
+  return getSubRegion<ParamVarRegion>(OriginExpr, Index,
+                                      getStackArgumentsRegion(SFC));
 }
 
 const BlockDataRegion *
@@ -1341,7 +1438,8 @@ static RegionOffset calculateOffset(const MemRegion *R) {
     case MemRegion::CXXThisRegionKind:
     case MemRegion::StringRegionKind:
     case MemRegion::ObjCStringRegionKind:
-    case MemRegion::VarRegionKind:
+    case MemRegion::NonParamVarRegionKind:
+    case MemRegion::ParamVarRegionKind:
     case MemRegion::CXXTempObjectRegionKind:
       // Usual base regions.
       goto Finish;
@@ -1497,7 +1595,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
   const VarRegion *OriginalVR = nullptr;
 
   if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) {
-    VR = MemMgr.getVarRegion(VD, this);
+    VR = MemMgr.getNonParamVarRegion(VD, this);
     OriginalVR = MemMgr.getVarRegion(VD, LC);
   }
   else {
@@ -1506,7 +1604,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
       OriginalVR = VR;
     }
     else {
-      VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+      VR = MemMgr.getNonParamVarRegion(VD, MemMgr.getUnknownRegion());
       OriginalVR = MemMgr.getVarRegion(VD, LC);
     }
   }
index b33129c..ea617bb 100644 (file)
@@ -134,7 +134,8 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
     case MemRegion::FieldRegionKind:
     case MemRegion::ObjCIvarRegionKind:
     case MemRegion::ObjCStringRegionKind:
-    case MemRegion::VarRegionKind:
+    case MemRegion::NonParamVarRegionKind:
+    case MemRegion::ParamVarRegionKind:
     case MemRegion::CXXTempObjectRegionKind:
     case MemRegion::CXXBaseObjectRegionKind:
     case MemRegion::CXXDerivedObjectRegionKind:
index 204df7c..4e095ef 100644 (file)
@@ -27,3 +27,15 @@ void test_2(struct S s) {
   clang_analyzer_explain_voidp(&s); // expected-warning-re{{{{^pointer to parameter 's'$}}}}
   clang_analyzer_explain_int(s.z);  // expected-warning-re{{{{^initial value of field 'z' of parameter 's'$}}}}
 }
+
+void test_3(int param) {
+  clang_analyzer_explain_voidp(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+void test_non_top_level(int param) {
+  clang_analyzer_explain_voidp(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+void test_4(int n) {
+  test_non_top_level(n);
+}
index f8ec234..0510e41 100644 (file)
@@ -19,6 +19,7 @@ struct S {
 
 void clang_analyzer_explain(int);
 void clang_analyzer_explain(void *);
+void clang_analyzer_explain(const int *);
 void clang_analyzer_explain(S);
 
 size_t clang_analyzer_getExtent(void *);
@@ -100,3 +101,30 @@ void test_6() {
   clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}}
   clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}}
 }
+
+class C_top_level {
+public:
+  C_top_level(int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+  }
+};
+
+class C_non_top_level {
+public:
+  C_non_top_level(int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+  }
+};
+
+void test_7(int n) {
+  C_non_top_level c(n);
+
+  auto lambda_top_level = [n](int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+  };
+  auto lambda_non_top_level = [n](int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+  };
+
+  lambda_non_top_level(n);
+}
index b2efebf..52f48c5 100644 (file)
@@ -28,3 +28,44 @@ void test_2() {
   };
   clang_analyzer_explain(&x); // expected-warning-re{{{{^pointer to block variable 'x'$}}}}
 }
+
+@interface O
++ (instancetype)top_level_class_method:(int)param;
++ (instancetype)non_top_level_class_method:(int)param;
+- top_level_instance_method:(int)param;
+- non_top_level_instance_method:(int)param;
+@end
+
+@implementation O
++ (instancetype)top_level_class_method:(int)param {
+  clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
++ (instancetype)non_top_level_class_method:(int)param {
+  clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+- top_level_instance_method:(int)param {
+  clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+- non_top_level_instance_method:(int)param {
+  clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+@end
+
+void test_3(int n, int m) {
+  O *o = [O non_top_level_class_method:n];
+  [o non_top_level_instance_method:m];
+
+  void (^block_top_level)(int) = ^(int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+    clang_analyzer_explain(&n);     // expected-warning-re{{{{^pointer to parameter 'n'$}}}}
+  };
+  void (^block_non_top_level)(int) = ^(int param) {
+    clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+    clang_analyzer_explain(&n);     // expected-warning-re{{{{^pointer to parameter 'n'$}}}}
+  };
+
+  block_non_top_level(n);
+}
index 8eded65..564eba8 100644 (file)
@@ -7,6 +7,7 @@ add_clang_unittest(StaticAnalysisTests
   AnalyzerOptionsTest.cpp
   CallDescriptionTest.cpp
   CallEventTest.cpp
+  ParamRegionTest.cpp
   RangeSetTest.cpp
   RegisterCustomCheckersTest.cpp
   StoreTest.cpp 
diff --git a/clang/unittests/StaticAnalyzer/ParamRegionTest.cpp b/clang/unittests/StaticAnalyzer/ParamRegionTest.cpp
new file mode 100644 (file)
index 0000000..9a666f7
--- /dev/null
@@ -0,0 +1,109 @@
+//===- unittests/StaticAnalyzer/ParamRegionTest.cpp -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reusables.h"
+
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ento {
+namespace {
+
+class ParamRegionTestConsumer : public ExprEngineConsumer {
+  void performTest(const Decl *D) {
+    StoreManager &StMgr = Eng.getStoreManager();
+    MemRegionManager &MRMgr = StMgr.getRegionManager();
+    const StackFrameContext *SFC =
+        Eng.getAnalysisDeclContextManager().getStackFrame(D);
+
+    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+      for (const auto *P : FD->parameters()) {
+        const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+        if (SFC->inTopFrame())
+          assert(isa<NonParamVarRegion>(Reg));
+        else
+          assert(isa<ParamVarRegion>(Reg));
+      }
+    } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+      for (const auto *P : CD->parameters()) {
+        const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+        if (SFC->inTopFrame())
+          assert(isa<NonParamVarRegion>(Reg));
+        else
+          assert(isa<ParamVarRegion>(Reg));
+      }
+    } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+      for (const auto *P : MD->parameters()) {
+        const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+        if (SFC->inTopFrame())
+          assert(isa<NonParamVarRegion>(Reg));
+        else
+          assert(isa<ParamVarRegion>(Reg));
+      }
+    }
+  }
+
+public:
+  ParamRegionTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
+
+  bool HandleTopLevelDecl(DeclGroupRef DG) override {
+    for (const auto *D : DG) {
+      performTest(D);
+    }
+    return true;
+  }
+};
+
+class ParamRegionTestAction : public ASTFrontendAction {
+public:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+                                                 StringRef File) override {
+    return std::make_unique<ParamRegionTestConsumer>(Compiler);
+  }
+};
+
+TEST(ParamRegion, ParamRegionTest) {
+  EXPECT_TRUE(
+      tooling::runToolOnCode(std::make_unique<ParamRegionTestAction>(),
+                             R"(void foo(int n) {
+                                  auto lambda = [n](int m) {
+                                    return n + m;
+                                  };
+
+                                  int k = lambda(2);
+                                }
+
+                                void bar(int l) {
+                                  foo(l);
+                                }
+
+                                struct S {
+                                  int n;
+                                  S(int nn): n(nn) {}
+                                };
+
+                                void baz(int p) {
+                                  S s(p);
+                                })"));
+  EXPECT_TRUE(
+      tooling::runToolOnCode(std::make_unique<ParamRegionTestAction>(),
+                             R"(@interface O
+                                + alloc;
+                                - initWithInt:(int)q;
+                                @end
+
+                                void qix(int r) {
+                                  O *o = [[O alloc] initWithInt:r];
+                                })",
+                             "input.m"));
+}
+
+} // namespace
+} // namespace ento
+} // namespace clang