clang-format: [JS] allow breaking after non-null assertions.

Summary:
Previously clang-format would not break after any !. However in TypeScript, ! can be used as a post fix operator for non-nullability:
    x.foo()!.bar()!;

With this change, clang-format will wrap after the ! if it is likely a post-fix non null operator.

Reviewers: djasper

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D30705

llvm-svn: 297606
This commit is contained in:
Martin Probst 2017-03-13 09:14:23 +00:00
parent b98ab89ebb
commit 22b8d26924
3 changed files with 28 additions and 5 deletions

View File

@ -54,6 +54,7 @@ namespace format {
TYPE(JavaAnnotation) \
TYPE(JsComputedPropertyName) \
TYPE(JsFatArrow) \
TYPE(JsNonNullAssertion) \
TYPE(JsTypeColon) \
TYPE(JsTypeOperator) \
TYPE(JsTypeOptionalQuestion) \

View File

@ -1030,6 +1030,23 @@ private:
// The token type is already known.
return;
if (Style.Language == FormatStyle::LK_JavaScript) {
if (Current.is(tok::exclaim)) {
if (Current.Previous &&
(Current.Previous->isOneOf(tok::identifier, tok::r_paren,
tok::r_square, tok::r_brace) ||
Current.Previous->Tok.isLiteral())) {
Current.Type = TT_JsNonNullAssertion;
return;
}
if (Current.Next &&
Current.Next->isOneOf(TT_BinaryOperator, Keywords.kw_as)) {
Current.Type = TT_JsNonNullAssertion;
return;
}
}
}
// Line.MightBeFunctionDecl can only be true after the parentheses of a
// function declaration have been found. In this case, 'Current' is a
// trailing token of this declaration and thus cannot be a name.
@ -2284,12 +2301,9 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
// 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 (Right.is(TT_JsNonNullAssertion))
return false;
if (Left.is(tok::exclaim) && Right.is(Keywords.kw_as))
if (Left.is(TT_JsNonNullAssertion) && Right.is(Keywords.kw_as))
return true; // "x! as string"
} else if (Style.Language == FormatStyle::LK_Java) {
if (Left.is(tok::r_square) && Right.is(tok::l_brace))
@ -2545,6 +2559,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
return false; // must not break before as in 'x as type' casts
if (Left.is(Keywords.kw_as))
return true;
if (Left.is(TT_JsNonNullAssertion))
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,

View File

@ -1714,6 +1714,12 @@ TEST_F(FormatTestJS, NonNullAssertionOperator) {
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) {