Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.
Boris Staletic edited this page Apr 3, 2018 · 33 revisions

Some C/C++ headers are not recognized

There are at least three sets of implicit include paths. They take effect without your -I option in .cquery or compile_commands.json

// a.cc
// system C header, usually in /usr/include
#include <stdio.h>
// system C++ header. The location varies among distributions, e.g. /usr/include/c++/{6,7.2.1}
#include <new>
// In Clang resource directory
#include <stddef.h>

Put a.cc in some directory with echo clang++ > .cquery. Open the file, you should be able to jump to stdio.h new stddef.h when you trigger textDocument/definition on the include lines.

Note that this might not work on Windows. To solve this, add the system include directories to compile_commands.json via your build system of choice using the INCLUDE environment variable (available after executing VsDevCmd.bat).

For CMake this can be achieved in a single line: target_include_directories(<target> SYSTEM PRIVATE $ENV{INCLUDE})

If -resource-dir is correct

If the initialization option cacheDirectory is /tmp/cquery, and the source file is /tmp/c/a.cc, jq . < /tmp/cquery/@tmp@c/a.cc.json to see if -resource-dir is correct, e.g. "-resource-dir=/home/ray/Dev/Util/cquery/build/debug/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/clang/5.0.1"

system C/C++ headers can be detected reliably. For Clang resource directory, there is logic in wscript to detect it when you run ./waf configure [OPTIONS]

  • For --bundled-clang=5.0.1: ../lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/clang/5.0.1 which is relative to the build/release/bin/cquery executable. The relative path of build/release/bin/cquery and build/release/lib/ cannot change, otherwise libclang.so used by cquery cannot find the Clang resource directory.
  • For --use-system-clang: it is recognized from -resource-dir option in the output of clang++ '-###' -xc /dev/null)
./waf configure --prefix /tmp/opt && ./waf install

-isystem

-isystem system include paths is usually unnecessary. But for cross compiling or on some bizarre system you may have to specify them. A simple approach other than trial and error (changing .cquery and restarting your editor) is to use c-index-test (if you use --bundled-clang, preferably the executable in the extracted tarball; if you link against system libclang, use something like /usr/bin/c-index-test)

build/debug/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/bin/c-index-test -index-file local /tmp/c/a.cc -isystem/usr/include/c++/7.3.0 -isystemyour_include_path2

Play with your -isystem options until you get a group of options that you can add to .cquery

If you want the cquery binary at a specific location use a symlink - do not move the binary itself.

Project root detection

For C++ projects, compile_commands.json is used by emacs-cquery to mark the project root. This is usually a symlink to the real compile_commands.json in a build directory:

proj
 build
  gen
    generated_file_in_build.cc
  compile_commands.json
 compile_commands.json -> build/compile_commands.json

In this example, the :rootUri of the generated C++ file is proj/build/ because of proj/build/compile_commands.json. However, the user wants it to be proj/. Customize cquery-project-root-matchers.

Includes

Here is an example.

include/a.h:

int bad;

a.cc:

int main(){return bad;}

.cquery:

%clang
%cpp -std=gnu++14
-Iinclude

cquery will save a file in cacheDirectory: jq . < /tmp/cquery/@tmp@c/a.cc.json

15
{
  "last_modification_time": 1520737513,
  "language": 1,
  "import_file": "/tmp/c/a.cc",
  "args": [
    "clang++",
    "-working-directory=/tmp/c",
    "-std=gnu++14",
    "-Iinclude",
    "/tmp/c/a.cc",
    "-resource-dir=/home/maskray/Dev/Util/cquery/build/debug/lib/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-14.04/lib/clang/6.0.0",
    "-Wno-unknown-warning-option",
    "-fparse-all-comments"
  ],
  "includes": [
    {
      "line": 0,
      "resolved_path": "/tmp/c/include/a.h"
    }
  ],
  "dependencies": [
    "/tmp/c/include/a.h"
  ],
  ...

Cquery reports AST deserialisation error when precompiled headers are used

Unfortunately, libclang is very picky when it comes to precompiled headers. Even if your system compiler and the bundled libclang are the same versions there will be problems. Namely, to avoid AST deresialisation errors while using PCH, you need to create them with the same toolchain which will be used inside cquery for completions.

However, making cquery use precompiled headers doesn't make much sense. Libclang will internally create the precompiled headers, but won't store them on permanent storage, so having precompiled headers upfront only speeds up the initial parse - precompiled headers shouldn't make a difference on subsequent parses.

Going back to "create precompiled headers with the same toolchain that will be used by cquery", you can still use precompiled headers, but you will be forced to use your system's libclang, not the one bundled with cquery (even though the bundled one is what's tested).

For reference, read Valloric/ycmd#892 and cquery-project/cquery#545.

Definitions

textDocument/definition can be used in many places. Some are current implementation details and may subject to change.

  • void foo(); A declaration jumps to the definition
  • void foo() {} The definition lists all declarations
  • A a; For variables of custom types, besides declarations of the variable, both the type and the variable jump to the declaration/definition of its type A
  • class C { jumps to declarations (and constructors/destructors)
  • a.field jumps to the member in the struct declaration
  • #include <map> jumps to the header
  • std::string a = "a"; takes you to the constructor. Many implicit constructors can also be jumped in this way.
  • a == b operator== for user defined operators
  • namespace ns { find original or extension namespaces
  • // ns::foo in comments, it recognizes the identifier around the cursor, approximately finds the best matching symbol and jumps to it; on ns, it jumps to the namespace

References

  • #include <iostream> lists all #include lines in the project pointing to the included file
  • [](){} lists all(?) lambda expressions thanks to implicit std::function move constructor
  • extern int a; If ReferenceContext.includeDeclaration is true, the definition and declarations are also listed.

$cquery/base

  • struct A:B{void f()override;}; lists B or B::f()

$cquery/derived

  • struct B{virtual void f();}; derived classes or virtual function overrides

$cquery/vars

  • A a; lists all instances of user-defined A.
  • int i; lists all instances of int.
(cquery-call-hierarchy nil) ; caller hierarchy
(cquery-call-hierarchy t)   ; callee hierarchy
(cquery-inheritance-hierarchy nil) ; base hierarchy
(cquery-inheritance-hierarchy t)   ; derived hierarchy

Recursively list members of a record type. 😂 nobody has implemented UI for the feature. Help wanted!