Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 54 additions & 38 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2132,48 +2132,70 @@ std::error_code parseConfiguration(llvm::MemoryBufferRef Config,
Input >> Styles;
if (Input.error())
return Input.error();
if (Styles.empty())
return make_error_code(ParseError::Success);

const auto StyleCount = Styles.size();

for (unsigned i = 0; i < Styles.size(); ++i) {
// Ensures that only the first configuration can skip the Language option.
if (Styles[i].Language == FormatStyle::LK_None && i != 0)
// Start from the second style as (only) the first one may be the default.
for (unsigned I = 1; I < StyleCount; ++I) {
const auto Lang = Styles[I].Language;
if (Lang == FormatStyle::LK_None)
return make_error_code(ParseError::Error);
// Ensure that each language is configured at most once.
for (unsigned j = 0; j < i; ++j) {
if (Styles[i].Language == Styles[j].Language) {
for (unsigned J = 0; J < I; ++J) {
if (Lang == Styles[J].Language) {
LLVM_DEBUG(llvm::dbgs()
<< "Duplicate languages in the config file on positions "
<< j << " and " << i << "\n");
<< J << " and " << I << '\n');
return make_error_code(ParseError::Error);
}
}
}
// Look for a suitable configuration starting from the end, so we can
// find the configuration for the specific language first, and the default
// configuration (which can only be at slot 0) after it.
FormatStyle::FormatStyleSet StyleSet;
bool LanguageFound = false;
for (const FormatStyle &Style : llvm::reverse(Styles)) {
const auto Lang = Style.Language;
if (Lang != FormatStyle::LK_None)
StyleSet.Add(Style);
if (Lang == Language ||
// For backward compatibility.
(Lang == FormatStyle::LK_Cpp && Language == FormatStyle::LK_C)) {
LanguageFound = true;
} else if (IsDotHFile && Language == FormatStyle::LK_Cpp &&
(Lang == FormatStyle::LK_C || Lang == FormatStyle::LK_ObjC)) {
Language = Lang;
LanguageFound = true;

int LanguagePos = -1; // Position of the style for Language.
int CppPos = -1; // Position of the style for C++.
int CPos = -1; // Position of the style for C.

// Search Styles for Language and store the positions of C++ and C styles in
// case Language is not found.
for (unsigned I = 0; I < StyleCount; ++I) {
const auto Lang = Styles[I].Language;
if (Lang == Language) {
LanguagePos = I;
break;
}
}
if (!LanguageFound) {
if (Styles.empty() || Styles[0].Language != FormatStyle::LK_None)
if (Lang == FormatStyle::LK_Cpp)
CppPos = I;
else if (Lang == FormatStyle::LK_C)
CPos = I;
}

// If Language is not found, use the default style if there is one. Otherwise,
// use the C style for C++ .h files and for backward compatibility, the C++
// style for .c files.
if (LanguagePos < 0) {
if (Styles[0].Language == FormatStyle::LK_None) // Default style.
LanguagePos = 0;
else if (IsDotHFile && Language == FormatStyle::LK_Cpp)
LanguagePos = CPos;
else if (!IsDotHFile && Language == FormatStyle::LK_C)
LanguagePos = CppPos;
if (LanguagePos < 0)
return make_error_code(ParseError::Unsuitable);
FormatStyle DefaultStyle = Styles[0];
DefaultStyle.Language = Language;
StyleSet.Add(std::move(DefaultStyle));
}
*Style = *StyleSet.Get(Language);

for (const auto &S : llvm::reverse(llvm::drop_begin(Styles)))
Style->StyleSet.Add(S);

*Style = Styles[LanguagePos];

if (LanguagePos == 0) {
if (Style->Language == FormatStyle::LK_None) // Default style.
Style->Language = Language;
Style->StyleSet.Add(*Style);
}

if (Style->InsertTrailingCommas != FormatStyle::TCS_None &&
Style->BinPackArguments) {
// See comment on FormatStyle::TSC_Wrapped.
Expand Down Expand Up @@ -2204,14 +2226,8 @@ FormatStyle::FormatStyleSet::Get(FormatStyle::LanguageKind Language) const {
if (!Styles)
return std::nullopt;
auto It = Styles->find(Language);
if (It == Styles->end()) {
if (Language != FormatStyle::LK_C)
return std::nullopt;
// For backward compatibility.
It = Styles->find(FormatStyle::LK_Cpp);
if (It == Styles->end())
return std::nullopt;
}
if (It == Styles->end())
return std::nullopt;
FormatStyle Style = It->second;
Style.StyleSet = *this;
return Style;
Expand Down
16 changes: 13 additions & 3 deletions clang/unittests/Format/ConfigParseTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,13 @@ TEST(ConfigParseTest, ParsesConfigurationWithLanguages) {
IndentWidth, 56u);
}

TEST(ConfigParseTest, AllowCommentOnlyConfigFile) {
FormatStyle Style = {};
Style.Language = FormatStyle::LK_Cpp;
EXPECT_EQ(parseConfiguration("#Language: C", &Style), ParseError::Success);
EXPECT_EQ(Style.Language, FormatStyle::LK_Cpp);
}

TEST(ConfigParseTest, AllowCppForC) {
FormatStyle Style = {};
Style.Language = FormatStyle::LK_C;
Expand All @@ -1269,7 +1276,7 @@ TEST(ConfigParseTest, AllowCppForC) {
ParseError::Success);
}

TEST(ConfigParseTest, HandleNonCppDotHFile) {
TEST(ConfigParseTest, HandleDotHFile) {
FormatStyle Style = {};
Style.Language = FormatStyle::LK_Cpp;
EXPECT_EQ(parseConfiguration("Language: C", &Style,
Expand All @@ -1280,11 +1287,14 @@ TEST(ConfigParseTest, HandleNonCppDotHFile) {

Style = {};
Style.Language = FormatStyle::LK_Cpp;
EXPECT_EQ(parseConfiguration("Language: ObjC", &Style,
EXPECT_EQ(parseConfiguration("Language: Cpp\n"
"...\n"
"Language: C",
&Style,
/*AllowUnknownOptions=*/false,
/*IsDotHFile=*/true),
ParseError::Success);
EXPECT_EQ(Style.Language, FormatStyle::LK_ObjC);
EXPECT_EQ(Style.Language, FormatStyle::LK_Cpp);
}

TEST(ConfigParseTest, UsesLanguageForBasedOnStyle) {
Expand Down
Loading