Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -1257,6 +1257,43 @@ +Matcher<Stmt>arrayInitIndexExprMatcher<ArrayInitIndexExpr>... +
The arrayInitIndexExpr consists of two subexpressions: a common expression
+(the source array) that is evaluated once up-front, and a per-element initializer
+that runs once for each array element. Within the per-element initializer,
+the current index may be obtained via an ArrayInitIndexExpr.
+
+Given
+  void testStructBinding() {
+    int a[2] = {1, 2};
+    auto [x, y] = a;
+  }
+arrayInitIndexExpr() matches the array index that implicitly iterates
+over the array `a` to copy each element to the anonymous array
+that backs the structured binding `[x, y]` elements of which are
+referred to by their aliases `x` and `y`.
+
+ + +Matcher<Stmt>arrayInitLoopExprMatcher<ArrayInitLoopExpr>... +
Matches a loop initializing the elements of an array in a number of contexts:
+ * in the implicit copy/move constructor for a class with an array member
+ * when a lambda-expression captures an array by value
+ * when a decomposition declaration decomposes an array
+
+Given
+  void testLambdaCapture() {
+    int a[10];
+    auto Lam1 = [a]() {
+      return;
+    };
+  }
+arrayInitLoopExpr() matches the implicit loop that initializes each element of
+the implicit array field inside the lambda object, that represents the array `a`
+captured by value.
+
+ + Matcher<Stmt>arraySubscriptExprMatcher<ArraySubscriptExpr>...
Matches array subscript expressions.
 
@@ -1455,6 +1492,16 @@
 
+Matcher<Stmt>coroutineBodyStmtMatcher<CoroutineBodyStmt>... +
Matches coroutine body statements.
+
+coroutineBodyStmt() matches the coroutine below
+  generator<int> gen() {
+    co_return;
+  }
+
+ + Matcher<Stmt>coyieldExprMatcher<CoyieldExpr>...
Matches co_yield expressions.
 
@@ -3037,10 +3084,11 @@
 
 
 Matcher<CXXConstructExpr>argumentCountAtLeastunsigned N
-
Checks that a call expression or a constructor call expression has
-at least the specified number of arguments (including absent default arguments).
+
Checks that a call expression or a constructor call expression has at least
+the specified number of arguments (including absent default arguments).
 
-Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2)))
+Example matches f(0, 0) and g(0, 0, 0)
+(matcher = callExpr(argumentCountAtLeast(2)))
   void f(int x, int y);
   void g(int x, int y, int z);
   f(0, 0);
@@ -3706,10 +3754,11 @@
 
 
 Matcher<CXXUnresolvedConstructExpr>argumentCountAtLeastunsigned N
-
Checks that a call expression or a constructor call expression has
-at least the specified number of arguments (including absent default arguments).
+
Checks that a call expression or a constructor call expression has at least
+the specified number of arguments (including absent default arguments).
 
-Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2)))
+Example matches f(0, 0) and g(0, 0, 0)
+(matcher = callExpr(argumentCountAtLeast(2)))
   void f(int x, int y);
   void g(int x, int y, int z);
   f(0, 0);
@@ -3728,10 +3777,11 @@
 
 
 Matcher<CallExpr>argumentCountAtLeastunsigned N
-
Checks that a call expression or a constructor call expression has
-at least the specified number of arguments (including absent default arguments).
+
Checks that a call expression or a constructor call expression has at least
+the specified number of arguments (including absent default arguments).
 
-Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2)))
+Example matches f(0, 0) and g(0, 0, 0)
+(matcher = callExpr(argumentCountAtLeast(2)))
   void f(int x, int y);
   void g(int x, int y, int z);
   f(0, 0);
@@ -4897,10 +4947,11 @@
 
 
 Matcher<ObjCMessageExpr>argumentCountAtLeastunsigned N
-
Checks that a call expression or a constructor call expression has
-at least the specified number of arguments (including absent default arguments).
+
Checks that a call expression or a constructor call expression has at least
+the specified number of arguments (including absent default arguments).
 
-Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2)))
+Example matches f(0, 0) and g(0, 0, 0)
+(matcher = callExpr(argumentCountAtLeast(2)))
   void f(int x, int y);
   void g(int x, int y, int z);
   f(0, 0);
@@ -6795,9 +6846,10 @@
 
 
 Matcher<CXXForRangeStmt>hasBodyMatcher<Stmt> InnerMatcher
-
Matches a 'for', 'while', 'do' statement or a function definition that has
-a given body. Note that in case of functions this matcher only matches the
-definition itself and not the other declarations of the same function.
+
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
 
 Given
   for (;;) {}
@@ -7682,6 +7734,30 @@
 
+Matcher<CoroutineBodyStmt>hasBodyMatcher<Stmt> InnerMatcher +
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
+
+Given
+  for (;;) {}
+forStmt(hasBody(compoundStmt()))
+  matches 'for (;;) {}'
+with compoundStmt()
+  matching '{}'
+
+Given
+  void f();
+  void f() {}
+functionDecl(hasBody(compoundStmt()))
+  matches 'void f() {}'
+with compoundStmt()
+  matching '{}'
+  but does not match 'void f();'
+
+ + Matcher<DecayedType>hasDecayedTypeMatcher<QualType> InnerType
Matches the decayed type, whoes decayed type matches InnerMatcher
 
@@ -7890,9 +7966,10 @@ Matcher<DoStmt>hasBodyMatcher<Stmt> InnerMatcher -
Matches a 'for', 'while', 'do' statement or a function definition that has
-a given body. Note that in case of functions this matcher only matches the
-definition itself and not the other declarations of the same function.
+
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
 
 Given
   for (;;) {}
@@ -8226,9 +8303,10 @@
 
 
 Matcher<ForStmt>hasBodyMatcher<Stmt> InnerMatcher
-
Matches a 'for', 'while', 'do' statement or a function definition that has
-a given body. Note that in case of functions this matcher only matches the
-definition itself and not the other declarations of the same function.
+
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
 
 Given
   for (;;) {}
@@ -8420,9 +8498,10 @@
 
 
 Matcher<FunctionDecl>hasBodyMatcher<Stmt> InnerMatcher
-
Matches a 'for', 'while', 'do' statement or a function definition that has
-a given body. Note that in case of functions this matcher only matches the
-definition itself and not the other declarations of the same function.
+
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
 
 Given
   for (;;) {}
@@ -10082,9 +10161,10 @@
 
 
 Matcher<WhileStmt>hasBodyMatcher<Stmt> InnerMatcher
-
Matches a 'for', 'while', 'do' statement or a function definition that has
-a given body. Note that in case of functions this matcher only matches the
-definition itself and not the other declarations of the same function.
+
Matches a 'for', 'while', 'while' statement or a function or coroutine
+definition that has a given body. Note that in case of functions or
+coroutines this matcher only matches the definition itself and not the
+other declarations of the same function or coroutine.
 
 Given
   for (;;) {}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -790,6 +790,8 @@
 
 - The ``hasBody`` matcher now matches coroutine body nodes in
   ``CoroutineBodyStmts``.
+  
+- Add ``arrayInitIndexExpr`` and ``arrayInitLoopExpr`` matchers.
 
 clang-format
 ------------
Index: clang/docs/tools/dump_ast_matchers.py
===================================================================
--- clang/docs/tools/dump_ast_matchers.py
+++ clang/docs/tools/dump_ast_matchers.py
@@ -12,10 +12,10 @@
     from urllib2 import urlopen
 
 CLASS_INDEX_PAGE_URL = "https://clang.llvm.org/doxygen/classes.html"
-try:
-    CLASS_INDEX_PAGE = urlopen(CLASS_INDEX_PAGE_URL).read().decode("utf-8")
-except Exception as e:
-    raise Exception("Unable to get %s: %s" % (CLASS_INDEX_PAGE_URL, e))
+#try:
+#    CLASS_INDEX_PAGE = urlopen(CLASS_INDEX_PAGE_URL).read().decode("utf-8")
+#except Exception as e:
+#    raise Exception("Unable to get %s: %s" % (CLASS_INDEX_PAGE_URL, e))
 
 MATCHERS_FILE = "../../include/clang/ASTMatchers/ASTMatchers.h"
 
@@ -58,7 +58,8 @@
         url = "https://clang.llvm.org/doxygen/classclang_1_1%s.html" % name
         if url not in doxygen_probes:
             search_str = 'href="classclang_1_1%s.html"' % name
-            doxygen_probes[url] = search_str in CLASS_INDEX_PAGE
+            # doxygen_probes[url] = search_str in CLASS_INDEX_PAGE
+            doxygen_probes[url] = True
             if not doxygen_probes[url]:
                 print("Did not find %s in class index page" % name)
         if doxygen_probes[url]:
@@ -186,7 +187,7 @@
     """
     if declaration.strip():
 
-        if re.match(r"^\s?(#|namespace|using)", declaration):
+        if re.match(r"^\s?(#|namespace|using|} // namespace ast_matchers } // namespace clang | template  using AstTypeMatcher = internal::VariadicDynCastAllOfMatcher;)", declaration):
             return
 
         # Node matchers are defined by writing:
Index: clang/include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchers.h
+++ clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -1971,6 +1971,45 @@
 extern const internal::VariadicDynCastAllOfMatcher
     cxxNoexceptExpr;
 
+/// Matches a loop initializing the elements of an array in a number of contexts:
+///  * in the implicit copy/move constructor for a class with an array member
+///  * when a lambda-expression captures an array by value
+///  * when a decomposition declaration decomposes an array
+///
+/// Given
+/// \code
+///   void testLambdaCapture() {
+///     int a[10];
+///     auto Lam1 = [a]() {
+///       return;
+///     };
+///   }
+/// \endcode
+/// arrayInitLoopExpr() matches the implicit loop that initializes each element of
+/// the implicit array field inside the lambda object, that represents the array `a`
+/// captured by value.
+extern const internal::VariadicDynCastAllOfMatcher
+    arrayInitLoopExpr;
+
+/// The arrayInitIndexExpr consists of two subexpressions: a common expression
+/// (the source array) that is evaluated once up-front, and a per-element initializer
+/// that runs once for each array element. Within the per-element initializer,
+/// the current index may be obtained via an ArrayInitIndexExpr.
+///
+/// Given
+/// \code
+///   void testStructBinding() {
+///     int a[2] = {1, 2};
+///     auto [x, y] = a;
+///   }
+/// \endcode
+/// arrayInitIndexExpr() matches the array index that implicitly iterates
+/// over the array `a` to copy each element to the anonymous array
+/// that backs the structured binding `[x, y]` elements of which are
+/// referred to by their aliases `x` and `y`.
+extern const internal::VariadicDynCastAllOfMatcher
+    arrayInitIndexExpr;
+
 /// Matches array subscript expressions.
 ///
 /// Given
Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -882,6 +882,10 @@
     cxxNoexceptExpr;
 const internal::VariadicDynCastAllOfMatcher
     arraySubscriptExpr;
+const internal::VariadicDynCastAllOfMatcher
+    arrayInitIndexExpr;
+const internal::VariadicDynCastAllOfMatcher
+    arrayInitLoopExpr;
 const internal::VariadicDynCastAllOfMatcher
     cxxDefaultArgExpr;
 const internal::VariadicDynCastAllOfMatcher
Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp
===================================================================
--- clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -134,6 +134,8 @@
   REGISTER_MATCHER(allOf);
   REGISTER_MATCHER(anyOf);
   REGISTER_MATCHER(anything);
+  REGISTER_MATCHER(arrayInitIndexExpr);
+  REGISTER_MATCHER(arrayInitLoopExpr);
   REGISTER_MATCHER(argumentCountIs);
   REGISTER_MATCHER(argumentCountAtLeast);
   REGISTER_MATCHER(arraySubscriptExpr);
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===================================================================
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -119,9 +119,6 @@
       return true;
     if (!match(*Node))
       return false;
-    // To skip callables:
-    if (isa(Node))
-      return true;
     return VisitorBase::TraverseStmt(Node);
   }
 
@@ -467,7 +464,9 @@
       return stmt(arraySubscriptExpr(
             hasBase(ignoringParenImpCasts(
               anyOf(hasPointerType(), hasArrayType()))),
-            unless(hasIndex(integerLiteral(equals(0)))))
+            unless(hasIndex(
+                anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
+             )))
             .bind(ArraySubscrTag));
     // clang-format on
   }
@@ -2199,6 +2198,13 @@
                                    UnsafeBufferUsageHandler &Handler,
                                    bool EmitSuggestions) {
   assert(D && D->getBody());
+  
+  // We do not want to visit a Lambda expression defined inside a method independently.
+  // Instead, it should be visited along with the outer method.
+  if (const auto *fd = dyn_cast(D)) {
+    if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
+      return;
+  }
 
   // Do not emit fixit suggestions for functions declared in an
   // extern "C" block.
@@ -2244,7 +2250,7 @@
        it != FixablesForAllVars.byVar.cend();) {
       // FIXME: need to deal with global variables later
       if ((!it->first->isLocalVarDecl() && !isa(it->first)) ||
-          Tracker.hasUnclaimedUses(it->first)) {
+          Tracker.hasUnclaimedUses(it->first) || it->first->isInitCapture()) {
       it = FixablesForAllVars.byVar.erase(it);
     } else {
       ++it;
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp
@@ -110,3 +110,70 @@
   m1(q, q, 8);
 }
 
+void unsafe_access_in_lamda() {
+  auto p = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+
+  auto my_lambda = [&](){
+    p[5] = 10;
+  };
+
+  foo(p);
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:8-[[@LINE-1]]:8}:".data()"
+}
+
+void fixits_in_lamda() {
+  auto p = new int[10];
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+
+  auto my_lambda = [&](){
+    foo(p);
+    // CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:10}:".data()"
+  };
+
+  p[5] = 10;
+}
+
+// FIXME: Emit fixits for lambda captured variables
+void fixits_in_lambda_capture() {
+  auto p = new int[10];
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  
+  auto my_lambda = [p](){ // No fixits emitted here.
+    foo(p);
+  };
+  
+  p[5] = 10;
+}
+
+void fixits_in_lambda_capture_reference() {
+  auto p = new int[10];
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+ 
+  auto my_lambda = [&p](){ // No fixits emitted here.
+    foo(p);
+  };
+
+  p[5] = 10;
+}
+
+void fixits_in_lambda_capture_rename() {
+  auto p = new int[10];
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:11}:"std::span p"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:12-[[@LINE-2]]:12}:"{"
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-3]]:23-[[@LINE-3]]:23}:", 10}"
+  
+  auto my_lambda = [x = p](){ // No fixits emitted here.
+    foo(x);
+  };
+
+  p[5] = 10;
+} 
Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -166,7 +166,6 @@
 int * gp = garray;  // expected-warning{{'gp' is an unsafe pointer used for buffer access}}
 int gvar = gp[1];   // FIXME: file scope unsafe buffer access is not warned
 
-// FIXME: Add test for lambda capture with initializer. E. g. auto Lam = [new_p = p]() {...
 void testLambdaCaptureAndGlobal(int * p) {
   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
   int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
@@ -175,6 +174,44 @@
     return p[1]           // expected-note{{used in buffer access here}}
       + a[1] + garray[1]  // expected-note2{{used in buffer access here}}
       + gp[1];            // expected-note{{used in buffer access here}}
+
+  };
+}
+
+auto file_scope_lambda = [](int *ptr) {
+  // expected-warning@-1{{'ptr' is an unsafe pointer used for buffer access}}
+  
+  ptr[5] = 10;  // expected-note{{used in buffer access here}}
+};
+
+void testLambdaCapture() {
+  int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
+  int b[10];              // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
+  int c[10];
+
+  auto Lam1 = [a]() {
+    return a[1];           // expected-note{{used in buffer access here}}
+  };
+
+  auto Lam2 = [x = b[3]]() { // expected-note{{used in buffer access here}}
+    return x;
+  };
+
+  auto Lam = [x = c]() { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
+    return x[3]; // expected-note{{used in buffer access here}}
+  };
+}
+
+void testLambdaImplicitCapture() {
+  int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
+  int b[10];              // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
+  
+  auto Lam1 = [=]() {
+    return a[1];           // expected-note{{used in buffer access here}}
+  };
+  
+  auto Lam2 = [&]() {
+    return b[1];           // expected-note{{used in buffer access here}}
   };
 }