Author: geoffreygaren
Date: 2026-09-24T00:24:05-07:00
New Revision: 162edfe631ac3ac87a9b30500a451a0fa853d164

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

LOG: [WebKit Checkers] Add alpha.webkit.UnborrowedCallArgsChecker (#225854)

Like alpha.webkit.UnborrowedLocalVarsChecker, but for function
arguments.

As in the local variable checker, we do not treat an origin that
outlives a function call as sufficient evidence of safety. We need to
prove not only that an object is live, but also that it prevents
interior destruction.

Assisted-by: Claude

Added: 
    clang/test/Analysis/Checkers/WebKit/unborrowed-call-args.cpp

Modified: 
    clang/docs/analyzer/checkers.md
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
    clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
    clang/test/Analysis/Checkers/WebKit/mock-canborrow.h

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.md b/clang/docs/analyzer/checkers.md
index c598bf05031e9..343b9482f8a62 100644
--- a/clang/docs/analyzer/checkers.md
+++ b/clang/docs/analyzer/checkers.md
@@ -4284,6 +4284,52 @@ The cost is that an identity function is reported even 
though its result really
 > }
 > ```
 
+#### alpha.webkit.UnborrowedCallArgsChecker
+
+The same rule as alpha.webkit.UnborrowedLocalVarsChecker, applied to function 
arguments.
+
+> ```cpp
+> void someFunction(char* pointer);
+> void someFunction(char& reference);
+> void someFunction(std::span<char> view);
+> void someFunctionByCopy(char value);
+>
+> void foo1(Vector<char>& buffer) {
+>   someFunction(buffer.data());   // warn
+>   someFunction(buffer[0]);       // warn
+>   someFunction(buffer.span());   // warn
+>   someFunctionByCopy(buffer[0]); // ok, someFunctionByCopy() receives a copy
+>                                  // of the element, not a view
+> }
+>
+> void foo2(Vector<char>& buffer) {
+>   buffer.append(buffer[0]);      // warn
+> }
+> ```
+
+The implicit object argument counts as an argument:
+
+> ```cpp
+> class Element {
+> public:
+>   void someMethod();
+> };
+>
+> void foo3(Vector<Element>& elements) {
+>   elements[0].someMethod(); // warn: 'this' is a pointer into elements
+> }
+> ```
+
+These examples do not warn:
+
+> ```cpp
+> void foo4(Vector<char>& buffer) {
+>   Borrow<Vector<char>> borrowed(buffer);
+>   someFunction(borrowed.get().data()); // ok, guarded by Borrow<T>
+>   someFunction(borrowed.get()[0]);     // ok, guarded by Borrow<T>
+> }
+> ```
+
 #### webkit.RetainPtrCtorAdoptChecker
 
 The goal of this rule is to make sure the constructors of RetainPtr and 
OSObjectPtr as well as adoptNS, adoptCF, and adoptOSObject are used correctly.

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 24abc8d4a4b8a..3d2428bdf92a5 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1786,6 +1786,10 @@ def UnretainedCallArgsChecker : 
Checker<"UnretainedCallArgsChecker">,
   HelpText<"Check unretained call arguments.">,
   Documentation<HasDocumentation>;
 
+def UnborrowedCallArgsChecker : Checker<"UnborrowedCallArgsChecker">,
+  HelpText<"Check call arguments holding a loan on a CanBorrow object that is 
not guarded by a Borrow.">,
+  Documentation<HasDocumentation>;
+
 def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">,
   HelpText<"Check uncounted local variables.">,
   Documentation<HasDocumentation>;

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 6ff56e9c1ce3b..995667225c961 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -454,7 +454,7 @@ bool tryToFindPtrOrigin(
       /*PtrIsLifetimeBoundToOrigin=*/false);
 }
 
-bool isASafeCallArg(const Expr *E) {
+bool originOutlivesCall(const Expr *E) {
   assert(E);
   auto IsCheckedLocalVarOrParam = [](const VarDecl *Decl) {
     auto Ty = Decl->getType();

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index 6736952134821..68f8623814bcf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -71,12 +71,12 @@ bool tryToFindPtrOrigin(
         callback);
 
 /// For \p E referring to a ref-countable/-counted pointer/reference we return
-/// whether it's a safe call argument. Examples: function parameter or
-/// this-pointer. The logic relies on the set of recursive rules we enforce for
-/// WebKit codebase.
+/// whether the pointee outlives the current function call. Examples: function
+/// parameter or this-pointer. Outliving the call is not by itself sufficient
+/// evidence of safety for a model that checks for interior destruction.
 ///
-/// \returns Whether \p E is a safe call arugment.
-bool isASafeCallArg(const clang::Expr *E);
+/// \returns Whether the pointee of \p E outlives the current function call.
+bool originOutlivesCall(const clang::Expr *E);
 
 /// \returns true if E is nullptr or __null.
 bool isNullPtr(const clang::Expr *E);

diff  --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 04c7bb88f5fce..f3450f5129b1b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -132,24 +132,21 @@ class RawPtrRefCallArgsChecker
     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
   }
 
-  template <typename CallOrConstrcut>
-  void visitCallOrConstructExpr(const CallOrConstrcut *CE,
+  template <typename CallOrConstruct>
+  void visitCallOrConstructExpr(const CallOrConstruct *CE,
                                 const FunctionDecl *F, const Decl *D) const {
     if (F) {
-      // 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);
-
-      if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
+      unsigned ArgIdx = 0;
+      if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
         checkThisArg(F, MemberCallExpr, D);
-
-      if (ArgIdx) {
-        auto *Arg = CE->getArg(0);
-        QualType ArgType = Arg->getType().getCanonicalType();
-        std::optional<bool> IsUnsafe = Model->isUnsafeType(ArgType);
-        if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg))
-          reportBugOnThis(F, Arg, D);
+      } else if (isa<CXXOperatorCallExpr>(CE) && isa<CXXMethodDecl>(F)) {
+        // An overloaded member operator (e.g. lambda or std::function call
+        // operator) receives the receiver object as argument 0; start the
+        // parameter loop at 1 so we don't match it against the operator's
+        // first declared parameter.
+        auto *Receiver = CE->getArg(0);
+        checkThisArg(F, Receiver, Receiver->getType(), D);
+        ArgIdx = 1;
       }
 
       for (auto P = F->param_begin();
@@ -206,10 +203,11 @@ class RawPtrRefCallArgsChecker
 
     if (auto *Receiver = E->getInstanceReceiver()) {
       std::optional<bool> IsUnsafe = Model->isUnsafePtr(E->getReceiverType());
-      if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
+      const Expr *Origin = nullptr;
+      if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver, &Origin)) {
         if (isAllocInit(E))
           return;
-        reportBugOnReceiver(E->getMethodDecl(), Receiver, D);
+        reportBugOnReceiver(E->getMethodDecl(), Receiver, D, Origin);
       }
     }
 
@@ -226,33 +224,47 @@ class RawPtrRefCallArgsChecker
       std::optional<bool> IsUnsafe = Model->isUnsafePtr(ArgType);
       if (!IsUnsafe || !(*IsUnsafe))
         continue;
-      if (isPtrOriginSafe(Arg))
+      const Expr *Origin = nullptr;
+      if (isPtrOriginSafe(Arg, &Origin))
         continue;
-      reportBug(MethodDecl, Arg, Param, D);
+      reportBug(MethodDecl, Arg, Param, D, Origin);
     }
   }
 
+  static bool isRefCountingOperation(const CXXMethodDecl *MD) {
+    if (!MD)
+      return false;
+    auto name = safeGetName(MD);
+    return name == "ref" || name == "deref" ||
+           name == "incrementCheckedPtrCount" ||
+           name == "decrementCheckedPtrCount";
+  }
+
   void checkThisArg(const NamedDecl *Callee,
                     const CXXMemberCallExpr *MemberCallExpr,
                     const Decl *DeclWithIssue) const {
-    if (auto *MD = MemberCallExpr->getMethodDecl()) {
-      auto name = safeGetName(MD);
-      if (name == "ref" || name == "deref")
-        return;
-      if (name == "incrementCheckedPtrCount" ||
-          name == "decrementCheckedPtrCount")
-        return;
-    }
-    auto *ThisExpr = MemberCallExpr->getImplicitObjectArgument();
-    QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
-    std::optional<bool> IsUnsafe = Model->isUnsafeType(ArgType);
+    if (isRefCountingOperation(MemberCallExpr->getMethodDecl()))
+      return;
+    checkThisArg(Callee, MemberCallExpr->getImplicitObjectArgument(),
+                 MemberCallExpr->getObjectType(), DeclWithIssue);
+  }
+
+  void checkThisArg(const NamedDecl *Callee, const Expr *Receiver,
+                    QualType ReceiverType, const Decl *DeclWithIssue) const {
+    // There is no ParmVarDecl for the implicit object parameter, so
+    // synthesize its type ('T&' per [over.match.funcs]) for the model to
+    // classify.
+    QualType ParamType = BR->getContext().getLValueReferenceType(
+        ReceiverType.getCanonicalType());
+    std::optional<bool> IsUnsafe = Model->isUnsafePtr(ParamType);
     if (!IsUnsafe || !*IsUnsafe)
       return;
 
-    if (isPtrOriginSafe(ThisExpr))
+    const Expr *Origin = nullptr;
+    if (isPtrOriginSafe(Receiver, &Origin))
       return;
 
-    reportBugOnThis(Callee, ThisExpr, DeclWithIssue);
+    reportBugOnThis(Callee, Receiver, DeclWithIssue, Origin);
   }
 
   void checkArg(const NamedDecl *Callee, const Expr *Arg, QualType ParamType,
@@ -264,13 +276,14 @@ class RawPtrRefCallArgsChecker
     if (auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
       Arg = DefaultArg->getExpr();
 
-    if (isPtrOriginSafe(Arg))
+    const Expr *Origin = nullptr;
+    if (isPtrOriginSafe(Arg, &Origin))
       return;
 
-    reportBug(Callee, Arg, Param, DeclWithIssue);
+    reportBug(Callee, Arg, Param, DeclWithIssue, Origin);
   }
 
-  bool isPtrOriginSafe(const Expr *Arg) const {
+  bool isPtrOriginSafe(const Expr *Arg, const Expr **Origin = nullptr) const {
     return tryToFindPtrOrigin(
         Arg, /*StopAtFirstRefCountedObj=*/true,
         Model->checksForInteriorDestruction(),
@@ -299,7 +312,8 @@ class RawPtrRefCallArgsChecker
             return true;
           if (isa<ObjCStringLiteral>(ArgOrigin))
             return true;
-          if (isASafeCallArg(ArgOrigin))
+          if (!Model->checksForInteriorDestruction() &&
+              originOutlivesCall(ArgOrigin))
             return true;
           if (EFA.isACallToEnsureFn(ArgOrigin)) {
             auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
@@ -309,6 +323,8 @@ class RawPtrRefCallArgsChecker
           }
           if (Model->isSafeExpr(ArgOrigin, PtrIsLifetimeBoundToOrigin))
             return true;
+          if (Origin && !*Origin)
+            *Origin = ArgOrigin;
           return false;
         });
   }
@@ -410,7 +426,8 @@ class RawPtrRefCallArgsChecker
   }
 
   void reportBug(const NamedDecl *Callee, const Expr *CallArg,
-                 const ParmVarDecl *Param, const Decl *DeclWithIssue) const {
+                 const ParmVarDecl *Param, const Decl *DeclWithIssue,
+                 const Expr *Origin) const {
     assert(CallArg);
 
     SmallString<100> Buf;
@@ -434,22 +451,7 @@ class RawPtrRefCallArgsChecker
     if (!paramName.empty() || Callee)
       Os << ")";
     Os << " is a ";
-    auto *ArgType = CallArg->getType().getTypePtr();
-
-    if (printPointer(Os, ArgType) == PrintDeclKind::Pointer) {
-      auto *RTC = Model->retainTypeChecker();
-      assert(RTC);
-      if (auto *Decl = RTC->getCanonicalDecl(CallArg->getType())) {
-        printQuotedQualifiedName(Os, Decl);
-      } else {
-        auto Typedef = ArgType->getAs<TypedefType>();
-        assert(Typedef);
-        printQuotedQualifiedName(Os, Typedef->getDecl());
-      }
-    } else {
-      Os << " ";
-      printTypeName(Os, CallArg->getType());
-    }
+    Model->describeHazard(Os, Origin, CallArg->getType());
 
     bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param;
     const SourceLocation SrcLocToReport =
@@ -464,7 +466,7 @@ class RawPtrRefCallArgsChecker
   }
 
   void reportBugOnThis(const NamedDecl *Callee, const Expr *CallArg,
-                       const Decl *DeclWithIssue) const {
+                       const Decl *DeclWithIssue, const Expr *Origin) const {
     assert(CallArg);
 
     const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
@@ -478,8 +480,8 @@ class RawPtrRefCallArgsChecker
       Os << " to ";
       printQuotedQualifiedName(Os, Callee);
     }
-    Os << ") is a raw pointer to " << Model->typeName() << " ";
-    printTypeName(Os, CallArg->getType());
+    Os << ") is a ";
+    printHazardOrPointerTo(Os, CallArg, Origin);
 
     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
@@ -489,7 +491,8 @@ class RawPtrRefCallArgsChecker
   }
 
   void reportBugOnReceiver(const NamedDecl *Callee, const Expr *CallArg,
-                           const Decl *DeclWithIssue) const {
+                           const Decl *DeclWithIssue,
+                           const Expr *Origin) const {
     assert(CallArg);
 
     const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
@@ -503,8 +506,8 @@ class RawPtrRefCallArgsChecker
       printQuotedQualifiedName(Os, Callee);
       Os << ")";
     }
-    Os << " is a raw pointer to " << Model->typeName() << " ";
-    printTypeName(Os, CallArg->getType());
+    Os << " is a ";
+    printHazardOrPointerTo(Os, CallArg, Origin);
 
     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
@@ -513,6 +516,18 @@ class RawPtrRefCallArgsChecker
     BR->emitReport(std::move(Report));
   }
 
+  void printHazardOrPointerTo(llvm::raw_svector_ostream &Os,
+                              const Expr *CallArg, const Expr *Origin) const {
+    if (Model->checksForInteriorDestruction()) {
+      Model->describeHazard(Os, Origin, CallArg->getType());
+      return;
+    }
+    // 'this' is a pointer even when the call is spelled with '.', so don't
+    // infer pointer vs reference from the argument type.
+    Os << "raw pointer to " << Model->typeName() << " ";
+    printTypeName(Os, CallArg->getType());
+  }
+
   void printArgument(llvm::raw_svector_ostream &Os, const Expr *Arg) const {
     SmallString<100> Buf;
     llvm::raw_svector_ostream ArgOs(Buf);
@@ -527,22 +542,6 @@ class RawPtrRefCallArgsChecker
     else
       Os << " '" << ArgCode << "'";
   }
-
-  enum class PrintDeclKind { Pointee, Pointer };
-  PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
-                             const Type *T) const {
-    // Retain/OS types are frequently spelled through a typedef (e.g. 
CFXXXRef);
-    // print the typedef name rather than desugaring to the pointee.
-    if (Model->retainTypeChecker() && isa<TypedefType>(T)) {
-      Os << Model->typeName() << " ";
-      return PrintDeclKind::Pointer;
-    }
-    T = T->getUnqualifiedDesugaredType();
-    bool IsPtr = isa<PointerType, ObjCObjectPointerType>(T);
-    Os << "raw " << (IsPtr ? "pointer" : "reference") << " to "
-       << Model->typeName();
-    return PrintDeclKind::Pointee;
-  }
 };
 
 class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
@@ -569,6 +568,14 @@ class UnretainedCallArgsChecker final : public 
RawPtrRefCallArgsChecker {
                                  makeRetainPtrSafetyModel()) {}
 };
 
+class UnborrowedCallArgsChecker final : public RawPtrRefCallArgsChecker {
+public:
+  UnborrowedCallArgsChecker()
+      : RawPtrRefCallArgsChecker("Loan on a CanBorrow object not guarded by "
+                                 "a Borrow",
+                                 makeBorrowSafetyModel()) {}
+};
+
 } // namespace
 
 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
@@ -594,3 +601,11 @@ void 
ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
 bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
   return true;
 }
+
+void ento::registerUnborrowedCallArgsChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<UnborrowedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUnborrowedCallArgsChecker(const CheckerManager &) {
+  return true;
+}

diff  --git a/clang/test/Analysis/Checkers/WebKit/mock-canborrow.h 
b/clang/test/Analysis/Checkers/WebKit/mock-canborrow.h
index 84737b30788a8..0bce868fc11ff 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-canborrow.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-canborrow.h
@@ -217,4 +217,10 @@ template <typename T> class Registry : public CanBorrow {
   unsigned m_size { 0 };
 };
 
+class Element {
+public:
+  void mutate();
+  void inspect() const;
+};
+
 #endif

diff  --git a/clang/test/Analysis/Checkers/WebKit/unborrowed-call-args.cpp 
b/clang/test/Analysis/Checkers/WebKit/unborrowed-call-args.cpp
new file mode 100644
index 0000000000000..72cd785725b41
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unborrowed-call-args.cpp
@@ -0,0 +1,204 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UnborrowedCallArgsChecker -verify %s
+
+#include "mock-canborrow.h"
+
+void process(char &);
+void processConst(const char &);
+void takePtr(char *);
+void takeView(StringView);
+void takeSpan(CharSpan);
+
+void takeContainer(Vector<char> &);
+void takeContainerPtr(Vector<char> *);
+
+Vector<char> makeVector();
+Vector<char> &getVectorRef();
+
+Vector<char> globalVector;
+
+namespace arg_origins {
+
+void from_local() {
+  Vector<char> vec;
+  process(vec[0]);
+  // expected-warning@-1{{Function argument 'vec[0]' (to 'process') is a loan 
on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void from_parameter(Vector<char> &vec) {
+  process(vec[0]);
+  // expected-warning@-1{{Function argument 'vec[0]' (to 'process') is a loan 
on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void from_global() {
+  process(globalVector[0]);
+  // expected-warning@-1{{Function argument 'globalVector[0]' (to 'process') 
is a loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void from_static_local() {
+  static Vector<char> cache;
+  process(cache[0]);
+  // expected-warning@-1{{Function argument 'cache[0]' (to 'process') is a 
loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+class Holder {
+public:
+  void from_member() {
+    process(m_vector[0]);
+    // expected-warning@-1{{Function argument 'this->m_vector[0]' (to 
'process') is a loan on CanBorrow type 'Vector<char>' that is not guarded by a 
Borrow}}
+  }
+
+private:
+  Vector<char> m_vector;
+};
+
+void from_call_result() {
+  process(makeVector()[0]);
+  // expected-warning@-1{{Function argument 'makeVector()[0]' (to 'process') 
is a loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void from_returned_reference() {
+  process(getVectorRef()[0]);
+  // expected-warning@-1{{Function argument 'getVectorRef()[0]' (to 'process') 
is a loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void buffer_pointer() {
+  Vector<char> vec;
+  takePtr(vec.data());
+  // expected-warning@-1{{Function argument 'vec.data()' (to 'takePtr') is a 
loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+void const_parameter() {
+  Vector<char> vec;
+  processConst(vec[0]);
+  // expected-warning@-1{{Function argument 'vec[0]' (to 'processConst') is a 
loan on CanBorrow type 'Vector<char>' that is not guarded by a Borrow}}
+}
+
+} // namespace arg_origins
+
+namespace implicit_object_arg {
+
+void mutating_method() {
+  Vector<Element> vec;
+  vec[0].mutate();
+  // expected-warning@-1{{Function argument 'vec[0]' (parameter 'this' to 
'Element::mutate') is a loan on CanBorrow type 'Vector<Element>' that is not 
guarded by a Borrow}}
+}
+
+void const_method() {
+  Vector<Element> vec;
+  vec[0].inspect();
+  // expected-warning@-1{{Function argument 'vec[0]' (parameter 'this' to 
'Element::inspect') is a loan on CanBorrow type 'Vector<Element>' that is not 
guarded by a Borrow}}
+}
+
+class Holder {
+public:
+  void from_member() {
+    m_elements[0].mutate();
+    // expected-warning@-1{{Function argument 'this->m_elements[0]' (parameter 
'this' to 'Element::mutate') is a loan on CanBorrow type 'Vector<Element>' that 
is not guarded by a Borrow}}
+  }
+
+private:
+  Vector<Element> m_elements;
+};
+
+void nested_container() {
+  Vector<Vector<char>> outer;
+  outer[0].append('x');
+  // expected-warning@-1{{Function argument 'outer[0]' (parameter 'this' to 
'Vector<char>::append') is a loan on CanBorrow type 'Vector<Vector<char>>' that 
is not guarded by a Borrow}}
+}
+
+} // namespace implicit_object_arg
+
+namespace not_a_loan {
+
+void passing_the_container() {
+  Vector<char> vec;
+  takeContainer(vec);
+}
+
+void passing_the_containers_address() {
+  Vector<char> vec;
+  takeContainerPtr(&vec);
+}
+
+void passing_an_alias() {
+  Vector<char> vec;
+  Vector<char> &alias = vec;
+  takeContainer(alias);
+}
+
+void method_on_the_container() {
+  Vector<char> vec;
+  vec.append('x');
+}
+
+void not_borrowable_container() {
+  NotBorrowable nb;
+  process(nb.at(0));
+}
+
+void smart_pointer_accessor(Owner<NotBorrowable> &owner) {
+  process(owner->at(0));
+}
+
+} // namespace not_a_loan
+
+namespace guarded {
+
+void borrowed_element() {
+  Vector<char> vec;
+  Borrow<Vector<char>> b(vec);
+  process(b.get()[0]);
+}
+
+void borrowed_buffer() {
+  Vector<char> vec;
+  Borrow<Vector<char>> b(vec);
+  takePtr(b.get().data());
+}
+
+void borrowed_implicit_object_arg() {
+  Vector<Element> vec;
+  Borrow<Vector<Element>> b(vec);
+  b.get()[0].mutate();
+}
+
+void constructing_the_borrow_is_not_a_loan() {
+  Vector<char> vec;
+  Borrow<Vector<char>> b(vec);
+}
+
+} // namespace guarded
+
+namespace nested_calls {
+
+void annotated_intermediate() {
+  Vector<char> vec;
+  takeView(makeView(vec.data()));
+  // expected-warning@-1{{Function argument 'makeView(vec.data())' (to 
'takeView') is a loan on CanBorrow type 'Vector<char>' that is not guarded by a 
Borrow}}
+  // expected-warning@-2{{Function argument 'vec.data()' (parameter 'data' to 
'makeView') is a loan on CanBorrow type 'Vector<char>' that is not guarded by a 
Borrow}}
+}
+
+void two_loans_one_call() {
+  Vector<char> vec;
+  pick(vec.data(), vec.data());
+  // expected-warning@-1{{Function argument 'vec.data()' (parameter 'a' to 
'pick') is a loan on CanBorrow type 'Vector<char>' that is not guarded by a 
Borrow}}
+  // expected-warning@-2{{Function argument 'vec.data()' (parameter 'b' to 
'pick') is a loan on CanBorrow type 'Vector<char>' that is not guarded by a 
Borrow}}
+}
+
+} // namespace nested_calls
+
+namespace known_gaps {
+
+void unannotated_intermediate() {
+  Vector<char> vec;
+  takeSpan(makeSpanUnannotated(vec));
+}
+
+inline void trivialSink(char &c) {}
+
+void trivial_callee() {
+  Vector<char> vec;
+  trivialSink(vec[0]);
+}
+
+} // namespace known_gaps


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to