aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/cplusplus/declarationcomments
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-08-08 12:50:01 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2023-08-10 14:26:27 +0000
commit74a0313fcf0aeffdaad9583db2ca8dc5f78e99a7 (patch)
tree0ffb5a82fa86452cfb7dfe6e1f82f672321a85a7 /tests/auto/cplusplus/declarationcomments
parentb128c585d951082f23faf609f1599944812dcdfa (diff)
CPlusPlus: Support associating comments with a declaration
This will serve as the basic building block for several comment-related features. Task-number: QTCREATORBUG-6934 Task-number: QTCREATORBUG-12051 Task-number: QTCREATORBUG-13877 Change-Id: Ic68587c0d7985dc731da9f539884590fcec764de Reviewed-by: David Schulz <david.schulz@qt.io>
Diffstat (limited to 'tests/auto/cplusplus/declarationcomments')
-rw-r--r--tests/auto/cplusplus/declarationcomments/CMakeLists.txt4
-rw-r--r--tests/auto/cplusplus/declarationcomments/declarationcomments.qbs6
-rw-r--r--tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp225
3 files changed, 235 insertions, 0 deletions
diff --git a/tests/auto/cplusplus/declarationcomments/CMakeLists.txt b/tests/auto/cplusplus/declarationcomments/CMakeLists.txt
new file mode 100644
index 0000000000..bb36a8e306
--- /dev/null
+++ b/tests/auto/cplusplus/declarationcomments/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qtc_test(tst_cplusplus_declarationcomments
+ DEPENDS CppEditor Utils
+ SOURCES tst_declarationcomments.cpp
+)
diff --git a/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs b/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs
new file mode 100644
index 0000000000..601eb30ec0
--- /dev/null
+++ b/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs
@@ -0,0 +1,6 @@
+import "../cplusplusautotest.qbs" as CPlusPlusAutotest
+
+CPlusPlusAutotest {
+ name: "declaration comments autotest"
+ files: "tst_declarationcomments.cpp"
+}
diff --git a/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp b/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp
new file mode 100644
index 0000000000..0a92cfbbd6
--- /dev/null
+++ b/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp
@@ -0,0 +1,225 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cplusplus/Overview.h"
+#include <cplusplus/declarationcomments.h>
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/SymbolVisitor.h>
+
+#include <QtTest>
+#include <QTextDocument>
+
+using namespace CPlusPlus;
+using namespace Utils;
+
+const char testSource[] = R"TheFile(
+//! Unrelated comment, even though it mentions MyEnum.
+
+//! Related comment because of proximity.
+//! Related comment that mentions MyEnum.
+//! Related comment that mentions MyEnumA.
+
+enum MyEnum { MyEnumA, MyEnumB };
+
+// Unrelated comment because of different comment type.
+//! Related comment that mentions MyEnumClass::A.
+
+enum class MyEnumClass { A, B };
+
+// Related comment because of proximity.
+void myFunc();
+
+/*
+ * Related comment because of proximity.
+ */
+template<typename T> class MyClassTemplate {};
+
+/*
+ * Related comment, because it mentions MyOtherClass.
+ */
+
+class MyOtherClass {
+ /// Related comment for function and parameter a2, but not parameter a.
+ /// @param a2
+ void memberFunc(int a, int a2);
+};
+
+//! Comment for member function at implementation site.
+void MyOtherClass::memberFunc(int, int) {}
+
+//! An unrelated comment
+
+void funcWithoutComment();
+
+)TheFile";
+
+class SymbolFinder : public SymbolVisitor
+{
+public:
+ SymbolFinder(const QString &name, Document *doc, int expectedOccurrence)
+ : m_name(name), m_doc(doc), m_expectedOccurrence(expectedOccurrence) {}
+ const Symbol *find();
+
+private:
+ bool preVisit(Symbol *) override { return !m_symbol; }
+ bool visit(Namespace *ns) override { return visitScope(ns); }
+ bool visit(Enum *e) override { return visitScope(e); }
+ bool visit(Class *c) override { return visitScope(c); }
+ bool visit(Function *f) override { return visitScope(f); }
+ bool visit(Argument *a) override;
+ bool visit(Declaration *d) override;
+
+ bool visitScope(Scope *scope);
+
+ const QString &m_name;
+ Document * const m_doc;
+ const Symbol *m_symbol = nullptr;
+ const int m_expectedOccurrence;
+ int m_tokenLocation = -1;
+};
+
+class TestDeclarationComments : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase();
+
+ void commentsForDecl_data();
+ void commentsForDecl();
+
+private:
+ Snapshot m_snapshot;
+ Document::Ptr m_cppDoc;
+ QTextDocument m_textDoc;
+};
+
+void TestDeclarationComments::initTestCase()
+{
+ m_cppDoc = m_snapshot.preprocessedDocument(testSource, "dummy.cpp");
+ m_cppDoc->check();
+ const bool hasErrors = !m_cppDoc->diagnosticMessages().isEmpty();
+ if (hasErrors) {
+ for (const Document::DiagnosticMessage &msg : m_cppDoc->diagnosticMessages())
+ qDebug() << '\t' << msg.text();
+ }
+ QVERIFY(!hasErrors);
+ m_textDoc.setPlainText(testSource);
+}
+
+void TestDeclarationComments::commentsForDecl_data()
+{
+ QTest::addColumn<QString>("symbolName");
+ QTest::addColumn<QString>("expectedCommentPrefix");
+ QTest::addColumn<int>("occurrence");
+
+ QTest::newRow("enum type") << "MyEnum" << "//! Related comment because of proximity" << 1;
+ QTest::newRow("enum value (positive)") << "MyEnumA"
+ << "//! Related comment because of proximity" << 1;
+ QTest::newRow("enum value (negative") << "MyEnumB" << QString() << 1;
+
+ QTest::newRow("enum class type") << "MyEnumClass"
+ << "//! Related comment that mentions MyEnumClass::A" << 1;
+ QTest::newRow("enum class value (positive)")
+ << "A" << "//! Related comment that mentions MyEnumClass::A" << 1;
+ QTest::newRow("enum class value (negative") << "B" << QString() << 1;
+
+ QTest::newRow("function declaration with comment")
+ << "myFunc" << "// Related comment because of proximity" << 1;
+
+ QTest::newRow("class template")
+ << "MyClassTemplate" << "/*\n * Related comment because of proximity." << 1;
+
+ QTest::newRow("class")
+ << "MyOtherClass" << "/*\n * Related comment, because it mentions MyOtherClass." << 1;
+ QTest::newRow("member function declaration")
+ << "memberFunc" << "/// Related comment for function and parameter a2, but not parameter a."
+ << 1;
+ QTest::newRow("function parameter (negative)") << "a" << QString() << 1;
+ QTest::newRow("function parameter (positive)") << "a2" << "/// Related comment for function "
+ "and parameter a2, but not parameter a." << 1;
+
+ QTest::newRow("member function definition") << "memberFunc" <<
+ "//! Comment for member function at implementation site." << 2;
+
+ QTest::newRow("function declaration without comment") << "funcWithoutComment" << QString() << 1;
+}
+
+void TestDeclarationComments::commentsForDecl()
+{
+ QFETCH(QString, symbolName);
+ QFETCH(QString, expectedCommentPrefix);
+ QFETCH(int, occurrence);
+
+ SymbolFinder finder(symbolName, m_cppDoc.get(), occurrence);
+ const Symbol * const symbol = finder.find();
+ QVERIFY(symbol);
+
+ const QList<Token> commentTokens = commentsForDeclaration(symbol, m_snapshot, m_textDoc);
+ if (expectedCommentPrefix.isEmpty()) {
+ QVERIFY(commentTokens.isEmpty());
+ return;
+ }
+ QVERIFY(!commentTokens.isEmpty());
+
+ const int firstCommentPos = m_cppDoc->translationUnit()->getTokenPositionInDocument(
+ commentTokens.first(), &m_textDoc);
+ const QString actualCommentPrefix = m_textDoc.toPlainText().mid(firstCommentPos,
+ expectedCommentPrefix.length());
+ QCOMPARE(actualCommentPrefix, expectedCommentPrefix);
+}
+
+
+const Symbol *SymbolFinder::find()
+{
+ for (int i = 0, occurrences = 0; i < m_doc->translationUnit()->tokenCount(); ++i) {
+ const Token &tok = m_doc->translationUnit()->tokenAt(i);
+ if (tok.kind() != T_IDENTIFIER)
+ continue;
+ if (tok.spell() != m_name)
+ continue;
+ if (++occurrences == m_expectedOccurrence) {
+ m_tokenLocation = i;
+ break;
+ }
+ }
+ if (m_tokenLocation == -1)
+ return nullptr;
+
+ visit(m_doc->globalNamespace());
+ return m_symbol;
+}
+
+bool SymbolFinder::visit(Argument *a)
+{
+ if (a->sourceLocation() == m_tokenLocation)
+ m_symbol = a;
+ return false;
+}
+
+bool SymbolFinder::visit(Declaration *d)
+{
+ if (d->sourceLocation() == m_tokenLocation) {
+ m_symbol = d;
+ return false;
+ }
+ if (const auto func = d->type().type()->asFunctionType())
+ return visit(func);
+ return true;
+}
+
+bool SymbolFinder::visitScope(Scope *scope)
+{
+ for (int i = 0; i < scope->memberCount(); ++i) {
+ Symbol * const s = scope->memberAt(i);
+ if (s->sourceLocation() == m_tokenLocation) {
+ m_symbol = s;
+ return false;
+ }
+ accept(s);
+ }
+ return false;
+}
+
+QTEST_APPLESS_MAIN(TestDeclarationComments)
+#include "tst_declarationcomments.moc"