[cfe-commits] r120088 - in /cfe/trunk: include/clang/AST/DeclBase.h include/clang/Driver/CC1Options.td include/clang/Frontend/ASTConsumers.h include/clang/Frontend/FrontendActions.h include/clang/Frontend/FrontendOptions.h lib/AST/CMakeLists.txt

Zhanyong Wan (λx.x x) wan at google.com
Mon Nov 29 10:34:28 PST 2010


I wrote this AST dumper in order to test RecursiveASTVisitor.
Therefore I picked the format with exactly that concern in mind.

We didn't try to upstream it because:

1. The format was in a flux as we were experimenting with what
information it should include and in what form.
2. We want to have complete control on the format such that the tests
for RecursiveASTVisitor can rely on it.
3. We aren't sure how interesting it will be to other people, given
its limited goal and the existence of other AST dumpers in Clang.
4. Our hands were full of other things that seemed more urgent.

The actual output that corresponds to the test code Craig posted can be found at

http://pastie.org/1333006

(Actually, some information is lost on pastie: we use different colors
to distinguish between Stmts, Types, and Decls.)  The number at the
beginning of each line is the SourceLoc line number of the AST node.
We don't print the file name as it only clutters the output without
adding value for our purpose.

As you can see, this is a compact format that resembles the output of
Unix's 'tree' command.  It's meant to be easy to parse by a human and
yet provide enough structure to be verified by FileCheck.  You can use
it as either a debugging tool or a testing tool.

You can read the comments I wrote for this AST dumper to understand
the subtle points about the format and the rationale behind them:

http://pastie.org/1333018

There shouldn't be much technical difficulty in upstreaming this --
the biggest constraint is for us to find the time to actually do it.
If people find this useful, I'll be happy to contribute it.

On Sun, Nov 28, 2010 at 6:48 PM, Craig Silverstein <csilvers at google.com> wrote:
> } I'd be at least interested in seeing it.
>
> We developed this functionality as a test for RecursiveASTVisitor.h.  As
> I understand it, the reason we haven't tried to submit it upstream yet
> is that it's still quite incomplete.  (It will probably need to be
> refactored into one test per kind of AST node, rather than a single
> monolithic test like we have now.)
>
> This is all Zhanyong's work, so I'll let him speak to how practical it
> would be to get something upstream.  I also don't want so share the test
> code itself without his ok, but I've attached the actual test file
> below.  It's at least enough to show what the output looks like, and how
> one might do FileCheck tests with it.
>
> craig
>
> --cut here--
>
> // Copyright 2010 Google Inc. All Rights Reserved.
> // Author: wan at google.com (Zhanyong Wan)
>
> // ast_visitor_test.py uses FileCheck to verify the Clang AST
> // generated by this file.  See http://llvm.org/cmds/FileCheck.html
> // for how FileCheck works.
> //
> // Please read the comments at the beginning of dump_ast.cc to learn
> // the meaning of the CHECK-NEXT lines in this file, and read README
> // for how to update this file in case ast_visitor_test fails.
>
> #include <stddef.h>   // for offsetof()
> #include <typeinfo>   // for typeid()
>
> ////////////////////////////////////////////////////////////
> // Tests traversing VarDecl.
>
> // A variable declaration without an initializer expression.
>
> // CHECK:                     .  |=VarDecl: start
> // CHECK-NEXT:                .  | `=BuiltinType: int
> int start;
>
> // A variable declaration with a simple initializer expression.
>
> // CHECK-NEXT:                .  |=VarDecl: n
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=IntegerLiteral
> int n = 0;
>
> // A const variable definition.
>
> // CHECK-NEXT:                .  |=VarDecl: kInteger
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=IntegerLiteral
> const int kInteger = 42;
>
> ////////////////////////////////////////////////////////////
> // Tests traversing an enum definition.
>
> // CHECK-NEXT:                .  |=EnumDecl: MyEnum
> // CHECK-NEXT:                .  | |=EnumType: MyEnum
> enum MyEnum {
>  // CHECK-NEXT:              .  | |=EnumConstantDecl: MyEnum::kVal1
>  // CHECK-NEXT:              .  | | `=ImplicitCastExpr
>  // CHECK-NEXT:              .  | |   `=IntegerLiteral
>  kVal1 = 0,
>  // CHECK-NEXT:              .  | `=EnumConstantDecl: MyEnum::kVal2
>  kVal2
> };
>
> ////////////////////////////////////////////////////////////
> // Tests traversing FunctionDecl.
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: FooClass
> class FooClass;
>
> // A declaration of a function that has no argument.
>
> // CHECK-NEXT:                .  |=FunctionDecl: VoidFunc
> // CHECK-NEXT:                .  | `=FunctionProtoType: void ()
> // CHECK-NEXT:                .  |   `=BuiltinType: void
> void VoidFunc();
>
> // A declaration of a function with default parameters.
>
> // CHECK-NEXT:                .  |=FunctionDecl: OneArgFunc
> // CHECK-NEXT:                .  | `=FunctionProtoType: float (int)
> // CHECK-NEXT:                .  |   |=BuiltinType: float
> // CHECK-NEXT:                .  |   `=ParmVarDecl: a
> // CHECK-NEXT:                .  |     |=BuiltinType: int
> // CHECK-NEXT:                .  |     |=BinaryOperator
> // CHECK-NEXT:                .  |     | |=IntegerLiteral
> // CHECK-NEXT:                .  |     | `=IntegerLiteral
> // CHECK-NEXT:                .  |     `=BinaryOperator
> // CHECK-NEXT:                .  |       |=IntegerLiteral
> // CHECK-NEXT:                .  |       `=IntegerLiteral
> float OneArgFunc(int a = 5*4);
>
> // A definition of a one-argument function.
>
> // CHECK-NEXT:                .  |=FunctionDecl: IntFunc
> // CHECK-NEXT:                .  | |=FunctionProtoType
> // CHECK-NEXT:                .  | | |=BuiltinType: int
> int IntFunc(
>    // CHECK-NEXT:            .  | | `=ParmVarDecl: foo
>    // CHECK-NEXT:            .  | |   `=PointerType: FooClass *
>    // CHECK-NEXT:            .  | |     `=RecordType: FooClass
>    FooClass* foo)
>    // CHECK-NEXT:            .  | `=CompoundStmt
> {
>  // CHECK-NEXT:              .  |   `=ReturnStmt
>  // CHECK-NEXT:              .  |     `=IntegerLiteral
>  return 0;
> }
>
> // A function declaration done via a function typedef.
>
> // CHECK-NEXT:                .  |=TypedefDecl: MyIntFuncType
> // CHECK-NEXT:                .  | `=FunctionProtoType: int (bool)
> // CHECK-NEXT:                .  |   |=BuiltinType: int
> // CHECK-NEXT:                .  |   `=ParmVarDecl: flag
> // CHECK-NEXT:                .  |     `=BuiltinType: bool
> typedef int MyIntFuncType(bool flag);
>
> // CHECK-NEXT:                .  |=FunctionDecl: MyIntFunc
> // CHECK-NEXT:                .  | `=TypedefType: MyIntFuncType
> MyIntFuncType MyIntFunc;
>
> ////////////////////////////////////////////////////////////
> // Tests template forward declarations.
>
> // A class template with a template type parameter and a non-type
> // template parameter.
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: ClassTemplate
> // TODO(wan): traverse the template parameters before the CXXRecordDecl.
> // CHECK-NEXT:                .  | |=CXXRecordDecl: ClassTemplate
> // CHECK-NEXT:                .  | |=TemplateTypeParmDecl: T
> // CHECK-NEXT:                .  | | `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | `=NonTypeTemplateParmDecl: N
> // CHECK-NEXT:                .  |   `=BuiltinType: int
> template <typename T, int N>
> class ClassTemplate;
>
> // A function template with a template type parameter and a template
> // template parameter.
>
> // CHECK-NEXT:                .  |=FunctionTemplateDecl: FunctionTemplate
> // TODO(wan): traverse the template parameters before the FunctionDecl
> // and FunctionProtoType.
> // CHECK-NEXT:                .  | |=FunctionDecl: FunctionTemplate
> // CHECK-NEXT:                .  | | `=FunctionProtoType: int (T const &)
> // CHECK-NEXT:                .  | |   |=BuiltinType: int
> // CHECK-NEXT:                .  | |   `=ParmVarDecl: x
> // CHECK-NEXT:                .  | |     `=LValueReferenceType: T const &
> // CHECK-NEXT:                .  | |       `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | |=TemplateTypeParmDecl: T
> // CHECK-NEXT:                .  | | `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | `=TemplateTemplateParmDecl: V
> // CHECK-NEXT:                .  |   `=TemplateTypeParmDecl: U
> // CHECK-NEXT:                .  |     `=TemplateTypeParmType: U
> template <class T, template <typename U> class V>
> int FunctionTemplate(const T& x);
>
> // A class template with default template arguments.
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: ClassTemplate2
> // CHECK-NEXT:                .  | |=CXXRecordDecl: ClassTemplate2
> template <
>  // CHECK-NEXT:              .  | |=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  | | |=TemplateTypeParmType: T
>  // CHECK-NEXT:              .  | | `=BuiltinType: int
>  class T=int,
>  // CHECK-NEXT:              .  | |=NonTypeTemplateParmDecl: N
>  // CHECK-NEXT:              .  | | |=BuiltinType: int
>  // CHECK-NEXT:              .  | | `=ImplicitCastExpr
>  // CHECK-NEXT:              .  | |   `=SizeOfAlignOfExpr
>  // CHECK-NEXT:              .  | |     `=TemplateTypeParmType: T
>  int N=sizeof(T),
>  // CHECK-NEXT:              .  | `=TemplateTemplateParmDecl: V
>  // CHECK-NEXT:              .  |   |=TemplateTypeParmDecl: U
>  // CHECK-NEXT:              .  |   | `=TemplateTypeParmType: U
>  // CHECK-NEXT:              .  |   `=NonTypeTemplateParmDecl: M
>  // CHECK-NEXT:              .  |     `=BuiltinType: int
>  // TODO(wan): traverse the default value ClassTemplate.
>  template <typename U, int M> class V=ClassTemplate>
> class ClassTemplate2;
>
> // Another template with default template arguments.
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: StructTemplate1
> // CHECK-NEXT:                .  | |=CXXRecordDecl: StructTemplate1
> template <
>  // CHECK-NEXT:              .  | |=NonTypeTemplateParmDecl: E
>  // CHECK-NEXT:              .  | | |=EnumType: MyEnum
>  // CHECK-NEXT:              .  | | `=DeclRefExpr
>  MyEnum E = kVal1,
>  // CHECK-NEXT:              .  | |=NonTypeTemplateParmDecl: B
>  // CHECK-NEXT:              .  | | |=BuiltinType: bool
>  // CHECK-NEXT:              .  | | `=ParenExpr
>  // CHECK-NEXT:              .  | |   `=BinaryOperator
>  // CHECK-NEXT:              .  | |     |=ImplicitCastExpr
>  // CHECK-NEXT:              .  | |     | `=DeclRefExpr
>  // CHECK-NEXT:              .  | |     `=IntegerLiteral
>  bool B = (kInteger < 5),
>    // CHECK-NEXT:            .  | `=NonTypeTemplateParmDecl: I
>    // CHECK-NEXT:            .  |   |=BuiltinType: int
>    // CHECK-NEXT:            .  |   `=ImplicitCastExpr
>    // CHECK-NEXT:            .  |     `=SizeOfAlignOfExpr
>    // CHECK-NEXT:            .  |       `=PointerType: void *
>    // CHECK-NEXT:            .  |         `=BuiltinType: void
>  int I = sizeof(void*)>
> struct StructTemplate1;
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: X
> // CHECK-NEXT:                .  | |=CXXRecordDecl: X
> // TODO(wan): traverse A before X.
> // CHECK-NEXT:                .  | `=TemplateTypeParmDecl: A
> // CHECK-NEXT:                .  |   `=TemplateTypeParmType: A
> template <class A>
> struct X {};
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: Foo
> class Foo {
>  // CHECK-NEXT:              .  | `=FieldDecl: Foo::x
>  // CHECK-NEXT:              .  |   `=ElaboratedType: struct X<int>
>  // CHECK-NEXT:              .  |     `=TemplateSpecializationType: X<int>
>  // CHECK-NEXT:              .  |       `=BuiltinType: int
>  struct X<int> x;
> };
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: Bar
> // CHECK-NEXT:                .  | |=CXXRecordDecl: Bar
> template <class A>
> class Bar {
>  // CHECK-NEXT:              .  | | `=FieldDecl: Bar::x
>  // CHECK-NEXT:              .  | |   `=ElaboratedType: struct X<A>
>  // CHECK-NEXT:              .  | |     `=TemplateSpecializationType: X<A>
>  // CHECK-NEXT:              .  | |       `=TemplateTypeParmType: A
>  // TODO(wan): traverse A before x.
>  // CHECK-NEXT:              .  | `=TemplateTypeParmDecl: A
>  // CHECK-NEXT:              .  |   `=TemplateTypeParmType: A
>  struct X<A> x;
> };
>
> ////////////////////////////////////////////////////////////
> // Tests traversing CXXRecordDecl.
>
> // A forward declaration.
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: FooClass
> class FooClass;
>
> // An empty class definition.
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: EmptyClass
> class EmptyClass {
> };
>
> // A class definition.
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: FooClass
> class FooClass {
>  ////////////////////////////////////////////////////////////
>  // Tests traversing AccessSpecDecl.
>
>  // CHECK-NEXT:              .  | |=AccessSpecDecl
>  public:
>
>  ////////////////////////////////////////////////////////////
>  // Tests traversing CXXConstructorDecl.
>
>  // A default constructor.
>
>  // CHECK-NEXT:              .  | |=CXXConstructorDecl: FooClass::FooClass
>  // CHECK-NEXT:              .  | | `=FunctionProtoType: void ()
>  // CHECK-NEXT:              .  | |   `=BuiltinType: void
>  FooClass();
>
>  // A single-argument constructor.
>
>  // CHECK-NEXT:              .  | |=CXXConstructorDecl: FooClass::FooClass
>  // CHECK-NEXT:              .  | | |=FunctionProtoType: void (int)
>  // CHECK-NEXT:              .  | | | |=BuiltinType: void
>  // CHECK-NEXT:              .  | | | `=ParmVarDecl: value
>  // CHECK-NEXT:              .  | | |   `=BuiltinType: int
>  FooClass(int value)
>      // CHECK-NEXT:          .  | | |=DeclRefExpr
>      : value_(value)
>        // CHECK-NEXT:        .  | | `=CompoundStmt
>  {}
>
>  // A ctor with more than one element in its initializer list.
>
>  // CHECK-NEXT:              .  | |=CXXConstructorDecl: FooClass::FooClass
>  // CHECK-NEXT:              .  | | |=FunctionProtoType: void (bool)
>  // CHECK-NEXT:              .  | | | |=BuiltinType: void
>  // CHECK-NEXT:              .  | | | `=ParmVarDecl: flag
>  // CHECK-NEXT:              .  | | |   `=BuiltinType: bool
>  FooClass(bool flag)
>      // CHECK-NEXT:          .  | | |=IntegerLiteral
>      : value_(42),
>        // CHECK-NEXT:        .  | | |=DeclRefExpr
>        flag_(flag)
>        // CHECK-NEXT:        .  | | `=CompoundStmt
>  {}
>
>  ////////////////////////////////////////////////////////////
>  // Tests traversing CXXDestructorDecl.
>
>  // CHECK-NEXT:              .  | |=CXXDestructorDecl: FooClass::~FooClass
>  // CHECK-NEXT:              .  | | |=FunctionProtoType: void ()
>  // CHECK-NEXT:              .  | | | `=BuiltinType: void
>  ~FooClass()
>      // CHECK-NEXT:          .  | | `=CompoundStmt
>  {}
>
>  ////////////////////////////////////////////////////////////
>  // Tests traversing CXXMethodDecl.
>
>  // A non-static method.
>
>  // CHECK-NEXT:              .  | |=CXXMethodDecl: FooClass::Method1
>  // CHECK-NEXT:              .  | | |=FunctionProtoType: void (bool)
>  // CHECK-NEXT:              .  | | | |=BuiltinType: void
>  void Method1(
>      // CHECK-NEXT:          .  | | | `=ParmVarDecl: flag
>      // CHECK-NEXT:          .  | | |   `=BuiltinType: bool
>      bool flag)
>      // CHECK-NEXT:          .  | | `=CompoundStmt
>  {}
>
>  ////////////////////////////////////////////////////////////
>  // Tests traversing CXXConversionDecl.
>
>  // CHECK-NEXT:              .  | |=CXXConversionDecl: FooClass::operator _Bool
>  // CHECK-NEXT:              .  | | |=FunctionProtoType: bool () const
>  // CHECK-NEXT:              .  | | | `=BuiltinType: bool
>  operator bool() const
>      // CHECK-NEXT:          .  | | `=CompoundStmt
>  {
>    // CHECK-NEXT:            .  | |   `=ReturnStmt
>    // CHECK-NEXT:            .  | |     `=CXXBoolLiteralExpr
>    return true;
>  }
>
>  // CHECK-NEXT:              .  | |=AccessSpecDecl
>  private:
>
>  ////////////////////////////////////////////////////////////
>  // Tests traversing FieldDecl.
>
>  // CHECK-NEXT:              .  | |=FieldDecl: FooClass::value_
>  // CHECK-NEXT:              .  | | `=BuiltinType: int
>  int value_;
>
>  // CHECK-NEXT:              .  | `=FieldDecl: FooClass::flag_
>  // CHECK-NEXT:              .  |   `=BuiltinType: bool
>  bool flag_;
> };
>
> ////////////////////////////////////////////////////////////
> // Tests traversing temporary objects.
>
> // A temporary object of a built-in (i.e. non-class) type.
>
> // CHECK-NEXT:                .  |=VarDecl: int_var
> // CHECK-NEXT:                .  | |=BuiltinType: int
> int int_var =
>  // CHECK-NEXT:              .  | `=CXXScalarValueInitExpr
>  // CHECK-NEXT:              .  |   `=BuiltinType: int
>  int();
>
> // A temporary object of a class type.
>
> // CHECK-NEXT:                .  |=VarDecl: empty_class_var
> // CHECK-NEXT:                .  | |=RecordType: EmptyClass
> EmptyClass empty_class_var =
>  // CHECK-NEXT:              .  | `=CXXConstructExpr
>  // CHECK-NEXT:              .  |   `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |     `=CXXTemporaryObjectExpr
>  // CHECK-NEXT:              .  |       `=RecordType: EmptyClass
>  EmptyClass();
>
> ////////////////////////////////////////////////////////////
> // Tests various types of arrays
>
> // CHECK-NEXT:                .  |=FunctionTemplateDecl: ArrayTestingFunction
> // CHECK-NEXT:                .  | |=FunctionDecl: ArrayTestingFunction
> // CHECK-NEXT:                .  | | |=FunctionProtoType: void (int *)
> // CHECK-NEXT:                .  | | | |=BuiltinType: void
> template<typename T, int Size> void ArrayTestingFunction(  // allows vla
>    // CHECK-NEXT:            .  | | | `=ParmVarDecl: a
>    // CHECK-NEXT:            .  | | |   `=IncompleteArrayType: int []
>    // CHECK-NEXT:            .  | | |     `=BuiltinType: int
>    // CHECK-NEXT:            .  | | `=CompoundStmt
>    int a[]) {
>  // CHECK-NEXT:              .  | |   |=DeclStmt
>  // CHECK-NEXT:              .  | |   | `=VarDecl: kConstInt
>  // CHECK-NEXT:              .  | |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  | |   |   `=IntegerLiteral
>  const int kConstInt = 5;
>  // CHECK-NEXT:              .  | |   |=DeclStmt
>  // CHECK-NEXT:              .  | |   | `=VarDecl: nonconst_int
>  // CHECK-NEXT:              .  | |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  | |   |   `=IntegerLiteral
>  int nonconst_int = 6;
>  // CHECK-NEXT:              .  | |   |=DeclStmt
>  // CHECK-NEXT:              .  | |   | `=VarDecl: array
>  // CHECK-NEXT:              .  | |   |   `=ConstantArrayType: int [5]
>  // CHECK-NEXT:              .  | |   |     |=BuiltinType: int
>  // CHECK-NEXT:              .  | |   |     `=DeclRefExpr
>  int array[kConstInt];
>  // CHECK-NEXT:              .  | |   |=DeclStmt
>  // CHECK-NEXT:              .  | |   | `=VarDecl: variable_length_array
>  // CHECK-NEXT:              .  | |   |   `=VariableArrayType: int [nonconst_int]
>  // CHECK-NEXT:              .  | |   |     |=BuiltinType: int
>  // CHECK-NEXT:              .  | |   |     `=DeclRefExpr
>  int variable_length_array[nonconst_int];
>  // CHECK-NEXT:              .  | |   `=DeclStmt
>  // CHECK-NEXT:              .  | |     `=VarDecl: dependent_type_array
>  // CHECK-NEXT:              .  | |       `=DependentSizedArrayType: T [Size]
>  // CHECK-NEXT:              .  | |         |=TemplateTypeParmType: T
>  // CHECK-NEXT:              .  | |         `=DeclRefExpr
>  T dependent_type_array[Size];
> }
> // TODO(csilvers): these should come before the body, not after.
> // CHECK-NEXT:                .  | |=TemplateTypeParmDecl: T
> // CHECK-NEXT:                .  | | `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | `=NonTypeTemplateParmDecl: Size
> // CHECK-NEXT:                .  |   `=BuiltinType: int
>
> ////////////////////////////////////////////////////////////
> // Tests sizeof, offsetof, typeid, and unary_type_trait
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: OffsetOfA
> struct OffsetOfA {
>  // CHECK-NEXT:              .  | `=FieldDecl: OffsetOfA::a
>  // CHECK-NEXT:              .  |   `=BuiltinType: int
>  int a;
> };
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: OffsetOfB
> struct OffsetOfB {
>  // CHECK-NEXT:              .  | `=FieldDecl: OffsetOfB::b
>  // CHECK-NEXT:              .  |   `=ConstantArrayType: OffsetOfA [10]
>  // CHECK-NEXT:              .  |     |=RecordType: OffsetOfA
>  // CHECK-NEXT:              .  |     `=IntegerLiteral
>  OffsetOfA b[10];
> };
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: OffsetOfC
> struct OffsetOfC {
>  // CHECK-NEXT:              .  | `=FieldDecl: OffsetOfC::c
>  // CHECK-NEXT:              .  |   `=ConstantArrayType: OffsetOfB [10]
>  // CHECK-NEXT:              .  |     |=RecordType: OffsetOfB
>  // CHECK-NEXT:              .  |     `=IntegerLiteral
>  OffsetOfB c[10];
> };
>
> // CHECK-NEXT:                .  |=FunctionDecl: StarOfTestingFunction
> // CHECK-NEXT:                .  | |=FunctionProtoType: void ()
> // CHECK-NEXT:                .  | | `=BuiltinType: void
> // CHECK-NEXT:                .  | `=CompoundStmt
> void StarOfTestingFunction() {   // allows vla
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: nonconst_int
>  // CHECK-NEXT:              .  |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |   `=IntegerLiteral
>  int nonconst_int = 6;
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: variable_length_array
>  // CHECK-NEXT:              .  |   |   `=VariableArrayType: int [nonconst_int]
>  // CHECK-NEXT:              .  |   |     |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |     `=DeclRefExpr
>  int variable_length_array[nonconst_int];
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: sizeof_var1
>  // CHECK-NEXT:              .  |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |   `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |   |     `=SizeOfAlignOfExpr
>  // CHECK-NEXT:              .  |   |       `=RecordType: OffsetOfA
>  const int sizeof_var1 = sizeof(OffsetOfA);
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: sizeof_var2
>  // CHECK-NEXT:              .  |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |   `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |   |     `=SizeOfAlignOfExpr
>  // CHECK-NEXT:              .  |   |       `=DeclRefExpr
>  const int sizeof_var2 = sizeof nonconst_int;
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: sizeof_var3
>  // CHECK-NEXT:              .  |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |   `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |   |     `=SizeOfAlignOfExpr
>  // CHECK-NEXT:              .  |   |       `=ParenExpr
>  // CHECK-NEXT:              .  |   |         `=DeclRefExpr
>  const int sizeof_var3 = sizeof(variable_length_array);
>  // CHECK-NEXT:              .  |   |=CStyleCastExpr
>  // CHECK-NEXT:              .  |   | |=BuiltinType: void
>  // CHECK-NEXT:              .  |   | `=CXXTypeidExpr
>  // CHECK-NEXT:              .  |   |   `=DeclRefExpr
>  (void)typeid(nonconst_int);
>  // CHECK-NEXT:              .  |   |=CStyleCastExpr
>  // CHECK-NEXT:              .  |   | |=BuiltinType: void
>  // CHECK-NEXT:              .  |   | `=CXXTypeidExpr
>  // CHECK-NEXT:              .  |   |   `=RecordType: OffsetOfB
>  (void)typeid(OffsetOfB);
>  // CHECK-NEXT:              .  |   |=DeclStmt
>  // CHECK-NEXT:              .  |   | `=VarDecl: offset
>  // CHECK-NEXT:              .  |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .  |   |   `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |   |     `=OffsetOfExpr
>  // CHECK-NEXT:              .  |   |       |=RecordType: OffsetOfC
>  // CHECK-NEXT:              .  |   |       |=BinaryOperator
>  // CHECK-NEXT:              .  |   |       | |=IntegerLiteral
>  // CHECK-NEXT:              .  |   |       | `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |   |       |   `=DeclRefExpr
>  // CHECK-NEXT:              .  |   |       `=DeclRefExpr
>  const int offset = offsetof(OffsetOfC, c[9 + kInteger].b[kInteger].a);
>  // CHECK-NEXT:              .  |   `=DeclStmt
>  // CHECK-NEXT:              .  |     `=VarDecl: unary_trait
>  // CHECK-NEXT:              .  |       |=BuiltinType: int
>  // CHECK-NEXT:              .  |       `=ImplicitCastExpr
>  // CHECK-NEXT:              .  |         `=UnaryTypeTraitExpr
>  // CHECK-NEXT:              .  |           `=RecordType: OffsetOfA
>  const int unary_trait = __is_enum(OffsetOfA);
> }
>
> ////////////////////////////////////////////////////////////
> // Tests exceptions.
>
> // CHECK-NEXT:                .  |=FunctionDecl: simple_function
> // CHECK-NEXT:                .  | `=FunctionProtoType: void (char) throw(int, float)
> // CHECK-NEXT:                .  |   |=BuiltinType: void
> // CHECK-NEXT:                .  |   |=ParmVarDecl: a
> // CHECK-NEXT:                .  |   | `=BuiltinType: char
> // CHECK-NEXT:                .  |   |=BuiltinType: int
> // CHECK-NEXT:                .  |   `=BuiltinType: float
> void simple_function(char a) throw(int, float);
>
> // CHECK-NEXT:                .  |=FunctionDecl: simple_function
> // CHECK-NEXT:                .  | |=FunctionProtoType: void (char) throw(int, float)
> // CHECK-NEXT:                .  | | |=BuiltinType: void
> // CHECK-NEXT:                .  | | |=ParmVarDecl: a
> // CHECK-NEXT:                .  | | | `=BuiltinType: char
> // CHECK-NEXT:                .  | | |=BuiltinType: int
> // CHECK-NEXT:                .  | | `=BuiltinType: float
> // CHECK-NEXT:                .  | `=CompoundStmt
> void simple_function(char a) throw(int, float) {
>  // CHECK-NEXT:              .  |   `=CXXTryStmt
>  // CHECK-NEXT:              .  |     |=CompoundStmt
>  try {
>    // CHECK-NEXT:            .  |     |=CXXCatchStmt
>    // CHECK-NEXT:            .  |     | |=VarDecl: i
>    // CHECK-NEXT:            .  |     | | `=BuiltinType: int
>    // CHECK-NEXT:            .  |     | `=CompoundStmt
>  } catch (int i) {
>    // CHECK-NEXT:            .  |     |   `=CXXThrowExpr
>   throw;
>   // CHECK-NEXT:             .  |     |=CXXCatchStmt
>   // CHECK-NEXT:             .  |     | |=VarDecl:
>   // CHECK-NEXT:             .  |     | | `=PointerType: int *
>   // CHECK-NEXT:             .  |     | |   `=BuiltinType: int
>   // CHECK-NEXT:             .  |     | `=CompoundStmt
>  } catch (int*) {
>    // CHECK-NEXT:            .  |     |=CXXCatchStmt
>    // CHECK-NEXT:            .  |     | |=VarDecl:
>    // CHECK-NEXT:            .  |     | | `=BuiltinType: float
>    // CHECK-NEXT:            .  |     | `=CompoundStmt
>  } catch (float) {
>    // CHECK-NEXT:            .  |     |   `=CXXThrowExpr
>    throw;
>    // CHECK-NEXT:            .  |     `=CXXCatchStmt
>    // CHECK-NEXT:            .  |       `=CompoundStmt
>  } catch (...) {
>  }
> }
>
> ////////////////////////////////////////////////////////////
> // Tests templatized functions
>
> // CHECK-NEXT:                .  |=FunctionTemplateDecl: TplFn
> // CHECK-NEXT:                .  | |=FunctionDecl: TplFn
> // CHECK-NEXT:                .  | | |=FunctionProtoType: int (T)
> // CHECK-NEXT:                .  | | | |=BuiltinType: int
> // CHECK-NEXT:                .  | | | `=ParmVarDecl: t
> // CHECK-NEXT:                .  | | |   `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | | `=CompoundStmt
> // CHECK-NEXT:                .  | |   `=ReturnStmt
> // CHECK-NEXT:                .  | |     `=IntegerLiteral
> // CHECK-NEXT:                .  | `=TemplateTypeParmDecl: T
> // CHECK-NEXT:                .  |   `=TemplateTypeParmType: T
> template<typename T> int TplFn(T t) { return 1; }
>
> // CHECK-NEXT:                .  |=FunctionDecl: TplFn
> // CHECK-NEXT:                .  | |=BuiltinType: char
> // CHECK-NEXT:                .  | |=FunctionProtoType: int (char)
> // CHECK-NEXT:                .  | | |=BuiltinType: int
> // CHECK-NEXT:                .  | | `=ParmVarDecl: t
> // CHECK-NEXT:                .  | |   `=BuiltinType: char
> // CHECK-NEXT:                .  | `=CompoundStmt
> // CHECK-NEXT:                .  |   `=ReturnStmt
> // CHECK-NEXT:                .  |     `=IntegerLiteral
> template<> int TplFn<char>(char t) { return 2; }
>
> // CHECK-NEXT:                .  |=FunctionTemplateDecl: TplFnTI
> // CHECK-NEXT:                .  | |=FunctionDecl: TplFnTI
> // CHECK-NEXT:                .  | | |=FunctionProtoType: int (T (&)[I])
> // CHECK-NEXT:                .  | | | |=BuiltinType: int
> // CHECK-NEXT:                .  | | | `=ParmVarDecl: t
> // CHECK-NEXT:                .  | | |   `=LValueReferenceType: T (&)[I]
> // CHECK-NEXT:                .  | | |     `=DependentSizedArrayType: T [I]
> // CHECK-NEXT:                .  | | |       |=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | | |       `=DeclRefExpr
> // CHECK-NEXT:                .  | | `=CompoundStmt
> // CHECK-NEXT:                .  | |   `=ReturnStmt
> // CHECK-NEXT:                .  | |     `=IntegerLiteral
> // CHECK-NEXT:                .  | |=TemplateTypeParmDecl: T
> // CHECK-NEXT:                .  | | `=TemplateTypeParmType: T
> // CHECK-NEXT:                .  | `=NonTypeTemplateParmDecl: I
> // CHECK-NEXT:                .  |   `=BuiltinType: int
> template<typename T, int I> int TplFnTI(T (&t)[I]) { return 3; }
>
> // CHECK-NEXT:                .  |=FunctionDecl: TplFnTI
> // CHECK-NEXT:                .  | |=BuiltinType: float
> // CHECK-NEXT:                .  | |=IntegerLiteral
> // CHECK-NEXT:                .  | |=FunctionProtoType: int (float (&)[5])
> // CHECK-NEXT:                .  | | |=BuiltinType: int
> // CHECK-NEXT:                .  | | `=ParmVarDecl: t
> // CHECK-NEXT:                .  | |   `=LValueReferenceType: float (&)[5]
> // CHECK-NEXT:                .  | |     `=ConstantArrayType: float [5]
> // CHECK-NEXT:                .  | |       |=BuiltinType: float
> // CHECK-NEXT:                .  | |       `=IntegerLiteral
> // CHECK-NEXT:                .  | `=CompoundStmt
> // CHECK-NEXT:                .  |   `=ReturnStmt
> // CHECK-NEXT:                .  |     `=IntegerLiteral
> template<> int TplFnTI<float, 5>(float (&t)[5]) { return 4; }
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var1
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=CallExpr
> // CHECK-NEXT:                .  |   |=ImplicitCastExpr
> // CHECK-NEXT:                .  |   | `=DeclRefExpr
> // CHECK-NEXT:                .  |   |   `=BuiltinType: int
> // CHECK-NEXT:                .  |   `=IntegerLiteral
> int tpl_fn_var1 = TplFn<int>(10);
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var2
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=CallExpr
> // CHECK-NEXT:                .  |   |=ImplicitCastExpr
> // CHECK-NEXT:                .  |   | `=DeclRefExpr
> // CHECK-NEXT:                .  |   |   `=BuiltinType: char
> // CHECK-NEXT:                .  |   `=CharacterLiteral
> int tpl_fn_var2 = TplFn<char>('a');
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var3
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=CallExpr
> // CHECK-NEXT:                .  |   |=ImplicitCastExpr
> // CHECK-NEXT:                .  |   | `=DeclRefExpr
> // CHECK-NEXT:                .  |   `=CharacterLiteral
> int tpl_fn_var3 = TplFn('b');
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var4_arg
> // CHECK-NEXT:                .  | `=ConstantArrayType: char [6]
> // CHECK-NEXT:                .  |   |=BuiltinType: char
> // CHECK-NEXT:                .  |   `=IntegerLiteral
> char tpl_fn_var4_arg[6];
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var4
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=CallExpr
> // CHECK-NEXT:                .  |   |=ImplicitCastExpr
> // CHECK-NEXT:                .  |   | `=DeclRefExpr
> // CHECK-NEXT:                .  |   |   |=BuiltinType: char
> // CHECK-NEXT:                .  |   |   `=IntegerLiteral
> // CHECK-NEXT:                .  |   `=DeclRefExpr
> int tpl_fn_var4 = TplFnTI<char, 6>(tpl_fn_var4_arg);
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var5_arg
> // CHECK-NEXT:                .  | `=ConstantArrayType: float [5]
> // CHECK-NEXT:                .  |   |=BuiltinType: float
> // CHECK-NEXT:                .  |   `=IntegerLiteral
> float tpl_fn_var5_arg[5];
>
> // CHECK-NEXT:                .  |=VarDecl: tpl_fn_var5
> // CHECK-NEXT:                .  | |=BuiltinType: int
> // CHECK-NEXT:                .  | `=CallExpr
> // CHECK-NEXT:                .  |   |=ImplicitCastExpr
> // CHECK-NEXT:                .  |   | `=DeclRefExpr
> // CHECK-NEXT:                .  |   `=DeclRefExpr
> int tpl_fn_var5 = TplFnTI(tpl_fn_var5_arg);
>
> // CHECK-NEXT:                .  |=NamespaceDecl: ns1
> namespace ns1 {
> // CHECK-NEXT:                .  | `=CXXRecordDecl: ns1::Class1
> class Class1;
> }  // namespace ns1
>
> // CHECK-NEXT:                .  |=FunctionDecl: ElaborationTest
> // CHECK-NEXT:                .  | `=FunctionProtoType
> // CHECK-NEXT:                .  |   |=BuiltinType: void
> void ElaborationTest(
>    // CHECK-NEXT:            .  |   |=ParmVarDecl:
>    // CHECK-NEXT:            .  |   | `=ElaboratedType: class ns1::Class1
>    // CHECK-NEXT:            .  |   |   `=RecordType: ns1::Class1
>    class ns1::Class1,
>    // CHECK-NEXT:            .  |   |=ParmVarDecl:
>    // CHECK-NEXT:            .  |   | `=ElaboratedType: ns1::Class1
>    // CHECK-NEXT:            .  |   |   `=RecordType: ns1::Class1
>    ns1::Class1,
>    // CHECK-NEXT:            .  |   |=ParmVarDecl:
>    // CHECK-NEXT:            .  |   | `=ElaboratedType: class FooClass
>    // CHECK-NEXT:            .  |   |   `=RecordType: FooClass
>    class FooClass,
>    // CHECK-NEXT:            .  |   `=ParmVarDecl:
>    // CHECK-NEXT:            .  |     `=RecordType: FooClass
>    FooClass);
>
> ////////////////////////////////////////////////////////////
> // Tests friend classes (templated and not)
>
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: FriendClass
> class FriendClass {
>  // CHECK-NEXT:              .  | |=AccessSpecDecl
>  public:
>  // CHECK-NEXT:              .  | |=CXXRecordDecl: FriendClass::NestedFriendClass
>  class NestedFriendClass {};
>  // CHECK-NEXT:              .  | `=ClassTemplateDecl: FriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  |   |=CXXRecordDecl: FriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  |   `=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  |     `=TemplateTypeParmType: T
>  template<typename T> class NestedTemplateFriendClass {};
> };
> // CHECK-NEXT:                .  |=ClassTemplateDecl: TemplateFriendClass
> // CHECK-NEXT:                .  | |=CXXRecordDecl: TemplateFriendClass
> template<typename T> class TemplateFriendClass {
>  // CHECK-NEXT:              .  | | |=AccessSpecDecl
>  public:
>  // CHECK-NEXT:              .  | | |=CXXRecordDecl: TemplateFriendClass::NestedFriendClass
>  class NestedFriendClass {};
>  // CHECK-NEXT:              .  | | `=ClassTemplateDecl: TemplateFriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  | |   |=CXXRecordDecl: TemplateFriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  | |   `=TemplateTypeParmDecl: U
>  // CHECK-NEXT:              .  | |     `=TemplateTypeParmType: U
>  template<typename U> class NestedTemplateFriendClass {};
>  // CHECK-NEXT:              .  | `=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  |   `=TemplateTypeParmType: T
> };
>
> // CHECK-NEXT:                .  |=CXXRecordDecl: BefriendingClass
> class BefriendingClass {
>  // CHECK-NEXT:              .  | |=FriendDecl
>  // CHECK-NEXT:              .  | | `=ElaboratedType: class FriendClass
>  // CHECK-NEXT:              .  | |   `=RecordType: FriendClass
>  friend class FriendClass;
>  // CHECK-NEXT:              .  | |=FriendDecl
>  // CHECK-NEXT:              .  | | `=ClassTemplateDecl: TemplateFriendClass
>  // CHECK-NEXT:              .  | |   |=CXXRecordDecl: TemplateFriendClass
>  // CHECK-NEXT:              .  | |   `=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  | |     `=TemplateTypeParmType: T
>  template<typename T> friend class TemplateFriendClass;
>  // CHECK-NEXT:              .  | |=FriendDecl
>  // CHECK-NEXT:              .  | | `=ElaboratedType: class TemplateFriendClass<int>
>  // CHECK-NEXT:              .  | |   `=TemplateSpecializationType: TemplateFriendClass<int>
>  // CHECK-NEXT:              .  | |     `=BuiltinType: int
>  friend class TemplateFriendClass<int>;
>  // CHECK-NEXT:              .  | |=FriendDecl
>  // CHECK-NEXT:              .  | | `=ElaboratedType: class FriendClass::NestedFriendClass
>  // CHECK-NEXT:              .  | |   |=RecordType: FriendClass
>  // CHECK-NEXT:              .  | |   `=RecordType: FriendClass::NestedFriendClass
>  friend class FriendClass::NestedFriendClass;
>  // CHECK-NEXT:              .  | `=FriendDecl
>  // CHECK-NEXT:              .  |   `=ClassTemplateDecl: FriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  |     |=CXXRecordDecl: FriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  |     | `=RecordType: FriendClass
>  // CHECK-NEXT:              .  |     `=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  |       `=TemplateTypeParmType: T
>  template<typename T> friend class FriendClass::NestedTemplateFriendClass;
> };
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: TemplateBefriendingClass
> // CHECK-NEXT:                .  | |=CXXRecordDecl: TemplateBefriendingClass
> template<typename T> class TemplateBefriendingClass {
>  // CHECK-NEXT:              .  | | |=FriendDecl
>  // CHECK-NEXT:              .  | | | `=DependentNameType: class TemplateFriendClass<T>::NestedFriendClass
>  // CHECK-NEXT:              .  | | |   `=TemplateSpecializationType: TemplateFriendClass<T>
>  // CHECK-NEXT:              .  | | |     `=TemplateTypeParmType: T
>  friend class TemplateFriendClass<T>::NestedFriendClass;
>  // CHECK-NEXT:              .  | | `=FriendDecl
>  // CHECK-NEXT:              .  | |   `=ClassTemplateDecl: TemplateFriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  | |     |=CXXRecordDecl: TemplateFriendClass::NestedTemplateFriendClass
>  // CHECK-NEXT:              .  | |     | `=TemplateSpecializationType: TemplateFriendClass<T>
>  // CHECK-NEXT:              .  | |     |   `=TemplateTypeParmType: T
>  // CHECK-NEXT:              .  | |     `=TemplateTypeParmDecl: U
>  // CHECK-NEXT:              .  | |       `=TemplateTypeParmType: U
>  template<typename U> friend class TemplateFriendClass<T>::NestedTemplateFriendClass;
>  // CHECK-NEXT:              .  | `=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .  |   `=TemplateTypeParmType: T
> };
>
> ////////////////////////////////////////////////////////////
> // Tests unresolved constructors: function-looking things involving tpl args
>
> // CHECK-NEXT:                .  |=ClassTemplateDecl: UnresolvedCtorHelper
> // CHECK-NEXT:                .  | |=CXXRecordDecl: UnresolvedCtorHelper
> // CHECK-NEXT:                .  | `=TemplateTypeParmDecl: A
> // CHECK-NEXT:                .  |   `=TemplateTypeParmType: A
> template<typename A> struct UnresolvedCtorHelper { };
>
> template <typename T, template<typename A> class U>
> // CHECK-NEXT:                .  `=FunctionTemplateDecl: TestUnresolvedCtor
> // CHECK-NEXT:                .    |=FunctionDecl: TestUnresolvedCtor
> // CHECK-NEXT:                .    | |=FunctionProtoType: void ()
> // CHECK-NEXT:                .    | | `=BuiltinType: void
> // CHECK-NEXT:                .    | `=CompoundStmt
> void TestUnresolvedCtor() {
>  // CHECK-NEXT:              .    |   |=DeclStmt
>  // CHECK-NEXT:              .    |   | `=VarDecl: x
>  // CHECK-NEXT:              .    |   |   |=BuiltinType: int
>  // CHECK-NEXT:              .    |   |   `=CXXUnresolvedConstructExpr
>  // CHECK-NEXT:              .    |   |     `=TemplateTypeParmType: T
>  int x = T();
>  // CHECK-NEXT:              .    |   |=BinaryOperator
>  // CHECK-NEXT:              .    |   | |=DeclRefExpr
>  // CHECK-NEXT:              .    |   | `=CXXUnresolvedConstructExpr
>  // CHECK-NEXT:              .    |   |   `=TemplateSpecializationType: U<int>
>  // CHECK-NEXT:              .    |   |     `=BuiltinType: int
>  x = U<int>();
>  // CHECK-NEXT:              .    |   |=BinaryOperator
>  // CHECK-NEXT:              .    |   | |=DeclRefExpr
>  // CHECK-NEXT:              .    |   | `=CXXUnresolvedConstructExpr
>  // CHECK-NEXT:              .    |   |   `=TemplateSpecializationType: U<T>
>  // CHECK-NEXT:              .    |   |     `=TemplateTypeParmType: T
>  x = U<T>();
>  // CHECK-NEXT:              .    |   `=BinaryOperator
>  // CHECK-NEXT:              .    |     |=DeclRefExpr
>  // CHECK-NEXT:              .    |     `=CXXUnresolvedConstructExpr
>  // CHECK-NEXT:              .    |       `=TemplateSpecializationType: UnresolvedCtorHelper<T>
>  // CHECK-NEXT:              .    |         `=TemplateTypeParmType: T
>  x = UnresolvedCtorHelper<T>();
>  // CHECK-NEXT:              .    |=TemplateTypeParmDecl: T
>  // CHECK-NEXT:              .    | `=TemplateTypeParmType: T
>  // CHECK-NEXT:              .    `=TemplateTemplateParmDecl: U
>  // CHECK-NEXT:              .      `=TemplateTypeParmDecl: A
>  // CHECK-NEXT:              .        `=TemplateTypeParmType: A
> }
>
> // CHECK-NEXT: EOF
>



-- 
Zhanyong




More information about the cfe-commits mailing list