clang-tools  12.0.0git
IncludeSorter.cpp
Go to the documentation of this file.
1 //===---------- IncludeSorter.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 "IncludeSorter.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Lex/Lexer.h"
12 
13 namespace clang {
14 namespace tidy {
15 namespace utils {
16 
17 namespace {
18 
19 StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
20  for (StringRef Suffix : Suffixes) {
21  if (Str.endswith(Suffix)) {
22  return Str.substr(0, Str.size() - Suffix.size());
23  }
24  }
25  return Str;
26 }
27 
28 StringRef MakeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
29  // The list of suffixes to remove from source file names to get the
30  // "canonical" file names.
31  // E.g. tools/sort_includes.cc and tools/sort_includes_test.cc
32  // would both canonicalize to tools/sort_includes and tools/sort_includes.h
33  // (once canonicalized) will match as being the main include file associated
34  // with the source files.
35  if (Style == IncludeSorter::IS_LLVM) {
36  return RemoveFirstSuffix(
37  RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
38  }
39  return RemoveFirstSuffix(
40  RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
41  {"_unittest", "_regtest", "_test"});
42 }
43 
44 // Scan to the end of the line and return the offset of the next line.
45 size_t FindNextLine(const char *Text) {
46  size_t EOLIndex = std::strcspn(Text, "\n");
47  return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1;
48 }
49 
51 DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
53  // Compute the two "canonical" forms of the include's filename sans extension.
54  // The first form is the include's filename without ".h" or "-inl.h" at the
55  // end. The second form is the first form with "/public/" in the file path
56  // replaced by "/internal/".
57  if (IsAngled) {
58  // If the system include (<foo>) ends with ".h", then it is a normal C-style
59  // include. Otherwise assume it is a C++-style extensionless include.
60  return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude
62  }
63  StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
64  if (CanonicalFile.endswith(CanonicalInclude)
65  || CanonicalInclude.endswith(CanonicalFile)) {
67  }
68  if (Style == IncludeSorter::IS_Google) {
69  std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
70  std::string AltCanonicalInclude =
71  Parts.first.str() + "/internal/" + Parts.second.str();
72  std::string ProtoCanonicalInclude =
73  Parts.first.str() + "/proto/" + Parts.second.str();
74 
75  // Determine the kind of this inclusion.
76  if (CanonicalFile.equals(AltCanonicalInclude) ||
77  CanonicalFile.equals(ProtoCanonicalInclude)) {
79  }
80  }
82 }
83 
84 } // namespace
85 
87  const FileID FileID, StringRef FileName,
88  IncludeStyle Style)
89  : SourceMgr(SourceMgr), Style(Style), CurrentFileID(FileID),
90  CanonicalFile(MakeCanonicalName(FileName, Style)) {}
91 
92 void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled,
93  SourceLocation HashLocation,
94  SourceLocation EndLocation) {
95  int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
96 
97  // Record the relevant location information for this inclusion directive.
98  IncludeLocations[FileName].push_back(
99  SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
100  SourceLocations.push_back(IncludeLocations[FileName].back());
101 
102  // Stop if this inclusion is a duplicate.
103  if (IncludeLocations[FileName].size() > 1)
104  return;
105 
106  // Add the included file's name to the appropriate bucket.
108  DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
109  if (Kind != IK_InvalidInclude)
110  IncludeBucket[Kind].push_back(FileName.str());
111 }
112 
113 Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
114  bool IsAngled) {
115  std::string IncludeStmt =
116  IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
117  : llvm::Twine("#include \"" + FileName + "\"\n").str();
118  if (SourceLocations.empty()) {
119  // If there are no includes in this file, add it in the first line.
120  // FIXME: insert after the file comment or the header guard, if present.
121  IncludeStmt.append("\n");
122  return FixItHint::CreateInsertion(
123  SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
124  }
125 
126  auto IncludeKind =
127  DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
128 
129  if (!IncludeBucket[IncludeKind].empty()) {
130  for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
131  if (FileName < IncludeEntry) {
132  const auto &Location = IncludeLocations[IncludeEntry][0];
133  return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
134  } else if (FileName == IncludeEntry) {
135  return llvm::None;
136  }
137  }
138  // FileName comes after all include entries in bucket, insert it after
139  // last.
140  const std::string &LastInclude = IncludeBucket[IncludeKind].back();
141  SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
142  return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
143  IncludeStmt);
144  }
145  // Find the non-empty include bucket to be sorted directly above
146  // 'IncludeKind'. If such a bucket exists, we'll want to sort the include
147  // after that bucket. If no such bucket exists, find the first non-empty
148  // include bucket in the file. In that case, we'll want to sort the include
149  // before that bucket.
150  IncludeKinds NonEmptyKind = IK_InvalidInclude;
151  for (int i = IK_InvalidInclude - 1; i >= 0; --i) {
152  if (!IncludeBucket[i].empty()) {
153  NonEmptyKind = static_cast<IncludeKinds>(i);
154  if (NonEmptyKind < IncludeKind)
155  break;
156  }
157  }
158  if (NonEmptyKind == IK_InvalidInclude) {
159  return llvm::None;
160  }
161 
162  if (NonEmptyKind < IncludeKind) {
163  // Create a block after.
164  const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
165  SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
166  IncludeStmt = '\n' + IncludeStmt;
167  return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
168  IncludeStmt);
169  }
170  // Create a block before.
171  const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
172  SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
173  IncludeStmt.append("\n");
174  return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
175  IncludeStmt);
176 }
177 
178 } // namespace utils
179 
180 llvm::ArrayRef<std::pair<utils::IncludeSorter::IncludeStyle, StringRef>>
182  static constexpr std::pair<utils::IncludeSorter::IncludeStyle, StringRef>
183  Mapping[] = {{utils::IncludeSorter::IS_LLVM, "llvm"},
184  {utils::IncludeSorter::IS_Google, "google"}};
185  return makeArrayRef(Mapping);
186 }
187 } // namespace tidy
188 } // namespace clang
e.g. #include "foo.h" when editing foo.cc
Definition: IncludeSorter.h:30
std::string Suffix
Definition: AddUsing.cpp:96
IncludeStyle
Supported include styles.
Definition: IncludeSorter.h:26
llvm::SourceMgr * SourceMgr
IncludeKinds
The classifications of inclusions, in the order they should be sorted.
Definition: IncludeSorter.h:29
BindArgumentKind Kind
bool IsAngled
true if this was an include with angle brackets
void AddInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation)
Adds the given include directive to the sorter.
total number of valid IncludeKinds
Definition: IncludeSorter.h:34
PathRef FileName
size_t Offset
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static ArrayRef< std::pair< T, StringRef > > getEnumMapping()=delete
Optional< FixItHint > CreateIncludeInsertion(StringRef FileName, bool IsAngled)
Creates a quoted inclusion directive in the right sort order.
IncludeSorter(const SourceManager *SourceMgr, const FileID FileID, StringRef FileName, IncludeStyle Style)
IncludeSorter constructor; takes the FileID and name of the file to be processed by the sorter...
static constexpr llvm::StringLiteral Suffixes