[clang] [alpha.webkit.UnretainedLocalVarsChecker] Add a checker for local variables to NS and CF types. (PR #127554)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 17 18:34:48 PST 2025


https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/127554

>From d6c4e1a23dc0cc6850c5cfdceecc2e1be8943592 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 17 Feb 2025 18:18:09 -0800
Subject: [PATCH 1/2] [alpha.webkit.UnretainedLocalVarsChecker] Add a checker
 for local variables to NS and CF types.

This PR adds alpha.webkit.UnretainedLocalVarsChecker by generalizing RawPtrRefLocalVarsChecker.
It checks local variables to NS or CF types are guarded with a RetainPtr or not. The new checker
is effective for NS and CF types in Objective-C++ code without ARC, and it's effective for CF
types in code with ARC.
---
 clang/docs/analyzer/checkers.rst              |   6 +
 .../clang/StaticAnalyzer/Checkers/Checkers.td |   4 +
 .../Checkers/WebKit/ASTUtils.cpp              |   8 +-
 .../StaticAnalyzer/Checkers/WebKit/ASTUtils.h |   2 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp     |  85 ++++-
 .../Checkers/WebKit/PtrTypesSemantics.h       |  14 +-
 .../WebKit/RawPtrRefCallArgsChecker.cpp       |  24 ++
 .../WebKit/RawPtrRefLocalVarsChecker.cpp      |  62 ++-
 .../WebKit/UncountedLambdaCapturesChecker.cpp |   4 +-
 .../Checkers/WebKit/objc-mock-types.h         | 148 +++++++
 .../WebKit/unretained-local-vars-arc.mm       |  46 +++
 .../Checkers/WebKit/unretained-local-vars.mm  | 360 ++++++++++++++++++
 12 files changed, 742 insertions(+), 21 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
 create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm
 create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 707067358fdfe..0a976866ea8de 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3668,6 +3668,12 @@ Here are some examples of situations that we warn about as they *might* be poten
       RefCountable* uncounted = counted.get(); // warn
     }
 
+alpha.webkit.UnretainedLocalVarsChecker
+""""""""""""""""""""""""""""""""""""""
+The goal of this rule is to make sure that any NS or CF local variable is backed by a RetainPtr with lifetime that is strictly larger than the scope of the unretained local variable. To be on the safe side we require the scope of an unretained variable to be embedded in the scope of Retainptr object that backs it.
+
+The rules of when to use and not to use RetainPtr are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+
 Debug Checkers
 ---------------
 
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 9bf491eac1e0e..410f841630660 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1782,4 +1782,8 @@ def UncheckedLocalVarsChecker : Checker<"UncheckedLocalVarsChecker">,
   HelpText<"Check unchecked local variables.">,
   Documentation<HasDocumentation>;
 
+def UnretainedLocalVarsChecker : Checker<"UnretainedLocalVarsChecker">,
+  HelpText<"Check unretained local variables.">,
+  Documentation<HasDocumentation>;
+
 } // end alpha.webkit
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index a12853d01819f..dc86c4fcc64b1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -24,6 +24,8 @@ bool isSafePtr(clang::CXXRecordDecl *Decl) {
 
 bool tryToFindPtrOrigin(
     const Expr *E, bool StopAtFirstRefCountedObj,
+    std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
+    std::function<bool(const clang::QualType)> isSafePtrType,
     std::function<bool(const clang::Expr *, bool)> callback) {
   while (E) {
     if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
@@ -53,9 +55,9 @@ bool tryToFindPtrOrigin(
     }
     if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
       return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
-                                callback) &&
+                                isSafePtr, isSafePtrType, callback) &&
              tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
-                                callback);
+                                isSafePtr, isSafePtrType, callback);
     }
     if (auto *cast = dyn_cast<CastExpr>(E)) {
       if (StopAtFirstRefCountedObj) {
@@ -92,7 +94,7 @@ bool tryToFindPtrOrigin(
       }
 
       if (auto *callee = call->getDirectCallee()) {
-        if (isCtorOfRefCounted(callee) || isCtorOfCheckedPtr(callee)) {
+        if (isCtorOfSafePtr(callee)) {
           if (StopAtFirstRefCountedObj)
             return callback(E, true);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index b508043d0f37f..e2cc7b976adfc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -54,6 +54,8 @@ class Expr;
 /// Returns false if any of calls to callbacks returned false. Otherwise true.
 bool tryToFindPtrOrigin(
     const clang::Expr *E, bool StopAtFirstRefCountedObj,
+    std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
+    std::function<bool(const clang::QualType)> isSafePtrType,
     std::function<bool(const clang::Expr *, bool)> callback);
 
 /// For \p E referring to a ref-countable/-counted pointer/reference we return
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 8340de9e5a7a9..6acd5215ae553 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -13,6 +13,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
 #include <optional>
 
 using namespace clang;
@@ -117,6 +118,10 @@ bool isRefType(const std::string &Name) {
          Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
 }
 
+bool isRetainPtr(const std::string &Name) {
+  return Name == "RetainPtr";
+}
+
 bool isCheckedPtr(const std::string &Name) {
   return Name == "CheckedPtr" || Name == "CheckedRef";
 }
@@ -140,8 +145,15 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) {
   return isCheckedPtr(safeGetName(F));
 }
 
+bool isCtorOfRetainPtr(const clang::FunctionDecl *F) {
+  const std::string &FunctionName = safeGetName(F);
+  return FunctionName == "RetainPtr" || FunctionName == "adoptNS" ||
+         FunctionName == "adoptCF";
+}
+
 bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
-  return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F);
+  return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) ||
+         isCtorOfRetainPtr(F);
 }
 
 template <typename Predicate>
@@ -163,11 +175,16 @@ static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
   return false;
 }
 
-bool isSafePtrType(const clang::QualType T) {
+bool isRefOrCheckedPtrType(const clang::QualType T) {
   return isPtrOfType(
       T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); });
 }
 
+bool isRetainPtrType(const clang::QualType T) {
+  return isPtrOfType(
+      T, [](auto Name) { return Name == "RetainPtr"; });
+}
+
 bool isOwnerPtrType(const clang::QualType T) {
   return isPtrOfType(T, [](auto Name) {
     return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" ||
@@ -195,6 +212,37 @@ std::optional<bool> isUnchecked(const QualType T) {
   return isUnchecked(T->getAsCXXRecordDecl());
 }
 
+std::optional<bool> isUnretained(const QualType T, bool IsARCEnabled) {
+  if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) {
+    if (auto *Decl = Subst->getAssociatedDecl()) {
+      if (isRetainPtr(safeGetName(Decl)))
+        return false;
+    }
+  }
+  if ((ento::cocoa::isCocoaObjectRef(T) && !IsARCEnabled) ||
+      ento::coreFoundation::isCFObjectRef(T))
+    return true;
+
+  // RetainPtr strips typedef for CF*Ref. Manually check for struct __CF* types.
+  auto CanonicalType = T.getCanonicalType();
+  auto *Type = CanonicalType.getTypePtrOrNull();
+  if (!Type)
+    return false;
+  auto Pointee = Type->getPointeeType();
+  auto *PointeeType = Pointee.getTypePtrOrNull();
+  if (!PointeeType)
+    return false;
+  auto *Record = PointeeType->getAsStructureType();
+  if (!Record)
+    return false;
+  auto *Decl = Record->getDecl();
+  if (!Decl)
+    return false;
+  auto TypeName = Decl->getName();
+  return TypeName.starts_with("__CF") || TypeName.starts_with("__CG") ||
+         TypeName.starts_with("__CM");
+}
+
 std::optional<bool> isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
@@ -230,16 +278,20 @@ std::optional<bool> isUncheckedPtr(const QualType T) {
   return false;
 }
 
-std::optional<bool> isUnsafePtr(const QualType T) {
+std::optional<bool> isUnsafePtr(const QualType T, bool IsArcEnabled) {
   if (T->isPointerType() || T->isReferenceType()) {
     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
       auto isUncountedPtr = isUncounted(CXXRD);
       auto isUncheckedPtr = isUnchecked(CXXRD);
-      if (isUncountedPtr && isUncheckedPtr)
-        return *isUncountedPtr || *isUncheckedPtr;
+      auto isUnretainedPtr = isUnretained(T, IsArcEnabled);
+      std::optional<bool> result;
       if (isUncountedPtr)
-        return *isUncountedPtr;
-      return isUncheckedPtr;
+        result = *isUncountedPtr;
+      if (isUncheckedPtr)
+        result = result ? *result || *isUncheckedPtr : *isUncheckedPtr;
+      if (isUnretainedPtr)
+        result = result ? *result || *isUnretainedPtr : *isUnretainedPtr;
+      return result;
     }
   }
   return false;
@@ -263,16 +315,24 @@ std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) {
          method == "impl"))
       return true;
 
+    if (className == "RetainPtr" && method == "get")
+      return true;
+
     // Ref<T> -> T conversion
     // FIXME: Currently allowing any Ref<T> -> whatever cast.
     if (isRefType(className)) {
       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M))
-        return isUnsafePtr(maybeRefToRawOperator->getConversionType());
+        return isUnsafePtr(maybeRefToRawOperator->getConversionType(), false);
     }
 
     if (isCheckedPtr(className)) {
       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M))
-        return isUnsafePtr(maybeRefToRawOperator->getConversionType());
+        return isUnsafePtr(maybeRefToRawOperator->getConversionType(), false);
+    }
+
+    if (className == "RetainPtr") {
+      if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M))
+        return isUnsafePtr(maybeRefToRawOperator->getConversionType(), false);
     }
   }
   return false;
@@ -297,6 +357,13 @@ bool isCheckedPtr(const CXXRecordDecl *R) {
   return false;
 }
 
+bool isRetainPtr(const CXXRecordDecl *R) {
+  assert(R);
+  if (auto *TmplR = R->getTemplateInstantiationPattern())
+    return safeGetName(TmplR) == "RetainPtr";
+  return false;
+}
+
 bool isPtrConversion(const FunctionDecl *F) {
   assert(F);
   if (isCtorOfRefCounted(F))
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 9adfc0f1f5726..d220b42bbad6d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -51,6 +51,9 @@ bool isRefCounted(const clang::CXXRecordDecl *Class);
 /// \returns true if \p Class is a CheckedPtr / CheckedRef, false if not.
 bool isCheckedPtr(const clang::CXXRecordDecl *Class);
 
+/// \returns true if \p Class is a RetainPtr, false if not.
+bool isRetainPtr(const clang::CXXRecordDecl *Class);
+
 /// \returns true if \p Class is ref-countable AND not ref-counted, false if
 /// not, std::nullopt if inconclusive.
 std::optional<bool> isUncounted(const clang::QualType T);
@@ -59,6 +62,10 @@ std::optional<bool> isUncounted(const clang::QualType T);
 /// not, std::nullopt if inconclusive.
 std::optional<bool> isUnchecked(const clang::QualType T);
 
+/// \returns true if \p Class is NS or CF objects AND not retained, false if
+/// not, std::nullopt if inconclusive.
+std::optional<bool> isUnretained(const clang::QualType T, bool IsARCEnabled);
+
 /// \returns true if \p Class is ref-countable AND not ref-counted, false if
 /// not, std::nullopt if inconclusive.
 std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class);
@@ -77,11 +84,14 @@ std::optional<bool> isUncheckedPtr(const clang::QualType T);
 
 /// \returns true if \p T is either a raw pointer or reference to an uncounted
 /// or unchecked class, false if not, std::nullopt if inconclusive.
-std::optional<bool> isUnsafePtr(const QualType T);
+std::optional<bool> isUnsafePtr(const QualType T, bool IsArcEnabled);
 
 /// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or its
 /// variant, false if not.
-bool isSafePtrType(const clang::QualType T);
+bool isRefOrCheckedPtrType(const clang::QualType T);
+
+/// \returns true if \p T is a RetainPtr, false if not.
+bool isRetainPtrType(const clang::QualType T);
 
 /// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or
 /// unique_ptr, false if not.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 56fa72c100ec8..af236f404e2d5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -41,6 +41,8 @@ class RawPtrRefCallArgsChecker
 
   virtual std::optional<bool> isUnsafeType(QualType) const = 0;
   virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
+  virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
+  virtual bool isSafePtrType(const QualType type) const = 0;
   virtual const char *ptrKind() const = 0;
 
   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -141,6 +143,12 @@ class RawPtrRefCallArgsChecker
 
   bool isPtrOriginSafe(const Expr *Arg) const {
     return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
+                              [&](const clang::CXXRecordDecl *Record) {
+                                return isSafePtr(Record);
+                              },
+                              [&](const clang::QualType T) {
+                                return isSafePtrType(T);
+                              },
                               [&](const clang::Expr *ArgOrigin, bool IsSafe) {
                                 if (IsSafe)
                                   return true;
@@ -315,6 +323,14 @@ class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
     return isUncountedPtr(QT);
   }
 
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRefCounted(Record) || isCheckedPtr(Record);
+  }
+
+  bool isSafePtrType(const QualType type) const final {
+    return isRefOrCheckedPtrType(type);
+  }
+
   const char *ptrKind() const final { return "uncounted"; }
 };
 
@@ -332,6 +348,14 @@ class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
     return isUncheckedPtr(QT);
   }
 
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRefCounted(Record) || isCheckedPtr(Record);
+  }
+
+  bool isSafePtrType(const QualType type) const final {
+    return isRefOrCheckedPtrType(type);
+  }
+
   const char *ptrKind() const final { return "unchecked"; }
 };
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index d586668399502..f7eacdd6b54e0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/ParentMapContext.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -81,7 +82,7 @@ struct GuardianVisitor : DynamicRecursiveASTVisitor {
   bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
     auto MethodName = safeGetName(MCE->getMethodDecl());
     if (MethodName == "swap" || MethodName == "leakRef" ||
-        MethodName == "releaseNonNull") {
+        MethodName == "releaseNonNull" || MethodName == "clear") {
       auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
       if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
         if (VarRef->getDecl() == Guardian)
@@ -173,10 +174,12 @@ class RawPtrRefLocalVarsChecker
       : Bug(this, description, "WebKit coding guidelines") {}
 
   virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
+  virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
+  virtual bool isSafePtrType(const QualType) const = 0;
   virtual const char *ptrKind() const = 0;
 
-  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
-                    BugReporter &BRArg) const {
+  virtual void checkASTDecl(const TranslationUnitDecl *TUD,
+                            AnalysisManager &MGR, BugReporter &BRArg) const {
     BR = &BRArg;
 
     // The calls to checkAST* from AnalysisConsumer don't
@@ -263,6 +266,12 @@ class RawPtrRefLocalVarsChecker
     if (IsUncountedPtr && *IsUncountedPtr) {
       if (tryToFindPtrOrigin(
               Value, /*StopAtFirstRefCountedObj=*/false,
+              [&](const clang::CXXRecordDecl *Record) {
+                return isSafePtr(Record);
+              },
+              [&](const clang::QualType Type) {
+                return isSafePtrType(Type);
+              },
               [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
                 if (!InitArgOrigin || IsSafe)
                   return true;
@@ -292,8 +301,7 @@ class RawPtrRefLocalVarsChecker
                           MaybeGuardianArgType->getAsCXXRecordDecl();
                       if (MaybeGuardianArgCXXRecord) {
                         if (MaybeGuardian->isLocalVarDecl() &&
-                            (isRefCounted(MaybeGuardianArgCXXRecord) ||
-                             isCheckedPtr(MaybeGuardianArgCXXRecord) ||
+                            (isSafePtr(MaybeGuardianArgCXXRecord) ||
                              isRefcountedStringsHack(MaybeGuardian)) &&
                             isGuardedScopeEmbeddedInGuardianScope(
                                 V, MaybeGuardian))
@@ -365,6 +373,12 @@ class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
   std::optional<bool> isUnsafePtr(const QualType T) const final {
     return isUncountedPtr(T);
   }
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRefCounted(Record) || isCheckedPtr(Record);
+  }
+  bool isSafePtrType(const QualType type) const final {
+    return isRefOrCheckedPtrType(type);
+  }
   const char *ptrKind() const final { return "uncounted"; }
 };
 
@@ -376,9 +390,39 @@ class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
   std::optional<bool> isUnsafePtr(const QualType T) const final {
     return isUncheckedPtr(T);
   }
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRefCounted(Record) || isCheckedPtr(Record);
+  }
+  bool isSafePtrType(const QualType type) const final {
+    return isRefOrCheckedPtrType(type);
+  }
   const char *ptrKind() const final { return "unchecked"; }
 };
 
+class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
+  mutable bool IsARCEnabled{false};
+public:
+  UnretainedLocalVarsChecker()
+      : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
+                                  "provably backed by a RetainPtr") {}
+  std::optional<bool> isUnsafePtr(const QualType T) const final {
+    return isUnretained(T, IsARCEnabled);
+  }
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRetainPtr(Record);
+  }
+  bool isSafePtrType(const QualType type) const final {
+    return isRetainPtrType(type);
+  }
+  const char *ptrKind() const final { return "unretained"; }
+
+  void checkASTDecl(const TranslationUnitDecl *TUD,
+                    AnalysisManager &MGR, BugReporter &BRArg) const final {
+    IsARCEnabled = TUD->getLangOpts().ObjCAutoRefCount;
+    RawPtrRefLocalVarsChecker::checkASTDecl(TUD, MGR, BRArg);
+  }
+};
+
 } // namespace
 
 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
@@ -396,3 +440,11 @@ void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
 bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
   return true;
 }
+
+void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<UnretainedLocalVarsChecker>();
+}
+
+bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
+  return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index 9527993d0edeb..fd710177e5426 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -61,7 +61,7 @@ class UncountedLambdaCapturesChecker
       }
 
       bool shouldCheckThis() {
-        auto result = !ClsType.isNull() ? isUnsafePtr(ClsType) : std::nullopt;
+        auto result = !ClsType.isNull() ? isUnsafePtr(ClsType, false) : std::nullopt;
         return result && *result;
       }
 
@@ -305,7 +305,7 @@ class UncountedLambdaCapturesChecker
         if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
           continue;
         QualType CapturedVarQualType = CapturedVar->getType();
-        auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());
+        auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType(), false);
         if (IsUncountedPtr && *IsUncountedPtr)
           reportBug(C, CapturedVar, CapturedVarQualType);
       } else if (C.capturesThis() && shouldCheckThis) {
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
new file mode 100644
index 0000000000000..69fbbdcd88f78
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -0,0 +1,148 @@
+
+typedef const void * CFTypeRef;
+typedef signed long CFIndex;
+typedef const struct __CFAllocator * CFAllocatorRef;
+typedef const struct __CFString * CFStringRef;
+typedef const struct __CFArray * CFArrayRef;
+typedef struct __CFArray * CFMutableArrayRef;
+extern const CFAllocatorRef kCFAllocatorDefault;
+CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity);
+extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
+CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues);
+CFIndex CFArrayGetCount(CFArrayRef theArray);
+extern CFTypeRef CFRetain(CFTypeRef cf);
+extern void CFRelease(CFTypeRef cf);
+
+__attribute__((objc_root_class))
+ at interface NSObject
++ (instancetype) alloc;
+- (instancetype) init;
+- (instancetype)retain;
+- (void)release;
+ at end
+
+ at interface SomeObj : NSObject
+- (void)doWork;
+- (SomeObj *)other;
+- (SomeObj *)next;
+- (int)value;
+ at end
+
+ at interface OtherObj : SomeObj
+- (void)doMoreWork:(OtherObj *)other;
+ at end
+
+namespace WTF {
+
+template<typename T> class RetainPtr;
+template<typename T> RetainPtr<T> adoptNS(T*);
+template<typename T> RetainPtr<T> adoptCF(T);
+
+template <typename T, typename S> T *downcast(S *t) { return static_cast<T*>(t); }
+
+template <typename T> struct RemovePointer {
+  typedef T Type;
+};
+
+template <typename T> struct RemovePointer<T*> {
+  typedef T Type;
+};
+
+template <typename T> struct IsPointer {
+  static constexpr bool value = false;
+};
+
+template <typename T> struct IsPointer<T*> {
+  static constexpr bool value = true;
+};
+
+template <typename T, bool isPointer> struct PtrTypeToCFOrObjC {
+  using PtrType = T;
+};
+
+template <typename T> struct PtrTypeToCFOrObjC<T, true> {
+  using PtrType = T;
+};
+
+template <typename T> struct PtrTypeToCFOrObjC<T, false> {
+  using PtrType = T*;
+};
+
+template <typename T> struct RetainPtr {
+//  using ValueType = typename RemovePointer<T>::Type;
+  using PtrType = typename PtrTypeToCFOrObjC<T, IsPointer<T>::value>::PtrType;
+  PtrType t;
+
+  RetainPtr() : t(nullptr) { }
+
+  RetainPtr(PtrType t)
+    : t(t) {
+    if (t)
+      CFRetain(t);
+  }
+  RetainPtr(RetainPtr&& o)
+    : RetainPtr(o.t)
+  {
+    o.t = nullptr;
+  }
+  RetainPtr(const RetainPtr& o)
+    : RetainPtr(o.t)
+  {
+  }
+  RetainPtr operator=(const RetainPtr& o)
+  {
+    if (t)
+      CFRelease(t);
+    t = o.t;
+    if (t)
+      CFRetain(t);
+    return *this;
+  }
+  ~RetainPtr() {
+    clear();
+  }
+  void clear() {
+    if (t)
+      CFRelease(t);
+    t = nullptr;
+  }
+  void swap(RetainPtr& o) {
+    PtrType tmp = t;
+    t = o.t;
+    o.t = tmp;
+  }
+  PtrType get() const { return t; }
+  PtrType operator->() const { return t; }
+  T &operator*() const { return *t; }
+  RetainPtr &operator=(PtrType t) {
+    RetainPtr o(t);
+    swap(o);
+    return *this;
+  }
+  operator PtrType() const { return t; }
+  operator bool() const { return t; }
+
+private:
+  template <typename U> friend RetainPtr<U> adoptNS(U*);
+  template <typename U> friend RetainPtr<U> adoptCF(U);
+
+  enum AdoptTag { Adopt };
+  RetainPtr(PtrType t, AdoptTag) : t(t) { }
+};
+
+template <typename T>
+RetainPtr<T> adoptNS(T* t) {
+  return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+}
+
+template <typename T>
+RetainPtr<T> adoptCF(T t) {
+  return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+}
+
+}
+
+using WTF::RetainPtr;
+using WTF::adoptNS;
+using WTF::adoptCF;
+using WTF::downcast;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm
new file mode 100644
index 0000000000000..92a718f7e3a4c
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm
@@ -0,0 +1,46 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -fobjc-arc -verify %s
+
+#import "objc-mock-types.h"
+
+SomeObj *provide();
+void someFunction();
+
+namespace raw_ptr {
+
+void foo() {
+  SomeObj *bar = [[SomeObj alloc] init];
+  [bar doWork];
+}
+
+void foo2() {
+  SomeObj *bar = provide();
+  [bar doWork];
+}
+
+void bar() {
+  CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 10);
+  // expected-warning at -1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  CFArrayAppendValue(array, nullptr);
+}
+
+} // namespace raw_ptr
+
+ at interface AnotherObj : NSObject
+- (void)foo:(SomeObj *)obj;
+ at end
+
+ at implementation AnotherObj
+- (void)foo:(SomeObj*)obj {
+  SomeObj* obj2 = obj;
+  SomeObj* obj3 = provide();
+  obj = nullptr;
+  [obj2 doWork];
+  [obj3 doWork];
+}
+
+- (void)bar {
+  CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 10);
+  // expected-warning at -1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  CFArrayAppendValue(array, nullptr);
+}
+ at end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
new file mode 100644
index 0000000000000..40333fc73e8c8
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
@@ -0,0 +1,360 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -verify %s
+
+#import "objc-mock-types.h"
+
+void someFunction();
+
+namespace raw_ptr {
+void foo() {
+  SomeObj *bar;
+  // FIXME: later on we might warn on uninitialized vars too
+}
+
+void bar(SomeObj *) {}
+} // namespace raw_ptr
+
+namespace pointer {
+void foo_ref() {
+  SomeObj *bar = [[SomeObj alloc] init];
+  // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  [bar doWork];
+}
+
+bool bar_ref(SomeObj *obj) {
+    return !!obj;
+}
+
+void cf_ptr() {
+  CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 10);
+  // expected-warning at -1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  CFArrayAppendValue(array, nullptr);
+}
+} // namespace pointer
+
+namespace guardian_scopes {
+void foo1() {
+  RetainPtr<SomeObj> foo;
+  {
+    SomeObj *bar = foo.get();
+  }
+}
+
+void foo2() {
+  RetainPtr<SomeObj> foo;
+  // missing embedded scope here
+  SomeObj *bar = foo.get();
+  // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  [bar doWork];
+}
+
+void foo3() {
+  RetainPtr<SomeObj> foo;
+  {
+    { SomeObj *bar = foo.get(); }
+  }
+}
+
+void foo4() {
+  {
+    RetainPtr<SomeObj> foo;
+    { SomeObj *bar = foo.get(); }
+  }
+}
+
+struct SelfReferencingStruct {
+  SelfReferencingStruct* ptr;
+  SomeObj* obj { nullptr };
+};
+
+void foo7(SomeObj* obj) {
+  SelfReferencingStruct bar = { &bar, obj };
+  [bar.obj doWork];
+}
+
+void foo8(SomeObj* obj) {
+  RetainPtr<SomeObj> foo;
+
+  {
+    SomeObj *bar = foo.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    foo = nullptr;
+    [bar doWork];
+  }
+  RetainPtr<SomeObj> baz;
+  {
+    SomeObj *bar = baz.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    baz = obj;
+    [bar doWork];
+  }
+
+  foo = nullptr;
+  {
+    SomeObj *bar = foo.get();
+    // No warning. It's okay to mutate RefPtr in an outer scope.
+    [bar doWork];
+  }
+  foo = obj;
+  {
+    SomeObj *bar = foo.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    foo.clear();
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = foo.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    foo = obj ? obj : nullptr;
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = [foo.get() other] ? foo.get() : nullptr;
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    foo = nullptr;
+    [bar doWork];
+  }
+}
+
+void foo9(SomeObj* o) {
+  RetainPtr<SomeObj> guardian(o);
+  {
+    SomeObj *bar = guardian.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    guardian = o; // We don't detect that we're setting it to the same value.
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = guardian.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    RetainPtr<SomeObj> other(bar); // We don't detect other has the same value as guardian.
+    guardian.swap(other);
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = guardian.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    RetainPtr<SomeObj> other(static_cast<RetainPtr<SomeObj>&&>(guardian));
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = guardian.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    guardian.clear();
+    [bar doWork];
+  }
+  {
+    SomeObj *bar = guardian.get();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    guardian = [o other] ? o : bar;
+    [bar doWork];
+  }
+}
+
+bool trivialFunction(CFMutableArrayRef array) { return !!array; }
+void foo10() {
+  RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10));
+  {
+    CFMutableArrayRef arrayRef = array.get();
+    CFArrayAppendValue(arrayRef, nullptr);
+  }
+  {
+    CFMutableArrayRef arrayRef = array.get();
+    // expected-warning at -1{{Local variable 'arrayRef' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    array = nullptr;
+    CFArrayAppendValue(arrayRef, nullptr);
+  }
+  {
+    CFMutableArrayRef arrayRef = array.get();
+    if (trivialFunction(arrayRef))
+      arrayRef = nullptr;
+  }
+}
+
+} // namespace guardian_scopes
+
+namespace auto_keyword {
+class Foo {
+  SomeObj *provide_obj();
+  CFMutableArrayRef provide_cf_array();
+  void doWork(CFMutableArrayRef);
+
+  void evil_func() {
+    SomeObj *bar = provide_obj();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    auto *baz = provide_obj();
+    // expected-warning at -1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    auto *baz2 = this->provide_obj();
+    // expected-warning at -1{{Local variable 'baz2' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    [[clang::suppress]] auto *baz_suppressed = provide_obj(); // no-warning
+  }
+
+  void func() {
+    SomeObj *bar = provide_obj();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    if (bar)
+      [bar doWork];
+  }
+
+  void bar() {
+    auto bar = provide_cf_array();
+    // expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    doWork(bar);
+    [[clang::suppress]] auto baz = provide_cf_array(); // no-warning
+    doWork(baz);
+  }
+
+};
+} // namespace auto_keyword
+
+namespace guardian_casts {
+void foo1() {
+  RetainPtr<NSObject> foo;
+  {
+    SomeObj *bar = downcast<SomeObj>(foo.get());
+    [bar doWork];
+  }
+}
+
+void foo2() {
+  RetainPtr<NSObject> foo;
+  {
+    SomeObj *bar = static_cast<SomeObj *>(downcast<SomeObj>(foo.get()));
+    someFunction();
+  }
+}
+} // namespace guardian_casts
+
+namespace conditional_op {
+SomeObj *provide_obj();
+bool bar();
+
+void foo() {
+  SomeObj *a = bar() ? nullptr : provide_obj();
+  // expected-warning at -1{{Local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  RetainPtr<SomeObj> b = provide_obj();
+  {
+    SomeObj* c = bar() ? nullptr : b.get();
+    [c doWork];
+    SomeObj* d = bar() ? b.get() : nullptr;
+    [d doWork];
+  }
+}
+
+} // namespace conditional_op
+
+namespace local_assignment_basic {
+
+SomeObj *provide_obj();
+
+void foo(SomeObj* a) {
+  SomeObj* b = a;
+  // expected-warning at -1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  if ([b other])
+    b = provide_obj();
+}
+
+void bar(SomeObj* a) {
+  SomeObj* b;
+  // expected-warning at -1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  b = provide_obj();
+}
+
+void baz() {
+  RetainPtr<SomeObj> a = provide_obj();
+  {
+    SomeObj* b = a.get();
+    // expected-warning at -1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+    b = provide_obj();
+  }
+}
+
+} // namespace local_assignment_basic
+
+namespace local_assignment_to_parameter {
+
+SomeObj *provide_obj();
+void someFunction();
+
+void foo(SomeObj* a) {
+  a = provide_obj();
+  // expected-warning at -1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  someFunction();
+  [a doWork];
+}
+
+CFMutableArrayRef provide_cf_array();
+void doWork(CFMutableArrayRef);
+
+void bar(CFMutableArrayRef a) {
+  a = provide_cf_array();
+  // expected-warning at -1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  doWork(a);
+}
+
+} // namespace local_assignment_to_parameter
+
+namespace local_assignment_to_static_local {
+
+SomeObj *provide_obj();
+void someFunction();
+
+void foo() {
+  static SomeObj* a = nullptr;
+  // expected-warning at -1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  a = provide_obj();
+  someFunction();
+  [a doWork];
+}
+
+CFMutableArrayRef provide_cf_array();
+void doWork(CFMutableArrayRef);
+
+void bar() {
+  static CFMutableArrayRef a = nullptr;
+  // expected-warning at -1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+  a = provide_cf_array();
+  doWork(a);
+}
+
+} // namespace local_assignment_to_static_local
+
+namespace local_assignment_to_global {
+
+SomeObj *provide_obj();
+void someFunction();
+
+SomeObj* g_a = nullptr;
+// expected-warning at -1{{Global variable 'local_assignment_to_global::g_a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+
+void foo() {
+  g_a = provide_obj();
+  someFunction();
+  [g_a doWork];
+}
+
+CFMutableArrayRef provide_cf_array();
+void doWork(CFMutableArrayRef);
+
+CFMutableArrayRef g_b = nullptr;
+// expected-warning at -1{{Global variable 'local_assignment_to_global::g_b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
+
+void bar() {
+  g_b = provide_cf_array();
+  doWork(g_b);
+}
+
+} // namespace local_assignment_to_global
+
+namespace local_var_for_singleton {
+  SomeObj *singleton();
+  SomeObj *otherSingleton();
+  void foo() {
+    SomeObj* bar = singleton();
+    SomeObj* baz = otherSingleton();
+  }
+
+  CFMutableArrayRef cfSingleton();
+  void bar() {
+    CFMutableArrayRef cf = cfSingleton();
+  }
+}

>From e9643fa0070b9f77f89c3211f51a6e5cd09da38f Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 17 Feb 2025 18:34:31 -0800
Subject: [PATCH 2/2] Fix formatting.

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 10 ++--
 .../WebKit/RawPtrRefCallArgsChecker.cpp       | 47 +++++++++----------
 .../WebKit/RawPtrRefLocalVarsChecker.cpp      | 11 ++---
 .../WebKit/UncountedLambdaCapturesChecker.cpp |  3 +-
 4 files changed, 32 insertions(+), 39 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6acd5215ae553..810fe49355855 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -118,9 +118,7 @@ bool isRefType(const std::string &Name) {
          Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
 }
 
-bool isRetainPtr(const std::string &Name) {
-  return Name == "RetainPtr";
-}
+bool isRetainPtr(const std::string &Name) { return Name == "RetainPtr"; }
 
 bool isCheckedPtr(const std::string &Name) {
   return Name == "CheckedPtr" || Name == "CheckedRef";
@@ -152,8 +150,7 @@ bool isCtorOfRetainPtr(const clang::FunctionDecl *F) {
 }
 
 bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
-  return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) ||
-         isCtorOfRetainPtr(F);
+  return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) || isCtorOfRetainPtr(F);
 }
 
 template <typename Predicate>
@@ -181,8 +178,7 @@ bool isRefOrCheckedPtrType(const clang::QualType T) {
 }
 
 bool isRetainPtrType(const clang::QualType T) {
-  return isPtrOfType(
-      T, [](auto Name) { return Name == "RetainPtr"; });
+  return isPtrOfType(T, [](auto Name) { return Name == "RetainPtr"; });
 }
 
 bool isOwnerPtrType(const clang::QualType T) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index af236f404e2d5..d633fbcbd798b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -142,31 +142,28 @@ class RawPtrRefCallArgsChecker
   }
 
   bool isPtrOriginSafe(const Expr *Arg) const {
-    return tryToFindPtrOrigin(Arg, /*StopAtFirstRefCountedObj=*/true,
-                              [&](const clang::CXXRecordDecl *Record) {
-                                return isSafePtr(Record);
-                              },
-                              [&](const clang::QualType T) {
-                                return isSafePtrType(T);
-                              },
-                              [&](const clang::Expr *ArgOrigin, bool IsSafe) {
-                                if (IsSafe)
-                                  return true;
-                                if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
-                                  // foo(nullptr)
-                                  return true;
-                                }
-                                if (isa<IntegerLiteral>(ArgOrigin)) {
-                                  // FIXME: Check the value.
-                                  // foo(NULL)
-                                  return true;
-                                }
-                                if (isASafeCallArg(ArgOrigin))
-                                  return true;
-                                if (EFA.isACallToEnsureFn(ArgOrigin))
-                                  return true;
-                                return false;
-                              });
+    return tryToFindPtrOrigin(
+        Arg, /*StopAtFirstRefCountedObj=*/true,
+        [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
+        [&](const clang::QualType T) { return isSafePtrType(T); },
+        [&](const clang::Expr *ArgOrigin, bool IsSafe) {
+          if (IsSafe)
+            return true;
+          if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
+            // foo(nullptr)
+            return true;
+          }
+          if (isa<IntegerLiteral>(ArgOrigin)) {
+            // FIXME: Check the value.
+            // foo(NULL)
+            return true;
+          }
+          if (isASafeCallArg(ArgOrigin))
+            return true;
+          if (EFA.isACallToEnsureFn(ArgOrigin))
+            return true;
+          return false;
+        });
   }
 
   bool shouldSkipCall(const CallExpr *CE) const {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index f7eacdd6b54e0..d0a82824798cc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -14,8 +14,8 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/ParentMapContext.h"
-#include "clang/Basic/SourceLocation.h"
 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -269,9 +269,7 @@ class RawPtrRefLocalVarsChecker
               [&](const clang::CXXRecordDecl *Record) {
                 return isSafePtr(Record);
               },
-              [&](const clang::QualType Type) {
-                return isSafePtrType(Type);
-              },
+              [&](const clang::QualType Type) { return isSafePtrType(Type); },
               [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
                 if (!InitArgOrigin || IsSafe)
                   return true;
@@ -401,6 +399,7 @@ class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
 
 class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
   mutable bool IsARCEnabled{false};
+
 public:
   UnretainedLocalVarsChecker()
       : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
@@ -416,8 +415,8 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
   }
   const char *ptrKind() const final { return "unretained"; }
 
-  void checkASTDecl(const TranslationUnitDecl *TUD,
-                    AnalysisManager &MGR, BugReporter &BRArg) const final {
+  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+                    BugReporter &BRArg) const final {
     IsARCEnabled = TUD->getLangOpts().ObjCAutoRefCount;
     RawPtrRefLocalVarsChecker::checkASTDecl(TUD, MGR, BRArg);
   }
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index fd710177e5426..506442f352288 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -61,7 +61,8 @@ class UncountedLambdaCapturesChecker
       }
 
       bool shouldCheckThis() {
-        auto result = !ClsType.isNull() ? isUnsafePtr(ClsType, false) : std::nullopt;
+        auto result =
+            !ClsType.isNull() ? isUnsafePtr(ClsType, false) : std::nullopt;
         return result && *result;
       }
 



More information about the cfe-commits mailing list