xref: /llvm-project/clang-tools-extra/clang-tidy/utils/IncludeInserter.cpp (revision da95d926f6fce4ed9707c77908ad96624268f134)
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:
IncludeInserterCallback(IncludeInserter * Inserter)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.
InclusionDirective(SourceLocation HashLocation,const Token & IncludeToken,StringRef FileNameRef,bool IsAngled,CharSourceRange FileNameRange,OptionalFileEntryRef,StringRef,StringRef,const Module *,bool,SrcMgr::CharacteristicKind)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(FileNameRef, IsAngled, HashLocation,
32                          IncludeToken.getEndLoc());
33   }
34 
35 private:
36   IncludeInserter *Inserter;
37 };
38 
IncludeInserter(IncludeSorter::IncludeStyle Style,bool SelfContainedDiags)39 IncludeInserter::IncludeInserter(IncludeSorter::IncludeStyle Style,
40                                  bool SelfContainedDiags)
41     : Style(Style), SelfContainedDiags(SelfContainedDiags) {}
42 
registerPreprocessor(Preprocessor * PP)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(std::make_unique<IncludeInserterCallback>(this));
53 }
54 
getOrCreate(FileID FileID)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         SourceMgr, FileID,
65         SourceMgr->getFilename(SourceMgr->getLocForStartOfFile(FileID)), Style);
66   }
67   return *Entry;
68 }
69 
70 std::optional<FixItHint>
createIncludeInsertion(FileID FileID,llvm::StringRef Header)71 IncludeInserter::createIncludeInsertion(FileID FileID, llvm::StringRef Header) {
72   bool IsAngled = Header.consume_front("<");
73   if (IsAngled != Header.consume_back(">"))
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(Header).second)
80     return std::nullopt;
81 
82   return getOrCreate(FileID).createIncludeInsertion(Header, IsAngled);
83 }
84 
85 std::optional<FixItHint>
createMainFileIncludeInsertion(StringRef Header)86 IncludeInserter::createMainFileIncludeInsertion(StringRef Header) {
87   assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call "
88                       "registerPreprocessor()?");
89   return createIncludeInsertion(SourceMgr->getMainFileID(), Header);
90 }
91 
addInclude(StringRef FileName,bool IsAngled,SourceLocation HashLocation,SourceLocation EndLocation)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(HashLocation);
98   getOrCreate(FileID).addInclude(FileName, IsAngled, HashLocation, EndLocation);
99 }
100 
101 } // namespace clang::tidy::utils
102