mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-30 21:56:43 +00:00
clang-format: Extend #include sorting functionality
Recognize the main module header as well as different #include categories. This should now mimic the behavior of llvm/utils/sort_includes.py as well as clang-tools-extra/clang-tidy/llvm/IncludeOrderCheck.cpp very closely. llvm-svn: 248782
This commit is contained in:
parent
98b3ee50ff
commit
85c472dccd
@ -259,6 +259,21 @@ struct FormatStyle {
|
||||
/// For example: BOOST_FOREACH.
|
||||
std::vector<std::string> ForEachMacros;
|
||||
|
||||
/// \brief Regular expressions denoting the different #include categories used
|
||||
/// for ordering #includes.
|
||||
///
|
||||
/// These regular expressions are matched against the filename of an include
|
||||
/// (including the <> or "") in order. The value belonging to the first
|
||||
/// matching regular expression is assigned and #includes are sorted first
|
||||
/// according to increasing category number and then alphabetically within
|
||||
/// each category.
|
||||
///
|
||||
/// If none of the regular expressions match, UINT_MAX is assigned as
|
||||
/// category. The main header for a source file automatically gets category 0,
|
||||
/// so that it is kept at the beginning of the #includes
|
||||
/// (http://llvm.org/docs/CodingStandards.html#include-style).
|
||||
std::vector<std::pair<std::string, unsigned>> IncludeCategories;
|
||||
|
||||
/// \brief Indent case labels one level from the switch statement.
|
||||
///
|
||||
/// When \c false, use the same indentation level as for the switch statement.
|
||||
|
@ -13,6 +13,7 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Format/Format.h"
|
||||
#include "ContinuationIndenter.h"
|
||||
#include "TokenAnnotator.h"
|
||||
#include "UnwrappedLineFormatter.h"
|
||||
@ -21,7 +22,6 @@
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
@ -375,6 +375,9 @@ FormatStyle getLLVMStyle() {
|
||||
LLVMStyle.ForEachMacros.push_back("foreach");
|
||||
LLVMStyle.ForEachMacros.push_back("Q_FOREACH");
|
||||
LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH");
|
||||
LLVMStyle.IncludeCategories = {{"^\"(llvm|llvm-c|clang|clang-c)/", 2},
|
||||
{"^(<|\"(gtest|isl|json)/)", 3},
|
||||
{".*", 1}};
|
||||
LLVMStyle.IndentCaseLabels = false;
|
||||
LLVMStyle.IndentWrappedFunctionNames = false;
|
||||
LLVMStyle.IndentWidth = 2;
|
||||
@ -423,6 +426,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
|
||||
GoogleStyle.AlwaysBreakTemplateDeclarations = true;
|
||||
GoogleStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = true;
|
||||
GoogleStyle.DerivePointerAlignment = true;
|
||||
GoogleStyle.IncludeCategories = {{"^<.*\\.h>", 1}, {"^<.*", 2}, {".*", 3}};
|
||||
GoogleStyle.IndentCaseLabels = true;
|
||||
GoogleStyle.KeepEmptyLinesAtTheStartOfBlocks = false;
|
||||
GoogleStyle.ObjCSpaceAfterProperty = false;
|
||||
@ -1575,7 +1579,7 @@ struct IncludeDirective {
|
||||
StringRef Filename;
|
||||
StringRef Text;
|
||||
unsigned Offset;
|
||||
bool IsAngled;
|
||||
unsigned Category;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
@ -1605,7 +1609,8 @@ static void sortIncludes(const FormatStyle &Style,
|
||||
for (unsigned i = 0, e = Includes.size(); i != e; ++i)
|
||||
Indices.push_back(i);
|
||||
std::sort(Indices.begin(), Indices.end(), [&](unsigned LHSI, unsigned RHSI) {
|
||||
return Includes[LHSI].Filename < Includes[RHSI].Filename;
|
||||
return std::tie(Includes[LHSI].Category, Includes[LHSI].Filename) <
|
||||
std::tie(Includes[RHSI].Category, Includes[RHSI].Filename);
|
||||
});
|
||||
|
||||
// If the #includes are out of order, we generate a single replacement fixing
|
||||
@ -1642,22 +1647,49 @@ tooling::Replacements sortIncludes(const FormatStyle &Style, StringRef Code,
|
||||
tooling::Replacements Replaces;
|
||||
unsigned Prev = 0;
|
||||
unsigned SearchFrom = 0;
|
||||
llvm::Regex IncludeRegex(R"(^[\t\ ]*#[\t\ ]*include[^"<]*["<]([^">]*)([">]))");
|
||||
llvm::Regex IncludeRegex(
|
||||
R"(^[\t\ ]*#[\t\ ]*include[^"<]*(["<][^">]*[">]))");
|
||||
SmallVector<StringRef, 4> Matches;
|
||||
SmallVector<IncludeDirective, 16> IncludesInBlock;
|
||||
|
||||
// In compiled files, consider the first #include to be the main #include of
|
||||
// the file if it is not a system #include. This ensures that the header
|
||||
// doesn't have hidden dependencies
|
||||
// (http://llvm.org/docs/CodingStandards.html#include-style).
|
||||
//
|
||||
// FIXME: Do some sanity checking, e.g. edit distance of the base name, to fix
|
||||
// cases where the first #include is unlikely to be the main header.
|
||||
bool LookForMainHeader = FileName.endswith(".c") ||
|
||||
FileName.endswith(".cc") ||
|
||||
FileName.endswith(".cpp")||
|
||||
FileName.endswith(".c++")||
|
||||
FileName.endswith(".cxx");
|
||||
|
||||
// Create pre-compiled regular expressions for the #include categories.
|
||||
SmallVector<llvm::Regex, 4> CategoryRegexs;
|
||||
for (const auto &IncludeBlock : Style.IncludeCategories)
|
||||
CategoryRegexs.emplace_back(IncludeBlock.first);
|
||||
|
||||
for (;;) {
|
||||
auto Pos = Code.find('\n', SearchFrom);
|
||||
StringRef Line =
|
||||
Code.substr(Prev, (Pos != StringRef::npos ? Pos : Code.size()) - Prev);
|
||||
if (!Line.endswith("\\")) {
|
||||
if (IncludeRegex.match(Line, &Matches)) {
|
||||
bool IsAngled = Matches[2] == ">";
|
||||
if (!IncludesInBlock.empty() &&
|
||||
IsAngled != IncludesInBlock.back().IsAngled) {
|
||||
sortIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces);
|
||||
IncludesInBlock.clear();
|
||||
unsigned Category;
|
||||
if (LookForMainHeader && !Matches[1].startswith("<")) {
|
||||
Category = 0;
|
||||
} else {
|
||||
Category = UINT_MAX;
|
||||
for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) {
|
||||
if (CategoryRegexs[i].match(Matches[1])) {
|
||||
Category = Style.IncludeCategories[i].second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
IncludesInBlock.push_back({Matches[1], Line, Prev, Matches[2] == ">"});
|
||||
LookForMainHeader = false;
|
||||
IncludesInBlock.push_back({Matches[1], Line, Prev, Category});
|
||||
} else if (!IncludesInBlock.empty()) {
|
||||
sortIncludes(Style, IncludesInBlock, Ranges, FileName, Replaces);
|
||||
IncludesInBlock.clear();
|
||||
|
@ -73,7 +73,7 @@ FallbackStyle("fallback-style",
|
||||
cl::init("LLVM"), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<std::string>
|
||||
AssumeFilename("assume-filename",
|
||||
AssumeFileName("assume-filename",
|
||||
cl::desc("When reading from stdin, clang-format assumes this\n"
|
||||
"filename to look for a style config file (with\n"
|
||||
"-style=file) and to determine the language."),
|
||||
@ -239,13 +239,13 @@ static bool format(StringRef FileName) {
|
||||
std::vector<tooling::Range> Ranges;
|
||||
if (fillRanges(Code.get(), Ranges))
|
||||
return true;
|
||||
FormatStyle FormatStyle = getStyle(
|
||||
Style, (FileName == "-") ? AssumeFilename : FileName, FallbackStyle);
|
||||
StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
|
||||
FormatStyle FormatStyle = getStyle(Style, AssumedFileName, FallbackStyle);
|
||||
Replacements Replaces;
|
||||
std::string ChangedCode;
|
||||
if (SortIncludes) {
|
||||
Replaces =
|
||||
sortIncludes(FormatStyle, Code->getBuffer(), Ranges, FileName);
|
||||
sortIncludes(FormatStyle, Code->getBuffer(), Ranges, AssumedFileName);
|
||||
ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
|
||||
for (const auto &R : Replaces)
|
||||
Ranges.push_back({R.getOffset(), R.getLength()});
|
||||
@ -324,7 +324,7 @@ int main(int argc, const char **argv) {
|
||||
if (DumpConfig) {
|
||||
std::string Config =
|
||||
clang::format::configurationAsText(clang::format::getStyle(
|
||||
Style, FileNames.empty() ? AssumeFilename : FileNames[0],
|
||||
Style, FileNames.empty() ? AssumeFileName : FileNames[0],
|
||||
FallbackStyle));
|
||||
llvm::outs() << Config << "\n";
|
||||
return 0;
|
||||
|
@ -20,13 +20,15 @@ namespace {
|
||||
|
||||
class SortIncludesTest : public ::testing::Test {
|
||||
protected:
|
||||
std::string sort(llvm::StringRef Code) {
|
||||
std::string sort(llvm::StringRef Code, StringRef FileName = "input.cpp") {
|
||||
std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
|
||||
std::string Sorted = applyAllReplacements(
|
||||
Code, sortIncludes(getLLVMStyle(), Code, Ranges, "input.cpp"));
|
||||
return applyAllReplacements(
|
||||
Sorted, reformat(getLLVMStyle(), Sorted, Ranges, "input.cpp"));
|
||||
std::string Sorted =
|
||||
applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName));
|
||||
return applyAllReplacements(Sorted,
|
||||
reformat(Style, Sorted, Ranges, FileName));
|
||||
}
|
||||
|
||||
FormatStyle Style = getLLVMStyle();
|
||||
};
|
||||
|
||||
TEST_F(SortIncludesTest, BasicSorting) {
|
||||
@ -76,13 +78,23 @@ TEST_F(SortIncludesTest, SortsLocallyInEachBlock) {
|
||||
"#include \"c.h\"\n"
|
||||
"\n"
|
||||
"#include \"b.h\"\n",
|
||||
sort("#include \"c.h\"\n"
|
||||
"#include \"a.h\"\n"
|
||||
sort("#include \"a.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"\n"
|
||||
"#include \"b.h\"\n"));
|
||||
}
|
||||
|
||||
TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) {
|
||||
EXPECT_EQ("#include \"a.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"#include <b.h>\n"
|
||||
"#include <d.h>\n",
|
||||
sort("#include <d.h>\n"
|
||||
"#include <b.h>\n"
|
||||
"#include \"c.h\"\n"
|
||||
"#include \"a.h\"\n"));
|
||||
|
||||
Style = getGoogleStyle(FormatStyle::LK_Cpp);
|
||||
EXPECT_EQ("#include <b.h>\n"
|
||||
"#include <d.h>\n"
|
||||
"#include \"a.h\"\n"
|
||||
@ -103,6 +115,24 @@ TEST_F(SortIncludesTest, HandlesMultilineIncludes) {
|
||||
"#include \"b.h\"\n"));
|
||||
}
|
||||
|
||||
TEST_F(SortIncludesTest, LeavesMainHeaderFirst) {
|
||||
EXPECT_EQ("#include \"llvm/a.h\"\n"
|
||||
"#include \"b.h\"\n"
|
||||
"#include \"c.h\"\n",
|
||||
sort("#include \"llvm/a.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"#include \"b.h\"\n"));
|
||||
|
||||
// Don't do this in headers.
|
||||
EXPECT_EQ("#include \"b.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"#include \"llvm/a.h\"\n",
|
||||
sort("#include \"llvm/a.h\"\n"
|
||||
"#include \"c.h\"\n"
|
||||
"#include \"b.h\"\n",
|
||||
"some_header.h"));
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
} // end namespace format
|
||||
} // end namespace clang
|
||||
|
Loading…
x
Reference in New Issue
Block a user