diff options
author | Daniel Jasper <djasper@google.com> | 2013-01-29 21:01:14 +0000 |
---|---|---|
committer | Daniel Jasper <djasper@google.com> | 2013-01-29 21:01:14 +0000 |
commit | 32d28ee6061930b2a9c170cbaaeea028c88a7b89 (patch) | |
tree | 897444a96325186bfc608451e86df3922b5c166e /lib/Format/TokenAnnotator.cpp | |
parent | fcf896078e58aeb7adecb1a0ae5c8e0052b17f9f (diff) |
Move the token annotator into separate files.
No functional changes. Also removed experimental-warning from all of
clang-format's files, as it is no longer accurate.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@173830 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Format/TokenAnnotator.cpp')
-rw-r--r-- | lib/Format/TokenAnnotator.cpp | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp new file mode 100644 index 0000000000..eabc8cff28 --- /dev/null +++ b/lib/Format/TokenAnnotator.cpp @@ -0,0 +1,875 @@ +//===--- TokenAnnotator.cpp - Format C++ code -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements a token annotator, i.e. creates +/// \c AnnotatedTokens out of \c FormatTokens with required extra information. +/// +//===----------------------------------------------------------------------===// + +#include "TokenAnnotator.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace format { + +/// \brief Returns if a token is an Objective-C selector name. +/// +/// For example, "bar" is a selector name in [foo bar:(4 + 5)]. +static bool isObjCSelectorName(const AnnotatedToken &Tok) { + return Tok.is(tok::identifier) && !Tok.Children.empty() && + Tok.Children[0].is(tok::colon) && + Tok.Children[0].Type == TT_ObjCMethodExpr; +} + +static bool isBinaryOperator(const AnnotatedToken &Tok) { + // Comma is a binary operator, but does not behave as such wrt. formatting. + return getPrecedence(Tok) > prec::Comma; +} + +/// \brief A parser that gathers additional information about tokens. +/// +/// The \c TokenAnnotator tries to matches parenthesis and square brakets and +/// store a parenthesis levels. It also tries to resolve matching "<" and ">" +/// into template parameter lists. +class AnnotatingParser { +public: + AnnotatingParser(AnnotatedToken &RootToken) + : CurrentToken(&RootToken), KeywordVirtualFound(false), + ColonIsObjCMethodExpr(false), ColonIsForRangeExpr(false) { + } + + /// \brief A helper class to manage AnnotatingParser::ColonIsObjCMethodExpr. + struct ObjCSelectorRAII { + AnnotatingParser &P; + bool ColonWasObjCMethodExpr; + + ObjCSelectorRAII(AnnotatingParser &P) + : P(P), ColonWasObjCMethodExpr(P.ColonIsObjCMethodExpr) { + } + + ~ObjCSelectorRAII() { P.ColonIsObjCMethodExpr = ColonWasObjCMethodExpr; } + + void markStart(AnnotatedToken &Left) { + P.ColonIsObjCMethodExpr = true; + Left.Type = TT_ObjCMethodExpr; + } + + void markEnd(AnnotatedToken &Right) { Right.Type = TT_ObjCMethodExpr; } + }; + + bool parseAngle() { + if (CurrentToken == NULL) + return false; + AnnotatedToken *Left = CurrentToken->Parent; + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::greater)) { + Left->MatchingParen = CurrentToken; + CurrentToken->MatchingParen = Left; + CurrentToken->Type = TT_TemplateCloser; + next(); + return true; + } + if (CurrentToken->is(tok::r_paren) || CurrentToken->is(tok::r_square) || + CurrentToken->is(tok::r_brace)) + return false; + if (CurrentToken->is(tok::pipepipe) || CurrentToken->is(tok::ampamp) || + CurrentToken->is(tok::question) || CurrentToken->is(tok::colon)) + return false; + if (CurrentToken->is(tok::comma)) + ++Left->ParameterCount; + if (!consumeToken()) + return false; + } + return false; + } + + bool parseParens(bool LookForDecls = false) { + if (CurrentToken == NULL) + return false; + bool StartsObjCMethodExpr = false; + AnnotatedToken *Left = CurrentToken->Parent; + if (CurrentToken->is(tok::caret)) { + // ^( starts a block. + Left->Type = TT_ObjCBlockLParen; + } else if (AnnotatedToken *MaybeSel = Left->Parent) { + // @selector( starts a selector. + if (MaybeSel->isObjCAtKeyword(tok::objc_selector) && MaybeSel->Parent && + MaybeSel->Parent->is(tok::at)) { + StartsObjCMethodExpr = true; + } + } + + ObjCSelectorRAII objCSelector(*this); + if (StartsObjCMethodExpr) + objCSelector.markStart(*Left); + + while (CurrentToken != NULL) { + // LookForDecls is set when "if (" has been seen. Check for + // 'identifier' '*' 'identifier' followed by not '=' -- this + // '*' has to be a binary operator but determineStarAmpUsage() will + // categorize it as an unary operator, so set the right type here. + if (LookForDecls && !CurrentToken->Children.empty()) { + AnnotatedToken &Prev = *CurrentToken->Parent; + AnnotatedToken &Next = CurrentToken->Children[0]; + if (Prev.Parent->is(tok::identifier) && + (Prev.is(tok::star) || Prev.is(tok::amp)) && + CurrentToken->is(tok::identifier) && Next.isNot(tok::equal)) { + Prev.Type = TT_BinaryOperator; + LookForDecls = false; + } + } + + if (CurrentToken->is(tok::r_paren)) { + Left->MatchingParen = CurrentToken; + CurrentToken->MatchingParen = Left; + + if (StartsObjCMethodExpr) + objCSelector.markEnd(*CurrentToken); + + next(); + return true; + } + if (CurrentToken->is(tok::r_square) || CurrentToken->is(tok::r_brace)) + return false; + if (CurrentToken->is(tok::comma)) + ++Left->ParameterCount; + if (!consumeToken()) + return false; + } + return false; + } + + bool parseSquare() { + if (!CurrentToken) + return false; + + // A '[' could be an index subscript (after an indentifier or after + // ')' or ']'), or it could be the start of an Objective-C method + // expression. + AnnotatedToken *Left = CurrentToken->Parent; + bool StartsObjCMethodExpr = + !Left->Parent || Left->Parent->is(tok::colon) || + Left->Parent->is(tok::l_square) || Left->Parent->is(tok::l_paren) || + Left->Parent->is(tok::kw_return) || Left->Parent->is(tok::kw_throw) || + getBinOpPrecedence(Left->Parent->FormatTok.Tok.getKind(), true, true) > + prec::Unknown; + + ObjCSelectorRAII objCSelector(*this); + if (StartsObjCMethodExpr) + objCSelector.markStart(*Left); + + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::r_square)) { + if (!CurrentToken->Children.empty() && + CurrentToken->Children[0].is(tok::l_paren)) { + // An ObjC method call can't be followed by an open parenthesis. + // FIXME: Do we incorrectly label ":" with this? + StartsObjCMethodExpr = false; + Left->Type = TT_Unknown; + } + if (StartsObjCMethodExpr) + objCSelector.markEnd(*CurrentToken); + Left->MatchingParen = CurrentToken; + CurrentToken->MatchingParen = Left; + next(); + return true; + } + if (CurrentToken->is(tok::r_paren) || CurrentToken->is(tok::r_brace)) + return false; + if (CurrentToken->is(tok::comma)) + ++Left->ParameterCount; + if (!consumeToken()) + return false; + } + return false; + } + + bool parseBrace() { + // Lines are fine to end with '{'. + if (CurrentToken == NULL) + return true; + AnnotatedToken *Left = CurrentToken->Parent; + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::r_brace)) { + Left->MatchingParen = CurrentToken; + CurrentToken->MatchingParen = Left; + next(); + return true; + } + if (CurrentToken->is(tok::r_paren) || CurrentToken->is(tok::r_square)) + return false; + if (!consumeToken()) + return false; + } + return true; + } + + bool parseConditional() { + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::colon)) { + CurrentToken->Type = TT_ConditionalExpr; + next(); + return true; + } + if (!consumeToken()) + return false; + } + return false; + } + + bool parseTemplateDeclaration() { + if (CurrentToken != NULL && CurrentToken->is(tok::less)) { + CurrentToken->Type = TT_TemplateOpener; + next(); + if (!parseAngle()) + return false; + CurrentToken->Parent->ClosesTemplateDeclaration = true; + return true; + } + return false; + } + + bool consumeToken() { + AnnotatedToken *Tok = CurrentToken; + next(); + switch (Tok->FormatTok.Tok.getKind()) { + case tok::plus: + case tok::minus: + // At the start of the line, +/- specific ObjectiveC method + // declarations. + if (Tok->Parent == NULL) + Tok->Type = TT_ObjCMethodSpecifier; + break; + case tok::colon: + // Colons from ?: are handled in parseConditional(). + if (Tok->Parent->is(tok::r_paren)) + Tok->Type = TT_CtorInitializerColon; + else if (ColonIsObjCMethodExpr) + Tok->Type = TT_ObjCMethodExpr; + else if (ColonIsForRangeExpr) + Tok->Type = TT_RangeBasedForLoopColon; + break; + case tok::kw_if: + case tok::kw_while: + if (CurrentToken != NULL && CurrentToken->is(tok::l_paren)) { + next(); + if (!parseParens(/*LookForDecls=*/ true)) + return false; + } + break; + case tok::kw_for: + ColonIsForRangeExpr = true; + next(); + if (!parseParens()) + return false; + break; + case tok::l_paren: + if (!parseParens()) + return false; + break; + case tok::l_square: + if (!parseSquare()) + return false; + break; + case tok::l_brace: + if (!parseBrace()) + return false; + break; + case tok::less: + if (parseAngle()) + Tok->Type = TT_TemplateOpener; + else { + Tok->Type = TT_BinaryOperator; + CurrentToken = Tok; + next(); + } + break; + case tok::r_paren: + case tok::r_square: + return false; + case tok::r_brace: + // Lines can start with '}'. + if (Tok->Parent != NULL) + return false; + break; + case tok::greater: + Tok->Type = TT_BinaryOperator; + break; + case tok::kw_operator: + if (CurrentToken != NULL && CurrentToken->is(tok::l_paren)) { + CurrentToken->Type = TT_OverloadedOperator; + next(); + if (CurrentToken != NULL && CurrentToken->is(tok::r_paren)) { + CurrentToken->Type = TT_OverloadedOperator; + next(); + } + } else { + while (CurrentToken != NULL && CurrentToken->isNot(tok::l_paren)) { + CurrentToken->Type = TT_OverloadedOperator; + next(); + } + } + break; + case tok::question: + parseConditional(); + break; + case tok::kw_template: + parseTemplateDeclaration(); + break; + default: + break; + } + return true; + } + + void parseIncludeDirective() { + next(); + if (CurrentToken != NULL && CurrentToken->is(tok::less)) { + next(); + while (CurrentToken != NULL) { + if (CurrentToken->isNot(tok::comment) || + !CurrentToken->Children.empty()) + CurrentToken->Type = TT_ImplicitStringLiteral; + next(); + } + } else { + while (CurrentToken != NULL) { + next(); + } + } + } + + void parseWarningOrError() { + next(); + // We still want to format the whitespace left of the first token of the + // warning or error. + next(); + while (CurrentToken != NULL) { + CurrentToken->Type = TT_ImplicitStringLiteral; + next(); + } + } + + void parsePreprocessorDirective() { + next(); + if (CurrentToken == NULL) + return; + // Hashes in the middle of a line can lead to any strange token + // sequence. + if (CurrentToken->FormatTok.Tok.getIdentifierInfo() == NULL) + return; + switch (CurrentToken->FormatTok.Tok.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_import: + parseIncludeDirective(); + break; + case tok::pp_error: + case tok::pp_warning: + parseWarningOrError(); + break; + default: + break; + } + } + + LineType parseLine() { + int PeriodsAndArrows = 0; + bool CanBeBuilderTypeStmt = true; + if (CurrentToken->is(tok::hash)) { + parsePreprocessorDirective(); + return LT_PreprocessorDirective; + } + while (CurrentToken != NULL) { + if (CurrentToken->is(tok::kw_virtual)) + KeywordVirtualFound = true; + if (CurrentToken->is(tok::period) || CurrentToken->is(tok::arrow)) + ++PeriodsAndArrows; + if (getPrecedence(*CurrentToken) > prec::Assignment && + CurrentToken->isNot(tok::less) && CurrentToken->isNot(tok::greater)) + CanBeBuilderTypeStmt = false; + if (!consumeToken()) + return LT_Invalid; + } + if (KeywordVirtualFound) + return LT_VirtualFunctionDecl; + + // Assume a builder-type call if there are 2 or more "." and "->". + if (PeriodsAndArrows >= 2 && CanBeBuilderTypeStmt) + return LT_BuilderTypeCall; + + return LT_Other; + } + + void next() { + if (CurrentToken != NULL && !CurrentToken->Children.empty()) + CurrentToken = &CurrentToken->Children[0]; + else + CurrentToken = NULL; + } + +private: + AnnotatedToken *CurrentToken; + bool KeywordVirtualFound; + bool ColonIsObjCMethodExpr; + bool ColonIsForRangeExpr; +}; + +void TokenAnnotator::annotate() { + AnnotatingParser Parser(Line.First); + Line.Type = Parser.parseLine(); + if (Line.Type == LT_Invalid) + return; + + bool LookForFunctionName = Line.MustBeDeclaration; + determineTokenTypes(Line.First, /*IsExpression=*/ false, LookForFunctionName); + + if (Line.First.Type == TT_ObjCMethodSpecifier) + Line.Type = LT_ObjCMethodDecl; + else if (Line.First.Type == TT_ObjCDecl) + Line.Type = LT_ObjCDecl; + else if (Line.First.Type == TT_ObjCProperty) + Line.Type = LT_ObjCProperty; + + Line.First.SpaceRequiredBefore = true; + Line.First.MustBreakBefore = Line.First.FormatTok.MustBreakBefore; + Line.First.CanBreakBefore = Line.First.MustBreakBefore; + + Line.First.TotalLength = Line.First.FormatTok.TokenLength; + if (!Line.First.Children.empty()) + calculateExtraInformation(Line.First.Children[0]); +} + +void TokenAnnotator::calculateExtraInformation(AnnotatedToken &Current) { + Current.SpaceRequiredBefore = spaceRequiredBefore(Current); + + if (Current.FormatTok.MustBreakBefore) { + Current.MustBreakBefore = true; + } else { + if (Current.Type == TT_LineComment) { + Current.MustBreakBefore = Current.FormatTok.NewlinesBefore > 0; + } else if ((Current.Parent->is(tok::comment) && + Current.FormatTok.NewlinesBefore > 0) || + (Current.is(tok::string_literal) && + Current.Parent->is(tok::string_literal))) { + Current.MustBreakBefore = true; + } else { + Current.MustBreakBefore = false; + } + } + Current.CanBreakBefore = Current.MustBreakBefore || canBreakBefore(Current); + if (Current.MustBreakBefore) + Current.TotalLength = Current.Parent->TotalLength + Style.ColumnLimit; + else + Current.TotalLength = + Current.Parent->TotalLength + Current.FormatTok.TokenLength + + (Current.SpaceRequiredBefore ? 1 : 0); + // FIXME: Only calculate this if CanBreakBefore is true once static + // initializers etc. are sorted out. + Current.SplitPenalty = splitPenalty(Current); + if (!Current.Children.empty()) + calculateExtraInformation(Current.Children[0]); +} + +unsigned TokenAnnotator::splitPenalty(const AnnotatedToken &Tok) { + const AnnotatedToken &Left = *Tok.Parent; + const AnnotatedToken &Right = Tok; + + if (Left.is(tok::l_brace) && Right.isNot(tok::l_brace)) + return 50; + if (Left.is(tok::equal) && Right.is(tok::l_brace)) + return 150; + if (Left.is(tok::coloncolon)) + return 500; + + if (Left.Type == TT_RangeBasedForLoopColon) + return 5; + + if (Right.is(tok::arrow) || Right.is(tok::period)) { + if (Left.is(tok::r_paren) && Line.Type == LT_BuilderTypeCall) + return 5; // Should be smaller than breaking at a nested comma. + return 150; + } + + // In for-loops, prefer breaking at ',' and ';'. + if (Line.First.is(tok::kw_for) && + (Left.isNot(tok::comma) && Left.isNot(tok::semi))) + return 20; + + if (Left.is(tok::semi) || Left.is(tok::comma)) + return 0; + + // In Objective-C method expressions, prefer breaking before "param:" over + // breaking after it. + if (isObjCSelectorName(Right)) + return 0; + if (Right.is(tok::colon) && Right.Type == TT_ObjCMethodExpr) + return 20; + + if (Left.is(tok::l_paren)) + return 20; + // FIXME: The penalty for a trailing "<" or "[" being higher than the + // penalty for a trainling "(" is a temporary workaround until we can + // properly avoid breaking in array subscripts or template parameters. + if (Left.is(tok::l_square) || Left.Type == TT_TemplateOpener) + return 50; + + if (Left.Type == TT_ConditionalExpr) + return prec::Assignment; + prec::Level Level = getPrecedence(Left); + + if (Level != prec::Unknown) + return Level; + + return 3; +} + +void TokenAnnotator::determineTokenTypes( + AnnotatedToken &Current, bool IsExpression, bool LookForFunctionName) { + if (getPrecedence(Current) == prec::Assignment) { + IsExpression = true; + AnnotatedToken *Previous = Current.Parent; + while (Previous != NULL) { + if (Previous->Type == TT_BinaryOperator && + (Previous->is(tok::star) || Previous->is(tok::amp))) { + Previous->Type = TT_PointerOrReference; + } + Previous = Previous->Parent; + } + } + if (Current.is(tok::kw_return) || Current.is(tok::kw_throw) || + (Current.is(tok::l_paren) && !Line.MustBeDeclaration && + (Current.Parent == NULL || Current.Parent->isNot(tok::kw_for)))) + IsExpression = true; + + if (Current.Type == TT_Unknown) { + if (LookForFunctionName && Current.is(tok::l_paren)) { + findFunctionName(&Current); + LookForFunctionName = false; + } else if (Current.is(tok::star) || Current.is(tok::amp)) { + Current.Type = determineStarAmpUsage(Current, IsExpression); + } else if (Current.is(tok::minus) || Current.is(tok::plus) || + Current.is(tok::caret)) { + Current.Type = determinePlusMinusCaretUsage(Current); + } else if (Current.is(tok::minusminus) || Current.is(tok::plusplus)) { + Current.Type = determineIncrementUsage(Current); + } else if (Current.is(tok::exclaim)) { + Current.Type = TT_UnaryOperator; + } else if (isBinaryOperator(Current)) { + Current.Type = TT_BinaryOperator; + } else if (Current.is(tok::comment)) { + std::string Data(Lexer::getSpelling(Current.FormatTok.Tok, SourceMgr, + Lex.getLangOpts())); + if (StringRef(Data).startswith("//")) + Current.Type = TT_LineComment; + else + Current.Type = TT_BlockComment; + } else if (Current.is(tok::r_paren) && + (Current.Parent->Type == TT_PointerOrReference || + Current.Parent->Type == TT_TemplateCloser) && + (Current.Children.empty() || + (Current.Children[0].isNot(tok::equal) && + Current.Children[0].isNot(tok::semi) && + Current.Children[0].isNot(tok::l_brace)))) { + // FIXME: We need to get smarter and understand more cases of casts. + Current.Type = TT_CastRParen; + } else if (Current.is(tok::at) && Current.Children.size()) { + switch (Current.Children[0].FormatTok.Tok.getObjCKeywordID()) { + case tok::objc_interface: + case tok::objc_implementation: + case tok::objc_protocol: + Current.Type = TT_ObjCDecl; + break; + case tok::objc_property: + Current.Type = TT_ObjCProperty; + break; + default: + break; + } + } + } + + if (!Current.Children.empty()) + determineTokenTypes(Current.Children[0], IsExpression, LookForFunctionName); +} + +void TokenAnnotator::findFunctionName(AnnotatedToken *Current) { + AnnotatedToken *Parent = Current->Parent; + while (Parent != NULL && Parent->Parent != NULL) { + if (Parent->is(tok::identifier) && + (Parent->Parent->is(tok::identifier) || Parent->Parent->Type == + TT_PointerOrReference || Parent->Parent->Type == TT_TemplateCloser)) { + Parent->Type = TT_StartOfName; + break; + } + Parent = Parent->Parent; + } +} + +TokenType TokenAnnotator::determineStarAmpUsage(const AnnotatedToken &Tok, + bool IsExpression) { + const AnnotatedToken *PrevToken = getPreviousToken(Tok); + if (PrevToken == NULL) + return TT_UnaryOperator; + + const AnnotatedToken *NextToken = getNextToken(Tok); + if (NextToken == NULL) + return TT_Unknown; + + if (NextToken->is(tok::l_square) && NextToken->Type != TT_ObjCMethodExpr) + return TT_PointerOrReference; + + if (PrevToken->is(tok::l_paren) || PrevToken->is(tok::l_square) || + PrevToken->is(tok::l_brace) || PrevToken->is(tok::comma) || + PrevToken->is(tok::kw_return) || PrevToken->is(tok::colon) || + PrevToken->Type == TT_BinaryOperator || + PrevToken->Type == TT_UnaryOperator || PrevToken->Type == TT_CastRParen) + return TT_UnaryOperator; + + if (PrevToken->FormatTok.Tok.isLiteral() || PrevToken->is(tok::r_paren) || + PrevToken->is(tok::r_square) || NextToken->FormatTok.Tok.isLiteral() || + NextToken->is(tok::plus) || NextToken->is(tok::minus) || + NextToken->is(tok::plusplus) || NextToken->is(tok::minusminus) || + NextToken->is(tok::tilde) || NextToken->is(tok::exclaim) || + NextToken->is(tok::l_paren) || NextToken->is(tok::l_square) || + NextToken->is(tok::kw_alignof) || NextToken->is(tok::kw_sizeof)) + return TT_BinaryOperator; + + if (NextToken->is(tok::comma) || NextToken->is(tok::r_paren) || + NextToken->is(tok::greater)) + return TT_PointerOrReference; + + // It is very unlikely that we are going to find a pointer or reference type + // definition on the RHS of an assignment. + if (IsExpression) + return TT_BinaryOperator; + + return TT_PointerOrReference; +} + +TokenType +TokenAnnotator::determinePlusMinusCaretUsage(const AnnotatedToken &Tok) { + const AnnotatedToken *PrevToken = getPreviousToken(Tok); + if (PrevToken == NULL) + return TT_UnaryOperator; + + // Use heuristics to recognize unary operators. + if (PrevToken->is(tok::equal) || PrevToken->is(tok::l_paren) || + PrevToken->is(tok::comma) || PrevToken->is(tok::l_square) || + PrevToken->is(tok::question) || PrevToken->is(tok::colon) || + PrevToken->is(tok::kw_return) || PrevToken->is(tok::kw_case) || + PrevToken->is(tok::at) || PrevToken->is(tok::l_brace)) + return TT_UnaryOperator; + + // There can't be to consecutive binary operators. + if (PrevToken->Type == TT_BinaryOperator) + return TT_UnaryOperator; + + // Fall back to marking the token as binary operator. + return TT_BinaryOperator; +} + +TokenType TokenAnnotator::determineIncrementUsage(const AnnotatedToken &Tok) { + const AnnotatedToken *PrevToken = getPreviousToken(Tok); + if (PrevToken == NULL) + return TT_UnaryOperator; + if (PrevToken->is(tok::r_paren) || PrevToken->is(tok::r_square) || + PrevToken->is(tok::identifier)) + return TT_TrailingUnaryOperator; + + return TT_UnaryOperator; +} + +bool TokenAnnotator::spaceRequiredBetween(const AnnotatedToken &Left, + const AnnotatedToken &Right) { + if (Right.is(tok::hashhash)) + return Left.is(tok::hash); + if (Left.is(tok::hashhash) || Left.is(tok::hash)) + return Right.is(tok::hash); + if (Right.is(tok::r_paren) || Right.is(tok::semi) || Right.is(tok::comma)) + return false; + if (Right.is(tok::less) && + (Left.is(tok::kw_template) || + (Line.Type == LT_ObjCDecl && Style.ObjCSpaceBeforeProtocolList))) + return true; + if (Left.is(tok::arrow) || Right.is(tok::arrow)) + return false; + if (Left.is(tok::exclaim) || Left.is(tok::tilde)) + return false; + if (Left.is(tok::at) && + (Right.is(tok::identifier) || Right.is(tok::string_literal) || + Right.is(tok::char_constant) || Right.is(tok::numeric_constant) || + Right.is(tok::l_paren) || Right.is(tok::l_brace) || + Right.is(tok::kw_true) || Right.is(tok::kw_false))) + return false; + if (Left.is(tok::coloncolon)) + return false; + if (Right.is(tok::coloncolon)) + return Left.isNot(tok::identifier) && Left.isNot(tok::greater); + if (Left.is(tok::less) || Right.is(tok::greater) || Right.is(tok::less)) + return false; + if (Right.is(tok::amp) || Right.is(tok::star)) + return Left.FormatTok.Tok.isLiteral() || + (Left.isNot(tok::star) && Left.isNot(tok::amp) && + !Style.PointerAndReferenceBindToType); + if (Left.is(tok::amp) || Left.is(tok::star)) + return Right.FormatTok.Tok.isLiteral() || + Style.PointerAndReferenceBindToType; + if (Right.is(tok::star) && Left.is(tok::l_paren)) + return false; + if (Left.is(tok::l_square) || Right.is(tok::r_square)) + return false; + if (Right.is(tok::l_square) && Right.Type != TT_ObjCMethodExpr) + return false; + if (Left.is(tok::period) || Right.is(tok::period)) + return false; + if (Left.is(tok::colon)) + return Left.Type != TT_ObjCMethodExpr; + if (Right.is(tok::colon)) + return Right.Type != TT_ObjCMethodExpr; + if (Left.is(tok::l_paren)) + return false; + if (Right.is(tok::l_paren)) { + return Line.Type == LT_ObjCDecl || Left.is(tok::kw_if) || + Left.is(tok::kw_for) || Left.is(tok::kw_while) || + Left.is(tok::kw_switch) || Left.is(tok::kw_return) || + Left.is(tok::kw_catch) || Left.is(tok::kw_new) || + Left.is(tok::kw_delete); + } + if (Left.is(tok::at) && + Right.FormatTok.Tok.getObjCKeywordID() != tok::objc_not_keyword) + return false; + if (Left.is(tok::l_brace) && Right.is(tok::r_brace)) + return false; + return true; +} + +bool TokenAnnotator::spaceRequiredBefore(const AnnotatedToken &Tok) { + if (Line.Type == LT_ObjCMethodDecl) { + if (Tok.is(tok::identifier) && !Tok.Children.empty() && + Tok.Children[0].is(tok::colon) && Tok.Parent->is(tok::identifier)) + return true; + if (Tok.is(tok::colon)) + return false; + if (Tok.Parent->Type == TT_ObjCMethodSpecifier) + return true; + if (Tok.Parent->is(tok::r_paren) && Tok.is(tok::identifier)) + // Don't space between ')' and <id> + return false; + if (Tok.Parent->is(tok::colon) && Tok.is(tok::l_paren)) + // Don't space between ':' and '(' + return false; + } + if (Line.Type == LT_ObjCProperty && + (Tok.is(tok::equal) || Tok.Parent->is(tok::equal))) + return false; + + if (Tok.Parent->is(tok::comma)) + return true; + if (Tok.Type == TT_CtorInitializerColon || Tok.Type == TT_ObjCBlockLParen) + return true; + if (Tok.Type == TT_OverloadedOperator) + return Tok.is(tok::identifier) || Tok.is(tok::kw_new) || + Tok.is(tok::kw_delete) || Tok.is(tok::kw_bool); + if (Tok.Parent->Type == TT_OverloadedOperator) + return false; + if (Tok.is(tok::colon)) + return Line.First.isNot(tok::kw_case) && !Tok.Children.empty() && + Tok.Type != TT_ObjCMethodExpr; + if (Tok.Parent->Type == TT_UnaryOperator || Tok.Parent->Type == TT_CastRParen) + return false; + if (Tok.Type == TT_UnaryOperator) + return Tok.Parent->isNot(tok::l_paren) && + Tok.Parent->isNot(tok::l_square) && Tok.Parent->isNot(tok::at) && + (Tok.Parent->isNot(tok::colon) || + Tok.Parent->Type != TT_ObjCMethodExpr); + if (Tok.Parent->is(tok::greater) && Tok.is(tok::greater)) { + return Tok.Type == TT_TemplateCloser && Tok.Parent->Type == + TT_TemplateCloser && Style.SplitTemplateClosingGreater; + } + if (Tok.Type == TT_BinaryOperator || Tok.Parent->Type == TT_BinaryOperator) + return true; + if (Tok.Parent->Type == TT_TemplateCloser && Tok.is(tok::l_paren)) + return false; + if (Tok.is(tok::less) && Line.First.is(tok::hash)) + return true; + if (Tok.Type == TT_TrailingUnaryOperator) + return false; + return spaceRequiredBetween(*Tok.Parent, Tok); +} + +bool TokenAnnotator::canBreakBefore(const AnnotatedToken &Right) { + const AnnotatedToken &Left = *Right.Parent; + if (Line.Type == LT_ObjCMethodDecl) { + if (Right.is(tok::identifier) && !Right.Children.empty() && + Right.Children[0].is(tok::colon) && Left.is(tok::identifier)) + return true; + if (Right.is(tok::identifier) && Left.is(tok::l_paren) && + Left.Parent->is(tok::colon)) + // Don't break this identifier as ':' or identifier + // before it will break. + return false; + if (Right.is(tok::colon) && Left.is(tok::identifier) && Left.CanBreakBefore) + // Don't break at ':' if identifier before it can beak. + return false; + } + if (Right.Type == TT_StartOfName && Style.AllowReturnTypeOnItsOwnLine) + return true; + if (Right.is(tok::colon) && Right.Type == TT_ObjCMethodExpr) + return false; + if (Left.is(tok::colon) && Left.Type == TT_ObjCMethodExpr) + return true; + if (isObjCSelectorName(Right)) + return true; + if (Left.ClosesTemplateDeclaration) + return true; + if (Right.Type == TT_ConditionalExpr || Right.is(tok::question)) + return true; + if (Left.Type == TT_RangeBasedForLoopColon) + return true; + if (Left.Type == TT_PointerOrReference || Left.Type == TT_TemplateCloser || + Left.Type == TT_UnaryOperator || Left.Type == TT_ConditionalExpr || + Left.is(tok::question)) + return false; + if (Left.is(tok::equal) && Line.Type == LT_VirtualFunctionDecl) + return false; + + if (Right.Type == TT_LineComment) + // We rely on MustBreakBefore being set correctly here as we should not + // change the "binding" behavior of a comment. + return false; + + // Allow breaking after a trailing 'const', e.g. after a method declaration, + // unless it is follow by ';', '{' or '='. + if (Left.is(tok::kw_const) && Left.Parent != NULL && + Left.Parent->is(tok::r_paren)) + return Right.isNot(tok::l_brace) && Right.isNot(tok::semi) && + Right.isNot(tok::equal); + + // We only break before r_brace if there was a corresponding break before + // the l_brace, which is tracked by BreakBeforeClosingBrace. + if (Right.is(tok::r_brace)) + return false; + + if (Right.is(tok::r_paren) || Right.is(tok::greater)) + return false; + return (isBinaryOperator(Left) && Left.isNot(tok::lessless)) || + Left.is(tok::comma) || Right.is(tok::lessless) || + Right.is(tok::arrow) || Right.is(tok::period) || + Right.is(tok::colon) || Left.is(tok::coloncolon) || + Left.is(tok::semi) || Left.is(tok::l_brace) || + (Left.is(tok::r_paren) && Left.Type != TT_CastRParen && + Right.is(tok::identifier)) || + (Left.is(tok::l_paren) && !Right.is(tok::r_paren)) || + (Left.is(tok::l_square) && !Right.is(tok::r_square)); +} + +} // namespace format +} // namespace clang |