From 5b30571753cef75c86c6bc11983a5a4e56d5c771 Mon Sep 17 00:00:00 2001 From: Bruno Ricci Date: Sun, 28 Oct 2018 12:30:53 +0000 Subject: [PATCH] [AST] Don't store data for GNU range case statement if not needed 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 | 220 +++++++++++++++++++++++------- clang/lib/AST/ASTDumper.cpp | 7 + clang/lib/AST/ASTImporter.cpp | 4 +- clang/lib/AST/Stmt.cpp | 27 +++- clang/lib/Sema/SemaStmt.cpp | 6 +- clang/lib/Serialization/ASTReaderStmt.cpp | 11 +- clang/lib/Serialization/ASTWriterStmt.cpp | 7 +- clang/test/Import/switch-stmt/test.cpp | 4 - clang/test/Misc/ast-dump-color.cpp | 2 - 9 files changed, 213 insertions(+), 75 deletions(-) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index a9bc89c..3cdd48f 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -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(this)->getSubStmt(); + return const_cast(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 { + 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) const { + return NumMandatoryStmtPtr + caseStmtIsGNURange(); + } + + unsigned numTrailingObjects(OverloadToken) 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(lhs); - SubExprs[RHS] = reinterpret_cast(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(SubExprs[LHS]); } - Expr *getRHS() { return reinterpret_cast(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(); + } + + /// 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() = L; + } + + Expr *getLHS() { + return reinterpret_cast(getTrailingObjects()[lhsOffset()]); + } const Expr *getLHS() const { - return reinterpret_cast(SubExprs[LHS]); + return reinterpret_cast(getTrailingObjects()[lhsOffset()]); + } + + void setLHS(Expr *Val) { + getTrailingObjects()[lhsOffset()] = reinterpret_cast(Val); + } + + Expr *getRHS() { + return caseStmtIsGNURange() ? reinterpret_cast( + getTrailingObjects()[rhsOffset()]) + : nullptr; } const Expr *getRHS() const { - return reinterpret_cast(SubExprs[RHS]); + return caseStmtIsGNURange() ? reinterpret_cast( + getTrailingObjects()[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()[rhsOffset()] = reinterpret_cast(Val); + } - void setSubStmt(Stmt *S) { SubExprs[SUBSTMT] = S; } - void setLHS(Expr *Val) { SubExprs[LHS] = reinterpret_cast(Val); } - void setRHS(Expr *Val) { SubExprs[RHS] = reinterpret_cast(Val); } + Stmt *getSubStmt() { return getTrailingObjects()[subStmtOffset()]; } + const Stmt *getSubStmt() const { + return getTrailingObjects()[subStmtOffset()]; + } - SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } + void setSubStmt(Stmt *S) { + getTrailingObjects()[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(), + getTrailingObjects() + + numTrailingObjects(OverloadToken())); } }; class DefaultStmt : public SwitchCase { - Stmt* SubStmt; + 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(this)) return CS->getEndLoc(); - return cast(this)->getEndLoc(); + else if (const auto *DS = dyn_cast(this)) + return DS->getEndLoc(); + llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!"); +} + +Stmt *SwitchCase::getSubStmt() { + if (auto *CS = dyn_cast(this)) + return CS->getSubStmt(); + else if (auto *DS = dyn_cast(this)) + return DS->getSubStmt(); + llvm_unreachable("SwitchCase is neither a CaseStmt nor a DefaultStmt!"); } /// LabelStmt - Represents a label, which has a substatement. For example: diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 4bac6d8..3c4a9ea 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -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()); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 10f58ab..0de50ae 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -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; diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 9ae514c..29fb60a 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -942,12 +942,6 @@ void SwitchStmt::setConditionVariable(const ASTContext &C, VarDecl *V) { VarRange.getEnd()); } -Stmt *SwitchCase::getSubStmt() { - if (isa(this)) - return cast(this)->getSubStmt(); - return cast(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(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( + 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( + 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) { diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 14218bf..67b5a84 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -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); + auto *CS = static_cast(caseStmt); CS->setSubStmt(SubStmt); } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f7f3f05..466dfa5 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -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: diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index ef2e06a..3fce691 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -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; } diff --git a/clang/test/Import/switch-stmt/test.cpp b/clang/test/Import/switch-stmt/test.cpp index c8b96c7..3ccfdb9 100644 --- a/clang/test/Import/switch-stmt/test.cpp +++ b/clang/test/Import/switch-stmt/test.cpp @@ -7,10 +7,8 @@ // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt @@ -22,11 +20,9 @@ // CHECK-NEXT: CompoundStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK-NEXT: CaseStmt // CHECK-NEXT: IntegerLiteral -// CHECK-NEXT: <> // CHECK-NEXT: BreakStmt // CHECK: SwitchStmt diff --git a/clang/test/Misc/ast-dump-color.cpp b/clang/test/Misc/ast-dump-color.cpp index ad7ea30..b1144e3 100644 --- a/clang/test/Misc/ast-dump-color.cpp +++ b/clang/test/Misc/ast-dump-color.cpp @@ -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]]<<>>[[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]]<<>>[[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]]>{{$}} -- 2.7.4