Index: clang/lib/Format/TokenAnnotator.h =================================================================== --- clang/lib/Format/TokenAnnotator.h +++ clang/lib/Format/TokenAnnotator.h @@ -170,17 +170,27 @@ /// \c UnwrappedLine. class TokenAnnotator { public: - TokenAnnotator(const FormatStyle &Style, const AdditionalKeywords &Keywords) - : Style(Style), Keywords(Keywords) {} + TokenAnnotator(const FormatStyle &Style, const AdditionalKeywords &Keywords); /// Adapts the indent levels of comment lines to the indent of the /// subsequent line. // FIXME: Can/should this be done in the UnwrappedLineParser? void setCommentLineLevels(SmallVectorImpl &Lines) const; - void annotate(AnnotatedLine &Line) const; + void annotate(AnnotatedLine &Line); void calculateFormattingInformation(AnnotatedLine &Line) const; + enum class ScopeType : std::int8_t { + // Not contained within scope block. + None, + // Contained in class declaration/definition. + Class, + // Contained within function definition. + Function, + // Contained within other scope block (loop, if/else, etc). + Other, + }; + private: /// Calculate the penalty for splitting before \c Tok. unsigned splitPenalty(const AnnotatedLine &Line, const FormatToken &Tok, @@ -220,6 +230,8 @@ const FormatStyle &Style; const AdditionalKeywords &Keywords; + + SmallVector Scopes; }; } // end namespace format Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -111,9 +111,10 @@ class AnnotatingParser { public: AnnotatingParser(const FormatStyle &Style, AnnotatedLine &Line, - const AdditionalKeywords &Keywords) - : Style(Style), Line(Line), CurrentToken(Line.First), AutoFound(false), - Keywords(Keywords) { + const AdditionalKeywords &Keywords, + SmallVector &TrackedScopes) + : Scopes(TrackedScopes), Style(Style), Line(Line), + CurrentToken(Line.First), AutoFound(false), Keywords(Keywords) { Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/false)); resetTokenMetadata(); } @@ -847,6 +848,9 @@ unsigned CommaCount = 0; while (CurrentToken) { if (CurrentToken->is(tok::r_brace)) { + // Handle unbalanced braces + if (Scopes.size() > 1) + Scopes.pop_back(); assert(OpeningBrace.Optional == CurrentToken->Optional); OpeningBrace.MatchingParen = CurrentToken; CurrentToken->MatchingParen = &OpeningBrace; @@ -1146,6 +1150,27 @@ if (Previous && Previous->getType() != TT_DictLiteral) Previous->setType(TT_SelectorName); } + switch (Tok->getType()) { + case TT_FunctionLBrace: + case TT_LambdaLBrace: + Scopes.push_back(TokenAnnotator::ScopeType::Function); + break; + case TT_ClassLBrace: + case TT_StructLBrace: + case TT_UnionLBrace: + Scopes.push_back(TokenAnnotator::ScopeType::Class); + break; + case TT_EnumLBrace: + case TT_ControlStatementLBrace: + case TT_ElseLBrace: + case TT_BracedListLBrace: + case TT_CompoundRequirementLBrace: + case TT_ObjCBlockLBrace: + case TT_RecordLBrace: + case TT_RequiresExpressionLBrace: + default: + Scopes.push_back(TokenAnnotator::ScopeType::Other); + } if (!parseBrace()) return false; break; @@ -1176,6 +1201,9 @@ case tok::r_square: return false; case tok::r_brace: + // Handle unbalanced braces + if (Scopes.size() > 1) + Scopes.pop_back(); // Lines can start with '}'. if (Tok->Previous) return false; @@ -2446,6 +2474,25 @@ if (IsExpression && !Contexts.back().CaretFound) return TT_BinaryOperator; + // Opeartors at class scope are likely pointer or reference members. + if (Scopes.back() == TokenAnnotator::ScopeType::Class) + return TT_PointerOrReference; + + // It's more likely that & represents operator& than an uninitialized + // reference. + if (Tok.is(tok::amp) && (PrevToken && PrevToken->Tok.isAnyIdentifier()) && + (!PrevToken->getPreviousNonComment() || + PrevToken->getPreviousNonComment()->isOneOf(tok::amp, tok::period, + tok::arrow, tok::arrowstar, + tok::periodstar)) && + (NextToken && NextToken->Tok.isAnyIdentifier()) && + (NextToken->getNextNonComment() && + (NextToken->getNextNonComment()->isOneOf( + tok::amp, tok::semi, tok::period, tok::arrow, tok::arrowstar, + tok::periodstar)))) { + return TT_BinaryOperator; + } + return TT_PointerOrReference; } @@ -2476,6 +2523,7 @@ } SmallVector Contexts; + SmallVector &Scopes; const FormatStyle &Style; AnnotatedLine &Line; @@ -2724,6 +2772,12 @@ } // end anonymous namespace +TokenAnnotator::TokenAnnotator(const FormatStyle &Style, + const AdditionalKeywords &Keywords) + : Style(Style), Keywords(Keywords) { + Scopes.push_back(ScopeType::None); +} + void TokenAnnotator::setCommentLineLevels( SmallVectorImpl &Lines) const { const AnnotatedLine *NextNonCommentLine = nullptr; @@ -2763,11 +2817,11 @@ return Result; } -void TokenAnnotator::annotate(AnnotatedLine &Line) const { +void TokenAnnotator::annotate(AnnotatedLine &Line) { for (auto &Child : Line.Children) annotate(*Child); - AnnotatingParser Parser(Style, Line, Keywords); + AnnotatingParser Parser(Style, Line, Keywords, Scopes); Line.Type = Parser.parseLine(); // With very deep nesting, ExpressionParser uses lots of stack and the Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -11267,6 +11267,13 @@ verifyFormat("int operator()(T (&&)[N]) { return 1; }"); verifyFormat("int operator()(T (&)[N]) { return 0; }"); + + verifyFormat("val1 & val2;"); + verifyFormat("val1 & val2 & val3;"); + verifyFormat("class c {\n" + " void func(type &a) { a & member; }\n" + " anotherType &member;\n" + "}"); } TEST_F(FormatTest, UnderstandsAttributes) { Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -175,6 +175,57 @@ ASSERT_EQ(Tokens.size(), 17u) << Tokens; EXPECT_TOKEN(Tokens[9], tok::ampamp, TT_PointerOrReference); EXPECT_TOKEN(Tokens[12], tok::ampamp, TT_PointerOrReference); + + Tokens = annotate("Type1 &val1 = val2;"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_PointerOrReference); + + Tokens = annotate("Type1 *val1 = &val2;"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::star, TT_PointerOrReference); + EXPECT_TOKEN(Tokens[4], tok::amp, TT_UnaryOperator); + + Tokens = annotate("val1 & val2;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); + + Tokens = annotate("val1 & val2.member;"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); + + Tokens = annotate("val1 & val2 & val3;"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator); + + Tokens = annotate("val1 & val2 // comment\n" + " & val3;"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[4], tok::amp, TT_BinaryOperator); + + Tokens = + annotate("val1 & val2.member & val3.member() & val4 & val5->member;"); + ASSERT_EQ(Tokens.size(), 19u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[5], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[11], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[13], tok::amp, TT_BinaryOperator); + + Tokens = annotate("class c {\n" + " void func(type &a) { a & member; }\n" + " anotherType &member;\n" + "}"); + ASSERT_EQ(Tokens.size(), 22u) << Tokens; + EXPECT_TOKEN(Tokens[7], tok::amp, TT_PointerOrReference); + EXPECT_TOKEN(Tokens[12], tok::amp, TT_BinaryOperator); + EXPECT_TOKEN(Tokens[17], tok::amp, TT_PointerOrReference); + + Tokens = annotate("struct S {\n" + " auto Mem = C & D;\n" + "}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens[7], tok::amp, TT_BinaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsUsesOfPlusAndMinus) {