Skip to content

[webkit.UncountedLambdaCapturesChecker] Support [[clang::noescape]] on a constructor #126869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 14, 2025

Conversation

rniwa
Copy link
Contributor

@rniwa rniwa commented Feb 12, 2025

Added the support for annotating a constructor's argument with [[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of CallExpr so that construction of closures like Function, CompletionHandler, etc... don't result in a warning.

…n a constructor

Added the support for annotating a constructor's argument with [[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of CallExpr so that
construction of closures like Function, CompletionHandler, etc... don't result in a warning.
@rniwa rniwa requested review from t-rasmud and haoNoQ February 12, 2025 07:44
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Feb 12, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 12, 2025

@llvm/pr-subscribers-clang

Author: Ryosuke Niwa (rniwa)

Changes

Added the support for annotating a constructor's argument with [[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of CallExpr so that construction of closures like Function, CompletionHandler, etc... don't result in a warning.


Full diff: https://github.com/llvm/llvm-project/pull/126869.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp (+25-1)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (+4)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index a56f48c83c660..972364a855eb4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -41,6 +41,7 @@ class UncountedLambdaCapturesChecker
       const UncountedLambdaCapturesChecker *Checker;
       llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
       llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
+      llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
       QualType ClsType;
 
       explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
@@ -106,6 +107,26 @@ class UncountedLambdaCapturesChecker
         return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn";
       }
 
+      bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
+        if (ConstructToIgnore.contains(CE))
+          return true;
+        if (auto *Callee = CE->getConstructor()) {
+          unsigned ArgIndex = 0;
+          for (auto *Param : Callee->parameters()) {
+            if (ArgIndex >= CE->getNumArgs())
+              return true;
+            auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
+            if (auto *L = findLambdaInArg(Arg)) {
+              LambdasToIgnore.insert(L);
+              if (!Param->hasAttr<NoEscapeAttr>())
+                Checker->visitLambdaExpr(L, shouldCheckThis());
+            }
+            ++ArgIndex;
+          }
+        }
+        return true;
+      }
+
       bool VisitCallExpr(CallExpr *CE) override {
         checkCalleeLambda(CE);
         if (auto *Callee = CE->getDirectCallee()) {
@@ -143,8 +164,10 @@ class UncountedLambdaCapturesChecker
         auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
         if (!CtorArg)
           return nullptr;
-        if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg))
+        if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
+          ConstructToIgnore.insert(CE);
           return Lambda;
+        }
         auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
         if (!DRE)
           return nullptr;
@@ -157,6 +180,7 @@ class UncountedLambdaCapturesChecker
         TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
         if (!TempExpr)
           return nullptr;
+        ConstructToIgnore.insert(CE);
         return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
       }
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 4f4a960282253..62a8245db99b9 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -298,7 +298,11 @@ struct RefCountableWithLambdaCapturingThis {
     callLambda([&]() -> RefPtr<RefCountable> {
       return obj->next();
     });
+    WTF::HashMap<int, RefPtr<RefCountable>> anotherMap([&] {
+      return obj->next();
+    });
   }
+
 };
 
 struct NonRefCountableWithLambdaCapturingThis {

@llvmbot
Copy link
Member

llvmbot commented Feb 12, 2025

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ryosuke Niwa (rniwa)

Changes

Added the support for annotating a constructor's argument with [[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of CallExpr so that construction of closures like Function, CompletionHandler, etc... don't result in a warning.


Full diff: https://github.com/llvm/llvm-project/pull/126869.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp (+25-1)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (+4)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index a56f48c83c660..972364a855eb4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -41,6 +41,7 @@ class UncountedLambdaCapturesChecker
       const UncountedLambdaCapturesChecker *Checker;
       llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
       llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
+      llvm::DenseSet<const CXXConstructExpr *> ConstructToIgnore;
       QualType ClsType;
 
       explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
@@ -106,6 +107,26 @@ class UncountedLambdaCapturesChecker
         return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn";
       }
 
+      bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
+        if (ConstructToIgnore.contains(CE))
+          return true;
+        if (auto *Callee = CE->getConstructor()) {
+          unsigned ArgIndex = 0;
+          for (auto *Param : Callee->parameters()) {
+            if (ArgIndex >= CE->getNumArgs())
+              return true;
+            auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
+            if (auto *L = findLambdaInArg(Arg)) {
+              LambdasToIgnore.insert(L);
+              if (!Param->hasAttr<NoEscapeAttr>())
+                Checker->visitLambdaExpr(L, shouldCheckThis());
+            }
+            ++ArgIndex;
+          }
+        }
+        return true;
+      }
+
       bool VisitCallExpr(CallExpr *CE) override {
         checkCalleeLambda(CE);
         if (auto *Callee = CE->getDirectCallee()) {
@@ -143,8 +164,10 @@ class UncountedLambdaCapturesChecker
         auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
         if (!CtorArg)
           return nullptr;
-        if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg))
+        if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
+          ConstructToIgnore.insert(CE);
           return Lambda;
+        }
         auto *DRE = dyn_cast<DeclRefExpr>(CtorArg);
         if (!DRE)
           return nullptr;
@@ -157,6 +180,7 @@ class UncountedLambdaCapturesChecker
         TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
         if (!TempExpr)
           return nullptr;
+        ConstructToIgnore.insert(CE);
         return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
       }
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 4f4a960282253..62a8245db99b9 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -298,7 +298,11 @@ struct RefCountableWithLambdaCapturingThis {
     callLambda([&]() -> RefPtr<RefCountable> {
       return obj->next();
     });
+    WTF::HashMap<int, RefPtr<RefCountable>> anotherMap([&] {
+      return obj->next();
+    });
   }
+
 };
 
 struct NonRefCountableWithLambdaCapturingThis {

if (ArgIndex >= CE->getNumArgs())
return true;
auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
if (auto *L = findLambdaInArg(Arg)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my understanding: Is the LambdaExpr not covered in VisitLambdaExpr? What's special about the expression being an argument to a constructor?

Copy link
Contributor Author

@rniwa rniwa Feb 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so we wanna ignore lambda expressions that are passed to [[clang::noescape]] argument. The code in VisitCallExpr and now VisitCXXConstructExpr is there to detect this specific case, and then ignore the argument.

Copy link
Contributor

@t-rasmud t-rasmud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@rniwa
Copy link
Contributor Author

rniwa commented Feb 14, 2025

Thanks for the review!

@rniwa rniwa merged commit da6ac95 into llvm:main Feb 14, 2025
11 checks passed
@rniwa rniwa deleted the support-noescape-on-constructor branch February 14, 2025 21:49
rniwa added a commit to rniwa/llvm-project that referenced this pull request Feb 15, 2025
…n a constructor (llvm#126869)

Added the support for annotating a constructor's argument with
[[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of
CallExpr so that construction of closures like Function,
CompletionHandler, etc... don't result in a warning.
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
…n a constructor (llvm#126869)

Added the support for annotating a constructor's argument with
[[clang::noescape]].

We explicitly ignore CXXConstructExpr which is visited as a part of
CallExpr so that construction of closures like Function,
CompletionHandler, etc... don't result in a warning.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants