forked from OSchip/llvm-project
[clang-format] Add possibility to be based on parent directory
This allows the define BasedOnStyle: InheritParentConfig and then clang-format looks into the parent directories for their .clang-format and takes that as a basis. Differential Revision: https://reviews.llvm.org/D93844
This commit is contained in:
parent
08c09bb89f
commit
25f753c51e
|
@ -154,6 +154,15 @@ the configuration (without a prefix: ``Auto``).
|
|||
* ``GNU``
|
||||
A style complying with the `GNU coding standards
|
||||
<https://www.gnu.org/prep/standards/standards.html>`_
|
||||
* ``InheritParentConfig``
|
||||
Not a real style, but allows to use the ``.clang-format`` file from the
|
||||
parent directory (or its parent if there is none). If there is no parent
|
||||
file found it falls back to the ``fallback`` style, and applies the changes
|
||||
to that.
|
||||
|
||||
With this option you can overwrite some parts of your main style for your
|
||||
subdirectories. This is also possible through the command line, e.g.:
|
||||
``--style={BasedOnStyle: InheritParentConfig, ColumnLimit: 20}``
|
||||
|
||||
.. START_FORMAT_STYLE_OPTIONS
|
||||
|
||||
|
|
|
@ -189,6 +189,9 @@ clang-format
|
|||
#include "B/A.h"
|
||||
#include "B/a.h"
|
||||
|
||||
- ``BasedOnStyle: InheritParentConfig`` allows to use the ``.clang-format`` of
|
||||
the parent directories to overwrite only parts of it.
|
||||
|
||||
libclang
|
||||
--------
|
||||
|
||||
|
|
|
@ -52,6 +52,11 @@ std::error_code make_error_code(ParseError e);
|
|||
/// The ``FormatStyle`` is used to configure the formatting to follow
|
||||
/// specific guidelines.
|
||||
struct FormatStyle {
|
||||
// If the BasedOn: was InheritParentConfig and this style needs the file from
|
||||
// the parent directories. It is not part of the actual style for formatting.
|
||||
// Thus the // instead of ///.
|
||||
bool InheritsParentConfig;
|
||||
|
||||
/// The extra indent or outdent of access modifiers, e.g. ``public:``.
|
||||
int AccessModifierOffset;
|
||||
|
||||
|
|
|
@ -908,6 +908,7 @@ static FormatStyle expandPresets(const FormatStyle &Style) {
|
|||
|
||||
FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
|
||||
FormatStyle LLVMStyle;
|
||||
LLVMStyle.InheritsParentConfig = false;
|
||||
LLVMStyle.Language = Language;
|
||||
LLVMStyle.AccessModifierOffset = -2;
|
||||
LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
|
||||
|
@ -1382,6 +1383,8 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language,
|
|||
*Style = getMicrosoftStyle(Language);
|
||||
} else if (Name.equals_lower("none")) {
|
||||
*Style = getNoStyle();
|
||||
} else if (Name.equals_lower("inheritparentconfig")) {
|
||||
Style->InheritsParentConfig = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -2947,21 +2950,36 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
|
|||
if (!getPredefinedStyle(FallbackStyleName, Style.Language, &FallbackStyle))
|
||||
return make_string_error("Invalid fallback style \"" + FallbackStyleName);
|
||||
|
||||
llvm::SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 1>
|
||||
ChildFormatTextToApply;
|
||||
|
||||
if (StyleName.startswith("{")) {
|
||||
// Parse YAML/JSON style from the command line.
|
||||
if (std::error_code ec = parseConfiguration(
|
||||
llvm::MemoryBufferRef(StyleName, "<command-line>"), &Style,
|
||||
AllowUnknownOptions))
|
||||
StringRef Source = "<command-line>";
|
||||
if (std::error_code ec =
|
||||
parseConfiguration(llvm::MemoryBufferRef(StyleName, Source), &Style,
|
||||
AllowUnknownOptions))
|
||||
return make_string_error("Error parsing -style: " + ec.message());
|
||||
return Style;
|
||||
if (Style.InheritsParentConfig)
|
||||
ChildFormatTextToApply.emplace_back(
|
||||
llvm::MemoryBuffer::getMemBuffer(StyleName, Source, false));
|
||||
else
|
||||
return Style;
|
||||
}
|
||||
|
||||
if (!StyleName.equals_lower("file")) {
|
||||
// If the style inherits the parent configuration it is a command line
|
||||
// configuration, which wants to inherit, so we have to skip the check of the
|
||||
// StyleName.
|
||||
if (!Style.InheritsParentConfig && !StyleName.equals_lower("file")) {
|
||||
if (!getPredefinedStyle(StyleName, Style.Language, &Style))
|
||||
return make_string_error("Invalid value for -style");
|
||||
return Style;
|
||||
if (!Style.InheritsParentConfig)
|
||||
return Style;
|
||||
}
|
||||
|
||||
// Reset possible inheritance
|
||||
Style.InheritsParentConfig = false;
|
||||
|
||||
// Look for .clang-format/_clang-format file in the file's parent directories.
|
||||
SmallString<128> UnsuitableConfigFiles;
|
||||
SmallString<128> Path(FileName);
|
||||
|
@ -3008,7 +3026,35 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
|
|||
}
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Using configuration file " << ConfigFile << "\n");
|
||||
return Style;
|
||||
|
||||
if (!Style.InheritsParentConfig) {
|
||||
if (ChildFormatTextToApply.empty())
|
||||
return Style;
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Applying child configurations\n");
|
||||
|
||||
for (const auto& MemBuf : llvm::reverse(ChildFormatTextToApply)){
|
||||
auto Ec = parseConfiguration(*MemBuf, &Style, AllowUnknownOptions);
|
||||
// It was already correctly parsed.
|
||||
assert(!Ec);
|
||||
static_cast<void>(Ec);
|
||||
}
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Inherits parent configuration\n");
|
||||
|
||||
// Reset inheritance of style
|
||||
Style.InheritsParentConfig = false;
|
||||
|
||||
ChildFormatTextToApply.emplace_back(std::move(*Text));
|
||||
|
||||
// Breaking out of the inner loop, since we don't want to parse
|
||||
// .clang-format AND _clang-format, if both exist. Then we continue the
|
||||
// inner loop (parent directories) in search for the parent
|
||||
// configuration.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3016,6 +3062,20 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
|
|||
return make_string_error("Configuration file(s) do(es) not support " +
|
||||
getLanguageName(Style.Language) + ": " +
|
||||
UnsuitableConfigFiles);
|
||||
|
||||
if (!ChildFormatTextToApply.empty()) {
|
||||
assert(ChildFormatTextToApply.size() == 1);
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Applying child configuration on fallback style\n");
|
||||
|
||||
auto Ec = parseConfiguration(*ChildFormatTextToApply.front(),
|
||||
&FallbackStyle, AllowUnknownOptions);
|
||||
// It was already correctly parsed.
|
||||
assert(!Ec);
|
||||
static_cast<void>(Ec);
|
||||
}
|
||||
|
||||
return FallbackStyle;
|
||||
}
|
||||
|
||||
|
|
|
@ -17923,6 +17923,111 @@ TEST(FormatStyle, GetStyleOfFile) {
|
|||
auto StyleTd = getStyle("file", "x.td", "llvm", "", &FS);
|
||||
ASSERT_TRUE((bool)StyleTd);
|
||||
ASSERT_EQ(*StyleTd, getLLVMStyle(FormatStyle::LK_TableGen));
|
||||
|
||||
// Test 9.1: overwriting a file style, when parent no file exists with no
|
||||
// fallback style
|
||||
ASSERT_TRUE(FS.addFile(
|
||||
"/e/sub/.clang-format", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: InheritParentConfig\n"
|
||||
"ColumnLimit: 20")));
|
||||
ASSERT_TRUE(FS.addFile("/e/sub/code.cpp", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("int i;")));
|
||||
auto Style9 = getStyle("file", "/e/sub/code.cpp", "none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, [] {
|
||||
auto Style = getNoStyle();
|
||||
Style.ColumnLimit = 20;
|
||||
return Style;
|
||||
}());
|
||||
|
||||
// Test 9.2: with LLVM fallback style
|
||||
Style9 = getStyle("file", "/e/sub/code.cpp", "LLVM", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, [] {
|
||||
auto Style = getLLVMStyle();
|
||||
Style.ColumnLimit = 20;
|
||||
return Style;
|
||||
}());
|
||||
|
||||
// Test 9.3: with a parent file
|
||||
ASSERT_TRUE(
|
||||
FS.addFile("/e/.clang-format", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google\n"
|
||||
"UseTab: Always")));
|
||||
Style9 = getStyle("file", "/e/sub/code.cpp", "none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, [] {
|
||||
auto Style = getGoogleStyle();
|
||||
Style.ColumnLimit = 20;
|
||||
Style.UseTab = FormatStyle::UT_Always;
|
||||
return Style;
|
||||
}());
|
||||
|
||||
// Test 9.4: propagate more than one level
|
||||
ASSERT_TRUE(FS.addFile("/e/sub/sub/code.cpp", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("int i;")));
|
||||
ASSERT_TRUE(FS.addFile("/e/sub/sub/.clang-format", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer(
|
||||
"BasedOnStyle: InheritParentConfig\n"
|
||||
"WhitespaceSensitiveMacros: ['FOO', 'BAR']")));
|
||||
std::vector<std::string> NonDefaultWhiteSpaceMacros{"FOO", "BAR"};
|
||||
|
||||
const auto SubSubStyle = [&NonDefaultWhiteSpaceMacros] {
|
||||
auto Style = getGoogleStyle();
|
||||
Style.ColumnLimit = 20;
|
||||
Style.UseTab = FormatStyle::UT_Always;
|
||||
Style.WhitespaceSensitiveMacros = NonDefaultWhiteSpaceMacros;
|
||||
return Style;
|
||||
}();
|
||||
|
||||
ASSERT_NE(Style9->WhitespaceSensitiveMacros, NonDefaultWhiteSpaceMacros);
|
||||
Style9 = getStyle("file", "/e/sub/sub/code.cpp", "none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, SubSubStyle);
|
||||
|
||||
// Test 9.5: use InheritParentConfig as style name
|
||||
Style9 =
|
||||
getStyle("inheritparentconfig", "/e/sub/sub/code.cpp", "none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, SubSubStyle);
|
||||
|
||||
// Test 9.6: use command line style with inheritance
|
||||
Style9 = getStyle("{BasedOnStyle: InheritParentConfig}", "/e/sub/code.cpp",
|
||||
"none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, SubSubStyle);
|
||||
|
||||
// Test 9.7: use command line style with inheritance and own config
|
||||
Style9 = getStyle("{BasedOnStyle: InheritParentConfig, "
|
||||
"WhitespaceSensitiveMacros: ['FOO', 'BAR']}",
|
||||
"/e/sub/code.cpp", "none", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, SubSubStyle);
|
||||
|
||||
// Test 9.8: use inheritance from a file without BasedOnStyle
|
||||
ASSERT_TRUE(FS.addFile("/e/withoutbase/.clang-format", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer("ColumnLimit: 123")));
|
||||
ASSERT_TRUE(
|
||||
FS.addFile("/e/withoutbase/sub/.clang-format", 0,
|
||||
llvm::MemoryBuffer::getMemBuffer(
|
||||
"BasedOnStyle: InheritParentConfig\nIndentWidth: 7")));
|
||||
// Make sure we do not use the fallback style
|
||||
Style9 = getStyle("file", "/e/withoutbase/code.cpp", "google", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, [] {
|
||||
auto Style = getLLVMStyle();
|
||||
Style.ColumnLimit = 123;
|
||||
return Style;
|
||||
}());
|
||||
|
||||
Style9 = getStyle("file", "/e/withoutbase/sub/code.cpp", "google", "", &FS);
|
||||
ASSERT_TRUE(static_cast<bool>(Style9));
|
||||
ASSERT_EQ(*Style9, [] {
|
||||
auto Style = getLLVMStyle();
|
||||
Style.ColumnLimit = 123;
|
||||
Style.IndentWidth = 7;
|
||||
return Style;
|
||||
}());
|
||||
}
|
||||
|
||||
TEST_F(ReplacementTest, FormatCodeAfterReplacements) {
|
||||
|
|
Loading…
Reference in New Issue