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 @@ -179,6 +179,14 @@ CompilerInstance(const CompilerInstance &) = delete; void operator=(const CompilerInstance &) = delete; public: + struct EntryStruct { + Optional<FileEntryRef> FE; + bool IsSystemFile; + bool IsTopLevelModuleMap; + }; + llvm::StringMap<std::deque<EntryStruct>> SortedFiles; + llvm::StringMap<llvm::BitVector> SearchPathUsage; + explicit CompilerInstance( std::shared_ptr<PCHContainerOperations> PCHContainerOps = std::make_shared<PCHContainerOperations>(), 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<std::string> Includes; std::vector<std::string> 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/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 @@ -1129,6 +1129,406 @@ return LangOpts.CPlusPlus ? Language::CXX : Language::C; } +class Translator { + CompilerInstance &A; + Preprocessor &APP; + SourceManager &ASM; + FileManager &AFM; + HeaderSearch &AHS; + + const CompilerInstance &B; + const Preprocessor &BPP; + const SourceManager &BSM; + const FileManager &BFM; + HeaderSearch &BHS; + + llvm::StringSet<> TranslatedModules; + +public: + Translator(CompilerInstance &A, const CompilerInstance &B) + : 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()) {} + + template <class T> Optional<T> translate(const Optional<T> &BO) { + if (!BO) + return None; + return translate(*BO); + } + + template <class T> const T *translate(const T *BP) { + if (!BP) + return nullptr; + return &translate(*BP); + } + + template <class T> T *translate(T *BP) { + if (!BP) + return nullptr; + return &translate(*BP); + } + + template <class T, class U> + llvm::PointerUnion<T, U> translate(llvm::PointerUnion<T, U> BPU) { + if (!BPU) + return nullptr; + if (BPU.template is<T>()) + return translate(BPU.template get<T>()); + return translate(BPU.template get<U>()); + } + + template <class T, unsigned N> + SmallVector<T, N> translate(const SmallVector<T, N> &BV) { + SmallVector<T, N> AV; + AV.reserve(BV.size()); + for (const T &Entry : BV) + AV.push_back(translate(Entry)); + return AV; + } + + template <class T, unsigned N> + llvm::SmallSetVector<T, N> translate(const llvm::SmallSetVector<T, N>& BSSV) { + llvm::SmallSetVector<T, N> ASSV; + for (const auto &Entry : BSSV) + ASSV.insert(translate(Entry)); + return ASSV; + } + + const FileEntry &translate(const FileEntry &BFE) { + if (auto MaybeAFE = AFM.getFile(BFE.getName())) + return **MaybeAFE; + return *AFM.getVirtualFile(BFE.getName(), BFE.getSize(), + BFE.getModificationTime()); + } + + FileEntryRef translate(FileEntryRef BFE) { + return *AFM.getOptionalFileRef(BFE.getName()); + } + + const DirectoryEntry &translate(const DirectoryEntry &BDE) { + return **AFM.getDirectory(BDE.getName()); + } + + 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 BFileCharacteristic = BSM.getFileCharacteristic(BLoc); + auto AFileCharacteristic = BFileCharacteristic; + + auto *BFileEntry = BSM.getFileEntryForID(BFileID); + auto *AFileEntry = translate(BFileEntry); + return ASM.getOrCreateFileID(AFileEntry, AFileCharacteristic); + }(); + + return ASM.getComposedLoc(AFileID, AOffset); + } + + Module::Header translate(const Module::Header &BHeader) { + return Module::Header{BHeader.NameAsWritten, + BHeader.PathRelativeToRootModuleDirectory, + translate(BHeader.Entry)}; + } + + std::vector<Module *> translate(Module::submodule_iterator Begin, + Module::submodule_iterator End) { + std::vector<Module *> ASubModules; + for (auto It = Begin; It != End; ++It) + ASubModules.push_back(translate(*It)); + return ASubModules; + } + + Module::UnresolvedHeaderDirective + translate(const Module::UnresolvedHeaderDirective &BD) { + return {BD.Kind, // + translate(BD.FileNameLoc), + BD.FileName, + BD.IsUmbrella, + BD.HasBuiltinHeader, + BD.Size, + BD.ModTime}; + } + + Module::ExportDecl translate(const Module::ExportDecl &BED) { + return {translate(BED.getPointer()), BED.getInt()}; + } + + ModuleId translate(const ModuleId &BId) { + ModuleId Res; + for (const auto &Element : BId) + Res.push_back({Element.first, translate(Element.second)}); + return Res; + } + + Module::UnresolvedExportDecl + translate(const Module::UnresolvedExportDecl &BUED) { + return {translate(BUED.ExportLoc), translate(BUED.Id), BUED.Wildcard}; + } + + Module::LinkLibrary translate(const Module::LinkLibrary &X) { + return X; + } + + Module::UnresolvedConflict translate(const Module::UnresolvedConflict &X) { + return {translate(X.Id), X.Message}; + } + + Module::Conflict translate(const Module::Conflict &X) { + return {translate(X.Other), X.Message}; + } + + const Module &translate(const Module &BMod) { + return translate(const_cast<Module &>(BMod)); + } + + Module &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 translateModule(StringRef BName) { + translate(B.getPreprocessor().getHeaderSearchInfo().lookupModule(BName)); + } + + IdentifierInfo &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 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 *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<IdentifierInfo *> AParams; + for (const IdentifierInfo *BParam : BMI->params()) + AParams.push_back(translate(const_cast<IdentifierInfo *>(BParam))); + AMI->setParameterList(AParams, APP.getPreprocessorAllocator()); + } + + // TODO: Complete preprocessing record. + + std::vector<Token> AToks; + for (const Token &BTok : BMI->tokens()) + AToks.push_back(translate(BTok)); + AMI->setTokens(AToks, APP.getPreprocessorAllocator()); + + return AMI; + } + + void translatePreprocessor() { + for (const auto &Entry : BPP.getIdentifierTable()) { + IdentifierInfo *BII = Entry.getValue(); + + if (!BII->hadMacroDefinition()) + continue; + + // Macro directive history is not read for modules. + + IdentifierInfo *BName = BII; + IdentifierInfo *AName = translate(BII); + + auto BLeafs = BPP.getLeafModuleMacros(BName); + SmallVector<ModuleMacro *, 8> Worklist(BLeafs.begin(), BLeafs.end()); + llvm::DenseMap<ModuleMacro *, unsigned> Visits; + while (!Worklist.empty()) { + auto *BMacro = Worklist.pop_back_val(); + + auto *AModule = translate(BMacro->getOwningModule()); + auto *AMacroInfo = translate(BMacro->getMacroInfo()); + + std::vector<ModuleMacro *> AOverrides; + for (auto *BOverride : BMacro->overrides()) { + auto *AOverrideModule = translate(BOverride->getOwningModule()); + AOverrides.push_back(APP.getModuleMacro(AOverrideModule, AName)); + } + + bool Inserted = false; + APP.addModuleMacro(AModule, AName, AMacroInfo, AOverrides, Inserted); + + // Enqueue overridden macros once we've visited all their ancestors. + for (auto *BModuleMacro : BMacro->overrides()) + if (++Visits[BModuleMacro] == BModuleMacro->getNumOverridingMacros()) + Worklist.push_back(BModuleMacro); + } + } + } +}; + +static std::set<const FileEntry *> +GetAffectingModuleMaps(const HeaderSearch &HS, Module *RootModule) { + std::set<const FileEntry *> ModuleMaps{}; + std::set<const Module *> ProcessedModules; + SmallVector<const Module *> ModulesToProcess{RootModule}; + + SmallVector<const FileEntry *, 16> 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<FileEntryRef> 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; +} + /// Compile a module file for the given module, using the options /// provided by the importing compiler instance. Returns true if the module /// was built without errors. @@ -1254,6 +1654,65 @@ [&]() { GenerateModuleFromModuleMapAction Action; Instance.ExecuteAction(Action); + + if (!Instance.getPreprocessorOpts().ScanningMode) + return; + + Translator T(ImportingInstance, Instance); + T.translateModule(ModuleName); + T.translatePreprocessor(); + + for (const auto &X : Instance.SearchPathUsage) + if (!ImportingInstance.SearchPathUsage.count(X.getKey())) + ImportingInstance.SearchPathUsage[X.getKey()] = X.getValue(); + + auto &HSUsage = ImportingInstance.SearchPathUsage[ModuleName]; + for (bool Used : Instance.getPreprocessor() + .getHeaderSearchInfo() + .computeUserEntryUsage()) + HSUsage.push_back(Used); + + 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<const FileEntry *> 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); + } + } }, DesiredStackSize); @@ -1364,6 +1823,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; @@ -2092,7 +2555,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/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<ASTConsumer> GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (CI.getPreprocessorOpts().ScanningMode) + return std::make_unique<ASTConsumer>(); + std::string Sysroot; if (!ComputeASTConsumerArguments(CI, /*ref*/ Sysroot)) return nullptr; @@ -185,6 +188,9 @@ std::unique_ptr<ASTConsumer> GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (CI.getPreprocessorOpts().ScanningMode) + return std::make_unique<ASTConsumer>(); + std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); if (!OS) return nullptr; 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/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" -#include "llvm/Support/TargetSelect.h" using namespace clang; using namespace tooling; @@ -17,10 +16,4 @@ ScanningMode Mode, ScanningOutputFormat Format, bool OptimizeArgs, bool EagerLoadModules) : Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs), - EagerLoadModules(EagerLoadModules) { - // Initialize targets for object file support. - llvm::InitializeAllTargets(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmPrinters(); - llvm::InitializeAllAsmParsers(); -} + EagerLoadModules(EagerLoadModules) {} 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 @@ -178,6 +178,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); 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 @@ -19,24 +19,22 @@ using namespace tooling; using namespace dependencies; -static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, - ASTReader &Reader, - const serialization::ModuleFile &MF) { +static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts, const Module *M, + CompilerInstance &CI) { // Only preserve search paths that were used during the dependency scan. std::vector<HeaderSearchOptions::Entry> Entries = Opts.UserEntries; Opts.UserEntries.clear(); llvm::BitVector SearchPathUsage(Entries.size()); - llvm::DenseSet<const serialization::ModuleFile *> Visited; - std::function<void(const serialization::ModuleFile *)> VisitMF = - [&](const serialization::ModuleFile *MF) { - SearchPathUsage |= MF->SearchPathUsage; - Visited.insert(MF); - for (const serialization::ModuleFile *Import : MF->Imports) - if (!Visited.contains(Import)) - VisitMF(Import); - }; - VisitMF(&MF); + llvm::DenseSet<const Module *> Visited; + std::function<void(const Module *)> VisitM = [&](const Module *M) { + SearchPathUsage |= CI.SearchPathUsage[M->getTopLevelModuleName()]; + Visited.insert(M); + for (const Module *Import : M->Imports) + if (!Visited.contains(Import)) + VisitM(Import); + }; + VisitM(M); for (auto Idx : SearchPathUsage.set_bits()) Opts.UserEntries.push_back(Entries[Idx]); @@ -380,7 +378,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 +408,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,40 +421,37 @@ 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) { - // __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 - // handle it like normal. With explicitly built modules we don't need - // to play VFS tricks, so replace it with the correct module map. - if (IF.getFile()->getName().endswith("__inferred_module.map")) { - MDC.addFileDep(MD, ModuleMap->getName()); - return; - } - MDC.addFileDep(MD, IF.getFile()->getName()); - }); + for (const auto &E : MDC.ScanInstance.SortedFiles[M->getFullModuleName()]) { + // __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 + // handle it like normal. With explicitly built modules we don't need + // to play VFS tricks, so replace it with the correct module map. + if (E.FE->getName().endswith("__inferred_module.map")) { + MDC.addFileDep(MD, ModuleMap->getName()); + continue; + } + MDC.addFileDep(MD, E.FE->getName()); + } llvm::DenseSet<const Module *> SeenDeps; addAllSubmodulePrebuiltDeps(M, MD, SeenDeps); addAllSubmoduleDeps(M, MD, SeenDeps); addAllAffectingClangModules(M, MD, SeenDeps); - MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps( - *MF, [&](const FileEntry *FE) { - if (FE->getName().endswith("__inferred_module.map")) - return; - MD.ModuleMapFileDeps.emplace_back(FE->getName()); - }); + for (const auto &E : MDC.ScanInstance.SortedFiles[M->getFullModuleName()]) { + if (!E.IsTopLevelModuleMap) + continue; + if (E.FE->getName().endswith("__inferred_module.map")) + continue; + MD.ModuleMapFileDeps.emplace_back(E.FE->getName()); + } CompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs( MD, [&](CompilerInvocation &BuildInvocation) { if (MDC.OptimizeArgs) - optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), - *MDC.ScanInstance.getASTReader(), *MF); + optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), M, + MDC.ScanInstance); }); 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,104 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- include/module.modulemap +module m { header "m.h"} +module x { header "x.h" } +//--- include/m.h +#include "x.h" +#define FOO +//--- include/x.h +// empty +//--- include/t.h + +//--- tu.c +#include "m.h" +#ifdef FOO +#include "t.h" +#endif + +//--- 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": "[[X_HASH:.*]]", +// CHECK-NEXT: "module-name": "x" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1", +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/cache/{{.*}}/m-{{.*}}.pcm", +// CHECK: "-emit-module", +// CHECK: "[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodules", +// CHECK: "-fmodule-name=m", +// CHECK: ], +// CHECK-NEXT: "context-hash": "[[M_HASH:.*]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/m.h", +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "m" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1", +// CHECK: "-o", +// CHECK-NEXT: "[[PREFIX]]/cache/{{.*}}/x-{{.*}}.pcm", +// CHECK: "-emit-module", +// CHECK: "[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodules", +// CHECK: "-fmodule-name=x", +// CHECK: ], +// CHECK-NEXT: "context-hash": "[[X_HASH]]", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/include/module.modulemap", +// CHECK-NEXT: "[[PREFIX]]/include/x.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "name": "x" +// 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": "[[M_HASH]]", +// CHECK-NEXT: "module-name": "m" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1", +// CHECK: "-fmodule-map-file=[[PREFIX]]/include/module.modulemap", +// CHECK: "-fmodule-file=m=[[PREFIX]]/cache/{{.*}}/m-{{.*}}.pcm", +// CHECK: ], +// CHECK-NEXT: "executable": "clang", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.c", +// CHECK-NEXT: "[[PREFIX]]/include/t.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "input-file": "[[PREFIX]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: }