[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / include-cleaner / lib / Record.cpp
blobdc3192b8baff34d69c7322eca7b4d0d4aadcec7a
1 //===--- Record.cpp - Record compiler events ------------------------------===//
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 //===----------------------------------------------------------------------===//
9 #include "clang-include-cleaner/Record.h"
10 #include "clang-include-cleaner/Types.h"
11 #include "clang/AST/ASTConsumer.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/DeclGroup.h"
14 #include "clang/Basic/SourceManager.h"
15 #include "clang/Basic/Specifiers.h"
16 #include "clang/Frontend/CompilerInstance.h"
17 #include "clang/Lex/MacroInfo.h"
18 #include "clang/Lex/PPCallbacks.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
21 #include "clang/Tooling/Inclusions/StandardLibrary.h"
22 #include <memory>
23 #include <utility>
25 namespace clang::include_cleaner {
26 namespace {
28 class PPRecorder : public PPCallbacks {
29 public:
30 PPRecorder(RecordedPP &Recorded, const Preprocessor &PP)
31 : Recorded(Recorded), PP(PP), SM(PP.getSourceManager()) {}
33 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
34 SrcMgr::CharacteristicKind FileType,
35 FileID PrevFID) override {
36 Active = SM.isWrittenInMainFile(Loc);
39 void InclusionDirective(SourceLocation Hash, const Token &IncludeTok,
40 StringRef SpelledFilename, bool IsAngled,
41 CharSourceRange FilenameRange,
42 OptionalFileEntryRef File, StringRef SearchPath,
43 StringRef RelativePath, const Module *,
44 SrcMgr::CharacteristicKind) override {
45 if (!Active)
46 return;
48 Include I;
49 I.HashLocation = Hash;
50 I.Resolved = File ? &File->getFileEntry() : nullptr;
51 I.Line = SM.getSpellingLineNumber(Hash);
52 I.Spelled = SpelledFilename;
53 I.Angled = IsAngled;
54 Recorded.Includes.add(I);
57 void MacroExpands(const Token &MacroName, const MacroDefinition &MD,
58 SourceRange Range, const MacroArgs *Args) override {
59 if (!Active)
60 return;
61 recordMacroRef(MacroName, *MD.getMacroInfo());
64 void MacroDefined(const Token &MacroName, const MacroDirective *MD) override {
65 if (!Active)
66 return;
68 const auto *MI = MD->getMacroInfo();
69 // The tokens of a macro definition could refer to a macro.
70 // Formally this reference isn't resolved until this macro is expanded,
71 // but we want to treat it as a reference anyway.
72 for (const auto &Tok : MI->tokens()) {
73 auto *II = Tok.getIdentifierInfo();
74 // Could this token be a reference to a macro? (Not param to this macro).
75 if (!II || !II->hadMacroDefinition() ||
76 llvm::is_contained(MI->params(), II))
77 continue;
78 if (const MacroInfo *MI = PP.getMacroInfo(II))
79 recordMacroRef(Tok, *MI);
83 void MacroUndefined(const Token &MacroName, const MacroDefinition &MD,
84 const MacroDirective *) override {
85 if (!Active)
86 return;
87 if (const auto *MI = MD.getMacroInfo())
88 recordMacroRef(MacroName, *MI);
91 void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
92 const MacroDefinition &MD) override {
93 if (!Active)
94 return;
95 if (const auto *MI = MD.getMacroInfo())
96 recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous);
99 void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
100 const MacroDefinition &MD) override {
101 if (!Active)
102 return;
103 if (const auto *MI = MD.getMacroInfo())
104 recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous);
107 using PPCallbacks::Elifdef;
108 using PPCallbacks::Elifndef;
109 void Elifdef(SourceLocation Loc, const Token &MacroNameTok,
110 const MacroDefinition &MD) override {
111 if (!Active)
112 return;
113 if (const auto *MI = MD.getMacroInfo())
114 recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous);
116 void Elifndef(SourceLocation Loc, const Token &MacroNameTok,
117 const MacroDefinition &MD) override {
118 if (!Active)
119 return;
120 if (const auto *MI = MD.getMacroInfo())
121 recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous);
124 void Defined(const Token &MacroNameTok, const MacroDefinition &MD,
125 SourceRange Range) override {
126 if (!Active)
127 return;
128 if (const auto *MI = MD.getMacroInfo())
129 recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous);
132 private:
133 void recordMacroRef(const Token &Tok, const MacroInfo &MI,
134 RefType RT = RefType::Explicit) {
135 if (MI.isBuiltinMacro())
136 return; // __FILE__ is not a reference.
137 Recorded.MacroReferences.push_back(
138 SymbolReference{Macro{Tok.getIdentifierInfo(), MI.getDefinitionLoc()},
139 Tok.getLocation(), RT});
142 bool Active = false;
143 RecordedPP &Recorded;
144 const Preprocessor &PP;
145 const SourceManager &SM;
148 } // namespace
150 class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
151 public:
152 RecordPragma(const CompilerInstance &CI, PragmaIncludes *Out)
153 : RecordPragma(CI.getPreprocessor(), Out) {}
154 RecordPragma(const Preprocessor &P, PragmaIncludes *Out)
155 : SM(P.getSourceManager()), HeaderInfo(P.getHeaderSearchInfo()), Out(Out),
156 UniqueStrings(Arena) {}
158 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
159 SrcMgr::CharacteristicKind FileType,
160 FileID PrevFID) override {
161 InMainFile = SM.isWrittenInMainFile(Loc);
163 if (Reason == PPCallbacks::ExitFile) {
164 // At file exit time HeaderSearchInfo is valid and can be used to
165 // determine whether the file was a self-contained header or not.
166 if (const FileEntry *FE = SM.getFileEntryForID(PrevFID)) {
167 if (tooling::isSelfContainedHeader(FE, SM, HeaderInfo))
168 Out->NonSelfContainedFiles.erase(FE->getUniqueID());
169 else
170 Out->NonSelfContainedFiles.insert(FE->getUniqueID());
175 void EndOfMainFile() override {
176 for (auto &It : Out->IWYUExportBy) {
177 llvm::sort(It.getSecond());
178 It.getSecond().erase(
179 std::unique(It.getSecond().begin(), It.getSecond().end()),
180 It.getSecond().end());
182 Out->Arena = std::move(Arena);
185 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
186 llvm::StringRef FileName, bool IsAngled,
187 CharSourceRange /*FilenameRange*/,
188 OptionalFileEntryRef File,
189 llvm::StringRef /*SearchPath*/,
190 llvm::StringRef /*RelativePath*/,
191 const clang::Module * /*Imported*/,
192 SrcMgr::CharacteristicKind FileKind) override {
193 FileID HashFID = SM.getFileID(HashLoc);
194 int HashLine = SM.getLineNumber(HashFID, SM.getFileOffset(HashLoc));
195 std::optional<Header> IncludedHeader;
196 if (IsAngled)
197 if (auto StandardHeader =
198 tooling::stdlib::Header::named("<" + FileName.str() + ">")) {
199 IncludedHeader = *StandardHeader;
201 if (!IncludedHeader && File)
202 IncludedHeader = &File->getFileEntry();
203 checkForExport(HashFID, HashLine, std::move(IncludedHeader));
204 checkForKeep(HashLine);
207 void checkForExport(FileID IncludingFile, int HashLine,
208 std::optional<Header> IncludedHeader) {
209 if (ExportStack.empty())
210 return;
211 auto &Top = ExportStack.back();
212 if (Top.SeenAtFile != IncludingFile)
213 return;
214 // Make sure current include is covered by the export pragma.
215 if ((Top.Block && HashLine > Top.SeenAtLine) ||
216 Top.SeenAtLine == HashLine) {
217 if (IncludedHeader) {
218 switch (IncludedHeader->kind()) {
219 case Header::Physical:
220 Out->IWYUExportBy[IncludedHeader->physical()->getUniqueID()]
221 .push_back(Top.Path);
222 break;
223 case Header::Standard:
224 Out->StdIWYUExportBy[IncludedHeader->standard()].push_back(Top.Path);
225 break;
226 case Header::Verbatim:
227 assert(false && "unexpected Verbatim header");
228 break;
231 // main-file #include with export pragma should never be removed.
232 if (Top.SeenAtFile == SM.getMainFileID())
233 Out->ShouldKeep.insert(HashLine);
235 if (!Top.Block) // Pop immediately for single-line export pragma.
236 ExportStack.pop_back();
239 void checkForKeep(int HashLine) {
240 if (!InMainFile || KeepStack.empty())
241 return;
242 KeepPragma &Top = KeepStack.back();
243 // Check if the current include is covered by a keep pragma.
244 if ((Top.Block && HashLine > Top.SeenAtLine) || Top.SeenAtLine == HashLine)
245 Out->ShouldKeep.insert(HashLine);
247 if (!Top.Block)
248 KeepStack.pop_back(); // Pop immediately for single-line keep pragma.
251 bool HandleComment(Preprocessor &PP, SourceRange Range) override {
252 auto &SM = PP.getSourceManager();
253 auto Pragma =
254 tooling::parseIWYUPragma(SM.getCharacterData(Range.getBegin()));
255 if (!Pragma)
256 return false;
258 if (Pragma->consume_front("private")) {
259 auto *FE = SM.getFileEntryForID(SM.getFileID(Range.getBegin()));
260 if (!FE)
261 return false;
262 StringRef PublicHeader;
263 if (Pragma->consume_front(", include ")) {
264 // We always insert using the spelling from the pragma.
265 PublicHeader = save(Pragma->startswith("<") || Pragma->startswith("\"")
266 ? (*Pragma)
267 : ("\"" + *Pragma + "\"").str());
269 Out->IWYUPublic.insert({FE->getLastRef().getUniqueID(), PublicHeader});
270 return false;
272 FileID CommentFID = SM.getFileID(Range.getBegin());
273 int CommentLine = SM.getLineNumber(SM.getFileID(Range.getBegin()),
274 SM.getFileOffset(Range.getBegin()));
275 // Record export pragma.
276 if (Pragma->startswith("export")) {
277 ExportStack.push_back({CommentLine, CommentFID,
278 save(SM.getFileEntryForID(CommentFID)->getName()),
279 false});
280 } else if (Pragma->startswith("begin_exports")) {
281 ExportStack.push_back({CommentLine, CommentFID,
282 save(SM.getFileEntryForID(CommentFID)->getName()),
283 true});
284 } else if (Pragma->startswith("end_exports")) {
285 // FIXME: be robust on unmatching cases. We should only pop the stack if
286 // the begin_exports and end_exports is in the same file.
287 if (!ExportStack.empty()) {
288 assert(ExportStack.back().Block);
289 ExportStack.pop_back();
293 if (InMainFile) {
294 if (Pragma->startswith("keep")) {
295 KeepStack.push_back({CommentLine, false});
296 } else if (Pragma->starts_with("begin_keep")) {
297 KeepStack.push_back({CommentLine, true});
298 } else if (Pragma->starts_with("end_keep") && !KeepStack.empty()) {
299 assert(KeepStack.back().Block);
300 KeepStack.pop_back();
303 return false;
306 private:
307 StringRef save(llvm::StringRef S) { return UniqueStrings.save(S); }
309 bool InMainFile = false;
310 const SourceManager &SM;
311 const HeaderSearch &HeaderInfo;
312 PragmaIncludes *Out;
313 llvm::BumpPtrAllocator Arena;
314 /// Intern table for strings. Contents are on the arena.
315 llvm::StringSaver UniqueStrings;
317 struct ExportPragma {
318 // The line number where we saw the begin_exports or export pragma.
319 int SeenAtLine = 0; // 1-based line number.
320 // The file where we saw the pragma.
321 FileID SeenAtFile;
322 // Name (per FileEntry::getName()) of the file SeenAtFile.
323 StringRef Path;
324 // true if it is a block begin/end_exports pragma; false if it is a
325 // single-line export pragma.
326 bool Block = false;
328 // A stack for tracking all open begin_exports or single-line export.
329 std::vector<ExportPragma> ExportStack;
331 struct KeepPragma {
332 // The line number where we saw the begin_keep or keep pragma.
333 int SeenAtLine = 0; // 1-based line number.
334 // true if it is a block begin/end_keep pragma; false if it is a
335 // single-line keep pragma.
336 bool Block = false;
338 // A stack for tracking all open begin_keep pragmas or single-line keeps.
339 std::vector<KeepPragma> KeepStack;
342 void PragmaIncludes::record(const CompilerInstance &CI) {
343 auto Record = std::make_unique<RecordPragma>(CI, this);
344 CI.getPreprocessor().addCommentHandler(Record.get());
345 CI.getPreprocessor().addPPCallbacks(std::move(Record));
348 void PragmaIncludes::record(Preprocessor &P) {
349 auto Record = std::make_unique<RecordPragma>(P, this);
350 P.addCommentHandler(Record.get());
351 P.addPPCallbacks(std::move(Record));
354 llvm::StringRef PragmaIncludes::getPublic(const FileEntry *F) const {
355 auto It = IWYUPublic.find(F->getUniqueID());
356 if (It == IWYUPublic.end())
357 return "";
358 return It->getSecond();
361 static llvm::SmallVector<const FileEntry *>
362 toFileEntries(llvm::ArrayRef<StringRef> FileNames, FileManager &FM) {
363 llvm::SmallVector<const FileEntry *> Results;
365 for (auto FName : FileNames) {
366 // FIMXE: log the failing cases?
367 if (auto FE = expectedToOptional(FM.getFileRef(FName)))
368 Results.push_back(*FE);
370 return Results;
372 llvm::SmallVector<const FileEntry *>
373 PragmaIncludes::getExporters(const FileEntry *File, FileManager &FM) const {
374 auto It = IWYUExportBy.find(File->getUniqueID());
375 if (It == IWYUExportBy.end())
376 return {};
378 return toFileEntries(It->getSecond(), FM);
380 llvm::SmallVector<const FileEntry *>
381 PragmaIncludes::getExporters(tooling::stdlib::Header StdHeader,
382 FileManager &FM) const {
383 auto It = StdIWYUExportBy.find(StdHeader);
384 if (It == StdIWYUExportBy.end())
385 return {};
386 return toFileEntries(It->getSecond(), FM);
389 bool PragmaIncludes::isSelfContained(const FileEntry *FE) const {
390 return !NonSelfContainedFiles.contains(FE->getUniqueID());
393 bool PragmaIncludes::isPrivate(const FileEntry *FE) const {
394 return IWYUPublic.contains(FE->getUniqueID());
397 namespace {
398 template <typename T> bool isImplicitTemplateSpecialization(const Decl *D) {
399 if (const auto *TD = dyn_cast<T>(D))
400 return TD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation;
401 return false;
403 } // namespace
405 std::unique_ptr<ASTConsumer> RecordedAST::record() {
406 class Recorder : public ASTConsumer {
407 RecordedAST *Out;
409 public:
410 Recorder(RecordedAST *Out) : Out(Out) {}
411 void Initialize(ASTContext &Ctx) override { Out->Ctx = &Ctx; }
412 bool HandleTopLevelDecl(DeclGroupRef DG) override {
413 const auto &SM = Out->Ctx->getSourceManager();
414 for (Decl *D : DG) {
415 if (!SM.isWrittenInMainFile(SM.getExpansionLoc(D->getLocation())))
416 continue;
417 if (isImplicitTemplateSpecialization<FunctionDecl>(D) ||
418 isImplicitTemplateSpecialization<CXXRecordDecl>(D) ||
419 isImplicitTemplateSpecialization<VarDecl>(D))
420 continue;
421 // FIXME: Filter out certain Obj-C as well.
422 Out->Roots.push_back(D);
424 return ASTConsumer::HandleTopLevelDecl(DG);
428 return std::make_unique<Recorder>(this);
431 std::unique_ptr<PPCallbacks> RecordedPP::record(const Preprocessor &PP) {
432 return std::make_unique<PPRecorder>(*this, PP);
435 } // namespace clang::include_cleaner