💨

VSCodeでIntelliSense用に設定しているC/C++のマクロが有効にならない問題の解決

2024/02/19に公開

VSCodeのC/C++モードのIntelliSenseが意図しない動作をしていた問題を解決できたのでメモ。

問題

  • C/C++モードの設定ファイル c_cpp_properties.jsondefines に、定義済みマクロとして認識してほしいマクロを記載しているが、IntelliSenseにそのマクロが認識されない

c_cpp_properties.json の記載例:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/usr/include/json-c",
                "/usr/include/cutter"
            ],
            "defines": [],
            "cStandard": "gnu99",
            "cppStandard": "gnu++17",
            "intelliSenseMode": "linux-gcc-x64",
            "configurationProvider": "ms-vscode.makefile-tools",
        }
    ],
    "version": 4
}

原因

  • configurationProvider で IntelliSense 用の設定ファイルが提供されている .c ファイルについては、c_cpp_properties.json の設定が使われない項目がある。defines もその項目に該当する。
     - 参考リンク https://code.visualstudio.com/docs/cpp/customize-default-settings-cpp#_system-include-pathdefines-resolution-strategies
  • このケースでは、ms-vscode.makefile-toolsが指定されており、Makefile Tools が事前に make --dry-run を実行したログを元に、各 .c ファイルについてコンパイルオプション等を記録して、その設定を利用して IntelliSense が動作している
  • つまり、Makefileのビルド対象に入っている .c ファイルについては、defines の設定値は考慮されない

対処方法

  • mergeConfigurations を設定し、configuration provider の設定値と c_cpp_properties.jsonの設定値をマージするように設定する

設定の例:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/usr/include/json-c",
                "/usr/include/cutter"
            ],
            "defines": [],
            "cStandard": "gnu99",
            "cppStandard": "gnu++17",
            "intelliSenseMode": "linux-gcc-x64",
            "configurationProvider": "ms-vscode.makefile-tools",
            "mergeConfigurations": true,
        }
    ],
    "version": 4
}

雑感

対処方法自体は簡単なものだったが、configuration provider という概念を理解していないために苦労した。
結局のところ、configuration provider とはコンパイルオプション等の configuration を C/C++モード(IntelliSense含む) に提供してくれる仕組みのことで、makefile-tools の場合には make --dry-run を裏で実行した出力を解析し、C/C++モード に与える設定ファイルを生成しているだけ。

settings.json に下記のような設定を1行加えると、その内実を確認できる。

    "makefile.extensionOutputFolder": "${workspaceFolder}/.vscode/makefile-tools",

これを書き加えて、コマンドパレットで "Makefile: Reset the Makefile Tools Extension workspace (略)" を実行してしばらく待つと、${workspaceFolder}/.vscode/makefile-tools/configurationCache.log というファイルが作成される。
これが configuration provider (makefile-tools) によって作成された IntelliSense に読み込ませる設定となっている。
中身はJSON形式で記載されているので、jq などで表示すれば何が書かれているかは簡単に解読できる。
この中で、各 .c ファイルについて下記のようなエントリが作成されている。

      [
        "/path/to/file.c",
        {
          "uri": {
            "$mid": 1,
            "fsPath": "/path/to/file.c",
            "path": "/path/to/file.c",
            "scheme": "file"
          },
          "configuration": {
            "defines": [
              "HAVE_CONFIG_H",
            ],
            "includePath": [],
            "forcedInclude": [],
            "compilerPath": "/usr/bin/gcc",
            "compilerArgs": [

冒頭の問題に戻ると、c_cpp_properties.json の defines の設定値が使われないのは、この configuration provider (makefile-tools) によって作成された値が利用されているからである。c_cpp_properties.json に記載されているのは、あくまでデフォルト値という扱いで、configuration provider から与えられている場合にはそちらが優先される。

参考

Discussion