aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugo Holgersson <hugo.holgersson@logikbyran.se>2016-11-05 15:29:10 +0100
committerHugo Holgersson <hugo.holgersson@logikbyran.se>2018-04-30 15:10:12 +0000
commit142ae0cdf9310ae924b9f92812fe8b2cb3569237 (patch)
tree7ea99ff18f3e742a35835364ee26664fb866bfaa
parentbb6eae5c3bda7692f1ad817cc869218df87e7451 (diff)
Clang: Add semantic C++ operator-token styling
We used to style overloaded operators in the same way as C++'s built-in operators. There was no way to tell if a + token would call a operator+() function or not. Now, if an operator is overloaded (redefined), we give it the "Overloaded Operator"-mixin so users can style it differently. Note: Calls to overloaded 'new' and 'delete' are not highlighted by "Overloaded Operator". This is because clang today always maps these to CXCursor_CXXNewExpr and CXCursor_CXXDeleteExpr with cursor.spelling == "" (empty string). So there is no (?) quick way for us to tell if a new/delete-token was overloaded or not. After follow-ups, follow symbol will work for operator overload usages in current translation unit. Commit is appended by Ivan Donchevskii. Task-number: QTCREATORBUG-19659 Change-Id: I157855d482a61ad2059642a1ee982089fcb7d312 Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
-rw-r--r--src/libs/clangsupport/clangsupport_global.h1
-rw-r--r--src/libs/clangsupport/tokeninfocontainer.cpp3
-rw-r--r--src/plugins/clangcodemodel/clanghighlightingresultreporter.cpp2
-rw-r--r--src/plugins/clangcodemodel/clangutils.cpp3
-rw-r--r--src/plugins/texteditor/texteditorconstants.cpp1
-rw-r--r--src/plugins/texteditor/texteditorconstants.h1
-rw-r--r--src/plugins/texteditor/texteditorsettings.cpp7
-rw-r--r--src/tools/clangbackend/source/clangstring.h39
-rw-r--r--src/tools/clangbackend/source/fulltokeninfo.cpp36
-rw-r--r--src/tools/clangbackend/source/fulltokeninfo.h3
-rw-r--r--src/tools/clangbackend/source/tokeninfo.cpp142
-rw-r--r--src/tools/clangbackend/source/tokeninfo.h5
-rw-r--r--tests/unit/unittest/clangstring-test.cpp31
-rw-r--r--tests/unit/unittest/data/highlightingmarks.cpp52
-rw-r--r--tests/unit/unittest/tokenprocessor-test.cpp266
15 files changed, 518 insertions, 74 deletions
diff --git a/src/libs/clangsupport/clangsupport_global.h b/src/libs/clangsupport/clangsupport_global.h
index 03f4528ee9..8c5b5f57b8 100644
--- a/src/libs/clangsupport/clangsupport_global.h
+++ b/src/libs/clangsupport/clangsupport_global.h
@@ -82,6 +82,7 @@ enum class HighlightingType : quint8
GlobalVariable,
Enumeration,
Operator,
+ OverloadedOperator,
Preprocessor,
PreprocessorDefinition,
PreprocessorExpansion,
diff --git a/src/libs/clangsupport/tokeninfocontainer.cpp b/src/libs/clangsupport/tokeninfocontainer.cpp
index 01485ce66a..157f93746c 100644
--- a/src/libs/clangsupport/tokeninfocontainer.cpp
+++ b/src/libs/clangsupport/tokeninfocontainer.cpp
@@ -46,6 +46,7 @@ static const char *highlightingTypeToCStringLiteral(HighlightingType type)
RETURN_TEXT_FOR_CASE(Field);
RETURN_TEXT_FOR_CASE(Enumeration);
RETURN_TEXT_FOR_CASE(Operator);
+ RETURN_TEXT_FOR_CASE(OverloadedOperator);
RETURN_TEXT_FOR_CASE(Preprocessor);
RETURN_TEXT_FOR_CASE(Label);
RETURN_TEXT_FOR_CASE(FunctionDefinition);
@@ -67,6 +68,8 @@ static const char *highlightingTypeToCStringLiteral(HighlightingType type)
RETURN_TEXT_FOR_CASE(ObjectiveCImplementation);
RETURN_TEXT_FOR_CASE(ObjectiveCProperty);
RETURN_TEXT_FOR_CASE(ObjectiveCMethod);
+ RETURN_TEXT_FOR_CASE(PrimitiveType);
+ RETURN_TEXT_FOR_CASE(Declaration);
default: return "UnhandledHighlightingType";
}
}
diff --git a/src/plugins/clangcodemodel/clanghighlightingresultreporter.cpp b/src/plugins/clangcodemodel/clanghighlightingresultreporter.cpp
index 54a3a39488..ef1b905a8d 100644
--- a/src/plugins/clangcodemodel/clanghighlightingresultreporter.cpp
+++ b/src/plugins/clangcodemodel/clanghighlightingresultreporter.cpp
@@ -70,6 +70,8 @@ TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type)
return TextEditor::C_OUTPUT_ARGUMENT;
case HighlightingType::Operator:
return TextEditor::C_OPERATOR;
+ case HighlightingType::OverloadedOperator:
+ return TextEditor::C_OVERLOADED_OPERATOR;
case HighlightingType::Comment:
return TextEditor::C_COMMENT;
case HighlightingType::StringLiteral:
diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp
index 799bc99e88..0b82c90f00 100644
--- a/src/plugins/clangcodemodel/clangutils.cpp
+++ b/src/plugins/clangcodemodel/clangutils.cpp
@@ -255,7 +255,8 @@ CPlusPlus::Icons::IconType iconTypeForToken(const ClangBackEnd::TokenInfoContain
ClangBackEnd::StorageClass storageClass = extraInfo.storageClass;
if (mainType == ClangBackEnd::HighlightingType::VirtualFunction
- || mainType == ClangBackEnd::HighlightingType::Function) {
+ || mainType == ClangBackEnd::HighlightingType::Function
+ || mainType == ClangBackEnd::HighlightingType::Operator) {
if (storageClass != ClangBackEnd::StorageClass::Static) {
switch (access) {
case ClangBackEnd::AccessSpecifier::Public:
diff --git a/src/plugins/texteditor/texteditorconstants.cpp b/src/plugins/texteditor/texteditorconstants.cpp
index 0715275be5..4e2f0e9d9d 100644
--- a/src/plugins/texteditor/texteditorconstants.cpp
+++ b/src/plugins/texteditor/texteditorconstants.cpp
@@ -63,6 +63,7 @@ const char *nameForStyle(TextStyle style)
case C_KEYWORD: return "Keyword";
case C_PRIMITIVE_TYPE: return "PrimitiveType";
case C_OPERATOR: return "Operator";
+ case C_OVERLOADED_OPERATOR: return "Overloaded Operator";
case C_PREPROCESSOR: return "Preprocessor";
case C_LABEL: return "Label";
case C_COMMENT: return "Comment";
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index 3101912e35..504a6d8ea2 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -59,6 +59,7 @@ enum TextStyle : quint8 {
C_KEYWORD,
C_PRIMITIVE_TYPE,
C_OPERATOR,
+ C_OVERLOADED_OPERATOR,
C_PREPROCESSOR,
C_LABEL,
C_COMMENT,
diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp
index 9ab2bfb98b..64a618f6fb 100644
--- a/src/plugins/texteditor/texteditorsettings.cpp
+++ b/src/plugins/texteditor/texteditorsettings.cpp
@@ -226,7 +226,8 @@ TextEditorSettings::TextEditorSettings()
tr("Reserved keywords of the programming language except "
"keywords denoting primitive types."), Qt::darkYellow);
formatDescr.emplace_back(C_OPERATOR, tr("Operator"),
- tr("Operators (for example operator++ or operator-=)."));
+ tr("Non user-defined language operators.\n"
+ "To style user-defined operators, use Overloaded Operator."));
formatDescr.emplace_back(C_PREPROCESSOR, tr("Preprocessor"),
tr("Preprocessor directives."), Qt::darkBlue);
formatDescr.emplace_back(C_LABEL, tr("Label"), tr("Labels for goto statements."),
@@ -313,6 +314,10 @@ TextEditorSettings::TextEditorSettings()
QColor(255, 190, 0),
QTextCharFormat::DotLine,
FormatDescription::ShowUnderlineControl);
+ formatDescr.emplace_back(C_OVERLOADED_OPERATOR,
+ tr("Overloaded Operators"),
+ tr("Calls and declarations of overloaded (user-defined) operators."),
+ Format::createMixinFormat());
Format declarationFormat = Format::createMixinFormat();
declarationFormat.setBold(true);
formatDescr.emplace_back(C_DECLARATION,
diff --git a/src/tools/clangbackend/source/clangstring.h b/src/tools/clangbackend/source/clangstring.h
index 685055cff0..ad8373c837 100644
--- a/src/tools/clangbackend/source/clangstring.h
+++ b/src/tools/clangbackend/source/clangstring.h
@@ -91,6 +91,11 @@ public:
return !isNull() && std::strlen(cString()) > 0;
}
+ bool startsWith(const char* str) const
+ {
+ return std::strncmp(cString(), str, strlen(str)) == 0;
+ }
+
friend bool operator==(const ClangString &first, const ClangString &second)
{
return std::strcmp(first.cString(), second.cString()) == 0;
@@ -107,6 +112,7 @@ public:
{
return second == first;
}
+
template<typename Type,
typename = typename std::enable_if<std::is_pointer<Type>::value>::type
>
@@ -123,6 +129,39 @@ public:
return second == first;
}
+ friend bool operator!=(const ClangString &first, const ClangString &second)
+ {
+ return !(first == second);
+ }
+
+ template<std::size_t Size>
+ friend bool operator!=(const ClangString &first, const char(&second)[Size])
+ {
+ return !(first == second);
+ }
+
+ template<std::size_t Size>
+ friend bool operator!=(const char(&first)[Size], const ClangString &second)
+ {
+ return second != first;
+ }
+
+ template<typename Type,
+ typename = typename std::enable_if<std::is_pointer<Type>::value>::type
+ >
+ friend bool operator!=(const ClangString &first, Type second)
+ {
+ return !(first == second);
+ }
+
+ template<typename Type,
+ typename = typename std::enable_if<std::is_pointer<Type>::value>::type
+ >
+ friend bool operator!=(Type first, const ClangString &second)
+ {
+ return !(first == second);
+ }
+
friend std::ostream &operator<<(std::ostream &os, const ClangString &string)
{
return os << string.cString();
diff --git a/src/tools/clangbackend/source/fulltokeninfo.cpp b/src/tools/clangbackend/source/fulltokeninfo.cpp
index 36fabc6cd2..d539138ce9 100644
--- a/src/tools/clangbackend/source/fulltokeninfo.cpp
+++ b/src/tools/clangbackend/source/fulltokeninfo.cpp
@@ -216,16 +216,16 @@ void FullTokenInfo::memberReferenceKind(const Cursor &cursor)
}
}
-void FullTokenInfo::keywordKind(const Cursor &cursor)
+void FullTokenInfo::keywordKind()
{
- TokenInfo::keywordKind(cursor);
+ TokenInfo::keywordKind();
- CXCursorKind cursorKind = cursor.kind();
+ CXCursorKind cursorKind = m_originalCursor.kind();
bool anonymous = false;
- if (clang_Cursor_isAnonymous(cursor.cx())) {
+ if (clang_Cursor_isAnonymous(m_originalCursor.cx())) {
anonymous = true;
} else {
- const Utf8String type = fullyQualifiedType(cursor);
+ const Utf8String type = fullyQualifiedType(m_originalCursor);
if (type.endsWith(Utf8StringLiteral(")"))
&& static_cast<const QByteArray &>(type).indexOf("(anonymous") >= 0) {
anonymous = true;
@@ -242,11 +242,33 @@ void FullTokenInfo::keywordKind(const Cursor &cursor)
m_types.mixinHighlightingTypes.push_back(HighlightingType::Namespace);
m_extraInfo.declaration = m_extraInfo.definition = true;
m_extraInfo.token = Utf8StringLiteral("anonymous");
- updateTypeSpelling(cursor);
- m_extraInfo.cursorRange = cursor.sourceRange();
+ updateTypeSpelling(m_originalCursor);
+ m_extraInfo.cursorRange = m_originalCursor.sourceRange();
}
}
+void FullTokenInfo::overloadedOperatorKind()
+{
+ TokenInfo::overloadedOperatorKind();
+
+ if (m_types.mixinHighlightingTypes.front() != HighlightingType::OverloadedOperator)
+ return;
+
+ // Overloaded operator
+ m_extraInfo.identifier = true;
+ if (!m_originalCursor.isDeclaration())
+ return;
+
+ // Overloaded operator declaration
+ m_extraInfo.declaration = true;
+ m_extraInfo.definition = m_originalCursor.isDefinition();
+
+ updateTypeSpelling(m_originalCursor, true);
+ m_extraInfo.cursorRange = m_originalCursor.sourceRange();
+ m_extraInfo.accessSpecifier = m_originalCursor.accessSpecifier();
+ m_extraInfo.storageClass = m_originalCursor.storageClass();
+}
+
void FullTokenInfo::evaluate()
{
m_extraInfo.token = ClangString(clang_getTokenSpelling(m_cxTranslationUnit, *m_cxToken));
diff --git a/src/tools/clangbackend/source/fulltokeninfo.h b/src/tools/clangbackend/source/fulltokeninfo.h
index 1544b47a80..a0ebb81005 100644
--- a/src/tools/clangbackend/source/fulltokeninfo.h
+++ b/src/tools/clangbackend/source/fulltokeninfo.h
@@ -48,7 +48,8 @@ protected:
void variableKind(const Cursor &cursor) override;
void fieldKind(const Cursor &cursor) override;
void memberReferenceKind(const Cursor &cursor) override;
- void keywordKind(const Cursor &cursor) override;
+ void keywordKind() override;
+ void overloadedOperatorKind() override;
private:
void updateTypeSpelling(const Cursor &cursor, bool functionLike = false);
void updatePropertyData();
diff --git a/src/tools/clangbackend/source/tokeninfo.cpp b/src/tools/clangbackend/source/tokeninfo.cpp
index 5da41a30d3..0638a3c6ea 100644
--- a/src/tools/clangbackend/source/tokeninfo.cpp
+++ b/src/tools/clangbackend/source/tokeninfo.cpp
@@ -98,9 +98,7 @@ TokenInfo::operator TokenInfoContainer() const
return TokenInfoContainer(m_line, m_column, m_length, m_types);
}
-namespace {
-
-bool isFinalFunction(const Cursor &cursor)
+static bool isFinalFunction(const Cursor &cursor)
{
auto referencedCursor = cursor.referenced();
if (referencedCursor.hasFinalFunctionAttribute())
@@ -109,7 +107,7 @@ bool isFinalFunction(const Cursor &cursor)
return false;
}
-bool isFunctionInFinalClass(const Cursor &cursor)
+static bool isFunctionInFinalClass(const Cursor &cursor)
{
auto functionBase = cursor.functionBaseDeclaration();
if (functionBase.isValid() && functionBase.hasFinalClassAttribute())
@@ -117,7 +115,6 @@ bool isFunctionInFinalClass(const Cursor &cursor)
return false;
}
-}
void TokenInfo::memberReferenceKind(const Cursor &cursor)
{
@@ -196,12 +193,11 @@ bool TokenInfo::isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) con
&& (m_originalCursor.isDeclaration() || m_originalCursor.isDefinition());
}
-namespace {
-bool isNotFinalFunction(const Cursor &cursor)
+static bool isNotFinalFunction(const Cursor &cursor)
{
return !cursor.hasFinalFunctionAttribute();
}
-}
+
bool TokenInfo::isRealDynamicCall(const Cursor &cursor) const
{
return m_originalCursor.isDynamicCall() && isNotFinalFunction(cursor);
@@ -246,9 +242,7 @@ void TokenInfo::collectOutputArguments(const Cursor &cursor)
filterOutPreviousOutputArguments();
}
-namespace {
-
-uint getEnd(CXSourceRange cxSourceRange)
+static uint getEnd(CXSourceRange cxSourceRange)
{
CXSourceLocation startSourceLocation = clang_getRangeEnd(cxSourceRange);
@@ -258,7 +252,6 @@ uint getEnd(CXSourceRange cxSourceRange)
return endOffset;
}
-}
void TokenInfo::filterOutPreviousOutputArguments()
{
@@ -456,8 +449,7 @@ void TokenInfo::identifierKind(const Cursor &cursor, Recursion recursion)
}
}
-namespace {
-HighlightingType literalKind(const Cursor &cursor)
+static HighlightingType literalKind(const Cursor &cursor)
{
switch (cursor.kind()) {
case CXCursor_CharacterLiteral:
@@ -476,32 +468,86 @@ HighlightingType literalKind(const Cursor &cursor)
Q_UNREACHABLE();
}
-bool hasOperatorName(const char *operatorString)
+static bool isTokenPartOfOperator(const Cursor &declarationCursor, CXToken *token)
{
- return std::strncmp(operatorString, "operator", 8) == 0;
+ Q_ASSERT(declarationCursor.isDeclaration());
+ const CXTranslationUnit cxTranslationUnit = declarationCursor.cxTranslationUnit();
+ const ClangString tokenName = clang_getTokenSpelling(cxTranslationUnit, *token);
+ if (tokenName == "operator")
+ return true;
+
+ if (tokenName == "(") {
+ // Valid operator declarations have at least one token after '(' so
+ // it's safe to proceed to token + 1 without extra checks.
+ const ClangString nextToken = clang_getTokenSpelling(cxTranslationUnit, *(token + 1));
+ if (nextToken != ")") {
+ // Argument lists' parentheses are not operator tokens.
+ // This '('-token opens a (non-empty) argument list.
+ return false;
+ }
+ }
+
+ // It's safe to evaluate the preceding token because we will at least have
+ // the 'operator'-keyword's token to the left.
+ CXToken *prevToken = token - 1;
+ if (clang_getTokenKind(*prevToken) == CXToken_Punctuation) {
+ if (tokenName == "(") {
+ // In an operator declaration, when a '(' follows another punctuation
+ // then this '(' opens an argument list. Ex: operator*()|operator()().
+ return false;
+ }
+
+ // This token is preceded by another punctuation token so this token
+ // could be the second token of a two-tokened operator such as
+ // operator+=|-=|*=|/=|<<|==|<=|++ or the third token of operator
+ // new[]|delete[]. We decrement one more time to hit one of the keywords:
+ // "operator" / "delete" / "new".
+ --prevToken;
+ }
+
+ const ClangString precedingKeyword =
+ clang_getTokenSpelling(cxTranslationUnit, *prevToken);
+
+ return precedingKeyword == "operator" ||
+ precedingKeyword == "new" ||
+ precedingKeyword == "delete";
}
-HighlightingType operatorKind(const Cursor &cursor)
+void TokenInfo::overloadedOperatorKind()
{
- if (hasOperatorName(cursor.spelling().cString()))
- return HighlightingType::Operator;
- else
- return HighlightingType::Invalid;
-}
+ bool inOperatorDeclaration = m_originalCursor.isDeclaration();
+ Cursor declarationCursor =
+ inOperatorDeclaration ? m_originalCursor :
+ m_originalCursor.referenced();
+ if (!declarationCursor.displayName().startsWith("operator"))
+ return;
+
+ if (inOperatorDeclaration && !isTokenPartOfOperator(declarationCursor, m_cxToken))
+ return;
+ if (m_types.mainHighlightingType == HighlightingType::Invalid)
+ m_types.mainHighlightingType = HighlightingType::Operator;
+ m_types.mixinHighlightingTypes.push_back(HighlightingType::OverloadedOperator);
}
-HighlightingType TokenInfo::punctuationKind(const Cursor &cursor)
+void TokenInfo::punctuationOrOperatorKind()
{
- HighlightingType highlightingType = HighlightingType::Invalid;
-
- switch (cursor.kind()) {
+ auto kind = m_originalCursor.kind();
+ switch (kind) {
+ case CXCursor_CallExpr:
+ collectOutputArguments(m_originalCursor);
+ Q_FALLTHROUGH();
+ case CXCursor_FunctionDecl:
+ case CXCursor_CXXMethod:
case CXCursor_DeclRefExpr:
- highlightingType = operatorKind(cursor);
+ // TODO(QTCREATORBUG-19948): Mark calls to overloaded new and delete.
+ // Today we can't because libclang sets these cursors' spelling to "".
+ // case CXCursor_CXXNewExpr:
+ // case CXCursor_CXXDeleteExpr:
+ overloadedOperatorKind();
break;
case CXCursor_Constructor:
- case CXCursor_CallExpr:
- collectOutputArguments(cursor);
+ collectOutputArguments(m_originalCursor);
break;
default:
break;
@@ -509,8 +555,6 @@ HighlightingType TokenInfo::punctuationKind(const Cursor &cursor)
if (isOutputArgument())
m_types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument);
-
- return highlightingType;
}
enum class PropertyPart
@@ -582,17 +626,20 @@ void TokenInfo::invalidFileKind()
}
}
-static HighlightingType highlightingTypeForKeyword(CXTranslationUnit cxTranslationUnit,
- CXToken *cxToken,
- const Cursor &cursor)
+void TokenInfo::keywordKind()
{
- switch (cursor.kind()) {
- case CXCursor_PreprocessingDirective: return HighlightingType::Preprocessor;
- case CXCursor_InclusionDirective: return HighlightingType::StringLiteral;
- default: break;
+ switch (m_originalCursor.kind()) {
+ case CXCursor_PreprocessingDirective:
+ m_types.mainHighlightingType = HighlightingType::Preprocessor;
+ return;
+ case CXCursor_InclusionDirective:
+ m_types.mainHighlightingType = HighlightingType::StringLiteral;
+ return;
+ default:
+ break;
}
- const ClangString spelling = clang_getTokenSpelling(cxTranslationUnit, *cxToken);
+ const ClangString spelling = clang_getTokenSpelling(m_cxTranslationUnit, *m_cxToken);
if (spelling == "bool"
|| spelling == "char"
|| spelling == "char16_t"
@@ -606,17 +653,14 @@ static HighlightingType highlightingTypeForKeyword(CXTranslationUnit cxTranslati
|| spelling == "unsigned"
|| spelling == "void"
|| spelling == "wchar_t") {
- return HighlightingType::PrimitiveType;
+ m_types.mainHighlightingType = HighlightingType::PrimitiveType;
+ return;
}
- return HighlightingType::Keyword;
-}
+ m_types.mainHighlightingType = HighlightingType::Keyword;
-void TokenInfo::keywordKind(const Cursor &cursor)
-{
- m_types.mainHighlightingType = highlightingTypeForKeyword(m_cxTranslationUnit,
- m_cxToken,
- cursor);
+ if (spelling == "new" || spelling == "delete" || spelling == "operator")
+ overloadedOperatorKind();
}
void TokenInfo::evaluate()
@@ -627,10 +671,10 @@ void TokenInfo::evaluate()
switch (cxTokenKind) {
case CXToken_Keyword:
- keywordKind(m_originalCursor);
+ keywordKind();
break;
case CXToken_Punctuation:
- m_types.mainHighlightingType = punctuationKind(m_originalCursor);
+ punctuationOrOperatorKind();
break;
case CXToken_Identifier:
identifierKind(m_originalCursor, Recursion::FirstPass);
diff --git a/src/tools/clangbackend/source/tokeninfo.h b/src/tools/clangbackend/source/tokeninfo.h
index 03aea72fd2..7cd0d48086 100644
--- a/src/tools/clangbackend/source/tokeninfo.h
+++ b/src/tools/clangbackend/source/tokeninfo.h
@@ -93,10 +93,11 @@ protected:
virtual void fieldKind(const Cursor &cursor);
virtual void functionKind(const Cursor &cursor, Recursion recursion);
virtual void memberReferenceKind(const Cursor &cursor);
- virtual HighlightingType punctuationKind(const Cursor &cursor);
virtual void typeKind(const Cursor &cursor);
- virtual void keywordKind(const Cursor &cursor);
+ virtual void keywordKind();
virtual void invalidFileKind();
+ virtual void overloadedOperatorKind();
+ virtual void punctuationOrOperatorKind();
Cursor m_originalCursor;
CXToken *m_cxToken = nullptr;
diff --git a/tests/unit/unittest/clangstring-test.cpp b/tests/unit/unittest/clangstring-test.cpp
index bea70a7ab0..8e30da93e2 100644
--- a/tests/unit/unittest/clangstring-test.cpp
+++ b/tests/unit/unittest/clangstring-test.cpp
@@ -105,9 +105,9 @@ TEST(ClangString, NotEqualBetweenClangStrings)
ClangString text(CXString{"text", 0});
ClangString text2(CXString{"text ", 0});
- bool textIsEqual = text == text2;
+ bool textIsNotEqual = text != text2;
- ASSERT_FALSE(textIsEqual);
+ ASSERT_TRUE(textIsNotEqual);
}
TEST(ClangString, EqualClangStringAndCString)
@@ -123,9 +123,9 @@ TEST(ClangString, NotEqualClangStringAndCString)
{
ClangString text(CXString{"text", 0});
- bool textIsEqual = text == "text ";
+ bool textIsNotEqual = text != "text ";
- ASSERT_FALSE(textIsEqual);
+ ASSERT_TRUE(textIsNotEqual);
}
TEST(ClangString, EqualCStringAndClangString)
@@ -137,6 +137,15 @@ TEST(ClangString, EqualCStringAndClangString)
ASSERT_TRUE(textIsEqual);
}
+TEST(ClangString, NotEqualCStringAndClangString)
+{
+ ClangString text(CXString{"text", 0});
+
+ bool textIsNotEqual = "text " != text;
+
+ ASSERT_TRUE(textIsNotEqual);
+}
+
TEST(ClangString, EqualClangStringPointerAndCString)
{
ClangString text(CXString{"text", 0});
@@ -152,9 +161,9 @@ TEST(ClangString, NotEqualClangStringPointerAndCString)
ClangString text(CXString{"text", 0});
const char *cString = "text ";
- bool textIsEqual = cString == text;
+ bool textIsNotEqual = cString != text;
- ASSERT_FALSE(textIsEqual);
+ ASSERT_TRUE(textIsNotEqual);
}
TEST(ClangString, EqualCStringAndClangStringPointer)
@@ -167,6 +176,16 @@ TEST(ClangString, EqualCStringAndClangStringPointer)
ASSERT_TRUE(textIsEqual);
}
+TEST(ClangString, NotEqualCStringAndClangStringPointer)
+{
+ ClangString text(CXString{"text", 0});
+ const char *cString = "text ";
+
+ bool textIsNotEqual = text != cString;
+
+ ASSERT_TRUE(textIsNotEqual);
+}
+
TEST(ClangString, NullStringHasNoContent)
{
ClangString text(CXString{nullptr, 0});
diff --git a/tests/unit/unittest/data/highlightingmarks.cpp b/tests/unit/unittest/data/highlightingmarks.cpp
index c4e18d65d1..edbe0a14c2 100644
--- a/tests/unit/unittest/data/highlightingmarks.cpp
+++ b/tests/unit/unittest/data/highlightingmarks.cpp
@@ -600,6 +600,44 @@ class Property {
Q_PROPERTY(const QString str READ getStr)
};
+struct X {
+ void operator*(int) {}
+};
+
+void operator*(X, float) {}
+
+void CallSite() {
+ X x;
+ int y = 10;
+ float z = 10;
+ x * y;
+ x * z;
+}
+
+struct Dummy {
+ Dummy operator<<=(int key);
+ Dummy operator()(int a);
+ int& operator[] (unsigned index);
+ void* operator new(unsigned size);
+ void operator delete(void* ptr);
+ void* operator new[](unsigned size);
+ void operator delete[](void* ptr);
+};
+
+void TryOverloadedOperators(Dummy object)
+{
+ object <<= 3;
+
+ Dummy stacked;
+ stacked(4);
+ stacked[1];
+ int *i = new int;
+ Dummy* use_new = new Dummy();
+ delete use_new;
+ Dummy* many = new Dummy[10];
+ delete [] many;
+}
+
enum {
Test = 0
};
@@ -611,3 +649,17 @@ class B {
};
};
}
+
+struct Dummy2 {
+ Dummy2 operator()();
+ int operator*();
+ Dummy2 operator=(int foo);
+};
+
+void TryOverloadedOperators2(Dummy object)
+{
+ Dummy2 dummy2;
+ dummy2();
+ *dummy2;
+ dummy2 = 3;
+}
diff --git a/tests/unit/unittest/tokenprocessor-test.cpp b/tests/unit/unittest/tokenprocessor-test.cpp
index ac202d8fd1..51daef634e 100644
--- a/tests/unit/unittest/tokenprocessor-test.cpp
+++ b/tests/unit/unittest/tokenprocessor-test.cpp
@@ -584,18 +584,270 @@ TEST_F(TokenProcessor, NonFinalVirtualFunctionCallPointer)
ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::VirtualFunction));
}
-TEST_F(TokenProcessor, PlusOperator)
+TEST_F(TokenProcessor, OverriddenPlusOperatorDeclaration)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(220, 67));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, CallToOverriddenPlusOperator)
{
const auto infos = translationUnit.tokenInfosInRange(sourceRange(224, 49));
- ASSERT_THAT(infos[6], HasOnlyType(HighlightingType::Operator));
+ ASSERT_THAT(infos[6], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
}
-TEST_F(TokenProcessor, PlusAssignOperator)
+TEST_F(TokenProcessor, CallToOverriddenPlusAssignOperator)
{
const auto infos = translationUnit.tokenInfosInRange(sourceRange(226, 24));
- ASSERT_THAT(infos[1], HasOnlyType(HighlightingType::Operator));
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, OverriddenStarOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(604, 26));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, OverriddenStarOperatorNonMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, IntegerCallToOverriddenBinaryOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(613, 9));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, FloatCallToOverriddenBinaryOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(614, 9));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, LeftShiftAssignmentOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(618, 32));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledLeftShiftAssignmentOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(629, 18));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::NumberLiteral));
+}
+
+TEST_F(TokenProcessor, FunctionCallOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(619, 29));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledFunctionCallOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(632, 16));
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, AccessOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(620, 38));
+
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[4], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[5], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledAccessOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(633, 16));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator));
+}
+
+TEST_F(TokenProcessor, NewOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(621, 39));
+
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator));
+ ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledNewOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(635, 34));
+
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // = is not marked.
+ // CLANG-UPGRADE-CHECK: Check if 'new' keyword usage cursor correctly returns referenced() cursor
+ // and uncomment this test in that case.
+ // ASSERT_THAT(infos[4], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // new
+}
+
+TEST_F(TokenProcessor, DeleteOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(622, 37));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // delete
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledDeleteOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(636, 20));
+
+ // CLANG-UPGRADE-CHECK: Check if 'delete' keyword usage cursor correctly returns referenced() cursor
+ // and uncomment this test in that case.
+ // ASSERT_THAT(infos[0], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // delete
+ ASSERT_THAT(infos[1], HasOnlyType(HighlightingType::LocalVariable));
+ ASSERT_THAT(infos[2], HasOnlyType(HighlightingType::Invalid)); // ; is a punctuation.
+}
+
+TEST_F(TokenProcessor, NewArrayOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(623, 41));
+
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // new
+ ASSERT_THAT(infos[4], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // [
+ ASSERT_THAT(infos[5], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // ]
+ ASSERT_THAT(infos[6], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledNewArrayOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(637, 34));
+
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // = is not marked.
+ // CLANG-UPGRADE-CHECK: Check if 'new' keyword usage cursor correctly returns referenced() cursor
+ // and uncomment this test in that case.
+ // ASSERT_THAT(infos[4], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // new
+}
+
+TEST_F(TokenProcessor, DeleteArrayOperatorMemberDefinition)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(624, 39));
+
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // delete
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // [
+ ASSERT_THAT(infos[4], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // ]
+ ASSERT_THAT(infos[5], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation.
+}
+
+TEST_F(TokenProcessor, CalledDeleteArrayOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(638, 20));
+
+ // CLANG-UPGRADE-CHECK: Check if 'delete' keyword usage cursor correctly returns referenced() cursor
+ // and uncomment this test in that case.
+ // ASSERT_THAT(infos[0], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // delete
+}
+
+TEST_F(TokenProcessor, CalledNotOverloadedOperator)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(634, 22));
+
+ ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Keyword)); // new
+}
+
+TEST_F(TokenProcessor, ParenthesisOperatorWithoutArguments)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(654, 25));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // operator
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '('
+ ASSERT_THAT(infos[3], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // ')'
+ ASSERT_THAT(infos[4], HasOnlyType(HighlightingType::Invalid)); // second '(' is a punctuation
+}
+
+TEST_F(TokenProcessor, CalledParenthesisOperatorWithoutArguments)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(662, 14));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '('
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // ')'
+}
+
+TEST_F(TokenProcessor, OperatorWithOnePunctuationTokenWithoutArguments)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(655, 25));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // operator
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '*'
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation
+}
+
+TEST_F(TokenProcessor, CalledOperatorWithOnePunctuationTokenWithoutArguments)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(663, 13));
+
+ ASSERT_THAT(infos[0], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '*'
+}
+
+TEST_F(TokenProcessor, EqualsOperatorOverload)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(656, 43));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Keyword, HighlightingType::OverloadedOperator)); // operator
+ ASSERT_THAT(infos[2], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '='
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid)); // ( is a punctuation
+}
+
+TEST_F(TokenProcessor, CalledEqualsOperatorOverload)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(664, 23));
+
+ ASSERT_THAT(infos[1], HasTwoTypes(HighlightingType::Operator, HighlightingType::OverloadedOperator)); // '='
+}
+
+TEST_F(TokenProcessor, LeftParenthesisIsAPunctuation)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[3], HasOnlyType(HighlightingType::Invalid));
+}
+
+TEST_F(TokenProcessor, SeparatingCommaIsAPunctuation)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[5], HasOnlyType(HighlightingType::Invalid));
+}
+
+TEST_F(TokenProcessor, RightParenthesisIsAPunctuation)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[7], HasOnlyType(HighlightingType::Invalid));
+}
+
+TEST_F(TokenProcessor, CurlyLeftParenthesisIsAPunctuation)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[8], HasOnlyType(HighlightingType::Invalid));
+}
+
+TEST_F(TokenProcessor, CurlyRightParenthesisIsAPunctuation)
+{
+ const auto infos = translationUnit.tokenInfosInRange(sourceRange(607, 29));
+
+ ASSERT_THAT(infos[9], HasOnlyType(HighlightingType::Invalid));
}
TEST_F(TokenProcessor, Comment)
@@ -1326,7 +1578,7 @@ TEST_F(TokenProcessor, CursorRange)
TEST_F(TokenProcessor, AnonymousEnum)
{
- const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(603, 7));
+ const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(641, 7));
ClangBackEnd::TokenInfoContainer container(infos[0]);
@@ -1336,7 +1588,7 @@ TEST_F(TokenProcessor, AnonymousEnum)
TEST_F(TokenProcessor, AnonymousNamespace)
{
- const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(607, 12));
+ const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(645, 12));
ClangBackEnd::TokenInfoContainer container(infos[0]);
@@ -1346,7 +1598,7 @@ TEST_F(TokenProcessor, AnonymousNamespace)
TEST_F(TokenProcessor, AnonymousStruct)
{
- const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(609, 13));
+ const auto infos = translationUnit.fullTokenInfosInRange(sourceRange(647, 13));
ClangBackEnd::TokenInfoContainer container(infos[0]);