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-exception 6 // 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 19 namespace clang::tooling { 20 21 class ExpandModularHeadersPPCallbacks::FileRecorder { 22 public: 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.count(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. Ideally 53 /// `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 62 private: 63 /// A set of files whose contents are to be recorded. 64 llvm::DenseSet<FileEntryRef> FilesToRecord; 65 }; 66 67 ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks( 68 CompilerInstance *CI, 69 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) 70 : Recorder(std::make_unique<FileRecorder>()), Compiler(*CI), 71 InMemoryFs(new llvm::vfs::InMemoryFileSystem), 72 Sources(Compiler.getSourceManager()), 73 // Forward the new diagnostics to the original DiagnosticConsumer. 74 Diags(new DiagnosticIDs, new DiagnosticOptions, 75 new ForwardingDiagnosticConsumer(Compiler.getDiagnosticClient())), 76 LangOpts(Compiler.getLangOpts()) { 77 // Add a FileSystem containing the extra files needed in place of modular 78 // headers. 79 OverlayFS->pushOverlay(InMemoryFs); 80 81 Diags.setSourceManager(&Sources); 82 // FIXME: Investigate whatever is there better way to initialize DiagEngine 83 // or whatever DiagEngine can be shared by multiple preprocessors 84 ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(), 85 Compiler.getVirtualFileSystem()); 86 87 LangOpts.Modules = false; 88 89 auto HSO = std::make_shared<HeaderSearchOptions>(); 90 *HSO = Compiler.getHeaderSearchOpts(); 91 92 HeaderInfo = std::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts, 93 &Compiler.getTarget()); 94 95 auto PO = std::make_shared<PreprocessorOptions>(); 96 *PO = Compiler.getPreprocessorOpts(); 97 98 PP = std::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources, 99 *HeaderInfo, ModuleLoader, 100 /*IILookup=*/nullptr, 101 /*OwnsHeaderSearch=*/false); 102 PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget()); 103 InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(), 104 Compiler.getFrontendOpts(), Compiler.getCodeGenOpts()); 105 ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts, 106 Compiler.getTarget().getTriple()); 107 } 108 109 ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; 110 111 Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { 112 return PP.get(); 113 } 114 115 void ExpandModularHeadersPPCallbacks::handleModuleFile( 116 serialization::ModuleFile *MF) { 117 if (!MF) 118 return; 119 // Avoid processing a ModuleFile more than once. 120 if (!VisitedModules.insert(MF).second) 121 return; 122 123 // Visit all the input files of this module and mark them to record their 124 // contents later. 125 Compiler.getASTReader()->visitInputFiles( 126 *MF, true, false, 127 [this](const serialization::InputFile &IF, bool /*IsSystem*/) { 128 Recorder->addNecessaryFile(*IF.getFile()); 129 }); 130 // Recursively handle all transitively imported modules. 131 for (auto *Import : MF->Imports) 132 handleModuleFile(Import); 133 } 134 135 void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) { 136 // Load all source locations present in the external sources. 137 for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) { 138 Sources.getLoadedSLocEntry(I, nullptr); 139 } 140 // Record contents of files we are interested in and add to the FileSystem. 141 for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) { 142 Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs); 143 } 144 Recorder->checkAllFilesRecorded(); 145 146 if (!StartedLexing) { 147 StartedLexing = true; 148 PP->Lex(CurrentToken); 149 } 150 while (!CurrentToken.is(tok::eof) && 151 Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) { 152 PP->Lex(CurrentToken); 153 } 154 } 155 156 void ExpandModularHeadersPPCallbacks::FileChanged( 157 SourceLocation Loc, FileChangeReason Reason, 158 SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) { 159 if (!EnteredMainFile) { 160 EnteredMainFile = true; 161 PP->EnterMainSourceFile(); 162 } 163 } 164 165 void ExpandModularHeadersPPCallbacks::InclusionDirective( 166 SourceLocation DirectiveLoc, const Token &IncludeToken, 167 StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange, 168 OptionalFileEntryRef IncludedFile, StringRef SearchPath, 169 StringRef RelativePath, const Module *SuggestedModule, bool ModuleImported, 170 SrcMgr::CharacteristicKind FileType) { 171 if (ModuleImported) { 172 serialization::ModuleFile *MF = 173 Compiler.getASTReader()->getModuleManager().lookup( 174 *SuggestedModule->getASTFile()); 175 handleModuleFile(MF); 176 } 177 parseToLocation(DirectiveLoc); 178 } 179 180 void ExpandModularHeadersPPCallbacks::EndOfMainFile() { 181 while (!CurrentToken.is(tok::eof)) 182 PP->Lex(CurrentToken); 183 } 184 185 // Handle all other callbacks. 186 // Just parse to the corresponding location to generate the same callback for 187 // the PPCallbacks registered in our custom preprocessor. 188 void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { 189 parseToLocation(Loc); 190 } 191 void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, 192 PragmaIntroducerKind) { 193 parseToLocation(Loc); 194 } 195 void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, 196 const IdentifierInfo *, 197 StringRef) { 198 parseToLocation(Loc); 199 } 200 void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, 201 StringRef, 202 StringRef) { 203 parseToLocation(Loc); 204 } 205 void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, 206 StringRef) { 207 parseToLocation(Loc); 208 } 209 void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, 210 StringRef, 211 PragmaMessageKind, 212 StringRef) { 213 parseToLocation(Loc); 214 } 215 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, 216 StringRef) { 217 parseToLocation(Loc); 218 } 219 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, 220 StringRef) { 221 parseToLocation(Loc); 222 } 223 void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, 224 StringRef, 225 diag::Severity, 226 StringRef) { 227 parseToLocation(Loc); 228 } 229 void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, 230 bool, OptionalFileEntryRef, 231 SrcMgr::CharacteristicKind) { 232 parseToLocation(Loc); 233 } 234 void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension( 235 SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc, 236 unsigned) { 237 // FIXME: Figure out whether it's the right location to parse to. 238 parseToLocation(NameLoc); 239 } 240 void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, 241 PragmaWarningSpecifier, 242 ArrayRef<int>) { 243 parseToLocation(Loc); 244 } 245 void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, 246 int) { 247 parseToLocation(Loc); 248 } 249 void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { 250 parseToLocation(Loc); 251 } 252 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( 253 SourceLocation Loc) { 254 parseToLocation(Loc); 255 } 256 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( 257 SourceLocation Loc) { 258 parseToLocation(Loc); 259 } 260 void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, 261 const MacroDefinition &, 262 SourceRange Range, 263 const MacroArgs *) { 264 // FIXME: Figure out whether it's the right location to parse to. 265 parseToLocation(Range.getBegin()); 266 } 267 void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, 268 const MacroDirective *MD) { 269 parseToLocation(MD->getLocation()); 270 } 271 void ExpandModularHeadersPPCallbacks::MacroUndefined( 272 const Token &, const MacroDefinition &, const MacroDirective *Undef) { 273 if (Undef) 274 parseToLocation(Undef->getLocation()); 275 } 276 void ExpandModularHeadersPPCallbacks::Defined(const Token &MacroNameTok, 277 const MacroDefinition &, 278 SourceRange Range) { 279 // FIXME: Figure out whether it's the right location to parse to. 280 parseToLocation(Range.getBegin()); 281 } 282 void ExpandModularHeadersPPCallbacks::SourceRangeSkipped( 283 SourceRange Range, SourceLocation EndifLoc) { 284 // FIXME: Figure out whether it's the right location to parse to. 285 parseToLocation(EndifLoc); 286 } 287 void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, 288 ConditionValueKind) { 289 parseToLocation(Loc); 290 } 291 void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, 292 ConditionValueKind, SourceLocation) { 293 parseToLocation(Loc); 294 } 295 void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, 296 const MacroDefinition &) { 297 parseToLocation(Loc); 298 } 299 void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, 300 const MacroDefinition &) { 301 parseToLocation(Loc); 302 } 303 void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { 304 parseToLocation(Loc); 305 } 306 void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, 307 SourceLocation) { 308 parseToLocation(Loc); 309 } 310 311 } // namespace clang::tooling 312