Author: Kay Hicketts Date: 2026-06-30T17:57:07Z New Revision: 166209c3b05c5e3c3eb930ae0e613b18c43797a9
URL: https://github.com/llvm/llvm-project/commit/166209c3b05c5e3c3eb930ae0e613b18c43797a9 DIFF: https://github.com/llvm/llvm-project/commit/166209c3b05c5e3c3eb930ae0e613b18c43797a9.diff LOG: Fix false match on nullPointerConstant for array indices (#201893) # Problem Detail In the code ``` int *p[1]; (void) p[0]; ``` The AST tree for ```p[0]``` looks like ``` |-CStyleCastExpr 0xc0ae6f1d8 <line:3:3, col:13> 'void' <ToVoid> | `-ArraySubscriptExpr 0xc0ae6f1a8 <col:10, col:13> 'int *' lvalue | |-ImplicitCastExpr 0xc0ae6f190 <col:10> 'int **' <ArrayToPointerDecay> | | `-DeclRefExpr 0xc0ae6f118 <col:10> 'int *[1]' lvalue Var 0xc0ae6f098 'p' 'int *[1]' | `-IntegerLiteral 0xc0ae6f138 <col:12> 'int' 0 ``` So the previous matcher was finding the integer literal and then seeing that the parent was a pointer type and thus we were getting a positive match for something that should be replaced by a null pointer, even though the index is just an integer. # Solution In a real null pointer scenario in cpp ```int *sausage = 0;``` The AST is like ``` | `-VarDecl 0xc0ae6f218 <col:3, col:18> col:8 sausage 'int *' cinit | `-ImplicitCastExpr 0xc0ae6f2a0 <col:18> 'int *' <NullToPointer> | `-IntegerLiteral 0xc0ae6f280 <col:18> 'int' 0 ``` In C it's the same but with the exception of explicit casts e.g. ```char *beans = (char*)0;``` which are represented as ``` | `-DeclStmt 0xacee134e8 <line:11:3, col:25> | `-VarDecl 0xacee13420 <col:3, col:24> col:9 beans 'char *' cinit | `-CStyleCastExpr 0xacee134c0 <col:17, col:24> 'char *' <NullToPointer> | `-IntegerLiteral 0xacee13488 <col:24> 'int' 0 ``` So the new matcher looks for an IntegerLiteral with value 0 that has a parent that does any kind of cast to a null pointer I used AI tools to help me build and understand the code. # Original Issue clang-tidy tools replace 0 with nullptr in index references due to misinterpreting index numbers as pointers Fixes: https://github.com/llvm/llvm-project/issues/87008 Added: Modified: clang/docs/ReleaseNotes.md clang/include/clang/ASTMatchers/ASTMatchers.h clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 31e7979612a76..26d5d08979391 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -981,6 +981,8 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the ### AST Matchers +- Fixed `nullPointerConstant` matcher falsely matching integer literal `0` + in non-null-pointer contexts such as array subscripts and pointer arithmetic. - Add `functionTypeLoc` matcher for matching `FunctionTypeLoc`. - Add missing support for `TraversalKind` in some `addMatcher()` overloads. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index bc0f35898a2c9..8108ee559acea 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -8367,9 +8367,9 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr> /// matches the initializer for v1, v2, v3, cp, and ip. Does not match the /// initializer for i. AST_MATCHER_FUNCTION(internal::Matcher<Expr>, nullPointerConstant) { - return anyOf( - gnuNullExpr(), cxxNullPtrLiteralExpr(), - integerLiteral(equals(0), hasParent(expr(hasType(pointerType()))))); + return anyOf(gnuNullExpr(), cxxNullPtrLiteralExpr(), + integerLiteral(equals(0), hasParent(castExpr( + hasCastKind(CK_NullToPointer))))); } /// Matches the DecompositionDecl the binding belongs to. diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index d9647c201fc30..2c27ab0805625 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -4049,6 +4049,12 @@ TEST_P(ASTMatchersTest, NullPointerConstant) { EXPECT_TRUE(matches("char *cp = (char *)0;", expr(nullPointerConstant()))); EXPECT_TRUE(matches("int *ip = 0;", expr(nullPointerConstant()))); EXPECT_FALSE(matches("int i = 0;", expr(nullPointerConstant()))); + EXPECT_FALSE( + matches("void f(){int *p[1]; (void)p[0];}", expr(nullPointerConstant()))); + EXPECT_TRUE(matches("void f(){int index; (void)index[(int*)0];}", + expr(nullPointerConstant()))); + EXPECT_FALSE(matches("void f(){int* ptr; (void)(ptr + 0);}", + expr(nullPointerConstant()))); } TEST_P(ASTMatchersTest, NullPointerConstant_GNUNull) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
