diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 03b6e0c9ef7440..b8da2c23b55ac0 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1962,7 +1962,7 @@ void UnwrappedLineParser::parseIfThenElse() { if (FormatTok->Tok.is(tok::l_paren)) parseParens(); // handle [[likely]] / [[unlikely]] - if (FormatTok->is(tok::l_square)) + if (FormatTok->is(tok::l_square) && tryToParseSimpleAttribute()) parseSquare(); bool NeedsUnwrappedLine = false; if (FormatTok->Tok.is(tok::l_brace)) { @@ -1981,7 +1981,7 @@ void UnwrappedLineParser::parseIfThenElse() { if (FormatTok->Tok.is(tok::kw_else)) { nextToken(); // handle [[likely]] / [[unlikely]] - if (FormatTok->is(tok::l_square)) + if (FormatTok->Tok.is(tok::l_square) && tryToParseSimpleAttribute()) parseSquare(); if (FormatTok->Tok.is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); @@ -2343,6 +2343,51 @@ bool UnwrappedLineParser::parseEnum() { // "} n, m;" will end up in one unwrapped line. } +namespace { +// A class used to set and restore the Token position when peeking +// ahead in the token source. +class ScopedTokenPosition { + unsigned StoredPosition; + FormatTokenSource *Tokens; + +public: + ScopedTokenPosition(FormatTokenSource *Tokens) : Tokens(Tokens) { + assert(Tokens && "Tokens expected to not be null"); + StoredPosition = Tokens->getPosition(); + } + + ~ScopedTokenPosition() { Tokens->setPosition(StoredPosition); } +}; +} // namespace + +// Look to see if we have [[ by looking ahead, if +// its not then rewind to the original position. +bool UnwrappedLineParser::tryToParseSimpleAttribute() { + ScopedTokenPosition AutoPosition(Tokens); + FormatToken *Tok = Tokens->getNextToken(); + // We already read the first [ check for the second. + if (Tok && !Tok->is(tok::l_square)) { + return false; + } + // Double check that the attribute is just something + // fairly simple. + while (Tok) { + if (Tok->is(tok::r_square)) { + break; + } + Tok = Tokens->getNextToken(); + } + Tok = Tokens->getNextToken(); + if (Tok && !Tok->is(tok::r_square)) { + return false; + } + Tok = Tokens->getNextToken(); + if (Tok && Tok->is(tok::semi)) { + return false; + } + return true; +} + void UnwrappedLineParser::parseJavaEnumBody() { // Determine whether the enum is simple, i.e. does not have a semicolon or // constants with class bodies. Simple enums can be formatted like braced diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 8d4118ab6dc7de..8b3aa4c84edbad 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -134,6 +134,7 @@ class UnwrappedLineParser { bool tryToParseLambdaIntroducer(); bool tryToParsePropertyAccessor(); void tryToParseJSFunction(); + bool tryToParseSimpleAttribute(); void addUnwrappedLine(); bool eof() const; // LevelDifference is the difference of levels after and before the current diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index a3b70bfd282457..eea0b364d97c59 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -16513,6 +16513,11 @@ TEST_F(FormatTest, LikelyUnlikely) { " return 42;\n" "}\n", Style); + + verifyFormat("if (argc > 5) [[gnu::unused]] {\n" + " return 29;\n" + "}", + Style); } TEST_F(FormatTest, LLVMDefaultStyle) { diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index d73d090a8ba37b..28d33dcdaa541f 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -1434,6 +1434,25 @@ TEST_F(FormatTestObjC, BreakLineBeforeNestedBlockParam) { " }]"); } +TEST_F(FormatTestObjC, IfNotUnlikely) { + Style = getGoogleStyle(FormatStyle::LK_ObjC); + + verifyFormat("if (argc < 5) [obj func:arg];"); + verifyFormat("if (argc < 5) [[obj1 method1:arg1] method2:arg2];"); + verifyFormat("if (argc < 5) [[foo bar] baz:i[0]];"); + verifyFormat("if (argc < 5) [[foo bar] baz:i[0]][1];"); + + verifyFormat("if (argc < 5)\n" + " [obj func:arg];\n" + "else\n" + " [obj func:arg2];"); + + verifyFormat("if (argc < 5) [[unlikely]]\n" + " [obj func:arg];\n" + "else [[likely]]\n" + " [obj func:arg2];"); +} + } // end namespace } // end namespace format } // end namespace clang