10#include "clang/Basic/SourceManager.h"
11#include "clang/Lex/Lexer.h"
20StringRef removeFirstSuffix(StringRef Str, ArrayRef<const char *>
Suffixes) {
22 if (Str.ends_with(
Suffix)) {
23 return Str.substr(0, Str.size() -
Suffix.size());
37 return removeFirstSuffix(
38 removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}), {
"Test"});
42 removeFirstSuffix(removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
43 ".hpp",
".mm",
".m"}),
44 {
"_unittest",
"_regtest",
"_test",
"Test"});
48 size_t StartIndex =
Canonical.find_last_of(
'/');
49 if (StartIndex == StringRef::npos) {
53 0,
Canonical.find_first_of(
'+', StartIndex));
55 return removeFirstSuffix(
56 removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}),
57 {
"_unittest",
"_regtest",
"_test"});
61size_t findNextLine(
const char *
Text) {
62 size_t EOLIndex = std::strcspn(
Text,
"\n");
63 return Text[EOLIndex] ==
'\0' ? EOLIndex : EOLIndex + 1;
67determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
79 StringRef CanonicalInclude = makeCanonicalName(IncludeFile, Style);
80 if (CanonicalFile.ends_with(CanonicalInclude) ||
81 CanonicalInclude.ends_with(CanonicalFile)) {
86 std::pair<StringRef, StringRef> Parts = CanonicalInclude.split(
"/public/");
87 StringRef FileCopy = CanonicalFile;
88 if (FileCopy.consume_front(Parts.first) &&
89 FileCopy.consume_back(Parts.second)) {
91 if (FileCopy.equals(
"/internal/") ||
92 FileCopy.equals(
"/proto/")) {
98 if (IncludeFile.ends_with(
".generated.h") ||
99 IncludeFile.ends_with(
".proto.h") ||
100 IncludeFile.ends_with(
".pbobjc.h")) {
107int compareHeaders(StringRef LHS, StringRef RHS,
110 const std::pair<const char *, const char *> &Mismatch =
111 std::mismatch(LHS.begin(), LHS.end(), RHS.begin(), RHS.end());
112 if ((Mismatch.first != LHS.end()) && (Mismatch.second != RHS.end())) {
113 if ((*Mismatch.first ==
'.') && (*Mismatch.second ==
'+')) {
116 if ((*Mismatch.first ==
'+') && (*Mismatch.second ==
'.')) {
121 return LHS.compare(RHS);
127 const FileID FileID, StringRef
FileName,
129 : SourceMgr(SourceMgr), Style(Style), CurrentFileID(FileID),
130 CanonicalFile(makeCanonicalName(
FileName, Style)) {}
133 SourceLocation HashLocation,
134 SourceLocation EndLocation) {
135 int Offset = findNextLine(SourceMgr->getCharacterData(EndLocation));
138 auto &IncludeLocation = IncludeLocations[
FileName];
139 IncludeLocation.push_back(
140 SourceRange(HashLocation, EndLocation.getLocWithOffset(
Offset)));
141 SourceLocations.push_back(IncludeLocation.back());
144 if (IncludeLocation.size() > 1)
154std::optional<FixItHint>
156 std::string IncludeStmt;
159 ? llvm::Twine(
"#import <" +
FileName +
">\n").str()
160 : llvm::Twine(
"#import \"" +
FileName +
"\"\n").str();
163 ? llvm::Twine(
"#include <" +
FileName +
">\n").str()
164 : llvm::Twine(
"#include \"" +
FileName +
"\"\n").str();
166 if (SourceLocations.empty()) {
169 IncludeStmt.append(
"\n");
170 return FixItHint::CreateInsertion(
171 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
177 if (!IncludeBucket[IncludeKind].empty()) {
178 for (
const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
179 if (compareHeaders(
FileName, IncludeEntry, Style) < 0) {
180 const auto &
Location = IncludeLocations[IncludeEntry][0];
181 return FixItHint::CreateInsertion(
Location.getBegin(), IncludeStmt);
189 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
190 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
191 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
201 if (!IncludeBucket[I].empty()) {
203 if (NonEmptyKind < IncludeKind)
211 if (NonEmptyKind < IncludeKind) {
213 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
214 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
215 IncludeStmt =
'\n' + IncludeStmt;
216 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
220 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
221 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
222 IncludeStmt.append(
"\n");
223 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
229llvm::ArrayRef<std::pair<utils::IncludeSorter::IncludeStyle, StringRef>>
231 static constexpr std::pair<utils::IncludeSorter::IncludeStyle, StringRef>
bool IsAngled
true if this was an include with angle brackets
static constexpr llvm::StringLiteral Suffixes
IncludeKinds
The classifications of inclusions, in the order they should be sorted.
@ IK_CSystemInclude
e.g. #include <stdio.h>
@ IK_NonSystemInclude
e.g. #include "bar.h"
@ IK_InvalidInclude
total number of valid IncludeKinds
@ IK_MainTUInclude
e.g. #include "foo.h" when editing foo.cc
@ IK_GeneratedInclude
e.g. #include "bar.proto.h"
@ IK_CXXSystemInclude
e.g. #include <vector>
void addInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation)
Adds the given include directive to the sorter.
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.
IncludeStyle
Supported include styles.
std::optional< FixItHint > createIncludeInsertion(StringRef FileName, bool IsAngled)
Creates a quoted inclusion directive in the right sort order.
@ Canonical
The two mix because the types refer to the same CanonicalType, but we do not elaborate as to how.
static ArrayRef< std::pair< T, StringRef > > getEnumMapping()=delete