diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -122,6 +122,30 @@ TYPE(CSharpGenericTypeConstraintComma) \ TYPE(Unknown) +/// Sorted Operators that can follow a C variable. +static const std::vector COperatorsFollowingVar = { + 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}; + /// Determines the semantic type of a syntactic token, e.g. whether "<" is a /// template opener or binary operator. enum TokenType : uint8_t { diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -100,10 +100,38 @@ 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.isAccessSpecifier(false)) { + if (!RootToken.Next) { + return true; + } + auto COperatorMatch = std::lower_bound(COperatorsFollowingVar.begin(), + COperatorsFollowingVar.end(), + RootToken.Next->Tok.getKind()); + if ((COperatorMatch == COperatorsFollowingVar.end() || + *COperatorMatch != RootToken.Next->Tok.getKind()) && + !RootToken.Next->Tok.is(tok::coloncolon)) { + 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. @@ -874,7 +902,7 @@ /// /// If \p NewLine is true, we format the nested block on separate lines, i.e. /// break after the "{", format all lines with correct indentation and the put - /// the closing "}" on yet another new line. + /// the closing "}" on yet another new line. /// /// This enables us to keep the simple structure of the /// \c UnwrappedLineFormatter, where we only have two options for each token: diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2707,14 +2707,28 @@ } void UnwrappedLineParser::parseAccessSpecifier() { + FormatToken *AccessSpecifierCandidate = FormatTok; nextToken(); // Understand Qt's slots. if (FormatTok->isOneOf(Keywords.kw_slots, Keywords.kw_qslots)) nextToken(); + + auto COperatorMatch = + std::lower_bound(COperatorsFollowingVar.begin(), + COperatorsFollowingVar.end(), FormatTok->Tok.getKind()); // 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 ((COperatorMatch == COperatorsFollowingVar.end() || + *COperatorMatch != FormatTok->Tok.getKind()) && + !FormatTok->Tok.is(tok::coloncolon)) { + // 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() { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -3123,6 +3123,46 @@ "label:\n" " signals.baz();\n" "}"); + verifyFormat("private[1];"); + verifyFormat("testArray[public] = 1;"); + verifyFormat("public();"); + verifyFormat("myFunc(public);"); + verifyFormat("std::vector 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) {