10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/PPCallbacks.h"
12#include "clang/Lex/Preprocessor.h"
13#include "llvm/ADT/STLExtras.h"
22 explicit IncludeOrderPPCallbacks(ClangTidyCheck &Check,
23 const SourceManager &SM)
24 : LookForMainModule(true), Check(Check), SM(SM) {}
26 void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
28 CharSourceRange FilenameRange,
29 OptionalFileEntryRef File, StringRef SearchPath,
30 StringRef RelativePath,
const Module *Imported,
31 SrcMgr::CharacteristicKind FileType)
override;
32 void EndOfMainFile()
override;
35 struct IncludeDirective {
43 typedef std::vector<IncludeDirective> FileIncludes;
44 std::map<clang::FileID, FileIncludes> IncludeDirectives;
45 bool LookForMainModule;
47 ClangTidyCheck &Check;
48 const SourceManager &SM;
54 Preprocessor *ModuleExpanderPP) {
55 PP->addPPCallbacks(::std::make_unique<IncludeOrderPPCallbacks>(*
this, SM));
81void IncludeOrderPPCallbacks::InclusionDirective(
82 SourceLocation HashLoc,
const Token &IncludeTok, StringRef
FileName,
83 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
84 StringRef SearchPath, StringRef RelativePath,
const Module *Imported,
85 SrcMgr::CharacteristicKind FileType) {
88 IncludeDirective ID = {HashLoc, FilenameRange, std::string(
FileName),
90 if (LookForMainModule && !
IsAngled) {
91 ID.IsMainModule =
true;
92 LookForMainModule =
false;
96 IncludeDirectives[SM.getFileID(HashLoc)].push_back(std::move(ID));
99void IncludeOrderPPCallbacks::EndOfMainFile() {
100 LookForMainModule =
true;
101 if (IncludeDirectives.empty())
111 for (
auto &Bucket : IncludeDirectives) {
112 auto &FileDirectives = Bucket.second;
113 std::vector<unsigned> Blocks(1, 0);
114 for (
unsigned I = 1,
E = FileDirectives.size(); I !=
E; ++I)
115 if (SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
116 SM.getExpansionLineNumber(FileDirectives[I - 1].Loc) + 1)
118 Blocks.push_back(FileDirectives.size());
121 std::vector<unsigned> IncludeIndices;
122 for (
unsigned I = 0,
E = FileDirectives.size(); I !=
E; ++I)
123 IncludeIndices.push_back(I);
126 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
127 llvm::sort(IncludeIndices.begin() + Blocks[BI],
128 IncludeIndices.begin() + Blocks[BI + 1],
129 [&FileDirectives](
unsigned LHSI,
unsigned RHSI) {
130 IncludeDirective &LHS = FileDirectives[LHSI];
131 IncludeDirective &RHS = FileDirectives[RHSI];
133 int PriorityLHS = getPriority(LHS.Filename, LHS.IsAngled,
135 int PriorityRHS = getPriority(RHS.Filename, RHS.IsAngled,
138 return std::tie(PriorityLHS, LHS.Filename) <
139 std::tie(PriorityRHS, RHS.Filename);
144 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
147 for (I = Blocks[BI],
E = Blocks[BI + 1]; I !=
E; ++I)
148 if (IncludeIndices[I] != I)
155 auto D = Check.diag(FileDirectives[I].
Loc,
156 "#includes are not sorted properly");
159 for (; I !=
E; ++I) {
160 if (IncludeIndices[I] == I)
162 const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
164 SourceLocation FromLoc = CopyFrom.Range.getBegin();
165 const char *FromData = SM.getCharacterData(FromLoc);
166 unsigned FromLen = std::strcspn(FromData,
"\n");
168 StringRef FixedName(FromData, FromLen);
170 SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
171 const char *ToData = SM.getCharacterData(ToLoc);
172 unsigned ToLen = std::strcspn(ToData,
"\n");
174 CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
176 D << FixItHint::CreateReplacement(ToRange, FixedName);
181 IncludeDirectives.clear();
CharSourceRange Range
SourceRange for the file name.
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
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule)