diff --git a/clang/include/clang/Basic/SourceLocation.h b/clang/include/clang/Basic/SourceLocation.h --- a/clang/include/clang/Basic/SourceLocation.h +++ b/clang/include/clang/Basic/SourceLocation.h @@ -57,6 +57,8 @@ private: friend class ASTWriter; friend class ASTReader; + friend class SimpleASTWriter; + friend class SimpleASTReader; friend class SourceManager; static FileID get(int V) { @@ -86,6 +88,7 @@ class SourceLocation { friend class ASTReader; friend class ASTWriter; + friend class SimpleASTReader; friend class SourceManager; friend struct llvm::FoldingSetTrait; diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -1782,6 +1782,8 @@ private: friend class ASTReader; friend class ASTWriter; + friend class SimpleASTReader; + friend class SimpleASTWriter; llvm::MemoryBufferRef getFakeBufferForRecovery() const; SrcMgr::ContentCache &getFakeContentCacheForRecovery() const; diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -38,6 +38,9 @@ namespace clang { class ASTContext; class ASTReader; +class SimpleASTReader; +class SimpleASTWriter; +class SimpleModuleCache; class CodeCompleteConsumer; class DiagnosticsEngine; class DiagnosticConsumer; @@ -118,6 +121,13 @@ /// The ASTReader, if one exists. IntrusiveRefCntPtr TheASTReader; + /// The simple ASTReader, if one exists. + std::shared_ptr TheSimpleASTReader; + /// The simple ASTWriter, if one exists. + std::shared_ptr TheSimpleASTWriter; + /// The simple module cache, if one exists. + std::shared_ptr TheSimpleModuleCache; + /// The module dependency collector for crashdumps std::shared_ptr ModuleDepCollector; @@ -515,6 +525,12 @@ IntrusiveRefCntPtr getASTReader() const; void setASTReader(IntrusiveRefCntPtr Reader); + std::shared_ptr getSimpleASTReader() const; + std::shared_ptr getSimpleASTWriter() const; + + std::shared_ptr getSimpleModuleCache(); + void setSimpleModuleCache(std::shared_ptr Cache); + std::shared_ptr getModuleDepCollector() const; void setModuleDepCollector( std::shared_ptr Collector); @@ -769,6 +785,8 @@ } void createASTReader(); + void createSimpleASTReader(); + void createSimpleASTWriter(); bool loadModuleFile(StringRef FileName); diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -1154,6 +1155,8 @@ /// Returns the FileID for the preprocessor predefines. FileID getPredefinesFileID() const { return PredefinesFileID; } + std::set getAffectingModuleMaps(Module *M); + /// \{ /// Accessors for preprocessor callbacks. /// diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -68,6 +68,8 @@ std::vector Includes; std::vector MacroIncludes; + bool ScanningMode = false; + /// Initialize the preprocessor with the compiler and target specific /// predefines. bool UsePredefines = true; diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -246,7 +246,7 @@ Flags &= ~Flag; } - /// Return the internal represtation of the flags. + /// Return the internal representation of the flags. /// /// This is only intended for low-level operations such as writing tokens to /// disk. @@ -254,6 +254,14 @@ return Flags; } + /// Set the internal representation of the flags. + /// + /// This is only intended for low-level operations such as writing tokens to + /// disk. + void setFlags(unsigned Flags) { + this->Flags = Flags; + } + /// Set a flag to either true or false. void setFlagValue(TokenFlags Flag, bool Val) { if (Val) diff --git a/clang/include/clang/Serialization/Simple/ASTReader.h b/clang/include/clang/Serialization/Simple/ASTReader.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/Simple/ASTReader.h @@ -0,0 +1,195 @@ +#ifndef LLVM_CLANG_SERIALIZATION_SIMPLE_ASTREADER_H +#define LLVM_CLANG_SERIALIZATION_SIMPLE_ASTREADER_H + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/ExternalPreprocessorSource.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Sema/ExternalSemaSource.h" +#include "clang/Serialization/Simple/ModuleCache.h" +#include "clang/Serialization/Simple/ModuleFile.h" +#include "clang/Serialization/Simple/ModuleManager.h" + +namespace clang { +/// The evil twin of ASTReader. +class SimpleASTReader : public ExternalPreprocessorSource, + public ExternalPreprocessingRecordSource, + public ExternalHeaderFileInfoSource, + public ExternalSemaSource, + public IdentifierInfoLookup, + public ExternalSLocEntrySource { + using SimpleModuleManager = serialization::SimpleModuleManager; + using SimpleModuleFile = serialization::SimpleModuleFile; + + std::shared_ptr Cache; + + SimpleModuleManager ModuleMgr; + +public: + // Custom { + explicit SimpleASTReader(std::shared_ptr Cache) + : Cache(std::move(Cache)) {} + + SimpleModuleManager &getModuleManager() { return ModuleMgr; } + + SimpleModuleCache &getCache() { return *Cache; } + + bool readAST(StringRef ModuleName, CompilerInstance &CI); + + void readASTCore(StringRef Name, SimpleModuleFile *ImportedBy, + llvm::SmallVectorImpl &Loaded); + void readASTBlock(SimpleModuleFile &F, CompilerInstance &CI); + + using InputFileVisitorTy = llvm::function_ref; + using TopLevelModuleMapVisitorTy = + llvm::function_ref; + + void visitInputFiles(SimpleModuleFile &MF, bool IncludeSystem, bool Complain, + InputFileVisitorTy Visitor); + + void visitTopLevelModuleMaps(SimpleModuleFile &MF, + TopLevelModuleMapVisitorTy Visitor); + + // Custom } + + // clang-format off + + // ExternalPreprocessorSource { + void ReadDefinedMacros() override {} + void updateOutOfDateIdentifier(IdentifierInfo &II) override {} + IdentifierInfo *GetIdentifier(unsigned int ID) override { return {}; } + Module *getModule(unsigned int ModuleID) override { return {}; } + // ExternalPreprocessorSource } + + // ExternalPreprocessingRecordSource { + PreprocessedEntity *ReadPreprocessedEntity(unsigned int Index) override { return {}; } + std::pair findPreprocessedEntitiesInRange(SourceRange Range) override { return {}; } + Optional isPreprocessedEntityInFileID(unsigned Index, FileID FID) override { return {}; } + SourceRange ReadSkippedRange(unsigned int Index) override { return {}; } + // ExternalPreprocessingRecordSource } + + // ExternalHeaderFileInfoSource { + HeaderFileInfo GetHeaderFileInfo(const FileEntry *FE) override { return {}; } + // ExternalHeaderFileInfoSource } + + // ExternalSemaSource { + // ExternalSemaSource } + + // IdentifierInfoLookup { + IdentifierInfo *get(StringRef Name) override { return {}; } + IdentifierIterator *getIdentifiers() override { return {}; } + // IdentifierInfoLookup } + + // ExternalSLocEntrySource { + bool ReadSLocEntry(int ID) override { return false; } + std::pair getModuleImportLoc(int ID) override { return {}; } + // ExternalSLocEntrySource } + + // clang-format on + + class ASTTransfer { + CompilerInstance &A; + Preprocessor &APP; + SourceManager &ASM; + FileManager &AFM; + HeaderSearch &AHS; + + CompilerInstance &B; + Preprocessor &BPP; + SourceManager &BSM; + FileManager &BFM; + HeaderSearch &BHS; + + const Module *BModule; + const SimpleModuleFile *AModuleFile; + + llvm::StringSet<> TranslatedModules; + + public: + ASTTransfer(CompilerInstance &A, CompilerInstance &B, + StringRef BModuleName); + + void translateFileManager(); + const FileEntry &translate(const FileEntry &BFE); + FileEntryRef translate(FileEntryRef BFE); + const DirectoryEntry &translate(const DirectoryEntry &BDE); + + void translateSourceManager(); + SourceLocation translate(SourceLocation Loc) const; + SourceLocation::UIntTy translate(SourceLocation::UIntTy Offset, + const SimpleModuleFile *MF = nullptr); + Optional translate(const SrcMgr::FileInfo &BFI); + SrcMgr::ContentCache *translate(const SrcMgr::ContentCache &BCC); + int translateSLocEntryID(int EntryID, const SimpleModuleFile *MF = nullptr); + + void translatePreprocessor(); + void translateModule(); + Module &translate(Module &BMod); + const Module &translate(const Module &BMod); + Module::Header translate(const Module::Header &BHeader); + std::vector translate(Module::submodule_iterator Begin, + Module::submodule_iterator End); + Module::UnresolvedHeaderDirective + translate(const Module::UnresolvedHeaderDirective &BD); + Module::ExportDecl translate(const Module::ExportDecl &BED); + ModuleId translate(const ModuleId &BId); + Module::UnresolvedExportDecl + translate(const Module::UnresolvedExportDecl &BUED); + Module::LinkLibrary translate(const Module::LinkLibrary &X); + Module::UnresolvedConflict translate(const Module::UnresolvedConflict &X); + Module::Conflict translate(const Module::Conflict &X); + IdentifierInfo &translate(IdentifierInfo &BII); + Token translate(const Token &BTok); + MacroInfo &translate(MacroInfo &BMI); + + template Optional translate(const Optional &BO) { + if (!BO) + return None; + return translate(*BO); + } + + template const T *translate(const T *BP) { + if (!BP) + return nullptr; + return &translate(*BP); + } + + template T *translate(T *BP) { + if (!BP) + return nullptr; + return &translate(*BP); + } + + template + llvm::PointerUnion translate(llvm::PointerUnion BPU) { + if (!BPU) + return nullptr; + if (BPU.template is()) + return translate(BPU.template get()); + return translate(BPU.template get()); + } + + template + SmallVector translate(const SmallVector &BV) { + SmallVector AV; + AV.reserve(BV.size()); + for (const T &Entry : BV) + AV.push_back(translate(Entry)); + return AV; + } + + template + llvm::SmallSetVector + translate(const llvm::SmallSetVector &BSSV) { + llvm::SmallSetVector ASSV; + for (const auto &Entry : BSSV) + ASSV.insert(translate(Entry)); + return ASSV; + } + }; +}; +} // namespace clang + +#endif // LLVM_CLANG_SERIALIZATION_SIMPLE_ASTREADER_H diff --git a/clang/include/clang/Serialization/Simple/ASTWriter.h b/clang/include/clang/Serialization/Simple/ASTWriter.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/Simple/ASTWriter.h @@ -0,0 +1,36 @@ +#ifndef LLVM_CLANG_SERIALIZATION_SIMPLE_ASTWRITER_H +#define LLVM_CLANG_SERIALIZATION_SIMPLE_ASTWRITER_H + +#include "clang/Serialization/Simple/ModuleCache.h" + +namespace clang { +/// The evil twin of ASTReader. +class SimpleASTWriter { + using SimpleModuleManager = serialization::SimpleModuleManager; + using SimpleModuleFile = serialization::SimpleModuleFile; + + std::shared_ptr Cache; + + SimpleASTReader *ASTRdr; + +public: + explicit SimpleASTWriter(std::shared_ptr Cache, + SimpleASTReader *ASTRdr) + : Cache(std::move(Cache)), ASTRdr(ASTRdr) {} + + void writeAST(StringRef ModuleName, Module *WritingModule, + std::shared_ptr CI) { + SimplePCM &PCM = + Cache->insert(ModuleName, SimplePCM(std::move(CI), WritingModule)); + + assert(ASTRdr); + SimpleModuleManager &ModuleMgr = ASTRdr->getModuleManager(); + for (const std::unique_ptr &M : ModuleMgr) + PCM.SLocEntryBaseOffsets.emplace_back(M->ModuleName, + M->SLocEntryBaseOffset); + // + } +}; +} // namespace clang + +#endif // LLVM_CLANG_SERIALIZATION_SIMPLE_ASTWRITER_H diff --git a/clang/include/clang/Serialization/Simple/ModuleCache.h b/clang/include/clang/Serialization/Simple/ModuleCache.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/Simple/ModuleCache.h @@ -0,0 +1,53 @@ +#ifndef LLVM_CLANG_SERIALIZATION_SIMPLE_MODULECACHE_H +#define LLVM_CLANG_SERIALIZATION_SIMPLE_MODULECACHE_H + +#include "clang/Basic/SourceLocation.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/StringMap.h" + +#include +#include +#include +#include + +namespace clang { +struct SimplePCM { + std::shared_ptr CI; + + Module *CompiledModule; + + std::vector> + SLocEntryBaseOffsets; + + SimplePCM(std::shared_ptr CI, Module *CompiledModule) + : CI(std::move(CI)), CompiledModule(CompiledModule) {} + + unsigned localValidSLocEntryCount() const { + // Subtracting 1 to ignore to invalid entry at index 0. + return CI->getSourceManager().local_sloc_entry_size() - 1; + } + + SourceLocation::UIntTy getLastLocalOffset() const { + // Subtracting 1 to get the last local offset. + return CI->getSourceManager().getNextLocalOffset() - 1; + } +}; + +class SimpleModuleCache { + llvm::StringMap PCMs; + +public: + SimplePCM &insert(StringRef Name, SimplePCM PCM) { + return PCMs.insert({Name, std::move(PCM)}).first->getValue(); + } + + SimplePCM *lookup(StringRef Name) { + auto It = PCMs.find(Name); + if (It == PCMs.end()) + return nullptr; + return &It->second; + } +}; +} // namespace clang + +#endif // LLVM_CLANG_SERIALIZATION_SIMPLE_MODULECACHE_H diff --git a/clang/include/clang/Serialization/Simple/ModuleFile.h b/clang/include/clang/Serialization/Simple/ModuleFile.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/Simple/ModuleFile.h @@ -0,0 +1,73 @@ +#ifndef LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEFILE_H +#define LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEFILE_H + +#include "clang/Basic/FileEntry.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Serialization/ContinuousRangeMap.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SetVector.h" + +namespace clang { +namespace serialization { +/// The input file that has been loaded from this AST file, along with +/// bools indicating whether this was an overridden buffer or if it was +/// out-of-date or not-found. +class SimpleInputFile { + enum { Overridden = 1, OutOfDate = 2, NotFound = 3 }; + llvm::PointerIntPair Val; + +public: + SimpleInputFile() = default; + + SimpleInputFile(FileEntryRef File, bool isOverridden = false, + bool isOutOfDate = false) { + assert(!(isOverridden && isOutOfDate) && + "an overridden cannot be out-of-date"); + unsigned intVal = 0; + if (isOverridden) + intVal = Overridden; + else if (isOutOfDate) + intVal = OutOfDate; + Val.setPointerAndInt(&File.getMapEntry(), intVal); + } + + static SimpleInputFile getNotFound() { + SimpleInputFile File; + File.Val.setInt(NotFound); + return File; + } + + OptionalFileEntryRefDegradesToFileEntryPtr getFile() const { + if (auto *P = Val.getPointer()) + return FileEntryRef(*P); + return None; + } + bool isOverridden() const { return Val.getInt() == Overridden; } + bool isOutOfDate() const { return Val.getInt() == OutOfDate; } + bool isNotFound() const { return Val.getInt() == NotFound; } +}; + +struct SimpleModuleFile { + std::string ModuleName; + + llvm::SetVector Imports; + + /// The base ID in the source manager's view of this module. + int SLocEntryBaseID = 0; + + /// The base offset in the source manager's view of this module. + SourceLocation::UIntTy SLocEntryBaseOffset = 0; + + /// Remapping table for source locations in this module. + ContinuousRangeMap + SLocRemap; + + // TODO: Pull this on-demand from SimpleInMemoryPCM? + llvm::BitVector SearchPathUsage; + + SimpleModuleFile(StringRef Name) : ModuleName(Name.str()) {} +}; +} // namespace serialization +} // namespace clang + +#endif // LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEFILE_H diff --git a/clang/include/clang/Serialization/Simple/ModuleManager.h b/clang/include/clang/Serialization/Simple/ModuleManager.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Serialization/Simple/ModuleManager.h @@ -0,0 +1,57 @@ +#ifndef LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEMANAGER_H +#define LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEMANAGER_H + +#include "clang/Serialization/Simple/ModuleFile.h" + +#include "llvm/ADT/SmallVector.h" + +#include + +namespace clang { +namespace serialization { +class SimpleModuleManager { + /// The chain of modules, in the order in which we started to load + /// them (this order isn't really useful for anything). + SmallVector, 2> Chain; + + /// All loaded modules, indexed by name. + llvm::StringMap Modules; + +public: + SimpleModuleFile *lookup(StringRef Name) const { + return Modules.lookup(Name); + } + + /// The result of attempting to add a new module. + enum AddModuleResult { + AlreadyLoaded, + NewlyLoaded, + }; + + AddModuleResult addModule(StringRef Name, SimpleModuleFile *ImportedBy, + SimpleModuleFile *&Module) { + auto UpdateModuleImports = [&](SimpleModuleFile &MF, + SimpleModuleFile *ImportedBy) { + if (ImportedBy) + ImportedBy->Imports.insert(&MF); + }; + + if (SimpleModuleFile *ModuleEntry = Modules.lookup(Name)) { + Module = ModuleEntry; + UpdateModuleImports(*ModuleEntry, ImportedBy); + return AlreadyLoaded; + } + + Chain.push_back(std::make_unique(Name)); + Module = Modules[Name] = Chain.back().get(); + UpdateModuleImports(*Module, ImportedBy); + return NewlyLoaded; + } + + auto begin() { return Chain.begin(); } + auto end() { return Chain.end(); } +}; +} // namespace serialization +} // namespace clang + +#endif // LLVM_CLANG_SERIALIZATION_SIMPLE_MODULEMANAGER_H diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -87,9 +87,6 @@ /// additionally appear in \c FileDeps as a dependency. std::string ClangModuleMapFile; - /// The path to where an implicit build would put the PCM for this module. - std::string ImplicitModulePCMPath; - /// A collection of absolute paths to files that this module directly depends /// on, not including transitive dependencies. llvm::StringSet<> FileDeps; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -39,6 +39,9 @@ #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/GlobalModuleIndex.h" #include "clang/Serialization/InMemoryModuleCache.h" +#include "clang/Serialization/Simple/ASTReader.h" +#include "clang/Serialization/Simple/ASTWriter.h" +#include "clang/Serialization/Simple/ModuleCache.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/Statistic.h" #include "llvm/Config/llvm-config.h" @@ -209,6 +212,23 @@ TheASTReader = std::move(Reader); } +std::shared_ptr CompilerInstance::getSimpleASTReader() const { + return TheSimpleASTReader; +} + +std::shared_ptr CompilerInstance::getSimpleASTWriter() const { + return TheSimpleASTWriter; +} + +std::shared_ptr CompilerInstance::getSimpleModuleCache() { + return TheSimpleModuleCache; +} + +void CompilerInstance::setSimpleModuleCache( + std::shared_ptr Cache) { + TheSimpleModuleCache = std::move(Cache); +} + std::shared_ptr CompilerInstance::getModuleDepCollector() const { return ModuleDepCollector; @@ -1212,8 +1232,10 @@ // module. Since we're sharing an in-memory module cache, // CompilerInstance::CompilerInstance is responsible for finalizing the // buffers to prevent use-after-frees. - CompilerInstance Instance(ImportingInstance.getPCHContainerOperations(), - &ImportingInstance.getModuleCache()); + auto InstancePtr = std::make_shared( + ImportingInstance.getPCHContainerOperations(), + &ImportingInstance.getModuleCache()); + CompilerInstance& Instance = *InstancePtr; auto &Inv = *Invocation; Instance.setInvocation(std::move(Invocation)); @@ -1229,6 +1251,8 @@ Instance.createSourceManager(Instance.getFileManager()); SourceManager &SourceMgr = Instance.getSourceManager(); + Instance.setSimpleModuleCache(ImportingInstance.getSimpleModuleCache()); + // Note that this module is part of the module build stack, so that we // can detect cycles in the module graph. SourceMgr.setModuleBuildStack( @@ -1248,6 +1272,10 @@ PreBuildStep(Instance); + if (Instance.getPreprocessorOpts().ScanningMode) { + llvm::errs() << "{{{{{{{{{{ " << ModuleName << "\n"; + } + // Execute the action to actually build the module in-place. Use a separate // thread so that we get a stack large enough. bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnThread( @@ -1257,6 +1285,24 @@ }, DesiredStackSize); + if (Instance.getPreprocessorOpts().ScanningMode) { + if (!Instance.getSimpleASTReader()) + Instance.createSimpleASTReader(); + if (!Instance.getSimpleASTWriter()) + Instance.createSimpleASTWriter(); + + assert(Instance.getLangOpts().isCompilingModule()); + Module *Module = + Instance.getPreprocessor().getHeaderSearchInfo().lookupModule( + Instance.getLangOpts().CurrentModule, SourceLocation(), + /*AllowSearch=*/false); + assert(Module); + + Instance.getSimpleASTWriter()->writeAST(ModuleName, Module, InstancePtr); + llvm::errs() << "}}}}}}}}}} " << ModuleName << "\n"; + ImportingInstance.getSimpleASTReader()->readAST(ModuleName, ImportingInstance); + } + PostBuildStep(Instance); ImportingInstance.getDiagnostics().Report(ImportLoc, @@ -1364,6 +1410,10 @@ SourceLocation ModuleNameLoc, Module *Module, StringRef ModuleFileName, bool *OutOfDate) { + // Do not attempt to read the AST file. + if (ImportingInstance.getPreprocessorOpts().ScanningMode) + return true; + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; @@ -1648,6 +1698,21 @@ } } +void CompilerInstance::createSimpleASTReader() { + if (TheSimpleASTReader) + return; + + TheSimpleASTReader = std::make_shared(TheSimpleModuleCache); +} + +void CompilerInstance::createSimpleASTWriter() { + if (TheSimpleASTWriter) + return; + + TheSimpleASTWriter = std::make_shared( + TheSimpleModuleCache, TheSimpleASTReader.get()); +} + void CompilerInstance::createASTReader() { if (TheASTReader) return; @@ -1799,6 +1864,14 @@ Module *M = HS.lookupModule(ModuleName, ImportLoc, true, !IsInclusionDirective); + if (getPreprocessorOpts().ScanningMode) { + // Create an ASTTransfer on demand. + if (!getSimpleASTReader()) + createSimpleASTReader(); + if (getSimpleASTReader()->readAST(ModuleName, *this)) + return M; + } + // Select the source and filename for loading the named module. std::string ModuleFilename; ModuleSource Source = @@ -2092,7 +2165,7 @@ // Make the named module visible, if it's not already part of the module // we are parsing. if (ModuleName != getLangOpts().CurrentModule) { - if (!Module->IsFromModuleFile && !MapPrivateSubModToTopLevel) { + if (!getPreprocessorOpts().ScanningMode && !Module->IsFromModuleFile && !MapPrivateSubModToTopLevel) { // We have an umbrella header or directory that doesn't actually include // all of the headers within the directory it covers. Complain about // this missing submodule and recover by forgetting that we ever saw diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -1129,7 +1129,7 @@ setCompilerInstance(nullptr); setCurrentInput(FrontendInputFile()); - CI.getLangOpts().setCompilingModule(LangOptions::CMK_None); +// CI.getLangOpts().setCompilingModule(LangOptions::CMK_None); } bool FrontendAction::shouldEraseOutputFiles() { diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -120,6 +120,9 @@ std::unique_ptr GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (CI.getPreprocessorOpts().ScanningMode) + return std::make_unique(); + std::string Sysroot; if (!ComputeASTConsumerArguments(CI, /*ref*/ Sysroot)) return nullptr; @@ -185,6 +188,9 @@ std::unique_ptr GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (CI.getPreprocessorOpts().ScanningMode) + return std::make_unique(); + std::unique_ptr OS = CreateOutputFile(CI, InFile); if (!OS) return nullptr; diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -535,6 +535,66 @@ return getHeaderSearchInfo().lookupModule(getLangOpts().ModuleName); } +std::set Preprocessor::getAffectingModuleMaps(Module *M) { + Module *RootModule = M; + const HeaderSearch &HS = HeaderInfo; + + std::set ModuleMaps{}; + std::set ProcessedModules; + SmallVector ModulesToProcess{RootModule}; + + SmallVector FilesByUID; + HS.getFileMgr().GetUniqueIDMapping(FilesByUID); + + if (FilesByUID.size() > HS.header_file_size()) + FilesByUID.resize(HS.header_file_size()); + + for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) { + const FileEntry *File = FilesByUID[UID]; + if (!File) + continue; + + const HeaderFileInfo *HFI = + HS.getExistingFileInfo(File, /*WantExternal*/ false); + if (!HFI || (HFI->isModuleHeader && !HFI->isCompilingModuleHeader)) + continue; + + for (const auto &KH : HS.findAllModulesForHeader(File)) { + if (!KH.getModule()) + continue; + ModulesToProcess.push_back(KH.getModule()); + } + } + + while (!ModulesToProcess.empty()) { + auto *CurrentModule = ModulesToProcess.pop_back_val(); + ProcessedModules.insert(CurrentModule); + + Optional ModuleMapFile = + HS.getModuleMap().getModuleMapFileForUniquing(CurrentModule); + if (!ModuleMapFile) { + continue; + } + + ModuleMaps.insert(*ModuleMapFile); + + for (auto *ImportedModule : (CurrentModule)->Imports) { + if (!ImportedModule || + ProcessedModules.find(ImportedModule) != ProcessedModules.end()) { + continue; + } + ModulesToProcess.push_back(ImportedModule); + } + + for (const Module *UndeclaredModule : CurrentModule->UndeclaredUses) + if (UndeclaredModule && + ProcessedModules.find(UndeclaredModule) == ProcessedModules.end()) + ModulesToProcess.push_back(UndeclaredModule); + } + + return ModuleMaps; +} + //===----------------------------------------------------------------------===// // Preprocessor Initialization Methods //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -161,64 +161,6 @@ namespace { -std::set GetAffectingModuleMaps(const HeaderSearch &HS, - Module *RootModule) { - std::set ModuleMaps{}; - std::set ProcessedModules; - SmallVector ModulesToProcess{RootModule}; - - SmallVector FilesByUID; - HS.getFileMgr().GetUniqueIDMapping(FilesByUID); - - if (FilesByUID.size() > HS.header_file_size()) - FilesByUID.resize(HS.header_file_size()); - - for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) { - const FileEntry *File = FilesByUID[UID]; - if (!File) - continue; - - const HeaderFileInfo *HFI = - HS.getExistingFileInfo(File, /*WantExternal*/ false); - if (!HFI || (HFI->isModuleHeader && !HFI->isCompilingModuleHeader)) - continue; - - for (const auto &KH : HS.findAllModulesForHeader(File)) { - if (!KH.getModule()) - continue; - ModulesToProcess.push_back(KH.getModule()); - } - } - - while (!ModulesToProcess.empty()) { - auto *CurrentModule = ModulesToProcess.pop_back_val(); - ProcessedModules.insert(CurrentModule); - - Optional ModuleMapFile = - HS.getModuleMap().getModuleMapFileForUniquing(CurrentModule); - if (!ModuleMapFile) { - continue; - } - - ModuleMaps.insert(*ModuleMapFile); - - for (auto *ImportedModule : (CurrentModule)->Imports) { - if (!ImportedModule || - ProcessedModules.find(ImportedModule) != ProcessedModules.end()) { - continue; - } - ModulesToProcess.push_back(ImportedModule); - } - - for (const Module *UndeclaredModule : CurrentModule->UndeclaredUses) - if (UndeclaredModule && - ProcessedModules.find(UndeclaredModule) == ProcessedModules.end()) - ModulesToProcess.push_back(UndeclaredModule); - } - - return ModuleMaps; -} - class ASTTypeWriter { ASTWriter &Writer; ASTWriter::RecordData Record; @@ -4519,8 +4461,7 @@ if (!WritingModule) return; - auto AffectingModuleMaps = - GetAffectingModuleMaps(PP->getHeaderSearchInfo(), WritingModule); + auto AffectingModuleMaps = PP->getAffectingModuleMaps(WritingModule); unsigned FileIDAdjustment = 0; unsigned OffsetAdjustment = 0; diff --git a/clang/lib/Serialization/CMakeLists.txt b/clang/lib/Serialization/CMakeLists.txt --- a/clang/lib/Serialization/CMakeLists.txt +++ b/clang/lib/Serialization/CMakeLists.txt @@ -21,6 +21,8 @@ ModuleManager.cpp PCHContainerOperations.cpp + Simple/ASTReader.cpp + ADDITIONAL_HEADERS ASTCommon.h ASTReaderInternals.h diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTContext.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/Bitstream/BitstreamWriter.h" @@ -39,6 +40,10 @@ } void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { + // Do not attempt to write the AST file. + if (PP.getPreprocessorOpts().ScanningMode) + return; + // Don't create a PCH if there were fatal failures during module loading. if (PP.getModuleLoader().HadFatalFailure) return; diff --git a/clang/lib/Serialization/Simple/ASTReader.cpp b/clang/lib/Serialization/Simple/ASTReader.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Serialization/Simple/ASTReader.cpp @@ -0,0 +1,708 @@ +#include "clang/Serialization/Simple/ASTReader.h" + +#include "clang/Lex/Preprocessor.h" + +using namespace clang; + +/* +namespace serialization2 { + SourceLocation translate(SourceLocation BLoc) { + if (BLoc.isInvalid()) + return {}; + +auto BOffset = BSM.getFileOffset(BLoc); +auto AOffset = BOffset; + +auto BFileID = BSM.getFileID(BLoc); +auto AFileID = [&]() { + if (BFileID == BPP.getPredefinesFileID()) + return APP.getPredefinesFileID(); + + auto *BSLocEntry = BSM.getSLocEntryForFile(BFileID); + + if (BSLocEntry->isFile()) { + if (auto BBuffer = + BSLocEntry->getFile().getContentCache().getBufferIfLoaded()) { + auto ABuffer = llvm::MemoryBuffer::getMemBuffer(*BBuffer); + + auto BFileCharacteristic = BSM.getFileCharacteristic(BLoc); + auto AFileCharacteristic = BFileCharacteristic; + + // TODO: Implement this. + int LoadedID = 0; + SourceLocation::UIntTy LoadedOffset = 0; + + return ASM.createFileID(std::move(ABuffer), AFileCharacteristic, + LoadedID, LoadedOffset); + } + } + + auto *BFileEntry = BSM.getFileEntryForID(BFileID); + auto *AFileEntry = translate(BFileEntry); + + auto BIncludeLoc = BSM.getIncludeLoc(BFileID); + auto AIncludeLoc = translate(BIncludeLoc); + + auto BFileCharacteristic = BSM.getFileCharacteristic(BLoc); + auto AFileCharacteristic = BFileCharacteristic; + + // TODO: Implement this. + int LoadedID = 0; + SourceLocation::UIntTy LoadedOffset = 0; + + return ASM.createFileID(AFileEntry, AIncludeLoc, AFileCharacteristic, + LoadedID, LoadedOffset); +}(); + +return ASM.getComposedLoc(AFileID, AOffset); +} +}; + +// TODO: Integrate this properly. +void foo() { + for (const auto &X : Instance.SortedFiles) + if (!ImportingInstance.SortedFiles.count(X.getKey())) + ImportingInstance.SortedFiles[X.getKey()] = X.getValue(); + + auto &SortedFiles = ImportingInstance.SortedFiles[ModuleName]; + + std::set AffectingModuleMaps = GetAffectingModuleMaps( + Instance.getPreprocessor().getHeaderSearchInfo(), + Instance.getPreprocessor().getHeaderSearchInfo().lookupModule( + ModuleName)); + + SourceManager &SrcMgr = Instance.getPreprocessor().getSourceManager(); + unsigned N = SrcMgr.local_sloc_entry_size(); + + for (unsigned I = 1; I != N; ++I) { + const SrcMgr::SLocEntry *SLoc = &SrcMgr.getLocalSLocEntry(I); + + if (!SLoc->isFile()) + continue; + const SrcMgr::FileInfo &File = SLoc->getFile(); + const SrcMgr::ContentCache *Cache = &File.getContentCache(); + if (!Cache->OrigEntry) + continue; + + if (!isModuleMap(File.getFileCharacteristic()) || + AffectingModuleMaps.empty() || + AffectingModuleMaps.find(Cache->OrigEntry) != + AffectingModuleMaps.end()) { + CompilerInstance::EntryStruct Entry{ + T.translate(Cache->OrigEntry), + isSystem(File.getFileCharacteristic()), + isModuleMap(File.getFileCharacteristic()) && + File.getIncludeLoc().isInvalid()}; + + if (Entry.IsSystemFile) + SortedFiles.push_back(Entry); + else + SortedFiles.push_front(Entry); + } + } +} +} // namespace +} // namespace serialization2 +*/ + +/// Mapping from a source location entry to whether it is affecting or not. +/// Mostly copied from ASTWriter.cpp. +llvm::BitVector collectNonAffectingInputFiles(Preprocessor &PP, Module *M) { + SourceManager &SrcMgr = PP.getSourceManager(); + unsigned BN = SrcMgr.local_sloc_entry_size(); + + llvm::BitVector IsSLocAffecting(BN, true); + + auto AffectingModuleMaps = PP.getAffectingModuleMaps(M); + + for (unsigned I = 1; I != BN; ++I) { + const SrcMgr::SLocEntry *SLoc = &SrcMgr.getLocalSLocEntry(I); + + if (!SLoc->isFile()) + continue; + const SrcMgr::FileInfo &File = SLoc->getFile(); + const SrcMgr::ContentCache *Cache = &File.getContentCache(); + if (!Cache->OrigEntry) + continue; + + if (!isModuleMap(File.getFileCharacteristic()) || + AffectingModuleMaps.empty() || + AffectingModuleMaps.find(Cache->OrigEntry) != AffectingModuleMaps.end()) + continue; + + IsSLocAffecting[I] = false; + } + + return IsSLocAffecting; +} + +bool SimpleASTReader::readAST(StringRef ModuleName, CompilerInstance &CI) { + SimplePCM *PCM = Cache->lookup(ModuleName); + if (PCM == nullptr) + return false; + + llvm::SmallVector LoadedMFs; + readASTCore(ModuleName, nullptr, LoadedMFs); + for (SimpleModuleFile *LoadedMF : LoadedMFs) + readASTBlock(*LoadedMF, CI); + + ASTTransfer T(CI, *PCM->CI, ModuleName); + T.translateFileManager(); + T.translateSourceManager(); + T.translatePreprocessor(); + T.translateModule(); + + return true; +} + +void SimpleASTReader::readASTCore( + StringRef Name, SimpleModuleFile *ImportedBy, + llvm::SmallVectorImpl &Loaded) { + SimpleModuleFile *M; + SimpleModuleManager::AddModuleResult AddResult = + ModuleMgr.addModule(Name, ImportedBy, M); + + switch (AddResult) { + case SimpleModuleManager::AlreadyLoaded: + return; + case SimpleModuleManager::NewlyLoaded: + break; + } + + // ReadControlBlock + SimplePCM *PCM = Cache->lookup(Name); + if (auto OtherReader = PCM->CI->getSimpleASTReader()) + for (auto &X : OtherReader->getModuleManager()) + readASTCore(X->ModuleName, M, Loaded); + + std::vector SearchPathUsage = + PCM->CI->getPreprocessor().getHeaderSearchInfo().computeUserEntryUsage(); + M->SearchPathUsage.resize(SearchPathUsage.size()); + for (unsigned I = 0; I < SearchPathUsage.size(); ++I) + M->SearchPathUsage[I] = SearchPathUsage[I]; + + Loaded.push_back(M); +} + +void SimpleASTReader::readASTBlock(SimpleModuleFile &F, CompilerInstance &CI) { + SimplePCM *PCM = Cache->lookup(F.ModuleName); + assert(PCM); + + std::tie(F.SLocEntryBaseID, F.SLocEntryBaseOffset) = + CI.getSourceManager().AllocateLoadedSLocEntries( + PCM->localValidSLocEntryCount(), PCM->getLastLocalOffset()); + + // These would be needed if we implemented the lazy-loading API + // (ExternalSLocEntrySource). +#if 0 + // Make our entry in the range map. BaseID is negative and growing, so + // we invert it. Because we invert it, though, we need the other end of + // the range. + unsigned RangeStart = + unsigned(-F.SLocEntryBaseID) - F.LocalNumSLocEntries + 1; + GlobalSLocEntryMap.insert(std::make_pair(RangeStart, &F)); + F.FirstLoc = SourceLocation::getFromRawEncoding(F.SLocEntryBaseOffset); + + // SLocEntryBaseOffset is lower than MaxLoadedOffset and decreasing. + assert((F.SLocEntryBaseOffset & SourceLocation::MacroIDBit) == 0); + ImportsInfo.DepRemaps[ModuleName].GlobalSLocOffsetMap.insert(std::make_pair( + SourceManager::MaxLoadedOffset - F.SLocEntryBaseOffset - SLocSpaceSize, + &F)); +#endif + + // Initialize the remapping table. + // Invalid stays invalid. + F.SLocRemap.insertOrReplace(std::make_pair(0U, 0)); + // This module. Base was 2 when being compiled. + F.SLocRemap.insertOrReplace(std::make_pair( + 2U, static_cast(F.SLocEntryBaseOffset - 2))); + + for (const auto &[Name, SLocOffset] : PCM->SLocEntryBaseOffsets) { + SimpleModuleFile *OM = ModuleMgr.lookup(Name); + + // Continuous range maps we may be updating in our module. + using SLocRemapBuilder = + ContinuousRangeMap::Builder; + SLocRemapBuilder SLocRemap(F.SLocRemap); + SLocRemap.insert({SLocOffset, static_cast( + OM->SLocEntryBaseOffset - SLocOffset)}); + } +} + +void SimpleASTReader::visitInputFiles(SimpleModuleFile &MF, bool IncludeSystem, + bool Complain, + InputFileVisitorTy Visitor) { + SimplePCM *PCM = Cache->lookup(MF.ModuleName); + assert(PCM); + SourceManager &SourceMgr = PCM->CI->getSourceManager(); + unsigned N = PCM->CI->getSourceManager().local_sloc_entry_size(); + for (unsigned I = 1; I != N; ++I) { + SrcMgr::SLocEntry &SLoc = SourceMgr.LocalSLocEntryTable[I]; + if (!SLoc.isFile()) + continue; + const SrcMgr::FileInfo &FI = SLoc.getFile(); + if (auto MaybeEntry = FI.getContentCache().OrigEntry) + Visitor(*MaybeEntry, false); + } +} + +void SimpleASTReader::visitTopLevelModuleMaps( + SimpleModuleFile &MF, TopLevelModuleMapVisitorTy Visitor) { + SimplePCM *PCM = Cache->lookup(MF.ModuleName); + assert(PCM); + + Preprocessor &PP = PCM->CI->getPreprocessor(); + SourceManager &SourceMgr = PP.getSourceManager(); + + llvm::BitVector IsInputFileAffecting = + collectNonAffectingInputFiles(PP, PCM->CompiledModule); + + unsigned N = SourceMgr.local_sloc_entry_size(); + for (unsigned I = 1; I != N; ++I) { + SrcMgr::SLocEntry &SLoc = SourceMgr.LocalSLocEntryTable[I]; + if (!SLoc.isFile()) + continue; + const SrcMgr::FileInfo &FI = SLoc.getFile(); + if (IsInputFileAffecting[I] && + SrcMgr::isModuleMap(FI.getFileCharacteristic())) + if (auto MaybeEntry = FI.getContentCache().OrigEntry) + Visitor(*MaybeEntry); + } +} + +SimpleASTReader::ASTTransfer::ASTTransfer(CompilerInstance &A, + CompilerInstance &B, + StringRef BModuleName) + : A(A), APP(A.getPreprocessor()), ASM(A.getSourceManager()), + AFM(A.getFileManager()), AHS(A.getPreprocessor().getHeaderSearchInfo()), + B(B), BPP(B.getPreprocessor()), BSM(B.getSourceManager()), + BFM(B.getFileManager()), BHS(B.getPreprocessor().getHeaderSearchInfo()), + BModule(BHS.getModuleMap().findModule(BModuleName)), + AModuleFile( + A.getSimpleASTReader()->getModuleManager().lookup(BModuleName)) {} + +void SimpleASTReader::ASTTransfer::translateFileManager() { + // +} + +void SimpleASTReader::ASTTransfer::translateSourceManager() { + // We can't translate SLocEntries eagerly. Creating a FileID + + for (const auto &MF : A.getSimpleASTReader()->getModuleManager()) { + SimplePCM *PCM = A.getSimpleASTReader()->getCache().lookup(MF->ModuleName); + assert(PCM); + + SourceManager &SourceMgr = PCM->CI->getSourceManager(); + unsigned N = SourceMgr.local_sloc_entry_size(); + for (unsigned I = 1; I != N; ++I) { + SrcMgr::SLocEntry &SLoc = SourceMgr.LocalSLocEntryTable[I]; + + int AFileID = translateSLocEntryID(I, MF.get()); + SourceLocation::UIntTy AOffset = translate(SLoc.getOffset(), MF.get()); + unsigned Index = -AFileID - 2; + + if (SLoc.isFile()) { + const SrcMgr::FileInfo &File = SLoc.getFile(); + const SrcMgr::ContentCache *Content = &File.getContentCache(); + + if (Content->OrigEntry) { + if (!ASM.SLocEntryLoaded[Index]) + ASM.createFileID(translate(*Content->OrigEntry), + translate(File.getIncludeLoc()), + File.getFileCharacteristic(), AFileID, AOffset); + } else { + llvm::Optional Buffer = + Content->getBufferOrNone(PCM->CI->getDiagnostics(), + PCM->CI->getFileManager()); + assert(Buffer); + if (!ASM.SLocEntryLoaded[Index]) + ASM.createFileID(*Buffer, File.getFileCharacteristic(), AFileID, + AOffset); + } + } else { + const SrcMgr::ExpansionInfo &Expansion = SLoc.getExpansion(); + + if (!ASM.SLocEntryLoaded[Index]) { + SourceLocation::UIntTy NextOffset = SourceMgr.getNextLocalOffset(); + if (I + 1 != N) + NextOffset = SourceMgr.getLocalSLocEntry(I + 1).getOffset(); + unsigned Length = NextOffset - SLoc.getOffset() - 1; + + ASM.createExpansionLoc(translate(Expansion.getSpellingLoc()), + translate(Expansion.getExpansionLocStart()), + translate(Expansion.getExpansionLocEnd()), + Length, Expansion.isExpansionTokenRange(), + AFileID, AOffset); + } + } + /* + // We will detect whether a file changed and return 'Failure' for it, but + // we will also try to fail gracefully by setting up the SLocEntry. + unsigned InputID = Record[4]; + InputFile IF = getInputFile(*F, InputID); + Optional File = IF.getFile(); + bool OverriddenBuffer = IF.isOverridden(); + + SourceLocation IncludeLoc = ReadSourceLocation(*F, Record[1]); + if (IncludeLoc.isInvalid() && F->Kind != MK_MainFile) { + // This is the module's main file. + IncludeLoc = getImportLocation(F); + } + SrcMgr::CharacteristicKind + FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; + FileID FID = SourceMgr.createFileID(*File, IncludeLoc, FileCharacter, ID, + BaseOffset + Record[0]); + SrcMgr::FileInfo &FileInfo = + const_cast(SourceMgr.getSLocEntry(FID).getFile()); + FileInfo.NumCreatedFIDs = Record[5]; + if (Record[3]) + FileInfo.setHasLineDirectives(); + + unsigned NumFileDecls = Record[7]; + if (NumFileDecls && ContextObj) { + const DeclID *FirstDecl = F->FileSortedDecls + Record[6]; + assert(F->FileSortedDecls && "FILE_SORTED_DECLS not encountered yet ?"); + FileDeclIDs[FID] = FileDeclsInfo(F, llvm::makeArrayRef(FirstDecl, + NumFileDecls)); + } + + const SrcMgr::ContentCache &ContentCache = + SourceMgr.getOrCreateContentCache(*File, isSystem(FileCharacter)); + if (OverriddenBuffer && !ContentCache.BufferOverridden && + ContentCache.ContentsEntry == ContentCache.OrigEntry && + !ContentCache.getBufferIfLoaded()) { + auto Buffer = ReadBuffer(SLocEntryCursor, File->getName()); + if (!Buffer) + return true; + SourceMgr.overrideFileContents(*File, std::move(Buffer)); + } + + break; + } + case SM_SLOC_BUFFER_ENTRY: { + const char *Name = Blob.data(); + unsigned Offset = Record[0]; + SrcMgr::CharacteristicKind + FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; + SourceLocation IncludeLoc = ReadSourceLocation(*F, Record[1]); + if (IncludeLoc.isInvalid() && F->isModule()) { + IncludeLoc = getImportLocation(F); + } + + auto Buffer = ReadBuffer(SLocEntryCursor, Name); + if (!Buffer) + return true; + SourceMgr.createFileID(std::move(Buffer), FileCharacter, ID, + BaseOffset + Offset, IncludeLoc); + break; + } + */ + } + } +} + +SourceLocation +SimpleASTReader::ASTTransfer::translate(SourceLocation Loc) const { + assert(AModuleFile->SLocRemap.find(Loc.getOffset()) != + AModuleFile->SLocRemap.end() && + "Cannot find offset to remap."); + SourceLocation::IntTy Remap = + AModuleFile->SLocRemap.find(Loc.getOffset())->second; + return Loc.getLocWithOffset(Remap); +} + +SourceLocation::UIntTy +SimpleASTReader::ASTTransfer::translate(SourceLocation::UIntTy Offset, + const SimpleModuleFile *MF) { + if (!MF) + MF = AModuleFile; + return Offset + MF->SLocRemap.find(Offset)->second; +} + +SrcMgr::ContentCache * +SimpleASTReader::ASTTransfer::translate(const SrcMgr::ContentCache &BCC) { + if (auto AOrigEntry = translate(BCC.OrigEntry)) + return &ASM.getOrCreateContentCache(*AOrigEntry, + /*isSystemFile=*/false); + return nullptr; +} + +Optional +SimpleASTReader::ASTTransfer::translate(const SrcMgr::FileInfo &BFI) { + if (auto *AContentCache = translate(BFI.getContentCache())) + return SrcMgr::FileInfo::get(translate(BFI.getIncludeLoc()), *AContentCache, + BFI.getFileCharacteristic(), BFI.getName()); + return None; +} + +int SimpleASTReader::ASTTransfer::translateSLocEntryID( + int EntryID, const SimpleModuleFile *MF) { + if (!MF) + MF = AModuleFile; + return MF->SLocEntryBaseID + EntryID - 1; +} + +const FileEntry &SimpleASTReader::ASTTransfer::translate(const FileEntry &BFE) { + if (auto MaybeAFE = AFM.getFile(BFE.getName())) + return **MaybeAFE; + return *AFM.getVirtualFile(BFE.getName(), BFE.getSize(), + BFE.getModificationTime()); +} + +FileEntryRef SimpleASTReader::ASTTransfer::translate(FileEntryRef BFE) { + if (auto AFE = AFM.getOptionalFileRef(BFE.getNameAsRequested())) + return *AFE; + return AFM.getVirtualFileRef(BFE.getNameAsRequested(), BFE.getSize(), + BFE.getModificationTime()); +} + +const DirectoryEntry & +SimpleASTReader::ASTTransfer::translate(const DirectoryEntry &BDE) { + return **AFM.getDirectory(BDE.getName()); +} + +Module::Header +SimpleASTReader::ASTTransfer::translate(const Module::Header &BHeader) { + return Module::Header{BHeader.NameAsWritten, + BHeader.PathRelativeToRootModuleDirectory, + translate(BHeader.Entry)}; +} + +std::vector +SimpleASTReader::ASTTransfer::translate(Module::submodule_iterator Begin, + Module::submodule_iterator End) { + std::vector ASubModules; + for (auto It = Begin; It != End; ++It) + ASubModules.push_back(translate(*It)); + return ASubModules; +} + +Module::UnresolvedHeaderDirective SimpleASTReader::ASTTransfer::translate( + const Module::UnresolvedHeaderDirective &BD) { + return {BD.Kind, // + translate(BD.FileNameLoc), + BD.FileName, + BD.IsUmbrella, + BD.HasBuiltinHeader, + BD.Size, + BD.ModTime}; +} + +Module::ExportDecl +SimpleASTReader::ASTTransfer::translate(const Module::ExportDecl &BED) { + return {translate(BED.getPointer()), BED.getInt()}; +} + +ModuleId SimpleASTReader::ASTTransfer::translate(const ModuleId &BId) { + ModuleId Res; + for (const auto &Element : BId) + Res.push_back({Element.first, translate(Element.second)}); + return Res; +} + +Module::UnresolvedExportDecl SimpleASTReader::ASTTransfer::translate( + const Module::UnresolvedExportDecl &BUED) { + return {translate(BUED.ExportLoc), translate(BUED.Id), BUED.Wildcard}; +} + +Module::LinkLibrary +SimpleASTReader::ASTTransfer::translate(const Module::LinkLibrary &X) { + return X; +} + +Module::UnresolvedConflict +SimpleASTReader::ASTTransfer::translate(const Module::UnresolvedConflict &X) { + return {translate(X.Id), X.Message}; +} + +Module::Conflict +SimpleASTReader::ASTTransfer::translate(const Module::Conflict &X) { + return {translate(X.Other), X.Message}; +} + +const Module &SimpleASTReader::ASTTransfer::translate(const Module &BMod) { + return translate(const_cast(BMod)); +} + +Module &SimpleASTReader::ASTTransfer::translate(Module &BMod) { + auto &AModMap = AHS.getModuleMap(); + + auto [AMod, New] = AModMap.findOrCreateModule( + BMod.Name, translate(BMod.Parent), BMod.IsFramework, BMod.IsExplicit); + + if (!TranslatedModules.insert({BMod.Name}).second) + return *AMod; + + AMod->Kind = BMod.Kind; + AMod->Directory = translate(BMod.Directory); + AMod->PresumedModuleMapFile = BMod.PresumedModuleMapFile; + AMod->DefinitionLoc = translate(BMod.DefinitionLoc); + AMod->Umbrella = translate(BMod.Umbrella); + AMod->UmbrellaAsWritten = BMod.UmbrellaAsWritten; + AMod->UmbrellaRelativeToRootModuleDirectory = + BMod.UmbrellaRelativeToRootModuleDirectory; + AMod->ExportAsModule = BMod.ExportAsModule; + + for (Module *BSubMod : BMod.submodules()) + translate(BSubMod); + + for (const FileEntry *BTopHeader : BMod.getTopHeaders(B.getFileManager())) + AMod->addTopHeader(translate(BTopHeader)); + + // TODO: Propagate VisibilityID to other data structures. + + for (auto Kind : {Module::HK_Normal, Module::HK_Textual, Module::HK_Private, + Module::HK_PrivateTextual, Module::HK_Excluded}) { + for (const auto &BH : BMod.Headers[Kind]) { + const auto &AH = translate(BH); + AModMap.addHeader(AMod, AH, ModuleMap::headerKindToRole(Kind), + BHS.getFileInfo(BH.Entry).isModuleHeader); + } + } + + AMod->UnresolvedHeaders = translate(BMod.UnresolvedHeaders); + AMod->MissingHeaders = translate(BMod.MissingHeaders); + AMod->Requirements = BMod.Requirements; + AMod->ShadowingModule = translate(BMod.ShadowingModule); + AMod->IsUnimportable = BMod.IsUnimportable; + AMod->HasIncompatibleModuleFile = BMod.HasIncompatibleModuleFile; + AMod->IsAvailable = BMod.IsAvailable; + AMod->IsFromModuleFile = BMod.IsFromModuleFile; + AMod->IsFramework = BMod.IsFramework; + AMod->IsExplicit = BMod.IsExplicit; + AMod->IsSystem = BMod.IsSystem; + AMod->IsExternC = BMod.IsExternC; + // This is being dropped by PCM files and propagating it triggers + // asserts. + // AMod->IsInferred = BMod.IsInferred; + AMod->InferSubmodules = BMod.InferSubmodules; + AMod->InferExplicitSubmodules = BMod.InferExplicitSubmodules; + AMod->InferExportWildcard = BMod.InferExportWildcard; + AMod->ConfigMacrosExhaustive = BMod.ConfigMacrosExhaustive; + AMod->NoUndeclaredIncludes = BMod.NoUndeclaredIncludes; + AMod->ModuleMapIsPrivate = BMod.ModuleMapIsPrivate; + AMod->NameVisibility = BMod.NameVisibility; + AMod->InferredSubmoduleLoc = translate(BMod.InferredSubmoduleLoc); + AMod->Imports = translate(BMod.Imports); + AMod->Exports = translate(BMod.Exports); + AMod->UnresolvedExports = translate(BMod.UnresolvedExports); + AMod->DirectUses = translate(BMod.DirectUses); + AMod->UnresolvedDirectUses = translate(BMod.UnresolvedDirectUses); + AMod->UndeclaredUses = translate(BMod.UndeclaredUses); + AMod->LinkLibraries = translate(BMod.LinkLibraries); + AMod->UseExportAsModuleLinkName = BMod.UseExportAsModuleLinkName; + AMod->ConfigMacros = BMod.ConfigMacros; + AMod->UnresolvedConflicts = BMod.UnresolvedConflicts; + AMod->Conflicts = BMod.Conflicts; + + return *AMod; +} + +void SimpleASTReader::ASTTransfer::translateModule() { translate(BModule); } + +IdentifierInfo &SimpleASTReader::ASTTransfer::translate(IdentifierInfo &BII) { + auto &AII = APP.getIdentifierTable().getOwn(BII.getName()); + + AII.setOutOfDate(false); + + // TODO: Check if is interesting. + + if (BII.hasRevertedTokenIDToIdentifier() && + BII.getTokenID() != tok::TokenKind::identifier) + AII.revertTokenIDToIdentifier(); + AII.setObjCOrBuiltinID(BII.getObjCOrBuiltinID()); + AII.setIsPoisoned(BII.isPoisoned()); + + AII.setObjCKeywordID(BII.getObjCKeywordID()); + AII.setHasMacroDefinition(BII.hasMacroDefinition()); + + return AII; +} + +Token SimpleASTReader::ASTTransfer::translate(const Token &BTok) { + Token ATok; + ATok.startToken(); + ATok.setLocation(translate(BTok.getLocation())); + ATok.setLength(BTok.getLength()); + ATok.setIdentifierInfo(translate(BTok.getIdentifierInfo())); + ATok.setKind(BTok.getKind()); + ATok.setFlags(BTok.getFlags()); + return ATok; +} + +MacroInfo &SimpleASTReader::ASTTransfer::translate(MacroInfo &BMI) { + auto BLoc = BMI.getDefinitionLoc(); + auto ALoc = translate(BLoc); + + MacroInfo &AMI = *APP.AllocateMacroInfo(ALoc); + AMI.setDefinitionEndLoc(BMI.getDefinitionEndLoc()); + AMI.setIsUsed(BMI.isUsed()); + AMI.setUsedForHeaderGuard(BMI.isUsedForHeaderGuard()); + + if (BMI.isFunctionLike()) { + AMI.setIsFunctionLike(); + if (BMI.isC99Varargs()) + AMI.setIsC99Varargs(); + if (BMI.isGNUVarargs()) + AMI.setIsGNUVarargs(); + if (BMI.hasCommaPasting()) + AMI.setHasCommaPasting(); + std::vector AParams; + for (const IdentifierInfo *BParam : BMI.params()) + AParams.push_back(translate(const_cast(BParam))); + AMI.setParameterList(AParams, APP.getPreprocessorAllocator()); + } + + // TODO: Complete preprocessing record. + + std::vector AToks; + for (const Token &BTok : BMI.tokens()) + AToks.push_back(translate(BTok)); + AMI.setTokens(AToks, APP.getPreprocessorAllocator()); + + return AMI; +} + +void SimpleASTReader::ASTTransfer::translatePreprocessor() { + for (const auto &BEntry : BPP.getIdentifierTable()) { + IdentifierInfo *BII = BEntry.getValue(); + + if (!BII->hadMacroDefinition()) + continue; + + // Macro directive history is not read for modules. + + IdentifierInfo *BName = BII; + IdentifierInfo *AName = translate(BII); + + // TODO: Why does this keep happening to me? + // BName->setOutOfDate(false); + + auto BLeafs = BPP.getLeafModuleMacros(BName); + SmallVector BMacros(BLeafs.begin(), BLeafs.end()); + llvm::DenseMap Visits; + for (unsigned I = 0; I < BMacros.size(); ++I) + // Enqueue overridden macros once we've visited all their ancestors. + for (auto *BModuleMacro : BMacros[I]->overrides()) + if (++Visits[BModuleMacro] == BModuleMacro->getNumOverridingMacros()) + BMacros.push_back(BModuleMacro); + + // Module macros are listed in reverse dependency order. + for (const auto *BMacro : llvm::reverse(BMacros)) { + auto *AModule = translate(BMacro->getOwningModule()); + auto *AMacroInfo = translate(BMacro->getMacroInfo()); + + std::vector AOverrides; + for (auto *BOverride : BMacro->overrides()) { + auto *AOverrideModule = translate(BOverride->getOwningModule()); + auto *AOverride = APP.getModuleMacro(AOverrideModule, AName); + assert(AOverride && "missing definition for overridden macro"); + AOverrides.push_back(AOverride); + } + + bool Inserted = false; + APP.addModuleMacro(AModule, AName, AMacroInfo, AOverrides, Inserted); + } + } +} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -19,6 +19,9 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/Simple/ASTReader.h" +#include "clang/Serialization/Simple/ASTWriter.h" +#include "clang/Serialization/Simple/ModuleCache.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" #include "clang/Tooling/Tooling.h" @@ -178,6 +181,9 @@ CompilerInstance &ScanInstance = *ScanInstanceStorage; ScanInstance.setInvocation(std::move(Invocation)); + ScanInstance.getFrontendOpts().BuildingImplicitModuleUsesLock = false; + ScanInstance.getPreprocessorOpts().ScanningMode = true; + // Create the compiler's actual diagnostics engine. sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); ScanInstance.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); @@ -199,6 +205,8 @@ ScanInstance.createSourceManager(*FileMgr); + ScanInstance.setSimpleModuleCache(std::make_shared()); + llvm::StringSet<> PrebuiltModulesInputFiles; // Store the list of prebuilt module files into header search options. This // will prevent the implicit build to create duplicate modules and will diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -11,6 +11,9 @@ #include "clang/Basic/MakeSupport.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/Simple/ASTReader.h" +#include "clang/Serialization/Simple/ASTWriter.h" +#include "clang/Serialization/Simple/ModuleCache.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" #include "llvm/Support/BLAKE3.h" #include "llvm/Support/StringSaver.h" @@ -20,19 +23,19 @@ using namespace dependencies; static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, - ASTReader &Reader, - const serialization::ModuleFile &MF) { + SimpleASTReader &Reader, + const serialization::SimpleModuleFile &MF) { // Only preserve search paths that were used during the dependency scan. std::vector Entries = Opts.UserEntries; Opts.UserEntries.clear(); llvm::BitVector SearchPathUsage(Entries.size()); - llvm::DenseSet Visited; - std::function VisitMF = - [&](const serialization::ModuleFile *MF) { + llvm::DenseSet Visited; + std::function VisitMF = + [&](const serialization::SimpleModuleFile *MF) { SearchPathUsage |= MF->SearchPathUsage; Visited.insert(MF); - for (const serialization::ModuleFile *Import : MF->Imports) + for (const serialization::SimpleModuleFile *Import : MF->Imports) if (!Visited.contains(Import)) VisitMF(Import); }; @@ -380,7 +383,7 @@ // -fmodule-name is used to compile a translation unit that imports this // module. In that case it can be skipped. The appropriate header // dependencies will still be reported as expected. - if (!M->getASTFile()) + if (M->getFullModuleName() == MDC.ScanInstance.getLangOpts().ModuleName) continue; handleTopLevelModule(M); } @@ -410,7 +413,6 @@ MD.ID.ModuleName = M->getFullModuleName(); MD.ImportedByMainFile = DirectModularDeps.contains(M); - MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName()); MD.IsSystem = M->IsSystem; ModuleMap &ModMapInfo = @@ -424,11 +426,15 @@ MD.ClangModuleMapFile = std::string(Path); } - serialization::ModuleFile *MF = - MDC.ScanInstance.getASTReader()->getModuleManager().lookup( - M->getASTFile()); - MDC.ScanInstance.getASTReader()->visitInputFiles( - *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) { + serialization::SimpleModuleFile *MF = + MDC.ScanInstance.getSimpleASTReader()->getModuleManager().lookup( + M->getFullModuleName()); + if (!MF) { + llvm::errs() << "assert MF null for " << M->getFullModuleName() << "\n"; + std::abort(); + } + MDC.ScanInstance.getSimpleASTReader()->visitInputFiles( + *MF, true, true, [&](const serialization::SimpleInputFile &IF, bool isSystem) { // __inferred_module.map is the result of the way in which an implicit // module build handles inferred modules. It adds an overlay VFS with // this file in the proper directory and relies on the rest of Clang to @@ -446,7 +452,7 @@ addAllSubmoduleDeps(M, MD, SeenDeps); addAllAffectingClangModules(M, MD, SeenDeps); - MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps( + MDC.ScanInstance.getSimpleASTReader()->visitTopLevelModuleMaps( *MF, [&](const FileEntry *FE) { if (FE->getName().endswith("__inferred_module.map")) return; @@ -457,7 +463,7 @@ MD, [&](CompilerInvocation &BuildInvocation) { if (MDC.OptimizeArgs) optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), - *MDC.ScanInstance.getASTReader(), *MF); + *MDC.ScanInstance.getSimpleASTReader(), *MF); }); MDC.associateWithContextHash(CI, MD); diff --git a/clang/test/ClangScanDeps/modules-basic.c b/clang/test/ClangScanDeps/modules-basic.c new file mode 100644 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-basic.c @@ -0,0 +1,181 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- include/module.modulemap +module top { header "top.h" } +module left { header "left.h" } +module right { header "right.h" } +module right_extra { header "right-extra.h" } +module bottom { header "bottom.h" } +//--- include/top.h +// +#define D1 +//--- include/left.h +/// +#include "top.h" +#define D2 +//--- include/right.h +//// +#include "right-extra.h" +#include "top.h" +#define D3 +//--- include/right-extra.h +///// +#define D4 +//--- include/bottom.h +////// +#include "left.h" +#include "right.h" +#define D5 +//--- include/t.h + +//--- tu.c +#include "bottom.h" +#ifdef D1 +#endif +#ifdef D2 +#endif +#ifdef D3 +#endif +#ifdef D4 +#endif +#ifdef D5 +#endif + +#define D1 +#define D2 +#define D3 +#define D4 +#define D5 + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -I DIR/include" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "left" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "right" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodule-file=left=[[PREFIX]]/cache/{{.*}}/left-{{.*}}.pcm", +// CHECK: "-fmodule-file=right=[[PREFIX]]/cache/{{.*}}/right-{{.*}}.pcm", +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/bottom.h", +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "bottom" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "top" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodule-file=top=[[PREFIX]]/cache/{{.*}}/top-{{.*}}.pcm", +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/left.h", +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "left" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "right_extra" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "top" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodule-file=right_extra=[[PREFIX]]/cache/{{.*}}/right_extra-{{.*}}.pcm", +// CHECK: "-fmodule-file=top=[[PREFIX]]/cache/{{.*}}/top-{{.*}}.pcm", +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "[[PREFIX]]/include/right.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "right" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "[[PREFIX]]/include/right-extra.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "right_extra" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "[[PREFIX]]/include/top.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "top" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-context-hash": "{{.*}}", +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "bottom" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-map-file=[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodule-file=bottom=[[PREFIX]]/cache/{{.*}}/bottom-{{.*}}.pcm", +// CHECK: ], +// CHECK-NEXT: "executable": "clang", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.c" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }