[clang-tools-extra] f329e3e - [clang-tidy] Add `bugprone-pointer-arithmetic-on-polymorphic-object` check (#91951)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 4 06:44:35 PDT 2024


Author: Discookie
Date: 2024-07-04T13:44:31Z
New Revision: f329e3ed9070aee3f4c0ebc80ed62f5c4b645d73

URL: https://github.com/llvm/llvm-project/commit/f329e3ed9070aee3f4c0ebc80ed62f5c4b645d73
DIFF: https://github.com/llvm/llvm-project/commit/f329e3ed9070aee3f4c0ebc80ed62f5c4b645d73.diff

LOG: [clang-tidy] Add `bugprone-pointer-arithmetic-on-polymorphic-object` check (#91951)

Finds pointer arithmetic on classes that declare a virtual function.

This check corresponds to the SEI Cert rule [CTR56-CPP: Do not use
pointer arithmetic on polymorphic
objects](https://wiki.sei.cmu.edu/confluence/display/cplusplus/CTR56-CPP.+Do+not+use+pointer+arithmetic+on+polymorphic+objects).

```cpp
struct Base {
  virtual void ~Base();
};

struct Derived : public Base {};

void foo(Base *b) {
  b += 1; // passing `Derived` to `foo()` results in UB
}
```

[Results on open-source
projects](https://codechecker-demo.eastus.cloudapp.azure.com/Default/runs?run=Discookie-ctr56-with-classnames).
Most of the Qtbase reports are from having a `virtual override`
declaration, and the LLVM reports are true positives, as far as I can
tell.

Added: 
    clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst
    clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 1b92d2e60cc17..689eb92a3d8d1 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -51,6 +51,7 @@
 #include "NotNullTerminatedResultCheck.h"
 #include "OptionalValueConversionCheck.h"
 #include "ParentVirtualCallCheck.h"
+#include "PointerArithmeticOnPolymorphicObjectCheck.h"
 #include "PosixReturnCheck.h"
 #include "RedundantBranchConditionCheck.h"
 #include "ReservedIdentifierCheck.h"
@@ -171,6 +172,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-multiple-statement-macro");
     CheckFactories.registerCheck<OptionalValueConversionCheck>(
         "bugprone-optional-value-conversion");
+    CheckFactories.registerCheck<PointerArithmeticOnPolymorphicObjectCheck>(
+        "bugprone-pointer-arithmetic-on-polymorphic-object");
     CheckFactories.registerCheck<RedundantBranchConditionCheck>(
         "bugprone-redundant-branch-condition");
     CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 2d303191f8865..cb0d8ae98bac5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -48,6 +48,7 @@ add_clang_library(clangTidyBugproneModule
   NotNullTerminatedResultCheck.cpp
   OptionalValueConversionCheck.cpp
   ParentVirtualCallCheck.cpp
+  PointerArithmeticOnPolymorphicObjectCheck.cpp
   PosixReturnCheck.cpp
   RedundantBranchConditionCheck.cpp
   ReservedIdentifierCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
new file mode 100644
index 0000000000000..6e6ad10fabbb3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.cpp
@@ -0,0 +1,81 @@
+//===--- PointerArithmeticOnPolymorphicObjectCheck.cpp - clang-tidy--------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PointerArithmeticOnPolymorphicObjectCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+AST_MATCHER(CXXRecordDecl, isAbstract) { return Node.isAbstract(); }
+AST_MATCHER(CXXRecordDecl, isPolymorphic) { return Node.isPolymorphic(); }
+} // namespace
+
+PointerArithmeticOnPolymorphicObjectCheck::
+    PointerArithmeticOnPolymorphicObjectCheck(StringRef Name,
+                                              ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreInheritedVirtualFunctions(
+          Options.get("IgnoreInheritedVirtualFunctions", false)) {}
+
+void PointerArithmeticOnPolymorphicObjectCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoreInheritedVirtualFunctions",
+                IgnoreInheritedVirtualFunctions);
+}
+
+void PointerArithmeticOnPolymorphicObjectCheck::registerMatchers(
+    MatchFinder *Finder) {
+  const auto PolymorphicPointerExpr =
+      expr(hasType(hasCanonicalType(pointerType(pointee(hasCanonicalType(
+               hasDeclaration(cxxRecordDecl(unless(isFinal()), isPolymorphic())
+                                  .bind("pointee"))))))))
+          .bind("pointer");
+
+  const auto PointerExprWithVirtualMethod =
+      expr(hasType(hasCanonicalType(
+               pointerType(pointee(hasCanonicalType(hasDeclaration(
+                   cxxRecordDecl(
+                       unless(isFinal()),
+                       anyOf(hasMethod(isVirtualAsWritten()), isAbstract()))
+                       .bind("pointee"))))))))
+          .bind("pointer");
+
+  const auto SelectedPointerExpr = IgnoreInheritedVirtualFunctions
+                                       ? PointerExprWithVirtualMethod
+                                       : PolymorphicPointerExpr;
+
+  const auto ArraySubscript = arraySubscriptExpr(hasBase(SelectedPointerExpr));
+
+  const auto BinaryOperators =
+      binaryOperator(hasAnyOperatorName("+", "-", "+=", "-="),
+                     hasEitherOperand(SelectedPointerExpr));
+
+  const auto UnaryOperators = unaryOperator(
+      hasAnyOperatorName("++", "--"), hasUnaryOperand(SelectedPointerExpr));
+
+  Finder->addMatcher(ArraySubscript, this);
+  Finder->addMatcher(BinaryOperators, this);
+  Finder->addMatcher(UnaryOperators, this);
+}
+
+void PointerArithmeticOnPolymorphicObjectCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *PointerExpr = Result.Nodes.getNodeAs<Expr>("pointer");
+  const auto *PointeeDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("pointee");
+
+  diag(PointerExpr->getBeginLoc(),
+       "pointer arithmetic on polymorphic object of type %0 can result in "
+       "undefined behavior if the dynamic type 
diff ers from the pointer type")
+      << PointeeDecl << PointerExpr->getSourceRange();
+}
+
+} // namespace clang::tidy::bugprone

diff  --git a/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h
new file mode 100644
index 0000000000000..84f2d8e74ba87
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/PointerArithmeticOnPolymorphicObjectCheck.h
@@ -0,0 +1,41 @@
+//===--- PointerArithmeticOnPolymorphicObjectCheck.h ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds pointer arithmetic performed on classes that contain a
+/// virtual function.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.html
+class PointerArithmeticOnPolymorphicObjectCheck : public ClangTidyCheck {
+public:
+  PointerArithmeticOnPolymorphicObjectCheck(StringRef Name,
+                                            ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+
+private:
+  const bool IgnoreInheritedVirtualFunctions;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_POINTERARITHMETICONPOLYMORPHICOBJECTCHECK_H

diff  --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
index 00370ee9b3004..ffb62b409b29b 100644
--- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "../bugprone/BadSignalToKillThreadCheck.h"
+#include "../bugprone/PointerArithmeticOnPolymorphicObjectCheck.h"
 #include "../bugprone/ReservedIdentifierCheck.h"
 #include "../bugprone/SignalHandlerCheck.h"
 #include "../bugprone/SignedCharMisuseCheck.h"
@@ -238,6 +239,10 @@ class CERTModule : public ClangTidyModule {
     // CON
     CheckFactories.registerCheck<bugprone::SpuriouslyWakeUpFunctionsCheck>(
         "cert-con54-cpp");
+    // CTR
+    CheckFactories
+        .registerCheck<bugprone::PointerArithmeticOnPolymorphicObjectCheck>(
+            "cert-ctr56-cpp");
     // DCL
     CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
     CheckFactories.registerCheck<bugprone::ReservedIdentifierCheck>(

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index fe819731f38b9..e570c8184f8b0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -137,6 +137,11 @@ New checks
   Detects error-prone Curiously Recurring Template Pattern usage, when the CRTP
   can be constructed outside itself and the derived class.
 
+- New :doc:`bugprone-pointer-arithmetic-on-polymorphic-object
+  <clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object>` check.
+
+  Finds pointer arithmetic performed on classes that contain a virtual function.
+
 - New :doc:`bugprone-return-const-ref-from-parameter
   <clang-tidy/checks/bugprone/return-const-ref-from-parameter>` check.
 
@@ -199,6 +204,11 @@ New checks
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
+- New alias :doc:`cert-ctr56-cpp <clang-tidy/checks/cert/ctr56-cpp>` to
+  :doc:`bugprone-pointer-arithmetic-on-polymorphic-object
+  <clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object>`
+  was added.
+
 - New alias :doc:`cert-int09-c <clang-tidy/checks/cert/int09-c>` to
   :doc:`readability-enum-initial-value <clang-tidy/checks/readability/enum-initial-value>`
   was added.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst
new file mode 100644
index 0000000000000..1884acd5f12b3
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/pointer-arithmetic-on-polymorphic-object.rst
@@ -0,0 +1,68 @@
+.. title:: clang-tidy - bugprone-pointer-arithmetic-on-polymorphic-object
+
+bugprone-pointer-arithmetic-on-polymorphic-object
+=================================================
+
+Finds pointer arithmetic performed on classes that contain a virtual function.
+
+Pointer arithmetic on polymorphic objects where the pointer's static type is
+
diff erent from its dynamic type is undefined behavior, as the two types could
+have 
diff erent sizes, and thus the vtable pointer could point to an
+invalid address.
+
+Finding pointers where the static type contains a virtual member function is a
+good heuristic, as the pointer is likely to point to a 
diff erent,
+derived object.
+
+Example:
+
+.. code-block:: c++
+
+  struct Base {
+    virtual void ~Base();
+  };
+
+  struct Derived : public Base {};
+
+  void foo() {
+    Base *b = new Derived[10];
+
+    b += 1;
+    // warning: pointer arithmetic on class that declares a virtual function can
+    // result in undefined behavior if the dynamic type 
diff ers from the
+    // pointer type
+
+    delete[] static_cast<Derived*>(b);
+  }
+
+Options
+-------
+
+.. option:: IgnoreInheritedVirtualFunctions
+
+  When `true`, objects that only inherit a virtual function are not checked.
+  Classes that do not declare a new virtual function are excluded
+  by default, as they make up the majority of false positives.
+  Default: `false`.
+
+  .. code-block:: c++
+  
+    void bar() {
+      Base *b = new Base[10];
+      b += 1; // warning, as Base declares a virtual destructor
+  
+      delete[] b;
+  
+      Derived *d = new Derived[10]; // Derived overrides the destructor, and
+                                    // declares no other virtual functions
+      d += 1; // warning only if IgnoreVirtualDeclarationsOnly is set to false
+  
+      delete[] d;
+    }
+
+References
+----------
+
+This check corresponds to the SEI Cert rule
+`CTR56-CPP. Do not use pointer arithmetic on polymorphic objects
+<https://wiki.sei.cmu.edu/confluence/display/cplusplus/CTR56-CPP.+Do+not+use+pointer+arithmetic+on+polymorphic+objects>`_.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst
new file mode 100644
index 0000000000000..e42acbe552359
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/ctr56-cpp.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-ctr56-cpp
+.. meta::
+    :http-equiv=refresh: 5;URL=../bugprone/pointer-arithmetic-on-polymorphic-object.html
+
+cert-ctr56-cpp
+==============
+
+The `cert-ctr56-cpp` check is an alias, please see
+:doc:`bugprone-pointer-arithmetic-on-polymorphic-object
+<../bugprone/pointer-arithmetic-on-polymorphic-object>` for more information.
\ No newline at end of file

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index a698cecc0825c..9671f3895f5d3 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -157,6 +157,7 @@ Clang-Tidy Checks
    :doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes"
    :doc:`bugprone-unused-return-value <bugprone/unused-return-value>`,
    :doc:`bugprone-use-after-move <bugprone/use-after-move>`,
+   :doc:`bugprone-pointer-arithmetic-on-polymorphic-object <bugprone/pointer-arithmetic-on-polymorphic-object>`,
    :doc:`bugprone-virtual-near-miss <bugprone/virtual-near-miss>`, "Yes"
    :doc:`cert-dcl50-cpp <cert/dcl50-cpp>`,
    :doc:`cert-dcl58-cpp <cert/dcl58-cpp>`,

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
new file mode 100644
index 0000000000000..caf24f79ad2d8
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-all.cpp
@@ -0,0 +1,140 @@
+// RUN: %check_clang_tidy %s bugprone-pointer-arithmetic-on-polymorphic-object %t --
+
+class Base {
+public:  
+  virtual ~Base() {}
+};
+
+class Derived : public Base {};
+
+class FinalDerived final : public Base {};
+
+class AbstractBase {
+public:
+  virtual void f() = 0;
+  virtual ~AbstractBase() {}
+};
+
+class AbstractInherited : public AbstractBase {};
+
+class AbstractOverride : public AbstractInherited {
+public:
+  void f() override {}
+};
+
+void operators() {
+  Base *b = new Derived[10];
+
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b = b + 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  --b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b[1];
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  delete[] static_cast<Derived*>(b);
+}
+
+void subclassWarnings() {
+  Base *b = new Base[10];
+
+  // False positive that's impossible to distinguish without
+  // path-sensitive analysis, but the code is bug-prone regardless.
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  delete[] b;
+
+  // Common false positive is a class that overrides all parent functions.
+  // Is a warning because of the check configuration.
+  Derived *d = new Derived[10];
+
+  d += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived'
+
+  delete[] d;
+
+  // Final classes cannot have a dynamic type.
+  FinalDerived *fd = new FinalDerived[10];
+
+  fd += 1;
+  // no-warning
+
+  delete[] fd;
+}
+
+void abstractWarnings() {
+  // Classes with an abstract member funtion are always matched.
+  AbstractBase *ab = new AbstractOverride[10];
+
+  ab += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractBase'
+
+  delete[] static_cast<AbstractOverride*>(ab);
+
+  AbstractInherited *ai = new AbstractOverride[10];
+
+  ai += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractInherited'
+
+  delete[] static_cast<AbstractOverride*>(ai);
+
+  // Is a warning because of the check configuration.
+  AbstractOverride *ao = new AbstractOverride[10];
+
+  ao += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractOverride'
+
+  delete[] ao;
+}
+
+template <typename T>
+void templateWarning(T *t) {
+  // FIXME: Tidy doesn't support template instantiation locations properly.
+  t += 1;
+  // no-warning
+}
+
+void functionArgument(Base *b) {
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  templateWarning(b);
+}
+
+using BaseAlias = Base;
+using DerivedAlias = Derived;
+using FinalDerivedAlias = FinalDerived;
+
+using BasePtr = Base*;
+using DerivedPtr = Derived*;
+using FinalDerivedPtr = FinalDerived*;
+
+void typeAliases(BaseAlias *b, DerivedAlias *d, FinalDerivedAlias *fd,
+                 BasePtr bp, DerivedPtr dp, FinalDerivedPtr fdp) {
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  d += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived'
+
+  fd += 1;
+  // no-warning
+
+  bp += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  dp += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Derived'
+
+  fdp += 1;
+  // 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
new file mode 100644
index 0000000000000..48757bbc9b10e
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/pointer-arithmetic-on-polymorphic-object-decl-only.cpp
@@ -0,0 +1,141 @@
+// RUN: %check_clang_tidy %s bugprone-pointer-arithmetic-on-polymorphic-object %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: {bugprone-pointer-arithmetic-on-polymorphic-object.IgnoreInheritedVirtualFunctions: true}}"
+
+class Base {
+public:  
+  virtual ~Base() {}
+};
+
+class Derived : public Base {};
+
+class FinalDerived final : public Base {};
+
+class AbstractBase {
+public:
+  virtual void f() = 0;
+  virtual ~AbstractBase() {}
+};
+
+class AbstractInherited : public AbstractBase {};
+
+class AbstractOverride : public AbstractInherited {
+public:
+  void f() override {}
+};
+
+void operators() {
+  Base *b = new Derived[10];
+
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b = b + 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  --b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  b[1];
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base' can result in undefined behavior if the dynamic type 
diff ers from the pointer type [bugprone-pointer-arithmetic-on-polymorphic-object]
+
+  delete[] static_cast<Derived*>(b);
+}
+
+void subclassWarnings() {
+  Base *b = new Base[10];
+
+  // False positive that's impossible to distinguish without
+  // path-sensitive analysis, but the code is bug-prone regardless.
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  delete[] b;
+
+  // Common false positive is a class that overrides all parent functions.
+  Derived *d = new Derived[10];
+
+  d += 1;
+  // no-warning
+
+  delete[] d;
+
+  // Final classes cannot have a dynamic type.
+  FinalDerived *fd = new FinalDerived[10];
+
+  fd += 1;
+  // no-warning
+
+  delete[] fd;
+}
+
+void abstractWarnings() {
+  // Classes with an abstract member funtion are always matched.
+  AbstractBase *ab = new AbstractOverride[10];
+
+  ab += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractBase'
+
+  delete[] static_cast<AbstractOverride*>(ab);
+
+  AbstractInherited *ai = new AbstractOverride[10];
+
+  ai += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'AbstractInherited'
+
+  delete[] static_cast<AbstractOverride*>(ai);
+
+  // If all abstract member functions are overridden, the class is not matched.
+  AbstractOverride *ao = new AbstractOverride[10];
+
+  ao += 1;
+  // no-warning
+
+  delete[] ao;
+}
+
+template <typename T>
+void templateWarning(T *t) {
+  // FIXME: Tidy doesn't support template instantiation locations properly.
+  t += 1;
+  // no-warning
+}
+
+void functionArgument(Base *b) {
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  templateWarning(b);
+}
+
+using BaseAlias = Base;
+using DerivedAlias = Derived;
+using FinalDerivedAlias = FinalDerived;
+
+using BasePtr = Base*;
+using DerivedPtr = Derived*;
+using FinalDerivedPtr = FinalDerived*;
+
+void typeAliases(BaseAlias *b, DerivedAlias *d, FinalDerivedAlias *fd,
+                 BasePtr bp, DerivedPtr dp, FinalDerivedPtr fdp) {
+  b += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  d += 1;
+  // no-warning
+
+  fd += 1;
+  // no-warning
+
+  bp += 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: pointer arithmetic on polymorphic object of type 'Base'
+
+  dp += 1;
+  // no-warning
+
+  fdp += 1;
+  // no-warning
+}


        


More information about the cfe-commits mailing list