I'm trying to write a linter for openmp code, but libclang hides the body of openmp directives. My problem is essentially the same as this unanswered issue from the llvm mailing list.
I took a look at clang's textual ast-dump, and the information I need seems to be present. However, when I use the binary dump to create a CXCursor to iterate over the tree using the libclang library, the subtrees captured by openmp directives seem to turn into "unexposed statements."
I have tried using clang's parseTranslationUnit function to generate the AST internally, but the result is the same. Is this a limitation of libclang, or could I be doing something wrong? Below are snapshots of my code, the textual AST dump and the command I used to generate it, and the output I get from walking the AST with libclang. Thanks!
AST Walker:
#include <iostream>
#include <stdlib.h>
#include <clang-c/Index.h>
using namespace std;
int main(int argc, char** argv)
{
CXIndex index = clang_createIndex(0, 0);
CXTranslationUnit unit = clang_createTranslationUnit(index, argv[1]); //.ast filename
if (unit == nullptr)
{
cerr << "Unable to parse translation unit. Quitting." << endl;
exit(-1);
}
printf("%d\n",clang_getNumDiagnostics(unit));
for (unsigned I = 0, N = clang_getNumDiagnostics(unit); I != N; ++I) {
CXDiagnostic Diag = clang_getDiagnostic(unit, I);
CXString String = clang_formatDiagnostic(Diag, clang_defaultDiagnosticDisplayOptions());
fprintf(stderr, "%s\n", clang_getCString(String));
clang_disposeString(String);
}
CXCursor cursor = clang_getTranslationUnitCursor(unit);
clang_visitChildren(
cursor,
[](CXCursor c, CXCursor parent, CXClientData client_data)
{
if(clang_Location_isFromMainFile(clang_getCursorLocation(c))){
unsigned loc = -1;
clang_getExpansionLocation(clang_getCursorLocation(c),NULL,&loc,NULL,NULL);
printf("%d: ",loc);
cout << clang_getCString(clang_getCursorSpelling(c))<< '+' << clang_getCString(clang_getCursorKindSpelling(clang_getCursorKind(c))) << endl;
}
return CXChildVisit_Recurse;
},
nullptr);
clang_disposeTranslationUnit(unit);
clang_disposeIndex(index);
}
Test File:
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int nthreads, tid;
/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel
{
/* Obtain thread number */
tid = omp_get_thread_num();
printf("Hello World from thread = %d\n", tid);
/* Only master thread does this */
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Number of threads = %d\n", nthreads);
}
} /* All threads join master thread and disband */
}
Textual AST:
`-FunctionDecl 0x7fbcd785a140 <helloworld.c:17:1, line:38:1> line:17:5 main 'int (int, char **)'
|-ParmVarDecl 0x7fbcd7859f38 <col:11, col:15> col:15 argc 'int'
|-ParmVarDecl 0x7fbcd785a020 <col:21, col:32> col:27 argv 'char **':'char **'
`-CompoundStmt 0x7fbcd785cd18 <line:18:1, line:38:1>
|-DeclStmt 0x7fbcd785a308 <line:19:1, col:18>
| |-VarDecl 0x7fbcd785a208 <col:1, col:5> col:5 used nthreads 'int'
| `-VarDecl 0x7fbcd785a288 <col:1, col:15> col:15 used tid 'int'
`-OMPParallelDirective 0x7fbcd785cce0 <line:22:1, col:44>
|-OMPPrivateClause 0x7fbcd785c538 <col:22, col:43>
| |-DeclRefExpr 0x7fbcd785a320 <col:30> 'int' lvalue Var 0x7fbcd785a208 'nthreads' 'int'
| `-DeclRefExpr 0x7fbcd785a340 <col:40> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
`-CapturedStmt 0x7fbcd785ccb8 <line:23:3, line:36:3>
`-CapturedDecl 0x7fbcd785c650 <<invalid sloc>> <invalid sloc> nothrow
|-CompoundStmt 0x7fbcd785cc90 <line:23:3, line:36:3> openmp_structured_block
| |-BinaryOperator 0x7fbcd785c8f0 <line:26:3, col:28> 'int' '='
| | |-DeclRefExpr 0x7fbcd785c848 <col:3> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
| | `-CallExpr 0x7fbcd785c8d0 <col:9, col:28> 'int'
| | `-ImplicitCastExpr 0x7fbcd785c8b8 <col:9> 'int (*)(void)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fbcd785c868 <col:9> 'int (void)' Function 0x7fbcd781f590 'omp_get_thread_num' 'int (void)'
| |-CallExpr 0x7fbcd785c9d0 <line:27:3, col:47> 'int'
| | |-ImplicitCastExpr 0x7fbcd785c9b8 <col:3> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x7fbcd785c910 <col:3> 'int (const char *, ...)' Function 0x7fbcd7847550 'printf' 'int (const char *, ...)'
| | |-ImplicitCastExpr 0x7fbcd785ca18 <col:10> 'const char *' <NoOp>
| | | `-ImplicitCastExpr 0x7fbcd785ca00 <col:10> 'char *' <ArrayToPointerDecay>
| | | `-StringLiteral 0x7fbcd785c930 <col:10> 'char [30]' lvalue "Hello World from thread = %d\n"
| | `-ImplicitCastExpr 0x7fbcd785ca30 <col:44> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7fbcd785c968 <col:44> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
| `-IfStmt 0x7fbcd785cc78 <line:30:3, line:34:5>
| |-BinaryOperator 0x7fbcd785caa0 <line:30:7, col:14> 'int' '=='
| | |-ImplicitCastExpr 0x7fbcd785ca88 <col:7> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7fbcd785ca48 <col:7> 'int' lvalue Var 0x7fbcd785a288 'tid' 'int'
| | `-IntegerLiteral 0x7fbcd785ca68 <col:14> 'int' 0
| `-CompoundStmt 0x7fbcd785cc58 <line:31:5, line:34:5>
| |-BinaryOperator 0x7fbcd785cb38 <line:32:5, col:36> 'int' '='
| | |-DeclRefExpr 0x7fbcd785cac0 <col:5> 'int' lvalue Var 0x7fbcd785a208 'nthreads' 'int'
| | `-CallExpr 0x7fbcd785cb18 <col:16, col:36> 'int'
| | `-ImplicitCastExpr 0x7fbcd785cb00 <col:16> 'int (*)(void)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fbcd785cae0 <col:16> 'int (void)' Function 0x7fbcd781f090 'omp_get_num_threads' 'int (void)'
| `-CallExpr 0x7fbcd785cbe0 <line:33:5, col:48> 'int'
| |-ImplicitCastExpr 0x7fbcd785cbc8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x7fbcd785cb58 <col:5> 'int (const char *, ...)' Function 0x7fbcd7847550 'printf' 'int (const char *, ...)'
| |-ImplicitCastExpr 0x7fbcd785cc28 <col:12> 'const char *' <NoOp>
| | `-ImplicitCastExpr 0x7fbcd785cc10 <col:12> 'char *' <ArrayToPointerDecay>
| | `-StringLiteral 0x7fbcd785cb78 <col:12> 'char [24]' lvalue "Number of threads = %d\n"
| `-ImplicitCastExpr 0x7fbcd785cc40 <col:40> 'int' <LValueToRValue>
Command to generate AST (change to emit-ast for the binary file):
clang -fopenmp -Xclang -ast-dump -L/usr/local/lib helloworld.c
Program Output:
0
13: omp.h+inclusion directive
14: stdio.h+inclusion directive
15: stdlib.h+inclusion directive
17: main+FunctionDecl
17: argc+ParmDecl
17: argv+ParmDecl
18: +CompoundStmt
19: +DeclStmt
19: nthreads+VarDecl
19: tid+VarDecl
22: +OMPParallelDirective
23: +UnexposedStmt
26: tid+DeclRefExpr
32: nthreads+DeclRefExpr