Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/modgraph/scanner.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,18 @@ std::expected<SourceUnit, ScanError> scan_file(const std::filesystem::path& file
++i;
}
if (name.empty()) continue;
// Partition import within the same module: prepend its name.
// Partition import within the same module: prepend the *base*
// module name. If the current TU itself owns a partition (e.g.
// its `export module foo:http;`), `u.provides->logicalName`
// already includes that suffix — concatenating naively would
// produce `foo:http:tls` instead of the intended `foo:tls`.
// Strip our own `:partition` first.
if (name.starts_with(":") && u.provides) {
name = u.provides->logicalName + name;
std::string base = u.provides->logicalName;
if (auto p = base.find(':'); p != std::string::npos) {
base.resize(p);
}
name = base + name;
}
u.requires_.push_back(ModuleId{name});
continue;
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/test_modgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,53 @@ export int answer();
std::filesystem::remove_all(dir);
}

TEST(Scanner, PartitionImportFromPrimaryInterface) {
// Primary module interface: `export module foo;` → logicalName = "foo".
// `import :tls;` resolves to "foo:tls".
auto dir = make_tempdir("mcpp-scanner");
write(dir / "src" / "foo.cppm", R"(export module foo;
import :tls;
)");
auto u = scan_file(dir / "src" / "foo.cppm", "pkg");
ASSERT_TRUE(u.has_value()) << u.error().format();
ASSERT_EQ(u->requires_.size(), 1u);
EXPECT_EQ(u->requires_[0].logicalName, "foo:tls");
std::filesystem::remove_all(dir);
}

TEST(Scanner, PartitionImportFromAnotherPartition) {
// Partition interface: `export module foo:http;` → logicalName = "foo:http".
// `import :tls;` must resolve to "foo:tls" (the sibling partition),
// NOT "foo:http:tls" (which is what a naive prepend produces).
auto dir = make_tempdir("mcpp-scanner");
write(dir / "src" / "http.cppm", R"(export module foo:http;
import :tls;
import :socket;
)");
auto u = scan_file(dir / "src" / "http.cppm", "pkg");
ASSERT_TRUE(u.has_value()) << u.error().format();
ASSERT_TRUE(u->provides.has_value());
EXPECT_EQ(u->provides->logicalName, "foo:http");
ASSERT_EQ(u->requires_.size(), 2u);
EXPECT_EQ(u->requires_[0].logicalName, "foo:tls");
EXPECT_EQ(u->requires_[1].logicalName, "foo:socket");
std::filesystem::remove_all(dir);
}

TEST(Scanner, PartitionImportWithDottedModuleName) {
// Dotted module names (xpkg-style, e.g. `mcpplibs.tinyhttps:http`)
// — only the colon-prefixed partition suffix is what we strip.
auto dir = make_tempdir("mcpp-scanner");
write(dir / "src" / "http.cppm", R"(export module mcpplibs.tinyhttps:http;
import :tls;
)");
auto u = scan_file(dir / "src" / "http.cppm", "pkg");
ASSERT_TRUE(u.has_value()) << u.error().format();
ASSERT_EQ(u->requires_.size(), 1u);
EXPECT_EQ(u->requires_[0].logicalName, "mcpplibs.tinyhttps:tls");
std::filesystem::remove_all(dir);
}

TEST(Scanner, RejectsConditionalImport) {
auto dir = make_tempdir("mcpp-scanner");
write(dir / "main.cpp", R"(import std;
Expand Down
Loading