================
@@ -21,25 +22,117 @@ AST_MATCHER(LambdaExpr, hasCoroutineBody) {
 }
 
 AST_MATCHER(LambdaExpr, hasCaptures) { return Node.capture_size() != 0U; }
+
+AST_MATCHER(LambdaExpr, hasDeducingThis) {
+  return Node.getCallOperator()->isExplicitObjectMemberFunction();
+}
 } // namespace
 
+AvoidCapturingLambdaCoroutinesCheck::AvoidCapturingLambdaCoroutinesCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      AllowExplicitObjectParameters(
+          Options.get("AllowExplicitObjectParameters", false)) {}
+
 void AvoidCapturingLambdaCoroutinesCheck::registerMatchers(
     MatchFinder *Finder) {
-  Finder->addMatcher(
-      lambdaExpr(hasCaptures(), hasCoroutineBody()).bind("lambda"), this);
+  auto Matcher = lambdaExpr(hasCaptures(), hasCoroutineBody());
+
+  if (AllowExplicitObjectParameters)
+    Matcher = lambdaExpr(hasCaptures(), hasCoroutineBody(),
+                         unless(hasDeducingThis()));
+
+  Finder->addMatcher(Matcher.bind("lambda"), this);
 }
 
 bool AvoidCapturingLambdaCoroutinesCheck::isLanguageVersionSupported(
     const LangOptions &LangOpts) const {
   return LangOpts.CPlusPlus20;
 }
 
+void AvoidCapturingLambdaCoroutinesCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "AllowExplicitObjectParameters",
+                AllowExplicitObjectParameters);
+}
+
 void AvoidCapturingLambdaCoroutinesCheck::check(
     const MatchFinder::MatchResult &Result) {
   const auto *MatchedLambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
-  diag(MatchedLambda->getExprLoc(),
-       "coroutine lambda may cause use-after-free, avoid captures or ensure "
-       "lambda closure object has guaranteed lifetime");
+
+  if (AllowExplicitObjectParameters && getLangOpts().CPlusPlus23) {
+    const CXXMethodDecl *Call = MatchedLambda->getCallOperator();
+    const bool HasExplicitParams = MatchedLambda->hasExplicitParameters();
+
+    auto DiagBuilder =
+        diag(MatchedLambda->getExprLoc(),
+             "coroutine lambda with captures may cause use-after-free; use "
+             "'this auto' as the first parameter to move captures into the "
+             "coroutine frame");
+
+    if (HasExplicitParams) {
+      const bool HasParams = !Call->param_empty();
+      if (HasParams) {
+        const ParmVarDecl *FirstParam = Call->parameters().front();
+        DiagBuilder << FixItHint::CreateInsertion(FirstParam->getBeginLoc(),
+                                                  "this auto, ");
+      } else {
+        DiagBuilder << FixItHint::CreateInsertion(
+            Call->getFunctionTypeLoc().getRParenLoc(), "this auto");
+      }
+    } else {
+      // No explicit parameter list — insert `(this auto) ` where the
+      // parameter list would go in the grammar:
+      // [captures] <tparams> t-requires front-attr (params)
+      // Start after the template parameter list (including its requires
+      // clause) or the capture list, then skip past any attributes that
+      // appear before the implicit parameter list position.
+      const auto &SM = *Result.SourceManager;
+      const auto &LO = getLangOpts();
+      SourceLocation InsertLoc;
+
+      if (const auto *TPL = MatchedLambda->getTemplateParameterList()) {
+        if (const Expr *RC = TPL->getRequiresClause())
+          InsertLoc = Lexer::getLocForEndOfToken(RC->getEndLoc(), 0, SM, LO);
+        else
+          InsertLoc =
+              Lexer::getLocForEndOfToken(TPL->getRAngleLoc(), 0, SM, LO);
+      } else {
+        InsertLoc = Lexer::getLocForEndOfToken(
+            MatchedLambda->getIntroducerRange().getEnd(), 0, SM, LO);
+      }
+
+      // Skip past any front-attributes. getRange() covers only the
+      // attribute name/arguments, not the enclosing brackets.
+      // Advance past the closing brackets based on the syntax:
+      //   `[[attr]]`                 — 2 tokens (`]]`)
+      //   `__attribute__((attr))`    — 2 tokens (`))`)
+      //   `__declspec(attr)`         — 1 token  (`)`)
+      //   keyword / other            — 0 tokens
+      if (Call->hasAttrs()) {
----------------
WillemKauf wrote:

If anyone has any better ideas than having to go the `Lexer` route here, I 
would love to hear them... 🙂 

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

Reply via email to