Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.
Fangrui Song edited this page Mar 11, 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",
    "-I./include",
    "/tmp/c/a.cc",
    "-resource-dir=/home/maskray/Dev/Util/cquery/build/debug/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/clang/5.0.1",
    "-Wno-unknown-warning-option",
    "-fparse-all-comments"
  ],

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.

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