diff options
Diffstat (limited to 'lib/Format/TokenAnnotator.cpp')
-rw-r--r-- | lib/Format/TokenAnnotator.cpp | 433 |
1 files changed, 301 insertions, 132 deletions
diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 4ba3f91..98f5709 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -15,6 +15,7 @@ #include "TokenAnnotator.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "format-token-annotator" @@ -43,8 +44,14 @@ private: bool parseAngle() { if (!CurrentToken) return false; - ScopedContextCreator ContextCreator(*this, tok::less, 10); FormatToken *Left = CurrentToken->Previous; + Left->ParentBracket = Contexts.back().ContextKind; + ScopedContextCreator ContextCreator(*this, tok::less, 10); + + // If this angle is in the context of an expression, we need to be more + // hesitant to detect it as opening template parameters. + bool InExprContext = Contexts.back().IsExpression; + Contexts.back().IsExpression = false; // If there's a template keyword before the opening angle bracket, this is a // template parameter, not an argument. @@ -68,8 +75,8 @@ private: next(); continue; } - if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace, - tok::colon, tok::question)) + if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace) || + (CurrentToken->isOneOf(tok::colon, tok::question) && InExprContext)) return false; // If a && or || is found and interpreted as a binary operator, this set // of angles is likely part of something like "a < b && c > d". If the @@ -92,6 +99,8 @@ private: bool parseParens(bool LookForDecls = false) { if (!CurrentToken) return false; + FormatToken *Left = CurrentToken->Previous; + Left->ParentBracket = Contexts.back().ContextKind; ScopedContextCreator ContextCreator(*this, tok::l_paren, 1); // FIXME: This is a bit of a hack. Do better. @@ -99,7 +108,6 @@ private: Contexts.size() == 2 && Contexts[0].ColonIsForRangeExpr; bool StartsObjCMethodExpr = false; - FormatToken *Left = CurrentToken->Previous; if (CurrentToken->is(tok::caret)) { // (^ can start a block type. Left->Type = TT_ObjCBlockLParen; @@ -117,22 +125,22 @@ private: Left->Previous->is(TT_BinaryOperator))) { // static_assert, if and while usually contain expressions. Contexts.back().IsExpression = true; - } else if (Line.InPPDirective && - (!Left->Previous || - !Left->Previous->isOneOf(tok::identifier, - TT_OverloadedOperator))) { - Contexts.back().IsExpression = true; } else if (Left->Previous && Left->Previous->is(tok::r_square) && Left->Previous->MatchingParen && Left->Previous->MatchingParen->is(TT_LambdaLSquare)) { // This is a parameter list of a lambda expression. Contexts.back().IsExpression = false; + } else if (Line.InPPDirective && + (!Left->Previous || + !Left->Previous->isOneOf(tok::identifier, + TT_OverloadedOperator))) { + Contexts.back().IsExpression = true; } else if (Contexts[Contexts.size() - 2].CaretFound) { // This is the parameter list of an ObjC block. Contexts.back().IsExpression = false; } else if (Left->Previous && Left->Previous->is(tok::kw___attribute)) { Left->Type = TT_AttributeParen; - } else if (Left->Previous && Left->Previous->IsForEachMacro) { + } else if (Left->Previous && Left->Previous->is(TT_ForEachMacro)) { // The first argument to a foreach macro is a declaration. Contexts.back().IsForEachMacro = true; Contexts.back().IsExpression = false; @@ -149,6 +157,8 @@ private: bool MightBeFunctionType = CurrentToken->is(tok::star); bool HasMultipleLines = false; bool HasMultipleParametersOnALine = false; + bool MightBeObjCForRangeLoop = + Left->Previous && Left->Previous->is(tok::kw_for); while (CurrentToken) { // LookForDecls is set when "if (" has been seen. Check for // 'identifier' '*' 'identifier' followed by not '=' -- this @@ -210,7 +220,8 @@ private: } if (CurrentToken->isOneOf(tok::r_square, tok::r_brace)) return false; - else if (CurrentToken->is(tok::l_brace)) + + if (CurrentToken->is(tok::l_brace)) Left->Type = TT_Unknown; // Not TT_ObjCBlockLParen if (CurrentToken->is(tok::comma) && CurrentToken->Next && !CurrentToken->Next->HasUnescapedNewline && @@ -219,6 +230,15 @@ private: if (CurrentToken->isOneOf(tok::kw_const, tok::kw_auto) || CurrentToken->isSimpleTypeSpecifier()) Contexts.back().IsExpression = false; + if (CurrentToken->isOneOf(tok::semi, tok::colon)) + MightBeObjCForRangeLoop = false; + if (MightBeObjCForRangeLoop && CurrentToken->is(Keywords.kw_in)) + CurrentToken->Type = TT_ObjCForIn; + // When we discover a 'new', we set CanBeExpression to 'false' in order to + // parse the type correctly. Reset that after a comma. + if (CurrentToken->is(tok::comma)) + Contexts.back().CanBeExpression = true; + FormatToken *Tok = CurrentToken; if (!consumeToken()) return false; @@ -237,6 +257,7 @@ private: // ')' or ']'), it could be the start of an Objective-C method // expression, or it could the the start of an Objective-C array literal. FormatToken *Left = CurrentToken->Previous; + Left->ParentBracket = Contexts.back().ContextKind; FormatToken *Parent = Left->getPreviousNonComment(); bool StartsObjCMethodExpr = Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && @@ -316,6 +337,7 @@ private: bool parseBrace() { if (CurrentToken) { FormatToken *Left = CurrentToken->Previous; + Left->ParentBracket = Contexts.back().ContextKind; if (Contexts.back().CaretFound) Left->Type = TT_ObjCBlockLBrace; @@ -342,7 +364,8 @@ private: Style.Language == FormatStyle::LK_Proto) && Previous->is(tok::identifier)) Previous->Type = TT_SelectorName; - if (CurrentToken->is(tok::colon)) + if (CurrentToken->is(tok::colon) || + Style.Language == FormatStyle::LK_JavaScript) Left->Type = TT_DictLiteral; } if (!consumeToken()) @@ -408,10 +431,18 @@ private: if (!Tok->Previous) return false; // Colons from ?: are handled in parseConditional(). - if (Tok->Previous->is(tok::r_paren) && Contexts.size() == 1 && - Line.First->isNot(tok::kw_case)) { - Tok->Type = TT_CtorInitializerColon; - } else if (Contexts.back().ColonIsDictLiteral) { + if (Style.Language == FormatStyle::LK_JavaScript) { + if (Contexts.back().ColonIsForRangeExpr || // colon in for loop + (Contexts.size() == 1 && // switch/case labels + !Line.First->isOneOf(tok::kw_enum, tok::kw_case)) || + Contexts.back().ContextKind == tok::l_paren || // function params + Contexts.back().ContextKind == tok::l_square || // array type + Line.MustBeDeclaration) { // method/property declaration + Tok->Type = TT_JsTypeColon; + break; + } + } + if (Contexts.back().ColonIsDictLiteral) { Tok->Type = TT_DictLiteral; } else if (Contexts.back().ColonIsObjCMethodExpr || Line.First->is(TT_ObjCMethodSpecifier)) { @@ -429,7 +460,10 @@ private: Tok->Type = TT_BitFieldColon; } else if (Contexts.size() == 1 && !Line.First->isOneOf(tok::kw_enum, tok::kw_case)) { - Tok->Type = TT_InheritanceColon; + if (Tok->Previous->is(tok::r_paren)) + Tok->Type = TT_CtorInitializerColon; + else + Tok->Type = TT_InheritanceColon; } else if (Tok->Previous->is(tok::identifier) && Tok->Next && Tok->Next->isOneOf(tok::r_paren, tok::comma)) { // This handles a special macro in ObjC code where selectors including @@ -471,13 +505,15 @@ private: return false; break; case tok::less: - if ((!Tok->Previous || + if (!NonTemplateLess.count(Tok) && + (!Tok->Previous || (!Tok->Previous->Tok.isLiteral() && !(Tok->Previous->is(tok::r_paren) && Contexts.size() > 1))) && parseAngle()) { Tok->Type = TT_TemplateOpener; } else { Tok->Type = TT_BinaryOperator; + NonTemplateLess.insert(Tok); CurrentToken = Tok; next(); } @@ -509,21 +545,34 @@ private: } break; case tok::question: + if (Style.Language == FormatStyle::LK_JavaScript && Tok->Next && + Tok->Next->isOneOf(tok::semi, tok::colon, tok::r_paren, + tok::r_brace)) { + // Question marks before semicolons, colons, etc. indicate optional + // types (fields, parameters), e.g. + // function(x?: string, y?) {...} + // class X { y?; } + Tok->Type = TT_JsTypeOptionalQuestion; + break; + } + // Declarations cannot be conditional expressions, this can only be part + // of a type declaration. + if (Line.MustBeDeclaration && + Style.Language == FormatStyle::LK_JavaScript) + break; parseConditional(); break; case tok::kw_template: parseTemplateDeclaration(); break; - case tok::identifier: - if (Line.First->is(tok::kw_for) && Tok->is(Keywords.kw_in) && - Tok->Previous->isNot(tok::colon)) - Tok->Type = TT_ObjCForIn; - break; case tok::comma: - if (Contexts.back().FirstStartOfName) - Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; if (Contexts.back().InCtorInitializer) Tok->Type = TT_CtorInitializerComma; + else if (Contexts.back().FirstStartOfName && + (Contexts.size() == 1 || Line.First->is(tok::kw_for))) { + Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; + Line.IsMultiVariableDeclStmt = true; + } if (Contexts.back().IsForEachMacro) Contexts.back().IsExpression = true; break; @@ -557,11 +606,14 @@ private: void parsePragma() { next(); // Consume "pragma". - if (CurrentToken && CurrentToken->TokenText == "mark") { + if (CurrentToken && + CurrentToken->isOneOf(Keywords.kw_mark, Keywords.kw_option)) { + bool IsMark = CurrentToken->is(Keywords.kw_mark); next(); // Consume "mark". next(); // Consume first token (so we fix leading whitespace). while (CurrentToken) { - CurrentToken->Type = TT_ImplicitStringLiteral; + if (IsMark || CurrentToken->Previous->is(TT_BinaryOperator)) + CurrentToken->Type = TT_ImplicitStringLiteral; next(); } } @@ -582,6 +634,7 @@ private: return Type; switch (CurrentToken->Tok.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: + case tok::pp_include_next: case tok::pp_import: next(); parseIncludeDirective(); @@ -609,9 +662,9 @@ private: public: LineType parseLine() { - if (CurrentToken->is(tok::hash)) { + NonTemplateLess.clear(); + if (CurrentToken->is(tok::hash)) return parsePreprocessorDirective(); - } // Directly allow to 'import <string-literal>' to support protocol buffer // definitions (code.google.com/p/protobuf) or missing "#" (either way we @@ -635,6 +688,15 @@ public: return LT_ImportStatement; } + // In .proto files, top-level options are very similar to import statements + // and should not be line-wrapped. + if (Style.Language == FormatStyle::LK_Proto && Line.Level == 0 && + CurrentToken->is(Keywords.kw_option)) { + next(); + if (CurrentToken && CurrentToken->is(tok::identifier)) + return LT_ImportStatement; + } + bool KeywordVirtualFound = false; bool ImportStatement = false; while (CurrentToken) { @@ -678,11 +740,13 @@ private: // Reset token type in case we have already looked at it and then // recovered from an error (e.g. failure to find the matching >). - if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_FunctionLBrace, - TT_ImplicitStringLiteral, TT_RegexLiteral, + if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_ForEachMacro, + TT_FunctionLBrace, TT_ImplicitStringLiteral, + TT_InlineASMBrace, TT_RegexLiteral, TT_TrailingReturnArrow)) CurrentToken->Type = TT_Unknown; CurrentToken->Role.reset(); + CurrentToken->MatchingParen = nullptr; CurrentToken->FakeLParens.clear(); CurrentToken->FakeRParens = 0; } @@ -705,27 +769,22 @@ private: Context(tok::TokenKind ContextKind, unsigned BindingStrength, bool IsExpression) : ContextKind(ContextKind), BindingStrength(BindingStrength), - LongestObjCSelectorName(0), ColonIsForRangeExpr(false), - ColonIsDictLiteral(false), ColonIsObjCMethodExpr(false), - FirstObjCSelectorName(nullptr), FirstStartOfName(nullptr), - IsExpression(IsExpression), CanBeExpression(true), - InTemplateArgument(false), InCtorInitializer(false), - CaretFound(false), IsForEachMacro(false) {} + IsExpression(IsExpression) {} tok::TokenKind ContextKind; unsigned BindingStrength; - unsigned LongestObjCSelectorName; - bool ColonIsForRangeExpr; - bool ColonIsDictLiteral; - bool ColonIsObjCMethodExpr; - FormatToken *FirstObjCSelectorName; - FormatToken *FirstStartOfName; bool IsExpression; - bool CanBeExpression; - bool InTemplateArgument; - bool InCtorInitializer; - bool CaretFound; - bool IsForEachMacro; + unsigned LongestObjCSelectorName = 0; + bool ColonIsForRangeExpr = false; + bool ColonIsDictLiteral = false; + bool ColonIsObjCMethodExpr = false; + FormatToken *FirstObjCSelectorName = nullptr; + FormatToken *FirstStartOfName = nullptr; + bool CanBeExpression = true; + bool InTemplateArgument = false; + bool InCtorInitializer = false; + bool CaretFound = false; + bool IsForEachMacro = false; }; /// \brief Puts a new \c Context onto the stack \c Contexts for the lifetime @@ -746,23 +805,29 @@ private: void modifyContext(const FormatToken &Current) { if (Current.getPrecedence() == prec::Assignment && - !Line.First->isOneOf(tok::kw_template, tok::kw_using, - TT_UnaryOperator) && + !Line.First->isOneOf(tok::kw_template, tok::kw_using) && (!Current.Previous || Current.Previous->isNot(tok::kw_operator))) { Contexts.back().IsExpression = true; - for (FormatToken *Previous = Current.Previous; - Previous && !Previous->isOneOf(tok::comma, tok::semi); - Previous = Previous->Previous) { - if (Previous->isOneOf(tok::r_square, tok::r_paren)) { - Previous = Previous->MatchingParen; - if (!Previous) + if (!Line.First->is(TT_UnaryOperator)) { + for (FormatToken *Previous = Current.Previous; + Previous && !Previous->isOneOf(tok::comma, tok::semi); + Previous = Previous->Previous) { + if (Previous->isOneOf(tok::r_square, tok::r_paren)) { + Previous = Previous->MatchingParen; + if (!Previous) + break; + } + if (Previous->opensScope()) break; + if (Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator) && + Previous->isOneOf(tok::star, tok::amp, tok::ampamp) && + Previous->Previous && Previous->Previous->isNot(tok::equal)) + Previous->Type = TT_PointerOrReference; } - if (Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator) && - Previous->isOneOf(tok::star, tok::amp) && Previous->Previous && - Previous->Previous->isNot(tok::equal)) - Previous->Type = TT_PointerOrReference; } + } else if (Current.is(tok::lessless) && + (!Current.Previous || !Current.Previous->is(tok::kw_operator))) { + Contexts.back().IsExpression = true; } else if (Current.isOneOf(tok::kw_return, tok::kw_throw)) { Contexts.back().IsExpression = true; } else if (Current.is(TT_TrailingReturnArrow)) { @@ -833,30 +898,56 @@ private: } else if (Current.isOneOf(tok::exclaim, tok::tilde)) { Current.Type = TT_UnaryOperator; } else if (Current.is(tok::question)) { - Current.Type = TT_ConditionalExpr; + if (Style.Language == FormatStyle::LK_JavaScript && + Line.MustBeDeclaration) { + // In JavaScript, `interface X { foo?(): bar; }` is an optional method + // on the interface, not a ternary expression. + Current.Type = TT_JsTypeOptionalQuestion; + } else { + Current.Type = TT_ConditionalExpr; + } } else if (Current.isBinaryOperator() && (!Current.Previous || Current.Previous->isNot(tok::l_square))) { Current.Type = TT_BinaryOperator; } else if (Current.is(tok::comment)) { - if (Current.TokenText.startswith("//")) + if (Current.TokenText.startswith("/*")) { + if (Current.TokenText.endswith("*/")) + Current.Type = TT_BlockComment; + else + // The lexer has for some reason determined a comment here. But we + // cannot really handle it, if it isn't properly terminated. + Current.Tok.setKind(tok::unknown); + } else { Current.Type = TT_LineComment; - else - Current.Type = TT_BlockComment; + } } else if (Current.is(tok::r_paren)) { if (rParenEndsCast(Current)) Current.Type = TT_CastRParen; + if (Current.MatchingParen && Current.Next && + !Current.Next->isBinaryOperator() && + !Current.Next->isOneOf(tok::semi, tok::colon, tok::l_brace)) + if (FormatToken *BeforeParen = Current.MatchingParen->Previous) + if (BeforeParen->is(tok::identifier) && + BeforeParen->TokenText == BeforeParen->TokenText.upper() && + (!BeforeParen->Previous || + BeforeParen->Previous->ClosesTemplateDeclaration)) + Current.Type = TT_FunctionAnnotationRParen; } else if (Current.is(tok::at) && Current.Next) { - switch (Current.Next->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.Next->isStringLiteral()) { + Current.Type = TT_ObjCStringLiteral; + } else { + switch (Current.Next->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; + } } } else if (Current.is(tok::period)) { FormatToken *PreviousNoComment = Current.getPreviousNonComment(); @@ -875,7 +966,9 @@ private: // Line.MightBeFunctionDecl can only be true after the parentheses of a // function declaration have been found. Current.Type = TT_TrailingAnnotation; - } else if (Style.Language == FormatStyle::LK_Java && Current.Previous) { + } else if ((Style.Language == FormatStyle::LK_Java || + Style.Language == FormatStyle::LK_JavaScript) && + Current.Previous) { if (Current.Previous->is(tok::at) && Current.isNot(Keywords.kw_interface)) { const FormatToken &AtToken = *Current.Previous; @@ -902,7 +995,7 @@ private: return false; if (Tok.Previous->is(TT_LeadingJavaAnnotation)) - return false; + return false; // Skip "const" as it does not have an influence on whether this is a name. FormatToken *PreviousNotConst = Tok.Previous; @@ -964,8 +1057,7 @@ private: bool IsSizeOfOrAlignOf = LeftOfParens && LeftOfParens->isOneOf(tok::kw_sizeof, tok::kw_alignof); if (ParensAreType && !ParensCouldEndDecl && !IsSizeOfOrAlignOf && - ((Contexts.size() > 1 && Contexts[Contexts.size() - 2].IsExpression) || - (Tok.Next && Tok.Next->isBinaryOperator()))) + (Contexts.size() > 1 && Contexts[Contexts.size() - 2].IsExpression)) IsCast = true; else if (Tok.Next && Tok.Next->isNot(tok::string_literal) && (Tok.Next->Tok.isLiteral() || @@ -995,7 +1087,8 @@ private: } for (; Prev != Tok.MatchingParen; Prev = Prev->Previous) { - if (!Prev || !Prev->isOneOf(tok::kw_const, tok::identifier)) { + if (!Prev || + !Prev->isOneOf(tok::kw_const, tok::identifier, tok::coloncolon)) { IsCast = false; break; } @@ -1032,7 +1125,7 @@ private: if (NextToken->is(tok::l_square) && NextToken->isNot(TT_LambdaLSquare)) return TT_PointerOrReference; - if (NextToken->isOneOf(tok::kw_operator, tok::comma)) + if (NextToken->isOneOf(tok::kw_operator, tok::comma, tok::semi)) return TT_PointerOrReference; if (PrevToken->is(tok::r_paren) && PrevToken->MatchingParen && @@ -1108,10 +1201,16 @@ private: FormatToken *CurrentToken; bool AutoFound; const AdditionalKeywords &Keywords; + + // Set of "<" tokens that do not open a template parameter list. If parseAngle + // determines that a specific token can't be a template opener, it will make + // same decision irrespective of the decisions for tokens leading up to it. + // Store this information to prevent this from causing exponential runtime. + llvm::SmallPtrSet<FormatToken *, 16> NonTemplateLess; }; -static int PrecedenceUnaryOperator = prec::PointerToMember + 1; -static int PrecedenceArrowAndPeriod = prec::PointerToMember + 2; +static const int PrecedenceUnaryOperator = prec::PointerToMember + 1; +static const int PrecedenceArrowAndPeriod = prec::PointerToMember + 2; /// \brief Parses binary expressions by inserting fake parenthesis based on /// operator precedence. @@ -1361,12 +1460,13 @@ static bool isFunctionDeclarationName(const FormatToken &Current) { assert(Next->is(tok::l_paren)); if (Next->Next == Next->MatchingParen) return true; - for (const FormatToken *Tok = Next->Next; Tok != Next->MatchingParen; + for (const FormatToken *Tok = Next->Next; Tok && Tok != Next->MatchingParen; Tok = Tok->Next) { if (Tok->is(tok::kw_const) || Tok->isSimpleTypeSpecifier() || Tok->isOneOf(TT_PointerOrReference, TT_StartOfName)) return true; - if (Tok->isOneOf(tok::l_brace, tok::string_literal) || Tok->Tok.isLiteral()) + if (Tok->isOneOf(tok::l_brace, tok::string_literal, TT_ObjCMethodExpr) || + Tok->Tok.isLiteral()) return false; } return false; @@ -1502,7 +1602,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Left.is(tok::comma) && Left.NestingLevel == 0) return 3; } else if (Style.Language == FormatStyle::LK_JavaScript) { - if (Right.is(Keywords.kw_function)) + if (Right.is(Keywords.kw_function) && Left.isNot(tok::comma)) return 100; } @@ -1512,6 +1612,9 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Right.is(tok::l_square)) { if (Style.Language == FormatStyle::LK_Proto) return 1; + // Slightly prefer formatting local lambda definitions like functions. + if (Right.is(TT_LambdaLSquare) && Left.is(tok::equal)) + return 50; if (!Right.isOneOf(TT_ObjCMethodExpr, TT_LambdaLSquare)) return 500; } @@ -1521,11 +1624,15 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Line.First->is(tok::kw_for) && Right.PartOfMultiVariableDeclStmt) return 3; if (Left.is(TT_StartOfName)) - return 20; + return 110; if (InFunctionDecl && Right.NestingLevel == 0) return Style.PenaltyReturnTypeOnItsOwnLine; return 200; } + if (Right.is(TT_PointerOrReference)) + return 190; + if (Right.is(TT_TrailingReturnArrow)) + return 110; if (Left.is(tok::equal) && Right.is(tok::l_brace)) return 150; if (Left.is(TT_CastRParen)) @@ -1575,6 +1682,8 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Left.is(tok::l_paren) && InFunctionDecl && Style.AlignAfterOpenBracket) return 100; + if (Left.is(tok::l_paren) && Left.Previous && Left.Previous->is(tok::kw_if)) + return 1000; if (Left.is(tok::equal) && InFunctionDecl) return 110; if (Right.is(tok::r_brace)) @@ -1591,7 +1700,8 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, return 50; if (Right.is(tok::lessless)) { - if (Left.is(tok::string_literal)) { + if (Left.is(tok::string_literal) && + (!Right.LastOperator || Right.OperatorIndex != 1)) { StringRef Content = Left.TokenText; if (Content.startswith("\"")) Content = Content.drop_front(1); @@ -1607,7 +1717,9 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Left.is(TT_ConditionalExpr)) return prec::Conditional; prec::Level Level = Left.getPrecedence(); - + if (Level != prec::Unknown) + return Level; + Level = Right.getPrecedence(); if (Level != prec::Unknown) return Level; @@ -1636,7 +1748,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, if (Right.isOneOf(tok::semi, tok::comma)) return false; if (Right.is(tok::less) && - (Left.isOneOf(tok::kw_template, tok::r_paren) || + (Left.is(tok::kw_template) || (Line.Type == LT_ObjCDecl && Style.ObjCSpaceBeforeProtocolList))) return true; if (Left.isOneOf(tok::exclaim, tok::tilde)) @@ -1655,17 +1767,27 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, if (Left.is(tok::l_square) && Right.is(tok::amp)) return false; if (Right.is(TT_PointerOrReference)) - return Left.Tok.isLiteral() || - (!Left.isOneOf(TT_PointerOrReference, tok::l_paren) && - Style.PointerAlignment != FormatStyle::PAS_Left); + return !(Left.is(tok::r_paren) && Left.MatchingParen && + (Left.MatchingParen->is(TT_OverloadedOperatorLParen) || + (Left.MatchingParen->Previous && + Left.MatchingParen->Previous->is( + TT_FunctionDeclarationName)))) && + (Left.Tok.isLiteral() || + (!Left.isOneOf(TT_PointerOrReference, tok::l_paren) && + (Style.PointerAlignment != FormatStyle::PAS_Left || + Line.IsMultiVariableDeclStmt))); if (Right.is(TT_FunctionTypeLParen) && Left.isNot(tok::l_paren) && (!Left.is(TT_PointerOrReference) || - Style.PointerAlignment != FormatStyle::PAS_Right)) + (Style.PointerAlignment != FormatStyle::PAS_Right && + !Line.IsMultiVariableDeclStmt))) return true; if (Left.is(TT_PointerOrReference)) return Right.Tok.isLiteral() || Right.is(TT_BlockComment) || - (!Right.isOneOf(TT_PointerOrReference, tok::l_paren) && - Style.PointerAlignment != FormatStyle::PAS_Right && Left.Previous && + (!Right.isOneOf(TT_PointerOrReference, TT_ArraySubscriptLSquare, + tok::l_paren) && + (Style.PointerAlignment != FormatStyle::PAS_Right && + !Line.IsMultiVariableDeclStmt) && + Left.Previous && !Left.Previous->isOneOf(tok::l_paren, tok::coloncolon)); if (Right.is(tok::star) && Left.is(tok::l_paren)) return false; @@ -1700,13 +1822,12 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return Line.Type == LT_ObjCDecl || Left.is(tok::semi) || (Style.SpaceBeforeParens != FormatStyle::SBPO_Never && (Left.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, - tok::kw_switch, tok::kw_case) || - (Left.isOneOf(tok::kw_try, tok::kw_catch, tok::kw_new, - tok::kw_delete) && - (!Left.Previous || Left.Previous->isNot(tok::period))) || - Left.IsForEachMacro)) || + tok::kw_switch, tok::kw_case, TT_ForEachMacro) || + (Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch, + tok::kw_new, tok::kw_delete) && + (!Left.Previous || Left.Previous->isNot(tok::period))))) || (Style.SpaceBeforeParens == FormatStyle::SBPO_Always && - (Left.is(tok::identifier) || Left.isFunctionLikeKeyword()) && + (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || Left.is(tok::r_paren)) && Line.Type != LT_PreprocessorDirective); } if (Left.is(tok::at) && Right.Tok.getObjCKeywordID() != tok::objc_not_keyword) @@ -1748,6 +1869,20 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, } else if (Style.Language == FormatStyle::LK_JavaScript) { if (Left.is(Keywords.kw_var)) return true; + if (Right.isOneOf(TT_JsTypeColon, TT_JsTypeOptionalQuestion)) + return false; + if ((Left.is(tok::l_brace) || Right.is(tok::r_brace)) && + Line.First->isOneOf(Keywords.kw_import, tok::kw_export)) + return false; + if (Left.is(tok::ellipsis)) + return false; + if (Left.is(TT_TemplateCloser) && + !Right.isOneOf(tok::equal, tok::l_brace, tok::comma, tok::l_square, + Keywords.kw_implements, Keywords.kw_extends)) + // Type assertions ('<type>expr') are not followed by whitespace. Other + // locations that should have whitespace following are identified by the + // above set of follower tokens. + return false; } else if (Style.Language == FormatStyle::LK_Java) { if (Left.is(tok::r_square) && Right.is(tok::l_brace)) return true; @@ -1789,16 +1924,29 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return Right.is(tok::coloncolon); if (Right.is(TT_OverloadedOperatorLParen)) return false; - if (Right.is(tok::colon)) - return !Line.First->isOneOf(tok::kw_case, tok::kw_default) && - Right.getNextNonComment() && Right.isNot(TT_ObjCMethodExpr) && - !Left.is(tok::question) && - !(Right.is(TT_InlineASMColon) && Left.is(tok::coloncolon)) && - (Right.isNot(TT_DictLiteral) || Style.SpacesInContainerLiterals); + if (Right.is(tok::colon)) { + if (Line.First->isOneOf(tok::kw_case, tok::kw_default) || + !Right.getNextNonComment() || Right.getNextNonComment()->is(tok::semi)) + return false; + if (Right.is(TT_ObjCMethodExpr)) + return false; + if (Left.is(tok::question)) + return false; + if (Right.is(TT_InlineASMColon) && Left.is(tok::coloncolon)) + return false; + if (Right.is(TT_DictLiteral)) + return Style.SpacesInContainerLiterals; + return true; + } if (Left.is(TT_UnaryOperator)) return Right.is(TT_BinaryOperator); + + // If the next token is a binary operator or a selector name, we have + // incorrectly classified the parenthesis as a cast. FIXME: Detect correctly. if (Left.is(TT_CastRParen)) - return Style.SpaceAfterCStyleCast || Right.is(TT_BinaryOperator); + return Style.SpaceAfterCStyleCast || + Right.isOneOf(TT_BinaryOperator, TT_SelectorName); + if (Left.is(tok::greater) && Right.is(tok::greater)) { return Right.is(TT_TemplateCloser) && Left.is(TT_TemplateCloser) && (Style.Standard != FormatStyle::LS_Cpp11 || Style.SpacesInAngles); @@ -1819,7 +1967,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if ((Right.is(TT_BinaryOperator) && !Left.is(tok::l_paren)) || Left.isOneOf(TT_BinaryOperator, TT_ConditionalExpr)) return true; - if (Left.is(TT_TemplateCloser) && Right.is(tok::l_paren)) + if (Left.is(TT_TemplateCloser) && Right.is(tok::l_paren) && + Right.isNot(TT_FunctionTypeLParen)) return Style.SpaceBeforeParens == FormatStyle::SBPO_Always; if (Right.is(TT_TemplateOpener) && Left.is(tok::r_paren) && Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) @@ -1850,9 +1999,12 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // intention is to insert a line break after it in order to make shuffling // around entries easier. const FormatToken *BeforeClosingBrace = nullptr; - if (Left.is(tok::l_brace) && Left.BlockKind != BK_Block && Left.MatchingParen) + if (Left.isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) && + Left.BlockKind != BK_Block && Left.MatchingParen) BeforeClosingBrace = Left.MatchingParen->Previous; - else if (Right.is(tok::r_brace) && Right.BlockKind != BK_Block) + else if (Right.MatchingParen && + Right.MatchingParen->isOneOf(tok::l_brace, + TT_ArrayInitializerLSquare)) BeforeClosingBrace = &Left; if (BeforeClosingBrace && (BeforeClosingBrace->is(tok::comma) || BeforeClosingBrace->isTrailingComment())) @@ -1862,8 +2014,10 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return Left.BlockKind != BK_BracedInit && Left.isNot(TT_CtorInitializerColon) && (Right.NewlinesBefore > 0 && Right.HasUnescapedNewline); - if (Right.Previous->isTrailingComment() || - (Right.isStringLiteral() && Right.Previous->isStringLiteral())) + if (Left.isTrailingComment()) + return true; + if (Left.isStringLiteral() && + (Right.isStringLiteral() || Right.is(TT_ObjCStringLiteral))) return true; if (Right.Previous->IsUnterminatedLiteral) return true; @@ -1889,6 +2043,8 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, Style.Language == FormatStyle::LK_Proto) // Don't put enums onto single lines in protocol buffers. return true; + if (Right.is(TT_InlineASMBrace)) + return Right.HasUnescapedNewline; if (Style.Language == FormatStyle::LK_JavaScript && Right.is(tok::r_brace) && Left.is(tok::l_brace) && !Left.Children.empty()) // Support AllowShortFunctionsOnASingleLine for JavaScript. @@ -1903,8 +2059,12 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return true; if (Left.is(TT_ObjCBlockLBrace) && !Style.AllowShortBlocksOnASingleLine) return true; - if (Right.is(tok::lessless) && Left.is(tok::identifier) && - Left.TokenText == "endl") + + if ((Style.Language == FormatStyle::LK_Java || + Style.Language == FormatStyle::LK_JavaScript) && + Left.is(TT_LeadingJavaAnnotation) && + Right.isNot(TT_LeadingJavaAnnotation) && Right.isNot(tok::l_paren) && + Line.Last->is(tok::l_brace)) return true; if (Style.Language == FormatStyle::LK_JavaScript) { @@ -1913,13 +2073,15 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, Left.Previous->is(tok::char_constant)) return true; if (Left.is(TT_DictLiteral) && Left.is(tok::l_brace) && - Left.NestingLevel == 0) + Left.NestingLevel == 0 && Left.Previous && + Left.Previous->is(tok::equal) && + Line.First->isOneOf(tok::identifier, Keywords.kw_import, + tok::kw_export) && + // kw_var is a pseudo-token that's a tok::identifier, so matches above. + !Line.First->is(Keywords.kw_var)) + // Enum style object literal. return true; } else if (Style.Language == FormatStyle::LK_Java) { - if (Left.is(TT_LeadingJavaAnnotation) && - Right.isNot(TT_LeadingJavaAnnotation) && Right.isNot(tok::l_paren) && - Line.Last->is(tok::l_brace)) - return true; if (Right.is(tok::plus) && Left.is(tok::string_literal) && Right.Next && Right.Next->is(tok::string_literal)) return true; @@ -1947,9 +2109,15 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return false; if (Left.isOneOf(TT_JavaAnnotation, TT_LeadingJavaAnnotation)) return !Right.is(tok::l_paren); + if (Right.is(TT_PointerOrReference)) + return Line.IsMultiVariableDeclStmt || + (Style.PointerAlignment == FormatStyle::PAS_Right && + (!Right.Next || Right.Next->isNot(TT_FunctionDeclarationName))); if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName) || Right.is(tok::kw_operator)) return true; + if (Left.is(TT_PointerOrReference)) + return false; if (Right.isTrailingComment()) // We rely on MustBreakBefore being set correctly here as we should not // change the "binding" behavior of a comment. @@ -1970,8 +2138,9 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return false; if (Left.is(tok::colon) && (Left.isOneOf(TT_DictLiteral, TT_ObjCMethodExpr))) return true; - if (Right.is(TT_SelectorName)) - return true; + if (Right.is(TT_SelectorName) || (Right.is(tok::identifier) && Right.Next && + Right.Next->is(TT_ObjCMethodExpr))) + return Left.isNot(tok::period); // FIXME: Properly parse ObjC calls. if (Left.is(tok::r_paren) && Line.Type == LT_ObjCProperty) return true; if (Left.ClosesTemplateDeclaration) @@ -1983,17 +2152,16 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return true; if (Right.is(TT_RangeBasedForLoopColon)) return false; - if (Left.isOneOf(TT_PointerOrReference, TT_TemplateCloser, - TT_UnaryOperator) || + if (Left.isOneOf(TT_TemplateCloser, TT_UnaryOperator) || Left.is(tok::kw_operator)) return false; - if (Left.is(tok::equal) && Line.Type == LT_VirtualFunctionDecl) + if (Left.is(tok::equal) && !Right.isOneOf(tok::kw_default, tok::kw_delete) && + Line.Type == LT_VirtualFunctionDecl) return false; if (Left.is(tok::l_paren) && Left.is(TT_AttributeParen)) return false; if (Left.is(tok::l_paren) && Left.Previous && - (Left.Previous->isOneOf(TT_BinaryOperator, TT_CastRParen) || - Left.Previous->is(tok::kw_if))) + (Left.Previous->isOneOf(TT_BinaryOperator, TT_CastRParen))) return false; if (Right.is(TT_ImplicitStringLiteral)) return false; @@ -2027,8 +2195,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, if (Right.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializersBeforeComma) return true; - if (Left.is(tok::greater) && Right.is(tok::greater) && - Left.isNot(TT_TemplateCloser)) + if ((Left.is(tok::greater) && Right.is(tok::greater)) || + (Left.is(tok::less) && Right.is(tok::less))) return false; if (Right.is(TT_BinaryOperator) && Style.BreakBeforeBinaryOperators != FormatStyle::BOS_None && @@ -2046,8 +2214,9 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return true; return Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace, tok::kw_class, tok::kw_struct) || - Right.isMemberAccess() || Right.is(TT_TrailingReturnArrow) || - Right.isOneOf(tok::lessless, tok::colon, tok::l_square, tok::at) || + Right.isMemberAccess() || + Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow, tok::lessless, + tok::colon, tok::l_square, tok::at) || (Left.is(tok::r_paren) && Right.isOneOf(tok::identifier, tok::kw_const)) || (Left.is(tok::l_paren) && !Right.is(tok::r_paren)); |