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>
This commit is contained in:
Hugo Holgersson
2016-11-05 15:29:10 +01:00
parent bb6eae5c3b
commit 142ae0cdf9
15 changed files with 519 additions and 75 deletions

View File

@@ -82,6 +82,7 @@ enum class HighlightingType : quint8
GlobalVariable,
Enumeration,
Operator,
OverloadedOperator,
Preprocessor,
PreprocessorDefinition,
PreprocessorExpansion,

View File

@@ -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";
}
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -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";

View File

@@ -59,6 +59,7 @@ enum TextStyle : quint8 {
C_KEYWORD,
C_PRIMITIVE_TYPE,
C_OPERATOR,
C_OVERLOADED_OPERATOR,
C_PREPROCESSOR,
C_LABEL,
C_COMMENT,

View File

@@ -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,

View File

@@ -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();

View File

@@ -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));

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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});

View File

@@ -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;
}

View File

@@ -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]);