Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -1257,6 +1257,43 @@ +
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`. +
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. +
Matches array subscript expressions. @@ -1455,6 +1492,16 @@
Matches coroutine body statements. + +coroutineBodyStmt() matches the coroutine below + generator<int> gen() { + co_return; + } +
Matches co_yield expressions. @@ -3037,10 +3084,11 @@- Matcher<CXXConstructExpr> argumentCountAtLeast unsigned 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> argumentCountAtLeast unsigned 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> argumentCountAtLeast unsigned 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> argumentCountAtLeast unsigned 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> hasBody Matcher<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> hasBody Matcher<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> hasDecayedType Matcher<QualType> InnerType @@ -7890,9 +7966,10 @@ Matches the decayed type, whoes decayed type matches InnerMatcher- Matcher<DoStmt> hasBody Matcher<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> hasBody Matcher<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> hasBody Matcher<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> hasBody Matcher<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 | templateusing 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}} }; }