1 //===--- Record.cpp - Record compiler events ------------------------------===//
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
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"
25 namespace clang::include_cleaner
{
28 class PPRecorder
: public PPCallbacks
{
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
{
49 I
.HashLocation
= Hash
;
50 I
.Resolved
= File
? &File
->getFileEntry() : nullptr;
51 I
.Line
= SM
.getSpellingLineNumber(Hash
);
52 I
.Spelled
= SpelledFilename
;
54 Recorded
.Includes
.add(I
);
57 void MacroExpands(const Token
&MacroName
, const MacroDefinition
&MD
,
58 SourceRange Range
, const MacroArgs
*Args
) override
{
61 recordMacroRef(MacroName
, *MD
.getMacroInfo());
64 void MacroDefined(const Token
&MacroName
, const MacroDirective
*MD
) override
{
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
))
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
{
87 if (const auto *MI
= MD
.getMacroInfo())
88 recordMacroRef(MacroName
, *MI
);
91 void Ifdef(SourceLocation Loc
, const Token
&MacroNameTok
,
92 const MacroDefinition
&MD
) override
{
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
{
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
{
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
{
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
{
128 if (const auto *MI
= MD
.getMacroInfo())
129 recordMacroRef(MacroNameTok
, *MI
, RefType::Ambiguous
);
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
});
143 RecordedPP
&Recorded
;
144 const Preprocessor
&PP
;
145 const SourceManager
&SM
;
150 class PragmaIncludes::RecordPragma
: public PPCallbacks
, public CommentHandler
{
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());
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
;
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())
211 auto &Top
= ExportStack
.back();
212 if (Top
.SeenAtFile
!= IncludingFile
)
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
);
223 case Header::Standard
:
224 Out
->StdIWYUExportBy
[IncludedHeader
->standard()].push_back(Top
.Path
);
226 case Header::Verbatim
:
227 assert(false && "unexpected Verbatim header");
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())
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
);
248 KeepStack
.pop_back(); // Pop immediately for single-line keep pragma.
251 bool HandleComment(Preprocessor
&PP
, SourceRange Range
) override
{
252 auto &SM
= PP
.getSourceManager();
254 tooling::parseIWYUPragma(SM
.getCharacterData(Range
.getBegin()));
258 if (Pragma
->consume_front("private")) {
259 auto *FE
= SM
.getFileEntryForID(SM
.getFileID(Range
.getBegin()));
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("\"")
267 : ("\"" + *Pragma
+ "\"").str());
269 Out
->IWYUPublic
.insert({FE
->getLastRef().getUniqueID(), PublicHeader
});
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()),
280 } else if (Pragma
->startswith("begin_exports")) {
281 ExportStack
.push_back({CommentLine
, CommentFID
,
282 save(SM
.getFileEntryForID(CommentFID
)->getName()),
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();
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();
307 StringRef
save(llvm::StringRef S
) { return UniqueStrings
.save(S
); }
309 bool InMainFile
= false;
310 const SourceManager
&SM
;
311 const HeaderSearch
&HeaderInfo
;
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.
322 // Name (per FileEntry::getName()) of the file SeenAtFile.
324 // true if it is a block begin/end_exports pragma; false if it is a
325 // single-line export pragma.
328 // A stack for tracking all open begin_exports or single-line export.
329 std::vector
<ExportPragma
> ExportStack
;
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.
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())
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
);
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())
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())
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());
398 template <typename T
> bool isImplicitTemplateSpecialization(const Decl
*D
) {
399 if (const auto *TD
= dyn_cast
<T
>(D
))
400 return TD
->getTemplateSpecializationKind() == TSK_ImplicitInstantiation
;
405 std::unique_ptr
<ASTConsumer
> RecordedAST::record() {
406 class Recorder
: public ASTConsumer
{
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();
415 if (!SM
.isWrittenInMainFile(SM
.getExpansionLoc(D
->getLocation())))
417 if (isImplicitTemplateSpecialization
<FunctionDecl
>(D
) ||
418 isImplicitTemplateSpecialization
<CXXRecordDecl
>(D
) ||
419 isImplicitTemplateSpecialization
<VarDecl
>(D
))
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