[AST] Don't store data for GNU range case statement if not needed
authorBruno Ricci <riccibrun@gmail.com>
Sun, 28 Oct 2018 12:30:53 +0000 (12:30 +0000)
committerBruno Ricci <riccibrun@gmail.com>
Sun, 28 Oct 2018 12:30:53 +0000 (12:30 +0000)
Don't store the data for case statements of the form LHS ... RHS if not
needed. This cuts the size of CaseStmt by 1 pointer + 1 SourceLocation in
the common case.

Also use the newly available space in the bit-fields of Stmt to store the
keyword location of SwitchCase and move the small accessor
SwitchCase::getSubStmt to the header.

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

Reviewed By: rjmccall

llvm-svn: 345472

clang/include/clang/AST/Stmt.h
clang/lib/AST/ASTDumper.cpp
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Stmt.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/Import/switch-stmt/test.cpp
clang/test/Misc/ast-dump-color.cpp

index a9bc89c..3cdd48f 100644 (file)
@@ -245,6 +245,20 @@ protected:
     SourceLocation RetLoc;
   };
 
+  class SwitchCaseBitfields {
+    friend class SwitchCase;
+    friend class CaseStmt;
+
+    unsigned : NumStmtBits;
+
+    /// Used by CaseStmt to store whether it is a case statement
+    /// of the form case LHS ... RHS (a GNU extension).
+    unsigned CaseStmtIsGNURange : 1;
+
+    /// The location of the "case" or "default" keyword.
+    SourceLocation KeywordLoc;
+  };
+
   //===--- Expression bitfields classes ---===//
 
   class ExprBitfields {
@@ -461,6 +475,7 @@ protected:
     ContinueStmtBitfields ContinueStmtBits;
     BreakStmtBitfields BreakStmtBits;
     ReturnStmtBitfields ReturnStmtBits;
+    SwitchCaseBitfields SwitchCaseBits;
 
     // Expressions
     ExprBitfields ExprBits;
@@ -877,36 +892,40 @@ public:
 // SwitchCase is the base class for CaseStmt and DefaultStmt,
 class SwitchCase : public Stmt {
 protected:
-  // A pointer to the following CaseStmt or DefaultStmt class,
-  // used by SwitchStmt.
-  SwitchCase *NextSwitchCase = nullptr;
-  SourceLocation KeywordLoc;
+  /// The location of the ":".
   SourceLocation ColonLoc;
 
+  // The location of the "case" or "default" keyword. Stored in SwitchCaseBits.
+  // SourceLocation KeywordLoc;
+
+  /// A pointer to the following CaseStmt or DefaultStmt class,
+  /// used by SwitchStmt.
+  SwitchCase *NextSwitchCase = nullptr;
+
   SwitchCase(StmtClass SC, SourceLocation KWLoc, SourceLocation ColonLoc)
-      : Stmt(SC), KeywordLoc(KWLoc), ColonLoc(ColonLoc) {}
+      : Stmt(SC), ColonLoc(ColonLoc) {
+    setKeywordLoc(KWLoc);
+  }
 
   SwitchCase(StmtClass SC, EmptyShell) : Stmt(SC) {}
 
 public:
   const SwitchCase *getNextSwitchCase() const { return NextSwitchCase; }
-
   SwitchCase *getNextSwitchCase() { return NextSwitchCase; }
-
   void setNextSwitchCase(SwitchCase *SC) { NextSwitchCase = SC; }
 
-  SourceLocation getKeywordLoc() const { return KeywordLoc; }
-  void setKeywordLoc(SourceLocation L) { KeywordLoc = L; }
+  SourceLocation getKeywordLoc() const { return SwitchCaseBits.KeywordLoc; }
+  void setKeywordLoc(SourceLocation L) { SwitchCaseBits.KeywordLoc = L; }
   SourceLocation getColonLoc() const { return ColonLoc; }
   void setColonLoc(SourceLocation L) { ColonLoc = L; }
 
-  Stmt *getSubStmt();
+  inline Stmt *getSubStmt();
   const Stmt *getSubStmt() const {
-    return const_cast<SwitchCase*>(this)->getSubStmt();
+    return const_cast<SwitchCase *>(this)->getSubStmt();
   }
 
-  SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; }
-  SourceLocation getEndLoc() const LLVM_READONLY;
+  SourceLocation getBeginLoc() const { return getKeywordLoc(); }
+  inline SourceLocation getEndLoc() const LLVM_READONLY;
 
   static bool classof(const Stmt *T) {
     return T->getStmtClass() == CaseStmtClass ||
@@ -914,52 +933,137 @@ public:
   }
 };
 
-class CaseStmt : public SwitchCase {
-  SourceLocation EllipsisLoc;
-  enum { LHS, RHS, SUBSTMT, END_EXPR };
-  Stmt* SubExprs[END_EXPR];  // The expression for the RHS is Non-null for
-                             // GNU "case 1 ... 4" extension
+/// CaseStmt - Represent a case statement. It can optionally be a GNU case
+/// statement of the form LHS ... RHS representing a range of cases.
+class CaseStmt final
+    : public SwitchCase,
+      private llvm::TrailingObjects<CaseStmt, Stmt *, SourceLocation> {
+  friend TrailingObjects;
+
+  // CaseStmt is followed by several trailing objects, some of which optional.
+  // Note that it would be more convenient to put the optional trailing objects
+  // at the end but this would impact children().
+  // The trailing objects are in order:
+  //
+  // * A "Stmt *" for the LHS of the case statement. Always present.
+  //
+  // * A "Stmt *" for the RHS of the case statement. This is a GNU extension
+  //   which allow ranges in cases statement of the form LHS ... RHS.
+  //   Present if and only if caseStmtIsGNURange() is true.
+  //
+  // * A "Stmt *" for the substatement of the case statement. Always present.
+  //
+  // * A SourceLocation for the location of the ... if this is a case statement
+  //   with a range. Present if and only if caseStmtIsGNURange() is true.
+  enum { LhsOffset = 0, SubStmtOffsetFromRhs = 1 };
+  enum { NumMandatoryStmtPtr = 2 };
 
-public:
+  unsigned numTrailingObjects(OverloadToken<Stmt *>) const {
+    return NumMandatoryStmtPtr + caseStmtIsGNURange();
+  }
+
+  unsigned numTrailingObjects(OverloadToken<SourceLocation>) const {
+    return caseStmtIsGNURange();
+  }
+
+  unsigned lhsOffset() const { return LhsOffset; }
+  unsigned rhsOffset() const { return LhsOffset + caseStmtIsGNURange(); }
+  unsigned subStmtOffset() const { return rhsOffset() + SubStmtOffsetFromRhs; }
+
+  /// Build a case statement assuming that the storage for the
+  /// trailing objects has been properly allocated.
   CaseStmt(Expr *lhs, Expr *rhs, SourceLocation caseLoc,
            SourceLocation ellipsisLoc, SourceLocation colonLoc)
-    : SwitchCase(CaseStmtClass, caseLoc, colonLoc) {
-    SubExprs[SUBSTMT] = nullptr;
-    SubExprs[LHS] = reinterpret_cast<Stmt*>(lhs);
-    SubExprs[RHS] = reinterpret_cast<Stmt*>(rhs);
-    EllipsisLoc = ellipsisLoc;
+      : SwitchCase(CaseStmtClass, caseLoc, colonLoc) {
+    setLHS(lhs);
+    setSubStmt(nullptr);
+    // Handle GNU case statements of the form LHS ... RHS.
+    bool IsGNURange = rhs != nullptr;
+    SwitchCaseBits.CaseStmtIsGNURange = IsGNURange;
+    if (IsGNURange) {
+      setRHS(rhs);
+      setEllipsisLoc(ellipsisLoc);
+    }
   }
 
   /// Build an empty switch case statement.
-  explicit CaseStmt(EmptyShell Empty) : SwitchCase(CaseStmtClass, Empty) {}
+  explicit CaseStmt(EmptyShell Empty, bool CaseStmtIsGNURange)
+      : SwitchCase(CaseStmtClass, Empty) {
+    SwitchCaseBits.CaseStmtIsGNURange = CaseStmtIsGNURange;
+  }
 
-  SourceLocation getCaseLoc() const { return KeywordLoc; }
-  void setCaseLoc(SourceLocation L) { KeywordLoc = L; }
-  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
-  void setEllipsisLoc(SourceLocation L) { EllipsisLoc = L; }
-  SourceLocation getColonLoc() const { return ColonLoc; }
-  void setColonLoc(SourceLocation L) { ColonLoc = L; }
+public:
+  /// Build a case statement.
+  static CaseStmt *Create(const ASTContext &Ctx, Expr *lhs, Expr *rhs,
+                          SourceLocation caseLoc, SourceLocation ellipsisLoc,
+                          SourceLocation colonLoc);
+
+  /// Build an empty case statement.
+  static CaseStmt *CreateEmpty(const ASTContext &Ctx, bool CaseStmtIsGNURange);
 
-  Expr *getLHS() { return reinterpret_cast<Expr*>(SubExprs[LHS]); }
-  Expr *getRHS() { return reinterpret_cast<Expr*>(SubExprs[RHS]); }
-  Stmt *getSubStmt() { return SubExprs[SUBSTMT]; }
+  /// True if this case statement is of the form case LHS ... RHS, which
+  /// is a GNU extension. In this case the RHS can be obtained with getRHS()
+  /// and the location of the ellipsis can be obtained with getEllipsisLoc().
+  bool caseStmtIsGNURange() const { return SwitchCaseBits.CaseStmtIsGNURange; }
+
+  SourceLocation getCaseLoc() const { return getKeywordLoc(); }
+  void setCaseLoc(SourceLocation L) { setKeywordLoc(L); }
+
+  /// Get the location of the ... in a case statement of the form LHS ... RHS.
+  SourceLocation getEllipsisLoc() const {
+    return caseStmtIsGNURange() ? *getTrailingObjects<SourceLocation>()
+                                : SourceLocation();
+  }
+
+  /// Set the location of the ... in a case statement of the form LHS ... RHS.
+  /// Assert that this case statement is of this form.
+  void setEllipsisLoc(SourceLocation L) {
+    assert(
+        caseStmtIsGNURange() &&
+        "setEllipsisLoc but this is not a case stmt of the form LHS ... RHS!");
+    *getTrailingObjects<SourceLocation>() = L;
+  }
+
+  Expr *getLHS() {
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[lhsOffset()]);
+  }
 
   const Expr *getLHS() const {
-    return reinterpret_cast<const Expr*>(SubExprs[LHS]);
+    return reinterpret_cast<Expr *>(getTrailingObjects<Stmt *>()[lhsOffset()]);
+  }
+
+  void setLHS(Expr *Val) {
+    getTrailingObjects<Stmt *>()[lhsOffset()] = reinterpret_cast<Stmt *>(Val);
+  }
+
+  Expr *getRHS() {
+    return caseStmtIsGNURange() ? reinterpret_cast<Expr *>(
+                                      getTrailingObjects<Stmt *>()[rhsOffset()])
+                                : nullptr;
   }
 
   const Expr *getRHS() const {
-    return reinterpret_cast<const Expr*>(SubExprs[RHS]);
+    return caseStmtIsGNURange() ? reinterpret_cast<Expr *>(
+                                      getTrailingObjects<Stmt *>()[rhsOffset()])
+                                : nullptr;
   }
 
-  const Stmt *getSubStmt() const { return SubExprs[SUBSTMT]; }
+  void setRHS(Expr *Val) {
+    assert(caseStmtIsGNURange() &&
+           "setRHS but this is not a case stmt of the form LHS ... RHS!");
+    getTrailingObjects<Stmt *>()[rhsOffset()] = reinterpret_cast<Stmt *>(Val);
+  }
 
-  void setSubStmt(Stmt *S) { SubExprs[SUBSTMT] = S; }
-  void setLHS(Expr *Val) { SubExprs[LHS] = reinterpret_cast<Stmt*>(Val); }
-  void setRHS(Expr *Val) { SubExprs[RHS] = reinterpret_cast<Stmt*>(Val); }
+  Stmt *getSubStmt() { return getTrailingObjects<Stmt *>()[subStmtOffset()]; }
+  const Stmt *getSubStmt() const {
+    return getTrailingObjects<Stmt *>()[subStmtOffset()];
+  }
 
-  SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; }
+  void setSubStmt(Stmt *S) {
+    getTrailingObjects<Stmt *>()[subStmtOffset()] = S;
+  }
 
+  SourceLocation getBeginLoc() const { return getKeywordLoc(); }
   SourceLocation getEndLoc() const LLVM_READONLY {
     // Handle deeply nested case statements with iteration instead of recursion.
     const CaseStmt *CS = this;
@@ -975,16 +1079,18 @@ public:
 
   // Iterators
   child_range children() {
-    return child_range(&SubExprs[0], &SubExprs[END_EXPR]);
+    return child_range(getTrailingObjects<Stmt *>(),
+                       getTrailingObjects<Stmt *>() +
+                           numTrailingObjects(OverloadToken<Stmt *>()));
   }
 };
 
 class DefaultStmt : public SwitchCase {
-  StmtSubStmt;
+  Stmt *SubStmt;
 
 public:
-  DefaultStmt(SourceLocation DL, SourceLocation CL, Stmt *substmt) :
-    SwitchCase(DefaultStmtClass, DL, CL), SubStmt(substmt) {}
+  DefaultStmt(SourceLocation DL, SourceLocation CL, Stmt *substmt)
+      : SwitchCase(DefaultStmtClass, DL, CL), SubStmt(substmt) {}
 
   /// Build an empty default statement.
   explicit DefaultStmt(EmptyShell Empty)
@@ -994,12 +1100,10 @@ public:
   const Stmt *getSubStmt() const { return SubStmt; }
   void setSubStmt(Stmt *S) { SubStmt = S; }
 
-  SourceLocation getDefaultLoc() const { return KeywordLoc; }
-  void setDefaultLoc(SourceLocation L) { KeywordLoc = L; }
-  SourceLocation getColonLoc() const { return ColonLoc; }
-  void setColonLoc(SourceLocation L) { ColonLoc = L; }
+  SourceLocation getDefaultLoc() const { return getKeywordLoc(); }
+  void setDefaultLoc(SourceLocation L) { setKeywordLoc(L); }
 
-  SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; }
+  SourceLocation getBeginLoc() const { return getKeywordLoc(); }
   SourceLocation getEndLoc() const LLVM_READONLY {
     return SubStmt->getEndLoc();
   }
@@ -1009,13 +1113,23 @@ public:
   }
 
   // Iterators
-  child_range children() { return child_range(&SubStmt, &SubStmt+1); }
+  child_range children() { return child_range(&SubStmt, &SubStmt + 1); }
 };
 
-inline SourceLocation SwitchCase::getEndLoc() const {
+SourceLocation SwitchCase::getEndLoc() const {
   if (const auto *CS = dyn_cast<CaseStmt>(this))
     return CS->getEndLoc();
-  return cast<DefaultStmt>(this)->getEndLoc();
+  else if (const auto *DS = dyn_cast<DefaultStmt>(this))
+    return DS->getEndLoc();
+  llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!");
+}
+
+Stmt *SwitchCase::getSubStmt() {
+  if (auto *CS = dyn_cast<CaseStmt>(this))
+    return CS->getSubStmt();
+  else if (auto *DS = dyn_cast<DefaultStmt>(this))
+    return DS->getSubStmt();
+  llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!");
 }
 
 /// LabelStmt - Represents a label, which has a substatement.  For example:
index 4bac6d8..3c4a9ea 100644 (file)
@@ -515,6 +515,7 @@ namespace  {
     void VisitLabelStmt(const LabelStmt *Node);
     void VisitGotoStmt(const GotoStmt *Node);
     void VisitCXXCatchStmt(const CXXCatchStmt *Node);
+    void VisitCaseStmt(const CaseStmt *Node);
     void VisitCapturedStmt(const CapturedStmt *Node);
 
     // OpenMP
@@ -2047,6 +2048,12 @@ void ASTDumper::VisitCXXCatchStmt(const CXXCatchStmt *Node) {
   dumpDecl(Node->getExceptionDecl());
 }
 
+void ASTDumper::VisitCaseStmt(const CaseStmt *Node) {
+  VisitStmt(Node);
+  if (Node->caseStmtIsGNURange())
+    OS << " gnu_range";
+}
+
 void ASTDumper::VisitCapturedStmt(const CapturedStmt *Node) {
   VisitStmt(Node);
   dumpDecl(Node->getCapturedDecl());
index 10f58ab..0de50ae 100644 (file)
@@ -5707,8 +5707,8 @@ ExpectedStmt ASTNodeImporter::VisitCaseStmt(CaseStmt *S) {
   std::tie(ToLHS, ToRHS, ToSubStmt, ToCaseLoc, ToEllipsisLoc, ToColonLoc) =
       *Imp;
 
-  auto *ToStmt = new (Importer.getToContext()) CaseStmt(
-      ToLHS, ToRHS, ToCaseLoc, ToEllipsisLoc, ToColonLoc);
+  auto *ToStmt = CaseStmt::Create(Importer.getToContext(), ToLHS, ToRHS,
+                                  ToCaseLoc, ToEllipsisLoc, ToColonLoc);
   ToStmt->setSubStmt(ToSubStmt);
 
   return ToStmt;
index 9ae514c..29fb60a 100644 (file)
@@ -942,12 +942,6 @@ void SwitchStmt::setConditionVariable(const ASTContext &C, VarDecl *V) {
                                    VarRange.getEnd());
 }
 
-Stmt *SwitchCase::getSubStmt() {
-  if (isa<CaseStmt>(this))
-    return cast<CaseStmt>(this)->getSubStmt();
-  return cast<DefaultStmt>(this)->getSubStmt();
-}
-
 WhileStmt::WhileStmt(const ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body,
                      SourceLocation WL)
   : Stmt(WhileStmtClass) {
@@ -991,6 +985,27 @@ Expr* ReturnStmt::getRetValue() {
   return cast_or_null<Expr>(RetExpr);
 }
 
+// CaseStmt
+CaseStmt *CaseStmt::Create(const ASTContext &Ctx, Expr *lhs, Expr *rhs,
+                           SourceLocation caseLoc, SourceLocation ellipsisLoc,
+                           SourceLocation colonLoc) {
+  bool CaseStmtIsGNURange = rhs != nullptr;
+  void *Mem = Ctx.Allocate(
+      totalSizeToAlloc<Stmt *, SourceLocation>(
+          NumMandatoryStmtPtr + CaseStmtIsGNURange, CaseStmtIsGNURange),
+      alignof(CaseStmt));
+  return new (Mem) CaseStmt(lhs, rhs, caseLoc, ellipsisLoc, colonLoc);
+}
+
+CaseStmt *CaseStmt::CreateEmpty(const ASTContext &Ctx,
+                                bool CaseStmtIsGNURange) {
+  void *Mem = Ctx.Allocate(
+      totalSizeToAlloc<Stmt *, SourceLocation>(
+          NumMandatoryStmtPtr + CaseStmtIsGNURange, CaseStmtIsGNURange),
+      alignof(CaseStmt));
+  return new (Mem) CaseStmt(EmptyShell(), CaseStmtIsGNURange);
+}
+
 SEHTryStmt::SEHTryStmt(bool IsCXXTry, SourceLocation TryLoc, Stmt *TryBlock,
                        Stmt *Handler)
     : Stmt(SEHTryStmtClass), IsCXXTry(IsCXXTry), TryLoc(TryLoc) {
index 14218bf..67b5a84 100644 (file)
@@ -462,8 +462,8 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
     return StmtError();
   }
 
-  CaseStmt *CS = new (Context)
-      CaseStmt(LHSVal.get(), RHSVal.get(), CaseLoc, DotDotDotLoc, ColonLoc);
+  auto *CS = CaseStmt::Create(Context, LHSVal.get(), RHSVal.get(),
+                              CaseLoc, DotDotDotLoc, ColonLoc);
   getCurFunction()->SwitchStack.back().getPointer()->addSwitchCase(CS);
   return CS;
 }
@@ -472,7 +472,7 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
 void Sema::ActOnCaseStmtBody(Stmt *caseStmt, Stmt *SubStmt) {
   DiagnoseUnusedExprResult(SubStmt);
 
-  CaseStmt *CS = static_cast<CaseStmt*>(caseStmt);
+  auto *CS = static_cast<CaseStmt *>(caseStmt);
   CS->setSubStmt(SubStmt);
 }
 
index f7f3f05..466dfa5 100644 (file)
@@ -177,10 +177,13 @@ void ASTStmtReader::VisitSwitchCase(SwitchCase *S) {
 
 void ASTStmtReader::VisitCaseStmt(CaseStmt *S) {
   VisitSwitchCase(S);
+  bool CaseStmtIsGNURange = Record.readInt();
   S->setLHS(Record.readSubExpr());
-  S->setRHS(Record.readSubExpr());
   S->setSubStmt(Record.readSubStmt());
-  S->setEllipsisLoc(ReadSourceLocation());
+  if (CaseStmtIsGNURange) {
+    S->setRHS(Record.readSubExpr());
+    S->setEllipsisLoc(ReadSourceLocation());
+  }
 }
 
 void ASTStmtReader::VisitDefaultStmt(DefaultStmt *S) {
@@ -2277,7 +2280,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
 
     case STMT_CASE:
-      S = new (Context) CaseStmt(Empty);
+      S = CaseStmt::CreateEmpty(
+          Context,
+          /*CaseStmtIsGNURange*/ Record[ASTStmtReader::NumStmtFields + 3]);
       break;
 
     case STMT_DEFAULT:
index ef2e06a..3fce691 100644 (file)
@@ -96,10 +96,13 @@ void ASTStmtWriter::VisitSwitchCase(SwitchCase *S) {
 
 void ASTStmtWriter::VisitCaseStmt(CaseStmt *S) {
   VisitSwitchCase(S);
+  Record.push_back(S->caseStmtIsGNURange());
   Record.AddStmt(S->getLHS());
-  Record.AddStmt(S->getRHS());
   Record.AddStmt(S->getSubStmt());
-  Record.AddSourceLocation(S->getEllipsisLoc());
+  if (S->caseStmtIsGNURange()) {
+    Record.AddStmt(S->getRHS());
+    Record.AddSourceLocation(S->getEllipsisLoc());
+  }
   Code = serialization::STMT_CASE;
 }
 
index c8b96c7..3ccfdb9 100644 (file)
@@ -7,10 +7,8 @@
 // CHECK-NEXT: CompoundStmt
 // CHECK-NEXT: CaseStmt
 // CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: CaseStmt
 // CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: BreakStmt
 
 // CHECK: SwitchStmt
 // CHECK-NEXT: CompoundStmt
 // CHECK-NEXT: CaseStmt
 // CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: BreakStmt
 // CHECK-NEXT: CaseStmt
 // CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: <<NULL>>
 // CHECK-NEXT: BreakStmt
 
 // CHECK: SwitchStmt
index ad7ea30..b1144e3 100644 (file)
@@ -51,13 +51,11 @@ struct Invalid {
 //CHECK: {{^}}[[Blue]]| |   `-[[RESET]][[MAGENTA]]CompoundStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:14[[RESET]], [[Yellow]]line:15:3[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |     |-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:3[[RESET]], [[Yellow]]line:12:27[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |     | |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:11:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 1[[RESET]]{{$}}
-//CHECK: {{^}}[[Blue]]| |     | |-[[RESET]][[Blue]]<<<NULL>>>[[RESET]]{{$}}
 //CHECK: {{^}}[[Blue]]| |     | `-[[RESET]][[MAGENTA]]AttributedStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:12:5[[RESET]], [[Yellow]]col:27[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |     |   |-[[RESET]][[BLUE]]FallThroughAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:7[[RESET]], [[Yellow]]col:14[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |     |   `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:27[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |     `-[[RESET]][[MAGENTA]]CaseStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:3[[RESET]], [[Yellow]]line:14:5[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| |       |-[[RESET]][[MAGENTA]]IntegerLiteral[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:13:8[[RESET]]> [[Green]]'int'[[RESET]][[Cyan]][[RESET]][[Cyan]][[RESET]][[CYAN]] 2[[RESET]]{{$}}
-//CHECK: {{^}}[[Blue]]| |       |-[[RESET]][[Blue]]<<<NULL>>>[[RESET]]{{$}}
 //CHECK: {{^}}[[Blue]]| |       `-[[RESET]][[MAGENTA]]NullStmt[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:14:5[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]| `-[[RESET]][[Blue]]FullComment[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:8:4[[RESET]], [[Yellow]]col:11[[RESET]]>{{$}}
 //CHECK: {{^}}[[Blue]]|   `-[[RESET]][[Blue]]ParagraphComment[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:4[[RESET]], [[Yellow]]col:11[[RESET]]>{{$}}