11 #include "clang/Lex/Lexer.h"
18 StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
19 for (StringRef Suffix : Suffixes) {
20 if (Str.endswith(Suffix)) {
21 return Str.substr(0, Str.size() - Suffix.size());
35 return RemoveFirstSuffix(
36 RemoveFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}), {
"Test"});
38 return RemoveFirstSuffix(
39 RemoveFirstSuffix(Str, {
".cc",
".cpp",
".c",
".h",
".hpp"}),
40 {
"_unittest",
"_regtest",
"_test"});
44 size_t FindNextLine(
const char *Text) {
45 size_t EOLIndex = std::strcspn(Text,
"\n");
46 return Text[EOLIndex] ==
'\0' ? EOLIndex : EOLIndex + 1;
50 DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
62 StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
63 if (CanonicalFile.equals(CanonicalInclude)) {
67 std::pair<StringRef, StringRef> Parts = CanonicalInclude.split(
"/public/");
68 std::string AltCanonicalInclude =
69 Parts.first.str() +
"/internal/" + Parts.second.str();
70 std::string ProtoCanonicalInclude =
71 Parts.first.str() +
"/proto/" + Parts.second.str();
74 if (CanonicalFile.equals(AltCanonicalInclude) ||
75 CanonicalFile.equals(ProtoCanonicalInclude)) {
85 const LangOptions *
LangOpts,
const FileID FileID,
87 : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
88 CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
92 SourceLocation HashLocation,
93 SourceLocation EndLocation) {
94 int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
97 IncludeLocations[FileName].push_back(
98 SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
99 SourceLocations.push_back(IncludeLocations[FileName].back());
102 if (IncludeLocations[FileName].size() > 1)
107 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
109 IncludeBucket[Kind].push_back(FileName.str());
114 std::string IncludeStmt =
115 IsAngled ? llvm::Twine(
"#include <" + FileName +
">\n").str()
116 : llvm::Twine(
"#include \"" + FileName +
"\"\n").str();
117 if (SourceLocations.empty()) {
120 IncludeStmt.append(
"\n");
121 return FixItHint::CreateInsertion(
122 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
126 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
128 if (!IncludeBucket[IncludeKind].empty()) {
129 for (
const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
130 if (FileName < IncludeEntry) {
131 const auto &
Location = IncludeLocations[IncludeEntry][0];
132 return FixItHint::CreateInsertion(
Location.getBegin(), IncludeStmt);
133 }
else if (FileName == IncludeEntry) {
139 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
140 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
141 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
151 if (!IncludeBucket[i].empty()) {
153 if (NonEmptyKind < IncludeKind)
161 if (NonEmptyKind < IncludeKind) {
163 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
164 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
165 IncludeStmt =
'\n' + IncludeStmt;
166 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
170 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
171 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
172 IncludeStmt.append(
"\n");
173 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
178 if (SourceLocations.empty())
181 typedef std::map<int, std::pair<SourceRange, std::string>>
182 FileLineToSourceEditMap;
183 FileLineToSourceEditMap Edits;
184 auto SourceLocationIterator = SourceLocations.begin();
185 auto SourceLocationIteratorEnd = SourceLocations.end();
190 std::sort(IncludeBucket[IncludeKind].begin(),
191 IncludeBucket[IncludeKind].end());
192 for (
const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
193 auto &
Location = IncludeLocations[IncludeEntry];
194 SourceRangeVector::iterator LocationIterator =
Location.begin();
195 SourceRangeVector::iterator LocationIteratorEnd =
Location.end();
196 SourceRange FirstLocation = *LocationIterator;
200 if (FirstLocation == *SourceLocationIterator)
205 for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
207 SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
208 Edits[LineNumber] = std::make_pair(*LocationIterator,
"");
209 SourceLocationIteratorEnd =
210 std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
214 if (FirstLocation == *SourceLocationIterator) {
217 ++SourceLocationIterator;
223 SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
224 if (Edits.find(LineNumber) == Edits.end()) {
225 Edits[LineNumber].first =
226 SourceRange(SourceLocationIterator->getBegin());
228 StringRef SourceText = Lexer::getSourceText(
229 CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
230 Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
234 IncludeBucket[IncludeKind].clear();
238 int CurrentEndLine = 0;
239 SourceRange CurrentRange;
240 std::string CurrentText;
241 std::vector<FixItHint> Fixes;
242 for (
const auto &LineEdit : Edits) {
245 if (LineEdit.first == CurrentEndLine + 1 &&
246 CurrentRange.getBegin() != CurrentRange.getEnd()) {
247 SourceRange EditRange = LineEdit.second.first;
248 if (EditRange.getBegin() != EditRange.getEnd()) {
250 CurrentRange.setEnd(EditRange.getEnd());
252 CurrentText += LineEdit.second.second;
255 if (CurrentEndLine) {
256 Fixes.push_back(CreateFixIt(CurrentRange, CurrentText));
259 CurrentEndLine = LineEdit.first;
260 CurrentRange = LineEdit.second.first;
261 CurrentText = LineEdit.second.second;
265 if (CurrentEndLine) {
266 Fixes.push_back(CreateFixIt(CurrentRange, CurrentText));
270 SourceLocations.clear();
271 IncludeLocations.clear();
277 FixItHint IncludeSorter::CreateFixIt(SourceRange EditRange,
278 const std::string &NewText) {
280 Fix.RemoveRange = CharSourceRange::getCharRange(EditRange);
281 Fix.CodeToInsert = NewText;
291 return Style ==
IS_LLVM ?
"llvm" :
"google";
Optional< FixItHint > CreateIncludeInsertion(StringRef FileName, bool IsAngled)
static IncludeStyle parseIncludeStyle(const std::string &Value)
void AddInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation)
IncludeSorter(const SourceManager *SourceMgr, const LangOptions *LangOpts, const FileID FileID, StringRef FileName, IncludeStyle Style)
static StringRef toString(IncludeStyle Style)
std::vector< FixItHint > GetEdits()
bool IsAngled
true if this was an include with angle brackets
static cl::opt< bool > Fix("fix", cl::desc("Apply suggested fixes. Without -fix-errors\n""clang-tidy will bail out if any compilation\n""errors were found."), cl::init(false), cl::cat(ClangTidyCategory))