[llvm-branch-commits] [clang] [analyzer][NFC] Index parameter lookups by argument position (PR #221977)

Benedek Kaibas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Sep 11 07:00:23 PDT 2026


https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/221977

>From b864364327448505a9d2911de02cb3f55c72fe06 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 7 Sep 2026 15:22:38 +0200
Subject: [PATCH 1/4] [analyzer] Replace getAdjustedParameterIndex with
 getDeclaredParameterIndex in CallEvent.cpp

---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 8eab3f19dc0a8..cbcc0c7d3b717 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -285,10 +285,10 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
     // TODO: This is unnecessary when there's no destructor, but that's
     // currently hard to figure out.
     if (getKind() != CE_CXXAllocator)
-      if (isArgumentConstructedDirectly(Idx))
-        if (auto AdjIdx = getAdjustedParameterIndex(Idx))
+      if (isArgumentConstructedDirectly(getASTArgumentIndex(Idx)))
+        if (std::optional<unsigned> DeclParamIdx = getDeclaredParameterIndex(Idx))
           if (const TypedValueRegion *TVR =
-                  getParameterLocation(*AdjIdx, BlockCount))
+                  getParameterLocation(*DeclParamIdx, BlockCount))
             ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
   }
 

>From 7c0f02d11535ceeba342e2328d4d432b5afdd50e Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 7 Sep 2026 15:36:43 +0200
Subject: [PATCH 2/4] Run clang-format once again.

---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index cbcc0c7d3b717..4eed56b2963b5 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -286,7 +286,8 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
     // currently hard to figure out.
     if (getKind() != CE_CXXAllocator)
       if (isArgumentConstructedDirectly(getASTArgumentIndex(Idx)))
-        if (std::optional<unsigned> DeclParamIdx = getDeclaredParameterIndex(Idx))
+        if (std::optional<unsigned> DeclParamIdx =
+                getDeclaredParameterIndex(Idx))
           if (const TypedValueRegion *TVR =
                   getParameterLocation(*DeclParamIdx, BlockCount))
             ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));

>From 3aa448a5e8cd832072cbeb0edfec6f24b2edf922 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Tue, 8 Sep 2026 14:18:45 +0200
Subject: [PATCH 3/4] [analyzer][NFC] Index parameter lookups by argument
 position

---
 .../Checkers/CallAndMessageChecker.cpp        |  9 +++---
 .../Checkers/NonNullParamChecker.cpp          | 31 ++++++++++++++++---
 .../Checkers/SmartPtrModeling.cpp             |  4 +++
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 3997da0ba5dc5..b9fbc32d50ff9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -577,14 +577,13 @@ ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
 
   const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
 
-  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+  ArrayRef<ParmVarDecl *> Params = Call.parameters();
   for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
-    const ParmVarDecl *ParamDecl = nullptr;
-    if (FD && i < FD->getNumParams())
-      ParamDecl = FD->getParamDecl(i);
+    // For variadic functions a corresponding parameter decl might not exist.
+    const ParmVarDecl *PVD = i < Params.size() ? Params[i] : nullptr;
     if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
                            Call.getArgExpr(i), i, checkUninitFields, Call, BT,
-                           ParamDecl))
+                           PVD))
       return nullptr;
   }
   return State;
diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 2cc633fa5649f..27f393cbb5489 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -46,10 +46,27 @@ class NonNullParamChecker
                                   const Expr *ArgE) const;
 };
 
+/// The bit vectors below are indexed by \p Call's own argument numbering
+/// and not by the declared parameter index. The two differ for an
+/// explicit object parameter. The explicit object parameter is declared
+/// as #0, but \p Call does not count the object as one of its arguments.
+///
+/// \returns how many leading declared parameters \p Call has no argument
+/// for.
+template <class CallType>
+unsigned getDeclParameterOffset(const CallType &Call) {
+  if (const auto *FD = llvm::dyn_cast_or_null<FunctionDecl>(Call.getDecl())) {
+    assert(FD->getNumParams() >= Call.parameters().size());
+    return FD->getNumParams() - Call.parameters().size();
+  }
+  return 0;
+}
+
 template <class CallType>
 void setBitsAccordingToFunctionAttributes(const CallType &Call,
                                           llvm::SmallBitVector &AttrNonNull) {
   const Decl *FD = Call.getDecl();
+  const unsigned Offset = getDeclParameterOffset(Call);
 
   for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
     if (!NonNull->args_size()) {
@@ -63,9 +80,12 @@ void setBitsAccordingToFunctionAttributes(const CallType &Call,
       // 'nonnull' attribute's parameters are 1-based and should be adjusted to
       // match actual AST parameter/argument indices.
       unsigned IdxAST = Idx.getASTIndex();
-      if (IdxAST >= AttrNonNull.size())
+      if (IdxAST < Offset)
+        continue;
+      const unsigned IdxArg = IdxAST - Offset;
+      if (IdxArg >= AttrNonNull.size())
         continue;
-      AttrNonNull.set(IdxAST);
+      AttrNonNull.set(IdxArg);
     }
   }
 }
@@ -73,13 +93,14 @@ void setBitsAccordingToFunctionAttributes(const CallType &Call,
 template <class CallType>
 void setBitsAccordingToParameterAttributes(const CallType &Call,
                                            llvm::SmallBitVector &AttrNonNull) {
+  unsigned ArgIdx = 0;
   for (const ParmVarDecl *Parameter : Call.parameters()) {
-    unsigned ParameterIndex = Parameter->getFunctionScopeIndex();
-    if (ParameterIndex == AttrNonNull.size())
+    if (ArgIdx == AttrNonNull.size())
       break;
 
     if (Parameter->hasAttr<NonNullAttr>())
-      AttrNonNull.set(ParameterIndex);
+      AttrNonNull.set(ArgIdx);
+    ++ArgIdx;
   }
 }
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index ad81e5076f931..cf5ef57a07b26 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -837,6 +837,10 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
   const MemRegion *ThisRegion =
       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
 
+  // `getCXXThisVal` does not model explicit object parameters and
+  // in case of an explicit object parameter it can be null.
+  if (!ThisRegion)
+    return;
   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
 
   SVal InnerPointerVal;

>From d957c72209b2e827e3cbbc917f249e3bb72c3e69 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Fri, 11 Sep 2026 15:59:40 +0200
Subject: [PATCH 4/4] Add comment for indexes and FIXME.

---
 .../Checkers/CallAndMessageChecker.cpp        | 21 ++++++++++++++-----
 .../Checkers/SmartPtrModeling.cpp             |  2 ++
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index b9fbc32d50ff9..8c98ec256b03c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -578,12 +578,23 @@ ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
   const BugType &BT = isa<ObjCMethodCall>(Call) ? MsgArgBug : CallArgBug;
 
   ArrayRef<ParmVarDecl *> Params = Call.parameters();
-  for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
+  // CallEvent uses three index spaces. ASTArgIdx indexes the AST argument list
+  // (CallExpr::getArg()). DeclParamIdx indexes the callee's declared parameters
+  // (FunctionDecl::getParamDecl()). NativeIdx indexes the arguments exposed by
+  // CallEvent through getArgExpr() and is bounded by getNumArgs().
+  //
+  // FIXME: CallEvent does not yet treat an explicit object parameter as an
+  // object the way it does for implicit `this`, so it stays a regular argument.
+  // Indexing still lines up today only by coincidence, because parameters()
+  // includes it too.
+  for (unsigned NativeIdx = 0, e = Call.getNumArgs(); NativeIdx != e; ++NativeIdx) {
     // For variadic functions a corresponding parameter decl might not exist.
-    const ParmVarDecl *PVD = i < Params.size() ? Params[i] : nullptr;
-    if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
-                           Call.getArgExpr(i), i, checkUninitFields, Call, BT,
-                           PVD))
+    const ParmVarDecl *PVD =
+        NativeIdx < Params.size() ? Params[NativeIdx] : nullptr;
+    if (PreVisitProcessArg(C, Call.getArgSVal(NativeIdx),
+                           Call.getArgSourceRange(NativeIdx),
+                           Call.getArgExpr(NativeIdx), NativeIdx,
+                           checkUninitFields, Call, BT, PVD))
       return nullptr;
   }
   return State;
diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index cf5ef57a07b26..fde0153220b89 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -839,6 +839,8 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
 
   // `getCXXThisVal` does not model explicit object parameters and
   // in case of an explicit object parameter it can be null.
+  // TODO: Instead of the early return the checker should support
+  // explicit object parameters.
   if (!ThisRegion)
     return;
   QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();



More information about the llvm-branch-commits mailing list