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;
42 -> std::invoke_result_t<F, const SourceManager &, Lexer &> {
43 SourceManagerForFile VirtualSM(
FileName, Code);
44 SourceManager &
SM = VirtualSM.get();
45 LangOptions LangOpts = createLangOpts();
46 Lexer Lex(
SM.getMainFileID(),
SM.getBufferOrFake(
SM.getMainFileID()),
SM,
48 return std::invoke(std::forward<F>(Callback), std::as_const(
SM), Lex);
55unsigned getOffsetAfterTokenSequence(
57 llvm::function_ref<
unsigned(
const SourceManager &, Lexer &, Token &)>
58 GetOffsetAfterSequence) {
59 return withLexer(
FileName, Code, Style,
60 [&](
const SourceManager &
SM, Lexer &Lex) {
63 Lex.LexFromRawLexer(
Tok);
64 return GetOffsetAfterSequence(
SM, Lex,
Tok);
72bool checkAndConsumeDirectiveWithName(
73 Lexer &Lex, StringRef Name, Token &
Tok,
74 std::optional<StringRef> RawIDName = std::nullopt) {
75 bool Matched =
Tok.is(tok::hash) && !Lex.LexFromRawLexer(
Tok) &&
76 Tok.is(tok::raw_identifier) &&
77 Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(
Tok) &&
78 Tok.is(tok::raw_identifier) &&
79 (!RawIDName ||
Tok.getRawIdentifier() == *RawIDName);
81 Lex.LexFromRawLexer(
Tok);
85void skipComments(Lexer &Lex, Token &
Tok) {
86 while (
Tok.is(tok::comment))
87 if (Lex.LexFromRawLexer(
Tok))
91bool checkAndConsumeModuleDecl(
const SourceManager &
SM, Lexer &Lex,
93 bool Matched =
Tok.is(tok::raw_identifier) &&
94 Tok.getRawIdentifier() ==
"module" &&
95 !Lex.LexFromRawLexer(
Tok) &&
Tok.is(tok::semi) &&
96 !Lex.LexFromRawLexer(
Tok);
107unsigned getMinHeaderInsertionOffset(StringRef
FileName, StringRef Code,
111 auto ConsumeHeaderGuardAndComment =
115 return getOffsetAfterTokenSequence(
117 [&Consume](
const SourceManager &
SM, Lexer &Lex, Token
Tok) {
118 skipComments(Lex,
Tok);
119 unsigned InitialOffset =
SM.getFileOffset(
Tok.getLocation());
120 return std::max(InitialOffset, Consume(
SM, Lex,
Tok));
124 auto ModuleDecl = ConsumeHeaderGuardAndComment(
125 [](
const SourceManager &
SM, Lexer &Lex, Token
Tok) ->
unsigned {
126 if (checkAndConsumeModuleDecl(
SM, Lex,
Tok)) {
127 skipComments(Lex,
Tok);
128 return SM.getFileOffset(
Tok.getLocation());
133 auto HeaderAndPPOffset = std::max(
135 ConsumeHeaderGuardAndComment(
136 [](
const SourceManager &
SM, Lexer &Lex, Token
Tok) ->
unsigned {
137 if (checkAndConsumeDirectiveWithName(Lex,
"ifndef",
Tok)) {
138 skipComments(Lex,
Tok);
139 if (checkAndConsumeDirectiveWithName(Lex,
"define",
Tok) &&
140 Tok.isAtStartOfLine())
141 return SM.getFileOffset(
Tok.getLocation());
146 ConsumeHeaderGuardAndComment(
147 [](
const SourceManager &
SM, Lexer &Lex, Token
Tok) ->
unsigned {
148 if (checkAndConsumeDirectiveWithName(Lex,
"pragma",
Tok,
150 return SM.getFileOffset(
Tok.getLocation());
153 return std::max(HeaderAndPPOffset, ModuleDecl);
160bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &
Tok) {
161 auto Matched = [&]() {
162 Lex.LexFromRawLexer(
Tok);
165 if (
Tok.is(tok::hash) && !Lex.LexFromRawLexer(
Tok) &&
166 Tok.is(tok::raw_identifier) &&
Tok.getRawIdentifier() ==
"include") {
167 if (Lex.LexFromRawLexer(
Tok))
169 if (
Tok.is(tok::string_literal))
171 if (
Tok.is(tok::less)) {
172 while (!Lex.LexFromRawLexer(
Tok) &&
Tok.isNot(tok::greater)) {
174 if (
Tok.is(tok::greater))
194unsigned getMaxHeaderInsertionOffset(StringRef
FileName, StringRef Code,
196 return getOffsetAfterTokenSequence(
198 [](
const SourceManager &
SM, Lexer &Lex, Token
Tok) {
199 skipComments(Lex,
Tok);
201 while (checkAndConsumeInclusiveDirective(Lex,
Tok))
209bool isFirstDeclModuleDecl(StringRef
FileName, StringRef Code,
212 FileName, Code, Style, [](
const SourceManager &
SM, Lexer &Lex) {
214 Lex.SetKeepWhitespaceMode(
false);
215 Lex.SetCommentRetentionState(
false);
218 if (Lex.LexFromRawLexer(tok))
229 if (tok.is(tok::raw_identifier) && tok.getRawIdentifier() ==
"export") {
230 if (Lex.LexFromRawLexer(tok))
235 if (!tok.is(tok::raw_identifier) ||
236 tok.getRawIdentifier() !=
"module" || Lex.LexFromRawLexer(tok))
240 return tok.is(tok::raw_identifier);
244inline StringRef trimInclude(StringRef IncludeName) {
245 return IncludeName.trim(
"\"<>");
248const char IncludeRegexPattern[] =
249 R
"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))";
256StringRef matchingStem(llvm::StringRef Path) {
257 StringRef Name = llvm::sys::path::filename(Path);
258 return Name.substr(0, Name.find(
'.', 1));
265 : Style(Style), FileName(FileName) {
266 for (
const auto &Category : Style.IncludeCategories) {
267 CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive
268 ? llvm::Regex::NoFlags
269 : llvm::Regex::IgnoreCase);
271 IsMainFile = FileName.ends_with(
".c") || FileName.ends_with(
".cc") ||
272 FileName.ends_with(
".cpp") || FileName.ends_with(
".c++") ||
273 FileName.ends_with(
".cxx") || FileName.ends_with(
".m") ||
274 FileName.ends_with(
".mm");
275 if (!Style.IncludeIsMainSourceRegex.empty()) {
276 llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex);
277 IsMainFile |= MainFileRegex.match(FileName);
282 bool CheckMainHeader)
const {
284 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
285 if (CategoryRegexs[i].
match(IncludeName)) {
286 Ret = Style.IncludeCategories[i].Priority;
289 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
295 bool CheckMainHeader)
const {
297 for (
unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
298 if (CategoryRegexs[i].
match(IncludeName)) {
299 Ret = Style.IncludeCategories[i].SortPriority;
301 Ret = Style.IncludeCategories[i].Priority;
304 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
308bool IncludeCategoryManager::isMainHeader(StringRef IncludeName)
const {
309 switch (Style.MainIncludeChar) {
310 case IncludeStyle::MICD_Quote:
311 if (!IncludeName.starts_with(
"\""))
315 if (!IncludeName.starts_with(
"<"))
318 case IncludeStyle::MICD_Any:
323 IncludeName.drop_front(1).drop_back(1);
326 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);
327 StringRef FileStem = llvm::sys::path::stem(
FileName);
328 StringRef MatchingFileStem = matchingStem(
FileName);
338 if (MatchingFileStem.starts_with_insensitive(HeaderStem))
339 Matching = MatchingFileStem;
340 else if (FileStem.equals_insensitive(HeaderStem))
342 if (!Matching.empty()) {
343 llvm::Regex MainIncludeRegex(HeaderStem.str() + Style.IncludeIsMainRegex,
344 llvm::Regex::IgnoreCase);
345 if (MainIncludeRegex.match(Matching))
355 : FileName(FileName), Code(Code), FirstIncludeOffset(-1),
356 MinInsertOffset(getMinHeaderInsertionOffset(FileName, Code, Style)),
357 MaxInsertOffset(MinInsertOffset +
358 getMaxHeaderInsertionOffset(
359 FileName, Code.drop_front(MinInsertOffset), Style)),
360 MainIncludeFound(
false),
361 ShouldInsertGlobalModuleFragmentDecl(
362 isFirstDeclModuleDecl(FileName, Code, Style)),
363 Categories(Style, FileName) {
367 for (
const auto &Category : Style.IncludeCategories)
368 Priorities.insert(Category.Priority);
370 Code.drop_front(MinInsertOffset).split(Lines,
"\n");
372 unsigned Offset = MinInsertOffset;
373 unsigned NextLineOffset;
376 NextLineOffset = std::min(Code.size(), Offset +
Line.size() + 1);
383 Offset, std::min(
Line.size() + 1, Code.size() - Offset)),
388 Offset = NextLineOffset;
395 auto Highest = Priorities.begin();
396 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);
398 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;
403 for (
auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I)
404 if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end())
405 CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)];
409void HeaderIncludes::addExistingInclude(
Include IncludeToAdd,
410 unsigned NextLineOffset) {
411 auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)];
412 Incs.push_back(std::move(IncludeToAdd));
413 auto &CurInclude = Incs.back();
416 if (CurInclude.R.getOffset() <= MaxInsertOffset) {
418 CurInclude.Name, !MainIncludeFound);
420 MainIncludeFound =
true;
421 CategoryEndOffsets[Priority] = NextLineOffset;
422 IncludesByPriority[Priority].push_back(&CurInclude);
423 if (FirstIncludeOffset < 0)
424 FirstIncludeOffset = CurInclude.R.getOffset();
428std::optional<tooling::Replacement>
431 assert(IncludeName == trimInclude(IncludeName));
435 auto It = ExistingIncludes.find(IncludeName);
436 if (It != ExistingIncludes.end()) {
437 for (
const auto &Inc : It->second)
439 ((IsAngled && StringRef(Inc.Name).starts_with(
"<")) ||
440 (!IsAngled && StringRef(Inc.Name).starts_with(
"\""))))
444 std::string(llvm::formatv(IsAngled ?
"<{0}>" :
"\"{0}\"", IncludeName));
445 StringRef QuotedName = Quoted;
446 int Priority = Categories.getIncludePriority(
447 QuotedName, !MainIncludeFound);
448 auto CatOffset = CategoryEndOffsets.find(Priority);
449 assert(CatOffset != CategoryEndOffsets.end());
450 unsigned InsertOffset = CatOffset->second;
451 auto Iter = IncludesByPriority.find(Priority);
452 if (Iter != IncludesByPriority.end()) {
453 for (
const auto *Inc : Iter->second) {
454 if (QuotedName < Inc->Name) {
455 InsertOffset = Inc->R.getOffset();
460 assert(InsertOffset <= Code.size());
461 llvm::StringRef DirectiveSpelling =
463 std::string NewInclude =
464 llvm::formatv(
"#{0} {1}\n", DirectiveSpelling, QuotedName);
469 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() !=
'\n'))
470 NewInclude =
"\n" + NewInclude;
471 if (ShouldInsertGlobalModuleFragmentDecl)
472 NewInclude =
"module;\n" + NewInclude;
477 bool IsAngled)
const {
478 assert(IncludeName == trimInclude(IncludeName));
480 auto Iter = ExistingIncludes.find(IncludeName);
481 if (Iter == ExistingIncludes.end())
483 for (
const auto &Inc : Iter->second) {
484 if ((IsAngled && StringRef(Inc.Name).starts_with(
"\"")) ||
485 (!IsAngled && StringRef(Inc.Name).starts_with(
"<")))
488 FileName, Inc.R.getOffset(), Inc.R.getLength(),
""));
490 auto ErrMsg =
"Unexpected conflicts in #include deletions: " +
491 llvm::toString(std::move(Err));
492 llvm_unreachable(ErrMsg.c_str());
Defines the SourceManager interface.
VerifyDiagnosticConsumer::Directive 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.
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