Zephyr: fixing LSP issues

Zephyr uses command-line arguments for GCC that the clangd LSP server doesn’t
understand. Here I present one approach how to fix this.

What is LSP?

LSP stands for “Language Server Protocol”, a JSON-based protocol that allows
tools to communicate with editors about language-specific information while
editing. This provides more precise insight into the code than just parsing, and
enables features like completion of variable/function/method/type names,
cross-references, and other advanced functionalities. LSP is widely used in
modern software development workflows.

Enable LSP

Out of the box, Zephyr doesn’t support LSP, but it’s easy enough to add. When
configuring for a board, you only need to ask CMake to create a compilation
database.

1
2
3
4
5
6
7
west build \
	--pristine \
	-b nucleo_f303re \
	-o "build.ninja" \
	-- \
	-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
	-DOVERLAY_CONFIG="nucleo_f303re.conf"

In line [BROKEN LINK: ref:cdb], we exactly do that. Once you’ve compiled your project
normally, you will now have such a compilation database in the “build/”
directory.

~/src/multi-board-zephyr$ ls -l build/compile_commands.json
-rw-r--r-- 1 holger holger 145793 Jan  5 08:40 build/compile_commands.json

This compilation database contains the exact set of source files that would be
compiled, along with the full set of compiler command-line arguments for each
file. As a result, an LSP daemon doesn’t have to parse e.g., Makefile,
meson.build, CMake files, etc. It simply looks at this one formalized database.

Use LSP in Emacs

On Linux, a good C/C++ LSP server is clangd. I currently use clangd-15, so I
tell Emacs / eglot about it:

  (add-to-list 'eglot-server-programs '(c-mode  .  ("clangd-15" "-j=2" "--clang-tidy")))
  (add-to-list 'eglot-server-programs '(c++-mode . ("clangd-15" "-j=2" "--clang-tidy")))

Observing the first errors

Now all is fine, I can start eglot ("M-x eglot"). All is well!

… but oh no, even a miniature project already shows an error:


Note the “!!” in the left fringe.

But what are these errors?


It turns out that Zephyr uses some command-line options that the GCC Compiler
doesn’t understand. The CLANG compiler (when compiling) ignores them. But not
the CLANGD language server. That one will bark about the not understood
command-line options.

In effect, you’ll see errors in any of your source files. However, in reality,
there aren’t any errors present.

Fixing these errors

Now, clangd takes all of the command-line arguments from the compilation
database. So after configuring, we simply modify the compilation database
directly. Therefore, we define a Makefile target for this:

.PHONY:: fix_lsp_compilation_database
fix_lsp_compilation_database:
	sed -i 's/--param=min-pagesize=0//g' build/compile_commands.json
	sed -i 's/--specs=picolibc.specs//g' build/compile_commands.json
	sed -i 's/-fno-defer-pop//g' build/compile_commands.json
	sed -i 's/-fno-freestanding//g' build/compile_commands.json
	sed -i 's/-fno-printf-return-value//g' build/compile_commands.json
	sed -i 's/-fno-reorder-functions//g' build/compile_commands.json
	sed -i 's/-mfp16-format=ieee//g' build/compile_commands.json

and call it directly after we configured for a specific board:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local: .west/config
	west build \
		--pristine \
		-b local \
		-o "build.ninja" \
		-- \
		-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
		-DOVERLAY_CONFIG="boards/arm/local/local_defconfig" \
		-DBOARD_ROOT=.
	$(MAKE) --no-print-directory fix_lsp_compilation_database
	west build

like this is done here in line 10.