307 lines · cpp
1//===- ExpandModularHeadersPPCallbacks.h - clang-tidy -----------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ExpandModularHeadersPPCallbacks.h"10#include "clang/Basic/FileManager.h"11#include "clang/Basic/TargetInfo.h"12#include "clang/Frontend/CompilerInstance.h"13#include "clang/Lex/PreprocessorOptions.h"14#include "clang/Serialization/ASTReader.h"15#include <optional>16 17#define DEBUG_TYPE "clang-tidy"18 19namespace clang::tooling {20 21class ExpandModularHeadersPPCallbacks::FileRecorder {22public:23 /// Records that a given file entry is needed for replaying callbacks.24 void addNecessaryFile(FileEntryRef File) {25 // Don't record modulemap files because it breaks same file detection.26 if (!(File.getName().ends_with("module.modulemap") ||27 File.getName().ends_with("module.private.modulemap") ||28 File.getName().ends_with("module.map") ||29 File.getName().ends_with("module_private.map")))30 FilesToRecord.insert(File);31 }32 33 /// Records content for a file and adds it to the FileSystem.34 void recordFileContent(FileEntryRef File,35 const SrcMgr::ContentCache &ContentCache,36 llvm::vfs::InMemoryFileSystem &InMemoryFs) {37 // Return if we are not interested in the contents of this file.38 if (!FilesToRecord.contains(File))39 return;40 41 // FIXME: Why is this happening? We might be losing contents here.42 std::optional<StringRef> Data = ContentCache.getBufferDataIfLoaded();43 if (!Data)44 return;45 46 InMemoryFs.addFile(File.getName(), /*ModificationTime=*/0,47 llvm::MemoryBuffer::getMemBufferCopy(*Data));48 // Remove the file from the set of necessary files.49 FilesToRecord.erase(File);50 }51 52 /// Makes sure we have contents for all the files we were interested in.53 /// Ideally `FilesToRecord` should be empty.54 void checkAllFilesRecorded() {55 LLVM_DEBUG({56 for (auto FileEntry : FilesToRecord)57 llvm::dbgs() << "Did not record contents for input file: "58 << FileEntry.getName() << "\n";59 });60 }61 62private:63 /// A set of files whose contents are to be recorded.64 llvm::DenseSet<FileEntryRef> FilesToRecord;65};66 67ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks(68 CompilerInstance *CI, llvm::vfs::OverlayFileSystem &OverlayFS)69 : Recorder(std::make_unique<FileRecorder>()), Compiler(*CI),70 InMemoryFs(new llvm::vfs::InMemoryFileSystem),71 Sources(Compiler.getSourceManager()),72 // Forward the new diagnostics to the original DiagnosticConsumer.73 Diags(DiagnosticIDs::create(), DiagOpts,74 new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())),75 LangOpts(Compiler.getLangOpts()), HSOpts(Compiler.getHeaderSearchOpts()) {76 // Add a FileSystem containing the extra files needed in place of modular77 // headers.78 OverlayFS.pushOverlay(InMemoryFs);79 80 Diags.setSourceManager(&Sources);81 // FIXME: Investigate whatever is there better way to initialize DiagEngine82 // or whatever DiagEngine can be shared by multiple preprocessors83 ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(),84 Compiler.getVirtualFileSystem());85 86 LangOpts.Modules = false;87 88 HeaderInfo = std::make_unique<HeaderSearch>(HSOpts, Sources, Diags, LangOpts,89 &Compiler.getTarget());90 91 PP = std::make_unique<clang::Preprocessor>(Compiler.getPreprocessorOpts(),92 Diags, LangOpts, Sources,93 *HeaderInfo, ModuleLoader,94 /*IILookup=*/nullptr,95 /*OwnsHeaderSearch=*/false);96 PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget());97 InitializePreprocessor(*PP, Compiler.getPreprocessorOpts(),98 Compiler.getPCHContainerReader(),99 Compiler.getFrontendOpts(), Compiler.getCodeGenOpts());100 ApplyHeaderSearchOptions(*HeaderInfo, HSOpts, LangOpts,101 Compiler.getTarget().getTriple());102}103 104ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default;105 106Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const {107 return PP.get();108}109 110void ExpandModularHeadersPPCallbacks::handleModuleFile(111 serialization::ModuleFile *MF) {112 if (!MF)113 return;114 // Avoid processing a ModuleFile more than once.115 if (!VisitedModules.insert(MF).second)116 return;117 118 // Visit all the input files of this module and mark them to record their119 // contents later.120 Compiler.getASTReader()->visitInputFiles(121 *MF, true, false,122 [this](const serialization::InputFile &IF, bool /*IsSystem*/) {123 Recorder->addNecessaryFile(*IF.getFile());124 });125 // Recursively handle all transitively imported modules.126 for (auto *Import : MF->Imports)127 handleModuleFile(Import);128}129 130void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) {131 // Load all source locations present in the external sources.132 for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) {133 Sources.getLoadedSLocEntry(I, nullptr);134 }135 // Record contents of files we are interested in and add to the FileSystem.136 for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) {137 Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs);138 }139 Recorder->checkAllFilesRecorded();140 141 if (!StartedLexing) {142 StartedLexing = true;143 PP->Lex(CurrentToken);144 }145 while (!CurrentToken.is(tok::eof) &&146 Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) {147 PP->Lex(CurrentToken);148 }149}150 151void ExpandModularHeadersPPCallbacks::FileChanged(152 SourceLocation Loc, FileChangeReason Reason,153 SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) {154 if (!EnteredMainFile) {155 EnteredMainFile = true;156 PP->EnterMainSourceFile();157 }158}159 160void ExpandModularHeadersPPCallbacks::InclusionDirective(161 SourceLocation DirectiveLoc, const Token &IncludeToken,162 StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange,163 OptionalFileEntryRef IncludedFile, StringRef SearchPath,164 StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported,165 SrcMgr::CharacteristicKind FileType) {166 if (ModuleImported) {167 serialization::ModuleFile *MF =168 Compiler.getASTReader()->getModuleManager().lookup(169 *SuggestedModule->getASTFile());170 handleModuleFile(MF);171 }172 parseToLocation(DirectiveLoc);173}174 175void ExpandModularHeadersPPCallbacks::EndOfMainFile() {176 while (!CurrentToken.is(tok::eof))177 PP->Lex(CurrentToken);178}179 180// Handle all other callbacks.181// Just parse to the corresponding location to generate the same callback for182// the PPCallbacks registered in our custom preprocessor.183void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) {184 parseToLocation(Loc);185}186void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc,187 PragmaIntroducerKind) {188 parseToLocation(Loc);189}190void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc,191 const IdentifierInfo *,192 StringRef) {193 parseToLocation(Loc);194}195void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc,196 StringRef,197 StringRef) {198 parseToLocation(Loc);199}200void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc,201 StringRef) {202 parseToLocation(Loc);203}204void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc,205 StringRef,206 PragmaMessageKind,207 StringRef) {208 parseToLocation(Loc);209}210void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc,211 StringRef) {212 parseToLocation(Loc);213}214void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc,215 StringRef) {216 parseToLocation(Loc);217}218void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc,219 StringRef,220 diag::Severity,221 StringRef) {222 parseToLocation(Loc);223}224void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef,225 bool, OptionalFileEntryRef,226 SrcMgr::CharacteristicKind) {227 parseToLocation(Loc);228}229void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension(230 SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc,231 unsigned) {232 // FIXME: Figure out whether it's the right location to parse to.233 parseToLocation(NameLoc);234}235void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc,236 PragmaWarningSpecifier,237 ArrayRef<int>) {238 parseToLocation(Loc);239}240void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc,241 int) {242 parseToLocation(Loc);243}244void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) {245 parseToLocation(Loc);246}247void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin(248 SourceLocation Loc) {249 parseToLocation(Loc);250}251void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd(252 SourceLocation Loc) {253 parseToLocation(Loc);254}255void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok,256 const MacroDefinition &,257 SourceRange Range,258 const MacroArgs *) {259 // FIXME: Figure out whether it's the right location to parse to.260 parseToLocation(Range.getBegin());261}262void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok,263 const MacroDirective *MD) {264 parseToLocation(MD->getLocation());265}266void ExpandModularHeadersPPCallbacks::MacroUndefined(267 const Token &, const MacroDefinition &, const MacroDirective *Undef) {268 if (Undef)269 parseToLocation(Undef->getLocation());270}271void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok,272 const MacroDefinition &,273 SourceRange Range) {274 // FIXME: Figure out whether it's the right location to parse to.275 parseToLocation(Range.getBegin());276}277void ExpandModularHeadersPPCallbacks::SourceRangeSkipped(278 SourceRange Range, SourceLocation EndifLoc) {279 // FIXME: Figure out whether it's the right location to parse to.280 parseToLocation(EndifLoc);281}282void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange,283 ConditionValueKind) {284 parseToLocation(Loc);285}286void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange,287 ConditionValueKind, SourceLocation) {288 parseToLocation(Loc);289}290void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &,291 const MacroDefinition &) {292 parseToLocation(Loc);293}294void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &,295 const MacroDefinition &) {296 parseToLocation(Loc);297}298void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) {299 parseToLocation(Loc);300}301void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc,302 SourceLocation) {303 parseToLocation(Loc);304}305 306} // namespace clang::tooling307