Skip to content
Paul Smith edited this page Jan 2, 2019 · 33 revisions

Compiling with Visual Studio

If your project is compiled with MSVC, you may set the compiler driver in .ccls to clang-cl, and use the initialization option clang.extraArgs to pass three options: -fms-extensions -fms-compatibility -fdelayed-template-parsing.

See https://clang.llvm.org/docs/MSVCCompatibility.html

Compiling with GCC

GCC is very similar to Clang and most options are portable. If you are using options with GCC which are not available in Clang you can use clang.excludeArgs to remove them.

If you want to convince Clang to use the GCC header files you can add the --gcc-toolchain option either via the .ccls file or the `clang.extraArgs initialization option. For example to choose the GCC system header files you might add:

--gcc-toolchain=/usr

to your .ccls file.

Full indexing takes a long time / crushes my system

For large projects, full indexing can be a significant burden. Once the initial index is complete, future indexing will be proportional to the number of files changed.

While full indexing is required for complete cross-reference support, if you are making targeted edits on large codebases it may not be worthwhile to index the entire thing. There are various ways to reduce the impact and scope of indexing.

  • You can reduce the number of CPU cores used for indexing by setting the index.threads initialization option.

  • You can avoid indexing parts of the codebase you are not interested in by setting the index.initialBlacklist initialization option.

  • You can avoid indexing any of the codebase by default, and only indexing files as you open them, by setting index.initialBlacklist to ["."] (a regular expression that will match every file).

Note that when you edit a header file, ccls will automatically open the associated (by name) source file if one exists, and index it as well to locate definitions of symbols declared in the header file. For example, if you edit a file a.h and ccls can locate a file a.cpp then it will index that as well.

Can ccls be started directly?

Yes ccls can be run in stand-alone mode using the --index option. If your client uses clang.extraArgs be sure to use the same ones when invoking ccls in stand-alone mode, otherwise the flags mismatch will cause re-indexing when invoked from the client.

Here's an example:

ccls --index=$HOME/llvm --init='{"clang":{"extraArgs": ["--gcc-toolchain=/usr"]}}'

Does ccls support compile_flags.txt?

No. https://github.com/MaskRay/ccls/issues/110

Some C/C++ headers are not recognized

First, if you are Emacs company mode user make sure company-lsp is used: run M-x company-diag and make sureUsed backend: includes company-lsp.

Checking header locations

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})

Verify the Clang Resource Directory is correct

Use the --log-file command line option to generate a log file and look for the line initialize.cc:nnn I use -resource-dir=<path>. Make sure the path <path> still exists and you can locate the file <path>/include/stddef.h.

If not, then you need to use the clang.resourceDir initialization option to tell ccls where to find the Clang resource directory. See also Clang Resource Directory.

-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.

Adding system include paths is usually unnecessary, but when cross-compiling or in some odd situations 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 the .ccls file or add through clang.extraArgs.

Inspect index files

If you need to inspect the contents of the index files, use cacheFormat to specify JSON output format.

You can use jq to format the JSON file to be more readable:

jq . < /tmp/ccls/@tmp@c/a.cc.json

Project root detection

emacs-ccls locates the project root in the following order:

  • .ccls-root. If this file exists in any parent directory, that directory is treated as the project root.
  • (lsp--suggest-project-root) which in turn calls (projectile-project-root). Usually you 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 reads .ccls or compile_commands.json in the directory. If neither exists, ccls fallbacks to %clang $path.

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

Some projects may require more than 1000 file descriptors. Remember to increase RLIMIT_NOFILE.

ulimit -n 32768

/etc/security/limits.conf:

* hard  nofile    32768
* soft  nofile    32768

Diagnostics in headers

For efficiency, headers are compiled on their own (as if clang [options] a.h) to utilize Clang's preamble optimization.

For headers that are not self-contained (e.g. some /usr/include/bits/*.h which require a predefined macro) ccls emits diagnostics that you don't see when building the project.

// a.c
#define M
#include "a.h"

// a.h
#ifndef M
#error M not defined
#endif

One approach ccls explored before was to compile its import file a.c, but it caused severe performance issues for some projects. For smaller files this does not matter and checking for a header guard may help the situation.

Multi-version headers

A header can be included in different translation units with different compiler flags. It may behave differently via #ifdef. Ideally, there should be some mechanism to switch among different views (compiler flags) when you are editing the file.

For efficiency, a header is only indexed with one set of compiler flags, so either the #if block or #else is indexed, the other is left blank.

For macros, there is code to track different usage, thus you may find them able to jump to multiple targets. index.multiVersion is an experimental option to index every version to certify that the author is aware of this issue :) It may not be very useful though. It is of type number but not boolean as the author is also aware of other dimensions of the multi-version problem.

Completion and diagnostics

While most cross reference requests (textDocument/definition textDocument/references $ccls/member ...) are served by the index (query.h:DB) and have a global view, completion and diagnostics are single-TU features. They use unsaved buffers and require re-parsing for each new request.

Definitions

textDocument/definition jumps to the definitions (there can be many across many TUs, thinking of odr violation) if available. If the cursor is on a definition, jump to declarations instead.

When jumping from a dependent name in a template, ccls tries its best to get every target when the information is available. If the template is written in a main file and you instantiate it multiple times, it can assuredly find all target. However if the template is in a header, because by default a header is indexed for one TU, you won't get all targets. See the discussion about index.multiVersion.

  • 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

Hover

textDocument/hover is served by the index, which is built when you save the file. This model is simple to implement and has lower CPU cycles, but the drawback is that you don't get immediate hover information for newly-created declarations that are not in the index.

See the initialization option index.onChange.

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.

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.

Find callers. If parameter callee:true is specified, find callees instead.

Specify hierarchy:true to enable hierarchical view.

Find base classes/overriden functions. If parameter derived:true is specified, find derived classes/functions instead. It also works fine for jumping between primary template and partial specialization.

Specify hierarchy:true to enable hierarchical view.

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

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

If parameter kind:3 is specified, list member functions/functions in a namespace

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

Cases

On a laptop with one (i7-6600U CPU @ 2.60GHz, hyper-threading, nproc=4), 4 indexers. llvm+clang+extra+lld+compiler-rt (2018-06-10)

  • initial indexing: 1 hour; load: 20s
  • resident set size is about 1GiB (the peak RSS during indexing is below 2GiB)
  • du -sh .ccls-cache => 580M

radare2 (2017-06-10)

CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld meson . debug --buildtype=debug --prefix ~/.config/radare2/debug
ln -s debug/compile_commands.json
  • initial indexing: 78s; load: 3s
  • 300MiB
  • du -sh .ccls-cache => 107M

Note: ccls can serve LSP requests while it is doing the initial indexing of the project.

Clone this wiki locally