I have a VSCode project using CMake Tools that utilizes CMake presets. Each preset corresponds to a different target platform, and therefore includes a different set of source files to be compiled.
However, when I right-click a symbol that has different definitions for different CMake presets and pick "Go to definition", it always prompts me about which symbol I want to go to, including definitions from source files that are not included in the current preset.
Is there any way to limit Intellisense so that it only considers definitions from source files that will actually be compiled in the current CMake preset?
Here is a reproducible example. Please bear in mind in requires the interaction of several files, all of which should be placed in the same project folder, along with one file that should be placed in ./.vscode
. I created this example on macOS and on Windows or Linux it might have slightly different behavior that requires modifying some of the steps.
CMakePresets.json
:
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "platform1",
"cacheVariables": {
"PLATFORM_NAME": "platform1"
}
},
{
"name": "platform2",
"cacheVariables": {
"PLATFORM_NAME": "platform2"
}
}
]
}
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.20)
project(symbol_demo C)
if (PLATFORM_NAME STREQUAL "platform1")
add_executable(symbol_demo main.c platform1.c)
elseif (PLATFORM_NAME STREQUAL "platform2")
add_executable(symbol_demo main.c platform2.c)
else()
message(FATAL_ERROR "Unknown preset: ${PLATFORM_NAME}")
endif()
main.c
#include <stdio.h>
const char * platform_name();
int main(int argc, const char *argv[]) {
printf("Platform: %s", platform_name());
}
platform1.c
const char * platform_name() {
return "platform1";
}
platform2.c
const char * platform_name() {
return "platform2";
}
.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "CMake",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
To reproduce the issue:
Launch Visual Studio Code and ensure the both of these extensions are installed: "CMake Tools" and "C/C++", both by Microsoft.
Open a new window, pick the menu File
> Open Folder
and then select the folder containing these files.
Execute the command: CMake: Select Configure Preset
and then pick platform1
Open main.c
in VSCode.
Right click on platform_name
in line 6
Select Go to Definition
Note that a bit of UI pops up asking whether to go to the definition in platform1.c
or platform2.c
even though the configure preset is platform1
and platform2.c
is not currently set to be compiled.
In short: I've raised this to the CMake Tools maintainers: Can CMake Tools disqualify candidates for Go to Reference that aren't in any source file groups? #4486. A maintainer commented that the kind of improvement you're looking for is feasible, but not with cpptools' current API (which I suspected). It would be good to have, but it's not currently on the roadmap.
If you want to read about my shallow digging before I reached out to the maintainer, read on.
I can reproduce with CMake 4.0.1 installed and CMake Tools 1.21.31. There's an issue with CMake Tools right now that it doesn't seem to be honouring its cmake.exportCompileCommandsFile
setting right now, but even when I make it generated the compile_commands.json file by setting CMAKE_EXPORT_COMPILE_COMMANDS
, the behaviour was the same. I can also reproduce this in a personal project if I just duplicate a .cpp file.
I could be wrong, but I don't think this is a configuration issue. I think it's probably just a UX miss or a limitation of cpptools' design. See the "browse" for global symbol searches feature:
The set of properties used in conjunction with IntelliSense to identify all symbols in your code base. These properties are used by features such as Go to Definition/Declaration, global symbol search, or when the "default" IntelliSense engine is unable to resolve the #includes in your source files.
If you read a little down, it says:
path
: A list of paths who's source files are parsed to be used in global symbol searches
It doesn't seem to go as granular as to files- just directories. I might be misreading this, but this could be the sort of design limitation I hypothesized about above.
cpptools has an API that CMake Tools uses to act as a configuration provider. The API has provideFolderBrowseConfiguration
. I'm guessing CMake Tools' implementation when it comes to CMake Presets is based for each file in compile_commands.json on its include directories (from my brief skimming of the related code to verify this).
In that personal project I mentioned above, the source file I copied was in a directory that I added to the include path for its target (to allow for "private headers"). If I move that file copy to a directory not in that target's include directories (but directly above one of them), then when I Go To Definition
on a usage of a function defined in that file, then the copy file doesn't get surfaced as a candidate. At the same time, this doesn't appear to be a general rule. If I move it to certain other directories, in the project that aren't in the file's include path it gets picked up. You could try fiddling with include directories and see if you can get some partitioning somehow (give the platformN.c files different parent directories and play with moving those directories' parent directory around or above main.c).
I do think the combination of CMake Tools and cpptools can reasonably give a better experience here. CMake Tools definitely knows what source files are being built by targets in the generated buildsystem (see ${binaryDir}/.cmake/api/v1/reply/target-<target_name>-<hash>.json
. CMake Tools is using the includes
property in here, but I don't understand fully how things work. There's also a sources
member in there that could support this feature in the future). There might be complexities I'm not considering like accounting for things that are reachable through header inclusions (but I don't think that applies to this repro, since the other source file isn't being included), or implementations that come from imported libraries (libraries that are already built and which CMake doesn't know about the non-header source files of).
Note that cpptools does have C_Cpp.files.exclude
and C_Cpp.codeAnalysis.exclude
settings, but I don't think those would help for this particular use-case, unless you're willing to go edit that every time you switch between your presets.