Skip to content

Commit a972a39

Browse files
authored
Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (#98489)
This is a rework of patch [D10833](https://reviews.llvm.org/D10833) previously posted on LLVM Phabricator by arthurp in 2015. It allows to retrieve the type of binary operator via libclangs python bindings. I did clean up the changes, removed unrelated changes and rebased the changeset to the latest main branch. As this is my first contribution to the LLVM project, let me know if any required tests or documentation are missing.
1 parent 1af3a89 commit a972a39

20 files changed

+1655
-771
lines changed

clang/bindings/python/clang/cindex.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,6 +1820,18 @@ def availability(self):
18201820

18211821
return AvailabilityKind.from_id(self._availability)
18221822

1823+
@property
1824+
def binary_operator(self):
1825+
"""
1826+
Retrieves the opcode if this cursor points to a binary operator
1827+
:return:
1828+
"""
1829+
1830+
if not hasattr(self, "_binopcode"):
1831+
self._binopcode = conf.lib.clang_Cursor_getBinaryOpcode(self)
1832+
1833+
return BinaryOperator.from_id(self._binopcode)
1834+
18231835
@property
18241836
def access_specifier(self):
18251837
"""
@@ -2110,6 +2122,55 @@ def from_cursor_result(res, fn, args):
21102122
return res
21112123

21122124

2125+
class BinaryOperator(BaseEnumeration):
2126+
"""
2127+
Describes the BinaryOperator of a declaration
2128+
"""
2129+
2130+
def __nonzero__(self):
2131+
"""Allows checks of the kind ```if cursor.binary_operator:```"""
2132+
return self.value != 0
2133+
2134+
@property
2135+
def is_assignment(self):
2136+
return BinaryOperator.Assign.value <= self.value < BinaryOperator.Comma.value
2137+
2138+
Invalid = 0
2139+
PtrMemD = 1
2140+
PtrMemI = 2
2141+
Mul = 3
2142+
Div = 4
2143+
Rem = 5
2144+
Add = 6
2145+
Sub = 7
2146+
Shl = 8
2147+
Shr = 9
2148+
Cmp = 10
2149+
LT = 11
2150+
GT = 12
2151+
LE = 13
2152+
GE = 14
2153+
EQ = 15
2154+
NE = 16
2155+
And = 17
2156+
Xor = 18
2157+
Or = 19
2158+
LAnd = 20
2159+
LOr = 21
2160+
Assign = 22
2161+
MulAssign = 23
2162+
DivAssign = 24
2163+
RemAssign = 25
2164+
AddAssign = 26
2165+
SubAssign = 27
2166+
ShlAssign = 28
2167+
ShrAssign = 29
2168+
AndAssign = 30
2169+
XorAssign = 31
2170+
OrAssign = 32
2171+
Comma = 33
2172+
2173+
21132174
class StorageClass(BaseEnumeration):
21142175
"""
21152176
Describes the storage class of a declaration
@@ -3847,6 +3908,7 @@ def write_main_file_to_stdout(self):
38473908
("clang_Cursor_getTemplateArgumentUnsignedValue", [Cursor, c_uint], c_ulonglong),
38483909
("clang_Cursor_isAnonymous", [Cursor], bool),
38493910
("clang_Cursor_isBitField", [Cursor], bool),
3911+
("clang_Cursor_getBinaryOpcode", [Cursor], c_int),
38503912
("clang_Cursor_getBriefCommentText", [Cursor], _CXString, _CXString.from_result),
38513913
("clang_Cursor_getRawCommentText", [Cursor], _CXString, _CXString.from_result),
38523914
("clang_Cursor_getOffsetOfField", [Cursor], c_longlong),
@@ -4016,6 +4078,7 @@ def function_exists(self, name):
40164078

40174079
__all__ = [
40184080
"AvailabilityKind",
4081+
"BinaryOperator",
40194082
"Config",
40204083
"CodeCompletionResults",
40214084
"CompilationDatabase",

clang/bindings/python/tests/cindex/test_cursor.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from clang.cindex import TemplateArgumentKind
1414
from clang.cindex import TranslationUnit
1515
from clang.cindex import TypeKind
16+
from clang.cindex import BinaryOperator
1617
from .util import get_cursor
1718
from .util import get_cursors
1819
from .util import get_tu
@@ -54,6 +55,64 @@ class C {
5455
void foo<-7, float, true>();
5556
"""
5657

58+
kBinops = """\
59+
struct C {
60+
int m;
61+
};
62+
63+
void func(void){
64+
int a, b;
65+
int C::* p = &C::
66+
67+
C c;
68+
c.*p;
69+
70+
C* pc;
71+
pc->*p;
72+
73+
a * b;
74+
a / b;
75+
a % b;
76+
a + b;
77+
a - b;
78+
79+
a << b;
80+
a >> b;
81+
82+
a < b;
83+
a > b;
84+
85+
a <= b;
86+
a >= b;
87+
a == b;
88+
a != b;
89+
90+
a & b;
91+
a ^ b;
92+
a | b;
93+
94+
a && b;
95+
a || b;
96+
97+
a = b;
98+
99+
a *= b;
100+
a /= b;
101+
a %= b;
102+
a += b;
103+
a -= b;
104+
105+
a <<= b;
106+
a >>= b;
107+
108+
a &= b;
109+
a ^= b;
110+
a |= b;
111+
a , b;
112+
113+
}
114+
"""
115+
57116

58117
class TestCursor(unittest.TestCase):
59118
def test_get_children(self):
@@ -695,3 +754,48 @@ def test_mangled_name(self):
695754
self.assertIn(
696755
foo.mangled_name, ("_Z3fooii", "__Z3fooii", "?foo@@YAHHH", "?foo@@YAHHH@Z")
697756
)
757+
758+
def test_binop(self):
759+
tu = get_tu(kBinops, lang="cpp")
760+
761+
operators = {
762+
# not exposed yet
763+
# ".*" : BinaryOperator.PtrMemD,
764+
"->*": BinaryOperator.PtrMemI,
765+
"*": BinaryOperator.Mul,
766+
"/": BinaryOperator.Div,
767+
"%": BinaryOperator.Rem,
768+
"+": BinaryOperator.Add,
769+
"-": BinaryOperator.Sub,
770+
"<<": BinaryOperator.Shl,
771+
">>": BinaryOperator.Shr,
772+
# tests do not run in C++2a mode so this operator is not available
773+
# "<=>" : BinaryOperator.Cmp,
774+
"<": BinaryOperator.LT,
775+
">": BinaryOperator.GT,
776+
"<=": BinaryOperator.LE,
777+
">=": BinaryOperator.GE,
778+
"==": BinaryOperator.EQ,
779+
"!=": BinaryOperator.NE,
780+
"&": BinaryOperator.And,
781+
"^": BinaryOperator.Xor,
782+
"|": BinaryOperator.Or,
783+
"&&": BinaryOperator.LAnd,
784+
"||": BinaryOperator.LOr,
785+
"=": BinaryOperator.Assign,
786+
"*=": BinaryOperator.MulAssign,
787+
"/=": BinaryOperator.DivAssign,
788+
"%=": BinaryOperator.RemAssign,
789+
"+=": BinaryOperator.AddAssign,
790+
"-=": BinaryOperator.SubAssign,
791+
"<<=": BinaryOperator.ShlAssign,
792+
">>=": BinaryOperator.ShrAssign,
793+
"&=": BinaryOperator.AndAssign,
794+
"^=": BinaryOperator.XorAssign,
795+
"|=": BinaryOperator.OrAssign,
796+
",": BinaryOperator.Comma,
797+
}
798+
799+
for op, typ in operators.items():
800+
c = get_cursor(tu, op)
801+
assert c.binary_operator == typ

clang/bindings/python/tests/cindex/test_enums.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
LinkageKind,
1313
TLSKind,
1414
StorageClass,
15+
BinaryOperator,
1516
)
1617

1718

@@ -28,6 +29,7 @@ class TestEnums(unittest.TestCase):
2829
LinkageKind,
2930
TLSKind,
3031
StorageClass,
32+
BinaryOperator,
3133
]
3234

3335
def test_from_id(self):

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,8 @@ Python Binding Changes
13091309
- Exposed `CXRewriter` API as `class Rewriter`.
13101310
- Add some missing kinds from Index.h (CursorKind: 149-156, 272-320, 420-437.
13111311
TemplateArgumentKind: 5-9. TypeKind: 161-175 and 178).
1312+
- Add support for retrieving binary operator information through
1313+
Cursor.binary_operator().
13121314

13131315
OpenMP Support
13141316
--------------

clang/include/clang-c/Index.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3750,6 +3750,59 @@ enum CX_StorageClass {
37503750
CX_SC_Register
37513751
};
37523752

3753+
/**
3754+
* Represents a specific kind of binary operator which can appear at a cursor.
3755+
*/
3756+
enum CX_BinaryOperatorKind {
3757+
CX_BO_Invalid = 0,
3758+
CX_BO_PtrMemD = 1,
3759+
CX_BO_PtrMemI = 2,
3760+
CX_BO_Mul = 3,
3761+
CX_BO_Div = 4,
3762+
CX_BO_Rem = 5,
3763+
CX_BO_Add = 6,
3764+
CX_BO_Sub = 7,
3765+
CX_BO_Shl = 8,
3766+
CX_BO_Shr = 9,
3767+
CX_BO_Cmp = 10,
3768+
CX_BO_LT = 11,
3769+
CX_BO_GT = 12,
3770+
CX_BO_LE = 13,
3771+
CX_BO_GE = 14,
3772+
CX_BO_EQ = 15,
3773+
CX_BO_NE = 16,
3774+
CX_BO_And = 17,
3775+
CX_BO_Xor = 18,
3776+
CX_BO_Or = 19,
3777+
CX_BO_LAnd = 20,
3778+
CX_BO_LOr = 21,
3779+
CX_BO_Assign = 22,
3780+
CX_BO_MulAssign = 23,
3781+
CX_BO_DivAssign = 24,
3782+
CX_BO_RemAssign = 25,
3783+
CX_BO_AddAssign = 26,
3784+
CX_BO_SubAssign = 27,
3785+
CX_BO_ShlAssign = 28,
3786+
CX_BO_ShrAssign = 29,
3787+
CX_BO_AndAssign = 30,
3788+
CX_BO_XorAssign = 31,
3789+
CX_BO_OrAssign = 32,
3790+
CX_BO_Comma = 33,
3791+
CX_BO_LAST = CX_BO_Comma
3792+
};
3793+
3794+
/**
3795+
* \brief Returns the operator code for the binary operator.
3796+
*/
3797+
CINDEX_LINKAGE enum CX_BinaryOperatorKind
3798+
clang_Cursor_getBinaryOpcode(CXCursor C);
3799+
3800+
/**
3801+
* \brief Returns a string containing the spelling of the binary operator.
3802+
*/
3803+
CINDEX_LINKAGE CXString
3804+
clang_Cursor_getBinaryOpcodeStr(enum CX_BinaryOperatorKind Op);
3805+
37533806
/**
37543807
* Returns the storage class for a function or variable declaration.
37553808
*

clang/test/Index/binop.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// RUN: c-index-test -test-print-binops %s | FileCheck %s
2+
3+
struct C {
4+
int m;
5+
};
6+
7+
void func(void) {
8+
#pragma clang diagnostic push
9+
#pragma clang diagnostic ignored "-Wunused-value"
10+
int a, b;
11+
int C::*p = &C::m;
12+
13+
C c;
14+
c.*p;
15+
16+
C *pc;
17+
pc->*p;
18+
19+
a *b;
20+
a / b;
21+
a % b;
22+
a + b;
23+
a - b;
24+
25+
a << b;
26+
a >> b;
27+
28+
a < b;
29+
a > b;
30+
31+
a <= b;
32+
a >= b;
33+
a == b;
34+
a != b;
35+
36+
a &b;
37+
a ^ b;
38+
a | b;
39+
40+
a &&b;
41+
a || b;
42+
43+
a = b;
44+
45+
a *= b;
46+
a /= b;
47+
a %= b;
48+
a += b;
49+
a -= b;
50+
51+
a <<= b;
52+
a >>= b;
53+
54+
a &= b;
55+
a ^= b;
56+
a |= b;
57+
a, b;
58+
#pragma clang diagnostic pop
59+
}
60+
61+
// CHECK: BinaryOperator=.* BinOp=.* 1
62+
// CHECK: BinaryOperator=->* BinOp=->* 2
63+
// CHECK: BinaryOperator=* BinOp=* 3
64+
// CHECK: BinaryOperator=/ BinOp=/ 4
65+
// CHECK: BinaryOperator=% BinOp=% 5
66+
// CHECK: BinaryOperator=+ BinOp=+ 6
67+
// CHECK: BinaryOperator=- BinOp=- 7
68+
// CHECK: BinaryOperator=<< BinOp=<< 8
69+
// CHECK: BinaryOperator=>> BinOp=>> 9
70+
// CHECK: BinaryOperator=< BinOp=< 11
71+
// CHECK: BinaryOperator=> BinOp=> 12
72+
// CHECK: BinaryOperator=<= BinOp=<= 13
73+
// CHECK: BinaryOperator=>= BinOp=>= 14
74+
// CHECK: BinaryOperator=== BinOp=== 15
75+
// CHECK: BinaryOperator=!= BinOp=!= 16
76+
// CHECK: BinaryOperator=& BinOp=& 17
77+
// CHECK: BinaryOperator=^ BinOp=^ 18
78+
// CHECK: BinaryOperator=| BinOp=| 19
79+
// CHECK: BinaryOperator=&& BinOp=&& 20
80+
// CHECK: BinaryOperator=|| BinOp=|| 21
81+
// CHECK: BinaryOperator== BinOp== 22
82+
// CHECK: CompoundAssignOperator=*= BinOp=*= 23
83+
// CHECK: CompoundAssignOperator=/= BinOp=/= 24
84+
// CHECK: CompoundAssignOperator=%= BinOp=%= 25
85+
// CHECK: CompoundAssignOperator=+= BinOp=+= 26
86+
// CHECK: CompoundAssignOperator=-= BinOp=-= 27
87+
// CHECK: CompoundAssignOperator=<<= BinOp=<<= 28
88+
// CHECK: CompoundAssignOperator=>>= BinOp=>>= 29
89+
// CHECK: CompoundAssignOperator=&= BinOp=&= 30
90+
// CHECK: CompoundAssignOperator=^= BinOp=^= 31
91+
// CHECK: CompoundAssignOperator=|= BinOp=|= 32
92+
// CHECK: BinaryOperator=, BinOp=, 33

0 commit comments

Comments
 (0)