Skip to content

依赖解析:命名空间内的包名为 version 时,点分依赖被误判为单个依赖配置 #918

Closed

Description

@julixian

问题

mcpp 2026.11.9.1 解析官方推荐的不加引号的点分依赖名时,如果命名空间下有一个包叫 version,会把整个命名空间表误认成单个依赖配置。

实际项目中的声明:

[dependencies]
gpp.core = { path = "../GalTranslPP" }
gpp.version = { path = "../GPPVersion" }

mcpp emit build-database --format json 对包含上述依赖的成员返回:

MCPP_BUILD_DATABASE_PLAN_FAILED: GPPGUI: workspace member 'GPPGUI':
GPPGUI/mcpp.toml: error: [dependencies."gpp"] must specify 'path', 'version', or 'git'

同样的问题发生在 CLI 和 Updater 成员中,它们也依赖 gpp.version。不包含这些普通依赖的 Core 和 Version 成员仍能被描述,结果是只有部分编译数据库可用。

对 IDE 的实际影响

mcppls 0.21.0 的诊断包记录了以上 producer-partial 错误,调用的 mcpp 为 2026.11.9.1,emit build-database 退出码为 1。

GUI 成员缺少有效编译命令后,打开 GPPGUI/UpdateWidget.cpp,引擎使用了带 Core 配置的命令:只有 QtCore 的包含路径和 QT_CORE_LIB,没有 QtWidgets、QtGui 和 ElaWidgetTools 的路径,因此出现:

UpdateWidget.h:3:10: fatal error: 'QWidget' file not found

QtWidgets/QWidget 文件实际存在。这里的 Qt 报错是依赖解析失败后的下游现象,根因不在 Qt 安装或头文件路径本身。

原因定位

v2026.11.9.1 的 modules/manifest/src/toml.cppm 中:

auto looks_like_inline_dep_spec = [](const t::Table& sub) {
    if (sub.empty()) return false;
    for (auto& [sk, sv] : sub)
        if (sk == "path" || sk == "version" || sk == "git" || sk == "workspace")
            return true;
    return false;
};

这里只检查键名,没有检查 sv 的类型。

TOML 将 gpp.version = { path = "../GPPVersion" } 解析成 gpp 命名空间表,其 version 子项的值是一个表。上述函数看到 version 键就返回 true,后续把 gpp 当作依赖,并尝试从其直接子项读取字符串 path/version/git。由于此时 version 实际是表,最终报 dependencies."gpp" 缺少来源。

函数前的注释假设 “no package is named version / path / git / workspace”,而 gpp.version 是实际存在的正常包名。当前 main 上仍能看到同样的键名判断。

可供独立复现的最小配置

下面是从实际项目缩减的示例;本次没有额外执行该独立示例,也没有运行构建。实际观察来自上述诊断包和对应版本源码核对。

目录:

repro/
  app/
    mcpp.toml
    main.cpp
  version/
    mcpp.toml
    version.cpp

app/mcpp.toml:

[package]
name = "app"
version = "0.1.0"
standard = "c++23"

[dependencies]
gpp.version = { path = "../version" }

[targets.app]
kind = "bin"
main = "main.cpp"

app/main.cpp:

int main() { return 0; }

version/mcpp.toml:

[package]
namespace = "gpp"
name = "version"
version = "0.1.0"
standard = "c++23"

[build]
sources = ["version.cpp"]

[targets.version]
kind = "lib"

version/version.cpp:

int example_version() { return 1; }

在 app 目录执行:

mcpp emit build-database --format json

预期应解析为依赖 gpp.version,而不是名为 gpp 的单个依赖。

预期修复方向

判别依赖配置和命名空间表时,应结合来源字段的值类型,不能只因存在同名键就判定为依赖配置。例如本例的 version 是表,应该继续作为命名空间内的包处理。建议覆盖包名为 version/path/git/workspace 的情况,并保留对真正依赖配置的错误诊断。

"gpp.version" = { path = "../GPPVersion" } 可以在结构上避开这次误判,但没有额外验证;而且官方文档从 2026.11.8.1 起已建议将带引号的点分键改成不带引号的形式,所以不应要求用户靠恢复旧写法绕过。

环境

  • Windows x64
  • mcpp 2026.11.9.1
  • mcppls 0.21.0
  • 项目工具链 Clang 23.1.3,目标 x86_64-pc-windows-msvc
  • mcppls 诊断包生成时间:2026-11-10T07:31:02.065Z

Activity

  1. speak-agent commented on Nov 10, 2026

    @speak-agent
    Member

    mcpp 2026.11.10.2(#920)修正了此问题。

    依赖表仅当 path / version / git 的值为字符串、或 workspace 的值为布尔时,才被视为单个依赖说明;同名键的值为表时,该键是命名空间中的包。因此

    [dependencies]
    gpp.core    = { path = "../GalTranslPP" }
    gpp.version = { path = "../GPPVersion" }

    声明 gpp.core 与 gpp.version 两个包,[workspace.dependencies] 同理。emit build-database 可完整描述依赖 gpp.version 的成员。

    验证:单元测试 Manifest/APackageNamedLikeASpecField;e2e 1074 以 issue 中的最小配置构建、运行并生成编译数据库(在 2026.11.10.1 上复现 [dependencies."gpp"] must specify ...,在 2026.11.10.2 上通过)。

    xlings install mcpp@2026.11.10.2 可获得此版本。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions