11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "clang/Lex/Preprocessor.h"
22 class IncludeOrderPPCallbacks :
public PPCallbacks {
24 explicit IncludeOrderPPCallbacks(ClangTidyCheck &
Check, SourceManager &
SM)
27 void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
29 CharSourceRange FilenameRange,
const FileEntry *
File,
30 StringRef SearchPath, StringRef RelativePath,
31 const Module *Imported)
override;
32 void EndOfMainFile()
override;
35 struct IncludeDirective {
43 typedef std::vector<IncludeDirective> FileIncludes;
53 Compiler.getPreprocessor().addPPCallbacks(
54 ::llvm::make_unique<IncludeOrderPPCallbacks>(
55 *
this, Compiler.getSourceManager()));
64 if (Filename.startswith(
"llvm/") || Filename.startswith(
"llvm-c/") ||
65 Filename.startswith(
"clang/") || Filename.startswith(
"clang-c/"))
69 if (IsAngled || Filename.startswith(
"gtest/"))
76 void IncludeOrderPPCallbacks::InclusionDirective(
77 SourceLocation HashLoc,
const Token &IncludeTok, StringRef FileName,
78 bool IsAngled, CharSourceRange FilenameRange,
const FileEntry *
File,
79 StringRef SearchPath, StringRef RelativePath,
const Module *Imported) {
82 IncludeDirective ID = {HashLoc, FilenameRange, FileName,
IsAngled,
false};
84 ID.IsMainModule =
true;
92 void IncludeOrderPPCallbacks::EndOfMainFile() {
105 auto &FileDirectives = Bucket.second;
106 std::vector<unsigned> Blocks(1, 0);
107 for (
unsigned I = 1, E = FileDirectives.size(); I != E; ++I)
108 if (
SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
109 SM.getExpansionLineNumber(FileDirectives[I - 1].
Loc) + 1)
111 Blocks.push_back(FileDirectives.size());
114 std::vector<unsigned> IncludeIndices;
115 for (
unsigned I = 0, E = FileDirectives.size(); I != E; ++I)
116 IncludeIndices.push_back(I);
119 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
120 std::sort(IncludeIndices.begin() + Blocks[BI],
121 IncludeIndices.begin() + Blocks[BI + 1],
122 [&FileDirectives](
unsigned LHSI,
unsigned RHSI) {
123 IncludeDirective &LHS = FileDirectives[LHSI];
124 IncludeDirective &RHS = FileDirectives[RHSI];
127 getPriority(LHS.Filename, LHS.IsAngled, LHS.IsMainModule);
129 getPriority(RHS.Filename, RHS.IsAngled, RHS.IsMainModule);
131 return std::tie(PriorityLHS, LHS.Filename) <
132 std::tie(PriorityRHS, RHS.Filename);
137 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
140 for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I)
141 if (IncludeIndices[I] != I)
148 auto D =
Check.diag(FileDirectives[I].
Loc,
149 "#includes are not sorted properly");
152 for (; I != E; ++I) {
153 if (IncludeIndices[I] == I)
155 const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
157 SourceLocation FromLoc = CopyFrom.Range.getBegin();
158 const char *FromData =
SM.getCharacterData(FromLoc);
159 unsigned FromLen = std::strcspn(FromData,
"\n");
161 StringRef FixedName(FromData, FromLen);
163 SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
164 const char *ToData =
SM.getCharacterData(ToLoc);
165 unsigned ToLen = std::strcspn(ToData,
"\n");
167 CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
169 D << FixItHint::CreateReplacement(ToRange, FixedName);
174 IncludeDirectives.clear();
SourceLocation Loc
'#' location in the include directive
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
std::map< clang::FileID, FileIncludes > IncludeDirectives
std::string Filename
Filename as a string.
bool IsAngled
true if this was an include with angle brackets
bool IsMainModule
true if this was the first include in a file
CharSourceRange Range
SourceRange for the file name.
static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule)