Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -2204,6 +2204,18 @@ return true; } +// Returns true if Tok is a postfix non-null assertion operator, as in +// `foo!.bar()`. +bool isJSNonNullOperator(const FormatToken &Tok) { + if (!Tok.Previous) + return false; + const FormatToken &Previous = *Tok.Previous; + return Tok.is(tok::exclaim) && + (Previous.isOneOf(tok::identifier, tok::r_paren, tok::r_square, + tok::r_brace) || + Previous.Tok.isLiteral()); +} + bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, const FormatToken &Right) { const FormatToken &Left = *Right.Previous; @@ -2277,10 +2289,7 @@ // locations that should have whitespace following are identified by the // above set of follower tokens. return false; - // Postfix non-null assertion operator, as in `foo!.bar()`. - if (Right.is(tok::exclaim) && (Left.isOneOf(tok::identifier, tok::r_paren, - tok::r_square, tok::r_brace) || - Left.Tok.isLiteral())) + if (isJSNonNullOperator(Right)) return false; if (Left.is(tok::exclaim) && Right.is(Keywords.kw_as)) return true; // "x! as string" @@ -2535,6 +2544,8 @@ return false; // must not break before as in 'x as type' casts if (Left.is(Keywords.kw_as)) return true; + if (isJSNonNullOperator(Left)) + return true; if (Left.is(Keywords.kw_declare) && Right.isOneOf(Keywords.kw_module, tok::kw_namespace, Keywords.kw_function, tok::kw_class, tok::kw_enum, Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -1679,6 +1679,12 @@ verifyFormat("let x = (foo)!;\n"); verifyFormat("let x = foo! - 1;\n"); verifyFormat("let x = {foo: 1}!;\n"); + verifyFormat( + "let x = hello.foo()!\n" + " .foo()!\n" + " .foo()!\n" + " .foo()!;\n", + getGoogleJSStyleWithColumns(20)); } TEST_F(FormatTestJS, Conditional) {