144564ac7SEric Liu //===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===// 244564ac7SEric Liu // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 644564ac7SEric Liu // 744564ac7SEric Liu //===----------------------------------------------------------------------===// 844564ac7SEric Liu 944564ac7SEric Liu #include "clang/Tooling/Inclusions/HeaderIncludes.h" 10e08464fbSReid Kleckner #include "clang/Basic/FileManager.h" 1144564ac7SEric Liu #include "clang/Basic/SourceManager.h" 1244564ac7SEric Liu #include "clang/Lex/Lexer.h" 13bb898704SEric Liu #include "llvm/Support/FormatVariadic.h" 1426682562SSam McCall #include "llvm/Support/Path.h" 15a1580d7bSKazu Hirata #include <optional> 1644564ac7SEric Liu 1744564ac7SEric Liu namespace clang { 1844564ac7SEric Liu namespace tooling { 1944564ac7SEric Liu namespace { 2044564ac7SEric Liu 2144564ac7SEric Liu LangOptions createLangOpts() { 2244564ac7SEric Liu LangOptions LangOpts; 2344564ac7SEric Liu LangOpts.CPlusPlus = 1; 2444564ac7SEric Liu LangOpts.CPlusPlus11 = 1; 2544564ac7SEric Liu LangOpts.CPlusPlus14 = 1; 2644564ac7SEric Liu LangOpts.LineComment = 1; 2744564ac7SEric Liu LangOpts.CXXOperatorNames = 1; 2844564ac7SEric Liu LangOpts.Bool = 1; 29fa98390bSErik Pilkington LangOpts.ObjC = 1; 3044564ac7SEric Liu LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally. 3144564ac7SEric Liu LangOpts.DeclSpecKeyword = 1; // To get __declspec. 3244564ac7SEric Liu LangOpts.WChar = 1; // To get wchar_t 3344564ac7SEric Liu return LangOpts; 3444564ac7SEric Liu } 3544564ac7SEric Liu 3644564ac7SEric Liu // Returns the offset after skipping a sequence of tokens, matched by \p 3744564ac7SEric Liu // GetOffsetAfterSequence, from the start of the code. 3844564ac7SEric Liu // \p GetOffsetAfterSequence should be a function that matches a sequence of 3944564ac7SEric Liu // tokens and returns an offset after the sequence. 4044564ac7SEric Liu unsigned getOffsetAfterTokenSequence( 4144564ac7SEric Liu StringRef FileName, StringRef Code, const IncludeStyle &Style, 4244564ac7SEric Liu llvm::function_ref<unsigned(const SourceManager &, Lexer &, Token &)> 4344564ac7SEric Liu GetOffsetAfterSequence) { 4444564ac7SEric Liu SourceManagerForFile VirtualSM(FileName, Code); 4544564ac7SEric Liu SourceManager &SM = VirtualSM.get(); 46b3e2dac2SDawid Jurczak LangOptions LangOpts = createLangOpts(); 47b3eff6b7SDuncan P. N. Exon Smith Lexer Lex(SM.getMainFileID(), SM.getBufferOrFake(SM.getMainFileID()), SM, 48b3e2dac2SDawid Jurczak LangOpts); 4944564ac7SEric Liu Token Tok; 5044564ac7SEric Liu // Get the first token. 5144564ac7SEric Liu Lex.LexFromRawLexer(Tok); 5244564ac7SEric Liu return GetOffsetAfterSequence(SM, Lex, Tok); 5344564ac7SEric Liu } 5444564ac7SEric Liu 5544564ac7SEric Liu // Check if a sequence of tokens is like "#<Name> <raw_identifier>". If it is, 5644564ac7SEric Liu // \p Tok will be the token after this directive; otherwise, it can be any token 5724739613SEric Liu // after the given \p Tok (including \p Tok). If \p RawIDName is provided, the 5824739613SEric Liu // (second) raw_identifier name is checked. 5924739613SEric Liu bool checkAndConsumeDirectiveWithName( 6024739613SEric Liu Lexer &Lex, StringRef Name, Token &Tok, 616ad0788cSKazu Hirata std::optional<StringRef> RawIDName = std::nullopt) { 6244564ac7SEric Liu bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && 6344564ac7SEric Liu Tok.is(tok::raw_identifier) && 6444564ac7SEric Liu Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) && 6524739613SEric Liu Tok.is(tok::raw_identifier) && 6624739613SEric Liu (!RawIDName || Tok.getRawIdentifier() == *RawIDName); 6744564ac7SEric Liu if (Matched) 6844564ac7SEric Liu Lex.LexFromRawLexer(Tok); 6944564ac7SEric Liu return Matched; 7044564ac7SEric Liu } 7144564ac7SEric Liu 7244564ac7SEric Liu void skipComments(Lexer &Lex, Token &Tok) { 7344564ac7SEric Liu while (Tok.is(tok::comment)) 7444564ac7SEric Liu if (Lex.LexFromRawLexer(Tok)) 7544564ac7SEric Liu return; 7644564ac7SEric Liu } 7744564ac7SEric Liu 7844564ac7SEric Liu // Returns the offset after header guard directives and any comments 7924739613SEric Liu // before/after header guards (e.g. #ifndef/#define pair, #pragma once). If no 8024739613SEric Liu // header guard is present in the code, this will return the offset after 8124739613SEric Liu // skipping all comments from the start of the code. 8244564ac7SEric Liu unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName, 8344564ac7SEric Liu StringRef Code, 8444564ac7SEric Liu const IncludeStyle &Style) { 8524739613SEric Liu // \p Consume returns location after header guard or 0 if no header guard is 8624739613SEric Liu // found. 8724739613SEric Liu auto ConsumeHeaderGuardAndComment = 8824739613SEric Liu [&](std::function<unsigned(const SourceManager &SM, Lexer &Lex, 8924739613SEric Liu Token Tok)> 9024739613SEric Liu Consume) { 9144564ac7SEric Liu return getOffsetAfterTokenSequence( 9244564ac7SEric Liu FileName, Code, Style, 9324739613SEric Liu [&Consume](const SourceManager &SM, Lexer &Lex, Token Tok) { 9444564ac7SEric Liu skipComments(Lex, Tok); 9544564ac7SEric Liu unsigned InitialOffset = SM.getFileOffset(Tok.getLocation()); 9624739613SEric Liu return std::max(InitialOffset, Consume(SM, Lex, Tok)); 9724739613SEric Liu }); 9824739613SEric Liu }; 9924739613SEric Liu return std::max( 10024739613SEric Liu // #ifndef/#define 10124739613SEric Liu ConsumeHeaderGuardAndComment( 10224739613SEric Liu [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned { 10344564ac7SEric Liu if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) { 10444564ac7SEric Liu skipComments(Lex, Tok); 105ea8e71c3SKadir Cetinkaya if (checkAndConsumeDirectiveWithName(Lex, "define", Tok) && 106ea8e71c3SKadir Cetinkaya Tok.isAtStartOfLine()) 10744564ac7SEric Liu return SM.getFileOffset(Tok.getLocation()); 10844564ac7SEric Liu } 10924739613SEric Liu return 0; 11024739613SEric Liu }), 11124739613SEric Liu // #pragma once 11224739613SEric Liu ConsumeHeaderGuardAndComment( 11324739613SEric Liu [](const SourceManager &SM, Lexer &Lex, Token Tok) -> unsigned { 11424739613SEric Liu if (checkAndConsumeDirectiveWithName(Lex, "pragma", Tok, 11524739613SEric Liu StringRef("once"))) 11624739613SEric Liu return SM.getFileOffset(Tok.getLocation()); 11724739613SEric Liu return 0; 11824739613SEric Liu })); 11944564ac7SEric Liu } 12044564ac7SEric Liu 12144564ac7SEric Liu // Check if a sequence of tokens is like 12244564ac7SEric Liu // "#include ("header.h" | <header.h>)". 12344564ac7SEric Liu // If it is, \p Tok will be the token after this directive; otherwise, it can be 12444564ac7SEric Liu // any token after the given \p Tok (including \p Tok). 12544564ac7SEric Liu bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) { 12644564ac7SEric Liu auto Matched = [&]() { 12744564ac7SEric Liu Lex.LexFromRawLexer(Tok); 12844564ac7SEric Liu return true; 12944564ac7SEric Liu }; 13044564ac7SEric Liu if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && 13144564ac7SEric Liu Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "include") { 13244564ac7SEric Liu if (Lex.LexFromRawLexer(Tok)) 13344564ac7SEric Liu return false; 13444564ac7SEric Liu if (Tok.is(tok::string_literal)) 13544564ac7SEric Liu return Matched(); 13644564ac7SEric Liu if (Tok.is(tok::less)) { 13744564ac7SEric Liu while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) { 13844564ac7SEric Liu } 13944564ac7SEric Liu if (Tok.is(tok::greater)) 14044564ac7SEric Liu return Matched(); 14144564ac7SEric Liu } 14244564ac7SEric Liu } 14344564ac7SEric Liu return false; 14444564ac7SEric Liu } 14544564ac7SEric Liu 14644564ac7SEric Liu // Returns the offset of the last #include directive after which a new 14744564ac7SEric Liu // #include can be inserted. This ignores #include's after the #include block(s) 14844564ac7SEric Liu // in the beginning of a file to avoid inserting headers into code sections 14944564ac7SEric Liu // where new #include's should not be added by default. 15044564ac7SEric Liu // These code sections include: 15144564ac7SEric Liu // - raw string literals (containing #include). 15244564ac7SEric Liu // - #if blocks. 15344564ac7SEric Liu // - Special #include's among declarations (e.g. functions). 15444564ac7SEric Liu // 15544564ac7SEric Liu // If no #include after which a new #include can be inserted, this returns the 15644564ac7SEric Liu // offset after skipping all comments from the start of the code. 15744564ac7SEric Liu // Inserting after an #include is not allowed if it comes after code that is not 15844564ac7SEric Liu // #include (e.g. pre-processing directive that is not #include, declarations). 15944564ac7SEric Liu unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code, 16044564ac7SEric Liu const IncludeStyle &Style) { 16144564ac7SEric Liu return getOffsetAfterTokenSequence( 16244564ac7SEric Liu FileName, Code, Style, 16344564ac7SEric Liu [](const SourceManager &SM, Lexer &Lex, Token Tok) { 16444564ac7SEric Liu skipComments(Lex, Tok); 16544564ac7SEric Liu unsigned MaxOffset = SM.getFileOffset(Tok.getLocation()); 16644564ac7SEric Liu while (checkAndConsumeInclusiveDirective(Lex, Tok)) 16744564ac7SEric Liu MaxOffset = SM.getFileOffset(Tok.getLocation()); 16844564ac7SEric Liu return MaxOffset; 16944564ac7SEric Liu }); 17044564ac7SEric Liu } 17144564ac7SEric Liu 172e8cc7490SKrasimir Georgiev inline StringRef trimInclude(StringRef IncludeName) { 173e8cc7490SKrasimir Georgiev return IncludeName.trim("\"<>"); 174e8cc7490SKrasimir Georgiev } 175e8cc7490SKrasimir Georgiev 176e8cc7490SKrasimir Georgiev const char IncludeRegexPattern[] = 177e8cc7490SKrasimir Georgiev R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; 178e8cc7490SKrasimir Georgiev 17926682562SSam McCall // The filename of Path excluding extension. 18026682562SSam McCall // Used to match implementation with headers, this differs from sys::path::stem: 18126682562SSam McCall // - in names with multiple dots (foo.cu.cc) it terminates at the *first* 18226682562SSam McCall // - an empty stem is never returned: /foo/.bar.x => .bar 18326682562SSam McCall // - we don't bother to handle . and .. specially 18426682562SSam McCall StringRef matchingStem(llvm::StringRef Path) { 18526682562SSam McCall StringRef Name = llvm::sys::path::filename(Path); 18626682562SSam McCall return Name.substr(0, Name.find('.', 1)); 18726682562SSam McCall } 18826682562SSam McCall 18944564ac7SEric Liu } // anonymous namespace 19044564ac7SEric Liu 19144564ac7SEric Liu IncludeCategoryManager::IncludeCategoryManager(const IncludeStyle &Style, 19244564ac7SEric Liu StringRef FileName) 19344564ac7SEric Liu : Style(Style), FileName(FileName) { 1948668eae2Smydeveloperday for (const auto &Category : Style.IncludeCategories) { 1958668eae2Smydeveloperday CategoryRegexs.emplace_back(Category.Regex, Category.RegexIsCaseSensitive 1968668eae2Smydeveloperday ? llvm::Regex::NoFlags 1978668eae2Smydeveloperday : llvm::Regex::IgnoreCase); 1988668eae2Smydeveloperday } 199f3dcc235SKazu Hirata IsMainFile = FileName.ends_with(".c") || FileName.ends_with(".cc") || 200f3dcc235SKazu Hirata FileName.ends_with(".cpp") || FileName.ends_with(".c++") || 201f3dcc235SKazu Hirata FileName.ends_with(".cxx") || FileName.ends_with(".m") || 202f3dcc235SKazu Hirata FileName.ends_with(".mm"); 203335ac2ebSmydeveloperday if (!Style.IncludeIsMainSourceRegex.empty()) { 204335ac2ebSmydeveloperday llvm::Regex MainFileRegex(Style.IncludeIsMainSourceRegex); 205335ac2ebSmydeveloperday IsMainFile |= MainFileRegex.match(FileName); 206335ac2ebSmydeveloperday } 20744564ac7SEric Liu } 20844564ac7SEric Liu 20944564ac7SEric Liu int IncludeCategoryManager::getIncludePriority(StringRef IncludeName, 21044564ac7SEric Liu bool CheckMainHeader) const { 21144564ac7SEric Liu int Ret = INT_MAX; 21244564ac7SEric Liu for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) 21344564ac7SEric Liu if (CategoryRegexs[i].match(IncludeName)) { 21444564ac7SEric Liu Ret = Style.IncludeCategories[i].Priority; 21544564ac7SEric Liu break; 21644564ac7SEric Liu } 21744564ac7SEric Liu if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) 21844564ac7SEric Liu Ret = 0; 21944564ac7SEric Liu return Ret; 22044564ac7SEric Liu } 22144564ac7SEric Liu 22252e44b14SPaul Hoad int IncludeCategoryManager::getSortIncludePriority(StringRef IncludeName, 22352e44b14SPaul Hoad bool CheckMainHeader) const { 22452e44b14SPaul Hoad int Ret = INT_MAX; 22552e44b14SPaul Hoad for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) 22652e44b14SPaul Hoad if (CategoryRegexs[i].match(IncludeName)) { 22752e44b14SPaul Hoad Ret = Style.IncludeCategories[i].SortPriority; 22852e44b14SPaul Hoad if (Ret == 0) 22952e44b14SPaul Hoad Ret = Style.IncludeCategories[i].Priority; 23052e44b14SPaul Hoad break; 23152e44b14SPaul Hoad } 23252e44b14SPaul Hoad if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) 23352e44b14SPaul Hoad Ret = 0; 23452e44b14SPaul Hoad return Ret; 23552e44b14SPaul Hoad } 23644564ac7SEric Liu bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { 237984dd15dSj-jorge switch (Style.MainIncludeChar) { 238984dd15dSj-jorge case IncludeStyle::MICD_Quote: 239f3dcc235SKazu Hirata if (!IncludeName.starts_with("\"")) 24044564ac7SEric Liu return false; 241984dd15dSj-jorge break; 242984dd15dSj-jorge case IncludeStyle::MICD_AngleBracket: 243984dd15dSj-jorge if (!IncludeName.starts_with("<")) 244984dd15dSj-jorge return false; 245984dd15dSj-jorge break; 246984dd15dSj-jorge case IncludeStyle::MICD_Any: 247984dd15dSj-jorge break; 248984dd15dSj-jorge } 249c1b209ccSHaojian Wu 25084048e23SHaojian Wu IncludeName = 25184048e23SHaojian Wu IncludeName.drop_front(1).drop_back(1); // remove the surrounding "" or <> 252c1b209ccSHaojian Wu // Not matchingStem: implementation files may have compound extensions but 253c1b209ccSHaojian Wu // headers may not. 25484048e23SHaojian Wu StringRef HeaderStem = llvm::sys::path::stem(IncludeName); 25584048e23SHaojian Wu StringRef FileStem = llvm::sys::path::stem(FileName); // foo.cu for foo.cu.cc 25684048e23SHaojian Wu StringRef MatchingFileStem = matchingStem(FileName); // foo for foo.cu.cc 25784048e23SHaojian Wu // main-header examples: 25884048e23SHaojian Wu // 1) foo.h => foo.cc 25984048e23SHaojian Wu // 2) foo.h => foo.cu.cc 26084048e23SHaojian Wu // 3) foo.proto.h => foo.proto.cc 26184048e23SHaojian Wu // 26284048e23SHaojian Wu // non-main-header examples: 26384048e23SHaojian Wu // 1) foo.h => bar.cc 26484048e23SHaojian Wu // 2) foo.proto.h => foo.cc 26584048e23SHaojian Wu StringRef Matching; 266ed1539c6SKazu Hirata if (MatchingFileStem.starts_with_insensitive(HeaderStem)) 26784048e23SHaojian Wu Matching = MatchingFileStem; // example 1), 2) 268e5c7c171SMartin Storsjö else if (FileStem.equals_insensitive(HeaderStem)) 26984048e23SHaojian Wu Matching = FileStem; // example 3) 27084048e23SHaojian Wu if (!Matching.empty()) { 271bb898704SEric Liu llvm::Regex MainIncludeRegex(HeaderStem.str() + Style.IncludeIsMainRegex, 27244564ac7SEric Liu llvm::Regex::IgnoreCase); 27384048e23SHaojian Wu if (MainIncludeRegex.match(Matching)) 27444564ac7SEric Liu return true; 27544564ac7SEric Liu } 27644564ac7SEric Liu return false; 27744564ac7SEric Liu } 27844564ac7SEric Liu 2797cbc9206Sowenca const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern); 2807cbc9206Sowenca 28144564ac7SEric Liu HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, 28244564ac7SEric Liu const IncludeStyle &Style) 28344564ac7SEric Liu : FileName(FileName), Code(Code), FirstIncludeOffset(-1), 28444564ac7SEric Liu MinInsertOffset( 28544564ac7SEric Liu getOffsetAfterHeaderGuardsAndComments(FileName, Code, Style)), 28644564ac7SEric Liu MaxInsertOffset(MinInsertOffset + 28744564ac7SEric Liu getMaxHeaderInsertionOffset( 28844564ac7SEric Liu FileName, Code.drop_front(MinInsertOffset), Style)), 28985c6d57eSHaojian Wu MainIncludeFound(false), 2907cbc9206Sowenca Categories(Style, FileName) { 29144564ac7SEric Liu // Add 0 for main header and INT_MAX for headers that are not in any 29244564ac7SEric Liu // category. 29344564ac7SEric Liu Priorities = {0, INT_MAX}; 29444564ac7SEric Liu for (const auto &Category : Style.IncludeCategories) 29544564ac7SEric Liu Priorities.insert(Category.Priority); 29644564ac7SEric Liu SmallVector<StringRef, 32> Lines; 29744564ac7SEric Liu Code.drop_front(MinInsertOffset).split(Lines, "\n"); 29844564ac7SEric Liu 29944564ac7SEric Liu unsigned Offset = MinInsertOffset; 30044564ac7SEric Liu unsigned NextLineOffset; 30144564ac7SEric Liu SmallVector<StringRef, 4> Matches; 30244564ac7SEric Liu for (auto Line : Lines) { 30344564ac7SEric Liu NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1); 30444564ac7SEric Liu if (IncludeRegex.match(Line, &Matches)) { 30544564ac7SEric Liu // If this is the last line without trailing newline, we need to make 30644564ac7SEric Liu // sure we don't delete across the file boundary. 30744564ac7SEric Liu addExistingInclude( 308e8cc7490SKrasimir Georgiev Include(Matches[2], 30944564ac7SEric Liu tooling::Range( 310fc46d6e6SDavid Goldman Offset, std::min(Line.size() + 1, Code.size() - Offset)), 311fc46d6e6SDavid Goldman Matches[1] == "import" ? tooling::IncludeDirective::Import 312fc46d6e6SDavid Goldman : tooling::IncludeDirective::Include), 31344564ac7SEric Liu NextLineOffset); 31444564ac7SEric Liu } 31544564ac7SEric Liu Offset = NextLineOffset; 31644564ac7SEric Liu } 31744564ac7SEric Liu 31844564ac7SEric Liu // Populate CategoryEndOfssets: 31944564ac7SEric Liu // - Ensure that CategoryEndOffset[Highest] is always populated. 32044564ac7SEric Liu // - If CategoryEndOffset[Priority] isn't set, use the next higher value 32144564ac7SEric Liu // that is set, up to CategoryEndOffset[Highest]. 32244564ac7SEric Liu auto Highest = Priorities.begin(); 323a8289a35SKazu Hirata auto [It, Inserted] = CategoryEndOffsets.try_emplace(*Highest); 324a8289a35SKazu Hirata if (Inserted) 325a8289a35SKazu Hirata It->second = FirstIncludeOffset >= 0 ? FirstIncludeOffset : MinInsertOffset; 32644564ac7SEric Liu // By this point, CategoryEndOffset[Highest] is always set appropriately: 32744564ac7SEric Liu // - to an appropriate location before/after existing #includes, or 32844564ac7SEric Liu // - to right after the header guard, or 32944564ac7SEric Liu // - to the beginning of the file. 33044564ac7SEric Liu for (auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I) 33144564ac7SEric Liu if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end()) 33244564ac7SEric Liu CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)]; 33344564ac7SEric Liu } 33444564ac7SEric Liu 33544564ac7SEric Liu // \p Offset: the start of the line following this include directive. 33644564ac7SEric Liu void HeaderIncludes::addExistingInclude(Include IncludeToAdd, 33744564ac7SEric Liu unsigned NextLineOffset) { 338*f13d3f72SKazu Hirata auto &Incs = ExistingIncludes[trimInclude(IncludeToAdd.Name)]; 339*f13d3f72SKazu Hirata Incs.push_back(std::move(IncludeToAdd)); 340*f13d3f72SKazu Hirata auto &CurInclude = Incs.back(); 34144564ac7SEric Liu // The header name with quotes or angle brackets. 34244564ac7SEric Liu // Only record the offset of current #include if we can insert after it. 34344564ac7SEric Liu if (CurInclude.R.getOffset() <= MaxInsertOffset) { 34444564ac7SEric Liu int Priority = Categories.getIncludePriority( 34585c6d57eSHaojian Wu CurInclude.Name, /*CheckMainHeader=*/!MainIncludeFound); 34685c6d57eSHaojian Wu if (Priority == 0) 34785c6d57eSHaojian Wu MainIncludeFound = true; 34844564ac7SEric Liu CategoryEndOffsets[Priority] = NextLineOffset; 34944564ac7SEric Liu IncludesByPriority[Priority].push_back(&CurInclude); 35044564ac7SEric Liu if (FirstIncludeOffset < 0) 35144564ac7SEric Liu FirstIncludeOffset = CurInclude.R.getOffset(); 35244564ac7SEric Liu } 35344564ac7SEric Liu } 35444564ac7SEric Liu 3556ad0788cSKazu Hirata std::optional<tooling::Replacement> 356fc46d6e6SDavid Goldman HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled, 357fc46d6e6SDavid Goldman IncludeDirective Directive) const { 35844564ac7SEric Liu assert(IncludeName == trimInclude(IncludeName)); 35944564ac7SEric Liu // If a <header> ("header") already exists in code, "header" (<header>) with 360fc46d6e6SDavid Goldman // different quotation and/or directive will still be inserted. 36144564ac7SEric Liu // FIXME: figure out if this is the best behavior. 36244564ac7SEric Liu auto It = ExistingIncludes.find(IncludeName); 363fc46d6e6SDavid Goldman if (It != ExistingIncludes.end()) { 36444564ac7SEric Liu for (const auto &Inc : It->second) 365fc46d6e6SDavid Goldman if (Inc.Directive == Directive && 366f3dcc235SKazu Hirata ((IsAngled && StringRef(Inc.Name).starts_with("<")) || 367f3dcc235SKazu Hirata (!IsAngled && StringRef(Inc.Name).starts_with("\"")))) 3685891420eSKazu Hirata return std::nullopt; 369fc46d6e6SDavid Goldman } 370bb898704SEric Liu std::string Quoted = 371adcd0268SBenjamin Kramer std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", IncludeName)); 37244564ac7SEric Liu StringRef QuotedName = Quoted; 37344564ac7SEric Liu int Priority = Categories.getIncludePriority( 37485c6d57eSHaojian Wu QuotedName, /*CheckMainHeader=*/!MainIncludeFound); 37544564ac7SEric Liu auto CatOffset = CategoryEndOffsets.find(Priority); 37644564ac7SEric Liu assert(CatOffset != CategoryEndOffsets.end()); 37744564ac7SEric Liu unsigned InsertOffset = CatOffset->second; // Fall back offset 37844564ac7SEric Liu auto Iter = IncludesByPriority.find(Priority); 37944564ac7SEric Liu if (Iter != IncludesByPriority.end()) { 38044564ac7SEric Liu for (const auto *Inc : Iter->second) { 38144564ac7SEric Liu if (QuotedName < Inc->Name) { 38244564ac7SEric Liu InsertOffset = Inc->R.getOffset(); 38344564ac7SEric Liu break; 38444564ac7SEric Liu } 38544564ac7SEric Liu } 38644564ac7SEric Liu } 38744564ac7SEric Liu assert(InsertOffset <= Code.size()); 388fc46d6e6SDavid Goldman llvm::StringRef DirectiveSpelling = 389fc46d6e6SDavid Goldman Directive == IncludeDirective::Include ? "include" : "import"; 390adcd0268SBenjamin Kramer std::string NewInclude = 391fc46d6e6SDavid Goldman llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName); 39244564ac7SEric Liu // When inserting headers at end of the code, also append '\n' to the code 39344564ac7SEric Liu // if it does not end with '\n'. 39444564ac7SEric Liu // FIXME: when inserting multiple #includes at the end of code, only one 39544564ac7SEric Liu // newline should be added. 39644564ac7SEric Liu if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n')) 39744564ac7SEric Liu NewInclude = "\n" + NewInclude; 39844564ac7SEric Liu return tooling::Replacement(FileName, InsertOffset, 0, NewInclude); 39944564ac7SEric Liu } 40044564ac7SEric Liu 40144564ac7SEric Liu tooling::Replacements HeaderIncludes::remove(llvm::StringRef IncludeName, 40244564ac7SEric Liu bool IsAngled) const { 40344564ac7SEric Liu assert(IncludeName == trimInclude(IncludeName)); 40444564ac7SEric Liu tooling::Replacements Result; 40544564ac7SEric Liu auto Iter = ExistingIncludes.find(IncludeName); 40644564ac7SEric Liu if (Iter == ExistingIncludes.end()) 40744564ac7SEric Liu return Result; 40844564ac7SEric Liu for (const auto &Inc : Iter->second) { 409f3dcc235SKazu Hirata if ((IsAngled && StringRef(Inc.Name).starts_with("\"")) || 410f3dcc235SKazu Hirata (!IsAngled && StringRef(Inc.Name).starts_with("<"))) 41144564ac7SEric Liu continue; 41244564ac7SEric Liu llvm::Error Err = Result.add(tooling::Replacement( 41344564ac7SEric Liu FileName, Inc.R.getOffset(), Inc.R.getLength(), "")); 41444564ac7SEric Liu if (Err) { 41544564ac7SEric Liu auto ErrMsg = "Unexpected conflicts in #include deletions: " + 41644564ac7SEric Liu llvm::toString(std::move(Err)); 41744564ac7SEric Liu llvm_unreachable(ErrMsg.c_str()); 41844564ac7SEric Liu } 41944564ac7SEric Liu } 42044564ac7SEric Liu return Result; 42144564ac7SEric Liu } 42244564ac7SEric Liu 42344564ac7SEric Liu } // namespace tooling 42444564ac7SEric Liu } // namespace clang 425