From ce032552c0df8306eefa55bc049b694762e334fa Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Mon, 23 Apr 2018 12:06:12 +0200 Subject: [PATCH] Clang: Support old-style SIGNAL/SLOT macro Color types and enable Ctrl+click for the functions and types inside SIGNAL/SLOT macros. Change-Id: Ic1c0b7372fe9a73c5607b1973d75a6656c75ef0e Reviewed-by: Nikolai Kosjar --- .../wrappedQtHeaders/QtCore/qobjectdefs.h | 3 + .../clangbackend/source/sourcelocation.cpp | 5 - .../clangbackend/source/sourcelocation.h | 20 ++-- src/tools/clangbackend/source/tokeninfo.cpp | 107 ++++++++++++++---- .../unit/unittest/data/highlightingmarks.cpp | 10 ++ tests/unit/unittest/tokenprocessor-test.cpp | 30 +++++ 6 files changed, 137 insertions(+), 38 deletions(-) diff --git a/share/qtcreator/cplusplus/wrappedQtHeaders/QtCore/qobjectdefs.h b/share/qtcreator/cplusplus/wrappedQtHeaders/QtCore/qobjectdefs.h index e2338223db7..c215327c680 100644 --- a/share/qtcreator/cplusplus/wrappedQtHeaders/QtCore/qobjectdefs.h +++ b/share/qtcreator/cplusplus/wrappedQtHeaders/QtCore/qobjectdefs.h @@ -61,6 +61,9 @@ // static_assert can be found as a class child but does not add extra AST nodes for completion #define Q_PROPERTY(arg) static_assert("Q_PROPERTY", #arg); +#define SIGNAL(arg) #arg +#define SLOT(arg) #arg + #pragma clang diagnostic pop #endif // WRAPPED_QOBJECT_DEFS_H diff --git a/src/tools/clangbackend/source/sourcelocation.cpp b/src/tools/clangbackend/source/sourcelocation.cpp index 77fd9e61da4..2450dcf590c 100644 --- a/src/tools/clangbackend/source/sourcelocation.cpp +++ b/src/tools/clangbackend/source/sourcelocation.cpp @@ -127,11 +127,6 @@ SourceLocation::SourceLocation(CXTranslationUnit cxTranslationUnit, clang_getFileLocation(cxSourceLocation, 0, 0, 0, &offset_); } -bool operator==(const SourceLocation &first, const SourceLocation &second) -{ - return clang_equalLocations(first.cxSourceLocation, second.cxSourceLocation); -} - SourceLocation::operator CXSourceLocation() const { return cxSourceLocation; diff --git a/src/tools/clangbackend/source/sourcelocation.h b/src/tools/clangbackend/source/sourcelocation.h index ac8b881294f..96db1778b26 100644 --- a/src/tools/clangbackend/source/sourcelocation.h +++ b/src/tools/clangbackend/source/sourcelocation.h @@ -36,12 +36,17 @@ class Document; class SourceLocation { - friend class Diagnostic; friend class SourceRange; friend class TranslationUnit; - friend class Cursor; - friend class FullTokenInfo; - friend bool operator==(const SourceLocation &first, const SourceLocation &second); + + friend bool operator==(const SourceLocation &first, const SourceLocation &second) + { + return clang_equalLocations(first.cxSourceLocation, second.cxSourceLocation); + } + friend bool operator!=(const SourceLocation &first, const SourceLocation &second) + { + return !(first == second); + } public: SourceLocation(); @@ -49,6 +54,8 @@ public: const Utf8String &filePath, uint line, uint column); + SourceLocation(CXTranslationUnit cxTranslationUnit, + CXSourceLocation cxSourceLocation); const Utf8String &filePath() const; uint line() const; @@ -58,9 +65,6 @@ public: SourceLocationContainer toSourceLocationContainer() const; private: - SourceLocation(CXTranslationUnit cxTranslationUnit, - CXSourceLocation cxSourceLocation); - operator CXSourceLocation() const; private: @@ -73,8 +77,6 @@ private: mutable bool isFilePathNormalized_ = true; }; -bool operator==(const SourceLocation &first, const SourceLocation &second); - std::ostream &operator<<(std::ostream &os, const SourceLocation &sourceLocation); } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/source/tokeninfo.cpp b/src/tools/clangbackend/source/tokeninfo.cpp index 0638a3c6ea6..c9311d22214 100644 --- a/src/tools/clangbackend/source/tokeninfo.cpp +++ b/src/tools/clangbackend/source/tokeninfo.cpp @@ -557,70 +557,129 @@ void TokenInfo::punctuationOrOperatorKind() m_types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument); } -enum class PropertyPart +enum class QtMacroPart { None, + SignalFunction, + SignalType, + SlotFunction, + SlotType, Type, Property, Keyword, FunctionOrPrimitiveType }; -static PropertyPart propertyPart(CXTranslationUnit tu, CXToken *token) +static bool isFirstTokenOfCursor(const Cursor &cursor, CXToken *token) +{ + const CXTranslationUnit cxTranslationUnit = cursor.cxTranslationUnit(); + const SourceLocation tokenLocation = SourceLocation(cxTranslationUnit, + clang_getTokenLocation(cxTranslationUnit, + *token)); + return cursor.sourceLocation() == tokenLocation; +} + +static bool isLastTokenOfCursor(const Cursor &cursor, CXToken *token) +{ + const CXTranslationUnit cxTranslationUnit = cursor.cxTranslationUnit(); + const SourceLocation tokenLocation = SourceLocation(cxTranslationUnit, + clang_getTokenLocation(cxTranslationUnit, + *token)); + return cursor.sourceRange().end() == tokenLocation; +} + +static bool isValidMacroToken(const Cursor &cursor, CXToken *token) +{ + // Proper macro token has at least '(' and ')' around it. + return !isFirstTokenOfCursor(cursor, token) && !isLastTokenOfCursor(cursor, token); +} + +static QtMacroPart propertyPart(CXTranslationUnit cxTranslationUnit, CXToken *token) { static constexpr const char *propertyKeywords[] = {"READ", "WRITE", "MEMBER", "RESET", "NOTIFY", "REVISION", "DESIGNABLE", "SCRIPTABLE", "STORED", "USER", "CONSTANT", "FINAL" }; - CXSourceLocation location = clang_getTokenLocation(tu, *token); - // If current token is inside Q_PROPERTY then the cursor from token's position will be - // the whole Q_PROPERTY macro cursor. - Cursor possibleQPropertyCursor = clang_getCursor(tu, location); - if (!(possibleQPropertyCursor.spelling() == "Q_PROPERTY")) - return PropertyPart::None; - - const ClangString currentToken = clang_getTokenSpelling(tu, *token); + const ClangString currentToken = clang_getTokenSpelling(cxTranslationUnit, *token); if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), currentToken) != std::end(propertyKeywords)) { - return PropertyPart::Keyword; + return QtMacroPart::Keyword; } - const ClangString nextToken = clang_getTokenSpelling(tu, *(token + 1)); - const ClangString previousToken = clang_getTokenSpelling(tu, *(token - 1)); + const ClangString nextToken = clang_getTokenSpelling(cxTranslationUnit, *(token + 1)); + const ClangString previousToken = clang_getTokenSpelling(cxTranslationUnit, *(token - 1)); if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), nextToken) != std::end(propertyKeywords)) { if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken) == std::end(propertyKeywords)) { - return PropertyPart::Property; + return QtMacroPart::Property; } - return PropertyPart::FunctionOrPrimitiveType; + return QtMacroPart::FunctionOrPrimitiveType; } if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken) != std::end(propertyKeywords)) { - return PropertyPart::FunctionOrPrimitiveType; + return QtMacroPart::FunctionOrPrimitiveType; } - return PropertyPart::Type; + return QtMacroPart::Type; +} + +static QtMacroPart signalSlotPart(CXTranslationUnit cxTranslationUnit, CXToken *token, bool signal) +{ + // We are inside macro so current token has at least '(' and macro name before it. + const ClangString prevToken = clang_getTokenSpelling(cxTranslationUnit, *(token - 2)); + if (signal) + return (prevToken == "SIGNAL") ? QtMacroPart::SignalFunction : QtMacroPart::SignalType; + return (prevToken == "SLOT") ? QtMacroPart::SlotFunction : QtMacroPart::SlotType; +} + +static QtMacroPart qtMacroPart(CXTranslationUnit cxTranslationUnit, CXToken *token) +{ + CXSourceLocation location = clang_getTokenLocation(cxTranslationUnit, *token); + + // If current token is inside macro then the cursor from token's position will be + // the whole macro cursor. + Cursor possibleQtMacroCursor = clang_getCursor(cxTranslationUnit, location); + if (!isValidMacroToken(possibleQtMacroCursor, token)) + return QtMacroPart::None; + + ClangString spelling = possibleQtMacroCursor.spelling(); + if (spelling == "Q_PROPERTY") + return propertyPart(cxTranslationUnit, token); + + if (spelling == "SIGNAL") + return signalSlotPart(cxTranslationUnit, token, true); + if (spelling == "SLOT") + return signalSlotPart(cxTranslationUnit, token, false); + return QtMacroPart::None; } void TokenInfo::invalidFileKind() { - const PropertyPart propPart = propertyPart(m_cxTranslationUnit, m_cxToken); + const QtMacroPart macroPart = qtMacroPart(m_cxTranslationUnit, m_cxToken); - switch (propPart) { - case PropertyPart::None: - case PropertyPart::Keyword: + switch (macroPart) { + case QtMacroPart::None: + case QtMacroPart::Keyword: m_types.mainHighlightingType = HighlightingType::Invalid; return; - case PropertyPart::Property: + case QtMacroPart::SignalFunction: + case QtMacroPart::SlotFunction: + m_types.mainHighlightingType = HighlightingType::Function; + break; + case QtMacroPart::SignalType: + case QtMacroPart::SlotType: + m_types.mainHighlightingType = HighlightingType::Type; + break; + case QtMacroPart::Property: m_types.mainHighlightingType = HighlightingType::QtProperty; return; - case PropertyPart::Type: + case QtMacroPart::Type: m_types.mainHighlightingType = HighlightingType::Type; return; - case PropertyPart::FunctionOrPrimitiveType: + case QtMacroPart::FunctionOrPrimitiveType: m_types.mainHighlightingType = HighlightingType::Function; return; } diff --git a/tests/unit/unittest/data/highlightingmarks.cpp b/tests/unit/unittest/data/highlightingmarks.cpp index edbe0a14c27..858f0bbf2ab 100644 --- a/tests/unit/unittest/data/highlightingmarks.cpp +++ b/tests/unit/unittest/data/highlightingmarks.cpp @@ -663,3 +663,13 @@ void TryOverloadedOperators2(Dummy object) *dummy2; dummy2 = 3; } + +int OperatorTest() { + return 1 < 2 ? 20 : 30; +} + +int signalSlotTest() { + SIGNAL(something(QString)); + SLOT(something(QString)); + SIGNAL(something(QString (*func1)(QString))); +} diff --git a/tests/unit/unittest/tokenprocessor-test.cpp b/tests/unit/unittest/tokenprocessor-test.cpp index 51daef634e4..c6df2044b57 100644 --- a/tests/unit/unittest/tokenprocessor-test.cpp +++ b/tests/unit/unittest/tokenprocessor-test.cpp @@ -1535,6 +1535,7 @@ TEST_F(TokenProcessor, QtPropertyName) { const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103)); + ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::PreprocessorExpansion)); ASSERT_THAT(infos[8], HasOnlyType(HighlightingType::QtProperty)); } @@ -1614,6 +1615,35 @@ TEST_F(TokenProcessor, LexicalParentIndex) ASSERT_THAT(containers[3].extraInfo.lexicalParentIndex, 1); } +TEST_F(TokenProcessor, QtOldStyleSignal) +{ + const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(672, 32)); + + ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::PreprocessorExpansion)); + ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::Function)); + ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Type)); +} + +TEST_F(TokenProcessor, QtOldStyleSlot) +{ + const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(673, 30)); + + ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::PreprocessorExpansion)); + ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::Function)); + ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Type)); +} + +TEST_F(TokenProcessor, QtOldStyleSignalFunctionPointerType) +{ + const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(674, 50)); + + ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::PreprocessorExpansion)); + ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::Function)); + ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Type)); + ASSERT_THAT(infos[7], HasOnlyType(HighlightingType::Type)); + ASSERT_THAT(infos[10], HasOnlyType(HighlightingType::Type)); +} + Data *TokenProcessor::d; void TokenProcessor::SetUpTestCase()