jacobdufault / cquery

C/C++ language server supporting multi-million line code base, powered by libclang. Emacs, Vim, VSCode, and others with language server protocol support. Cross references, completion, diagnostics, semantic highlighting and more
MIT License
2.35k stars 163 forks source link

Overloaded functions not resolved properly inside a template #806

Open Kelvin-Ng opened 6 years ago

Kelvin-Ng commented 6 years ago

Consider the follow code:

#include <iostream>

template <typename T>
void hello(const T& x) {
    std::cout << "Hello " << x << std::endl;
}

void hello(const int& x) {
    std::cout << "Hello " << (x + 1) << std::endl;
}

template <typename T>
void hello2(const T& x) {
    hello(x);
}

int main() {
    hello2(1);
    hello2("abc");
}

while compile_commands.json is:

[
{ "directory": "/data/kelvin/test_cquery2", "file": "test.cpp", "output": "/tmp/test-6a1c53.o", "arguments": ["/data/opt/llvm-7.0.0-bottle/bin/clang-7", "-xc++", "test.cpp", "--driver-mode=g++", "-Wall", "-std=c++14", "-o", "test", "--target=x86_64-unknown-linux-gnu"]}
]

When I ask for jumping to the definition of hello() inside the body of hello2(), it always jump to template <typename T> void hello(const T& x). However, it should list both template <typename T> void hello(const T& x) and void hello(const int& x) and allow me to choose one of them to jump to.

Kelvin-Ng commented 6 years ago

I have fixed it by this patch:

diff --git a/src/messages/text_document_definition.cc b/src/messages/text_document_definition.cc
index ad688ea..7cd9bf1 100644
--- a/src/messages/text_document_definition.cc
+++ b/src/messages/text_document_definition.cc
@@ -82,7 +82,6 @@ struct Handler_TextDocumentDefinition
               spell.range.Contains(target_line, target_column)) {
             on_def = spell;
             uses.clear();
-            return false;
           }
           // We use spelling start and extent end because this causes vscode
           // to highlight the entire definition when previewing / hoving with
@@ -101,8 +100,6 @@ struct Handler_TextDocumentDefinition
           uses.push_back(*on_def);
       }
       AddRange(&out.result, GetLsLocations(db, working_files, uses));
-      if (!out.result.empty())
-        break;
     }

     // No symbols - check for includes.
Kelvin-Ng commented 6 years ago

Here is a similar issue:


template <typename T>
class Foo {
  public:
    int x;
    int y;
};

template <>
class Foo<int> {
  public:
    int x;
    int z;
};

template <typename T>
void bar() {
    Foo<T> foo;
    foo.x = 5;
}

int main() {
    bar<int>(); // -- (1)
    bar<std::string>(); // -- (2)
}

If (1) is chosen, with my patch, it lists both the int x definitions when looking for definition of foo.x. However, if (2) is chosen, even with my patch, it lists only the int x definition of the general version. Is it an expected behavior? (because template<> class Foo<int> actually does not exist if it is never used?)