diff --git a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h --- a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -129,6 +129,23 @@ llvm::Regex IncludeRegex; }; +/// \returns a regex that can match various styles of C++ includes. +/// For example: +/// \code +/// #include +/// @import bar; +/// #include "bar.h" +/// \endcode +llvm::Regex getCppIncludeRegex(); + +/// \returns the last match in the list of matches that is not empty. +llvm::StringRef getIncludeNameFromMatches( + const llvm::SmallVectorImpl &Matches); + +/// \returns the given include name and removes the following symbols from the +/// beginning and ending of the include name: " > < ; +llvm::StringRef trimInclude(llvm::StringRef IncludeName); + } // namespace tooling } // namespace clang diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -2722,23 +2722,6 @@ } } -namespace { - -const char CppIncludeRegexPattern[] = - R"(^[\t\ ]*[@#][\t\ ]*(import|include)([^"]*("[^"]+")|[^<]*(<[^>]+>)|[\t\ ]*([^;]+;)))"; -} // anonymous namespace - -// Returns the last match group in the above regex (CppIncludeRegexPattern) that -// is not empty. -StringRef getIncludeNameFromMatches(const SmallVectorImpl &Matches) { - for (int i = Matches.size() - 1; i > 0; i--) { - if (!Matches[i].empty()) - return Matches[i]; - } - llvm_unreachable("No non-empty match group found in list of matches"); - return StringRef(); -} - tooling::Replacements sortCppIncludes(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, StringRef FileName, @@ -2748,7 +2731,7 @@ .StartsWith("\xEF\xBB\xBF", 3) // UTF-8 BOM .Default(0); unsigned SearchFrom = 0; - llvm::Regex IncludeRegex(CppIncludeRegexPattern); + llvm::Regex IncludeRegex(tooling::getCppIncludeRegex()); SmallVector Matches; SmallVector IncludesInBlock; @@ -2804,7 +2787,7 @@ bool MergeWithNextLine = Trimmed.endswith("\\"); if (!FormattingOff && !MergeWithNextLine) { if (IncludeRegex.match(Line, &Matches)) { - StringRef IncludeName = getIncludeNameFromMatches(Matches); + StringRef IncludeName = tooling::getIncludeNameFromMatches(Matches); // This addresses https://github.com/llvm/llvm-project/issues/38995 bool WithSemicolon = false; if (!IncludeName.startswith("\"") && !IncludeName.startswith("<") && @@ -3087,20 +3070,15 @@ inline bool isHeaderInsertion(const tooling::Replacement &Replace) { return Replace.getOffset() == UINT_MAX && Replace.getLength() == 0 && - llvm::Regex(CppIncludeRegexPattern) - .match(Replace.getReplacementText()); + tooling::getCppIncludeRegex().match(Replace.getReplacementText()); } inline bool isHeaderDeletion(const tooling::Replacement &Replace) { return Replace.getOffset() == UINT_MAX && Replace.getLength() == 1; } -inline StringRef trimInclude(StringRef IncludeName) { - return IncludeName.trim("\"<>;"); -} - // FIXME: insert empty lines between newly created blocks. -tooling::Replacements +static tooling::Replacements fixCppIncludeInsertions(StringRef Code, const tooling::Replacements &Replaces, const FormatStyle &Style) { if (!Style.isCpp()) @@ -3132,7 +3110,7 @@ for (const auto &Header : HeadersToDelete) { tooling::Replacements Replaces = - Includes.remove(trimInclude(Header), Header.startswith("<")); + Includes.remove(tooling::trimInclude(Header), Header.startswith("<")); for (const auto &R : Replaces) { auto Err = Result.add(R); if (Err) { @@ -3144,7 +3122,7 @@ } } - llvm::Regex IncludeRegex = llvm::Regex(CppIncludeRegexPattern); + llvm::Regex IncludeRegex = tooling::getCppIncludeRegex(); llvm::SmallVector Matches; for (const auto &R : HeaderInsertions) { auto IncludeDirective = R.getReplacementText(); @@ -3152,9 +3130,9 @@ assert(Matched && "Header insertion replacement must have replacement text " "'#include ...'"); (void)Matched; - StringRef IncludeName = getIncludeNameFromMatches(Matches); - auto Replace = - Includes.insert(trimInclude(IncludeName), IncludeName.startswith("<")); + StringRef IncludeName = tooling::getIncludeNameFromMatches(Matches); + auto Replace = Includes.insert(tooling::trimInclude(IncludeName), + IncludeName.startswith("<")); if (Replace) { auto Err = Result.add(*Replace); if (Err) { diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -169,24 +169,6 @@ }); } -inline StringRef trimInclude(StringRef IncludeName) { - return IncludeName.trim("\"<>;"); -} - -const char IncludeRegexPattern[] = - R"(^[\t\ ]*[@#][\t\ ]*(import|include)([^"]*("[^"]+")|[^<]*(<[^>]+>)|[\t\ ]*([^;]+;)))"; - -// Returns the last match group in the above regex (IncludeRegexPattern) that -// is not empty. -StringRef getIncludeNameFromMatches(const SmallVectorImpl &Matches) { - for (int i = Matches.size() - 1; i > 0; i--) { - if (!Matches[i].empty()) - return Matches[i]; - } - llvm_unreachable("No non-empty match group found in list of matches"); - return StringRef(); -} - // The filename of Path excluding extension. // Used to match implementation with headers, this differs from sys::path::stem: // - in names with multiple dots (foo.cu.cc) it terminates at the *first* @@ -285,8 +267,7 @@ MaxInsertOffset(MinInsertOffset + getMaxHeaderInsertionOffset( FileName, Code.drop_front(MinInsertOffset), Style)), - Categories(Style, FileName), - IncludeRegex(llvm::Regex(IncludeRegexPattern)) { + Categories(Style, FileName), IncludeRegex(getCppIncludeRegex()) { // Add 0 for main header and INT_MAX for headers that are not in any // category. Priorities = {0, INT_MAX}; @@ -301,7 +282,7 @@ for (auto Line : Lines) { NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1); if (IncludeRegex.match(Line, &Matches)) { - StringRef IncludeName = getIncludeNameFromMatches(Matches); + StringRef IncludeName = tooling::getIncludeNameFromMatches(Matches); // If this is the last line without trailing newline, we need to make // sure we don't delete across the file boundary. addExistingInclude( @@ -415,5 +396,25 @@ return Result; } +llvm::Regex getCppIncludeRegex() { + static const char CppIncludeRegexPattern[] = + R"(^[\t\ ]*[@#][\t\ ]*(import|include)([^"]*("[^"]+")|[^<]*(<[^>]+>)|[\t\ ]*([^;]+;)))"; + return llvm::Regex(CppIncludeRegexPattern); +} + +llvm::StringRef getIncludeNameFromMatches( + const llvm::SmallVectorImpl &Matches) { + for (auto Match : llvm::reverse(Matches)) { + if (!Match.empty()) + return Match; + } + llvm_unreachable("No non-empty match group found in list of matches"); + return llvm::StringRef(); +} + +llvm::StringRef trimInclude(llvm::StringRef IncludeName) { + return IncludeName.trim("\"<>;"); +} + } // namespace tooling } // namespace clang