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