clang-tools  11.0.0
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 
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
clang::tidy::utils::IncludeSorter::IK_CXXSystemInclude
e.g. #include <vector>
Definition: IncludeSorter.h:32
Suffix
std::string Suffix
Definition: AddUsing.cpp:96
Location
Definition: Modularize.cpp:383
clang::tidy::utils::IncludeSorter::IncludeStyle
IncludeStyle
Supported include styles.
Definition: IncludeSorter.h:26
clang::tidy::utils::IncludeSorter::IS_Google
Definition: IncludeSorter.h:26
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
clang::tidy::utils::IncludeSorter::IK_InvalidInclude
total number of valid IncludeKinds
Definition: IncludeSorter.h:34
Text
std::string Text
Definition: HTMLGenerator.cpp:80
SourceMgr
llvm::SourceMgr * SourceMgr
Definition: ConfigCompile.cpp:72
Offset
size_t Offset
Definition: CodeComplete.cpp:1044
clang::tidy::utils::IncludeSorter::IK_MainTUInclude
e.g. #include "foo.h" when editing foo.cc
Definition: IncludeSorter.h:30
Suffixes
static constexpr llvm::StringLiteral Suffixes
Definition: UppercaseLiteralSuffixCheck.cpp:32
clang::tidy::utils::IncludeSorter::AddInclude
void AddInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation)
Adds the given include directive to the sorter.
Definition: IncludeSorter.cpp:92
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:40
clang::tidy::OptionEnumMapping::getEnumMapping
static ArrayRef< std::pair< T, StringRef > > getEnumMapping()=delete
clang::tidy::utils::IncludeSorter::IncludeKinds
IncludeKinds
The classifications of inclusions, in the order they should be sorted.
Definition: IncludeSorter.h:29
IncludeSorter.h
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::IncludeSorter::CreateIncludeInsertion
Optional< FixItHint > CreateIncludeInsertion(StringRef FileName, bool IsAngled)
Creates a quoted inclusion directive in the right sort order.
Definition: IncludeSorter.cpp:113
FileName
PathRef FileName
Definition: CodeComplete.cpp:1043
clang::tidy::utils::IncludeSorter::IS_LLVM
Definition: IncludeSorter.h:26
clang::tidy::utils::IncludeSorter::IncludeSorter
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.
Definition: IncludeSorter.cpp:86
clang::tidy::utils::IncludeSorter::IK_CSystemInclude
e.g. #include <stdio.h>
Definition: IncludeSorter.h:31
clang::tidy::utils::IncludeSorter::IK_NonSystemInclude
e.g. #include "bar.h"
Definition: IncludeSorter.h:33