From 142ae0cdf9310ae924b9f92812fe8b2cb3569237 Mon Sep 17 00:00:00 2001 From: Hugo Holgersson Date: Sat, 5 Nov 2016 15:29:10 +0100 Subject: 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 --- src/libs/clangsupport/clangsupport_global.h | 1 + src/libs/clangsupport/tokeninfocontainer.cpp | 3 + .../clanghighlightingresultreporter.cpp | 2 + src/plugins/clangcodemodel/clangutils.cpp | 3 +- src/plugins/texteditor/texteditorconstants.cpp | 1 + src/plugins/texteditor/texteditorconstants.h | 1 + src/plugins/texteditor/texteditorsettings.cpp | 7 +- src/tools/clangbackend/source/clangstring.h | 39 +++ src/tools/clangbackend/source/fulltokeninfo.cpp | 36 ++- src/tools/clangbackend/source/fulltokeninfo.h | 3 +- src/tools/clangbackend/source/tokeninfo.cpp | 142 +++++++---- src/tools/clangbackend/source/tokeninfo.h | 5 +- tests/unit/unittest/clangstring-test.cpp | 31 ++- tests/unit/unittest/data/highlightingmarks.cpp | 52 ++++ tests/unit/unittest/tokenprocessor-test.cpp | 266 ++++++++++++++++++++- 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::value>::type > @@ -123,6 +129,39 @@ public: return second == first; } + friend bool operator!=(const ClangString &first, const ClangString &second) + { + return !(first == second); + } + + template + friend bool operator!=(const ClangString &first, const char(&second)[Size]) + { + return !(first == second); + } + + template + friend bool operator!=(const char(&first)[Size], const ClangString &second) + { + return second != first; + } + + template::value>::type + > + friend bool operator!=(const ClangString &first, Type second) + { + return !(first == second); + } + + template::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(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]); -- cgit v1.2.3