diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -172,8 +172,6 @@ bool spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right); - bool spaceRequiredBefore(const AnnotatedLine &Line, const FormatToken &Right); - bool mustBreakBefore(const AnnotatedLine &Line, const FormatToken &Right); bool canBreakBefore(const AnnotatedLine &Line, const FormatToken &Right); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2733,7 +2733,7 @@ } } } else if (Current->SpacesRequiredBefore == 0 && - spaceRequiredBefore(Line, *Current)) { + spaceRequiredBetween(Line, *Prev, *Current)) { Current->SpacesRequiredBefore = 1; } @@ -3071,6 +3071,406 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right) { + // If the token is finalized don't touch it (as it could be in a + // clang-format-off section). + if (Left.Finalized) + return Right.hasWhitespaceBefore(); + + if (Right.Tok.getIdentifierInfo() && Left.Tok.getIdentifierInfo()) + return true; // Never ever merge two identifiers. + + // Leave a space between * and /* to avoid C4138 `comment end` found outside + // of comment. + if (Left.is(tok::star) && Right.is(tok::comment)) + return true; + + if (Style.isCpp()) { + // Space between import . + // or import .....; + if (Left.is(Keywords.kw_import) && Right.isOneOf(tok::less, tok::ellipsis)) + return true; + // Space between `module :` and `import :`. + if (Left.isOneOf(Keywords.kw_module, Keywords.kw_import) && + Right.is(TT_ModulePartitionColon)) + return true; + // No space between import foo:bar but keep a space between import :bar; + if (Left.is(tok::identifier) && Right.is(TT_ModulePartitionColon)) + return false; + // No space between :bar; + if (Left.is(TT_ModulePartitionColon) && + Right.isOneOf(tok::identifier, tok::kw_private)) + return false; + if (Left.is(tok::ellipsis) && Right.is(tok::identifier) && + Line.First->is(Keywords.kw_import)) + return false; + // Space in __attribute__((attr)) ::type. + if (Left.is(TT_AttributeParen) && Right.is(tok::coloncolon)) + return true; + + if (Left.is(tok::kw_operator)) + return Right.is(tok::coloncolon); + if (Right.is(tok::l_brace) && Right.is(BK_BracedInit) && + !Left.opensScope() && Style.SpaceBeforeCpp11BracedList) + return true; + if (Left.is(tok::less) && Left.is(TT_OverloadedOperator) && + Right.is(TT_TemplateOpener)) + return true; + } else if (Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) { + if (Right.is(tok::period) && + Left.isOneOf(Keywords.kw_optional, Keywords.kw_required, + Keywords.kw_repeated, Keywords.kw_extend)) + return true; + if (Right.is(tok::l_paren) && + Left.isOneOf(Keywords.kw_returns, Keywords.kw_option)) + return true; + if (Right.isOneOf(tok::l_brace, tok::less) && Left.is(TT_SelectorName)) + return true; + // Slashes occur in text protocol extension syntax: [type/type] { ... }. + if (Left.is(tok::slash) || Right.is(tok::slash)) + return false; + if (Left.MatchingParen && + Left.MatchingParen->is(TT_ProtoExtensionLSquare) && + Right.isOneOf(tok::l_brace, tok::less)) + return !Style.Cpp11BracedListStyle; + // A percent is probably part of a formatting specification, such as %lld. + if (Left.is(tok::percent)) + return false; + // Preserve the existence of a space before a percent for cases like 0x%04x + // and "%d %d" + if (Left.is(tok::numeric_constant) && Right.is(tok::percent)) + return Right.hasWhitespaceBefore(); + } else if (Style.isJson()) { + if (Right.is(tok::colon)) + return false; + } else if (Style.isCSharp()) { + // Require spaces around '{' and before '}' unless they appear in + // interpolated strings. Interpolated strings are merged into a single token + // so cannot have spaces inserted by this function. + + // No space between 'this' and '[' + if (Left.is(tok::kw_this) && Right.is(tok::l_square)) + return false; + + // No space between 'new' and '(' + if (Left.is(tok::kw_new) && Right.is(tok::l_paren)) + return false; + + // Space before { (including space within '{ {'). + if (Right.is(tok::l_brace)) + return true; + + // Spaces inside braces. + if (Left.is(tok::l_brace) && Right.isNot(tok::r_brace)) + return true; + + if (Left.isNot(tok::l_brace) && Right.is(tok::r_brace)) + return true; + + // Spaces around '=>'. + if (Left.is(TT_FatArrow) || Right.is(TT_FatArrow)) + return true; + + // No spaces around attribute target colons + if (Left.is(TT_AttributeColon) || Right.is(TT_AttributeColon)) + return false; + + // space between type and variable e.g. Dictionary foo; + if (Left.is(TT_TemplateCloser) && Right.is(TT_StartOfName)) + return true; + + // spaces inside square brackets. + if (Left.is(tok::l_square) || Right.is(tok::r_square)) + return Style.SpacesInSquareBrackets; + + // No space before ? in nullable types. + if (Right.is(TT_CSharpNullable)) + return false; + + // No space before null forgiving '!'. + if (Right.is(TT_NonNullAssertion)) + return false; + + // No space between consecutive commas '[,,]'. + if (Left.is(tok::comma) && Right.is(tok::comma)) + return false; + + // space after var in `var (key, value)` + if (Left.is(Keywords.kw_var) && Right.is(tok::l_paren)) + return true; + + // space between keywords and paren e.g. "using (" + if (Right.is(tok::l_paren)) + if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when, + Keywords.kw_lock)) + return Style.SpaceBeforeParensOptions.AfterControlStatements || + spaceRequiredBeforeParens(Right); + + // space between method modifier and opening parenthesis of a tuple return + // type + if (Left.isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected, + tok::kw_virtual, tok::kw_extern, tok::kw_static, + Keywords.kw_internal, Keywords.kw_abstract, + Keywords.kw_sealed, Keywords.kw_override, + Keywords.kw_async, Keywords.kw_unsafe) && + Right.is(tok::l_paren)) + return true; + } else if (Style.isJavaScript()) { + if (Left.is(TT_FatArrow)) + return true; + // for await ( ... + if (Right.is(tok::l_paren) && Left.is(Keywords.kw_await) && Left.Previous && + Left.Previous->is(tok::kw_for)) + return true; + if (Left.is(Keywords.kw_async) && Right.is(tok::l_paren) && + Right.MatchingParen) { + const FormatToken *Next = Right.MatchingParen->getNextNonComment(); + // An async arrow function, for example: `x = async () => foo();`, + // as opposed to calling a function called async: `x = async();` + if (Next && Next->is(TT_FatArrow)) + return true; + } + if ((Left.is(TT_TemplateString) && Left.TokenText.endswith("${")) || + (Right.is(TT_TemplateString) && Right.TokenText.startswith("}"))) + return false; + // In tagged template literals ("html`bar baz`"), there is no space between + // the tag identifier and the template string. + if (Keywords.IsJavaScriptIdentifier(Left, + /* AcceptIdentifierName= */ false) && + Right.is(TT_TemplateString)) + return false; + if (Right.is(tok::star) && + Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) + return false; + if (Right.isOneOf(tok::l_brace, tok::l_square) && + Left.isOneOf(Keywords.kw_function, Keywords.kw_yield, + Keywords.kw_extends, Keywords.kw_implements)) + return true; + if (Right.is(tok::l_paren)) { + // JS methods can use some keywords as names (e.g. `delete()`). + if (Line.MustBeDeclaration && Left.Tok.getIdentifierInfo()) + return false; + // Valid JS method names can include keywords, e.g. `foo.delete()` or + // `bar.instanceof()`. Recognize call positions by preceding period. + if (Left.Previous && Left.Previous->is(tok::period) && + Left.Tok.getIdentifierInfo()) + return false; + // Additional unary JavaScript operators that need a space after. + if (Left.isOneOf(tok::kw_throw, Keywords.kw_await, Keywords.kw_typeof, + tok::kw_void)) + return true; + } + // `foo as const;` casts into a const type. + if (Left.endsSequence(tok::kw_const, Keywords.kw_as)) + return false; + if ((Left.isOneOf(Keywords.kw_let, Keywords.kw_var, Keywords.kw_in, + tok::kw_const) || + // "of" is only a keyword if it appears after another identifier + // (e.g. as "const x of y" in a for loop), or after a destructuring + // operation (const [x, y] of z, const {a, b} of c). + (Left.is(Keywords.kw_of) && Left.Previous && + (Left.Previous->is(tok::identifier) || + Left.Previous->isOneOf(tok::r_square, tok::r_brace)))) && + (!Left.Previous || !Left.Previous->is(tok::period))) + return true; + if (Left.isOneOf(tok::kw_for, Keywords.kw_as) && Left.Previous && + Left.Previous->is(tok::period) && Right.is(tok::l_paren)) + return false; + if (Left.is(Keywords.kw_as) && + Right.isOneOf(tok::l_square, tok::l_brace, tok::l_paren)) + return true; + if (Left.is(tok::kw_default) && Left.Previous && + Left.Previous->is(tok::kw_export)) + return true; + if (Left.is(Keywords.kw_is) && Right.is(tok::l_brace)) + return true; + if (Right.isOneOf(TT_JsTypeColon, TT_JsTypeOptionalQuestion)) + return false; + if (Left.is(TT_JsTypeOperator) || Right.is(TT_JsTypeOperator)) + 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 ('expr') are not followed by whitespace. Other + // locations that should have whitespace following are identified by the + // above set of follower tokens. + return false; + if (Right.is(TT_NonNullAssertion)) + return false; + if (Left.is(TT_NonNullAssertion) && + Right.isOneOf(Keywords.kw_as, Keywords.kw_in)) + return true; // "x! as string", "x! in y" + } else if (Style.Language == FormatStyle::LK_Java) { + if (Left.is(tok::r_square) && Right.is(tok::l_brace)) + return true; + if (Left.is(Keywords.kw_synchronized) && Right.is(tok::l_paren)) + return Style.SpaceBeforeParensOptions.AfterControlStatements || + spaceRequiredBeforeParens(Right); + if ((Left.isOneOf(tok::kw_static, tok::kw_public, tok::kw_private, + tok::kw_protected) || + Left.isOneOf(Keywords.kw_final, Keywords.kw_abstract, + Keywords.kw_native)) && + Right.is(TT_TemplateOpener)) + return true; + } + if (Left.is(TT_ImplicitStringLiteral)) + return Right.hasWhitespaceBefore(); + if (Line.Type == LT_ObjCMethodDecl) { + if (Left.is(TT_ObjCMethodSpecifier)) + return true; + if (Left.is(tok::r_paren) && canBeObjCSelectorComponent(Right)) + // Don't space between ')' and or ')' and 'new'. 'new' is not a + // keyword in Objective-C, and '+ (instancetype)new;' is a standard class + // method declaration. + return false; + } + if (Line.Type == LT_ObjCProperty && + (Right.is(tok::equal) || Left.is(tok::equal))) + return false; + + if (Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow) || + Left.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow)) + return true; + if (Left.is(tok::comma) && !Right.is(TT_OverloadedOperatorLParen)) + return true; + if (Right.is(tok::comma)) + return false; + if (Right.is(TT_ObjCBlockLParen)) + return true; + if (Right.is(TT_CtorInitializerColon)) + return Style.SpaceBeforeCtorInitializerColon; + if (Right.is(TT_InheritanceColon) && !Style.SpaceBeforeInheritanceColon) + return false; + if (Right.is(TT_RangeBasedForLoopColon) && + !Style.SpaceBeforeRangeBasedForLoopColon) + return false; + if (Left.is(TT_BitFieldColon)) + return Style.BitFieldColonSpacing == FormatStyle::BFCS_Both || + Style.BitFieldColonSpacing == FormatStyle::BFCS_After; + if (Right.is(tok::colon)) { + if (Line.First->isOneOf(tok::kw_default, tok::kw_case)) + return Style.SpaceBeforeCaseColon; + const FormatToken *Next = Right.getNextNonComment(); + if (!Next || Next->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; + if (Right.is(TT_AttributeColon)) + return false; + if (Right.is(TT_CSharpNamedArgumentColon)) + return false; + if (Right.is(TT_BitFieldColon)) + return Style.BitFieldColonSpacing == FormatStyle::BFCS_Both || + Style.BitFieldColonSpacing == FormatStyle::BFCS_Before; + return true; + } + // Do not merge "- -" into "--". + if ((Left.isOneOf(tok::minus, tok::minusminus) && + Right.isOneOf(tok::minus, tok::minusminus)) || + (Left.isOneOf(tok::plus, tok::plusplus) && + Right.isOneOf(tok::plus, tok::plusplus))) + return true; + if (Left.is(TT_UnaryOperator)) { + if (!Right.is(tok::l_paren)) { + // The alternative operators for ~ and ! are "compl" and "not". + // If they are used instead, we do not want to combine them with + // the token to the right, unless that is a left paren. + if (Left.is(tok::exclaim) && Left.TokenText == "not") + return true; + if (Left.is(tok::tilde) && Left.TokenText == "compl") + return true; + // Lambda captures allow for a lone &, so "&]" needs to be properly + // handled. + if (Left.is(tok::amp) && Right.is(tok::r_square)) + return Style.SpacesInSquareBrackets; + } + return (Style.SpaceAfterLogicalNot && Left.is(tok::exclaim)) || + 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.isOneOf(TT_BinaryOperator, TT_SelectorName); + + auto ShouldAddSpacesInAngles = [this, &Right]() { + if (this->Style.SpacesInAngles == FormatStyle::SIAS_Always) + return true; + if (this->Style.SpacesInAngles == FormatStyle::SIAS_Leave) + return Right.hasWhitespaceBefore(); + return false; + }; + + if (Left.is(tok::greater) && Right.is(tok::greater)) { + if (Style.Language == FormatStyle::LK_TextProto || + (Style.Language == FormatStyle::LK_Proto && Left.is(TT_DictLiteral))) + return !Style.Cpp11BracedListStyle; + return Right.is(TT_TemplateCloser) && Left.is(TT_TemplateCloser) && + ((Style.Standard < FormatStyle::LS_Cpp11) || + ShouldAddSpacesInAngles()); + } + if (Right.isOneOf(tok::arrow, tok::arrowstar, tok::periodstar) || + Left.isOneOf(tok::arrow, tok::period, tok::arrowstar, tok::periodstar) || + (Right.is(tok::period) && Right.isNot(TT_DesignatedInitializerPeriod))) + return false; + if (!Style.SpaceBeforeAssignmentOperators && Left.isNot(TT_TemplateCloser) && + Right.getPrecedence() == prec::Assignment) + return false; + if (Style.Language == FormatStyle::LK_Java && Right.is(tok::coloncolon) && + (Left.is(tok::identifier) || Left.is(tok::kw_this))) + return false; + if (Right.is(tok::coloncolon) && Left.is(tok::identifier)) + // Generally don't remove existing spaces between an identifier and "::". + // The identifier might actually be a macro name such as ALWAYS_INLINE. If + // this turns out to be too lenient, add analysis of the identifier itself. + return Right.hasWhitespaceBefore(); + if (Right.is(tok::coloncolon) && + !Left.isOneOf(tok::l_brace, tok::comment, tok::l_paren)) + // Put a space between < and :: in vector< ::std::string > + return (Left.is(TT_TemplateOpener) && + ((Style.Standard < FormatStyle::LS_Cpp11) || + ShouldAddSpacesInAngles())) || + !(Left.isOneOf(tok::l_paren, tok::r_paren, tok::l_square, + tok::kw___super, TT_TemplateOpener, + TT_TemplateCloser)) || + (Left.is(tok::l_paren) && Style.SpacesInParentheses); + if ((Left.is(TT_TemplateOpener)) != (Right.is(TT_TemplateCloser))) + return ShouldAddSpacesInAngles(); + // Space before TT_StructuredBindingLSquare. + if (Right.is(TT_StructuredBindingLSquare)) + return !Left.isOneOf(tok::amp, tok::ampamp) || + getTokenReferenceAlignment(Left) != FormatStyle::PAS_Right; + // Space before & or && following a TT_StructuredBindingLSquare. + if (Right.Next && Right.Next->is(TT_StructuredBindingLSquare) && + Right.isOneOf(tok::amp, tok::ampamp)) + return getTokenReferenceAlignment(Right) != FormatStyle::PAS_Left; + if ((Right.is(TT_BinaryOperator) && !Left.is(tok::l_paren)) || + (Left.isOneOf(TT_BinaryOperator, TT_ConditionalExpr) && + !Right.is(tok::r_paren))) + return true; + if (Right.is(TT_TemplateOpener) && Left.is(tok::r_paren) && + Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) + return false; + if (Right.is(tok::less) && Left.isNot(tok::l_paren) && + Line.startsWith(tok::hash)) + return true; + if (Right.is(TT_TrailingUnaryOperator)) + return false; + if (Left.is(TT_RegexLiteral)) + return false; + if (Left.is(tok::kw_return) && !Right.isOneOf(tok::semi, tok::r_paren, tok::hashhash)) return true; @@ -3448,412 +3848,6 @@ return true; } -bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, - const FormatToken &Right) { - const FormatToken &Left = *Right.Previous; - - // If the token is finalized don't touch it (as it could be in a - // clang-format-off section). - if (Left.Finalized) - return Right.hasWhitespaceBefore(); - - if (Right.Tok.getIdentifierInfo() && Left.Tok.getIdentifierInfo()) - return true; // Never ever merge two identifiers. - - // Leave a space between * and /* to avoid C4138 `comment end` found outside - // of comment. - if (Left.is(tok::star) && Right.is(tok::comment)) - return true; - - if (Style.isCpp()) { - // Space between import . - // or import .....; - if (Left.is(Keywords.kw_import) && Right.isOneOf(tok::less, tok::ellipsis)) - return true; - // Space between `module :` and `import :`. - if (Left.isOneOf(Keywords.kw_module, Keywords.kw_import) && - Right.is(TT_ModulePartitionColon)) - return true; - // No space between import foo:bar but keep a space between import :bar; - if (Left.is(tok::identifier) && Right.is(TT_ModulePartitionColon)) - return false; - // No space between :bar; - if (Left.is(TT_ModulePartitionColon) && - Right.isOneOf(tok::identifier, tok::kw_private)) - return false; - if (Left.is(tok::ellipsis) && Right.is(tok::identifier) && - Line.First->is(Keywords.kw_import)) - return false; - // Space in __attribute__((attr)) ::type. - if (Left.is(TT_AttributeParen) && Right.is(tok::coloncolon)) - return true; - - if (Left.is(tok::kw_operator)) - return Right.is(tok::coloncolon); - if (Right.is(tok::l_brace) && Right.is(BK_BracedInit) && - !Left.opensScope() && Style.SpaceBeforeCpp11BracedList) - return true; - if (Left.is(tok::less) && Left.is(TT_OverloadedOperator) && - Right.is(TT_TemplateOpener)) - return true; - } else if (Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TextProto) { - if (Right.is(tok::period) && - Left.isOneOf(Keywords.kw_optional, Keywords.kw_required, - Keywords.kw_repeated, Keywords.kw_extend)) - return true; - if (Right.is(tok::l_paren) && - Left.isOneOf(Keywords.kw_returns, Keywords.kw_option)) - return true; - if (Right.isOneOf(tok::l_brace, tok::less) && Left.is(TT_SelectorName)) - return true; - // Slashes occur in text protocol extension syntax: [type/type] { ... }. - if (Left.is(tok::slash) || Right.is(tok::slash)) - return false; - if (Left.MatchingParen && - Left.MatchingParen->is(TT_ProtoExtensionLSquare) && - Right.isOneOf(tok::l_brace, tok::less)) - return !Style.Cpp11BracedListStyle; - // A percent is probably part of a formatting specification, such as %lld. - if (Left.is(tok::percent)) - return false; - // Preserve the existence of a space before a percent for cases like 0x%04x - // and "%d %d" - if (Left.is(tok::numeric_constant) && Right.is(tok::percent)) - return Right.hasWhitespaceBefore(); - } else if (Style.isJson()) { - if (Right.is(tok::colon)) - return false; - } else if (Style.isCSharp()) { - // Require spaces around '{' and before '}' unless they appear in - // interpolated strings. Interpolated strings are merged into a single token - // so cannot have spaces inserted by this function. - - // No space between 'this' and '[' - if (Left.is(tok::kw_this) && Right.is(tok::l_square)) - return false; - - // No space between 'new' and '(' - if (Left.is(tok::kw_new) && Right.is(tok::l_paren)) - return false; - - // Space before { (including space within '{ {'). - if (Right.is(tok::l_brace)) - return true; - - // Spaces inside braces. - if (Left.is(tok::l_brace) && Right.isNot(tok::r_brace)) - return true; - - if (Left.isNot(tok::l_brace) && Right.is(tok::r_brace)) - return true; - - // Spaces around '=>'. - if (Left.is(TT_FatArrow) || Right.is(TT_FatArrow)) - return true; - - // No spaces around attribute target colons - if (Left.is(TT_AttributeColon) || Right.is(TT_AttributeColon)) - return false; - - // space between type and variable e.g. Dictionary foo; - if (Left.is(TT_TemplateCloser) && Right.is(TT_StartOfName)) - return true; - - // spaces inside square brackets. - if (Left.is(tok::l_square) || Right.is(tok::r_square)) - return Style.SpacesInSquareBrackets; - - // No space before ? in nullable types. - if (Right.is(TT_CSharpNullable)) - return false; - - // No space before null forgiving '!'. - if (Right.is(TT_NonNullAssertion)) - return false; - - // No space between consecutive commas '[,,]'. - if (Left.is(tok::comma) && Right.is(tok::comma)) - return false; - - // space after var in `var (key, value)` - if (Left.is(Keywords.kw_var) && Right.is(tok::l_paren)) - return true; - - // space between keywords and paren e.g. "using (" - if (Right.is(tok::l_paren)) - if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when, - Keywords.kw_lock)) - return Style.SpaceBeforeParensOptions.AfterControlStatements || - spaceRequiredBeforeParens(Right); - - // space between method modifier and opening parenthesis of a tuple return - // type - if (Left.isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected, - tok::kw_virtual, tok::kw_extern, tok::kw_static, - Keywords.kw_internal, Keywords.kw_abstract, - Keywords.kw_sealed, Keywords.kw_override, - Keywords.kw_async, Keywords.kw_unsafe) && - Right.is(tok::l_paren)) - return true; - } else if (Style.isJavaScript()) { - if (Left.is(TT_FatArrow)) - return true; - // for await ( ... - if (Right.is(tok::l_paren) && Left.is(Keywords.kw_await) && Left.Previous && - Left.Previous->is(tok::kw_for)) - return true; - if (Left.is(Keywords.kw_async) && Right.is(tok::l_paren) && - Right.MatchingParen) { - const FormatToken *Next = Right.MatchingParen->getNextNonComment(); - // An async arrow function, for example: `x = async () => foo();`, - // as opposed to calling a function called async: `x = async();` - if (Next && Next->is(TT_FatArrow)) - return true; - } - if ((Left.is(TT_TemplateString) && Left.TokenText.endswith("${")) || - (Right.is(TT_TemplateString) && Right.TokenText.startswith("}"))) - return false; - // In tagged template literals ("html`bar baz`"), there is no space between - // the tag identifier and the template string. - if (Keywords.IsJavaScriptIdentifier(Left, - /* AcceptIdentifierName= */ false) && - Right.is(TT_TemplateString)) - return false; - if (Right.is(tok::star) && - Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) - return false; - if (Right.isOneOf(tok::l_brace, tok::l_square) && - Left.isOneOf(Keywords.kw_function, Keywords.kw_yield, - Keywords.kw_extends, Keywords.kw_implements)) - return true; - if (Right.is(tok::l_paren)) { - // JS methods can use some keywords as names (e.g. `delete()`). - if (Line.MustBeDeclaration && Left.Tok.getIdentifierInfo()) - return false; - // Valid JS method names can include keywords, e.g. `foo.delete()` or - // `bar.instanceof()`. Recognize call positions by preceding period. - if (Left.Previous && Left.Previous->is(tok::period) && - Left.Tok.getIdentifierInfo()) - return false; - // Additional unary JavaScript operators that need a space after. - if (Left.isOneOf(tok::kw_throw, Keywords.kw_await, Keywords.kw_typeof, - tok::kw_void)) - return true; - } - // `foo as const;` casts into a const type. - if (Left.endsSequence(tok::kw_const, Keywords.kw_as)) - return false; - if ((Left.isOneOf(Keywords.kw_let, Keywords.kw_var, Keywords.kw_in, - tok::kw_const) || - // "of" is only a keyword if it appears after another identifier - // (e.g. as "const x of y" in a for loop), or after a destructuring - // operation (const [x, y] of z, const {a, b} of c). - (Left.is(Keywords.kw_of) && Left.Previous && - (Left.Previous->is(tok::identifier) || - Left.Previous->isOneOf(tok::r_square, tok::r_brace)))) && - (!Left.Previous || !Left.Previous->is(tok::period))) - return true; - if (Left.isOneOf(tok::kw_for, Keywords.kw_as) && Left.Previous && - Left.Previous->is(tok::period) && Right.is(tok::l_paren)) - return false; - if (Left.is(Keywords.kw_as) && - Right.isOneOf(tok::l_square, tok::l_brace, tok::l_paren)) - return true; - if (Left.is(tok::kw_default) && Left.Previous && - Left.Previous->is(tok::kw_export)) - return true; - if (Left.is(Keywords.kw_is) && Right.is(tok::l_brace)) - return true; - if (Right.isOneOf(TT_JsTypeColon, TT_JsTypeOptionalQuestion)) - return false; - if (Left.is(TT_JsTypeOperator) || Right.is(TT_JsTypeOperator)) - 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 ('expr') are not followed by whitespace. Other - // locations that should have whitespace following are identified by the - // above set of follower tokens. - return false; - if (Right.is(TT_NonNullAssertion)) - return false; - if (Left.is(TT_NonNullAssertion) && - Right.isOneOf(Keywords.kw_as, Keywords.kw_in)) - return true; // "x! as string", "x! in y" - } else if (Style.Language == FormatStyle::LK_Java) { - if (Left.is(tok::r_square) && Right.is(tok::l_brace)) - return true; - if (Left.is(Keywords.kw_synchronized) && Right.is(tok::l_paren)) - return Style.SpaceBeforeParensOptions.AfterControlStatements || - spaceRequiredBeforeParens(Right); - if ((Left.isOneOf(tok::kw_static, tok::kw_public, tok::kw_private, - tok::kw_protected) || - Left.isOneOf(Keywords.kw_final, Keywords.kw_abstract, - Keywords.kw_native)) && - Right.is(TT_TemplateOpener)) - return true; - } - if (Left.is(TT_ImplicitStringLiteral)) - return Right.hasWhitespaceBefore(); - if (Line.Type == LT_ObjCMethodDecl) { - if (Left.is(TT_ObjCMethodSpecifier)) - return true; - if (Left.is(tok::r_paren) && canBeObjCSelectorComponent(Right)) - // Don't space between ')' and or ')' and 'new'. 'new' is not a - // keyword in Objective-C, and '+ (instancetype)new;' is a standard class - // method declaration. - return false; - } - if (Line.Type == LT_ObjCProperty && - (Right.is(tok::equal) || Left.is(tok::equal))) - return false; - - if (Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow) || - Left.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow)) - return true; - if (Left.is(tok::comma) && !Right.is(TT_OverloadedOperatorLParen)) - return true; - if (Right.is(tok::comma)) - return false; - if (Right.is(TT_ObjCBlockLParen)) - return true; - if (Right.is(TT_CtorInitializerColon)) - return Style.SpaceBeforeCtorInitializerColon; - if (Right.is(TT_InheritanceColon) && !Style.SpaceBeforeInheritanceColon) - return false; - if (Right.is(TT_RangeBasedForLoopColon) && - !Style.SpaceBeforeRangeBasedForLoopColon) - return false; - if (Left.is(TT_BitFieldColon)) - return Style.BitFieldColonSpacing == FormatStyle::BFCS_Both || - Style.BitFieldColonSpacing == FormatStyle::BFCS_After; - if (Right.is(tok::colon)) { - if (Line.First->isOneOf(tok::kw_default, tok::kw_case)) - return Style.SpaceBeforeCaseColon; - const FormatToken *Next = Right.getNextNonComment(); - if (!Next || Next->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; - if (Right.is(TT_AttributeColon)) - return false; - if (Right.is(TT_CSharpNamedArgumentColon)) - return false; - if (Right.is(TT_BitFieldColon)) - return Style.BitFieldColonSpacing == FormatStyle::BFCS_Both || - Style.BitFieldColonSpacing == FormatStyle::BFCS_Before; - return true; - } - // Do not merge "- -" into "--". - if ((Left.isOneOf(tok::minus, tok::minusminus) && - Right.isOneOf(tok::minus, tok::minusminus)) || - (Left.isOneOf(tok::plus, tok::plusplus) && - Right.isOneOf(tok::plus, tok::plusplus))) - return true; - if (Left.is(TT_UnaryOperator)) { - if (!Right.is(tok::l_paren)) { - // The alternative operators for ~ and ! are "compl" and "not". - // If they are used instead, we do not want to combine them with - // the token to the right, unless that is a left paren. - if (Left.is(tok::exclaim) && Left.TokenText == "not") - return true; - if (Left.is(tok::tilde) && Left.TokenText == "compl") - return true; - // Lambda captures allow for a lone &, so "&]" needs to be properly - // handled. - if (Left.is(tok::amp) && Right.is(tok::r_square)) - return Style.SpacesInSquareBrackets; - } - return (Style.SpaceAfterLogicalNot && Left.is(tok::exclaim)) || - 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.isOneOf(TT_BinaryOperator, TT_SelectorName); - - auto ShouldAddSpacesInAngles = [this, &Right]() { - if (this->Style.SpacesInAngles == FormatStyle::SIAS_Always) - return true; - if (this->Style.SpacesInAngles == FormatStyle::SIAS_Leave) - return Right.hasWhitespaceBefore(); - return false; - }; - - if (Left.is(tok::greater) && Right.is(tok::greater)) { - if (Style.Language == FormatStyle::LK_TextProto || - (Style.Language == FormatStyle::LK_Proto && Left.is(TT_DictLiteral))) - return !Style.Cpp11BracedListStyle; - return Right.is(TT_TemplateCloser) && Left.is(TT_TemplateCloser) && - ((Style.Standard < FormatStyle::LS_Cpp11) || - ShouldAddSpacesInAngles()); - } - if (Right.isOneOf(tok::arrow, tok::arrowstar, tok::periodstar) || - Left.isOneOf(tok::arrow, tok::period, tok::arrowstar, tok::periodstar) || - (Right.is(tok::period) && Right.isNot(TT_DesignatedInitializerPeriod))) - return false; - if (!Style.SpaceBeforeAssignmentOperators && Left.isNot(TT_TemplateCloser) && - Right.getPrecedence() == prec::Assignment) - return false; - if (Style.Language == FormatStyle::LK_Java && Right.is(tok::coloncolon) && - (Left.is(tok::identifier) || Left.is(tok::kw_this))) - return false; - if (Right.is(tok::coloncolon) && Left.is(tok::identifier)) - // Generally don't remove existing spaces between an identifier and "::". - // The identifier might actually be a macro name such as ALWAYS_INLINE. If - // this turns out to be too lenient, add analysis of the identifier itself. - return Right.hasWhitespaceBefore(); - if (Right.is(tok::coloncolon) && - !Left.isOneOf(tok::l_brace, tok::comment, tok::l_paren)) - // Put a space between < and :: in vector< ::std::string > - return (Left.is(TT_TemplateOpener) && - ((Style.Standard < FormatStyle::LS_Cpp11) || - ShouldAddSpacesInAngles())) || - !(Left.isOneOf(tok::l_paren, tok::r_paren, tok::l_square, - tok::kw___super, TT_TemplateOpener, - TT_TemplateCloser)) || - (Left.is(tok::l_paren) && Style.SpacesInParentheses); - if ((Left.is(TT_TemplateOpener)) != (Right.is(TT_TemplateCloser))) - return ShouldAddSpacesInAngles(); - // Space before TT_StructuredBindingLSquare. - if (Right.is(TT_StructuredBindingLSquare)) - return !Left.isOneOf(tok::amp, tok::ampamp) || - getTokenReferenceAlignment(Left) != FormatStyle::PAS_Right; - // Space before & or && following a TT_StructuredBindingLSquare. - if (Right.Next && Right.Next->is(TT_StructuredBindingLSquare) && - Right.isOneOf(tok::amp, tok::ampamp)) - return getTokenReferenceAlignment(Right) != FormatStyle::PAS_Left; - if ((Right.is(TT_BinaryOperator) && !Left.is(tok::l_paren)) || - (Left.isOneOf(TT_BinaryOperator, TT_ConditionalExpr) && - !Right.is(tok::r_paren))) - return true; - if (Right.is(TT_TemplateOpener) && Left.is(tok::r_paren) && - Left.MatchingParen && Left.MatchingParen->is(TT_OverloadedOperatorLParen)) - return false; - if (Right.is(tok::less) && Left.isNot(tok::l_paren) && - Line.startsWith(tok::hash)) - return true; - if (Right.is(TT_TrailingUnaryOperator)) - return false; - if (Left.is(TT_RegexLiteral)) - return false; - return spaceRequiredBetween(Line, Left, Right); -} - // Returns 'true' if 'Tok' is a brace we'd want to break before in Allman style. static bool isAllmanBrace(const FormatToken &Tok) { return Tok.is(tok::l_brace) && Tok.is(BK_Block) &&