clang-format: [JS] fix union type spacing in object & array types.
authorMartin Probst <martin@probst.io>
Tue, 8 Aug 2017 15:00:58 +0000 (15:00 +0000)
committerMartin Probst <martin@probst.io>
Tue, 8 Aug 2017 15:00:58 +0000 (15:00 +0000)
Summary:
Previously, clang-format would insert whitespace in union types nested in object
and array types, as it wouldn't recognize those as a type operator:

    const x: {foo: number | null};
    const x: [number | null];

While this is correct for actual binary operators, clang-format should not
insert whitespace into union and intersection types to mark those:

    const x: {foo: number|null};
    const x: [number|null];

This change propagates that the context is not an expression by inspecting
the preceding token and marking as non-expression if it was a type colon.

Reviewers: djasper

Subscribers: klimek

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

llvm-svn: 310367

clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTestJS.cpp

index 7ea34d4..37583cd 100644 (file)
@@ -372,6 +372,10 @@ private:
 
     ScopedContextCreator ContextCreator(*this, tok::l_square, BindingIncrease);
     Contexts.back().IsExpression = true;
+    if (Style.Language == FormatStyle::LK_JavaScript && Parent &&
+        Parent->is(TT_JsTypeColon))
+      Contexts.back().IsExpression = false;
+
     Contexts.back().ColonIsObjCMethodExpr = StartsObjCMethodExpr;
 
     while (CurrentToken) {
@@ -439,6 +443,9 @@ private:
       Contexts.back().ColonIsDictLiteral = true;
       if (Left->BlockKind == BK_BracedInit)
         Contexts.back().IsExpression = true;
+      if (Style.Language == FormatStyle::LK_JavaScript && Left->Previous &&
+          Left->Previous->is(TT_JsTypeColon))
+        Contexts.back().IsExpression = false;
 
       while (CurrentToken) {
         if (CurrentToken->is(tok::r_brace)) {
@@ -531,6 +538,8 @@ private:
              !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
+            (!Contexts.back().IsExpression &&
+             Contexts.back().ContextKind == tok::l_brace) || // object type
             (Contexts.size() == 1 &&
              Line.MustBeDeclaration)) { // method/property declaration
           Contexts.back().IsExpression = false;
index 12ff750..033c024 100644 (file)
@@ -1363,6 +1363,18 @@ TEST_F(FormatTestJS, UnionIntersectionTypes) {
                "};");
 }
 
+TEST_F(FormatTestJS, UnionIntersectionTypesInObjectType) {
+  verifyFormat("let x: {x: number|null} = {x: number | null};");
+  verifyFormat("let nested: {x: {y: number|null}};");
+  verifyFormat("let mixed: {x: [number|null, {w: number}]};");
+  verifyFormat("class X {\n"
+               "  contructor(x: {\n"
+               "    a: a|null,\n"
+               "    b: b|null,\n"
+               "  }) {}\n"
+               "}");
+}
+
 TEST_F(FormatTestJS, ClassDeclarations) {
   verifyFormat("class C {\n  x: string = 12;\n}");
   verifyFormat("class C {\n  x(): string => 12;\n}");