0

Suppose I have a Bazel project with this structure:

WORKSPACE
- includes
    - a.h
    - BUILD
- includes_b
    - b.h
    - BUILD

with the following files:

includes/BUILD

cc_library(
    name = "a",
    srcs = [],
    hdrs = [
        "a.h",
    ],
    deps = [],
    visibility = ["//visibility:public"]
)

includes/a.h

struct A{
  public:
  int val;
};

includes_b/BUILD

cc_library(
    name = "b",
    srcs = [],
    hdrs = [
        "b.h",
    ],
    deps = ["//includes:a"],
    visibility = ["//visibility:public"]
)

includes_b/b.h

#include "includes/a.h"

struct B{
  public:
  int val2;
  A a;
};

I am using linclang in Python to parse C++ source code: my script is the following:

import clang.cindex
import typing


def traverse(node, output_struct, file_parsed):
    print(node.spelling, node.kind)
    if node.kind == clang.cindex.CursorKind.STRUCT_DECL:
        if node.semantic_parent.spelling != "std":
            print("struct", node.spelling, "; parent: ",
                  node.semantic_parent.spelling)
            if node.semantic_parent.spelling == file_parsed:
                traverse.current_namespace = ""
            output_struct[node.spelling] = {
                "type": "struct",
                "attributes": [],
                "namespace": traverse.current_namespace
            }
    elif node.kind == clang.cindex.CursorKind.CLASS_DECL:
        if node.semantic_parent.spelling != "std":
            print("class", node.spelling, "; parent: ",
                  node.semantic_parent.spelling)
    elif node.kind == clang.cindex.CursorKind.ENUM_DECL:
        if node.semantic_parent.spelling != "std":
            print("field", node.spelling, "; parent: ",
                  node.semantic_parent.spelling)
            if node.semantic_parent.spelling == file_parsed:
                traverse.current_namespace = ""
        output_struct[node.spelling] = {
            "type": "enum",
            "attributes": [],
            "namespace": traverse.current_namespace
        }
    elif node.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
        if node.semantic_parent.spelling != "std":
            print("enum_const", node.spelling, "; enum value: ",
                  node.enum_value, "; parent: ", node.semantic_parent.spelling)
        output_struct[node.semantic_parent.spelling]["attributes"].append(
            (node.spelling, node.enum_value))

    elif node.kind == clang.cindex.CursorKind.NAMESPACE:
        print("namespace", node.spelling, "; parent: ",
              node.semantic_parent.spelling)
        if node.semantic_parent.spelling == file_parsed:
            traverse.current_namespace = node.spelling
        elif node.semantic_parent.spelling == traverse.current_namespace:
            traverse.current_namespace += "::" + node.spelling
    elif node.kind == clang.cindex.CursorKind.FIELD_DECL:
        # enum_type, enum_value, semantic_parent, lexical_parent
        if node.semantic_parent.spelling != "std":
            print("field declaration", node.spelling, node.type.spelling,
                  "; parent: ", node.semantic_parent.spelling)
        if node.semantic_parent.spelling in output_struct.keys():  # fpos not found
            output_struct[node.semantic_parent.spelling]["attributes"].append(
                (node.spelling, node.type.spelling))
    print("+++++++++++++++++Sons of " + node.spelling +"+++++++++++++++++++++++++++++++++")
    for child in node.get_children():
        print(child.spelling)
    print("+++++++++++++++++++++END SONS+++++++++++++++++++++++++++++")
    for child in node.get_children():
        traverse(child, output_struct=output_struct, file_parsed=file_parsed)


if __name__ == "__main__":
    index = clang.cindex.Index.create()
    file_parsed = "/absolute/path/clang_test/includes_b/b.h"
    parsed_struct = {}
    translation_unit = index.parse(
        file_parsed, args=['-x', 'c++', '-std=c++14'])
    traverse(translation_unit.cursor, parsed_struct, file_parsed)
    print(parsed_struct)

The problem is the following: the a attribute of struct B, instead of being recognised as A type, is regognised as int type, because apparently types unrecognised by clang are by default int.

The problem is that within the Bazel project, relative paths are used, so clang is unable to locate the header a.h parsing the file b.h. Prove of this is the fact that if I change the include with the absolute path, then everything works and a is recognised as of type A and also a.h is parsed.

How can I make libclang parse also the header a.h when analysing the b.h file? I was thinking to extract the working directory with os.getcwd() and then pre-pend it to the include string, but I cannot find the point where libclang analyse the include directory.

I have tried to use the clang.cindex.CursorKind.INCLUSION_DIRECTIVE but no node of this kind was defined.

How can I tell libclang where to search that header file?

Botje
  • 26,269
  • 3
  • 31
  • 41
roschach
  • 8,390
  • 14
  • 74
  • 124

0 Answers0