10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/PPCallbacks.h"
12#include "clang/Lex/Preprocessor.h"
13#include "llvm/ADT/STLExtras.h"
18class IncludeOrderPPCallbacks :
public PPCallbacks {
20 explicit IncludeOrderPPCallbacks(ClangTidyCheck &Check,
21 const SourceManager &SM)
22 : Check(Check), SM(SM) {}
24 void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
25 StringRef FileName,
bool IsAngled,
26 CharSourceRange FilenameRange,
27 OptionalFileEntryRef File, StringRef SearchPath,
28 StringRef RelativePath,
const Module *SuggestedModule,
30 SrcMgr::CharacteristicKind FileType)
override;
31 void EndOfMainFile()
override;
34 struct IncludeDirective {
36 CharSourceRange Range;
42 using FileIncludes = std::vector<IncludeDirective>;
43 llvm::DenseMap<FileID, FileIncludes> IncludeDirectives;
44 bool LookForMainModule =
true;
46 ClangTidyCheck &Check;
47 const SourceManager &SM;
53 Preprocessor *ModuleExpanderPP) {
54 PP->addPPCallbacks(::std::make_unique<IncludeOrderPPCallbacks>(*
this, SM));
57static int getPriority(StringRef Filename,
bool IsAngled,
bool IsMainModule) {
63 if (Filename.starts_with(
"llvm/") || Filename.starts_with(
"llvm-c/") ||
64 Filename.starts_with(
"clang/") || Filename.starts_with(
"clang-c/"))
69 if (Filename.starts_with(
"gtest/") || Filename.starts_with(
"gmock/"))
80void IncludeOrderPPCallbacks::InclusionDirective(
81 SourceLocation HashLoc,
const Token &IncludeTok, StringRef FileName,
82 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
83 StringRef SearchPath, StringRef RelativePath,
const Module *SuggestedModule,
84 bool ModuleImported, SrcMgr::CharacteristicKind FileType) {
87 IncludeDirective ID = {HashLoc, FilenameRange, std::string(FileName),
89 if (LookForMainModule && !IsAngled) {
90 ID.IsMainModule =
true;
91 LookForMainModule =
false;
95 IncludeDirectives[SM.getFileID(HashLoc)].push_back(std::move(ID));
98void IncludeOrderPPCallbacks::EndOfMainFile() {
99 LookForMainModule =
true;
100 if (IncludeDirectives.empty())
110 for (
auto &Bucket : IncludeDirectives) {
111 auto &FileDirectives = Bucket.second;
112 std::vector<unsigned> Blocks(1, 0);
113 for (
unsigned I = 1, E = FileDirectives.size(); I != E; ++I)
114 if (SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
115 SM.getExpansionLineNumber(FileDirectives[I - 1].Loc) + 1)
117 Blocks.push_back(FileDirectives.size());
120 std::vector<unsigned> IncludeIndices;
121 for (
unsigned I = 0, E = FileDirectives.size(); I != E; ++I)
122 IncludeIndices.push_back(I);
125 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
126 llvm::sort(IncludeIndices.begin() + Blocks[BI],
127 IncludeIndices.begin() + Blocks[BI + 1],
128 [&FileDirectives](
unsigned LHSI,
unsigned RHSI) {
129 IncludeDirective &LHS = FileDirectives[LHSI];
130 IncludeDirective &RHS = FileDirectives[RHSI];
132 int PriorityLHS = getPriority(LHS.Filename, LHS.IsAngled,
134 int PriorityRHS = getPriority(RHS.Filename, RHS.IsAngled,
137 return std::tie(PriorityLHS, LHS.Filename) <
138 std::tie(PriorityRHS, RHS.Filename);
143 for (
unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
145 unsigned I = 0, E = 0;
146 for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I)
147 if (IncludeIndices[I] != I)
154 auto D = Check.diag(FileDirectives[I].Loc,
155 "#includes are not sorted properly");
158 for (; I != E; ++I) {
159 if (IncludeIndices[I] == I)
161 const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
163 const SourceLocation FromLoc = CopyFrom.Range.getBegin();
164 const char *FromData = SM.getCharacterData(FromLoc);
165 const unsigned FromLen = std::strcspn(FromData,
"\n");
167 const StringRef FixedName(FromData, FromLen);
169 const SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
170 const char *ToData = SM.getCharacterData(ToLoc);
171 const unsigned ToLen = std::strcspn(ToData,
"\n");
173 CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
175 D << FixItHint::CreateReplacement(ToRange, FixedName);
180 IncludeDirectives.clear();
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule)