Skip to content
Fangrui Song edited this page Sep 7, 2018 · 33 revisions

Some C/C++ headers are not recognized

Make sure company-lsp is used

M-x company-diag, make sureUsed backend: includes company-lsp.

There are at least three sets of implicit include paths. They take effect without your -I option in .ccls 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 lib/clang/7.0.0, lib/clang/7.0.0/include/stddef.h
#include <stddef.h>

Put a.cc in some directory with echo clang++ > .ccls. 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})

Check if -resource-dir is correct

Check Clang_EXECUTABLE in your CMakeCache.txt. The output of command $Clang_EXECUTABLE -print-resource-dir will be passed to -DDEFAULT_RESOURCE_DIRECTORY. Make sure you can locate include/stddef.h in the resource directory.

Read Initialization options how to set "cacheFormat": "json". If "cacheDirectory": "/tmp/ccls", and the source file is /tmp/c/a.cc, run jq . < /tmp/ccls/@tmp@c/a.cc.json to see if -resource-dir is correct, e.g. "-resource-dir=/home/ray/ccls/Debug/lib/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.04/lib/clang/6.0.0"

-isystem

ccls infers system search paths (e.g. /usr/include). The underneath mechanism is similar to that of clang -v -E -x c++ /dev/null.

-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 .ccls and restarting your editor) is to use c-index-test.

Debug/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-16.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 .ccls

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

If you want to specify additional search paths:

  • print '%clang\n%cpp -std=gnu++17\n-isystem/tmp/include' > .ccls
  • emacs-ccls: (setq ccls-extra-init-params '(:clang (:extraArgs ["-isystem", "/tmp/include"])))

-std=c++1z -std=c++17 bits/unordered_map.h

In C++17 mode, it is possible to cause clang to crash when bits/unordered_map.h is indexed. See https://bugs.llvm.org/show_bug.cgi?id=37695 for details. The workaround is to add -D__cpp_deduction_guides=0 -Wno-macro-redefined to the initialization option clang.extraArgs

In Emacs, it is:

(setq ccls-extra-init-params
  '(:clang (:extraArgs ("-D__cpp_deduction_guides=0" "-Wno-macro-redefined"))))

Project root detection

emacs-ccls locates the project root with ccls-project-root-matchers:

  • .ccls-root. If this file exists in any parent directory, that directory is treated as the project root.
  • (projectile-project-root). Then this function is called. You likely don't want /usr/include/c++/8/algorithm to be treated as in the project /usr/include/c++/8/, (setq projectile-require-project-root t) inhibits the behavior.

The root directory is sent to ccls (the language server) through the rootUri field in the initialize request. ccls finds .ccls or compile_commands.json in the directory.

proj
  .ccls-root  # Use this file if you want subproject files to be associated with the root project
  compile_commands.json
  subproj0  # without .ccls-root, files will be associated with this root directory
    .git
  subproj1
    .git

Maximum number of file descriptors

When indexing ccls itself, some files require more than 1000 file descriptors. Remember to increase RLIMIT_NOFILE.

ulimit -n 32768

/etc/security/limits.conf:

* hard  nofile    32768
* soft  nofile    32768

Includes

Here is an example.

include/a.h:

int bad;

a.cc:

int main(){return bad;}

.ccls:

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

ccls will save a file in cacheDirectory: jq . < /tmp/ccls/@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/ccls/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"
  ],
  ...

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.
  • If no references is found but the point is on the first line, list #include lines referencing current file.

$ccls/memberHierarchy flat:true derived:false

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

textDocument/implementation

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

$ccls/vars

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

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

Clone this wiki locally