[clang] ef353b0 - Introduce a new WebKit checker for a unchecked call arguments (#113708) (#114522)

via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 7 08:34:40 PST 2024


Author: Ryosuke Niwa
Date: 2024-11-07T08:34:37-08:00
New Revision: ef353b02b0728f2328c3494c70dc426d58d23508

URL: https://github.com/llvm/llvm-project/commit/ef353b02b0728f2328c3494c70dc426d58d23508
DIFF: https://github.com/llvm/llvm-project/commit/ef353b02b0728f2328c3494c70dc426d58d23508.diff

LOG: Introduce a new WebKit checker for a unchecked call arguments (#113708) (#114522)

This PR introduces alpha.webkit.UncheckedCallArgsChecker which detects a
function argument which is a raw reference or a raw pointer to a
CheckedPtr capable object.

Added: 
    clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
    clang/test/Analysis/Checkers/WebKit/call-args-checked-ptr.cpp
    clang/test/Analysis/Checkers/WebKit/call-args-checked-return-value.cpp

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
    llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn

Removed: 
    clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 8a9e2076a875f3..f34b25cd04bd18 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3560,6 +3560,12 @@ We also define a set of safe transformations which if passed a safe value as an
 - casts
 - unary operators like ``&`` or ``*``
 
+alpha.webkit.UncheckedCallArgsChecker
+"""""""""""""""""""""""""""""""""""""
+The goal of this rule is to make sure that lifetime of any dynamically allocated CheckedPtr capable object passed as a call argument keeps its memory region past the end of the call. This applies to call to any function, method, lambda, function pointer or functor. CheckedPtr capable objects aren't supposed to be allocated on stack so we check arguments for parameters of raw pointers and references to unchecked types.
+
+The rules of when to use and not to use CheckedPtr / CheckedRef are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+
 alpha.webkit.UncountedLocalVarsChecker
 """"""""""""""""""""""""""""""""""""""
 The goal of this rule is to make sure that any uncounted local variable is backed by a ref-counted object with lifetime that is strictly larger than the scope of the uncounted local variable. To be on the safe side we require the scope of an uncounted variable to be embedded in the scope of ref-counted object that backs it.

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 86d62b58cac0fb..b03e707d638742 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1760,6 +1760,10 @@ def UncountedCallArgsChecker : Checker<"UncountedCallArgsChecker">,
   HelpText<"Check uncounted call arguments.">,
   Documentation<HasDocumentation>;
 
+def UncheckedCallArgsChecker : Checker<"UncheckedCallArgsChecker">,
+  HelpText<"Check unchecked call arguments.">,
+  Documentation<HasDocumentation>;
+
 def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">,
   HelpText<"Check uncounted local variables.">,
   Documentation<HasDocumentation>;

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index c6e5afdc42424c..f40318f46dea1a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -134,7 +134,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   WebKit/ASTUtils.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
-  WebKit/UncountedCallArgsChecker.cpp
+  WebKit/RawPtrRefCallArgsChecker.cpp
   WebKit/UncountedLambdaCapturesChecker.cpp
   WebKit/RawPtrRefLocalVarsChecker.cpp
 

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 487cde26012e56..31bebdb07dbdc2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -174,6 +174,16 @@ std::optional<bool> isUncounted(const QualType T) {
   return isUncounted(T->getAsCXXRecordDecl());
 }
 
+std::optional<bool> isUnchecked(const QualType T) {
+  if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) {
+    if (auto *Decl = Subst->getAssociatedDecl()) {
+      if (isCheckedPtr(safeGetName(Decl)))
+        return false;
+    }
+  }
+  return isUnchecked(T->getAsCXXRecordDecl());
+}
+
 std::optional<bool> isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
@@ -188,7 +198,7 @@ std::optional<bool> isUncounted(const CXXRecordDecl* Class)
 }
 
 std::optional<bool> isUnchecked(const CXXRecordDecl *Class) {
-  if (isCheckedPtr(Class))
+  if (!Class || isCheckedPtr(Class))
     return false; // Cheaper than below
   return isCheckedPtrCapable(Class);
 }

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 30bdaed706bb53..7c6c0a63f22aba 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -55,10 +55,18 @@ bool isCheckedPtr(const clang::CXXRecordDecl *Class);
 /// not, std::nullopt if inconclusive.
 std::optional<bool> isUncounted(const clang::QualType T);
 
+/// \returns true if \p Class is CheckedPtr capable AND not checked, false if
+/// not, std::nullopt if inconclusive.
+std::optional<bool> isUnchecked(const clang::QualType T);
+
 /// \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);
 
+/// \returns true if \p Class is CheckedPtr capable AND not checked, false if
+/// not, std::nullopt if inconclusive.
+std::optional<bool> isUnchecked(const clang::CXXRecordDecl *Class);
+
 /// \returns true if \p T is either a raw pointer or reference to an uncounted
 /// class, false if not, std::nullopt if inconclusive.
 std::optional<bool> isUncountedPtr(const clang::QualType T);

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
similarity index 81%
rename from clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
rename to clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 177c196127c7f0..d7f766da6aae06 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -1,4 +1,4 @@
-//=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==//
+//=======- RawPtrRefCallArgsChecker.cpp --------------------------*- C++ -*-==//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -27,16 +27,20 @@ using namespace ento;
 
 namespace {
 
-class UncountedCallArgsChecker
+class RawPtrRefCallArgsChecker
     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
-  BugType Bug{this,
-            "Uncounted call argument for a raw pointer/reference parameter",
-            "WebKit coding guidelines"};
+  BugType Bug;
   mutable BugReporter *BR;
 
   TrivialFunctionAnalysis TFA;
 
 public:
+  RawPtrRefCallArgsChecker(const char *description)
+      : Bug(this, description, "WebKit coding guidelines") {}
+
+  virtual std::optional<bool> isUnsafeType(QualType) const = 0;
+  virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
+  virtual const char *ptrKind() const = 0;
 
   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
                     BugReporter &BRArg) const {
@@ -48,10 +52,10 @@ class UncountedCallArgsChecker
     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
       using Base = RecursiveASTVisitor<LocalVisitor>;
 
-      const UncountedCallArgsChecker *Checker;
+      const RawPtrRefCallArgsChecker *Checker;
       Decl *DeclWithIssue{nullptr};
 
-      explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
+      explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker)
           : Checker(Checker) {
         assert(Checker);
       }
@@ -89,7 +93,8 @@ class UncountedCallArgsChecker
     if (auto *F = CE->getDirectCallee()) {
       // Skip the first argument for overloaded member operators (e. g. lambda
       // or std::function call operator).
-      unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+      unsigned ArgIdx =
+          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
 
       if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
         if (auto *MD = MemberCallExpr->getMethodDecl()) {
@@ -102,8 +107,8 @@ class UncountedCallArgsChecker
         }
         auto *E = MemberCallExpr->getImplicitObjectArgument();
         QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
-        std::optional<bool> IsUncounted = isUncounted(ArgType);
-        if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+        std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
+        if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
           reportBugOnThis(E, D);
       }
 
@@ -119,7 +124,7 @@ class UncountedCallArgsChecker
 
         QualType ArgType = (*P)->getType().getCanonicalType();
         // FIXME: more complex types (arrays, references to raw pointers, etc)
-        std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
+        std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
         if (!IsUncounted || !(*IsUncounted))
           continue;
 
@@ -265,7 +270,7 @@ class UncountedCallArgsChecker
       Os << " for parameter ";
       printQuotedQualifiedName(Os, Param);
     }
-    Os << " is uncounted and unsafe.";
+    Os << " is " << ptrKind() << " and unsafe.";
 
     const SourceLocation SrcLocToReport =
         isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
@@ -283,15 +288,53 @@ class UncountedCallArgsChecker
 
     const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
 
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+    Os << "Call argument for 'this' parameter is " << ptrKind();
+    Os << " and unsafe.";
+
     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
-    auto Report = std::make_unique<BasicBugReport>(
-        Bug, "Call argument for 'this' parameter is uncounted and unsafe.",
-        BSLoc);
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
     Report->addRange(CallArg->getSourceRange());
     Report->setDeclWithIssue(DeclWithIssue);
     BR->emitReport(std::move(Report));
   }
 };
+
+class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
+public:
+  UncountedCallArgsChecker()
+      : RawPtrRefCallArgsChecker("Uncounted call argument for a raw "
+                                 "pointer/reference parameter") {}
+
+  std::optional<bool> isUnsafeType(QualType QT) const final {
+    return isUncounted(QT);
+  }
+
+  std::optional<bool> isUnsafePtr(QualType QT) const final {
+    return isUncountedPtr(QT);
+  }
+
+  const char *ptrKind() const final { return "uncounted"; }
+};
+
+class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
+public:
+  UncheckedCallArgsChecker()
+      : RawPtrRefCallArgsChecker("Unchecked call argument for a raw "
+                                 "pointer/reference parameter") {}
+
+  std::optional<bool> isUnsafeType(QualType QT) const final {
+    return isUnchecked(QT);
+  }
+
+  std::optional<bool> isUnsafePtr(QualType QT) const final {
+    return isUncheckedPtr(QT);
+  }
+
+  const char *ptrKind() const final { return "unchecked"; }
+};
+
 } // namespace
 
 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
@@ -301,3 +344,11 @@ void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
   return true;
 }
+
+void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<UncheckedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
+  return true;
+}

diff  --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked-ptr.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked-ptr.cpp
new file mode 100644
index 00000000000000..34ff0c70e230ea
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked-ptr.cpp
@@ -0,0 +1,367 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+CheckedObj* provide();
+void consume_refcntbl(CheckedObj*);
+void some_function();
+
+namespace simple {
+  void foo() {
+    consume_refcntbl(provide());
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+  }
+
+  // Test that the checker works with [[clang::suppress]].
+  void foo_suppressed() {
+    [[clang::suppress]]
+    consume_refcntbl(provide()); // no-warning
+  }
+}
+
+namespace multi_arg {
+  void consume_refcntbl(int, CheckedObj* foo, bool);
+  void foo() {
+    consume_refcntbl(42, provide(), true);
+    // expected-warning at -1{{Call argument for parameter 'foo' is unchecked and unsafe}}
+  }
+}
+
+namespace ref_counted {
+  CheckedRef<CheckedObj> provide_ref_counted() { return CheckedRef<CheckedObj>{}; }
+  void consume_ref_counted(CheckedRef<CheckedObj>) {}
+
+  void foo() {
+    consume_refcntbl(provide_ref_counted().ptr());
+    // no warning
+  }
+}
+
+namespace methods {
+  struct Consumer {
+    void consume_ptr(CheckedObj* ptr);
+    void consume_ref(const CheckedObj& ref);
+  };
+
+  void foo() {
+    Consumer c;
+
+    c.consume_ptr(provide());
+    // expected-warning at -1{{Call argument for parameter 'ptr' is unchecked and unsafe}}
+    c.consume_ref(*provide());
+    // expected-warning at -1{{Call argument for parameter 'ref' is unchecked and unsafe}}
+  }
+
+  void foo2() {
+    struct Consumer {
+      void consume(CheckedObj*) { some_function(); }
+      void whatever() {
+        consume(provide());
+        // expected-warning at -1{{Call argument is unchecked and unsafe}}
+      }
+    };
+  }
+
+  void foo3() {
+    struct Consumer {
+      void consume(CheckedObj*) { some_function(); }
+      void whatever() {
+        this->consume(provide());
+        // expected-warning at -1{{Call argument is unchecked and unsafe}}
+      }
+    };
+  }
+}
+
+namespace casts {
+  CheckedObj* downcast(CheckedObj*);
+
+  void foo() {
+    consume_refcntbl(provide());
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(static_cast<CheckedObj*>(provide()));
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(dynamic_cast<CheckedObj*>(provide()));
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(const_cast<CheckedObj*>(provide()));
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(reinterpret_cast<CheckedObj*>(provide()));
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(downcast(provide()));
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+
+    consume_refcntbl(
+      static_cast<CheckedObj*>(
+        downcast(
+          static_cast<CheckedObj*>(
+            provide()
+          )
+        )
+      )
+    );
+    // expected-warning at -8{{Call argument is unchecked and unsafe}}
+  }
+}
+
+namespace null_ptr {
+  void foo_ref() {
+    consume_refcntbl(nullptr);
+    consume_refcntbl(0);
+  }
+}
+
+namespace ref_counted_lookalike {
+  struct Decoy {
+    CheckedObj* get() { return nullptr; }
+  };
+
+  void foo() {
+    Decoy D;
+
+    consume_refcntbl(D.get());
+    // expected-warning at -1{{Call argument is unchecked and unsafe}}
+  }
+}
+
+namespace Ref_to_reference_conversion_operator {
+  template<typename T> struct Ref {
+    Ref() = default;
+    Ref(T*) { }
+    T* get() { return nullptr; }
+    operator T& () { return t; }
+    T t;
+  };
+
+  void consume_ref(CheckedObj&) {}
+
+  void foo() {
+    CheckedRef<CheckedObj> bar;
+    consume_ref(bar);
+  }
+}
+
+namespace param_formarding_function {
+  void consume_ref_countable_ref(CheckedObj&);
+  void consume_ref_countable_ptr(CheckedObj*);
+
+  namespace ptr {
+    void foo(CheckedObj* param) {
+      consume_ref_countable_ptr(param);
+    }
+  }
+
+  namespace ref {
+    void foo(CheckedObj& param) {
+      consume_ref_countable_ref(param);
+    }
+  }
+
+  namespace ref_deref_operators {
+    void foo_ref(CheckedObj& param) {
+      consume_ref_countable_ptr(&param);
+    }
+
+    void foo_ptr(CheckedObj* param) {
+      consume_ref_countable_ref(*param);
+    }
+  }
+
+  namespace casts {
+
+  CheckedObj* downcast(CheckedObj*) { return nullptr; }
+
+  template<class T>
+  T* bitwise_cast(T*) { return nullptr; }
+
+    void foo(CheckedObj* param) {
+      consume_ref_countable_ptr(downcast(param));
+      consume_ref_countable_ptr(bitwise_cast(param));
+     }
+  }
+}
+
+namespace param_formarding_lambda {
+  auto consume_ref_countable_ref = [](CheckedObj&) { some_function(); };
+  auto consume_ref_countable_ptr = [](CheckedObj*) { some_function(); };
+
+  namespace ptr {
+    void foo(CheckedObj* param) {
+      consume_ref_countable_ptr(param);
+    }
+  }
+
+  namespace ref {
+    void foo(CheckedObj& param) {
+      consume_ref_countable_ref(param);
+    }
+  }
+
+  namespace ref_deref_operators {
+    void foo_ref(CheckedObj& param) {
+      consume_ref_countable_ptr(&param);
+    }
+
+    void foo_ptr(CheckedObj* param) {
+      consume_ref_countable_ref(*param);
+    }
+  }
+
+  namespace casts {
+
+  CheckedObj* downcast(CheckedObj*) { return nullptr; }
+
+  template<class T>
+  T* bitwise_cast(T*) { return nullptr; }
+
+    void foo(CheckedObj* param) {
+      consume_ref_countable_ptr(downcast(param));
+      consume_ref_countable_ptr(bitwise_cast(param));
+    }
+  }
+}
+
+namespace param_forwarding_method {
+  struct methodclass {
+    void consume_ref_countable_ref(CheckedObj&) {};
+    static void consume_ref_countable_ptr(CheckedObj*) {};
+  };
+
+  namespace ptr {
+    void foo(CheckedObj* param) {
+      methodclass::consume_ref_countable_ptr(param);
+     }
+  }
+
+  namespace ref {
+    void foo(CheckedObj& param) {
+      methodclass mc;
+      mc.consume_ref_countable_ref(param);
+     }
+  }
+
+  namespace ref_deref_operators {
+    void foo_ref(CheckedObj& param) {
+      methodclass::consume_ref_countable_ptr(&param);
+     }
+
+    void foo_ptr(CheckedObj* param) {
+      methodclass mc;
+      mc.consume_ref_countable_ref(*param);
+     }
+  }
+
+  namespace casts {
+
+  CheckedObj* downcast(CheckedObj*) { return nullptr; }
+
+  template<class T>
+  T* bitwise_cast(T*) { return nullptr; }
+
+    void foo(CheckedObj* param) {
+      methodclass::consume_ref_countable_ptr(downcast(param));
+       methodclass::consume_ref_countable_ptr(bitwise_cast(param));
+     }
+  }
+}
+
+namespace downcast {
+  void consume_ref_countable(CheckedObj*) {}
+  CheckedObj* downcast(CheckedObj*) { return nullptr; }
+
+  void foo() {
+    CheckedPtr<CheckedObj> bar;
+    consume_ref_countable( downcast(bar.get()) );
+  }
+}
+
+namespace string_impl {
+  struct String {
+    CheckedObj* impl() { return nullptr; }
+  };
+
+  struct AtomString {
+    CheckedObj rc;
+    CheckedObj& impl() { return rc; }
+  };
+
+  void consume_ptr(CheckedObj*) {}
+  void consume_ref(CheckedObj&) {}
+
+  namespace simple {
+    void foo() {
+      String s;
+      AtomString as;
+      consume_ptr(s.impl());
+      consume_ref(as.impl());
+    }
+  }
+}
+
+namespace default_arg {
+  CheckedObj* global;
+
+  void function_with_default_arg(CheckedObj* param = global);
+  // expected-warning at -1{{Call argument for parameter 'param' is unchecked and unsafe}}
+
+  void foo() {
+    function_with_default_arg();
+  }
+}
+
+namespace cxx_member_func {
+  CheckedRef<CheckedObj> provideProtected();
+  void foo() {
+    provide()->trivial();
+    provide()->method();
+    // expected-warning at -1{{Call argument for 'this' parameter is unchecked and unsafe}}
+    provideProtected()->method();
+    (provideProtected())->method();
+  };
+}
+
+namespace cxx_member_operator_call {
+  // The hidden this-pointer argument without a corresponding parameter caused couple bugs in parameter <-> argument attribution.
+  struct Foo {
+    Foo& operator+(CheckedObj* bad);
+    friend Foo& operator-(Foo& lhs, CheckedObj* bad);
+    void operator()(CheckedObj* bad);
+  };
+
+  CheckedObj* global;
+
+  void foo() {
+    Foo f;
+    f + global;
+    // expected-warning at -1{{Call argument for parameter 'bad' is unchecked and unsafe}}
+    f - global;
+    // expected-warning at -1{{Call argument for parameter 'bad' is unchecked and unsafe}}
+    f(global);
+    // expected-warning at -1{{Call argument for parameter 'bad' is unchecked and unsafe}}
+  }
+}
+
+namespace call_with_ptr_on_ref {
+  CheckedRef<CheckedObj> provideProtected();
+  void bar(CheckedObj* bad);
+  bool baz();
+  void foo(bool v) {
+    bar(v ? nullptr : provideProtected().ptr());
+    bar(baz() ? provideProtected().ptr() : nullptr);
+    bar(v ? provide() : provideProtected().ptr());
+    // expected-warning at -1{{Call argument for parameter 'bad' is unchecked and unsafe}}
+    bar(v ? provideProtected().ptr() : provide());
+    // expected-warning at -1{{Call argument for parameter 'bad' is unchecked and unsafe}}
+  }
+}
+
+namespace call_with_explicit_temporary_obj {
+  void foo() {
+    CheckedRef { *provide() }->method();
+    CheckedPtr { provide() }->method();
+  }
+}

diff  --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked-return-value.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked-return-value.cpp
new file mode 100644
index 00000000000000..33f7f8272a2560
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked-return-value.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+class Checkable {
+public:
+  void ref() const;
+  void deref() const;
+};
+
+class Object {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction(Checkable&);
+};
+
+RefPtr<Object> object();
+RefPtr<Checkable> protectedTargetObject();
+
+void testFunction() {
+  if (RefPtr obj = object())
+    obj->someFunction(*protectedTargetObject());
+}

diff  --git a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn
index 6a4c92390d773b..8579141aa7f99c 100644
--- a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn
@@ -142,7 +142,7 @@ static_library("Checkers") {
     "WebKit/PtrTypesSemantics.cpp",
     "WebKit/RawPtrRefMemberChecker.cpp",
     "WebKit/RefCntblBaseVirtualDtorChecker.cpp",
-    "WebKit/UncountedCallArgsChecker.cpp",
+    "WebKit/RawPtrRefCallArgsChecker.cpp",
     "WebKit/UncountedLambdaCapturesChecker.cpp",
     "WebKit/RawPtrRefLocalVarsChecker.cpp",
     "cert/InvalidPtrChecker.cpp",


        


More information about the cfe-commits mailing list