When compile_commands.json contains some source files expressed as
relative paths, we can get duplicate responses to findDefinitions. The
responses only differ by the URI, which are different versions of the
same file:
"result": [ { ... "uri": "file:///home/emaisin/src/ls-interact/cpp-test/build/../src/first.h" }, { ... "uri": "file:///home/emaisin/src/ls-interact/cpp-test/src/first.h" } ]
In getAbsoluteFilePath, we try to obtain the realpath of the FileEntry
by calling tryGetRealPathName. However, this doesn't work for
FileEntries that don't actually open the file, because their
RealPathName field is never populated.
I changed getAbsoluteFilePath so that if tryGetRealPathName returns an
empty string, we try to find the real path using VFS.getRealPath. This
requires to pass the VFS all the way from ClangdServer.
I tried to write a test case similar to
TEST(GoToDefinition, RelPathsInCompileCommand)
that would trigger this issue, but I wasn't able to. No matter what I
do, the paths are returned by the FileManager as already normalized /
without the "..". There must be something different in the test case
than in real execution that I don't see.
Repro instructions are in bug #37963, though I can include them in the
commit message if you prefer.
With the recent performance regression due to getRealPath() mentioned in D51159, I think we should re-evaluate whether VFS::getRealPath(), which calls expensive real_path system call on real FS, was necessary to fix the bug mentioned in the patch summary. I think vfs::makeAbsolutePath with remove_dot_dot (without symlink resolution) should be sufficient to address the issue. The code completion latency has increased dramatically with the real_path call, so I would also expect Xrefs/findDefinition to slow down due to this. As SymbolCollector is not in a latency sensitive code paths, it might be OK for it to call real_path, but I think we should try to avoid using real_path elsewhere.
In general, it's unclear whether clangd should always resolve symlink (it might not always be what users want), and it should probably be a decision made by the build system integration. I think we would need a more careful design if we decide to handle symlinks in clangd.