11 #include "clang/Lex/Lexer.h"
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());
36 return RemoveFirstSuffix(
37 RemoveFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}), {
"Test"});
39 return RemoveFirstSuffix(
40 RemoveFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}),
41 {
"_unittest",
"_regtest",
"_test"});
45 size_t FindNextLine(
const char *Text) {
46 size_t EOLIndex = std::strcspn(Text,
"\n");
47 return Text[EOLIndex] ==
'\0' ? EOLIndex : EOLIndex + 1;
51 DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
63 StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
64 if (CanonicalFile.endswith(CanonicalInclude)
65 || CanonicalInclude.endswith(CanonicalFile)) {
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();
76 if (CanonicalFile.equals(AltCanonicalInclude) ||
77 CanonicalFile.equals(ProtoCanonicalInclude)) {
87 const LangOptions *
LangOpts,
const FileID FileID,
89 : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
90 CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
94 SourceLocation HashLocation,
95 SourceLocation EndLocation) {
96 int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
99 IncludeLocations[FileName].push_back(
100 SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
101 SourceLocations.push_back(IncludeLocations[FileName].back());
104 if (IncludeLocations[FileName].size() > 1)
109 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
111 IncludeBucket[
Kind].push_back(FileName.str());
116 std::string IncludeStmt =
117 IsAngled ? llvm::Twine(
"#include <" + FileName +
">\n").str()
118 : llvm::Twine(
"#include \"" + FileName +
"\"\n").str();
119 if (SourceLocations.empty()) {
122 IncludeStmt.append(
"\n");
123 return FixItHint::CreateInsertion(
124 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
128 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
130 if (!IncludeBucket[IncludeKind].empty()) {
131 for (
const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
132 if (FileName < IncludeEntry) {
133 const auto &
Location = IncludeLocations[IncludeEntry][0];
134 return FixItHint::CreateInsertion(
Location.getBegin(), IncludeStmt);
135 }
else if (FileName == IncludeEntry) {
141 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
142 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
143 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
153 if (!IncludeBucket[i].empty()) {
155 if (NonEmptyKind < IncludeKind)
163 if (NonEmptyKind < IncludeKind) {
165 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
166 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
167 IncludeStmt =
'\n' + IncludeStmt;
168 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
172 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
173 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
174 IncludeStmt.append(
"\n");
175 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
180 if (SourceLocations.empty())
183 typedef std::map<int, std::pair<SourceRange, std::string>>
184 FileLineToSourceEditMap;
185 FileLineToSourceEditMap Edits;
186 auto SourceLocationIterator = SourceLocations.begin();
187 auto SourceLocationIteratorEnd = SourceLocations.end();
192 std::sort(IncludeBucket[IncludeKind].begin(),
193 IncludeBucket[IncludeKind].end());
194 for (
const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
195 auto &
Location = IncludeLocations[IncludeEntry];
196 SourceRangeVector::iterator LocationIterator =
Location.begin();
197 SourceRangeVector::iterator LocationIteratorEnd =
Location.end();
198 SourceRange FirstLocation = *LocationIterator;
202 if (FirstLocation == *SourceLocationIterator)
207 for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
209 SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
210 Edits[LineNumber] = std::make_pair(*LocationIterator,
"");
211 SourceLocationIteratorEnd =
212 std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
216 if (FirstLocation == *SourceLocationIterator) {
219 ++SourceLocationIterator;
225 SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
226 if (Edits.find(LineNumber) == Edits.end()) {
227 Edits[LineNumber].first =
228 SourceRange(SourceLocationIterator->getBegin());
230 StringRef SourceText = Lexer::getSourceText(
231 CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
232 Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
236 IncludeBucket[IncludeKind].clear();
240 int CurrentEndLine = 0;
241 SourceRange CurrentRange;
242 std::string CurrentText;
243 std::vector<FixItHint> Fixes;
244 for (
const auto &LineEdit : Edits) {
247 if (LineEdit.first == CurrentEndLine + 1 &&
248 CurrentRange.getBegin() != CurrentRange.getEnd()) {
249 SourceRange EditRange = LineEdit.second.first;
250 if (EditRange.getBegin() != EditRange.getEnd()) {
252 CurrentRange.setEnd(EditRange.getEnd());
254 CurrentText += LineEdit.second.second;
257 if (CurrentEndLine) {
258 Fixes.push_back(FixItHint::CreateReplacement(
259 CharSourceRange::getCharRange(CurrentRange), CurrentText));
262 CurrentEndLine = LineEdit.first;
263 CurrentRange = LineEdit.second.first;
264 CurrentText = LineEdit.second.second;
268 if (CurrentEndLine) {
269 Fixes.push_back(FixItHint::CreateReplacement(
270 CharSourceRange::getCharRange(CurrentRange), CurrentText));
274 SourceLocations.clear();
275 IncludeLocations.clear();
285 return Style ==
IS_LLVM ?
"llvm" :
"google";
e.g. #include "foo.h" when editing foo.cc
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
IncludeStyle
Supported include styles.
IncludeKinds
The classifications of inclusions, in the order they should be sorted.
IncludeSorter(const SourceManager *SourceMgr, const LangOptions *LangOpts, const FileID FileID, StringRef FileName, IncludeStyle Style)
IncludeSorter constructor; takes the FileID and name of the file to be processed by the sorter...
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
static IncludeStyle parseIncludeStyle(const std::string &Value)
Converts "llvm" to IS_LLVM, otherwise returns IS_Google.
Optional< FixItHint > CreateIncludeInsertion(StringRef FileName, bool IsAngled)
Creates a quoted inclusion directive in the right sort order.
std::vector< FixItHint > GetEdits()
Returns the edits needed to sort the current set of includes and reset the internal state (so that di...