1 //===--- HeaderIncludes.h - Insert/Delete #includes for C++ code--*- C++-*-===// 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 #ifndef LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H 10 #define LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H 11 12 #include "clang/Basic/SourceManager.h" 13 #include "clang/Tooling/Core/Replacement.h" 14 #include "clang/Tooling/Inclusions/IncludeStyle.h" 15 #include "llvm/Support/Path.h" 16 #include "llvm/Support/Regex.h" 17 #include <list> 18 #include <optional> 19 #include <unordered_map> 20 21 namespace clang { 22 namespace tooling { 23 24 /// This class manages priorities of C++ #include categories and calculates 25 /// priorities for headers. 26 /// FIXME(ioeric): move this class into implementation file when clang-format's 27 /// include sorting functions are also moved here. 28 class IncludeCategoryManager { 29 public: 30 IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName); 31 32 /// Returns the priority of the category which \p IncludeName belongs to. 33 /// If \p CheckMainHeader is true and \p IncludeName is a main header, returns 34 /// 0. Otherwise, returns the priority of the matching category or INT_MAX. 35 /// NOTE: this API is not thread-safe! 36 int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; 37 int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; 38 39 private: 40 bool isMainHeader(StringRef IncludeName) const; 41 42 const IncludeStyle Style; 43 bool IsMainFile; 44 std::string FileName; 45 SmallVector<llvm::Regex, 4> CategoryRegexs; 46 }; 47 48 enum class IncludeDirective { Include, Import }; 49 50 /// Generates replacements for inserting or deleting #include directives in a 51 /// file. 52 class HeaderIncludes { 53 public: 54 HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, 55 const IncludeStyle &Style); 56 57 /// Inserts an #include or #import directive of \p Header into the code. 58 /// If \p IsAngled is true, \p Header will be quoted with <> in the directive; 59 /// otherwise, it will be quoted with "". 60 /// 61 /// When searching for points to insert new header, this ignores #include's 62 /// after the #include block(s) in the beginning of a file to avoid inserting 63 /// headers into code sections where new #include's should not be added by 64 /// default. These code sections include: 65 /// - raw string literals (containing #include). 66 /// - #if blocks. 67 /// - Special #include's among declarations (e.g. functions). 68 /// 69 /// Returns a replacement that inserts the new header into a suitable #include 70 /// block of the same category. This respects the order of the existing 71 /// #includes in the block; if the existing #includes are not already sorted, 72 /// this will simply insert the #include in front of the first #include of the 73 /// same category in the code that should be sorted after \p IncludeName. If 74 /// \p IncludeName already exists (with exactly the same spelling), this 75 /// returns std::nullopt. 76 std::optional<tooling::Replacement> insert(llvm::StringRef Header, 77 bool IsAngled, 78 IncludeDirective Directive) const; 79 80 /// Removes all existing #includes and #imports of \p Header quoted with <> if 81 /// \p IsAngled is true or "" if \p IsAngled is false. 82 /// This doesn't resolve the header file path; it only deletes #includes and 83 /// #imports with exactly the same spelling. 84 tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const; 85 86 // Matches a whole #include directive. 87 static const llvm::Regex IncludeRegex; 88 89 private: 90 struct Include { IncludeInclude91 Include(StringRef Name, tooling::Range R, IncludeDirective D) 92 : Name(Name), R(R), Directive(D) {} 93 94 // An include header quoted with either <> or "". 95 std::string Name; 96 // The range of the whole line of include directive including any leading 97 // whitespaces and trailing comment. 98 tooling::Range R; 99 // Either #include or #import. 100 IncludeDirective Directive; 101 }; 102 103 void addExistingInclude(Include IncludeToAdd, unsigned NextLineOffset); 104 105 std::string FileName; 106 std::string Code; 107 108 // Map from include name (quotation trimmed) to a list of existing includes 109 // (in case there are more than one) with the name in the current file. <x> 110 // and "x" will be treated as the same header when deleting #includes. 111 // std::list is used for pointers stability (see IncludesByPriority) 112 llvm::StringMap<std::list<Include>> ExistingIncludes; 113 114 /// Map from priorities of #include categories to all #includes in the same 115 /// category. This is used to find #includes of the same category when 116 /// inserting new #includes. #includes in the same categories are sorted in 117 /// in the order they appear in the source file. 118 /// See comment for "FormatStyle::IncludeCategories" for details about include 119 /// priorities. 120 std::unordered_map<int, llvm::SmallVector<const Include *, 8>> 121 IncludesByPriority; 122 123 int FirstIncludeOffset; 124 // All new headers should be inserted after this offset (e.g. after header 125 // guards, file comment). 126 unsigned MinInsertOffset; 127 // Max insertion offset in the original code. For example, we want to avoid 128 // inserting new #includes into the actual code section (e.g. after a 129 // declaration). 130 unsigned MaxInsertOffset; 131 // True if we find the main-file header in the Code. 132 bool MainIncludeFound; 133 IncludeCategoryManager Categories; 134 // Record the offset of the end of the last include in each category. 135 std::unordered_map<int, int> CategoryEndOffsets; 136 137 // All possible priorities. 138 std::set<int> Priorities; 139 }; 140 141 } // namespace tooling 142 } // namespace clang 143 144 #endif // LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H 145