| //===- ExpandModularHeadersPPCallbacks.h - clang-tidy -----------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ExpandModularHeadersPPCallbacks.h" |
| #include "clang/Basic/FileManager.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/PreprocessorOptions.h" |
| #include "clang/Serialization/ASTReader.h" |
| |
| #define DEBUG_TYPE "clang-tidy" |
| |
| namespace clang { |
| namespace tooling { |
| |
| class ExpandModularHeadersPPCallbacks::FileRecorder { |
| public: |
| /// Records that a given file entry is needed for replaying callbacks. |
| void addNecessaryFile(const FileEntry *File) { |
| // Don't record modulemap files because it breaks same file detection. |
| if (!(File->getName().endswith("module.modulemap") || |
| File->getName().endswith("module.private.modulemap") || |
| File->getName().endswith("module.map") || |
| File->getName().endswith("module_private.map"))) |
| FilesToRecord.insert(File); |
| } |
| |
| /// Records content for a file and adds it to the FileSystem. |
| void recordFileContent(const FileEntry *File, |
| const SrcMgr::ContentCache &ContentCache, |
| llvm::vfs::InMemoryFileSystem &InMemoryFs) { |
| // Return if we are not interested in the contents of this file. |
| if (!FilesToRecord.count(File)) |
| return; |
| |
| // FIXME: Why is this happening? We might be losing contents here. |
| llvm::Optional<StringRef> Data = ContentCache.getBufferDataIfLoaded(); |
| if (!Data) |
| return; |
| |
| InMemoryFs.addFile(File->getName(), /*ModificationTime=*/0, |
| llvm::MemoryBuffer::getMemBufferCopy(*Data)); |
| // Remove the file from the set of necessary files. |
| FilesToRecord.erase(File); |
| } |
| |
| /// Makes sure we have contents for all the files we were interested in. Ideally |
| /// `FilesToRecord` should be empty. |
| void checkAllFilesRecorded() { |
| LLVM_DEBUG({ |
| for (auto FileEntry : FilesToRecord) |
| llvm::dbgs() << "Did not record contents for input file: " |
| << FileEntry->getName() << "\n"; |
| }); |
| } |
| |
| private: |
| /// A set of files whose contents are to be recorded. |
| llvm::DenseSet<const FileEntry *> FilesToRecord; |
| }; |
| |
| ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks( |
| CompilerInstance *CI, |
| IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) |
| : Recorder(std::make_unique<FileRecorder>()), Compiler(*CI), |
| InMemoryFs(new llvm::vfs::InMemoryFileSystem), |
| Sources(Compiler.getSourceManager()), |
| // Forward the new diagnostics to the original DiagnosticConsumer. |
| Diags(new DiagnosticIDs, new DiagnosticOptions, |
| new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())), |
| LangOpts(Compiler.getLangOpts()) { |
| // Add a FileSystem containing the extra files needed in place of modular |
| // headers. |
| OverlayFS->pushOverlay(InMemoryFs); |
| |
| Diags.setSourceManager(&Sources); |
| |
| LangOpts.Modules = false; |
| |
| auto HSO = std::make_shared<HeaderSearchOptions>(); |
| *HSO = Compiler.getHeaderSearchOpts(); |
| |
| HeaderInfo = std::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts, |
| &Compiler.getTarget()); |
| |
| auto PO = std::make_shared<PreprocessorOptions>(); |
| *PO = Compiler.getPreprocessorOpts(); |
| |
| PP = std::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources, |
| *HeaderInfo, ModuleLoader, |
| /*IILookup=*/nullptr, |
| /*OwnsHeaderSearch=*/false); |
| PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget()); |
| InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(), |
| Compiler.getFrontendOpts()); |
| ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts, |
| Compiler.getTarget().getTriple()); |
| } |
| |
| ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; |
| |
| Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { |
| return PP.get(); |
| } |
| |
| void ExpandModularHeadersPPCallbacks::handleModuleFile( |
| serialization::ModuleFile *MF) { |
| if (!MF) |
| return; |
| // Avoid processing a ModuleFile more than once. |
| if (VisitedModules.count(MF)) |
| return; |
| VisitedModules.insert(MF); |
| |
| // Visit all the input files of this module and mark them to record their |
| // contents later. |
| Compiler.getASTReader()->visitInputFiles( |
| *MF, true, false, |
| [this](const serialization::InputFile &IF, bool /*IsSystem*/) { |
| Recorder->addNecessaryFile(IF.getFile()); |
| }); |
| // Recursively handle all transitively imported modules. |
| for (auto *Import : MF->Imports) |
| handleModuleFile(Import); |
| } |
| |
| void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) { |
| // Load all source locations present in the external sources. |
| for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) { |
| Sources.getLoadedSLocEntry(I, nullptr); |
| } |
| // Record contents of files we are interested in and add to the FileSystem. |
| for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) { |
| Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs); |
| } |
| Recorder->checkAllFilesRecorded(); |
| |
| if (!StartedLexing) { |
| StartedLexing = true; |
| PP->Lex(CurrentToken); |
| } |
| while (!CurrentToken.is(tok::eof) && |
| Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) { |
| PP->Lex(CurrentToken); |
| } |
| } |
| |
| void ExpandModularHeadersPPCallbacks::FileChanged( |
| SourceLocation Loc, FileChangeReason Reason, |
| SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) { |
| if (!EnteredMainFile) { |
| EnteredMainFile = true; |
| PP->EnterMainSourceFile(); |
| } |
| } |
| |
| void ExpandModularHeadersPPCallbacks::InclusionDirective( |
| SourceLocation DirectiveLoc, const Token &IncludeToken, |
| StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange, |
| const FileEntry *IncludedFile, StringRef SearchPath, StringRef RelativePath, |
| const Module *Imported, SrcMgr::CharacteristicKind FileType) { |
| if (Imported) { |
| serialization::ModuleFile *MF = |
| Compiler.getASTReader()->getModuleManager().lookup( |
| Imported->getASTFile()); |
| handleModuleFile(MF); |
| } |
| parseToLocation(DirectiveLoc); |
| } |
| |
| void ExpandModularHeadersPPCallbacks::EndOfMainFile() { |
| while (!CurrentToken.is(tok::eof)) |
| PP->Lex(CurrentToken); |
| } |
| |
| // Handle all other callbacks. |
| // Just parse to the corresponding location to generate the same callback for |
| // the PPCallbacks registered in our custom preprocessor. |
| void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, |
| PragmaIntroducerKind) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, |
| const IdentifierInfo *, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, |
| StringRef, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, |
| StringRef, |
| PragmaMessageKind, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, |
| StringRef, |
| diag::Severity, |
| StringRef) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, |
| bool, Optional<FileEntryRef>, |
| SrcMgr::CharacteristicKind) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension( |
| SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc, |
| unsigned) { |
| // FIXME: Figure out whether it's the right location to parse to. |
| parseToLocation(NameLoc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, |
| PragmaWarningSpecifier, |
| ArrayRef<int>) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, |
| int) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( |
| SourceLocation Loc) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( |
| SourceLocation Loc) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, |
| const MacroDefinition &, |
| SourceRange Range, |
| const MacroArgs *) { |
| // FIXME: Figure out whether it's the right location to parse to. |
| parseToLocation(Range.getBegin()); |
| } |
| void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, |
| const MacroDirective *MD) { |
| parseToLocation(MD->getLocation()); |
| } |
| void ExpandModularHeadersPPCallbacks::MacroUndefined( |
| const Token &, const MacroDefinition &, const MacroDirective *Undef) { |
| if (Undef) |
| parseToLocation(Undef->getLocation()); |
| } |
| void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok, |
| const MacroDefinition &, |
| SourceRange Range) { |
| // FIXME: Figure out whether it's the right location to parse to. |
| parseToLocation(Range.getBegin()); |
| } |
| void ExpandModularHeadersPPCallbacks::SourceRangeSkipped( |
| SourceRange Range, SourceLocation EndifLoc) { |
| // FIXME: Figure out whether it's the right location to parse to. |
| parseToLocation(EndifLoc); |
| } |
| void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, |
| ConditionValueKind) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, |
| ConditionValueKind, SourceLocation) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, |
| const MacroDefinition &) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, |
| const MacroDefinition &) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { |
| parseToLocation(Loc); |
| } |
| void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, |
| SourceLocation) { |
| parseToLocation(Loc); |
| } |
| |
| } // namespace tooling |
| } // namespace clang |