1 #include "clang_cursor.h"
2
3 #include "clang_utils.h"
4
5 #include <string.h>
6
7 #include <algorithm>
8 #include <cassert>
9
ResolveCXSourceRange(const CXSourceRange & range,CXFile * cx_file)10 Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) {
11 CXSourceLocation start = clang_getRangeStart(range);
12 CXSourceLocation end = clang_getRangeEnd(range);
13
14 unsigned int start_line, start_column;
15 clang_getSpellingLocation(start, cx_file, &start_line, &start_column,
16 nullptr);
17 unsigned int end_line, end_column;
18 clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
19
20 return Range(Position((int16_t)start_line - 1, (int16_t)start_column - 1),
21 Position((int16_t)end_line - 1, (int16_t)end_column - 1));
22 }
23
ClangType()24 ClangType::ClangType() : cx_type() {}
25
ClangType(const CXType & other)26 ClangType::ClangType(const CXType& other) : cx_type(other) {}
27
operator ==(const ClangType & rhs) const28 bool ClangType::operator==(const ClangType& rhs) const {
29 return clang_equalTypes(cx_type, rhs.cx_type);
30 }
31
get_declaration() const32 ClangCursor ClangType::get_declaration() const {
33 return clang_getTypeDeclaration(cx_type);
34 }
35
get_usr() const36 std::string ClangType::get_usr() const {
37 return ClangCursor(clang_getTypeDeclaration(cx_type)).get_usr();
38 }
39
get_usr_hash() const40 Usr ClangType::get_usr_hash() const {
41 if (is_builtin())
42 return static_cast<Usr>(cx_type.kind);
43 return ClangCursor(clang_getTypeDeclaration(cx_type)).get_usr_hash();
44 }
45
get_canonical() const46 ClangType ClangType::get_canonical() const {
47 return clang_getCanonicalType(cx_type);
48 }
49
strip_qualifiers() const50 ClangType ClangType::strip_qualifiers() const {
51 CXType cx = cx_type;
52 while (1) {
53 switch (cx.kind) {
54 default:
55 break;
56 case CXType_ConstantArray:
57 case CXType_DependentSizedArray:
58 case CXType_IncompleteArray:
59 case CXType_VariableArray:
60 cx = clang_getElementType(cx);
61 continue;
62 case CXType_BlockPointer:
63 case CXType_LValueReference:
64 case CXType_MemberPointer:
65 case CXType_ObjCObjectPointer:
66 case CXType_Pointer:
67 case CXType_RValueReference:
68 cx = clang_getPointeeType(cx);
69 continue;
70 }
71 break;
72 }
73
74 return cx;
75 }
76
get_spell_name() const77 std::string ClangType::get_spell_name() const {
78 return ToString(clang_getTypeSpelling(cx_type));
79 }
80
get_return_type() const81 ClangType ClangType::get_return_type() const {
82 return ClangType(clang_getResultType(cx_type));
83 }
84
get_arguments() const85 std::vector<ClangType> ClangType::get_arguments() const {
86 int size = clang_getNumArgTypes(cx_type);
87 if (size < 0)
88 return {};
89 std::vector<ClangType> types(size);
90 for (int i = 0; i < size; ++i)
91 types.emplace_back(clang_getArgType(cx_type, i));
92 return types;
93 }
94
get_template_arguments() const95 std::vector<ClangType> ClangType::get_template_arguments() const {
96 int size = clang_Type_getNumTemplateArguments(cx_type);
97 assert(size >= 0);
98 if (size < 0)
99 return std::vector<ClangType>();
100
101 std::vector<ClangType> types(size);
102 for (int i = 0; i < size; ++i)
103 types.emplace_back(clang_Type_getTemplateArgumentAsType(cx_type, i));
104 return types;
105 }
106
107 static_assert(sizeof(ClangCursor) == sizeof(CXCursor),
108 "Cursor must be the same size as CXCursor");
109
ClangCursor()110 ClangCursor::ClangCursor() : cx_cursor(clang_getNullCursor()) {}
111
ClangCursor(const CXCursor & other)112 ClangCursor::ClangCursor(const CXCursor& other) : cx_cursor(other) {}
113
operator bool() const114 ClangCursor::operator bool() const {
115 return !clang_Cursor_isNull(cx_cursor);
116 }
117
operator ==(const ClangCursor & rhs) const118 bool ClangCursor::operator==(const ClangCursor& rhs) const {
119 return clang_equalCursors(cx_cursor, rhs.cx_cursor);
120 }
121
operator !=(const ClangCursor & rhs) const122 bool ClangCursor::operator!=(const ClangCursor& rhs) const {
123 return !(*this == rhs);
124 }
125
get_kind() const126 CXCursorKind ClangCursor::get_kind() const {
127 return cx_cursor.kind;
128 }
129
get_type() const130 ClangType ClangCursor::get_type() const {
131 return ClangType(clang_getCursorType(cx_cursor));
132 }
133
get_spell_name() const134 std::string ClangCursor::get_spell_name() const {
135 return ::ToString(clang_getCursorSpelling(cx_cursor));
136 }
137
get_spell(CXFile * cx_file) const138 Range ClangCursor::get_spell(CXFile* cx_file) const {
139 // TODO for Objective-C methods and Objective-C message expressions, there are
140 // multiple pieces for each selector identifier.
141 CXSourceRange range = clang_Cursor_getSpellingNameRange(cx_cursor, 0, 0);
142 return ResolveCXSourceRange(range, cx_file);
143 }
144
get_extent() const145 Range ClangCursor::get_extent() const {
146 CXSourceRange range = clang_getCursorExtent(cx_cursor);
147 return ResolveCXSourceRange(range, nullptr);
148 }
149
get_display_name() const150 std::string ClangCursor::get_display_name() const {
151 return ::ToString(clang_getCursorDisplayName(cx_cursor));
152 }
153
get_usr() const154 std::string ClangCursor::get_usr() const {
155 return ::ToString(clang_getCursorUSR(cx_cursor));
156 }
157
get_usr_hash() const158 Usr ClangCursor::get_usr_hash() const {
159 CXString usr = clang_getCursorUSR(cx_cursor);
160 Usr ret = HashUsr(clang_getCString(usr));
161 clang_disposeString(usr);
162 return ret;
163 }
164
get_opt_usr_hash() const165 optional<Usr> ClangCursor::get_opt_usr_hash() const {
166 CXString usr = clang_getCursorUSR(cx_cursor);
167 const char* str = clang_getCString(usr);
168 if (!str || str[0] == '\0') {
169 clang_disposeString(usr);
170 return nullopt;
171 }
172 Usr ret = HashUsr(str);
173 clang_disposeString(usr);
174 return ret;
175 }
176
is_definition() const177 bool ClangCursor::is_definition() const {
178 return clang_isCursorDefinition(cx_cursor);
179 }
180
template_specialization_to_template_definition() const181 ClangCursor ClangCursor::template_specialization_to_template_definition()
182 const {
183 CXCursor definition = clang_getSpecializedCursorTemplate(cx_cursor);
184 if (definition.kind == CXCursor_FirstInvalid)
185 return cx_cursor;
186 return definition;
187 }
188
get_referenced() const189 ClangCursor ClangCursor::get_referenced() const {
190 return ClangCursor(clang_getCursorReferenced(cx_cursor));
191 }
192
get_canonical() const193 ClangCursor ClangCursor::get_canonical() const {
194 return ClangCursor(clang_getCanonicalCursor(cx_cursor));
195 }
196
get_definition() const197 ClangCursor ClangCursor::get_definition() const {
198 return ClangCursor(clang_getCursorDefinition(cx_cursor));
199 }
200
get_lexical_parent() const201 ClangCursor ClangCursor::get_lexical_parent() const {
202 return ClangCursor(clang_getCursorLexicalParent(cx_cursor));
203 }
204
get_semantic_parent() const205 ClangCursor ClangCursor::get_semantic_parent() const {
206 return ClangCursor(clang_getCursorSemanticParent(cx_cursor));
207 }
208
get_arguments() const209 std::vector<ClangCursor> ClangCursor::get_arguments() const {
210 int size = clang_Cursor_getNumArguments(cx_cursor);
211 if (size < 0)
212 return std::vector<ClangCursor>();
213
214 std::vector<ClangCursor> cursors(size);
215 for (int i = 0; i < size; ++i)
216 cursors.emplace_back(clang_Cursor_getArgument(cx_cursor, i));
217 return cursors;
218 }
219
is_valid_kind() const220 bool ClangCursor::is_valid_kind() const {
221 CXCursor referenced = clang_getCursorReferenced(cx_cursor);
222 if (clang_Cursor_isNull(referenced))
223 return false;
224
225 CXCursorKind kind = get_kind();
226 return kind > CXCursor_UnexposedDecl &&
227 (kind < CXCursor_FirstInvalid || kind > CXCursor_LastInvalid);
228 }
229
get_type_description() const230 std::string ClangCursor::get_type_description() const {
231 auto type = clang_getCursorType(cx_cursor);
232 return ::ToString(clang_getTypeSpelling(type));
233 }
234
get_comments() const235 std::string ClangCursor::get_comments() const {
236 CXSourceRange range = clang_Cursor_getCommentRange(cx_cursor);
237 if (clang_Range_isNull(range))
238 return {};
239
240 unsigned start_column;
241 clang_getSpellingLocation(clang_getRangeStart(range), nullptr, nullptr,
242 &start_column, nullptr);
243
244 // Get associated comment text.
245 CXString cx_raw = clang_Cursor_getRawCommentText(cx_cursor);
246 int pad = -1;
247 std::string ret;
248 for (const char* p = clang_getCString(cx_raw); *p;) {
249 // The first line starts with a comment marker, but the rest needs
250 // un-indenting.
251 unsigned skip = start_column - 1;
252 for (; skip > 0 && (*p == ' ' || *p == '\t'); p++)
253 skip--;
254 const char* q = p;
255 while (*q != '\n' && *q)
256 q++;
257 if (*q)
258 q++;
259 // A minimalist approach to skip Doxygen comment markers.
260 // See https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
261 if (pad < 0) {
262 // First line, detect the length of comment marker and put into |pad|
263 const char* begin = p;
264 while (*p == '/' || *p == '*')
265 p++;
266 if (*p == '<' || *p == '!')
267 p++;
268 if (*p == ' ')
269 p++;
270 pad = int(p - begin);
271 } else {
272 // Other lines, skip |pad| bytes
273 int prefix = pad;
274 while (prefix > 0 &&
275 (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!'))
276 prefix--, p++;
277 }
278 ret.insert(ret.end(), p, q);
279 p = q;
280 }
281 clang_disposeString(cx_raw);
282 while (ret.size() && isspace(ret.back()))
283 ret.pop_back();
284 if (EndsWith(ret, "*/")) {
285 ret.resize(ret.size() - 2);
286 } else if (EndsWith(ret, "\n/")) {
287 ret.resize(ret.size() - 2);
288 }
289 while (ret.size() && isspace(ret.back()))
290 ret.pop_back();
291 if (ret.empty())
292 return {};
293 return ret;
294 }
295
ToString() const296 std::string ClangCursor::ToString() const {
297 return ::ToString(get_kind()) + " " + get_spell_name();
298 }
299