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 @@ -121,6 +121,30 @@ TYPE(CSharpGenericTypeConstraintComma) \ TYPE(Unknown) +/// Operators that can follow a C variable. +static const std::set C_OperatorsFollowingVar = { + 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,27 @@ 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; + // Handle malformed access specifier i.e. 'private' without trailing ':' + else if ((RootToken.isAccessSpecifier(false) && + (!RootToken.Next || + (!C_OperatorsFollowingVar.count( + 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. 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 @@ -2494,14 +2494,22 @@ } void UnwrappedLineParser::parseAccessSpecifier() { + auto *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(); + // is not a variable name or namespacename + } else if (!C_OperatorsFollowingVar.count(FormatTok->Tok.getKind()) && + !FormatTok->Tok.is(tok::coloncolon)) + addUnwrappedLine(); + // consider the accessSpecifier to be a C identifier + else if (AccessSpecifierCandidate) + 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 @@ -3030,6 +3030,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) {