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.equals(CanonicalInclude)) {
68 std::pair<StringRef, StringRef> Parts = CanonicalInclude.split(
"/public/");
69 std::string AltCanonicalInclude =
70 Parts.first.str() +
"/internal/" + Parts.second.str();
71 std::string ProtoCanonicalInclude =
72 Parts.first.str() +
"/proto/" + Parts.second.str();
75 if (CanonicalFile.equals(AltCanonicalInclude) ||
76 CanonicalFile.equals(ProtoCanonicalInclude)) {
86 const LangOptions *
LangOpts,
const FileID FileID,
88 : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
89 CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
93 SourceLocation HashLocation,
94 SourceLocation EndLocation) {
95 int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
98 IncludeLocations[FileName].push_back(
99 SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
100 SourceLocations.push_back(IncludeLocations[FileName].back());
103 if (IncludeLocations[FileName].size() > 1)
108 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
110 IncludeBucket[
Kind].push_back(FileName.str());
115 std::string IncludeStmt =
116 IsAngled ? llvm::Twine(
"#include <" + FileName +
">\n").str()
117 : llvm::Twine(
"#include \"" + FileName +
"\"\n").str();
118 if (SourceLocations.empty()) {
121 IncludeStmt.append(
"\n");
122 return FixItHint::CreateInsertion(
123 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
127 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
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) {
140 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
141 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
142 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
152 if (!IncludeBucket[i].empty()) {
154 if (NonEmptyKind < IncludeKind)
162 if (NonEmptyKind < IncludeKind) {
164 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
165 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
166 IncludeStmt =
'\n' + IncludeStmt;
167 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
171 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
172 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
173 IncludeStmt.append(
"\n");
174 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
179 if (SourceLocations.empty())
182 typedef std::map<int, std::pair<SourceRange, std::string>>
183 FileLineToSourceEditMap;
184 FileLineToSourceEditMap Edits;
185 auto SourceLocationIterator = SourceLocations.begin();
186 auto SourceLocationIteratorEnd = SourceLocations.end();
191 std::sort(IncludeBucket[IncludeKind].begin(),
192 IncludeBucket[IncludeKind].end());
193 for (
const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
194 auto &
Location = IncludeLocations[IncludeEntry];
195 SourceRangeVector::iterator LocationIterator =
Location.begin();
196 SourceRangeVector::iterator LocationIteratorEnd =
Location.end();
197 SourceRange FirstLocation = *LocationIterator;
201 if (FirstLocation == *SourceLocationIterator)
206 for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
208 SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
209 Edits[LineNumber] = std::make_pair(*LocationIterator,
"");
210 SourceLocationIteratorEnd =
211 std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
215 if (FirstLocation == *SourceLocationIterator) {
218 ++SourceLocationIterator;
224 SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
225 if (Edits.find(LineNumber) == Edits.end()) {
226 Edits[LineNumber].first =
227 SourceRange(SourceLocationIterator->getBegin());
229 StringRef SourceText = Lexer::getSourceText(
230 CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
231 Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
235 IncludeBucket[IncludeKind].clear();
239 int CurrentEndLine = 0;
240 SourceRange CurrentRange;
241 std::string CurrentText;
242 std::vector<FixItHint> Fixes;
243 for (
const auto &LineEdit : Edits) {
246 if (LineEdit.first == CurrentEndLine + 1 &&
247 CurrentRange.getBegin() != CurrentRange.getEnd()) {
248 SourceRange EditRange = LineEdit.second.first;
249 if (EditRange.getBegin() != EditRange.getEnd()) {
251 CurrentRange.setEnd(EditRange.getEnd());
253 CurrentText += LineEdit.second.second;
256 if (CurrentEndLine) {
257 Fixes.push_back(FixItHint::CreateReplacement(
258 CharSourceRange::getCharRange(CurrentRange), CurrentText));
261 CurrentEndLine = LineEdit.first;
262 CurrentRange = LineEdit.second.first;
263 CurrentText = LineEdit.second.second;
267 if (CurrentEndLine) {
268 Fixes.push_back(FixItHint::CreateReplacement(
269 CharSourceRange::getCharRange(CurrentRange), CurrentText));
273 SourceLocations.clear();
274 IncludeLocations.clear();
284 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...