Clang: Properly handle Q_PROPERTY in TokenInfo

Transform Q_PROPERTY into unique AST node.
Mark different parts with types and search for parent
in FullTokenInfos.

Change-Id: Iaa1ec0c73d34773edf5605d3682bd6a290d195de
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
Ivan Donchevskii
2018-01-26 15:12:38 +01:00
parent b5f63a76b2
commit 66c7629814
15 changed files with 235 additions and 27 deletions

View File

@@ -23,11 +23,17 @@
** **
****************************************************************************/ ****************************************************************************/
#ifndef WRAPPED_QOBJECT_DEFS_H
#define WRAPPED_QOBJECT_DEFS_H
// Include qobjectdefs.h from Qt ... // Include qobjectdefs.h from Qt ...
#include_next <qobjectdefs.h> #include_next <qobjectdefs.h>
#include <utility>
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmacro-redefined" #pragma clang diagnostic ignored "-Wmacro-redefined"
#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
// ...and redefine macros for tagging signals/slots // ...and redefine macros for tagging signals/slots
#ifdef signals #ifdef signals
@@ -54,4 +60,15 @@
# define Q_SLOT __attribute__((annotate("qt_slot"))) # define Q_SLOT __attribute__((annotate("qt_slot")))
#endif #endif
template <char... chars>
using QPropertyMagicString = std::integer_sequence<char, chars...>;
template <class T, T... chars>
constexpr QPropertyMagicString<chars...> operator""_qpropstr() { return { }; }
// Create unique AST node for the property.
#define Q_PROPERTY(arg) void QPropertyMagicFunction(decltype(#arg ## _qpropstr));
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif // WRAPPED_QOBJECT_DEFS_H

View File

@@ -95,7 +95,8 @@ enum class HighlightingType : quint8
Enum, Enum,
Union, Union,
TypeAlias, TypeAlias,
Typedef Typedef,
QtProperty
}; };
enum class StorageClass : quint8 enum class StorageClass : quint8

View File

@@ -59,6 +59,7 @@ static const char *highlightingTypeToCStringLiteral(HighlightingType type)
RETURN_TEXT_FOR_CASE(Union); RETURN_TEXT_FOR_CASE(Union);
RETURN_TEXT_FOR_CASE(TypeAlias); RETURN_TEXT_FOR_CASE(TypeAlias);
RETURN_TEXT_FOR_CASE(Typedef); RETURN_TEXT_FOR_CASE(Typedef);
RETURN_TEXT_FOR_CASE(QtProperty);
default: return "UnhandledHighlightingType"; default: return "UnhandledHighlightingType";
} }
} }
@@ -79,7 +80,6 @@ QDebug operator<<(QDebug debug, const ExtraInfo &extraInfo)
<< extraInfo.definition << ", " << extraInfo.definition << ", "
<< extraInfo.signal << ", " << extraInfo.signal << ", "
<< extraInfo.slot << ", " << extraInfo.slot << ", "
<< extraInfo.property
<< ")"; << ")";
return debug; return debug;
} }

View File

@@ -56,13 +56,12 @@ struct ExtraInfo
, definition(false) , definition(false)
, signal(false) , signal(false)
, slot(false) , slot(false)
, property(false)
{ {
} }
ExtraInfo(Utf8String token, Utf8String typeSpelling, Utf8String resultTypeSpelling, ExtraInfo(Utf8String token, Utf8String typeSpelling, Utf8String resultTypeSpelling,
Utf8String semanticParentTypeSpelling, AccessSpecifier accessSpecifier, Utf8String semanticParentTypeSpelling, AccessSpecifier accessSpecifier,
StorageClass storageClass, bool isIdentifier, bool isInclusion, StorageClass storageClass, bool isIdentifier, bool isInclusion,
bool isDeclaration, bool isDefinition, bool isSignal, bool isSlot, bool isProperty) bool isDeclaration, bool isDefinition, bool isSignal, bool isSlot)
: token(token) : token(token)
, typeSpelling(typeSpelling) , typeSpelling(typeSpelling)
, resultTypeSpelling(resultTypeSpelling) , resultTypeSpelling(resultTypeSpelling)
@@ -75,7 +74,6 @@ struct ExtraInfo
, definition(isDefinition) , definition(isDefinition)
, signal(isSignal) , signal(isSignal)
, slot(isSlot) , slot(isSlot)
, property(isProperty)
{ {
} }
Utf8String token; Utf8String token;
@@ -90,7 +88,6 @@ struct ExtraInfo
bool definition : 1; bool definition : 1;
bool signal : 1; bool signal : 1;
bool slot : 1; bool slot : 1;
bool property : 1;
}; };
inline QDataStream &operator<<(QDataStream &out, const ExtraInfo &extraInfo); inline QDataStream &operator<<(QDataStream &out, const ExtraInfo &extraInfo);
@@ -215,7 +212,6 @@ inline QDataStream &operator<<(QDataStream &out, const ExtraInfo &extraInfo)
out << extraInfo.definition; out << extraInfo.definition;
out << extraInfo.signal; out << extraInfo.signal;
out << extraInfo.slot; out << extraInfo.slot;
out << extraInfo.property;
return out; return out;
} }
@@ -234,7 +230,6 @@ inline QDataStream &operator>>(QDataStream &in, ExtraInfo &extraInfo)
bool isDefinition; bool isDefinition;
bool isSignal; bool isSignal;
bool isSlot; bool isSlot;
bool isProperty;
in >> accessSpecifier; in >> accessSpecifier;
in >> storageClass; in >> storageClass;
@@ -244,7 +239,6 @@ inline QDataStream &operator>>(QDataStream &in, ExtraInfo &extraInfo)
in >> isDefinition; in >> isDefinition;
in >> isSignal; in >> isSignal;
in >> isSlot; in >> isSlot;
in >> isProperty;
extraInfo.accessSpecifier = static_cast<AccessSpecifier>(accessSpecifier); extraInfo.accessSpecifier = static_cast<AccessSpecifier>(accessSpecifier);
extraInfo.storageClass = static_cast<StorageClass>(storageClass); extraInfo.storageClass = static_cast<StorageClass>(storageClass);
@@ -254,7 +248,6 @@ inline QDataStream &operator>>(QDataStream &in, ExtraInfo &extraInfo)
extraInfo.definition = isDefinition; extraInfo.definition = isDefinition;
extraInfo.signal = isSignal; extraInfo.signal = isSignal;
extraInfo.slot = isSlot; extraInfo.slot = isSlot;
extraInfo.property = isProperty;
return in; return in;
} }
@@ -271,8 +264,7 @@ inline bool operator==(const ExtraInfo &first, const ExtraInfo &second)
&& first.declaration == second.declaration && first.declaration == second.declaration
&& first.definition == second.definition && first.definition == second.definition
&& first.signal == second.signal && first.signal == second.signal
&& first.slot == second.slot && first.slot == second.slot;
&& first.property == second.property;
} }
inline QDataStream &operator<<(QDataStream &out, HighlightingType highlightingType) inline QDataStream &operator<<(QDataStream &out, HighlightingType highlightingType)

View File

@@ -50,6 +50,7 @@ TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type)
case HighlightingType::LocalVariable: case HighlightingType::LocalVariable:
return TextEditor::C_LOCAL; return TextEditor::C_LOCAL;
case HighlightingType::Field: case HighlightingType::Field:
case HighlightingType::QtProperty:
return TextEditor::C_FIELD; return TextEditor::C_FIELD;
case HighlightingType::GlobalVariable: case HighlightingType::GlobalVariable:
return TextEditor::C_GLOBAL; return TextEditor::C_GLOBAL;

View File

@@ -26,6 +26,9 @@
#include "clangstring.h" #include "clangstring.h"
#include "cursor.h" #include "cursor.h"
#include "fulltokeninfo.h" #include "fulltokeninfo.h"
#include "sourcerange.h"
#include <utils/predicates.h>
namespace ClangBackEnd { namespace ClangBackEnd {
@@ -72,6 +75,73 @@ void FullTokenInfo::updateTypeSpelling(const Cursor &cursor, bool functionLike)
+ (hasSpaceAfterReturnType ? 1 : 0)); + (hasSpaceAfterReturnType ? 1 : 0));
} }
static Utf8String propertyParentSpelling(CXTranslationUnit cxTranslationUnit,
const Utf8String &filePath,
uint line, uint column)
{
// Q_PROPERTY expands into QPropertyMagicFunction which can be found as a child of
// the containing class.
Cursor tuCursor = clang_getTranslationUnitCursor(cxTranslationUnit);
Utf8String parentSpelling;
tuCursor.visit([&filePath, line, column, &parentSpelling](CXCursor cxCursor, CXCursor parent) {
const CXCursorKind kind = clang_getCursorKind(cxCursor);
if (kind == CXCursor_Namespace || kind == CXCursor_StructDecl
|| kind == CXCursor_ClassDecl || kind == CXCursor_CXXMethod) {
Cursor cursor(cxCursor);
const SourceRange range = cursor.sourceRange();
if (range.start().filePath() != filePath)
return CXChildVisit_Continue;
if (range.contains(line, column)) {
if (kind == CXCursor_Namespace || kind == CXCursor_StructDecl
|| kind == CXCursor_ClassDecl) {
return CXChildVisit_Recurse;
}
// CXCursor_CXXMethod case. This is Q_PROPERTY_MAGIC_FUNCTION
parentSpelling = Cursor(parent).type().spelling();
return CXChildVisit_Break;
}
}
return CXChildVisit_Continue;
});
return parentSpelling;
}
static Utf8String getPropertyType(const char *const lineContents, uint propertyPosition)
{
const char *typeStart = std::strstr(lineContents, "Q_PROPERTY") + 10;
typeStart += std::strspn(typeStart, "( \t\n\r");
if (typeStart - lineContents >= propertyPosition)
return Utf8String();
auto typeEnd = std::find_if(std::reverse_iterator<const char*>(lineContents + propertyPosition),
std::reverse_iterator<const char*>(typeStart),
Utils::unequalTo(' '));
return Utf8String(typeStart, static_cast<int>(&(*typeEnd) + 1 - typeStart));
}
void FullTokenInfo::updatePropertyData()
{
CXSourceLocation cxLocation(clang_getTokenLocation(m_cxTranslationUnit, *m_cxToken));
const SourceLocation location(m_cxTranslationUnit, cxLocation);
m_extraInfo.semanticParentTypeSpelling = propertyParentSpelling(m_cxTranslationUnit,
location.filePath(),
line(),
column());
m_extraInfo.declaration = true;
m_extraInfo.definition = true;
#if defined(CINDEX_VERSION_HAS_GETFILECONTENTS_BACKPORTED) || CINDEX_VERSION_MINOR >= 47
// Extract property type from the source code
CXFile cxFile;
uint offset;
clang_getFileLocation(cxLocation, &cxFile, nullptr, nullptr, &offset);
const uint propertyPosition = column() - 1;
const char *const contents = clang_getFileContents(m_cxTranslationUnit, cxFile, nullptr);
const char *const lineContents = &contents[offset - propertyPosition];
m_extraInfo.typeSpelling = getPropertyType(lineContents, propertyPosition);
#endif
}
void FullTokenInfo::identifierKind(const Cursor &cursor, Recursion recursion) void FullTokenInfo::identifierKind(const Cursor &cursor, Recursion recursion)
{ {
updateTypeSpelling(cursor); updateTypeSpelling(cursor);
@@ -79,6 +149,8 @@ void FullTokenInfo::identifierKind(const Cursor &cursor, Recursion recursion)
TokenInfo::identifierKind(cursor, recursion); TokenInfo::identifierKind(cursor, recursion);
m_extraInfo.identifier = (cursor.kind() != CXCursor_PreprocessingDirective); m_extraInfo.identifier = (cursor.kind() != CXCursor_PreprocessingDirective);
if (types().mainHighlightingType == HighlightingType::QtProperty)
updatePropertyData();
} }
void FullTokenInfo::referencedTypeKind(const Cursor &cursor) void FullTokenInfo::referencedTypeKind(const Cursor &cursor)
@@ -138,8 +210,6 @@ void FullTokenInfo::memberReferenceKind(const Cursor &cursor)
void FullTokenInfo::evaluate() void FullTokenInfo::evaluate()
{ {
TokenInfo::evaluate();
m_extraInfo.token = ClangString(clang_getTokenSpelling(m_cxTranslationUnit, *m_cxToken)); m_extraInfo.token = ClangString(clang_getTokenSpelling(m_cxTranslationUnit, *m_cxToken));
auto cxTokenKind = clang_getTokenKind(*m_cxToken); auto cxTokenKind = clang_getTokenKind(*m_cxToken);
@@ -148,6 +218,8 @@ void FullTokenInfo::evaluate()
m_extraInfo.definition = m_originalCursor.isDefinition(); m_extraInfo.definition = m_originalCursor.isDefinition();
} }
m_extraInfo.includeDirectivePath = (m_originalCursor.kind() == CXCursor_InclusionDirective); m_extraInfo.includeDirectivePath = (m_originalCursor.kind() == CXCursor_InclusionDirective);
TokenInfo::evaluate();
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -48,6 +48,7 @@ protected:
void memberReferenceKind(const Cursor &cursor) override; void memberReferenceKind(const Cursor &cursor) override;
private: private:
void updateTypeSpelling(const Cursor &cursor, bool functionLike = false); void updateTypeSpelling(const Cursor &cursor, bool functionLike = false);
void updatePropertyData();
ExtraInfo m_extraInfo; ExtraInfo m_extraInfo;
}; };

View File

@@ -40,6 +40,7 @@ class SourceLocation
friend class SourceRange; friend class SourceRange;
friend class TranslationUnit; friend class TranslationUnit;
friend class Cursor; friend class Cursor;
friend class FullTokenInfo;
friend bool operator==(const SourceLocation &first, const SourceLocation &second); friend bool operator==(const SourceLocation &first, const SourceLocation &second);
public: public:

View File

@@ -69,10 +69,13 @@ bool SourceRange::contains(unsigned line, unsigned column) const
const SourceLocation start_ = start(); const SourceLocation start_ = start();
const SourceLocation end_ = end(); const SourceLocation end_ = end();
return start_.line() <= line if (line < start_.line() || line > end_.line())
&& start_.column() <= column return false;
&& line <= end_.line() if (line == start_.line() && column < start_.column())
&& column <= end_.column(); return false;
if (line == end_.line() && column > end_.column())
return false;
return true;
} }
SourceRangeContainer SourceRange::toSourceRangeContainer() const SourceRangeContainer SourceRange::toSourceRangeContainer() const

View File

@@ -32,7 +32,7 @@
#include <utils/qtcfallthrough.h> #include <utils/qtcfallthrough.h>
#include <QDebug> #include <array>
namespace ClangBackEnd { namespace ClangBackEnd {
@@ -421,6 +421,9 @@ void TokenInfo::identifierKind(const Cursor &cursor, Recursion recursion)
case CXCursor_LabelStmt: case CXCursor_LabelStmt:
m_types.mainHighlightingType = HighlightingType::Label; m_types.mainHighlightingType = HighlightingType::Label;
break; break;
case CXCursor_InvalidFile:
invalidFileKind();
break;
default: default:
break; break;
} }
@@ -466,10 +469,15 @@ HighlightingType TokenInfo::punctuationKind(const Cursor &cursor)
HighlightingType highlightingType = HighlightingType::Invalid; HighlightingType highlightingType = HighlightingType::Invalid;
switch (cursor.kind()) { switch (cursor.kind()) {
case CXCursor_DeclRefExpr: highlightingType = operatorKind(cursor); break; case CXCursor_DeclRefExpr:
highlightingType = operatorKind(cursor);
break;
case CXCursor_Constructor: case CXCursor_Constructor:
case CXCursor_CallExpr: collectOutputArguments(cursor); break; case CXCursor_CallExpr:
default: break; collectOutputArguments(cursor);
break;
default:
break;
} }
if (isOutputArgument()) if (isOutputArgument())
@@ -478,6 +486,75 @@ HighlightingType TokenInfo::punctuationKind(const Cursor &cursor)
return highlightingType; return highlightingType;
} }
enum class PropertyPart
{
None,
Type,
Property,
Keyword,
FunctionOrPrimitiveType
};
static PropertyPart propertyPart(CXTranslationUnit tu, 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);
if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), currentToken)
!= std::end(propertyKeywords)) {
return PropertyPart::Keyword;
}
const ClangString nextToken = clang_getTokenSpelling(tu, *(token + 1));
const ClangString previousToken = clang_getTokenSpelling(tu, *(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 PropertyPart::FunctionOrPrimitiveType;
}
if (std::find(std::begin(propertyKeywords), std::end(propertyKeywords), previousToken)
!= std::end(propertyKeywords)) {
return PropertyPart::FunctionOrPrimitiveType;
}
return PropertyPart::Type;
}
void TokenInfo::invalidFileKind()
{
const PropertyPart propPart = propertyPart(m_cxTranslationUnit, m_cxToken);
switch (propPart) {
case PropertyPart::None:
case PropertyPart::Keyword:
m_types.mainHighlightingType = HighlightingType::Invalid;
return;
case PropertyPart::Property:
m_types.mainHighlightingType = HighlightingType::QtProperty;
return;
case PropertyPart::Type:
m_types.mainHighlightingType = HighlightingType::Type;
return;
case PropertyPart::FunctionOrPrimitiveType:
m_types.mainHighlightingType = HighlightingType::Function;
return;
}
}
static HighlightingType highlightingTypeForKeyword(CXTranslationUnit cxTranslationUnit, static HighlightingType highlightingTypeForKeyword(CXTranslationUnit cxTranslationUnit,
CXToken *cxToken, CXToken *cxToken,
const Cursor &cursor) const Cursor &cursor)

View File

@@ -94,6 +94,7 @@ protected:
virtual void memberReferenceKind(const Cursor &cursor); virtual void memberReferenceKind(const Cursor &cursor);
virtual HighlightingType punctuationKind(const Cursor &cursor); virtual HighlightingType punctuationKind(const Cursor &cursor);
virtual void typeKind(const Cursor &cursor); virtual void typeKind(const Cursor &cursor);
virtual void invalidFileKind();
Cursor m_originalCursor; Cursor m_originalCursor;
CXToken *m_cxToken; CXToken *m_cxToken;

View File

@@ -680,7 +680,7 @@ void ClangCodeModelServer::expectDocumentAnnotationsChangedForFileBWithSpecificH
AccessSpecifier::Invalid, AccessSpecifier::Invalid,
StorageClass::None, StorageClass::None,
true, false, true, true, true, false, true, true,
false, false, false}); false, false});
EXPECT_CALL(mockClangCodeModelClient, EXPECT_CALL(mockClangCodeModelClient,
documentAnnotationsChanged( documentAnnotationsChanged(

View File

@@ -592,3 +592,10 @@ class WithVirtualFunctionDefined {
namespace NFoo { namespace NBar { namespace NTest { class NamespaceTypeSpelling; } } } namespace NFoo { namespace NBar { namespace NTest { class NamespaceTypeSpelling; } } }
Undeclared u; Undeclared u;
#include "../../../../share/qtcreator/cplusplus/wrappedQtHeaders/QtCore/qobjectdefs.h"
class Property {
Q_PROPERTY(const volatile unsigned long long * prop READ getProp WRITE setProp NOTIFY propChanged)
Q_PROPERTY(const QString str READ getStr)
};

View File

@@ -500,6 +500,7 @@ static const char *highlightingTypeToCStringLiteral(HighlightingType type)
RETURN_TEXT_FOR_CASE(Union); RETURN_TEXT_FOR_CASE(Union);
RETURN_TEXT_FOR_CASE(TypeAlias); RETURN_TEXT_FOR_CASE(TypeAlias);
RETURN_TEXT_FOR_CASE(Typedef); RETURN_TEXT_FOR_CASE(Typedef);
RETURN_TEXT_FOR_CASE(QtProperty);
} }
return ""; return "";
@@ -538,8 +539,7 @@ std::ostream &operator<<(std::ostream &os, const ExtraInfo &extraInfo)
<< extraInfo.declaration << ", " << extraInfo.declaration << ", "
<< extraInfo.definition << ", " << extraInfo.definition << ", "
<< extraInfo.signal << ", " << extraInfo.signal << ", "
<< extraInfo.slot << ", " << extraInfo.slot
<< extraInfo.property
<< ")"; << ")";
return os; return os;
} }

View File

@@ -1257,18 +1257,53 @@ TEST_F(TokenProcessor, NamespaceTypeSpelling)
TEST_F(TokenProcessor, DISABLED_WITHOUT_INVALIDDECL_PATCH(TypeNameOfInvalidDeclarationIsInvalid)) TEST_F(TokenProcessor, DISABLED_WITHOUT_INVALIDDECL_PATCH(TypeNameOfInvalidDeclarationIsInvalid))
{ {
const auto infos = translationUnit.tokenInfosInRange(sourceRange(592, 14)); const auto infos = translationUnit.tokenInfosInRange(sourceRange(594, 14));
ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::Invalid)); ASSERT_THAT(infos[0], HasOnlyType(HighlightingType::Invalid));
} }
TEST_F(TokenProcessor, DISABLED_WITHOUT_INVALIDDECL_PATCH(VariableNameOfInvalidDeclarationIsInvalid)) TEST_F(TokenProcessor, DISABLED_WITHOUT_INVALIDDECL_PATCH(VariableNameOfInvalidDeclarationIsInvalid))
{ {
const auto infos = translationUnit.tokenInfosInRange(sourceRange(592, 14)); const auto infos = translationUnit.tokenInfosInRange(sourceRange(594, 14));
ASSERT_THAT(infos[1], HasOnlyType(HighlightingType::Invalid)); ASSERT_THAT(infos[1], HasOnlyType(HighlightingType::Invalid));
} }
TEST_F(TokenProcessor, QtPropertyName)
{
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103));
ASSERT_THAT(infos[8], HasOnlyType(HighlightingType::QtProperty));
}
TEST_F(TokenProcessor, QtPropertyFunction)
{
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103));
ASSERT_THAT(infos[10], HasOnlyType(HighlightingType::Function));
}
TEST_F(TokenProcessor, QtPropertyInternalKeyword)
{
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103));
ASSERT_THAT(infos[9], HasOnlyType(HighlightingType::Invalid));
}
TEST_F(TokenProcessor, QtPropertyLastToken)
{
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(599, 103));
ASSERT_THAT(infos[14], HasOnlyType(HighlightingType::Function));
}
TEST_F(TokenProcessor, QtPropertyType)
{
const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(600, 46));
ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Type));
}
Data *TokenProcessor::d; Data *TokenProcessor::d;
void TokenProcessor::SetUpTestCase() void TokenProcessor::SetUpTestCase()