https://github.com/Hao-Chen2337 created 
https://github.com/llvm/llvm-project/pull/215724

## Summary

This PR fixes a crash in clangd / clang-tidy: running the 
`bugprone-pointer-arithmetic-on-polymorphic-object` check on code that does 
pointer arithmetic on a pointer to an **incomplete type** (a type that is 
forward-declared but never defined) makes the matcher hit an assertion and 
abort.

I searched the known issues and did not find an existing report for this bug 
(not 100% sure, but I did not find one). I first hit it in the clangd VS Code 
extension — clangd kept crashing and restarting when opening certain files. To 
rule out an extension-specific problem, I built an assertions-enabled clangd 
from the latest official source and ran it standalone with `--check`; the crash 
reproduces reliably on both Windows and Linux.

## Fix

First, the assertion (assertions-enabled build):

```
clang-tidy: .../clang/include/clang/AST/DeclCXX.h:463:
  clang::CXXRecordDecl::DefinitionData& clang::CXXRecordDecl::data() const:
  Assertion `DD && "queried property of class with no definition"' failed.
ASTMatcher: Matching 'bugprone-pointer-arithmetic-on-polymorphic-object'
  against: ArraySubscriptExpr : <...repro.cpp:4:26, col:29>
```

In a release build (e.g. clangd 22.1.6) the same path is a silent SIGSEGV, exit 
code 0xC0000005.

Minimal reproducer (create the files in a temp directory first):

```
mkdir -p /tmp/repro && cd /tmp/repro

cat > repro.cpp <<'EOF'
#include <map>
std::map<const Incomplete *, int> m;
struct Incomplete;
void f(Incomplete * p) { m[p]; }
EOF

cat > .clang-tidy <<'EOF'
Checks: 'bugprone-pointer-arithmetic-on-polymorphic-object'
EOF
```

Then reproduce (use the path to your own clangd / clang-tidy):

```
clangd --check=repro.cpp
clang-tidy repro.cpp 
-checks='bugprone-pointer-arithmetic-on-polymorphic-object' --
```

> Note: do not put a `compile_commands.json` in the directory (with a real 
> compile command the crash does not reproduce), and run from that directory.

The problem is in the check's two custom matchers:

```
AST_MATCHER(CXXRecordDecl, isAbstract)   { return Node.isAbstract(); }
AST_MATCHER(CXXRecordDecl, isPolymorphic){ return Node.isPolymorphic(); }
```

They call `CXXRecordDecl::isAbstract()/isPolymorphic()`, which access 
`CXXRecordDecl::data()`. An incomplete type has no `DefinitionData`, so 
`data()` dereferences null.

The fix guards both matchers with `hasDefinition()`: a class without a 
definition is simply treated as not polymorphic/abstract, so `data()` is never 
reached.

```
AST_MATCHER(CXXRecordDecl, isAbstract) {
  return Node.hasDefinition() && Node.isAbstract();
}
AST_MATCHER(CXXRecordDecl, isPolymorphic) {
  return Node.hasDefinition() && Node.isPolymorphic();
}
```

This follows the same guard pattern as the fix for #140685 (PR #205973).

## Test

Added a regression case to each of the two test variants, covering the two 
matcher paths of the check.

The regression code is intentionally ill-formed — pointer arithmetic on an 
incomplete type is already a compile error. Why keep it in a template?

- If the code sits at file scope, the compiler error-recovers it the moment it 
sees the error, so the expression leaves no usable AST node and the matcher 
never sees it — the crash cannot be reproduced. Inside an uninstantiated 
template, the body is not semantically checked yet (template bodies are only 
analysed on instantiation), so the node survives and the matcher reaches it.
- As a bonus, since the template is never instantiated, the intentional compile 
error is not reported either, so the test does not need 
`-expect-clang-tidy-error`.

Verified: both cases crash before the fix (red) and pass after it (green).

>From f1ab8c7ff4783ab86e0dc1f2fc0df121154c170a Mon Sep 17 00:00:00 2001
From: Hao-Chen2337 <[email protected]>
Date: Wed, 12 Aug 2026 11:13:14 +0800
Subject: [PATCH] [clang-tidy] Fix crash on pointer arithmetic with an
 incomplete type in bugprone-pointer-arithmetic-on-polymorphic-object

---
 .../PointerArithmeticOnPolymorphicObjectCheck.cpp    |  8 ++++++--
 .../pointer-arithmetic-on-polymorphic-object-all.cpp | 12 ++++++++++++
 ...er-arithmetic-on-polymorphic-object-decl-only.cpp | 12 ++++++++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
 
b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
index ef5fee9f3d2c3..e54a77953283a 100644
--- 
a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
+++ 
b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
@@ -15,8 +15,12 @@ using namespace clang::ast_matchers;
 namespace clang::tidy::bugprone {
 
 namespace {
-AST_MATCHER(CXXRecordDecl, isAbstract) { return Node.isAbstract(); }
-AST_MATCHER(CXXRecordDecl, isPolymorphic) { return Node.isPolymorphic(); }
+AST_MATCHER(CXXRecordDecl, isAbstract) {
+  return Node.hasDefinition() && Node.isAbstract();
+}
+AST_MATCHER(CXXRecordDecl, isPolymorphic) {
+  return Node.hasDefinition() && Node.isPolymorphic();
+}
 } // namespace
 
 PointerArithmeticOnPolymorphicObjectCheck::
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
index f8163a2fd3fb8..ccdc8e5761b59 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
@@ -150,3 +150,15 @@ struct TemplateHolder : Base {
     (void)x;
   }
 };
+
+// Regression test: pointer arithmetic on a pointer to an incomplete type must
+// not crash the matcher. Kept in a template: the ill-formed subscript survives
+// in the AST only in a lazily-analysed template body.
+template <typename T>
+struct IncompletePointeeCrash {
+  struct Incomplete;
+  void f(Incomplete *p) {
+    p[0];
+    // no-warning
+  }
+};
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp
index 48757bbc9b10e..5a14f0617d950 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp
@@ -139,3 +139,15 @@ void typeAliases(BaseAlias *b, DerivedAlias *d, 
FinalDerivedAlias *fd,
   fdp += 1;
   // no-warning
 }
+
+// Regression test: pointer arithmetic on a pointer to an incomplete type must
+// not crash the matcher. Kept in a template: the ill-formed subscript survives
+// in the AST only in a lazily-analysed template body.
+template <typename T>
+struct IncompletePointeeCrash {
+  struct Incomplete;
+  void f(Incomplete *p) {
+    p[0];
+    // no-warning
+  }
+};

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

Reply via email to