Skip to content

Commit d1aed48

Browse files
psigillitomkurdej
authored andcommitted
[clang-format] Handle C variables with name that matches c++ access specifier
Reviewed By: MyDeveloperDay, curdeius, HazardyKnusperkeks Differential Revision: https://reviews.llvm.org/D117416
1 parent 446425f commit d1aed48

File tree

4 files changed

+102
-6
lines changed

4 files changed

+102
-6
lines changed

clang/lib/Format/FormatToken.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,34 @@ namespace format {
123123
TYPE(CSharpGenericTypeConstraintComma) \
124124
TYPE(Unknown)
125125

126+
/// Sorted operators that can follow a C variable.
127+
static const std::vector<clang::tok::TokenKind> COperatorsFollowingVar = [] {
128+
std::vector<clang::tok::TokenKind> ReturnVal = {
129+
tok::l_square, tok::r_square,
130+
tok::l_paren, tok::r_paren,
131+
tok::r_brace, tok::period,
132+
tok::ellipsis, tok::ampamp,
133+
tok::ampequal, tok::star,
134+
tok::starequal, tok::plus,
135+
tok::plusplus, tok::plusequal,
136+
tok::minus, tok::arrow,
137+
tok::minusminus, tok::minusequal,
138+
tok::exclaim, tok::exclaimequal,
139+
tok::slash, tok::slashequal,
140+
tok::percent, tok::percentequal,
141+
tok::less, tok::lessless,
142+
tok::lessequal, tok::lesslessequal,
143+
tok::greater, tok::greatergreater,
144+
tok::greaterequal, tok::greatergreaterequal,
145+
tok::caret, tok::caretequal,
146+
tok::pipe, tok::pipepipe,
147+
tok::pipeequal, tok::question,
148+
tok::semi, tok::equal,
149+
tok::equalequal, tok::comma};
150+
assert(std::is_sorted(ReturnVal.begin(), ReturnVal.end()));
151+
return ReturnVal;
152+
}();
153+
126154
/// Determines the semantic type of a syntactic token, e.g. whether "<" is a
127155
/// template opener or binary operator.
128156
enum TokenType : uint8_t {

clang/lib/Format/UnwrappedLineFormatter.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,27 @@ class LevelIndentTracker {
100100
if (Style.Language == FormatStyle::LK_Java || Style.isJavaScript() ||
101101
Style.isCSharp())
102102
return 0;
103-
if (RootToken.isAccessSpecifier(false) ||
104-
RootToken.isObjCAccessSpecifier() ||
105-
(RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
106-
RootToken.Next && RootToken.Next->is(tok::colon))) {
103+
104+
auto IsAccessModifier = [this, &RootToken]() {
105+
if (RootToken.isAccessSpecifier(Style.isCpp()))
106+
return true;
107+
else if (RootToken.isObjCAccessSpecifier())
108+
return true;
109+
// Handle Qt signals.
110+
else if ((RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
111+
RootToken.Next && RootToken.Next->is(tok::colon)))
112+
return true;
113+
else if (RootToken.Next &&
114+
RootToken.Next->isOneOf(Keywords.kw_slots, Keywords.kw_qslots) &&
115+
RootToken.Next->Next && RootToken.Next->Next->is(tok::colon))
116+
return true;
117+
// Handle malformed access specifier e.g. 'private' without trailing ':'.
118+
else if (!RootToken.Next && RootToken.isAccessSpecifier(false))
119+
return true;
120+
return false;
121+
};
122+
123+
if (IsAccessModifier()) {
107124
// The AccessModifierOffset may be overridden by IndentAccessModifiers,
108125
// in which case we take a negative value of the IndentWidth to simulate
109126
// the upper indent level.

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2708,14 +2708,25 @@ void UnwrappedLineParser::parseSwitch() {
27082708
}
27092709

27102710
void UnwrappedLineParser::parseAccessSpecifier() {
2711+
FormatToken *AccessSpecifierCandidate = FormatTok;
27112712
nextToken();
27122713
// Understand Qt's slots.
27132714
if (FormatTok->isOneOf(Keywords.kw_slots, Keywords.kw_qslots))
27142715
nextToken();
27152716
// Otherwise, we don't know what it is, and we'd better keep the next token.
2716-
if (FormatTok->Tok.is(tok::colon))
2717+
if (FormatTok->Tok.is(tok::colon)) {
27172718
nextToken();
2718-
addUnwrappedLine();
2719+
addUnwrappedLine();
2720+
} else if (!FormatTok->Tok.is(tok::coloncolon) &&
2721+
!std::binary_search(COperatorsFollowingVar.begin(),
2722+
COperatorsFollowingVar.end(),
2723+
FormatTok->Tok.getKind())) {
2724+
// Not a variable name nor namespace name.
2725+
addUnwrappedLine();
2726+
} else if (AccessSpecifierCandidate) {
2727+
// Consider the access specifier to be a C identifier.
2728+
AccessSpecifierCandidate->Tok.setKind(tok::identifier);
2729+
}
27192730
}
27202731

27212732
void UnwrappedLineParser::parseConcept() {

clang/unittests/Format/FormatTest.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,6 +3123,46 @@ TEST_F(FormatTest, UnderstandsAccessSpecifiers) {
31233123
"label:\n"
31243124
" signals.baz();\n"
31253125
"}");
3126+
verifyFormat("private[1];");
3127+
verifyFormat("testArray[public] = 1;");
3128+
verifyFormat("public();");
3129+
verifyFormat("myFunc(public);");
3130+
verifyFormat("std::vector<int> testVec = {private};");
3131+
verifyFormat("private.p = 1;");
3132+
verifyFormat("void function(private...){};");
3133+
verifyFormat("if (private && public)\n");
3134+
verifyFormat("private &= true;");
3135+
verifyFormat("int x = private * public;");
3136+
verifyFormat("public *= private;");
3137+
verifyFormat("int x = public + private;");
3138+
verifyFormat("private++;");
3139+
verifyFormat("++private;");
3140+
verifyFormat("public += private;");
3141+
verifyFormat("public = public - private;");
3142+
verifyFormat("public->foo();");
3143+
verifyFormat("private--;");
3144+
verifyFormat("--private;");
3145+
verifyFormat("public -= 1;");
3146+
verifyFormat("if (!private && !public)\n");
3147+
verifyFormat("public != private;");
3148+
verifyFormat("int x = public / private;");
3149+
verifyFormat("public /= 2;");
3150+
verifyFormat("public = public % 2;");
3151+
verifyFormat("public %= 2;");
3152+
verifyFormat("if (public < private)\n");
3153+
verifyFormat("public << private;");
3154+
verifyFormat("public <<= private;");
3155+
verifyFormat("if (public > private)\n");
3156+
verifyFormat("public >> private;");
3157+
verifyFormat("public >>= private;");
3158+
verifyFormat("public ^ private;");
3159+
verifyFormat("public ^= private;");
3160+
verifyFormat("public | private;");
3161+
verifyFormat("public |= private;");
3162+
verifyFormat("auto x = private ? 1 : 2;");
3163+
verifyFormat("if (public == private)\n");
3164+
verifyFormat("void foo(public, private)");
3165+
verifyFormat("public::foo();");
31263166
}
31273167

31283168
TEST_F(FormatTest, SeparatesLogicalBlocks) {

0 commit comments

Comments
 (0)