1 | //===-------- IncludeInserter.cpp - clang-tidy ----------------------------===// |
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 "IncludeInserter.h" |
10 | #include "clang/Lex/PPCallbacks.h" |
11 | #include "clang/Lex/Preprocessor.h" |
12 | #include "clang/Lex/Token.h" |
13 | #include <optional> |
14 | |
15 | namespace clang::tidy::utils { |
16 | |
17 | class IncludeInserterCallback : public PPCallbacks { |
18 | public: |
19 | explicit IncludeInserterCallback(IncludeInserter *Inserter) |
20 | : Inserter(Inserter) {} |
21 | // Implements PPCallbacks::InclusionDirective(). Records the names and source |
22 | // locations of the inclusions in the main source file being processed. |
23 | void InclusionDirective(SourceLocation HashLocation, |
24 | const Token &IncludeToken, StringRef FileNameRef, |
25 | bool IsAngled, CharSourceRange FileNameRange, |
26 | OptionalFileEntryRef /*IncludedFile*/, |
27 | StringRef /*SearchPath*/, StringRef /*RelativePath*/, |
28 | const Module * /*SuggestedModule*/, |
29 | bool /*ModuleImported*/, |
30 | SrcMgr::CharacteristicKind /*FileType*/) override { |
31 | Inserter->addInclude(FileName: FileNameRef, IsAngled, HashLocation, |
32 | EndLocation: IncludeToken.getEndLoc()); |
33 | } |
34 | |
35 | private: |
36 | IncludeInserter *Inserter; |
37 | }; |
38 | |
39 | IncludeInserter::IncludeInserter(IncludeSorter::IncludeStyle Style, |
40 | bool SelfContainedDiags) |
41 | : Style(Style), SelfContainedDiags(SelfContainedDiags) {} |
42 | |
43 | void IncludeInserter::registerPreprocessor(Preprocessor *PP) { |
44 | assert(PP && "PP shouldn't be null" ); |
45 | SourceMgr = &PP->getSourceManager(); |
46 | |
47 | // If this gets registered multiple times, clear the maps |
48 | if (!IncludeSorterByFile.empty()) |
49 | IncludeSorterByFile.clear(); |
50 | if (!InsertedHeaders.empty()) |
51 | InsertedHeaders.clear(); |
52 | PP->addPPCallbacks(C: std::make_unique<IncludeInserterCallback>(args: this)); |
53 | } |
54 | |
55 | IncludeSorter &IncludeInserter::getOrCreate(FileID FileID) { |
56 | assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call " |
57 | "registerPreprocessor()?" ); |
58 | // std::unique_ptr is cheap to construct, so force a construction now to save |
59 | // the lookup needed if we were to insert into the map. |
60 | std::unique_ptr<IncludeSorter> &Entry = IncludeSorterByFile[FileID]; |
61 | if (!Entry) { |
62 | // If it wasn't found, Entry will be default constructed to nullptr. |
63 | Entry = std::make_unique<IncludeSorter>( |
64 | args&: SourceMgr, args&: FileID, |
65 | args: SourceMgr->getFilename(SpellingLoc: SourceMgr->getLocForStartOfFile(FID: FileID)), args: Style); |
66 | } |
67 | return *Entry; |
68 | } |
69 | |
70 | std::optional<FixItHint> |
71 | IncludeInserter::createIncludeInsertion(FileID FileID, llvm::StringRef ) { |
72 | bool IsAngled = Header.consume_front(Prefix: "<" ); |
73 | if (IsAngled != Header.consume_back(Suffix: ">" )) |
74 | return std::nullopt; |
75 | // We assume the same Header will never be included both angled and not |
76 | // angled. |
77 | // In self contained diags mode we don't track what headers we have already |
78 | // inserted. |
79 | if (!SelfContainedDiags && !InsertedHeaders[FileID].insert(key: Header).second) |
80 | return std::nullopt; |
81 | |
82 | return getOrCreate(FileID).createIncludeInsertion(FileName: Header, IsAngled); |
83 | } |
84 | |
85 | std::optional<FixItHint> |
86 | IncludeInserter::createMainFileIncludeInsertion(StringRef ) { |
87 | assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call " |
88 | "registerPreprocessor()?" ); |
89 | return createIncludeInsertion(FileID: SourceMgr->getMainFileID(), Header); |
90 | } |
91 | |
92 | void IncludeInserter::addInclude(StringRef FileName, bool IsAngled, |
93 | SourceLocation HashLocation, |
94 | SourceLocation EndLocation) { |
95 | assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call " |
96 | "registerPreprocessor()?" ); |
97 | FileID FileID = SourceMgr->getFileID(SpellingLoc: HashLocation); |
98 | getOrCreate(FileID).addInclude(FileName, IsAngled, HashLocation, EndLocation); |
99 | } |
100 | |
101 | } // namespace clang::tidy::utils |
102 | |