New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support finding definition of function called from templates #174

Open
MaskRay opened this Issue Dec 22, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@MaskRay
Member

MaskRay commented Dec 22, 2017

void bar(int x) {}
template <class T>
void foo(T a) {
  bar(a);  // textDocument/definitions on `bar` does not return `void bar(int x)`
}

Function calls from (a function template or a member function of a class template or a member template) are not handled by clang-c/Index.h IndexerCallbacks::indexEntityReference and thus not processed by cquery's indexer.cc:OnIndexReference.

Maybe we need to traverse function/class/method templates and use functions like:

CINDEX_LINKAGE unsigned clang_getNumOverloadedDecls(CXCursor cursor);

to get possible candidates of callees (e.g. bar(a)). When templates are instantiated, the definition lookup might refer to other functions found via argument-dependent lookup. But an approach that does not handle ADL should be good enough in practice.

@MaskRay MaskRay changed the title from Support finding definition of function called from a template function/member function of a class template to Support finding definition of function called from templates Dec 22, 2017

@jacobdufault

This comment has been minimized.

Show comment
Hide comment
@jacobdufault

jacobdufault Dec 22, 2017

Member

If you can find a way to do this using libclang APIs, that'd be fantastic - I haven't had much luck though. In the past I've tried using the cursor-level APIs (ie, see DumpCursor) but found that libclang does not expose this information. Maybe it is possible if the template is instantiated, as you say, though.

Member

jacobdufault commented Dec 22, 2017

If you can find a way to do this using libclang APIs, that'd be fantastic - I haven't had much luck though. In the past I've tried using the cursor-level APIs (ie, see DumpCursor) but found that libclang does not expose this information. Maybe it is possible if the template is instantiated, as you say, though.

MaskRay added a commit to MaskRay/cquery that referenced this issue Dec 24, 2017

MaskRay added a commit to MaskRay/cquery that referenced this issue Dec 24, 2017

@MaskRay MaskRay closed this in 99220b4 Dec 24, 2017

MaskRay added a commit that referenced this issue Dec 24, 2017

@MaskRay

This comment has been minimized.

Show comment
Hide comment
@MaskRay

MaskRay Dec 24, 2017

Member

c9d6f5c 99220b4 5e1008e nearly fix the issue. But I reverted this feature in 8e194af due to a deadlock.

The first 3 commits resolve this issue in this manner:

In OnIndexDeclaration, CXIdxEntity_Template declarations are traversed to collect CXCursor_OverloadedDeclRef in the tree and resolve them in TemplateVisitor

1350
        // CXCursor_OverloadedDeclRef in templates are not processed by
        // OnIndexReference, thus we use TemplateVisitor to collect function
        // references.
        if (decl->entityInfo->templateKind == CXIdxEntity_Template) {
          // TODO put db and caller into client data
          TemplateVisitorData data;
          data.db = db;
          data.container = decl_cursor;
          decl_cursor.VisitChildren(&TemplateVisitor, &data);
        }

However, 8e194af reverted the change with a TODO break. Because I find that cquery may sometimes deadlock (an indexer thread gets stuck in a futex syscall called by pthread_setcancelstate, which in turn is transitively called by clang_indexTranslationUnit

1048

    case CXCursor_OverloadedDeclRef: {
      break; // TODO data->db seems to be incorrect and may cause deadlock

      unsigned num_overloaded = clang_getNumOverloadedDecls(cursor.cx_cursor);

I have changed indexerCount to 1 to ensure this is not caused by concurrent indexers.

Member

MaskRay commented Dec 24, 2017

c9d6f5c 99220b4 5e1008e nearly fix the issue. But I reverted this feature in 8e194af due to a deadlock.

The first 3 commits resolve this issue in this manner:

In OnIndexDeclaration, CXIdxEntity_Template declarations are traversed to collect CXCursor_OverloadedDeclRef in the tree and resolve them in TemplateVisitor

1350
        // CXCursor_OverloadedDeclRef in templates are not processed by
        // OnIndexReference, thus we use TemplateVisitor to collect function
        // references.
        if (decl->entityInfo->templateKind == CXIdxEntity_Template) {
          // TODO put db and caller into client data
          TemplateVisitorData data;
          data.db = db;
          data.container = decl_cursor;
          decl_cursor.VisitChildren(&TemplateVisitor, &data);
        }

However, 8e194af reverted the change with a TODO break. Because I find that cquery may sometimes deadlock (an indexer thread gets stuck in a futex syscall called by pthread_setcancelstate, which in turn is transitively called by clang_indexTranslationUnit

1048

    case CXCursor_OverloadedDeclRef: {
      break; // TODO data->db seems to be incorrect and may cause deadlock

      unsigned num_overloaded = clang_getNumOverloadedDecls(cursor.cx_cursor);

I have changed indexerCount to 1 to ensure this is not caused by concurrent indexers.

@MaskRay MaskRay reopened this Dec 24, 2017

@MaskRay MaskRay closed this in 6fb40a8 Dec 24, 2017

@MaskRay

This comment has been minimized.

Show comment
Hide comment
@MaskRay

MaskRay Dec 24, 2017

Member

The dead lock in #174 (comment) is resolved. I should be aware that ToFuncId invalidates IndexFunc* pointers.

cquery is still unable to find references in the following code snippet

template <class U>
struct D {
  void bar();
  U x;
};

template <class T>
void foo(T a) {
    D<T> e;
    e.bar(); // CallExpr + MemberRefExpr
    e.x; // MemberRefExpr
}
CompoundStmt 
  DeclStmt 
    VarDecl e
      TemplateRef D
      TypeRef T
  CallExpr 
    MemberRefExpr 
      DeclRefExpr e
  MemberRefExpr 
    DeclRefExpr e

The above is addressed.


template <class U>
struct D {
  U x;
  void bar();
};

template <class T>
void foo() {
    D<T>* e = new D<T>;
    e->x;
    e->bar();
}
Member

MaskRay commented Dec 24, 2017

The dead lock in #174 (comment) is resolved. I should be aware that ToFuncId invalidates IndexFunc* pointers.

cquery is still unable to find references in the following code snippet

template <class U>
struct D {
  void bar();
  U x;
};

template <class T>
void foo(T a) {
    D<T> e;
    e.bar(); // CallExpr + MemberRefExpr
    e.x; // MemberRefExpr
}
CompoundStmt 
  DeclStmt 
    VarDecl e
      TemplateRef D
      TypeRef T
  CallExpr 
    MemberRefExpr 
      DeclRefExpr e
  MemberRefExpr 
    DeclRefExpr e

The above is addressed.


template <class U>
struct D {
  U x;
  void bar();
};

template <class T>
void foo() {
    D<T>* e = new D<T>;
    e->x;
    e->bar();
}

@MaskRay MaskRay reopened this Dec 25, 2017

@MaskRay MaskRay closed this in 37ee7d3 Dec 31, 2017

MaskRay added a commit that referenced this issue Dec 31, 2017

@MaskRay MaskRay reopened this Dec 31, 2017

@MaskRay MaskRay closed this in d442d63 Dec 31, 2017

MaskRay added a commit that referenced this issue Dec 31, 2017

MaskRay added a commit that referenced this issue Dec 31, 2017

@MaskRay MaskRay reopened this Jan 2, 2018

MaskRay added a commit that referenced this issue Jan 3, 2018

Use cursor extent instead of cursor spelling range for CXCursor_Membe…
…rRefExpr #174

Members of non-concrete template types do not have useful spelling ranges (likely unexposed).

C<int> f; f.x // .x produces a MemberRefExpr which has a spelling range of `x`.

C<T> e; e.x // .x produces a MemberRefExpr which has a spelling range of `e` (weird).

To make `e.x` (MemberRefExpr with empty spelling name) able to find
definition, We use cursor extent (larger than spelling range) `e.x`. It
would be better if we could restrict the ranges to `.x` or just `x`.
Nevertheless, larger ranges are less specific, and should do no harm
because they will be overriden by more specific variable references `e`.

@MaskRay MaskRay added the libclang label Jan 13, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment