| 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 | |