feat: add scan_module_decl() fallback for conditional module declarations (#373)
## Summary - Add `scan_module_decl()` — a lightweight preprocessor-based fallback that resolves module declarations inside `#if`/`#ifdef` conditionals. When `scan()` detects `need_preprocess=true`, this function runs clang's preprocessor to evaluate conditions and extract the actual module name. It stops lexing as soon as the module declaration is found, making it much cheaper than `scan_precise()`. - Integrate the fallback into `scan_dependency_graph()` for wave 0 source files, so conditional module declarations (e.g. `#ifdef USE_MODULES / export module M; / #endif`) are correctly registered in the dependency graph. - Add comprehensive test cases covering all C++20 module declaration forms from cppreference, including `scan_module_decl()` tests for conditional resolution and `scan_precise()` tests for module import semantics. ## Test plan - [x] All 310 unit tests pass (0 failures, 9 skipped) - [x] `scan()` tests cover: primary interface, implementation, dotted names, partitions, GMF, conditional module declarations, private module fragment - [x] `scan_module_decl()` tests cover: basic, conditional with `-D`, conditional with `#if` expression, GMF with conditional, implementation unit, dotted name, partition, no-module file - [x] `scan_precise()` tests cover: named import, multiple imports, dotted import, partition import, export-import, export-import partition, implementation import, GMF with import, mixed includes/imports, no-module file 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Improved detection of module declarations hidden by conditional compilation via a lightweight fallback scan. Resolved module vs. interface classification is cached to avoid repeated work and is used consistently in dependency mapping. * Better handling and classification of module imports, partitions, and global-fragment includes when building module relationships. * **Tests** * Added comprehensive unit tests covering module declaration extraction, fallback resolution under preprocessor guards, imports, partitions, includes, and macro-driven cases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
49
tests/unit/syntax/module_scan_fixture.h
Normal file
49
tests/unit/syntax/module_scan_fixture.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "test/test.h"
|
||||
#include "syntax/scan.h"
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
/// Helper that sets up a TestVFS with a main file and optional extra files,
|
||||
/// then calls the given scan function with standard C++20 arguments.
|
||||
struct ModuleScanFixture {
|
||||
llvm::IntrusiveRefCntPtr<TestVFS> vfs = llvm::makeIntrusiveRefCnt<TestVFS>();
|
||||
std::string main_path;
|
||||
std::vector<const char*> args;
|
||||
|
||||
// Non-copyable/non-movable: args stores raw pointers (main_path.c_str())
|
||||
// that would dangle after copy/move.
|
||||
ModuleScanFixture(const ModuleScanFixture&) = delete;
|
||||
ModuleScanFixture& operator=(const ModuleScanFixture&) = delete;
|
||||
ModuleScanFixture(ModuleScanFixture&&) = delete;
|
||||
ModuleScanFixture& operator=(ModuleScanFixture&&) = delete;
|
||||
|
||||
/// Create fixture with main file content and optional extra defines.
|
||||
ModuleScanFixture(llvm::StringRef filename,
|
||||
llvm::StringRef content,
|
||||
std::initializer_list<const char*> extra_args = {}) {
|
||||
main_path = TestVFS::path(filename);
|
||||
vfs->add(filename, content);
|
||||
args.push_back("clang++");
|
||||
args.push_back("-std=c++20");
|
||||
for(auto a: extra_args) {
|
||||
args.push_back(a);
|
||||
}
|
||||
args.push_back(main_path.c_str());
|
||||
}
|
||||
|
||||
void add_file(llvm::StringRef name, llvm::StringRef content = "") {
|
||||
vfs->add(name, content);
|
||||
}
|
||||
|
||||
ScanResult decl() {
|
||||
return scan_module_decl(args, TestVFS::root(), {}, nullptr, vfs);
|
||||
}
|
||||
|
||||
ScanResult precise() {
|
||||
return scan_precise(args, TestVFS::root(), {}, nullptr, vfs);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice::testing
|
||||
Reference in New Issue
Block a user