This is an archive of the discontinued LLVM Phabricator instance.

Add [some] template argument-extraction calls to libclang & cindex
ClosedPublic

Authored by rspringer on Oct 5 2014, 3:40 PM.

Details

Reviewers
eliben
akyrtzi
Summary

Adds the following calls to libclang/cindex:

  • clang_Cursor_getNumTemplateArgument: Retrieves the number of template arguments specified in a FunctionDecl
  • clang_Cursor_getTemplateArgumentKind: Retrieves a CXTemplateArgumentKind (defined in this change, corresponds to clang::TemplateArgument::ArgKind) the number of template arguments specified in a FunctionDecl
  • clang_Cursor_getTemplateArgumentType: Retrieves a CXType describing the n'th [template type] argument of a FunctionDecl
  • clang_Cursor_getTemplateArgumentValue: Retrives, as a [signed] long long, the integral value of the n'th argument of a FunctionDecl
  • clang_Cursor_getTemplateArgumentUnsignedValue: Retrives, as an unsigned long long, the integral value of the n'th argument of a FunctionDecl

Diff Detail

Event Timeline

rspringer updated this revision to Diff 14437.Oct 5 2014, 3:40 PM
rspringer retitled this revision from to Add [some[ template argument-extraction calls to libclang & cindex.
rspringer updated this object.
rspringer edited the test plan for this revision. (Show Details)
rspringer added a reviewer: eliben.
rspringer updated this object.
rspringer added a subscriber: Unknown Object (MLST).
rspringer retitled this revision from Add [some[ template argument-extraction calls to libclang & cindex to Add [some] template argument-extraction calls to libclang & cindex.Oct 5 2014, 7:28 PM
rspringer edited subscribers, added: Unknown Object (MLST); removed: Unknown Object (MLST).Oct 6 2014, 6:53 AM
eliben added inline comments.Oct 6 2014, 9:25 AM
bindings/python/tests/cindex/test_cursor.py
260

Since you're really only looking for one cursor for 'foo' here, maybe this loop can be simplified away? I.g. no need for 'found' etc. The test and its kin can be made shorter

264

Just:

assert foo.get_num_template_arguments() == 3

?

287

Why the ctypes casts here?

include/clang-c/Index.h
2947

Since this is all only for function decls that are specializations, IMHO the comment should mention this. A small code sample could help too

tools/libclang/CXCursor.cpp
1091

Why 0 and not -1 here?

Updated patch coming shortly.

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

That makes sense - it took me a few tries to get my test case code written correctly, so "found" was verifying, for me, that a template was present.
However, since the FUNCTION_DECL is returned after the FUNCTION_TEMPLATE, I can eliminate the loops.

264

Yep. Done.

287

get_template_argument_value and get_template_argument_unsigned_value return the values as 64-bit signed and unsigned integers, respectively, but the values as declared in the template are ints and bools, respectively, so I want to cast them to 32-bit values to make sure they're extracted correctly as those values.

It's really not necessary for the bools (obviously), but I suppose I'm a fan of symmetry. I'm happy to change that, though, if you think they're just clutter.

include/clang-c/Index.h
2947

Comment updated and examples added. It may be a touch verbose now, but I'll let you be the judge.

tools/libclang/CXCursor.cpp
1091

Oversight. Fixed & thanks!

rspringer updated this revision to Diff 14466.Oct 6 2014, 11:23 AM

Updated patch, based on last round of review comments.

rspringer added inline comments.Oct 6 2014, 2:44 PM
bindings/python/tests/cindex/test_cursor.py
287

After further thought, I realized this was only necessary b/c I was defining get_template_argument_value in terms of get_template_argument_unsigned_value (which zero-extends, rather than sign-extends, its argument). When going to fix that, I realized that a large body of the code in that area could be refactored, which I've now done.
It's a lot better now; PTAL.

rspringer updated this revision to Diff 14476.Oct 6 2014, 2:45 PM

Factored out common code between clang_Cursor_getTemplateArgumentType, clang_Cursor_getTemplateArgumentValue, and clang_Cursor_getTemplateArgumentUnsignedValue, and simplified test cases.

eliben edited edge metadata.Oct 6 2014, 2:52 PM

This looks good; just a couple of nits

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

One empty line here for consistency?

include/clang-c/Index.h
3016

I think an example with an actual unsigned would be more useful here :)

akyrtzi edited edge metadata.Oct 6 2014, 10:24 PM

Hi Rob,

Thank you for the enhancements in the libclang API!
Looks good, some nitpicks:

+ * If the argument cursor cannot be converted into a template function
+ * declaration, -1 is returned.
<..>
+CINDEX_LINKAGE int clang_Cursor_getNumTemplateArguments(CXCursor C);

+ * If the argument CXCursor does not represent a FunctionDecl whose I'th
+ * template argument has a kind of ArgKind::Integral, an invalid type is\
+ * returned (in debug mode, this will assert).
<..>
+CINDEX_LINKAGE CXType clang_Cursor_getTemplateArgumentType(CXCursor C,
+ unsigned I);

The convention in the libclang API is that if the function can return an unambiguous invalid value, we do not assert. If you document the behavior of the function ("-1 is returned") you should allow the client to depend on it for both debug and release.
I suggest removing the assertions in clang_Cursor_getNumTemplateArguments and clang_Cursor_getTemplateArgumentType.

+ * If the argument CXCursor does not represent a FunctionDecl whose I'th
+ * template argument has a kind of ArgKind::Integral, an undefined value will be
+ * returned (in debug mode, this call will fail an assert).
<..>
+ * If called with I = 1 or 2, -7 or true will be returned, respectively.
+ * For I == 0, an invalid value will be returned or an assert will be triggered.
+ */
+CINDEX_LINKAGE long long clang_Cursor_getTemplateArgumentValue(CXCursor C,
+ unsigned I);

I suggest to keep the assertion in code, but don't document it and be so specific, say "it is undefined to call this function on a CXCursor that does not represent a FunctionDecl or the I'th template argument is not an integral value"
Same for clang_Cursor_getTemplateArgumentUnsignedValue.

+ if (Cursor.kind == CXCursor_FunctionDecl) {
+ // Collect the template parameter kinds from the base template.
+ unsigned NumTemplateArgs = clang_Cursor_getNumTemplateArguments(Cursor);
+ enum CXCursorKind *TemplateParamKinds = (enum CXCursorKind*)
+ malloc(NumTemplateArgs * sizeof(enum CXCursorKind));
+ VisitTemplateParamsData VTPD;
+ VTPD.TemplateParamKinds = TemplateParamKinds;
+ VTPD.CurrentIndex = 0;
+ clang_visitChildren(SpecializationOf, visitTemplateParams,
+ &VTPD);
+
+ for (unsigned I = 0; I < NumTemplateArgs; I++) {
+ if (TemplateParamKinds[I] == CXCursor_TemplateTypeParameter) {
+ CXType T = clang_Cursor_getTemplateArgumentType(Cursor, I);
+ CXString S = clang_getTypeSpelling(T);
+ printf(" [Template arg %d: type: %s]", I, clang_getCString(S));
+ clang_disposeString(S);
+ } else if (TemplateParamKinds[I] ==
+ CXCursor_NonTypeTemplateParameter) {
+ printf(" [Template arg %d: intval: %lld]",
+ I, clang_Cursor_getTemplateArgumentValue(Cursor, I));
+ }
+ }
+ free(TemplateParamKinds);
+ }

}

This indicates that we are missing another function to make using the others convenient. We are missing a function to get the kind of the I'th template argument, maybe have a function return if the I'th argument is a type or value ?

Also could you please add some tests for this new functionality, e.g. enhance 'test/Index/index-templates.cpp' ?

Hi Argyrios,
Thanks a ton for the review! I think the patch is much better now, for it.

Responses:
+ The convention in the libclang API is that if the function can return an unambiguous invalid value, we do not + assert. If you document the behavior of the function ("-1 is returned") you should allow the client to depend on it for both debug and release.
+ I suggest removing the assertions in clang_Cursor_getNumTemplateArguments and clang_Cursor_getTemplateArgumentType.

Done.
One question: ...getNumTemplateArguments and other functions depend on the helper clang_Cursor_getTemplateArgument, which also has an unambiguous "bad" return ("false"), so I've removed the asserts there, as well (or else getNumTemplateArguments would still throw them). It seems like a shame, though, to lose the debugging information that was captured in the assert message ("Invalid template argument index", for example, in the prior patch revision). Is there a convention to preserve that information somehow, or is that not standard Clang practice?

+ This indicates that we are missing another function to make using the others convenient. We are missing a function to get the kind of the I'th template argument, maybe have a function return if the I'th argument is a type or value ?
+ Also could you please add some tests for this new functionality, e.g. enhance 'test/Index/index-templates.cpp' ?

That's a good call - you're absolutely right. I've added clang_Cursor_getTemplateArgumentKind and appropriate unit tests. Give them a once-over when you get a chance.

While porting this change to cindex.py, I realized that there was a lot of duplicated code for enumeration representation. I may have bitten off more than I can chew, but I at least took a stab at unifying all that somewhat. It definitely needs to be re-reviewed. :)

Again - thank you both for the reviews (updated patch coming shortly)!

Updated tests are still passing.

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

Done.

include/clang-c/Index.h
3016

That's...a good point. Done.

rspringer updated this revision to Diff 14580.Oct 8 2014, 9:55 AM
rspringer edited edge metadata.

I overlooked the request to update test/Index/index-templates.cpp; I'll get right on that.

rspringer updated this revision to Diff 14606.Oct 8 2014, 4:07 PM
rspringer updated this object.

Added some tests to index-templates.cpp; I can add more if necessary/desired (...but I was having some difficulty getting more TemplateArgument::ArgKinds to be expressed).

rspringer updated this revision to Diff 14740.Oct 10 2014, 11:25 AM

Updated based on out-of-thread comments:

  • Added enum to encode failures of clang_Cursor_getTemplateArgument
  • Corrected clang_Cursor_getTemplateArgumentType comment.
rspringer updated this revision to Diff 14744.Oct 10 2014, 12:51 PM

Updated a comment with an updated type name (ArgKind::Integral -> CXTemplateArgKind_Integral).

eliben accepted this revision.Oct 10 2014, 1:11 PM
eliben edited edge metadata.

Committed in r219529.

This revision is now accepted and ready to land.Oct 10 2014, 1:11 PM