17#include "llvm/ADT/DenseMap.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/STLFunctionalExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Support/Error.h"
22#include "llvm/Support/ErrorHandling.h"
23#include "llvm/Support/FormatVariadic.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/Regex.h"
41LangOptions createLangOpts() {
43 LangOpts.CPlusPlus = 1;
44 LangOpts.CPlusPlus11 = 1;
45 LangOpts.CPlusPlus14 = 1;
46 LangOpts.LineComment = 1;
47 LangOpts.CXXOperatorNames = 1;
50 LangOpts.MicrosoftExt = 1;
51 LangOpts.DeclSpecKeyword = 1;
63 -> std::invoke_result_t<F, const SourceManager &, Lexer &> {
64 SourceManagerForFile VirtualSM(
FileName, Code);
65 SourceManager &SM = VirtualSM.get();
66 LangOptions LangOpts = createLangOpts();
67 Lexer Lex(SM.getMainFileID(), SM.getBufferOrFake(SM.getMainFileID()), SM,
69 return std::invoke(std::forward<F>(
Callback), std::as_const(SM), Lex);
76unsigned getOffsetAfterTokenSequence(
78 llvm::function_ref<
unsigned(
const SourceManager &, Lexer &, Token &)>
79 GetOffsetAfterSequence) {
80 return withLexer(
FileName, Code, Style,
81 [&](
const SourceManager &SM, Lexer &Lex) {
84 Lex.LexFromRawLexer(
Tok);
85 return GetOffsetAfterSequence(SM, Lex,
Tok);
93bool checkAndConsumeDirectiveWithName(
94 Lexer &Lex, StringRef Name, Token &
Tok,
95 std::optional<StringRef> RawIDName = std::nullopt) {
96 bool Matched =
Tok.is(tok::hash) && !Lex.LexFromRawLexer(
Tok) &&
97 Tok.is(tok::raw_identifier) &&
98 Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(
Tok) &&
99 Tok.is(tok::raw_identifier) &&
100 (!RawIDName ||
Tok.getRawIdentifier() == *RawIDName);
102 Lex.LexFromRawLexer(
Tok);
106void skipComments(Lexer &Lex, Token &
Tok) {
107 while (
Tok.is(tok::comment))
108 if (Lex.LexFromRawLexer(
Tok))
112bool checkAndConsumeModuleDecl(
const SourceManager &SM, Lexer &Lex,
114 bool Matched =
Tok.is(tok::raw_identifier) &&
115 Tok.getRawIdentifier() ==
"module" &&
116 !Lex.LexFromRawLexer(
Tok) &&
Tok.is(tok::semi) &&
117 !Lex.LexFromRawLexer(
Tok);
128unsigned getMinHeaderInsertionOffset(StringRef
FileName, StringRef Code,
132 auto ConsumeHeaderGuardAndComment =
133 [&](
std::function<unsigned(
const SourceManager &SM, Lexer &Lex,
136 return getOffsetAfterTokenSequence(
138 [&Consume](
const SourceManager &SM, Lexer &Lex, Token
Tok) {
139 skipComments(Lex,
Tok);
140 unsigned InitialOffset = SM.getFileOffset(
Tok.getLocation());
141 return std::max(InitialOffset, Consume(SM, Lex,
Tok));
145 auto ModuleDecl = ConsumeHeaderGuardAndComment(
146 [](
const SourceManager &SM, Lexer &Lex, Token
Tok) ->
unsigned {
147 if (checkAndConsumeModuleDecl(SM, Lex,
Tok)) {
148 skipComments(Lex,
Tok);
149 return SM.getFileOffset(
Tok.getLocation());
154 auto HeaderAndPPOffset = std::max(
156 ConsumeHeaderGuardAndComment(
157 [](
const SourceManager &SM, Lexer &Lex, Token
Tok) ->
unsigned {
158 if (checkAndConsumeDirectiveWithName(Lex,
"ifndef",
Tok)) {
159 skipComments(Lex,
Tok);
160 if (checkAndConsumeDirectiveWithName(Lex,
"define",
Tok) &&
161 Tok.isAtStartOfLine())
162 return SM.getFileOffset(
Tok.getLocation());
167 ConsumeHeaderGuardAndComment(
168 [](
const SourceManager &SM, Lexer &Lex, Token
Tok) ->
unsigned {
169 if (checkAndConsumeDirectiveWithName(Lex,
"pragma",
Tok,
171 return SM.getFileOffset(
Tok.getLocation());
174 return std::max(HeaderAndPPOffset, ModuleDecl);
181bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &
Tok) {
182 auto Matched = [&]() {
183 Lex.LexFromRawLexer(
Tok);
186 if (
Tok.is(tok::hash) && !Lex.LexFromRawLexer(
Tok) &&
187 Tok.is(tok::raw_identifier) &&
188 (
Tok.getRawIdentifier() ==
"include" ||
189 Tok.getRawIdentifier() ==
"import")) {
190 if (Lex.LexFromRawLexer(
Tok))
192 if (
Tok.is(tok::string_literal))
194 if (
Tok.is(tok::less)) {
195 while (!Lex.LexFromRawLexer(
Tok) &&
Tok.isNot(tok::greater)) {
197 if (
Tok.is(tok::greater))
217unsigned getMaxHeaderInsertionOffset(StringRef
FileName, StringRef Code,
219 return getOffsetAfterTokenSequence(
221 [](
const SourceManager &SM, Lexer &Lex, Token
Tok) {
222 skipComments(Lex,
Tok);
223 unsigned MaxOffset = SM.getFileOffset(
Tok.getLocation());
224 while (checkAndConsumeInclusiveDirective(Lex,
Tok))
232bool isFirstDeclModuleDecl(StringRef
FileName, StringRef Code,
235 FileName, Code, Style, [](
const SourceManager &SM, Lexer &Lex) {
237 Lex.SetKeepWhitespaceMode(
false);
238 Lex.SetCommentRetentionState(
false);
241 if (Lex.LexFromRawLexer(tok))
252 if (tok.is(tok::raw_identifier) && tok.getRawIdentifier() ==
"export") {
253 if (Lex.LexFromRawLexer(tok))
258 if (!tok.is(tok::raw_identifier) ||
259 tok.getRawIdentifier() !=
"module" || Lex.LexFromRawLexer(tok))
263 return tok.is(tok::raw_identifier);
267inline StringRef trimInclude(StringRef IncludeName) {
268 return IncludeName.trim(
"\"<>");
271const char IncludeRegexPattern[] =
272 "^[\t ]*#[\t ]*(import|include)[^\"<]*([\"<][^\">]*[\">])";
279StringRef matchingStem(llvm::StringRef Path) {
280 StringRef Name = llvm::sys::path::filename(Path);
281 return Name.substr(0, Name.find(
'.', 1));
288 : Style(Style), FileName(FileName) {
289 for (
const auto &Category : Style.IncludeCategories) {
290 CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive
291 ? llvm::Regex::NoFlags
292 : llvm::Regex::IgnoreCase);
294 IsMainFile = FileName.ends_with(
".c") || FileName.ends_with(
".cc") ||
295 FileName.ends_with(
".cpp") || FileName.ends_with(
".c++") ||
296 FileName.ends_with(
".cxx") || FileName.ends_with(
".m") ||
297 FileName.ends_with(
".mm");
298 if (!Style.IncludeIsMainSourceRegex.empty()) {
299 llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex);
300 IsMainFile |= MainFileRegex.match(FileName);
305 bool CheckMainHeader)
const {
307 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
308 if (CategoryRegexs[i].
match(IncludeName)) {
309 Ret = Style.IncludeCategories[i].Priority;
312 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
318 bool CheckMainHeader)
const {
320 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
321 if (CategoryRegexs[i].
match(IncludeName)) {
322 Ret = Style.IncludeCategories[i].SortPriority;
324 Ret = Style.IncludeCategories[i].Priority;
327 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
331bool IncludeCategoryManager::isMainHeader(StringRef IncludeName)
const {
332 switch (Style.MainIncludeChar) {
334 if (!IncludeName.starts_with(
"\""))
338 if (!IncludeName.starts_with(
"<"))
346 IncludeName.drop_front(1).drop_back(1);
349 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);
350 StringRef FileStem = llvm::sys::path::stem(
FileName);
351 StringRef MatchingFileStem = matchingStem(
FileName);
361 if (MatchingFileStem.starts_with_insensitive(HeaderStem))
362 Matching = MatchingFileStem;
363 else if (FileStem.equals_insensitive(HeaderStem))
365 if (!Matching.empty()) {
366 llvm::Regex MainIncludeRegex(llvm::Regex::escape(HeaderStem) +
367 Style.IncludeIsMainRegex,
368 llvm::Regex::IgnoreCase);
369 if (MainIncludeRegex.match(Matching))
379 : FileName(FileName), Code(Code), FirstIncludeOffset(-1),
380 MinInsertOffset(getMinHeaderInsertionOffset(FileName, Code, Style)),
381 MaxInsertOffset(MinInsertOffset +
382 getMaxHeaderInsertionOffset(
383 FileName, Code.drop_front(MinInsertOffset), Style)),
384 MainIncludeFound(
false),
385 ShouldInsertGlobalModuleFragmentDecl(
386 isFirstDeclModuleDecl(FileName, Code, Style)),
387 Categories(Style, FileName) {
391 for (
const auto &Category : Style.IncludeCategories)
392 Priorities.insert(Category.Priority);
394 Code.drop_front(MinInsertOffset).split(Lines,
"\n");
396 unsigned Offset = MinInsertOffset;
397 unsigned NextLineOffset;
400 NextLineOffset = std::min(Code.size(), Offset +
Line.size() + 1);
407 Offset, std::min(
Line.size() + 1, Code.size() - Offset)),
412 Offset = NextLineOffset;
419 auto Highest = Priorities.begin();
420 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);
422 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;
427 for (
auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I)
428 if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end())
429 CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)];
433void HeaderIncludes::addExistingInclude(
Include IncludeToAdd,
434 unsigned NextLineOffset) {
435 auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)];
436 Incs.push_back(std::move(IncludeToAdd));
437 auto &CurInclude = Incs.back();
440 if (CurInclude.R.getOffset() <= MaxInsertOffset) {
442 CurInclude.Name, !MainIncludeFound);
444 MainIncludeFound =
true;
445 CategoryEndOffsets[Priority] = NextLineOffset;
446 IncludesByPriority[Priority].push_back(&CurInclude);
447 if (FirstIncludeOffset < 0)
448 FirstIncludeOffset = CurInclude.R.getOffset();
452std::optional<tooling::Replacement>
455 assert(Header == trimInclude(Header));
459 auto It = ExistingIncludes.find(Header);
460 if (It != ExistingIncludes.end()) {
461 for (
const auto &Inc : It->second) {
462 bool SameQuotation = (IsAngled && StringRef(Inc.Name).starts_with(
"<")) ||
463 (!IsAngled && StringRef(Inc.Name).starts_with(
"\""));
476 char Open = IsAngled ?
'<' :
'"';
477 char Close = IsAngled ?
'>' :
'"';
478 std::string NewInclude =
479 llvm::formatv(
"#import {0}{1}{2}\n",
Open, Header, Close);
482 Inc.R.getLength(), NewInclude);
487 std::string(llvm::formatv(IsAngled ?
"<{0}>" :
"\"{0}\"", Header));
488 StringRef QuotedName = Quoted;
489 int Priority = Categories.getIncludePriority(
490 QuotedName, !MainIncludeFound);
491 auto CatOffset = CategoryEndOffsets.find(Priority);
492 assert(CatOffset != CategoryEndOffsets.end());
493 unsigned InsertOffset = CatOffset->second;
494 auto Iter = IncludesByPriority.find(Priority);
495 if (Iter != IncludesByPriority.end()) {
496 for (
const auto *Inc : Iter->second) {
497 if (QuotedName < Inc->Name) {
498 InsertOffset = Inc->R.getOffset();
503 assert(InsertOffset <= Code.size());
504 llvm::StringRef DirectiveSpelling =
506 std::string NewInclude =
507 llvm::formatv(
"#{0} {1}\n", DirectiveSpelling, QuotedName);
512 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() !=
'\n'))
513 NewInclude =
"\n" + NewInclude;
514 if (ShouldInsertGlobalModuleFragmentDecl)
515 NewInclude =
"module;\n" + NewInclude;
523 if (RawOrSpelledHeader.starts_with(
"<")) {
524 Header = RawOrSpelledHeader.trim(
"<>").str();
525 this->IsAngled = QuoteStyle != QuoteStyle::QUOTED;
526 }
else if (RawOrSpelledHeader.starts_with(
"\"")) {
527 Header = RawOrSpelledHeader.trim(
"\"").str();
528 this->IsAngled = QuoteStyle == QuoteStyle::ANGLED;
538 std::vector<HeaderToInsert> SortedHeaders = Headers.vec();
541 std::string QuotedL =
542 std::string(llvm::formatv(L.
IsAngled ?
"<{0}>" :
"\"{0}\"", L.
Header));
543 std::string QuotedR =
544 std::string(llvm::formatv(R.IsAngled ?
"<{0}>" :
"\"{0}\"", R.Header));
545 int PriorityL = Categories.getIncludePriority(
546 QuotedL, !MainIncludeFound);
547 int PriorityR = Categories.getIncludePriority(
548 QuotedR, !MainIncludeFound);
549 if (PriorityL != PriorityR)
550 return PriorityL < PriorityR;
552 return L.
Header < R.Header;
558 std::unique(SortedHeaders.begin(), SortedHeaders.end(),
560 return L.Header == R.Header && L.IsAngled == R.IsAngled;
562 SortedHeaders.end());
564 struct InsertionInfo {
568 llvm::DenseMap<unsigned, InsertionInfo> InsertionsByOffset;
570 for (
const auto &H : SortedHeaders) {
571 if (
auto Insertion =
insert(H.Header, H.IsAngled, H.Directive)) {
572 auto &Info = InsertionsByOffset[Insertion->getOffset()];
573 Info.Text += Insertion->getReplacementText();
574 if (Insertion->getLength() > 0) {
575 assert(Info.Length == 0 &&
"Multiple replacements at same offset?");
576 Info.Length = Insertion->getLength();
581 for (
const auto &Entry : InsertionsByOffset) {
582 const auto &Info = Entry.second;
583 const unsigned Offset = Entry.first;
592 bool IsAngled)
const {
593 assert(Header == trimInclude(Header));
595 auto Iter = ExistingIncludes.find(Header);
596 if (Iter == ExistingIncludes.end())
598 for (
const auto &Inc : Iter->second) {
599 if ((IsAngled && StringRef(Inc.Name).starts_with(
"\"")) ||
600 (!IsAngled && StringRef(Inc.Name).starts_with(
"<")))
603 FileName, Inc.R.getOffset(), Inc.R.getLength(),
""));
605 auto ErrMsg =
"Unexpected conflicts in #include deletions: " +
606 llvm::toString(std::move(Err));
607 llvm_unreachable(ErrMsg.c_str());
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the SourceManager interface.
Defines the clang::TokenKind enum and support functions.
@ Open
The standard open() call: int open(const char *path, int oflag, ...);.
VerifyDiagnosticConsumer::Directive Directive
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
Top level wrappers for InstallAPI frontend operations.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
@ Result
The result type of a method or function.
for(const auto &A :T->param_types())
int const char * function