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