10#include "clang/Basic/SourceManager.h"
11#include "clang/Lex/Lexer.h"
20StringRef removeFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
21 for (StringRef Suffix : Suffixes) {
22 if (Str.consume_back(Suffix))
28StringRef makeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
35 if (Style == IncludeSorter::IS_LLVM) {
36 return removeFirstSuffix(
37 removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}), {
"Test"});
39 if (Style == IncludeSorter::IS_Google_ObjC) {
41 removeFirstSuffix(removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
42 ".hpp",
".mm",
".m"}),
43 {
"_unittest",
"_regtest",
"_test",
"Test"});
47 size_t StartIndex =
Canonical.find_last_of(
'/');
48 if (StartIndex == StringRef::npos) {
53 return removeFirstSuffix(
54 removeFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}),
55 {
"_unittest",
"_regtest",
"_test"});
59size_t findNextLine(
const char *Text) {
60 size_t EOLIndex = std::strcspn(Text,
"\n");
61 return Text[EOLIndex] ==
'\0' ? EOLIndex : EOLIndex + 1;
64IncludeSorter::IncludeKinds
65determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
66 bool IsAngled, IncludeSorter::IncludeStyle Style) {
74 return IncludeFile.ends_with(
".h") ? IncludeSorter::IK_CSystemInclude
75 : IncludeSorter::IK_CXXSystemInclude;
77 StringRef CanonicalInclude = makeCanonicalName(IncludeFile, Style);
78 if (CanonicalFile.ends_with(CanonicalInclude) ||
79 CanonicalInclude.ends_with(CanonicalFile)) {
80 return IncludeSorter::IK_MainTUInclude;
82 if ((Style == IncludeSorter::IS_Google) ||
83 (Style == IncludeSorter::IS_Google_ObjC)) {
84 std::pair<StringRef, StringRef> Parts = CanonicalInclude.split(
"/public/");
85 StringRef FileCopy = CanonicalFile;
86 if (FileCopy.consume_front(Parts.first) &&
87 FileCopy.consume_back(Parts.second)) {
89 if (FileCopy ==
"/internal/" || FileCopy ==
"/proto/") {
90 return IncludeSorter::IK_MainTUInclude;
94 if (Style == IncludeSorter::IS_Google_ObjC) {
95 if (IncludeFile.ends_with(
".generated.h") ||
96 IncludeFile.ends_with(
".proto.h") ||
97 IncludeFile.ends_with(
".pbobjc.h")) {
98 return IncludeSorter::IK_GeneratedInclude;
101 return IncludeSorter::IK_NonSystemInclude;
104int compareHeaders(StringRef LHS, StringRef RHS,
105 IncludeSorter::IncludeStyle Style) {
106 if (Style == IncludeSorter::IncludeStyle::IS_Google_ObjC) {
107 const std::pair<const char *, const char *> &Mismatch =
108 std::mismatch(LHS.begin(), LHS.end(), RHS.begin(), RHS.end());
109 if ((Mismatch.first != LHS.end()) && (Mismatch.second != RHS.end())) {
110 if ((*Mismatch.first ==
'.') && (*Mismatch.second ==
'+')) {
113 if ((*Mismatch.first ==
'+') && (*Mismatch.second ==
'.')) {
118 return LHS.compare(RHS);
123IncludeSorter::IncludeSorter(
const SourceManager *SourceMgr,
124 const FileID FileID, StringRef FileName,
126 : SourceMgr(SourceMgr), Style(Style), CurrentFileID(FileID),
127 CanonicalFile(makeCanonicalName(FileName, Style)) {}
129void IncludeSorter::addInclude(StringRef FileName,
bool IsAngled,
130 SourceLocation HashLocation,
131 SourceLocation EndLocation) {
132 int Offset = findNextLine(SourceMgr->getCharacterData(EndLocation));
135 auto &IncludeLocation = IncludeLocations[FileName];
136 IncludeLocation.push_back(
137 SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
138 SourceLocations.push_back(IncludeLocation.back());
141 if (IncludeLocation.size() > 1)
146 determineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
147 if (Kind != IK_InvalidInclude)
148 IncludeBucket[Kind].push_back(FileName.str());
151std::optional<FixItHint>
152IncludeSorter::createIncludeInsertion(StringRef FileName,
bool IsAngled) {
153 std::string IncludeStmt;
154 if (Style == IncludeStyle::IS_Google_ObjC) {
155 IncludeStmt = IsAngled
156 ? llvm::Twine(
"#import <" + FileName +
">\n").str()
157 : llvm::Twine(
"#import \"" + FileName +
"\"\n").str();
159 IncludeStmt = IsAngled
160 ? llvm::Twine(
"#include <" + FileName +
">\n").str()
161 : llvm::Twine(
"#include \"" + FileName +
"\"\n").str();
163 if (SourceLocations.empty()) {
166 IncludeStmt.append(
"\n");
167 return FixItHint::CreateInsertion(
168 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
172 determineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
174 if (!IncludeBucket[IncludeKind].empty()) {
175 for (
const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
176 if (compareHeaders(FileName, IncludeEntry, Style) < 0) {
177 const auto &
Location = IncludeLocations[IncludeEntry][0];
178 return FixItHint::CreateInsertion(
Location.getBegin(), IncludeStmt);
180 if (FileName == IncludeEntry) {
186 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
187 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
188 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
196 IncludeKinds NonEmptyKind = IK_InvalidInclude;
197 for (
int I = IK_InvalidInclude - 1; I >= 0; --I) {
198 if (!IncludeBucket[I].empty()) {
199 NonEmptyKind =
static_cast<IncludeKinds
>(I);
200 if (NonEmptyKind < IncludeKind)
204 if (NonEmptyKind == IK_InvalidInclude) {
208 if (NonEmptyKind < IncludeKind) {
210 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
211 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
212 IncludeStmt =
'\n' + IncludeStmt;
213 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
217 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
218 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
219 IncludeStmt.append(
"\n");
220 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
226llvm::ArrayRef<std::pair<utils::IncludeSorter::IncludeStyle, StringRef>>
228 static constexpr std::pair<utils::IncludeSorter::IncludeStyle, StringRef>
229 Mapping[] = {{utils::IncludeSorter::IS_LLVM,
"llvm"},
230 {utils::IncludeSorter::IS_Google,
"google"},
231 {utils::IncludeSorter::IS_Google_ObjC,
"google-objc"}};
@ Canonical
The two mix because the types refer to the same CanonicalType, but we do not elaborate as to how.
static ArrayRef< std::pair< utils::IncludeSorter::IncludeStyle, StringRef > > getEnumMapping()