Skip to content

Commit

Permalink
[clang-format] Handle C variables with name that matches c++ access s…
Browse files Browse the repository at this point in the history
…pecifier

Reviewed By: MyDeveloperDay, curdeius, HazardyKnusperkeks

Differential Revision: https://reviews.llvm.org/D117416
  • Loading branch information
psigillito authored and mkurdej committed Jan 30, 2022
1 parent 446425f commit d1aed48
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 6 deletions.
28 changes: 28 additions & 0 deletions clang/lib/Format/FormatToken.h
Expand Up @@ -123,6 +123,34 @@ namespace format {
TYPE(CSharpGenericTypeConstraintComma) \
TYPE(Unknown)

/// Sorted operators that can follow a C variable.
static const std::vector<clang::tok::TokenKind> COperatorsFollowingVar = [] {
std::vector<clang::tok::TokenKind> ReturnVal = {
tok::l_square, tok::r_square,
tok::l_paren, tok::r_paren,
tok::r_brace, tok::period,
tok::ellipsis, tok::ampamp,
tok::ampequal, tok::star,
tok::starequal, tok::plus,
tok::plusplus, tok::plusequal,
tok::minus, tok::arrow,
tok::minusminus, tok::minusequal,
tok::exclaim, tok::exclaimequal,
tok::slash, tok::slashequal,
tok::percent, tok::percentequal,
tok::less, tok::lessless,
tok::lessequal, tok::lesslessequal,
tok::greater, tok::greatergreater,
tok::greaterequal, tok::greatergreaterequal,
tok::caret, tok::caretequal,
tok::pipe, tok::pipepipe,
tok::pipeequal, tok::question,
tok::semi, tok::equal,
tok::equalequal, tok::comma};
assert(std::is_sorted(ReturnVal.begin(), ReturnVal.end()));
return ReturnVal;
}();

/// Determines the semantic type of a syntactic token, e.g. whether "<" is a
/// template opener or binary operator.
enum TokenType : uint8_t {
Expand Down
25 changes: 21 additions & 4 deletions clang/lib/Format/UnwrappedLineFormatter.cpp
Expand Up @@ -100,10 +100,27 @@ class LevelIndentTracker {
if (Style.Language == FormatStyle::LK_Java || Style.isJavaScript() ||
Style.isCSharp())
return 0;
if (RootToken.isAccessSpecifier(false) ||
RootToken.isObjCAccessSpecifier() ||
(RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
RootToken.Next && RootToken.Next->is(tok::colon))) {

auto IsAccessModifier = [this, &RootToken]() {
if (RootToken.isAccessSpecifier(Style.isCpp()))
return true;
else if (RootToken.isObjCAccessSpecifier())
return true;
// Handle Qt signals.
else if ((RootToken.isOneOf(Keywords.kw_signals, Keywords.kw_qsignals) &&
RootToken.Next && RootToken.Next->is(tok::colon)))
return true;
else if (RootToken.Next &&
RootToken.Next->isOneOf(Keywords.kw_slots, Keywords.kw_qslots) &&
RootToken.Next->Next && RootToken.Next->Next->is(tok::colon))
return true;
// Handle malformed access specifier e.g. 'private' without trailing ':'.
else if (!RootToken.Next && RootToken.isAccessSpecifier(false))
return true;
return false;
};

if (IsAccessModifier()) {
// The AccessModifierOffset may be overridden by IndentAccessModifiers,
// in which case we take a negative value of the IndentWidth to simulate
// the upper indent level.
Expand Down
15 changes: 13 additions & 2 deletions clang/lib/Format/UnwrappedLineParser.cpp
Expand Up @@ -2708,14 +2708,25 @@ void UnwrappedLineParser::parseSwitch() {
}

void UnwrappedLineParser::parseAccessSpecifier() {
FormatToken *AccessSpecifierCandidate = FormatTok;
nextToken();
// Understand Qt's slots.
if (FormatTok->isOneOf(Keywords.kw_slots, Keywords.kw_qslots))
nextToken();
// Otherwise, we don't know what it is, and we'd better keep the next token.
if (FormatTok->Tok.is(tok::colon))
if (FormatTok->Tok.is(tok::colon)) {
nextToken();
addUnwrappedLine();
addUnwrappedLine();
} else if (!FormatTok->Tok.is(tok::coloncolon) &&
!std::binary_search(COperatorsFollowingVar.begin(),
COperatorsFollowingVar.end(),
FormatTok->Tok.getKind())) {
// Not a variable name nor namespace name.
addUnwrappedLine();
} else if (AccessSpecifierCandidate) {
// Consider the access specifier to be a C identifier.
AccessSpecifierCandidate->Tok.setKind(tok::identifier);
}
}

void UnwrappedLineParser::parseConcept() {
Expand Down
40 changes: 40 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -3123,6 +3123,46 @@ TEST_F(FormatTest, UnderstandsAccessSpecifiers) {
"label:\n"
" signals.baz();\n"
"}");
verifyFormat("private[1];");
verifyFormat("testArray[public] = 1;");
verifyFormat("public();");
verifyFormat("myFunc(public);");
verifyFormat("std::vector<int> testVec = {private};");
verifyFormat("private.p = 1;");
verifyFormat("void function(private...){};");
verifyFormat("if (private && public)\n");
verifyFormat("private &= true;");
verifyFormat("int x = private * public;");
verifyFormat("public *= private;");
verifyFormat("int x = public + private;");
verifyFormat("private++;");
verifyFormat("++private;");
verifyFormat("public += private;");
verifyFormat("public = public - private;");
verifyFormat("public->foo();");
verifyFormat("private--;");
verifyFormat("--private;");
verifyFormat("public -= 1;");
verifyFormat("if (!private && !public)\n");
verifyFormat("public != private;");
verifyFormat("int x = public / private;");
verifyFormat("public /= 2;");
verifyFormat("public = public % 2;");
verifyFormat("public %= 2;");
verifyFormat("if (public < private)\n");
verifyFormat("public << private;");
verifyFormat("public <<= private;");
verifyFormat("if (public > private)\n");
verifyFormat("public >> private;");
verifyFormat("public >>= private;");
verifyFormat("public ^ private;");
verifyFormat("public ^= private;");
verifyFormat("public | private;");
verifyFormat("public |= private;");
verifyFormat("auto x = private ? 1 : 2;");
verifyFormat("if (public == private)\n");
verifyFormat("void foo(public, private)");
verifyFormat("public::foo();");
}

TEST_F(FormatTest, SeparatesLogicalBlocks) {
Expand Down

0 comments on commit d1aed48

Please sign in to comment.