1336815Sdim//===--- HeaderIncludes.h - Insert/Delete #includes for C++ code--*- C++-*-===//
2336815Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6336815Sdim//
7336815Sdim//===----------------------------------------------------------------------===//
8336815Sdim
9336815Sdim#ifndef LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H
10336815Sdim#define LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H
11336815Sdim
12336815Sdim#include "clang/Basic/SourceManager.h"
13336815Sdim#include "clang/Tooling/Core/Replacement.h"
14336815Sdim#include "clang/Tooling/Inclusions/IncludeStyle.h"
15336815Sdim#include "llvm/Support/Path.h"
16336815Sdim#include "llvm/Support/Regex.h"
17336815Sdim#include <unordered_map>
18336815Sdim
19336815Sdimnamespace clang {
20336815Sdimnamespace tooling {
21336815Sdim
22336815Sdim/// This class manages priorities of C++ #include categories and calculates
23336815Sdim/// priorities for headers.
24336815Sdim/// FIXME(ioeric): move this class into implementation file when clang-format's
25336815Sdim/// include sorting functions are also moved here.
26336815Sdimclass IncludeCategoryManager {
27336815Sdimpublic:
28336815Sdim  IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName);
29336815Sdim
30336815Sdim  /// Returns the priority of the category which \p IncludeName belongs to.
31336815Sdim  /// If \p CheckMainHeader is true and \p IncludeName is a main header, returns
32336815Sdim  /// 0. Otherwise, returns the priority of the matching category or INT_MAX.
33336815Sdim  /// NOTE: this API is not thread-safe!
34336815Sdim  int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const;
35360784Sdim  int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const;
36336815Sdim
37336815Sdimprivate:
38336815Sdim  bool isMainHeader(StringRef IncludeName) const;
39336815Sdim
40336815Sdim  const IncludeStyle Style;
41336815Sdim  bool IsMainFile;
42336815Sdim  std::string FileName;
43336815Sdim  // This refers to a substring in FileName.
44336815Sdim  StringRef FileStem;
45360784Sdim  SmallVector<llvm::Regex, 4> CategoryRegexs;
46336815Sdim};
47336815Sdim
48336815Sdim/// Generates replacements for inserting or deleting #include directives in a
49336815Sdim/// file.
50336815Sdimclass HeaderIncludes {
51336815Sdimpublic:
52336815Sdim  HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code,
53336815Sdim                 const IncludeStyle &Style);
54336815Sdim
55336815Sdim  /// Inserts an #include directive of \p Header into the code. If \p IsAngled
56336815Sdim  /// is true, \p Header will be quoted with <> in the directive; otherwise, it
57336815Sdim  /// will be quoted with "".
58336815Sdim  ///
59336815Sdim  /// When searching for points to insert new header, this ignores #include's
60336815Sdim  /// after the #include block(s) in the beginning of a file to avoid inserting
61336815Sdim  /// headers into code sections where new #include's should not be added by
62336815Sdim  /// default. These code sections include:
63336815Sdim  ///   - raw string literals (containing #include).
64336815Sdim  ///   - #if blocks.
65336815Sdim  ///   - Special #include's among declarations (e.g. functions).
66336815Sdim  ///
67336815Sdim  /// Returns a replacement that inserts the new header into a suitable #include
68336815Sdim  /// block of the same category. This respects the order of the existing
69336815Sdim  /// #includes in the block; if the existing #includes are not already sorted,
70336815Sdim  /// this will simply insert the #include in front of the first #include of the
71336815Sdim  /// same category in the code that should be sorted after \p IncludeName. If
72336815Sdim  /// \p IncludeName already exists (with exactly the same spelling), this
73336815Sdim  /// returns None.
74336815Sdim  llvm::Optional<tooling::Replacement> insert(llvm::StringRef Header,
75336815Sdim                                              bool IsAngled) const;
76336815Sdim
77336815Sdim  /// Removes all existing #includes of \p Header quoted with <> if \p IsAngled
78336815Sdim  /// is true or "" if \p IsAngled is false.
79336815Sdim  /// This doesn't resolve the header file path; it only deletes #includes with
80336815Sdim  /// exactly the same spelling.
81336815Sdim  tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const;
82336815Sdim
83336815Sdimprivate:
84336815Sdim  struct Include {
85336815Sdim    Include(StringRef Name, tooling::Range R) : Name(Name), R(R) {}
86336815Sdim
87336815Sdim    // An include header quoted with either <> or "".
88336815Sdim    std::string Name;
89336815Sdim    // The range of the whole line of include directive including any eading
90336815Sdim    // whitespaces and trailing comment.
91336815Sdim    tooling::Range R;
92336815Sdim  };
93336815Sdim
94336815Sdim  void addExistingInclude(Include IncludeToAdd, unsigned NextLineOffset);
95336815Sdim
96336815Sdim  std::string FileName;
97336815Sdim  std::string Code;
98336815Sdim
99336815Sdim  // Map from include name (quotation trimmed) to a list of existing includes
100336815Sdim  // (in case there are more than one) with the name in the current file. <x>
101336815Sdim  // and "x" will be treated as the same header when deleting #includes.
102336815Sdim  llvm::StringMap<llvm::SmallVector<Include, 1>> ExistingIncludes;
103336815Sdim
104336815Sdim  /// Map from priorities of #include categories to all #includes in the same
105336815Sdim  /// category. This is used to find #includes of the same category when
106336815Sdim  /// inserting new #includes. #includes in the same categories are sorted in
107336815Sdim  /// in the order they appear in the source file.
108336815Sdim  /// See comment for "FormatStyle::IncludeCategories" for details about include
109336815Sdim  /// priorities.
110336815Sdim  std::unordered_map<int, llvm::SmallVector<const Include *, 8>>
111336815Sdim      IncludesByPriority;
112336815Sdim
113336815Sdim  int FirstIncludeOffset;
114336815Sdim  // All new headers should be inserted after this offset (e.g. after header
115336815Sdim  // guards, file comment).
116336815Sdim  unsigned MinInsertOffset;
117336815Sdim  // Max insertion offset in the original code. For example, we want to avoid
118336815Sdim  // inserting new #includes into the actual code section (e.g. after a
119336815Sdim  // declaration).
120336815Sdim  unsigned MaxInsertOffset;
121336815Sdim  IncludeCategoryManager Categories;
122336815Sdim  // Record the offset of the end of the last include in each category.
123336815Sdim  std::unordered_map<int, int> CategoryEndOffsets;
124336815Sdim
125336815Sdim  // All possible priorities.
126336815Sdim  std::set<int> Priorities;
127336815Sdim
128336815Sdim  // Matches a whole #include directive.
129336815Sdim  llvm::Regex IncludeRegex;
130336815Sdim};
131336815Sdim
132336815Sdim
133336815Sdim} // namespace tooling
134336815Sdim} // namespace clang
135336815Sdim
136336815Sdim#endif // LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H
137