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,13 @@ CompilerInstance(const CompilerInstance &) = delete; void operator=(const CompilerInstance &) = delete; public: + struct EntryStruct { + Optional FE; + bool IsSystemFile; + bool IsTopLevelModuleMap; + }; + llvm::StringMap> SortedFiles; + explicit CompilerInstance( std::shared_ptr PCHContainerOps = std::make_shared(), 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,344 @@ 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 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; + } + + const FileEntry &translate(const FileEntry &BFE) { + return **AFM.getFile(BFE.getName()); + } + + 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 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 + 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(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; + 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(ATok.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 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 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 Worklist(BLeafs.begin(), BLeafs.end()); + llvm::DenseMap Visits; + while (!Worklist.empty()) { + auto *BMacro = Worklist.pop_back_val(); + + auto *AModule = translate(BMacro->getOwningModule()); + auto *AMacroInfo = translate(BMacro->getMacroInfo()); + + std::vector 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); + } + } + } +}; + /// 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 +1592,48 @@ [&]() { GenerateModuleFromModuleMapAction Action; Instance.ExecuteAction(Action); + + Translator T(ImportingInstance, Instance); + T.translateModule(ModuleName); + T.translatePreprocessor(); + + std::set AffectingModuleMaps; + std::set SkippedModuleMaps; + + auto &SortedFiles = ImportingInstance.SortedFiles[ModuleName]; + for (unsigned I = 1, N = SourceMgr.local_sloc_entry_size(); I != N; ++I) { + // Get this source location entry. + const SrcMgr::SLocEntry *SLoc = &SourceMgr.getLocalSLocEntry(I); + + // We only care about file entries that were not overridden. + if (!SLoc->isFile()) + continue; + const SrcMgr::FileInfo &File = SLoc->getFile(); + const SrcMgr::ContentCache *Cache = &File.getContentCache(); + if (!Cache->OrigEntry) + continue; + + if (isModuleMap(File.getFileCharacteristic()) && + !isSystem(File.getFileCharacteristic()) && + !AffectingModuleMaps.empty() && + AffectingModuleMaps.find(Cache->OrigEntry) == + AffectingModuleMaps.end()) { + SkippedModuleMaps.insert(Cache->OrigEntry); + // Do not emit modulemaps that do not affect current module. + continue; + } + + 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 +1744,9 @@ SourceLocation ModuleNameLoc, Module *Module, StringRef ModuleFileName, bool *OutOfDate) { + // Do not attempt to read the AST file. + return true; + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; @@ -2092,7 +2475,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 (false && !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/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -39,6 +39,9 @@ } void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { + // Do not attempt to write the AST file. + 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/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -375,15 +375,8 @@ if (!MDC.isPrebuiltModule(M)) DirectModularDeps.insert(M); - for (const Module *M : DirectModularDeps) { - // A top-level module might not be actually imported as a module when - // -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()) - continue; + for (const Module *M : DirectModularDeps) handleTopLevelModule(M); - } MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts); @@ -410,7 +403,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 +416,30 @@ 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()); - }); - - llvm::DenseSet 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.FE->getName().endswith("__inferred_module.map")) { + MDC.addFileDep(MD, ModuleMap->getName()); + continue; + } + MDC.addFileDep(MD, E.FE->getName()); + } + if (M->NoUndeclaredIncludes) { + for (const auto &E : MDC.ScanInstance.SortedFiles[M->getFullModuleName()]) { + if (E.FE->getName().endswith("__inferred_module.map")) + continue; + // The top-level modulemap of this module will be the input file. We + // don't need to specify it as a module map. + if (E.FE == ModuleMap) + continue; + MD.ModuleMapFileDeps.push_back(E.FE->getName().str()); + } + } CompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs( MD, [&](CompilerInvocation &BuildInvocation) { - if (MDC.OptimizeArgs) - optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), - *MDC.ScanInstance.getASTReader(), *MF); +// if (MDC.OptimizeArgs) +// optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(), +// *MDC.ScanInstance.getASTReader(), *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,76 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- include/module.modulemap +module m { header "m.h"} +//--- include/m.h +#define FOO +//--- 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: "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: "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: }