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 16 #define DEBUG_TYPE "clang-tidy" 17 18 namespace clang { 19 namespace tooling { 20 21 class ExpandModularHeadersPPCallbacks::FileRecorder { 22 public: 23 /// Records that a given file entry is needed for replaying callbacks. 24 void addNecessaryFile(const FileEntry *File) { 25 // Don't record modulemap files because it breaks same file detection. 26 if (!(File->getName().endswith("module.modulemap") || 27 File->getName().endswith("module.private.modulemap") || 28 File->getName().endswith("module.map") || 29 File->getName().endswith("module_private.map"))) 30 FilesToRecord.insert(File); 31 } 32 33 /// Records content for a file and adds it to the FileSystem. 34 void recordFileContent(const FileEntry *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 llvm::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<const FileEntry *> 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 83 LangOpts.Modules = false; 84 85 auto HSO = std::make_shared<HeaderSearchOptions>(); 86 *HSO = Compiler.getHeaderSearchOpts(); 87 88 HeaderInfo = std::make_unique<HeaderSearch>(HSO, Sources, Diags, LangOpts, 89 &Compiler.getTarget()); 90 91 auto PO = std::make_shared<PreprocessorOptions>(); 92 *PO = Compiler.getPreprocessorOpts(); 93 94 PP = std::make_unique<clang::Preprocessor>(PO, Diags, LangOpts, Sources, 95 *HeaderInfo, ModuleLoader, 96 /*IILookup=*/nullptr, 97 /*OwnsHeaderSearch=*/false); 98 PP->Initialize(Compiler.getTarget(), Compiler.getAuxTarget()); 99 InitializePreprocessor(*PP, *PO, Compiler.getPCHContainerReader(), 100 Compiler.getFrontendOpts()); 101 ApplyHeaderSearchOptions(*HeaderInfo, *HSO, LangOpts, 102 Compiler.getTarget().getTriple()); 103 } 104 105 ExpandModularHeadersPPCallbacks::~ExpandModularHeadersPPCallbacks() = default; 106 107 Preprocessor *ExpandModularHeadersPPCallbacks::getPreprocessor() const { 108 return PP.get(); 109 } 110 111 void ExpandModularHeadersPPCallbacks::handleModuleFile( 112 serialization::ModuleFile *MF) { 113 if (!MF) 114 return; 115 // Avoid processing a ModuleFile more than once. 116 if (VisitedModules.count(MF)) 117 return; 118 VisitedModules.insert(MF); 119 120 // Visit all the input files of this module and mark them to record their 121 // contents later. 122 Compiler.getASTReader()->visitInputFiles( 123 *MF, true, false, 124 [this](const serialization::InputFile &IF, bool /*IsSystem*/) { 125 Recorder->addNecessaryFile(IF.getFile()); 126 }); 127 // Recursively handle all transitively imported modules. 128 for (auto *Import : MF->Imports) 129 handleModuleFile(Import); 130 } 131 132 void ExpandModularHeadersPPCallbacks::parseToLocation(SourceLocation Loc) { 133 // Load all source locations present in the external sources. 134 for (unsigned I = 0, N = Sources.loaded_sloc_entry_size(); I != N; ++I) { 135 Sources.getLoadedSLocEntry(I, nullptr); 136 } 137 // Record contents of files we are interested in and add to the FileSystem. 138 for (auto It = Sources.fileinfo_begin(); It != Sources.fileinfo_end(); ++It) { 139 Recorder->recordFileContent(It->getFirst(), *It->getSecond(), *InMemoryFs); 140 } 141 Recorder->checkAllFilesRecorded(); 142 143 if (!StartedLexing) { 144 StartedLexing = true; 145 PP->Lex(CurrentToken); 146 } 147 while (!CurrentToken.is(tok::eof) && 148 Sources.isBeforeInTranslationUnit(CurrentToken.getLocation(), Loc)) { 149 PP->Lex(CurrentToken); 150 } 151 } 152 153 void ExpandModularHeadersPPCallbacks::FileChanged( 154 SourceLocation Loc, FileChangeReason Reason, 155 SrcMgr::CharacteristicKind FileType, FileID PrevFID = FileID()) { 156 if (!EnteredMainFile) { 157 EnteredMainFile = true; 158 PP->EnterMainSourceFile(); 159 } 160 } 161 162 void ExpandModularHeadersPPCallbacks::InclusionDirective( 163 SourceLocation DirectiveLoc, const Token &IncludeToken, 164 StringRef IncludedFilename, bool IsAngled, CharSourceRange FilenameRange, 165 const FileEntry *IncludedFile, StringRef SearchPath, StringRef RelativePath, 166 const Module *Imported, SrcMgr::CharacteristicKind FileType) { 167 if (Imported) { 168 serialization::ModuleFile *MF = 169 Compiler.getASTReader()->getModuleManager().lookup( 170 Imported->getASTFile()); 171 handleModuleFile(MF); 172 } 173 parseToLocation(DirectiveLoc); 174 } 175 176 void ExpandModularHeadersPPCallbacks::EndOfMainFile() { 177 while (!CurrentToken.is(tok::eof)) 178 PP->Lex(CurrentToken); 179 } 180 181 // Handle all other callbacks. 182 // Just parse to the corresponding location to generate the same callback for 183 // the PPCallbacks registered in our custom preprocessor. 184 void ExpandModularHeadersPPCallbacks::Ident(SourceLocation Loc, StringRef) { 185 parseToLocation(Loc); 186 } 187 void ExpandModularHeadersPPCallbacks::PragmaDirective(SourceLocation Loc, 188 PragmaIntroducerKind) { 189 parseToLocation(Loc); 190 } 191 void ExpandModularHeadersPPCallbacks::PragmaComment(SourceLocation Loc, 192 const IdentifierInfo *, 193 StringRef) { 194 parseToLocation(Loc); 195 } 196 void ExpandModularHeadersPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, 197 StringRef, 198 StringRef) { 199 parseToLocation(Loc); 200 } 201 void ExpandModularHeadersPPCallbacks::PragmaDebug(SourceLocation Loc, 202 StringRef) { 203 parseToLocation(Loc); 204 } 205 void ExpandModularHeadersPPCallbacks::PragmaMessage(SourceLocation Loc, 206 StringRef, 207 PragmaMessageKind, 208 StringRef) { 209 parseToLocation(Loc); 210 } 211 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPush(SourceLocation Loc, 212 StringRef) { 213 parseToLocation(Loc); 214 } 215 void ExpandModularHeadersPPCallbacks::PragmaDiagnosticPop(SourceLocation Loc, 216 StringRef) { 217 parseToLocation(Loc); 218 } 219 void ExpandModularHeadersPPCallbacks::PragmaDiagnostic(SourceLocation Loc, 220 StringRef, 221 diag::Severity, 222 StringRef) { 223 parseToLocation(Loc); 224 } 225 void ExpandModularHeadersPPCallbacks::HasInclude(SourceLocation Loc, StringRef, 226 bool, Optional<FileEntryRef>, 227 SrcMgr::CharacteristicKind) { 228 parseToLocation(Loc); 229 } 230 void ExpandModularHeadersPPCallbacks::PragmaOpenCLExtension( 231 SourceLocation NameLoc, const IdentifierInfo *, SourceLocation StateLoc, 232 unsigned) { 233 // FIXME: Figure out whether it's the right location to parse to. 234 parseToLocation(NameLoc); 235 } 236 void ExpandModularHeadersPPCallbacks::PragmaWarning(SourceLocation Loc, 237 StringRef, ArrayRef<int>) { 238 parseToLocation(Loc); 239 } 240 void ExpandModularHeadersPPCallbacks::PragmaWarningPush(SourceLocation Loc, 241 int) { 242 parseToLocation(Loc); 243 } 244 void ExpandModularHeadersPPCallbacks::PragmaWarningPop(SourceLocation Loc) { 245 parseToLocation(Loc); 246 } 247 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullBegin( 248 SourceLocation Loc) { 249 parseToLocation(Loc); 250 } 251 void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( 252 SourceLocation Loc) { 253 parseToLocation(Loc); 254 } 255 void 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 } 262 void ExpandModularHeadersPPCallbacks::MacroDefined(const Token &MacroNameTok, 263 const MacroDirective *MD) { 264 parseToLocation(MD->getLocation()); 265 } 266 void ExpandModularHeadersPPCallbacks::MacroUndefined( 267 const Token &, const MacroDefinition &, const MacroDirective *Undef) { 268 if (Undef) 269 parseToLocation(Undef->getLocation()); 270 } 271 void 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 } 277 void ExpandModularHeadersPPCallbacks::SourceRangeSkipped( 278 SourceRange Range, SourceLocation EndifLoc) { 279 // FIXME: Figure out whether it's the right location to parse to. 280 parseToLocation(EndifLoc); 281 } 282 void ExpandModularHeadersPPCallbacks::If(SourceLocation Loc, SourceRange, 283 ConditionValueKind) { 284 parseToLocation(Loc); 285 } 286 void ExpandModularHeadersPPCallbacks::Elif(SourceLocation Loc, SourceRange, 287 ConditionValueKind, SourceLocation) { 288 parseToLocation(Loc); 289 } 290 void ExpandModularHeadersPPCallbacks::Ifdef(SourceLocation Loc, const Token &, 291 const MacroDefinition &) { 292 parseToLocation(Loc); 293 } 294 void ExpandModularHeadersPPCallbacks::Ifndef(SourceLocation Loc, const Token &, 295 const MacroDefinition &) { 296 parseToLocation(Loc); 297 } 298 void ExpandModularHeadersPPCallbacks::Else(SourceLocation Loc, SourceLocation) { 299 parseToLocation(Loc); 300 } 301 void ExpandModularHeadersPPCallbacks::Endif(SourceLocation Loc, 302 SourceLocation) { 303 parseToLocation(Loc); 304 } 305 306 } // namespace tooling 307 } // namespace clang 308