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
