12#include "llvm/Support/FormatVariadic.h"
13#include "llvm/Support/Path.h"
20LangOptions createLangOpts() {
22 LangOpts.CPlusPlus = 1;
23 LangOpts.CPlusPlus11 = 1;
24 LangOpts.CPlusPlus14 = 1;
25 LangOpts.LineComment = 1;
26 LangOpts.CXXOperatorNames = 1;
29 LangOpts.MicrosoftExt = 1;
30 LangOpts.DeclSpecKeyword = 1;
39unsigned getOffsetAfterTokenSequence(
40 StringRef
FileName, StringRef Code,
const IncludeStyle &Style,
41 llvm::function_ref<
unsigned(
const SourceManager &, Lexer &, Token &)>
42 GetOffsetAfterSequence) {
43 SourceManagerForFile VirtualSM(
FileName, Code);
44 SourceManager &
SM = VirtualSM.get();
45 LangOptions LangOpts = createLangOpts();
46 Lexer Lex(
SM.getMainFileID(),
SM.getBufferOrFake(
SM.getMainFileID()),
SM,
50 Lex.LexFromRawLexer(Tok);
51 return GetOffsetAfterSequence(
SM, Lex, Tok);
58bool checkAndConsumeDirectiveWithName(
59 Lexer &Lex, StringRef Name, Token &Tok,
60 std::optional<StringRef> RawIDName = std::nullopt) {
61 bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
62 Tok.is(tok::raw_identifier) &&
63 Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) &&
64 Tok.is(tok::raw_identifier) &&
65 (!RawIDName || Tok.getRawIdentifier() == *RawIDName);
67 Lex.LexFromRawLexer(Tok);
71void skipComments(Lexer &Lex, Token &Tok) {
72 while (Tok.is(tok::comment))
73 if (Lex.LexFromRawLexer(Tok))
77bool checkAndConsumeModuleDecl(
const SourceManager &
SM, Lexer &Lex,
79 bool Matched = Tok.is(tok::raw_identifier) &&
80 Tok.getRawIdentifier() ==
"module" &&
81 !Lex.LexFromRawLexer(Tok) && Tok.is(tok::semi) &&
82 !Lex.LexFromRawLexer(Tok);
93unsigned getMinHeaderInsertionOffset(StringRef
FileName, StringRef Code,
94 const IncludeStyle &Style) {
97 auto ConsumeHeaderGuardAndComment =
101 return getOffsetAfterTokenSequence(
103 [&Consume](
const SourceManager &
SM, Lexer &Lex, Token Tok) {
104 skipComments(Lex, Tok);
105 unsigned InitialOffset =
SM.getFileOffset(Tok.getLocation());
106 return std::max(InitialOffset, Consume(
SM, Lex, Tok));
110 auto ModuleDecl = ConsumeHeaderGuardAndComment(
111 [](
const SourceManager &
SM, Lexer &Lex, Token Tok) ->
unsigned {
112 if (checkAndConsumeModuleDecl(
SM, Lex, Tok)) {
113 skipComments(Lex, Tok);
114 return SM.getFileOffset(Tok.getLocation());
119 auto HeaderAndPPOffset = std::max(
121 ConsumeHeaderGuardAndComment(
122 [](
const SourceManager &
SM, Lexer &Lex, Token Tok) ->
unsigned {
123 if (checkAndConsumeDirectiveWithName(Lex,
"ifndef", Tok)) {
124 skipComments(Lex, Tok);
125 if (checkAndConsumeDirectiveWithName(Lex,
"define", Tok) &&
126 Tok.isAtStartOfLine())
127 return SM.getFileOffset(Tok.getLocation());
132 ConsumeHeaderGuardAndComment(
133 [](
const SourceManager &
SM, Lexer &Lex, Token Tok) ->
unsigned {
134 if (checkAndConsumeDirectiveWithName(Lex,
"pragma", Tok,
136 return SM.getFileOffset(Tok.getLocation());
139 return std::max(HeaderAndPPOffset, ModuleDecl);
146bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) {
147 auto Matched = [&]() {
148 Lex.LexFromRawLexer(Tok);
151 if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
152 Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() ==
"include") {
153 if (Lex.LexFromRawLexer(Tok))
155 if (Tok.is(tok::string_literal))
157 if (Tok.is(tok::less)) {
158 while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) {
160 if (Tok.is(tok::greater))
180unsigned getMaxHeaderInsertionOffset(StringRef
FileName, StringRef Code,
181 const IncludeStyle &Style) {
182 return getOffsetAfterTokenSequence(
184 [](
const SourceManager &
SM, Lexer &Lex, Token Tok) {
185 skipComments(Lex, Tok);
186 unsigned MaxOffset =
SM.getFileOffset(Tok.getLocation());
187 while (checkAndConsumeInclusiveDirective(Lex, Tok))
193inline StringRef trimInclude(StringRef IncludeName) {
194 return IncludeName.trim(
"\"<>");
197const char IncludeRegexPattern[] =
198 R
"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))";
205StringRef matchingStem(llvm::StringRef
Path) {
206 StringRef Name = llvm::sys::path::filename(
Path);
207 return Name.substr(0, Name.find(
'.', 1));
215 for (
const auto &
Category : Style.IncludeCategories) {
217 ? llvm::Regex::NoFlags
218 : llvm::Regex::IgnoreCase);
224 if (!Style.IncludeIsMainSourceRegex.empty()) {
225 llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex);
226 IsMainFile |= MainFileRegex.match(
FileName);
231 bool CheckMainHeader)
const {
233 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
234 if (CategoryRegexs[i].
match(IncludeName)) {
235 Ret = Style.IncludeCategories[i].Priority;
238 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
244 bool CheckMainHeader)
const {
246 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
247 if (CategoryRegexs[i].
match(IncludeName)) {
248 Ret = Style.IncludeCategories[i].SortPriority;
250 Ret = Style.IncludeCategories[i].Priority;
253 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
257bool IncludeCategoryManager::isMainHeader(StringRef IncludeName)
const {
258 switch (Style.MainIncludeChar) {
260 if (!IncludeName.starts_with(
"\""))
264 if (!IncludeName.starts_with(
"<"))
272 IncludeName.drop_front(1).drop_back(1);
275 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);
276 StringRef FileStem = llvm::sys::path::stem(FileName);
277 StringRef MatchingFileStem = matchingStem(FileName);
287 if (MatchingFileStem.starts_with_insensitive(HeaderStem))
288 Matching = MatchingFileStem;
289 else if (FileStem.equals_insensitive(HeaderStem))
291 if (!Matching.empty()) {
292 llvm::Regex MainIncludeRegex(HeaderStem.str() + Style.IncludeIsMainRegex,
293 llvm::Regex::IgnoreCase);
294 if (MainIncludeRegex.match(Matching))
305 MinInsertOffset(getMinHeaderInsertionOffset(
FileName, Code, Style)),
306 MaxInsertOffset(MinInsertOffset +
307 getMaxHeaderInsertionOffset(
308 FileName, Code.drop_front(MinInsertOffset), Style)),
313 for (
const auto &
Category : Style.IncludeCategories)
314 Priorities.insert(
Category.Priority);
316 Code.drop_front(MinInsertOffset).split(Lines,
"\n");
318 unsigned Offset = MinInsertOffset;
319 unsigned NextLineOffset;
321 for (
auto Line : Lines) {
322 NextLineOffset = std::min(Code.size(), Offset +
Line.size() + 1);
329 Offset, std::min(
Line.size() + 1, Code.size() - Offset)),
334 Offset = NextLineOffset;
341 auto Highest = Priorities.begin();
342 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);
344 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;
349 for (
auto I = ++Priorities.begin(),
E = Priorities.end(); I !=
E; ++I)
350 if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end())
351 CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)];
355void HeaderIncludes::addExistingInclude(
Include IncludeToAdd,
356 unsigned NextLineOffset) {
357 auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)];
358 Incs.push_back(std::move(IncludeToAdd));
359 auto &CurInclude = Incs.back();
362 if (CurInclude.R.getOffset() <= MaxInsertOffset) {
364 CurInclude.Name, !MainIncludeFound);
366 MainIncludeFound =
true;
367 CategoryEndOffsets[
Priority] = NextLineOffset;
368 IncludesByPriority[
Priority].push_back(&CurInclude);
369 if (FirstIncludeOffset < 0)
370 FirstIncludeOffset = CurInclude.R.getOffset();
374std::optional<tooling::Replacement>
377 assert(IncludeName == trimInclude(IncludeName));
381 auto It = ExistingIncludes.find(IncludeName);
382 if (It != ExistingIncludes.end()) {
383 for (
const auto &Inc : It->second)
385 ((IsAngled && StringRef(Inc.Name).starts_with(
"<")) ||
386 (!IsAngled && StringRef(Inc.Name).starts_with(
"\""))))
390 std::string(llvm::formatv(IsAngled ?
"<{0}>" :
"\"{0}\"", IncludeName));
391 StringRef QuotedName = Quoted;
393 QuotedName, !MainIncludeFound);
394 auto CatOffset = CategoryEndOffsets.find(
Priority);
395 assert(CatOffset != CategoryEndOffsets.end());
396 unsigned InsertOffset = CatOffset->second;
398 if (
Iter != IncludesByPriority.end()) {
399 for (
const auto *Inc :
Iter->second) {
400 if (QuotedName < Inc->Name) {
401 InsertOffset = Inc->R.getOffset();
406 assert(InsertOffset <= Code.size());
407 llvm::StringRef DirectiveSpelling =
409 std::string NewInclude =
410 llvm::formatv(
"#{0} {1}\n", DirectiveSpelling, QuotedName);
415 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() !=
'\n'))
416 NewInclude =
"\n" + NewInclude;
421 bool IsAngled)
const {
422 assert(IncludeName == trimInclude(IncludeName));
424 auto Iter = ExistingIncludes.find(IncludeName);
425 if (
Iter == ExistingIncludes.end())
427 for (
const auto &Inc :
Iter->second) {
428 if ((IsAngled && StringRef(Inc.Name).starts_with(
"\"")) ||
429 (!IsAngled && StringRef(Inc.Name).starts_with(
"<")))
432 FileName, Inc.R.getOffset(), Inc.R.getLength(),
""));
434 auto ErrMsg =
"Unexpected conflicts in #include deletions: " +
435 llvm::toString(std::move(Err));
436 llvm_unreachable(ErrMsg.c_str());
Defines the SourceManager interface.
Directive - Abstract class representing a parsed verify directive.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
int const char * function