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 <nikolai.kosjar@qt.io>
This commit is contained in:
Ivan Donchevskii
2018-04-23 12:06:12 +02:00
parent a2e8c68fee
commit ce032552c0
6 changed files with 137 additions and 38 deletions

View File

@@ -61,6 +61,9 @@
// static_assert can be found as a class child but does not add extra AST nodes for completion // 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 Q_PROPERTY(arg) static_assert("Q_PROPERTY", #arg);
#define SIGNAL(arg) #arg
#define SLOT(arg) #arg
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif // WRAPPED_QOBJECT_DEFS_H #endif // WRAPPED_QOBJECT_DEFS_H

View File

@@ -127,11 +127,6 @@ SourceLocation::SourceLocation(CXTranslationUnit cxTranslationUnit,
clang_getFileLocation(cxSourceLocation, 0, 0, 0, &offset_); 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 SourceLocation::operator CXSourceLocation() const
{ {
return cxSourceLocation; return cxSourceLocation;

View File

@@ -36,12 +36,17 @@ class Document;
class SourceLocation class SourceLocation
{ {
friend class Diagnostic;
friend class SourceRange; friend class SourceRange;
friend class TranslationUnit; 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: public:
SourceLocation(); SourceLocation();
@@ -49,6 +54,8 @@ public:
const Utf8String &filePath, const Utf8String &filePath,
uint line, uint line,
uint column); uint column);
SourceLocation(CXTranslationUnit cxTranslationUnit,
CXSourceLocation cxSourceLocation);
const Utf8String &filePath() const; const Utf8String &filePath() const;
uint line() const; uint line() const;
@@ -58,9 +65,6 @@ public:
SourceLocationContainer toSourceLocationContainer() const; SourceLocationContainer toSourceLocationContainer() const;
private: private:
SourceLocation(CXTranslationUnit cxTranslationUnit,
CXSourceLocation cxSourceLocation);
operator CXSourceLocation() const; operator CXSourceLocation() const;
private: private:
@@ -73,8 +77,6 @@ private:
mutable bool isFilePathNormalized_ = true; mutable bool isFilePathNormalized_ = true;
}; };
bool operator==(const SourceLocation &first, const SourceLocation &second);
std::ostream &operator<<(std::ostream &os, const SourceLocation &sourceLocation); std::ostream &operator<<(std::ostream &os, const SourceLocation &sourceLocation);
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -557,70 +557,129 @@ void TokenInfo::punctuationOrOperatorKind()
m_types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument); m_types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument);
} }
enum class PropertyPart enum class QtMacroPart
{ {
None, None,
SignalFunction,
SignalType,
SlotFunction,
SlotType,
Type, Type,
Property, Property,
Keyword, Keyword,
FunctionOrPrimitiveType 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[] static constexpr const char *propertyKeywords[]
= {"READ", "WRITE", "MEMBER", "RESET", "NOTIFY", "REVISION", "DESIGNABLE", = {"READ", "WRITE", "MEMBER", "RESET", "NOTIFY", "REVISION", "DESIGNABLE",
"SCRIPTABLE", "STORED", "USER", "CONSTANT", "FINAL" "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 const ClangString currentToken = clang_getTokenSpelling(cxTranslationUnit, *token);
// 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);
if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), currentToken) if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), currentToken)
!= std::end(propertyKeywords)) { != std::end(propertyKeywords)) {
return PropertyPart::Keyword; return QtMacroPart::Keyword;
} }
const ClangString nextToken = clang_getTokenSpelling(tu, *(token + 1)); const ClangString nextToken = clang_getTokenSpelling(cxTranslationUnit, *(token + 1));
const ClangString previousToken = clang_getTokenSpelling(tu, *(token - 1)); const ClangString previousToken = clang_getTokenSpelling(cxTranslationUnit, *(token - 1));
if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), nextToken) if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), nextToken)
!= std::end(propertyKeywords)) { != std::end(propertyKeywords)) {
if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken) if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken)
== std::end(propertyKeywords)) { == 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) if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken)
!= std::end(propertyKeywords)) { != 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() void TokenInfo::invalidFileKind()
{ {
const PropertyPart propPart = propertyPart(m_cxTranslationUnit, m_cxToken); const QtMacroPart macroPart = qtMacroPart(m_cxTranslationUnit, m_cxToken);
switch (propPart) { switch (macroPart) {
case PropertyPart::None: case QtMacroPart::None:
case PropertyPart::Keyword: case QtMacroPart::Keyword:
m_types.mainHighlightingType = HighlightingType::Invalid; m_types.mainHighlightingType = HighlightingType::Invalid;
return; 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; m_types.mainHighlightingType = HighlightingType::QtProperty;
return; return;
case PropertyPart::Type: case QtMacroPart::Type:
m_types.mainHighlightingType = HighlightingType::Type; m_types.mainHighlightingType = HighlightingType::Type;
return; return;
case PropertyPart::FunctionOrPrimitiveType: case QtMacroPart::FunctionOrPrimitiveType:
m_types.mainHighlightingType = HighlightingType::Function; m_types.mainHighlightingType = HighlightingType::Function;
return; return;
} }

View File

@@ -663,3 +663,13 @@ void TryOverloadedOperators2(Dummy object)
*dummy2; *dummy2;
dummy2 = 3; dummy2 = 3;
} }
int OperatorTest() {
return 1 < 2 ? 20 : 30;
}
int signalSlotTest() {
SIGNAL(something(QString));
SLOT(something(QString));
SIGNAL(something(QString (*func1)(QString)));
}

View File

@@ -1535,6 +1535,7 @@ TEST_F(TokenProcessor, QtPropertyName)
{ {
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103)); const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103));
ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::PreprocessorExpansion));
ASSERT_THAT(infos[8], HasOnlyType(HighlightingType::QtProperty)); ASSERT_THAT(infos[8], HasOnlyType(HighlightingType::QtProperty));
} }
@@ -1614,6 +1615,35 @@ TEST_F(TokenProcessor, LexicalParentIndex)
ASSERT_THAT(containers[3].extraInfo.lexicalParentIndex, 1); 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; Data *TokenProcessor::d;
void TokenProcessor::SetUpTestCase() void TokenProcessor::SetUpTestCase()