[kdevelop/kdevelop] plugins/clang: Detect final attribute on destructors

Martin Bednar <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 6c742b0664ebc2279d9798d2022062574ade363e by Martin Bednar, on behalf of Yin Zhijie.
Committed on 29/07/2026 at 11:45.
Pushed by bednar into branch 'master'.

Detect final attribute on destructors

M  +18   -0    plugins/clang/tests/files/classfunctions.cpp
M  +17   -0    plugins/clang/tests/files/functions.cpp
M  +20   -0    plugins/clang/tests/test_navigationcontext.cpp
M  +2    -1    plugins/clang/util/clangutils.cpp

https://invent.kde.org/kdevelop/kdevelop/-/commit/6c742b0664ebc2279d9798d2022062574ade363e

diff --git a/plugins/clang/tests/files/classfunctions.cpp b/plugins/clang/tests/files/classfunctions.cpp
index 2b1fc90a2b..c3db33cb78 100644
--- a/plugins/clang/tests/files/classfunctions.cpp
+++ b/plugins/clang/tests/files/classfunctions.cpp
@@ -80,3 +80,21 @@ public:
     /// "isDefinition" : true
     explicit operator bool() const noexcept final {return true;}
 };
+
+class FinalDestructorBase
+{
+public:
+    /// "isAbstract" : true
+    virtual ~FinalDestructorBase() = 0;
+};
+
+class FinalDestructor final : public FinalDestructorBase
+{
+public:
+    /// "isNoexcept" : true,
+    /// "isAbstract" : false,
+    /// "isFinal" : true,
+    /// "isVirtual" : true,
+    /// "isDefinition" : true
+    ~FinalDestructor() final {}
+};
diff --git a/plugins/clang/tests/files/functions.cpp b/plugins/clang/tests/files/functions.cpp
index 9e31be8823..26da8523b1 100644
--- a/plugins/clang/tests/files/functions.cpp
+++ b/plugins/clang/tests/files/functions.cpp
@@ -42,6 +42,12 @@ public:
     void nonConstMethod();
 };
 
+class FinalDestructorBase
+{
+public:
+    virtual ~FinalDestructorBase() = default;
+};
+
 class Derived : public Base
 {
 public:
@@ -53,6 +59,17 @@ public:
     void bar3() final;
 };
 
+class FinalDestructor final : public FinalDestructorBase
+{
+public:
+    /// "isNoexcept" : true,
+    /// "isStatic" : false,
+    /// "isVirtual" : true,
+    /// "isAbstract" : false,
+    /// "isFinal" : true
+    ~FinalDestructor() final = default;
+};
+
 /// "isNoexcept" : false
 void bar1() {}
 /// "isNoexcept" : true
diff --git a/plugins/clang/tests/test_navigationcontext.cpp b/plugins/clang/tests/test_navigationcontext.cpp
index 36fd808b2c..397693a028 100644
--- a/plugins/clang/tests/test_navigationcontext.cpp
+++ b/plugins/clang/tests/test_navigationcontext.cpp
@@ -56,6 +56,15 @@ void TestNavigationContext::testDeclarationDetailsVirtualFinalAbstract()
             virtual ~Derived();
             virtual bool isFinal() final;
         };
+
+        class DestructorBase {
+        public:
+            virtual ~DestructorBase() = default;
+        };
+
+        class FinalDestructor final : public DestructorBase {
+            ~FinalDestructor() final;
+        };
     )"),
                   QStringLiteral("cpp"));
     file.parse(TopDUContext::AllDeclarationsContextsAndUses);
@@ -78,6 +87,11 @@ void TestNavigationContext::testDeclarationDetailsVirtualFinalAbstract()
         QCOMPARE(isFinalDecls.size(), 1);
         const DeclarationPointer isFinalDecl(isFinalDecls.first());
 
+        const auto finalDestructorDecls =
+            top->findDeclarations(QualifiedIdentifier(u"FinalDestructor::~FinalDestructor"));
+        QCOMPARE(finalDestructorDecls.size(), 1);
+        const DeclarationPointer finalDestructorDecl(finalDestructorDecls.first());
+
         const QStringList onlyVirtualDetails = TestDeclarationNavigationContext::declarationDetails(onlyVirtualDecl);
         QVERIFY(onlyVirtualDetails.contains(QStringLiteral("virtual")));
         QVERIFY(!onlyVirtualDetails.contains(QStringLiteral("abstract")));
@@ -92,6 +106,12 @@ void TestNavigationContext::testDeclarationDetailsVirtualFinalAbstract()
         QVERIFY(isFinalDetails.contains(QStringLiteral("final")));
         QVERIFY(!isFinalDetails.contains(QStringLiteral("virtual")));
         QVERIFY(!isFinalDetails.contains(QStringLiteral("abstract")));
+
+        const QStringList finalDestructorDetails =
+            TestDeclarationNavigationContext::declarationDetails(finalDestructorDecl);
+        QVERIFY(finalDestructorDetails.contains(QStringLiteral("final")));
+        QVERIFY(!finalDestructorDetails.contains(QStringLiteral("virtual")));
+        QVERIFY(!finalDestructorDetails.contains(QStringLiteral("abstract")));
     }
 }
 
diff --git a/plugins/clang/util/clangutils.cpp b/plugins/clang/util/clangutils.cpp
index 93fa5b90d4..5f0fc71444 100644
--- a/plugins/clang/util/clangutils.cpp
+++ b/plugins/clang/util/clangutils.cpp
@@ -402,7 +402,8 @@ KDevelop::ClassFunctionFlags ClangUtils::specialAttributes(CXCursor cursor)
     // check for our injected attributes to detect Qt signals and slots
     // see also the contents of wrappedQtHeaders/QtCore/qobjectdefs.h
     ClassFunctionFlags flags = {};
-    if (cursor.kind == CXCursor_CXXMethod || cursor.kind == CXCursor_ConversionFunction) {
+    if (cursor.kind == CXCursor_CXXMethod || cursor.kind == CXCursor_ConversionFunction
+        || cursor.kind == CXCursor_Destructor) {
         clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor /*parent*/, CXClientData data) -> CXChildVisitResult {
             auto& flags = *static_cast<ClassFunctionFlags*>(data);
             switch (cursor.kind) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.