clang 24.0.0git
HeaderIncludes.cpp
Go to the documentation of this file.
1//===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/Basic/LLVM.h"
13#include "clang/Lex/Lexer.h"
14#include "clang/Lex/Token.h"
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"
26#include <algorithm>
27#include <cassert>
28#include <climits>
29#include <functional>
30#include <iterator>
31#include <optional>
32#include <string>
33#include <type_traits>
34#include <utility>
35#include <vector>
36
37namespace clang {
38namespace tooling {
39namespace {
40
41LangOptions createLangOpts() {
42 LangOptions LangOpts;
43 LangOpts.CPlusPlus = 1;
44 LangOpts.CPlusPlus11 = 1;
45 LangOpts.CPlusPlus14 = 1;
46 LangOpts.LineComment = 1;
47 LangOpts.CXXOperatorNames = 1;
48 LangOpts.Bool = 1;
49 LangOpts.ObjC = 1;
50 LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally.
51 LangOpts.DeclSpecKeyword = 1; // To get __declspec.
52 LangOpts.WChar = 1; // To get wchar_t
53 return LangOpts;
54}
55
56// Create a new lexer on the given \p Code and calls \p Callback with the
57// created source manager and lexer. \p Callback must be a callable object that
58// could be invoked with (const SourceManager &, Lexer &). This function returns
59// whatever \p Callback returns.
60template <typename F>
61auto withLexer(StringRef FileName, StringRef Code, const IncludeStyle &Style,
62 F &&Callback)
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,
68 LangOpts);
69 return std::invoke(std::forward<F>(Callback), std::as_const(SM), Lex);
70}
71
72// Returns the offset after skipping a sequence of tokens, matched by \p
73// GetOffsetAfterSequence, from the start of the code.
74// \p GetOffsetAfterSequence should be a function that matches a sequence of
75// tokens and returns an offset after the sequence.
76unsigned getOffsetAfterTokenSequence(
77 StringRef FileName, StringRef Code, const IncludeStyle &Style,
78 llvm::function_ref<unsigned(const SourceManager &, Lexer &, Token &)>
79 GetOffsetAfterSequence) {
80 return withLexer(FileName, Code, Style,
81 [&](const SourceManager &SM, Lexer &Lex) {
82 Token Tok;
83 // Get the first token.
84 Lex.LexFromRawLexer(Tok);
85 return GetOffsetAfterSequence(SM, Lex, Tok);
86 });
87}
88
89// Check if a sequence of tokens is like "#<Name> <raw_identifier>". If it is,
90// \p Tok will be the token after this directive; otherwise, it can be any token
91// after the given \p Tok (including \p Tok). If \p RawIDName is provided, the
92// (second) raw_identifier name is checked.
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);
101 if (Matched)
102 Lex.LexFromRawLexer(Tok);
103 return Matched;
104}
105
106void skipComments(Lexer &Lex, Token &Tok) {
107 while (Tok.is(tok::comment))
108 if (Lex.LexFromRawLexer(Tok))
109 return;
110}
111
112bool checkAndConsumeModuleDecl(const SourceManager &SM, Lexer &Lex,
113 Token &Tok) {
114 bool Matched = Tok.is(tok::raw_identifier) &&
115 Tok.getRawIdentifier() == "module" &&
116 !Lex.LexFromRawLexer(Tok) && Tok.is(tok::semi) &&
117 !Lex.LexFromRawLexer(Tok);
118 return Matched;
119}
120
121// Determines the minimum offset into the file where we want to insert header
122// includes. This will be put (when available):
123// - after `#pragma once`
124// - after header guards (`#ifdef` and `#define`)
125// - after opening global module (`module;`)
126// - after any comments at the start of the file or immediately following one of
127// the above constructs
128unsigned getMinHeaderInsertionOffset(StringRef FileName, StringRef Code,
129 const IncludeStyle &Style) {
130 // \p Consume returns location after header guard or 0 if no header guard is
131 // found.
132 auto ConsumeHeaderGuardAndComment =
133 [&](std::function<unsigned(const SourceManager &SM, Lexer &Lex,
134 Token Tok)>
135 Consume) {
136 return getOffsetAfterTokenSequence(
137 FileName, Code, Style,
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));
142 });
143 };
144
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());
150 }
151 return 0;
152 });
153
154 auto HeaderAndPPOffset = std::max(
155 // #ifndef/#define
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());
163 }
164 return 0;
165 }),
166 // #pragma once
167 ConsumeHeaderGuardAndComment(
168 [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned {
169 if (checkAndConsumeDirectiveWithName(Lex, "pragma", Tok,
170 StringRef("once")))
171 return SM.getFileOffset(Tok.getLocation());
172 return 0;
173 }));
174 return std::max(HeaderAndPPOffset, ModuleDecl);
175}
176
177// Check if a sequence of tokens is like
178// "#(include | import) ("header.h" | <header.h>)".
179// If it is, \p Tok will be the token after this directive; otherwise, it can be
180// any token after the given \p Tok (including \p Tok).
181bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) {
182 auto Matched = [&]() {
183 Lex.LexFromRawLexer(Tok);
184 return true;
185 };
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))
191 return false;
192 if (Tok.is(tok::string_literal))
193 return Matched();
194 if (Tok.is(tok::less)) {
195 while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) {
196 }
197 if (Tok.is(tok::greater))
198 return Matched();
199 }
200 }
201 return false;
202}
203
204// Returns the offset of the last #include directive after which a new
205// #include can be inserted. This ignores #include's after the #include block(s)
206// in the beginning of a file to avoid inserting headers into code sections
207// where new #include's should not be added by default.
208// These code sections include:
209// - raw string literals (containing #include).
210// - #if blocks.
211// - Special #include's among declarations (e.g. functions).
212//
213// If no #include after which a new #include can be inserted, this returns the
214// offset after skipping all comments from the start of the code.
215// Inserting after an #include is not allowed if it comes after code that is not
216// #include (e.g. pre-processing directive that is not #include, declarations).
217unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code,
218 const IncludeStyle &Style) {
219 return getOffsetAfterTokenSequence(
220 FileName, Code, Style,
221 [](const SourceManager &SM, Lexer &Lex, Token Tok) {
222 skipComments(Lex, Tok);
223 unsigned MaxOffset = SM.getFileOffset(Tok.getLocation());
224 while (checkAndConsumeInclusiveDirective(Lex, Tok))
225 MaxOffset = SM.getFileOffset(Tok.getLocation());
226 return MaxOffset;
227 });
228}
229
230// Check whether the first declaration in the code is a C++20 module
231// declaration, and it is not preceded by any preprocessor directives.
232bool isFirstDeclModuleDecl(StringRef FileName, StringRef Code,
233 const IncludeStyle &Style) {
234 return withLexer(
235 FileName, Code, Style, [](const SourceManager &SM, Lexer &Lex) {
236 // Let the lexer skip any comments and whitespaces for us.
237 Lex.SetKeepWhitespaceMode(false);
238 Lex.SetCommentRetentionState(false);
239
240 Token tok;
241 if (Lex.LexFromRawLexer(tok))
242 return false;
243
244 // A module declaration is made up of the following token sequence:
245 // export? module <ident> ('.' <ident>)* <partition> <attr> ;
246 //
247 // For convenience, we don't actually lex the whole declaration -- it's
248 // enough to distinguish a module declaration to just ensure an <ident>
249 // is following the "module" keyword.
250
251 // Lex the optional "export" keyword.
252 if (tok.is(tok::raw_identifier) && tok.getRawIdentifier() == "export") {
253 if (Lex.LexFromRawLexer(tok))
254 return false;
255 }
256
257 // Lex the "module" keyword.
258 if (!tok.is(tok::raw_identifier) ||
259 tok.getRawIdentifier() != "module" || Lex.LexFromRawLexer(tok))
260 return false;
261
262 // Make sure an identifier follows the "module" keyword.
263 return tok.is(tok::raw_identifier);
264 });
265}
266
267inline StringRef trimInclude(StringRef IncludeName) {
268 return IncludeName.trim("\"<>");
269}
270
271const char IncludeRegexPattern[] =
272 "^[\t ]*#[\t ]*(import|include)[^\"<]*([\"<][^\">]*[\">])";
273
274// The filename of Path excluding extension.
275// Used to match implementation with headers, this differs from sys::path::stem:
276// - in names with multiple dots (foo.cu.cc) it terminates at the *first*
277// - an empty stem is never returned: /foo/.bar.x => .bar
278// - we don't bother to handle . and .. specially
279StringRef matchingStem(llvm::StringRef Path) {
280 StringRef Name = llvm::sys::path::filename(Path);
281 return Name.substr(0, Name.find('.', 1));
282}
283
284} // anonymous namespace
285
287 StringRef FileName)
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);
293 }
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);
301 }
302}
303
305 bool CheckMainHeader) const {
306 int Ret = INT_MAX;
307 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
308 if (CategoryRegexs[i].match(IncludeName)) {
309 Ret = Style.IncludeCategories[i].Priority;
310 break;
311 }
312 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
313 Ret = 0;
314 return Ret;
315}
316
318 bool CheckMainHeader) const {
319 int Ret = INT_MAX;
320 for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i)
321 if (CategoryRegexs[i].match(IncludeName)) {
322 Ret = Style.IncludeCategories[i].SortPriority;
323 if (Ret == 0)
324 Ret = Style.IncludeCategories[i].Priority;
325 break;
326 }
327 if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName))
328 Ret = 0;
329 return Ret;
330}
331bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
332 switch (Style.MainIncludeChar) {
334 if (!IncludeName.starts_with("\""))
335 return false;
336 break;
338 if (!IncludeName.starts_with("<"))
339 return false;
340 break;
342 break;
343 }
344
345 IncludeName =
346 IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <>
347 // Not matchingStem: implementation files may have compound extensions but
348 // headers may not.
349 StringRef HeaderStem = llvm::sys::path::stem(IncludeName);
350 StringRef FileStem = llvm::sys::path::stem(FileName); // foo.cu for foo.cu.cc
351 StringRef MatchingFileStem = matchingStem(FileName); // foo for foo.cu.cc
352 // main-header examples:
353 // 1) foo.h => foo.cc
354 // 2) foo.h => foo.cu.cc
355 // 3) foo.proto.h => foo.proto.cc
356 //
357 // non-main-header examples:
358 // 1) foo.h => bar.cc
359 // 2) foo.proto.h => foo.cc
360 StringRef Matching;
361 if (MatchingFileStem.starts_with_insensitive(HeaderStem))
362 Matching = MatchingFileStem; // example 1), 2)
363 else if (FileStem.equals_insensitive(HeaderStem))
364 Matching = FileStem; // example 3)
365 if (!Matching.empty()) {
366 llvm::Regex MainIncludeRegex(llvm::Regex::escape(HeaderStem) +
367 Style.IncludeIsMainRegex,
368 llvm::Regex::IgnoreCase);
369 if (MainIncludeRegex.match(Matching))
370 return true;
371 }
372 return false;
373}
374
375const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern);
376
377HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
378 const IncludeStyle &Style)
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) {
388 // Add 0 for main header and INT_MAX for headers that are not in any
389 // category.
390 Priorities = {0, INT_MAX};
391 for (const auto &Category : Style.IncludeCategories)
392 Priorities.insert(Category.Priority);
394 Code.drop_front(MinInsertOffset).split(Lines, "\n");
395
396 unsigned Offset = MinInsertOffset;
397 unsigned NextLineOffset;
399 for (auto Line : Lines) {
400 NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1);
401 if (IncludeRegex.match(Line, &Matches)) {
402 // If this is the last line without trailing newline, we need to make
403 // sure we don't delete across the file boundary.
404 addExistingInclude(
405 Include(Matches[2],
407 Offset, std::min(Line.size() + 1, Code.size() - Offset)),
408 Matches[1] == "import" ? tooling::IncludeDirective::Import
410 NextLineOffset);
411 }
412 Offset = NextLineOffset;
413 }
414
415 // Populate CategoryEndOfssets:
416 // - Ensure that CategoryEndOffset[Highest] is always populated.
417 // - If CategoryEndOffset[Priority] isn't set, use the next higher value
418 // that is set, up to CategoryEndOffset[Highest].
419 auto Highest = Priorities.begin();
420 auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest);
421 if (Inserted)
422 It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset;
423 // By this point, CategoryEndOffset[Highest] is always set appropriately:
424 // - to an appropriate location before/after existing #includes, or
425 // - to right after the header guard, or
426 // - to the beginning of the file.
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)];
430}
431
432// \p Offset: the start of the line following this include directive.
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();
438 // The header name with quotes or angle brackets.
439 // Only record the offset of current #include if we can insert after it.
440 if (CurInclude.R.getOffset() <= MaxInsertOffset) {
441 int Priority = Categories.getIncludePriority(
442 CurInclude.Name, /*CheckMainHeader=*/!MainIncludeFound);
443 if (Priority == 0)
444 MainIncludeFound = true;
445 CategoryEndOffsets[Priority] = NextLineOffset;
446 IncludesByPriority[Priority].push_back(&CurInclude);
447 if (FirstIncludeOffset < 0)
448 FirstIncludeOffset = CurInclude.R.getOffset();
449 }
450}
451
452std::optional<tooling::Replacement>
453HeaderIncludes::insert(llvm::StringRef Header, bool IsAngled,
455 assert(Header == trimInclude(Header));
456 // If a <header> ("header") already exists in code, "header" (<header>) with
457 // different quotation will still be inserted.
458 // FIXME: figure out if this is the best behavior.
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("\""));
464 if (SameQuotation) {
465 // If the directive is the same, or if the directive is an include and
466 // the existing directive is an import, then we don't need to insert
467 // the header.
468 if ((Inc.Directive == Directive) ||
469 (Inc.Directive == IncludeDirective::Import &&
471 return std::nullopt;
472 }
473
474 // "import" outranks "include" with the assumption that includes are
475 // designed to handle multiple inclusions while import is not.
476 char Open = IsAngled ? '<' : '"';
477 char Close = IsAngled ? '>' : '"';
478 std::string NewInclude =
479 llvm::formatv("#import {0}{1}{2}\n", Open, Header, Close);
480
481 return tooling::Replacement(FileName, Inc.R.getOffset(),
482 Inc.R.getLength(), NewInclude);
483 }
484 }
485 }
486 std::string Quoted =
487 std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", Header));
488 StringRef QuotedName = Quoted;
489 int Priority = Categories.getIncludePriority(
490 QuotedName, /*CheckMainHeader=*/!MainIncludeFound);
491 auto CatOffset = CategoryEndOffsets.find(Priority);
492 assert(CatOffset != CategoryEndOffsets.end());
493 unsigned InsertOffset = CatOffset->second; // Fall back offset
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();
499 break;
500 }
501 }
502 }
503 assert(InsertOffset <= Code.size());
504 llvm::StringRef DirectiveSpelling =
505 Directive == IncludeDirective::Include ? "include" : "import";
506 std::string NewInclude =
507 llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName);
508 // When inserting headers at end of the code, also append '\n' to the code
509 // if it does not end with '\n'.
510 // FIXME: when inserting multiple #includes at the end of code, only one
511 // newline should be added.
512 if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n'))
513 NewInclude = "\n" + NewInclude;
514 if (ShouldInsertGlobalModuleFragmentDecl)
515 NewInclude = "module;\n" + NewInclude;
516 return tooling::Replacement(FileName, InsertOffset, 0, NewInclude);
517}
518
520 llvm::StringRef RawOrSpelledHeader, IncludeDirective Directive,
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;
529 }
530}
531
535 if (Headers.empty())
536 return Result;
537
538 std::vector<HeaderToInsert> SortedHeaders = Headers.vec();
539 llvm::stable_sort(SortedHeaders, [&](const HeaderToInsert &L,
540 const HeaderToInsert &R) {
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, /*CheckMainHeader=*/!MainIncludeFound);
547 int PriorityR = Categories.getIncludePriority(
548 QuotedR, /*CheckMainHeader=*/!MainIncludeFound);
549 if (PriorityL != PriorityR)
550 return PriorityL < PriorityR;
551 if (L.Header != R.Header)
552 return L.Header < R.Header;
553 if (L.IsAngled != R.IsAngled)
554 return L.IsAngled < R.IsAngled;
555 return L.Directive > R.Directive;
556 });
557 SortedHeaders.erase(
558 std::unique(SortedHeaders.begin(), SortedHeaders.end(),
559 [](const HeaderToInsert &L, const HeaderToInsert &R) {
560 return L.Header == R.Header && L.IsAngled == R.IsAngled;
561 }),
562 SortedHeaders.end());
563
564 struct InsertionInfo {
565 std::string Text;
566 unsigned Length = 0;
567 };
568 llvm::DenseMap<unsigned, InsertionInfo> InsertionsByOffset;
569
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();
577 }
578 }
579 }
580
581 for (const auto &Entry : InsertionsByOffset) {
582 const auto &Info = Entry.second;
583 const unsigned Offset = Entry.first;
584 cantFail(Result.add(
585 tooling::Replacement(FileName, Offset, Info.Length, Info.Text)));
586 }
587
588 return Result;
589}
590
592 bool IsAngled) const {
593 assert(Header == trimInclude(Header));
595 auto Iter = ExistingIncludes.find(Header);
596 if (Iter == ExistingIncludes.end())
597 return Result;
598 for (const auto &Inc : Iter->second) {
599 if ((IsAngled && StringRef(Inc.Name).starts_with("\"")) ||
600 (!IsAngled && StringRef(Inc.Name).starts_with("<")))
601 continue;
602 llvm::Error Err = Result.add(tooling::Replacement(
603 FileName, Inc.R.getOffset(), Inc.R.getLength(), ""));
604 if (Err) {
605 auto ErrMsg = "Unexpected conflicts in #include deletions: " +
606 llvm::toString(std::move(Err));
607 llvm_unreachable(ErrMsg.c_str());
608 }
609 }
610 return Result;
611}
612
613} // namespace tooling
614} // namespace clang
Token Tok
The Token.
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
tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const
Removes all existing includes and imports of Header quoted with <> if IsAngled is true or "" if IsAng...
static const llvm::Regex IncludeRegex
HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, const IncludeStyle &Style)
std::optional< tooling::Replacement > insert(llvm::StringRef Header, bool IsAngled, IncludeDirective Directive) const
Inserts an include or import directive of Header into the code.
int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const
Returns the priority of the category which IncludeName belongs to.
IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName)
int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const
A source range independent of the SourceManager.
Definition Replacement.h:44
A text replacement.
Definition Replacement.h:83
Maintains a set of replacements that are conflict-free.
#define INT_MAX
Definition limits.h:50
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())))
Stmt Stmt * Callback
Definition StmtOpenMP.h:919
@ Result
The result type of a method or function.
Definition TypeBase.h:906
for(const auto &A :T->param_types())
int const char * function
Definition c++config.h:31
#define false
Definition stdbool.h:26
Represents a single header directive to be inserted in a batch operation.
HeaderToInsert(llvm::StringRef RawOrSpelledHeader, IncludeDirective Directive=IncludeDirective::Include, QuoteStyle QuoteStyle=QuoteStyle::AUTO)
Style for sorting and grouping C++ include directives.
@ MICD_Quote
Main include uses quotes: #include "foo.hpp" (the default).
@ MICD_AngleBracket
Main include uses angle brackets: #include <foo.hpp>.
@ MICD_Any
Main include uses either quotes or angle brackets.