forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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)));
|
||||||
|
}
|
||||||
|
@@ -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()
|
||||||
|
Reference in New Issue
Block a user