Make clang-format cleaner remove redundant commas in list and redundant colon in constructor initializer.

Summary: Make clang-format cleaner remove redundant commas/colons in constructor initializer list.

Reviewers: klimek, djasper

Subscribers: cfe-commits, klimek

Differential Revision: http://reviews.llvm.org/D19804

llvm-svn: 269888
This commit is contained in:
Eric Liu
2016-05-18 08:02:56 +00:00
parent eb10307347
commit ce5e4bc7ac
3 changed files with 179 additions and 4 deletions

View File

@@ -1765,6 +1765,15 @@ public:
checkEmptyNamespace(AnnotatedLines);
for (auto &Line : AnnotatedLines) {
if (Line->Affected) {
cleanupRight(Line->First, tok::comma, tok::comma);
cleanupRight(Line->First, TT_CtorInitializerColon, tok::comma);
cleanupLeft(Line->First, TT_CtorInitializerComma, tok::l_brace);
cleanupLeft(Line->First, TT_CtorInitializerColon, tok::l_brace);
}
}
return generateFixes();
}
@@ -1853,6 +1862,45 @@ private:
return true;
}
// Checks pairs {start, start->next},..., {end->previous, end} and deletes one
// of the token in the pair if the left token has \p LK token kind and the
// right token has \p RK token kind. If \p DeleteLeft is true, the left token
// is deleted on match; otherwise, the right token is deleted.
template <typename LeftKind, typename RightKind>
void cleanupPair(FormatToken *Start, LeftKind LK, RightKind RK,
bool DeleteLeft) {
auto NextNotDeleted = [this](const FormatToken &Tok) -> FormatToken * {
for (auto *Res = Tok.Next; Res; Res = Res->Next)
if (!Res->is(tok::comment) &&
DeletedTokens.find(Res) == DeletedTokens.end())
return Res;
return nullptr;
};
for (auto *Left = Start; Left;) {
auto *Right = NextNotDeleted(*Left);
if (!Right)
break;
if (Left->is(LK) && Right->is(RK)) {
deleteToken(DeleteLeft ? Left : Right);
// If the right token is deleted, we should keep the left token
// unchanged and pair it with the new right token.
if (!DeleteLeft)
continue;
}
Left = Right;
}
}
template <typename LeftKind, typename RightKind>
void cleanupLeft(FormatToken *Start, LeftKind LK, RightKind RK) {
cleanupPair(Start, LK, RK, /*DeleteLeft=*/true);
}
template <typename LeftKind, typename RightKind>
void cleanupRight(FormatToken *Start, LeftKind LK, RightKind RK) {
cleanupPair(Start, LK, RK, /*DeleteLeft=*/false);
}
// Delete the given token.
inline void deleteToken(FormatToken *Tok) {
if (Tok)

View File

@@ -920,6 +920,10 @@ private:
Contexts.back().IsExpression = false;
} else if (Current.is(TT_LambdaArrow) || Current.is(Keywords.kw_assert)) {
Contexts.back().IsExpression = Style.Language == FormatStyle::LK_Java;
} else if (Current.Previous &&
Current.Previous->is(TT_CtorInitializerColon)) {
Contexts.back().IsExpression = true;
Contexts.back().InCtorInitializer = true;
} else if (Current.isOneOf(tok::r_paren, tok::greater, tok::comma)) {
for (FormatToken *Previous = Current.Previous;
Previous && Previous->isOneOf(tok::star, tok::amp);
@@ -927,10 +931,6 @@ private:
Previous->Type = TT_PointerOrReference;
if (Line.MustBeDeclaration && !Contexts.front().InCtorInitializer)
Contexts.back().IsExpression = false;
} else if (Current.Previous &&
Current.Previous->is(TT_CtorInitializerColon)) {
Contexts.back().IsExpression = true;
Contexts.back().InCtorInitializer = true;
} else if (Current.is(tok::kw_new)) {
Contexts.back().CanBeExpression = false;
} else if (Current.isOneOf(tok::semi, tok::exclaim)) {

View File

@@ -113,6 +113,133 @@ TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) {
EXPECT_EQ(Expected, Result);
}
TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
std::string Code = "class A {\nA() : , {} };";
std::string Expected = "class A {\nA() {} };";
std::vector<tooling::Range> Ranges;
Ranges.push_back(tooling::Range(17, 0));
Ranges.push_back(tooling::Range(19, 0));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
Code = "class A {\nA() : x(1), {} };";
Expected = "class A {\nA() : x(1) {} };";
Ranges.clear();
Ranges.push_back(tooling::Range(23, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
Code = "class A {\nA() :,,,,{} };";
Expected = "class A {\nA() {} };";
Ranges.clear();
Ranges.push_back(tooling::Range(15, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
TEST_F(CleanupTest, ListSimpleRedundantComma) {
std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }";
std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }";
std::vector<tooling::Range> Ranges;
Ranges.push_back(tooling::Range(40, 0));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
Code = "int main() { f(1,,2,3,,4);}";
Expected = "int main() { f(1,2,3,4);}";
Ranges.clear();
Ranges.push_back(tooling::Range(17, 0));
Ranges.push_back(tooling::Range(22, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
TEST_F(CleanupTest, CtorInitializationBracesInParens) {
std::string Code = "class A {\nA() : x({1}),, {} };";
std::string Expected = "class A {\nA() : x({1}) {} };";
std::vector<tooling::Range> Ranges;
Ranges.push_back(tooling::Range(24, 0));
Ranges.push_back(tooling::Range(26, 0));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
std::string Code =
"class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
std::string Expected =
"class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
// Set the affected range to be "int x = 0", which does not intercept the
// constructor initialization list.
std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
Code = "class A {\nA() : x(1), {} };";
Expected = "class A {\nA() : x(1), {} };";
// No range. Fixer should do nothing.
Ranges.clear();
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
// FIXME: delete comments too.
TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) {
// Remove redundant commas around comment.
std::string Code = "class A {\nA() : x({1}), /* comment */, {} };";
std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };";
std::vector<tooling::Range> Ranges;
Ranges.push_back(tooling::Range(25, 0));
Ranges.push_back(tooling::Range(40, 0));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
// Remove trailing comma and ignore comment.
Code = "class A {\nA() : x({1}), // comment\n{} };";
Expected = "class A {\nA() : x({1}) // comment\n{} };";
Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
// Remove trailing comma and ignore comment.
Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
Expected = "class A {\nA() : x({1}), // comment\n y(1){} };";
Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
// Remove trailing comma and ignore comment.
Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };";
Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
// Remove trailing comma and ignore comment.
Code = "class A {\nA() : , // comment\n y(1),{} };";
Expected = "class A {\nA() : // comment\n y(1){} };";
Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0));
Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
TEST_F(CleanupTest, CtorInitializerInNamespace) {
std::string Code = "namespace A {\n"
"namespace B {\n" // missing r_brace
"} // namespace A\n\n"
"namespace C {\n"
"class A { A() : x(0),, {} };\n"
"inline namespace E { namespace { } }\n"
"}";
std::string Expected = "namespace A {\n"
"\n\n\nnamespace C {\n"
"class A { A() : x(0) {} };\n \n"
"}";
std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
std::string Result = cleanup(Code, Ranges);
EXPECT_EQ(Expected, Result);
}
} // end namespace
} // end namespace format
} // end namespace clang