FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
FormatStyle LLVMStyle;
+ LLVMStyle.InheritsParentConfig = false;
LLVMStyle.Language = Language;
LLVMStyle.AccessModifierOffset = -2;
LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
*Style = getMicrosoftStyle(Language);
} else if (Name.equals_lower("none")) {
*Style = getNoStyle();
+ } else if (Name.equals_lower("inheritparentconfig")) {
+ Style->InheritsParentConfig = true;
} else {
return false;
}
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);
}
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;
}
}
}
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;
}
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) {