[clang] libclang: Query operator kind on declaration and expression cursors (PR #216553)
Martin Bednár via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 13 04:37:09 PDT 2026
https://github.com/Serafean updated https://github.com/llvm/llvm-project/pull/216553
>From 3360ccc8b37ffd068b554bb917aff211bed86d20 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Bedn=C3=A1r?= <martin at serafean.cz>
Date: Sun, 16 Aug 2026 00:00:07 +0200
Subject: [PATCH 1/5] Report operator kind for Declarations and Exprs
---
clang/docs/ReleaseNotes.md | 1 +
clang/tools/libclang/CIndex.cpp | 54 +++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c348ddaf23017f..b312bde887615c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -476,6 +476,7 @@ features cannot lower the translation-unit ABI level;
### libclang
- visit identifier initializers in lambda capture as VarDecl instead of VariableRef. Warning: this changes behaviour.
+- Allow querying Unary and Binary operator types on declaration cursors.
### Code Completion
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 7c9da867909f55..83b9842fb0db2c 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -10224,6 +10224,16 @@ CXString clang_getBinaryOperatorKindSpelling(enum CXBinaryOperatorKind kind) {
}
enum CXBinaryOperatorKind clang_getCursorBinaryOperatorKind(CXCursor cursor) {
+
+ auto retOp = [](OverloadedOperatorKind Kind, int numArgs) {
+ if (!(Kind == OO_None || Kind == OO_PlusPlus || Kind == OO_MinusMinus ||
+ numArgs != 2)) {
+ auto opcode = BinaryOperator::getOverloadedOpcode(Kind);
+ return static_cast<CXBinaryOperatorKind>(opcode + 1);
+ }
+ return CXBinaryOperator_Invalid;
+ };
+
if (clang_isExpression(cursor.kind)) {
const Expr *expr = getCursorExpr(cursor);
@@ -10232,6 +10242,22 @@ enum CXBinaryOperatorKind clang_getCursorBinaryOperatorKind(CXCursor cursor) {
if (const auto *op = dyn_cast<CXXRewrittenBinaryOperator>(expr))
return static_cast<CXBinaryOperatorKind>(op->getOpcode() + 1);
+
+ if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(expr)) {
+ return retOp(OCE->getOperator(), OCE->getNumArgs());
+ }
+ }
+
+ if (clang_isDeclaration(cursor.kind)) {
+ const auto *decl = getCursorDecl(cursor);
+
+ const auto *funcDecl = dyn_cast<FunctionDecl>(decl);
+ if (!funcDecl)
+ return CXBinaryOperator_Invalid;
+
+ return retOp(funcDecl->getOverloadedOperator(),
+ (funcDecl->isCXXClassMember()) ? funcDecl->getNumParams() + 1
+ : funcDecl->getNumParams());
}
return CXBinaryOperator_Invalid;
@@ -10246,11 +10272,39 @@ CXString clang_getUnaryOperatorKindSpelling(enum CXUnaryOperatorKind kind) {
}
enum CXUnaryOperatorKind clang_getCursorUnaryOperatorKind(CXCursor cursor) {
+
+ auto retOp = [](OverloadedOperatorKind op, int argNum) {
+ const bool postfix =
+ argNum == 2 && (op == OO_PlusPlus || op == OO_MinusMinus);
+
+ if (op == OO_None || (!postfix && argNum != 1))
+ return CXUnaryOperator_Invalid;
+
+ const auto uop = UnaryOperator::getOverloadedOpcode(op, postfix);
+ return static_cast<CXUnaryOperatorKind>(uop + 1);
+ };
+
if (clang_isExpression(cursor.kind)) {
const Expr *expr = getCursorExpr(cursor);
if (const auto *op = dyn_cast<UnaryOperator>(expr))
return static_cast<CXUnaryOperatorKind>(op->getOpcode() + 1);
+
+ if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(expr)) {
+ return retOp(OCE->getOperator(), OCE->getNumArgs());
+ }
+ }
+
+ if (clang_isDeclaration(cursor.kind)) {
+ const auto *decl = getCursorDecl(cursor);
+
+ const auto *funcDecl = dyn_cast<FunctionDecl>(decl);
+ if (!funcDecl)
+ return CXUnaryOperator_Invalid;
+
+ return retOp(funcDecl->getOverloadedOperator(),
+ (funcDecl->isCXXClassMember()) ? funcDecl->getNumParams() + 1
+ : funcDecl->getNumParams());
}
return CXUnaryOperator_Invalid;
>From 6300718233206b5d4798a079d12545d71745fedd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Bedn=C3=A1r?= <martin at serafean.cz>
Date: Sun, 16 Aug 2026 00:01:02 +0200
Subject: [PATCH 2/5] tests
---
clang/test/Index/binop.cpp | 437 +++++++++++++++++++++++-
clang/test/Index/unop.cpp | 175 ++++++++++
clang/tools/c-index-test/c-index-test.c | 22 +-
3 files changed, 632 insertions(+), 2 deletions(-)
create mode 100644 clang/test/Index/unop.cpp
diff --git a/clang/test/Index/binop.cpp b/clang/test/Index/binop.cpp
index 576fd73cc2abfe..743a0279a896ff 100644
--- a/clang/test/Index/binop.cpp
+++ b/clang/test/Index/binop.cpp
@@ -1,4 +1,4 @@
-// RUN: c-index-test -test-print-binops %s | FileCheck %s
+// RUN: c-index-test -test-print-binops -std=c++20 %s | FileCheck %s
struct C {
int m;
@@ -90,3 +90,438 @@ void func(void) {
// CHECK: CompoundAssignOperator=^= BinOp=^= 31
// CHECK: CompoundAssignOperator=|= BinOp=|= 32
// CHECK: BinaryOperator=, BinOp=, 33
+
+struct D {
+ D() = default;
+ D& operator+(){return *this;}
+ D& operator-(){return *this;}
+ int& operator->*(int D::*i){return this->i;}
+ D& operator*(const D&){return *this;}
+ D& operator/(const D&){return *this;}
+ D& operator%(const D&){return *this;}
+ D& operator+(const D&){return *this;}
+ D& operator-(const D&){return *this;}
+ D& operator<<(const D&){return *this;}
+ D& operator>>(const D&){return *this;}
+ bool operator<(const D&){return true;}
+ bool operator>(const D&){return true;}
+ bool operator<=(const D&){return true;}
+ bool operator>=(const D&){return true;}
+ bool operator==(const D&){return true;}
+ bool operator!=(const D&){return true;}
+ D& operator&(const D&){return *this;}
+ D& operator^(const D&){return *this;}
+ D& operator|(const D&){return *this;}
+ bool operator&&(const D&){return true;}
+ bool operator||(const D&){return true;}
+ D& operator=(const D&);
+ D& operator*=(const D&){return *this;}
+ D& operator/=(const D&){return *this;}
+ D& operator%=(const D&){return *this;}
+ D& operator+=(const D&){return *this;}
+ D& operator-=(const D&){return *this;}
+ D& operator<<=(const D&){return *this;}
+ D& operator>>=(const D&){return *this;}
+ D& operator&=(const D&){return *this;}
+ D& operator^=(const D&){return *this;}
+ D& operator|=(const D&){return *this;}
+ D& operator,(const D&){return *this;}
+
+ // Negative test of --/++
+ D& operator++(int){return *this;};
+ D& operator++(){return *this;};
+ D& operator--(int){return *this;};
+ D& operator--(){return *this;};
+ void foo();
+ int i;
+};
+
+// CHECK: CXXMethod=operator+:96:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=operator-:97:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=operator->*:98:8 (Definition) BinOp=->* 2
+// CHECK: CXXMethod=operator*:99:6 (Definition) BinOp=* 3
+// CHECK: CXXMethod=operator/:100:6 (Definition) BinOp=/ 4
+// CHECK: CXXMethod=operator%:101:6 (Definition) BinOp=% 5
+// CHECK: CXXMethod=operator+:102:6 (Definition) BinOp=+ 6
+// CHECK: CXXMethod=operator-:103:6 (Definition) BinOp=- 7
+// CHECK: CXXMethod=operator<<:104:6 (Definition) BinOp=<< 8
+// CHECK: CXXMethod=operator>>:105:6 (Definition) BinOp=>> 9
+// CHECK: CXXMethod=operator<:106:8 (Definition) BinOp=< 11
+// CHECK: CXXMethod=operator>:107:8 (Definition) BinOp=> 12
+// CHECK: CXXMethod=operator<=:108:8 (Definition) BinOp=<= 13
+// CHECK: CXXMethod=operator>=:109:8 (Definition) BinOp=>= 14
+// CHECK: CXXMethod=operator==:110:8 (Definition) BinOp=== 15
+// CHECK: CXXMethod=operator!=:111:8 (Definition) BinOp=!= 16
+// CHECK: CXXMethod=operator&:112:6 (Definition) BinOp=& 17
+// CHECK: CXXMethod=operator^:113:6 (Definition) BinOp=^ 18
+// CHECK: CXXMethod=operator|:114:6 (Definition) BinOp=| 19
+// CHECK: CXXMethod=operator&&:115:8 (Definition) BinOp=&& 20
+// CHECK: CXXMethod=operator||:116:8 (Definition) BinOp=|| 21
+// CHECK: CXXMethod=operator=:117:6 (copy-assignment operator) BinOp== 22
+// CHECK: CXXMethod=operator*=:118:6 (Definition) BinOp=*= 23
+// CHECK: CXXMethod=operator/=:119:6 (Definition) BinOp=/= 24
+// CHECK: CXXMethod=operator%=:120:6 (Definition) BinOp=%= 25
+// CHECK: CXXMethod=operator+=:121:6 (Definition) BinOp=+= 26
+// CHECK: CXXMethod=operator-=:122:6 (Definition) BinOp=-= 27
+// CHECK: CXXMethod=operator<<=:123:6 (Definition) BinOp=<<= 28
+// CHECK: CXXMethod=operator>>=:124:6 (Definition) BinOp=>>= 29
+// CHECK: CXXMethod=operator&=:125:6 (Definition) BinOp=&= 30
+// CHECK: CXXMethod=operator^=:126:6 (Definition) BinOp=^= 31
+// CHECK: CXXMethod=operator|=:127:6 (Definition) BinOp=|= 32
+// CHECK: CXXMethod=operator,:128:6 (Definition) BinOp=, 33
+// CHECK: CXXMethod=operator++:131:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=operator++:132:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=operator--:133:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=operator--:134:6 (Definition) BinOp= 0
+// CHECK: CXXMethod=foo:135:8 BinOp= 0
+
+
+void func2(void) {
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ D a, b;
+ int D::*p = &D::i;
+
+ D *pc;
+ a->*p;
+
+ a *b;
+ a / b;
+ a % b;
+ a + b;
+ a - b;
+
+ a << b;
+ a >> b;
+
+ a < b;
+ a > b;
+
+ a <= b;
+ a >= b;
+ a == b;
+ a != b;
+
+ a &b;
+ a ^ b;
+ a | b;
+
+ a &&b;
+ a || b;
+
+ a = b;
+
+ a *= b;
+ a /= b;
+ a %= b;
+ a += b;
+ a -= b;
+
+ a <<= b;
+ a >>= b;
+
+ a &= b;
+ a ^= b;
+ a |= b;
+ a, b;
+
+ // Negative test
+ a++;
+ ++a;
+ a--;
+ --a;
+
+ +a;
+ -a;
+ #pragma clang diagnostic pop
+}
+
+// CHECK: FunctionDecl=func2:179:6 (Definition) BinOp= 0
+// CHECK: CallExpr=D:95:3 BinOp= 0
+// CHECK: CallExpr=D:95:3 BinOp= 0
+// CHECK: CallExpr=operator->*:98:8 BinOp=->* 2
+// CHECK: CallExpr=operator*:99:6 BinOp=* 3
+// CHECK: CallExpr=operator/:100:6 BinOp=/ 4
+// CHECK: CallExpr=operator%:101:6 BinOp=% 5
+// CHECK: CallExpr=operator+:102:6 BinOp=+ 6
+// CHECK: CallExpr=operator-:103:6 BinOp=- 7
+// CHECK: CallExpr=operator<<:104:6 BinOp=<< 8
+// CHECK: CallExpr=operator>>:105:6 BinOp=>> 9
+// CHECK: CallExpr=operator<:106:8 BinOp=< 11
+// CHECK: CallExpr=operator>:107:8 BinOp=> 12
+// CHECK: CallExpr=operator<=:108:8 BinOp=<= 13
+// CHECK: CallExpr=operator>=:109:8 BinOp=>= 14
+// CHECK: CallExpr=operator==:110:8 BinOp=== 15
+// CHECK: CallExpr=operator!=:111:8 BinOp=!= 16
+// CHECK: CallExpr=operator&:112:6 BinOp=& 17
+// CHECK: CallExpr=operator^:113:6 BinOp=^ 18
+// CHECK: CallExpr=operator|:114:6 BinOp=| 19
+// CHECK: CallExpr=operator&&:115:8 BinOp=&& 20
+// CHECK: CallExpr=operator||:116:8 BinOp=|| 21
+// CHECK: CallExpr=operator=:117:6 BinOp== 22
+// CHECK: CallExpr=operator*=:118:6 BinOp=*= 23
+// CHECK: CallExpr=operator/=:119:6 BinOp=/= 24
+// CHECK: CallExpr=operator%=:120:6 BinOp=%= 25
+// CHECK: CallExpr=operator+=:121:6 BinOp=+= 26
+// CHECK: CallExpr=operator-=:122:6 BinOp=-= 27
+// CHECK: CallExpr=operator<<=:123:6 BinOp=<<= 28
+// CHECK: CallExpr=operator>>=:124:6 BinOp=>>= 29
+// CHECK: CallExpr=operator&=:125:6 BinOp=&= 30
+// CHECK: CallExpr=operator^=:126:6 BinOp=^= 31
+// CHECK: CallExpr=operator|=:127:6 BinOp=|= 32
+// CHECK: CallExpr=operator,:128:6 BinOp=, 33
+// CHECK: CallExpr=operator++:131:6 BinOp= 0
+// CHECK: CallExpr=operator++:132:6 BinOp= 0
+// CHECK: CallExpr=operator--:133:6 BinOp= 0
+// CHECK: CallExpr=operator--:134:6 BinOp= 0
+// CHECK: CallExpr=operator+:96:6 BinOp= 0
+// CHECK: CallExpr=operator-:97:6 BinOp= 0
+
+
+struct E{
+ int i;
+};
+
+int& operator->*(const E&, int E::*i);
+E operator*(const E&, const E&);
+E operator/(const E&, const E&);
+E operator%(const E&, const E&);
+E operator+(const E&, const E&);
+E operator-(const E&, const E&);
+E operator<<(const E&, const E&);
+E operator>>(const E&, const E&);
+bool operator<(const E&, const E&);
+bool operator>(const E&, const E&);
+bool operator<=(const E&, const E&);
+bool operator>=(const E&, const E&);
+bool operator==(const E&, const E&);
+bool operator!=(const E&, const E&);
+E operator&(const E&, const E&);
+E operator^(const E&, const E&);
+E operator|(const E&, const E&);
+bool operator&&(const E&, const E&);
+bool operator||(const E&, const E&);
+E operator*=(const E&, const E&);
+E operator/=(const E&, const E&);
+E operator%=(const E&, const E&);
+E operator+=(const E&, const E&);
+E operator-=(const E&, const E&);
+E operator<<=(const E&, const E&);
+E operator>>=(const E&, const E&);
+E operator&=(const E&, const E&);
+E operator^=(const E&, const E&);
+E operator|=(const E&, const E&);
+E operator,(const E&, const E&);
+E operator++(const E&a, int);
+E operator++(const E&a );
+E operator--(const E&a, int);
+E operator--(const E&a);
+void foo(const E&, const E&);
+E operator+(const E&);
+E operator-(const E&);
+
+// CHECK: FunctionDecl=operator->*:285:6 BinOp=->* 2
+// CHECK: FunctionDecl=operator*:286:3 BinOp=* 3
+// CHECK: FunctionDecl=operator/:287:3 BinOp=/ 4
+// CHECK: FunctionDecl=operator%:288:3 BinOp=% 5
+// CHECK: FunctionDecl=operator+:289:3 BinOp=+ 6
+// CHECK: FunctionDecl=operator-:290:3 BinOp=- 7
+// CHECK: FunctionDecl=operator<<:291:3 BinOp=<< 8
+// CHECK: FunctionDecl=operator>>:292:3 BinOp=>> 9
+// CHECK: FunctionDecl=operator<:293:6 BinOp=< 11
+// CHECK: FunctionDecl=operator>:294:6 BinOp=> 12
+// CHECK: FunctionDecl=operator<=:295:6 BinOp=<= 13
+// CHECK: FunctionDecl=operator>=:296:6 BinOp=>= 14
+// CHECK: FunctionDecl=operator==:297:6 BinOp=== 15
+// CHECK: FunctionDecl=operator!=:298:6 BinOp=!= 16
+// CHECK: FunctionDecl=operator&:299:3 BinOp=& 17
+// CHECK: FunctionDecl=operator^:300:3 BinOp=^ 18
+// CHECK: FunctionDecl=operator|:301:3 BinOp=| 19
+// CHECK: FunctionDecl=operator&&:302:6 BinOp=&& 20
+// CHECK: FunctionDecl=operator||:303:6 BinOp=|| 21
+// CHECK: FunctionDecl=operator*=:304:3 BinOp=*= 23
+// CHECK: FunctionDecl=operator/=:305:3 BinOp=/= 24
+// CHECK: FunctionDecl=operator%=:306:3 BinOp=%= 25
+// CHECK: FunctionDecl=operator+=:307:3 BinOp=+= 26
+// CHECK: FunctionDecl=operator-=:308:3 BinOp=-= 27
+// CHECK: FunctionDecl=operator<<=:309:3 BinOp=<<= 28
+// CHECK: FunctionDecl=operator>>=:310:3 BinOp=>>= 29
+// CHECK: FunctionDecl=operator&=:311:3 BinOp=&= 30
+// CHECK: FunctionDecl=operator^=:312:3 BinOp=^= 31
+// CHECK: FunctionDecl=operator|=:313:3 BinOp=|= 32
+// CHECK: FunctionDecl=operator,:314:3 BinOp=, 33
+// CHECK: FunctionDecl=operator++:315:3 BinOp= 0
+// CHECK: FunctionDecl=operator++:316:3 BinOp= 0
+// CHECK: FunctionDecl=operator--:317:3 BinOp= 0
+// CHECK: FunctionDecl=operator--:318:3 BinOp= 0
+// CHECK: FunctionDecl=foo:319:6 BinOp= 0
+// CHECK: FunctionDecl=operator+:320:3 BinOp= 0
+// CHECK: FunctionDecl=operator-:321:3 BinOp= 0
+
+void func3(void) {
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ E a, b;
+ int E::*p = &E::i;
+
+ E *pc;
+ a->*p;
+
+ a *b;
+ a / b;
+ a % b;
+ a + b;
+ a - b;
+
+ a << b;
+ a >> b;
+
+ a < b;
+ a > b;
+
+ a <= b;
+ a >= b;
+ a == b;
+ a != b;
+
+ a &b;
+ a ^ b;
+ a | b;
+
+ a &&b;
+ a || b;
+
+ a = b;
+
+ a *= b;
+ a /= b;
+ a %= b;
+ a += b;
+ a -= b;
+
+ a <<= b;
+ a >>= b;
+
+ a &= b;
+ a ^= b;
+ a |= b;
+ a, b;
+
+ // Negative test
+ a++;
+ ++a;
+ a--;
+ --a;
+
+ +a;
+ -a;
+ #pragma clang diagnostic pop
+}
+
+// CHECK: FunctionDecl=func3:361:6 (Definition) BinOp= 0
+// CHECK: CallExpr=E:281:8 BinOp= 0
+// CHECK: CallExpr=E:281:8 BinOp= 0
+// CHECK: CallExpr=operator->*:285:6 BinOp=->* 2
+// CHECK: CallExpr=operator*:286:3 BinOp=* 3
+// CHECK: CallExpr=operator/:287:3 BinOp=/ 4
+// CHECK: CallExpr=operator%:288:3 BinOp=% 5
+// CHECK: CallExpr=operator+:289:3 BinOp=+ 6
+// CHECK: CallExpr=operator-:290:3 BinOp=- 7
+// CHECK: CallExpr=operator<<:291:3 BinOp=<< 8
+// CHECK: CallExpr=operator>>:292:3 BinOp=>> 9
+// CHECK: CallExpr=operator<:293:6 BinOp=< 11
+// CHECK: CallExpr=operator>:294:6 BinOp=> 12
+// CHECK: CallExpr=operator<=:295:6 BinOp=<= 13
+// CHECK: CallExpr=operator>=:296:6 BinOp=>= 14
+// CHECK: CallExpr=operator==:297:6 BinOp=== 15
+// CHECK: CallExpr=operator!=:298:6 BinOp=!= 16
+// CHECK: CallExpr=operator&:299:3 BinOp=& 17
+// CHECK: CallExpr=operator^:300:3 BinOp=^ 18
+// CHECK: CallExpr=operator|:301:3 BinOp=| 19
+// CHECK: CallExpr=operator&&:302:6 BinOp=&& 20
+// CHECK: CallExpr=operator||:303:6 BinOp=|| 21
+// CHECK: CallExpr=operator=:281:8 BinOp== 22
+// CHECK: CallExpr=operator*=:304:3 BinOp=*= 23
+// CHECK: CallExpr=operator/=:305:3 BinOp=/= 24
+// CHECK: CallExpr=operator%=:306:3 BinOp=%= 25
+// CHECK: CallExpr=operator+=:307:3 BinOp=+= 26
+// CHECK: CallExpr=operator-=:308:3 BinOp=-= 27
+// CHECK: CallExpr=operator<<=:309:3 BinOp=<<= 28
+// CHECK: CallExpr=operator>>=:310:3 BinOp=>>= 29
+// CHECK: CallExpr=operator&=:311:3 BinOp=&= 30
+// CHECK: CallExpr=operator^=:312:3 BinOp=^= 31
+// CHECK: CallExpr=operator|=:313:3 BinOp=|= 32
+// CHECK: CallExpr=operator,:314:3 BinOp=, 33
+// CHECK: CallExpr=operator++:315:3 BinOp= 0
+// CHECK: CallExpr=operator++:316:3 BinOp= 0
+// CHECK: CallExpr=operator--:317:3 BinOp= 0
+// CHECK: CallExpr=operator--:318:3 BinOp= 0
+// CHECK: CallExpr=operator+:320:3 BinOp= 0
+// CHECK: CallExpr=operator-:321:3 BinOp= 0
+
+
+struct space1{
+ int operator<=>(const space1&) const;
+ bool operator==(const space1&) const;
+};
+
+void func4(){
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ space1 s1, s2;
+ s1 <=> s2;
+ s1 < s2;
+ s1 > s2;
+ s1 <= s2;
+ s1 >= s2;
+ s1 == s2;
+ s1 != s2;
+#pragma clang diagnostic pop
+}
+
+// CHECK: FunctionDecl=func4:468:6 (Definition) BinOp= 0
+// CHECK: CallExpr=space1:463:8 BinOp= 0
+// CHECK: CallExpr=space1:463:8 BinOp= 0
+// CHECK: CallExpr=operator<=>:464:7 BinOp=<=> 10
+// CHECK: BinaryOperator=< BinOp=< 11
+// CHECK: CallExpr=operator<=>:464:7 BinOp=<=> 10
+// CHECK: BinaryOperator=> BinOp=> 12
+// CHECK: CallExpr=operator<=>:464:7 BinOp=<=> 10
+// CHECK: BinaryOperator=<= BinOp=<= 13
+// CHECK: CallExpr=operator<=>:464:7 BinOp=<=> 10
+// CHECK: BinaryOperator=>= BinOp=>= 14
+// CHECK: CallExpr=operator<=>:464:7 BinOp=<=> 10
+// CHECK: CallExpr=operator==:465:8 BinOp=== 15
+// CHECK: CallExpr=operator==:465:8 BinOp=== 15
+
+struct space2{};
+int operator<=>(const space2&, const space2&);
+bool operator ==(const space2 &, const space2&);
+void func5(){
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ space2 s1, s2;
+ s1 <=> s2;
+ s1 < s2;
+ s1 > s2;
+ s1 <= s2;
+ s1 >= s2;
+ s1 == s2;
+ s1 != s2;
+#pragma clang diagnostic pop
+}
+
+// CHECK: FunctionDecl=func5:500:6 (Definition) BinOp= 0
+// CHECK: CallExpr=space2:497:8 BinOp= 0
+// CHECK: CallExpr=space2:497:8 BinOp= 0
+// CHECK: CallExpr=operator<=>:498:5 BinOp=<=> 10
+// CHECK: BinaryOperator=< BinOp=< 11
+// CHECK: CallExpr=operator<=>:498:5 BinOp=<=> 10
+// CHECK: BinaryOperator=> BinOp=> 12
+// CHECK: CallExpr=operator<=>:498:5 BinOp=<=> 10
+// CHECK: BinaryOperator=<= BinOp=<= 13
+// CHECK: CallExpr=operator<=>:498:5 BinOp=<=> 10
+// CHECK: BinaryOperator=>= BinOp=>= 14
+// CHECK: CallExpr=operator<=>:498:5 BinOp=<=> 10
+// CHECK: CallExpr=operator==:499:6 BinOp=== 15
+// CHECK: CallExpr=operator==:499:6 BinOp=== 15
diff --git a/clang/test/Index/unop.cpp b/clang/test/Index/unop.cpp
new file mode 100644
index 00000000000000..56e5fd46a707eb
--- /dev/null
+++ b/clang/test/Index/unop.cpp
@@ -0,0 +1,175 @@
+// RUN: c-index-test -test-print-unops -std=c++20 %s | FileCheck %s
+void func(){
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ int i;
+ i++;
+ ++i;
+ i--;
+ --i;
+ int *p = &i;
+ *p;
+ int c= +i;
+ int d= -i;
+ ~i;
+ !i;
+ #pragma clang diagnostic pop
+}
+// CHECK: UnaryOperator= UnOp=++ 1
+// CHECK: UnaryOperator= UnOp=++ 3
+// CHECK: UnaryOperator= UnOp=-- 2
+// CHECK: UnaryOperator= UnOp=-- 4
+// CHECK: UnaryOperator= UnOp=& 5
+// CHECK: UnaryOperator= UnOp=* 6
+// CHECK: UnaryOperator= UnOp=+ 7
+// CHECK: UnaryOperator= UnOp=- 8
+// CHECK: UnaryOperator= UnOp=~ 9
+// CHECK: UnaryOperator= UnOp=! 10
+
+struct C{
+ C() = default;
+ C& operator++(int);
+ C& operator++();
+ C& operator--(int);
+ C& operator--();
+ C& operator*();
+ C* operator&();
+ C& operator+();
+ C& operator-();
+ C& operator!();
+ C& operator~();
+ void operator co_await();
+ void foo();
+ C& operator+(const C&);
+ C& operator-(const C&);
+};
+
+// CHECK: CXXMethod=operator++:31:8 UnOp=++ 1
+// CHECK: CXXMethod=operator++:32:8 UnOp=++ 3
+// CHECK: CXXMethod=operator--:33:8 UnOp=-- 2
+// CHECK: CXXMethod=operator--:34:8 UnOp=-- 4
+// CHECK: CXXMethod=operator*:35:8 UnOp=* 6
+// CHECK: CXXMethod=operator&:36:8 UnOp=& 5
+// CHECK: CXXMethod=operator+:37:8 UnOp=+ 7
+// CHECK: CXXMethod=operator-:38:8 UnOp=- 8
+// CHECK: CXXMethod=operator!:39:8 UnOp=! 10
+// CHECK: CXXMethod=operator~:40:8 UnOp=~ 9
+// CHECK: CXXMethod=operator co_await:41:10 UnOp=co_await 14
+// CHECK: CXXMethod=foo:42:10 UnOp= 0
+// CHECK: CXXMethod=operator+:43:8 UnOp= 0
+// CHECK: CXXMethod=operator-:44:8 UnOp= 0
+
+void func2(){
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ C i;
+ i++;
+ ++i;
+ i--;
+ --i;
+ C *p = &i;
+ *i;
+ +i;
+ C n = +i;
+ -i;
+ C m = -i;
+
+ ~i;
+ !i;
+
+ i + i;
+ i - i;
+ #pragma clang diagnostic pop
+}
+
+// CHECK: CallExpr=C:30:5 UnOp= 0
+// CHECK: CallExpr=operator++:31:8 UnOp=++ 1
+// CHECK: CallExpr=operator++:32:8 UnOp=++ 3
+// CHECK: CallExpr=operator--:33:8 UnOp=-- 2
+// CHECK: CallExpr=operator--:34:8 UnOp=-- 4
+// CHECK: CallExpr=operator&:36:8 UnOp=& 5
+// CHECK: CallExpr=operator*:35:8 UnOp=* 6
+// CHECK: CallExpr=operator+:37:8 UnOp=+ 7
+// CHECK: CallExpr=C:29:8 UnOp= 0
+// CHECK: CallExpr=operator+:37:8 UnOp=+ 7
+// CHECK: CallExpr=operator-:38:8 UnOp=- 8
+// CHECK: CallExpr=C:29:8 UnOp= 0
+// CHECK: CallExpr=operator-:38:8 UnOp=- 8
+// CHECK: CallExpr=operator~:40:8 UnOp=~ 9
+// CHECK: CallExpr=operator!:39:8 UnOp=! 10
+// CHECK: CallExpr=operator+:43:8 UnOp= 0
+// CHECK: CallExpr=operator-:44:8 UnOp= 0
+
+
+struct D {
+
+};
+
+D operator++(const D&a, int);
+D operator++(const D&a );
+D operator--(const D&a, int);
+D operator--(const D&a);
+D operator*(const D&a);
+D operator&(const D&a);
+D operator+(const D&a);
+D operator-(const D&a);
+D operator!(const D&a);
+D operator~(const D&a);
+void operator co_await(const D &d);
+void foo(const D&);
+D operator+(const D&a, const D&);
+D operator-(const D&a, const D&);
+
+// CHECK: FunctionDecl=operator++:108:3 UnOp=++ 1
+// CHECK: FunctionDecl=operator++:109:3 UnOp=++ 3
+// CHECK: FunctionDecl=operator--:110:3 UnOp=-- 2
+// CHECK: FunctionDecl=operator--:111:3 UnOp=-- 4
+// CHECK: FunctionDecl=operator*:112:3 UnOp=* 6
+// CHECK: FunctionDecl=operator&:113:3 UnOp=& 5
+// CHECK: FunctionDecl=operator+:114:3 UnOp=+ 7
+// CHECK: FunctionDecl=operator-:115:3 UnOp=- 8
+// CHECK: FunctionDecl=operator!:116:3 UnOp=! 10
+// CHECK: FunctionDecl=operator~:117:3 UnOp=~ 9
+// CHECK: FunctionDecl=operator co_await:118:6 UnOp=co_await 14
+// CHECK: FunctionDecl=foo:119:6 UnOp= 0
+// CHECK: FunctionDecl=operator+:120:3 UnOp= 0
+// CHECK: FunctionDecl=operator-:121:3 UnOp= 0
+
+void func3(){
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunused-value"
+ D i;
+ i++;
+ ++i;
+ i--;
+ --i;
+ &i;
+ *i;
+ +i;
+ D n = +i;
+ -i;
+ D m = -i;
+
+ ~i;
+ !i;
+ i + i;
+ i - i;
+
+ #pragma clang diagnostic pop
+}
+
+// CHECK: CallExpr=D:104:8 UnOp= 0
+// CHECK: CallExpr=operator++:108:3 UnOp=++ 1
+// CHECK: CallExpr=operator++:109:3 UnOp=++ 3
+// CHECK: CallExpr=operator--:110:3 UnOp=-- 2
+// CHECK: CallExpr=operator--:111:3 UnOp=-- 4
+// CHECK: CallExpr=operator&:113:3 UnOp=& 5
+// CHECK: CallExpr=operator*:112:3 UnOp=* 6
+// CHECK: CallExpr=operator+:114:3 UnOp=+ 7
+// CHECK: CallExpr=operator+:114:3 UnOp=+ 7
+// CHECK: CallExpr=operator-:115:3 UnOp=- 8
+// CHECK: CallExpr=operator-:115:3 UnOp=- 8
+// CHECK: CallExpr=operator~:117:3 UnOp=~ 9
+// CHECK: CallExpr=operator!:116:3 UnOp=! 10
+// CHECK: CallExpr=operator+:120:3 UnOp= 0
+// CHECK: CallExpr=operator-:121:3 UnOp= 0
diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c
index 4b3e105aa7aff5..6cfd42e2877692 100644
--- a/clang/tools/c-index-test/c-index-test.c
+++ b/clang/tools/c-index-test/c-index-test.c
@@ -1865,7 +1865,9 @@ static enum CXChildVisitResult PrintBinOps(CXCursor C, CXCursor p,
enum CXCursorKind ck = clang_getCursorKind(C);
enum CXBinaryOperatorKind bok;
CXString opstr;
- if (ck != CXCursor_BinaryOperator && ck != CXCursor_CompoundAssignOperator)
+ if (ck != CXCursor_BinaryOperator && ck != CXCursor_CompoundAssignOperator &&
+ ck != CXCursor_CXXMethod && ck != CXCursor_FunctionDecl &&
+ ck != CXCursor_CallExpr)
return CXChildVisit_Recurse;
PrintCursor(C, NULL);
@@ -1876,6 +1878,22 @@ static enum CXChildVisitResult PrintBinOps(CXCursor C, CXCursor p,
return CXChildVisit_Recurse;
}
+static enum CXChildVisitResult PrintUnOps(CXCursor C, CXCursor p,
+ CXClientData d) {
+ enum CXCursorKind ck = clang_getCursorKind(C);
+ enum CXUnaryOperatorKind uok;
+ CXString opstr;
+ if (ck != CXCursor_UnaryOperator && ck != CXCursor_CXXMethod &&
+ ck != CXCursor_FunctionDecl && ck != CXCursor_CallExpr)
+ return CXChildVisit_Recurse;
+
+ PrintCursor(C, NULL);
+ uok = clang_getCursorUnaryOperatorKind(C);
+ opstr = clang_getUnaryOperatorKindSpelling(uok);
+ printf(" UnOp=%s %d\n", clang_getCString(opstr), uok);
+ clang_disposeString(opstr);
+ return CXChildVisit_Recurse;
+}
/******************************************************************************/
/* Mangling testing. */
/******************************************************************************/
@@ -5177,6 +5195,8 @@ int cindextest_main(int argc, const char **argv) {
PrintBitWidth, 0);
else if (argc > 2 && strcmp(argv[1], "-test-print-binops") == 0)
return perform_test_load_source(argc - 2, argv + 2, "all", PrintBinOps, 0);
+ else if (argc > 2 && strcmp(argv[1], "-test-print-unops") == 0)
+ return perform_test_load_source(argc - 2, argv + 2, "all", PrintUnOps, 0);
else if (argc > 2 && strcmp(argv[1], "-test-print-mangle") == 0)
return perform_test_load_tu(argv[2], "all", NULL, PrintMangledName, NULL);
else if (argc > 2 && strcmp(argv[1], "-test-print-manglings") == 0)
>From db055e9d83086cc3a7655fdbef7b7c01f9f7fdb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Bedn=C3=A1r?= <martin at serafean.cz>
Date: Sat, 12 Sep 2026 21:22:49 +0200
Subject: [PATCH 3/5] Add clang_Function_get{Unary/Binary}OperatorKind
---
clang/include/clang-c/Index.h | 6 ++++++
clang/tools/libclang/CIndex.cpp | 14 ++++++++++++++
clang/tools/libclang/libclang.map | 6 ++++++
3 files changed, 26 insertions(+)
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 8427236e0b4444..70b0447d88dea5 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -6958,6 +6958,12 @@ clang_getUnaryOperatorKindSpelling(enum CXUnaryOperatorKind kind);
CINDEX_LINKAGE enum CXUnaryOperatorKind
clang_getCursorUnaryOperatorKind(CXCursor cursor);
+CINDEX_LINKAGE enum CXBinaryOperatorKind
+clang_Function_getBinaryOperatorKind(CXCursor C);
+
+CINDEX_LINKAGE enum CXUnaryOperatorKind
+clang_Function_getUnaryOperatorKind(CXCursor C);
+
/**
* @}
*/
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 83b9842fb0db2c..1d26def0310e6e 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -9665,6 +9665,20 @@ unsigned clang_EnumDecl_isScoped(CXCursor C) {
return (Enum && Enum->isScoped()) ? 1 : 0;
}
+enum CXBinaryOperatorKind clang_Function_getBinaryOperatorKind(CXCursor C) {
+ if (clang_isDeclaration(C.kind)) {
+ return clang_getCursorBinaryOperatorKind(C);
+ }
+ return CXBinaryOperator_Invalid;
+}
+
+enum CXUnaryOperatorKind clang_Function_getUnaryOperatorKind(CXCursor C) {
+ if (clang_isDeclaration(C.kind)) {
+ return clang_getCursorUnaryOperatorKind(C);
+ }
+ return CXUnaryOperator_Invalid;
+}
+
//===----------------------------------------------------------------------===//
// Attribute introspection.
//===----------------------------------------------------------------------===//
diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map
index 57f281096929d1..efc82c4fb8d14e 100644
--- a/clang/tools/libclang/libclang.map
+++ b/clang/tools/libclang/libclang.map
@@ -463,6 +463,12 @@ LLVM_23 {
clang_ModuleCache_pruneWithCallback;
};
+LLVM_24 {
+ global:
+ clang_Function_getBinaryOperatorKind;
+ clang_Function_getUnaryOperatorKind;
+};
+
# Example of how to add a new symbol version entry. If you do add a new symbol
# version, please update the example to depend on the version you added.
# LLVM_X {
>From 613c9df77330391acc212cf975553ddd0432b0de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Bedn=C3=A1r?= <martin at serafean.cz>
Date: Sat, 12 Sep 2026 22:42:39 +0200
Subject: [PATCH 4/5] Don't crash on "Multi" operators
---
clang/tools/libclang/CIndex.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 1d26def0310e6e..b89d9e60aec844 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -10240,6 +10240,14 @@ CXString clang_getBinaryOperatorKindSpelling(enum CXBinaryOperatorKind kind) {
enum CXBinaryOperatorKind clang_getCursorBinaryOperatorKind(CXCursor cursor) {
auto retOp = [](OverloadedOperatorKind Kind, int numArgs) {
+ if (Kind == OO_Subscript || Kind == OO_Call || Kind == OO_New ||
+ Kind == OO_Array_New || Kind == OO_Delete || Kind == OO_Array_Delete ||
+ Kind == OO_Conditional) {
+ // TODO: how to handle these?
+ // These operators aren't handled in getOverloadedOpcode and crash.
+ return CXBinaryOperator_Invalid;
+ }
+
if (!(Kind == OO_None || Kind == OO_PlusPlus || Kind == OO_MinusMinus ||
numArgs != 2)) {
auto opcode = BinaryOperator::getOverloadedOpcode(Kind);
>From 8a21b989af3903b0128efc5bf0edc1461b70c14b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Bedn=C3=A1r?= <martin at serafean.cz>
Date: Sun, 13 Sep 2026 13:28:56 +0200
Subject: [PATCH 5/5] Use switch instead of if/else
---
clang/tools/libclang/CIndex.cpp | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index b89d9e60aec844..c383f02ed7b157 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -10240,16 +10240,25 @@ CXString clang_getBinaryOperatorKindSpelling(enum CXBinaryOperatorKind kind) {
enum CXBinaryOperatorKind clang_getCursorBinaryOperatorKind(CXCursor cursor) {
auto retOp = [](OverloadedOperatorKind Kind, int numArgs) {
- if (Kind == OO_Subscript || Kind == OO_Call || Kind == OO_New ||
- Kind == OO_Array_New || Kind == OO_Delete || Kind == OO_Array_Delete ||
- Kind == OO_Conditional) {
+ switch (Kind) {
+ case OO_Subscript:
+ case OO_Call:
+ case OO_New:
+ case OO_Array_New:
+ case OO_Delete:
+ case OO_Array_Delete:
+ case OO_Conditional:
// TODO: how to handle these?
// These operators aren't handled in getOverloadedOpcode and crash.
+ case OO_PlusPlus:
+ case OO_MinusMinus:
+ // Post-inc/dec and are considered unary despite having 2 arguments.
+ case OO_None:
return CXBinaryOperator_Invalid;
- }
+ default:
+ if (numArgs != 2)
+ return CXBinaryOperator_Invalid;
- if (!(Kind == OO_None || Kind == OO_PlusPlus || Kind == OO_MinusMinus ||
- numArgs != 2)) {
auto opcode = BinaryOperator::getOverloadedOpcode(Kind);
return static_cast<CXBinaryOperatorKind>(opcode + 1);
}
More information about the cfe-commits
mailing list