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 : 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 *SuggestedModule,
32 SrcMgr::CharacteristicKind FileType)
override;
33 void EndOfMainFile()
override;
36 struct IncludeDirective {
44 using FileIncludes = std::vector<IncludeDirective>;
45 std::map<clang::FileID, FileIncludes> IncludeDirectives;
46 bool LookForMainModule =
true;
48 ClangTidyCheck &Check;
49 const SourceManager &SM;
55 Preprocessor *ModuleExpanderPP) {
56 PP->addPPCallbacks(::std::make_unique<IncludeOrderPPCallbacks>(*
this, SM));
82void IncludeOrderPPCallbacks::InclusionDirective(
83 SourceLocation HashLoc,
const Token &IncludeTok, StringRef
FileName,
84 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
85 StringRef SearchPath, StringRef RelativePath,
const Module *SuggestedModule,
86 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
89 IncludeDirective
ID = {HashLoc, FilenameRange, std::string(
FileName),
91 if (LookForMainModule && !
IsAngled) {
92 ID.IsMainModule =
true;
93 LookForMainModule =
false;
97 IncludeDirectives[SM.getFileID(HashLoc)].push_back(std::move(
ID));
100void IncludeOrderPPCallbacks::EndOfMainFile() {
101 LookForMainModule =
true;
102 if (IncludeDirectives.empty())
112 for (
auto &Bucket : IncludeDirectives) {
113 auto &FileDirectives = Bucket.second;
114 std::vector<unsigned> Blocks(1, 0);
115 for (
unsigned I = 1,
E = FileDirectives.size(); I !=
E; ++I)
116 if (SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
117 SM.getExpansionLineNumber(FileDirectives[I - 1].Loc) + 1)
119 Blocks.push_back(FileDirectives.size());
122 std::vector<unsigned> IncludeIndices;
123 for (
unsigned I = 0,
E = FileDirectives.size(); I !=
E; ++I)
124 IncludeIndices.push_back(I);
127 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
128 llvm::sort(IncludeIndices.begin() + Blocks[BI],
129 IncludeIndices.begin() + Blocks[BI + 1],
130 [&FileDirectives](
unsigned LHSI,
unsigned RHSI) {
131 IncludeDirective &LHS = FileDirectives[LHSI];
132 IncludeDirective &RHS = FileDirectives[RHSI];
134 int PriorityLHS = getPriority(LHS.Filename, LHS.IsAngled,
136 int PriorityRHS = getPriority(RHS.Filename, RHS.IsAngled,
139 return std::tie(PriorityLHS, LHS.Filename) <
140 std::tie(PriorityRHS, RHS.Filename);
145 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
147 unsigned I = 0,
E = 0;
148 for (I = Blocks[BI],
E = Blocks[BI + 1]; I !=
E; ++I)
149 if (IncludeIndices[I] != I)
156 auto D = Check.diag(FileDirectives[I].
Loc,
157 "#includes are not sorted properly");
160 for (; I !=
E; ++I) {
161 if (IncludeIndices[I] == I)
163 const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
165 SourceLocation FromLoc = CopyFrom.Range.getBegin();
166 const char *FromData = SM.getCharacterData(FromLoc);
167 unsigned FromLen = std::strcspn(FromData,
"\n");
169 StringRef FixedName(FromData, FromLen);
171 SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
172 const char *ToData = SM.getCharacterData(ToLoc);
173 unsigned ToLen = std::strcspn(ToData,
"\n");
175 CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
177 D << FixItHint::CreateReplacement(ToRange, FixedName);
182 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)